All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH v9 00/24] hw/arm: add Faraday A369 SoC platform support
@ 2013-03-25 12:09 Kuo-Jung Su
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 01/24] target-arm: add Faraday ARMv5TE processors support Kuo-Jung Su
                   ` (23 more replies)
  0 siblings, 24 replies; 35+ messages in thread
From: Kuo-Jung Su @ 2013-03-25 12:09 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, i.mitsyanko, Blue Swirl, Paul Brook, Kuo-Jung Su,
	Andreas, fred.konrad

From: Kuo-Jung Su <dantesu@faraday-tech.com>

These patches introduce Faraday A369 SoC platform support.

Here are some public documents for your reference.

http://www.faraday-tech.com/html/documentation/index.html

The partial version of faraday cpu datasheet with only
the CP15 and MPU description are available at my Google Drive:

https://docs.google.com/folder/d/0BwfiewvSmUgAalh5TkxyZWtlWEE/edit?usp=sharing

The pre-built A369 images are also available at my Google Drive:

https://docs.google.com/folder/d/0BwfiewvSmUgAX2pTTmtUMGFCZW8/edit

Here is the image file list:

1. android-4.0.4/zImage: A369 linux-3.0.31
2. android-4.0.4/romfs-4.0.4.tar.bz2: android-4.0.4
3. nand.img.bz2: A369 nand flash image
4. rom.img.bz2: A369 embedded ROM image
5. u-boot: A369 u-boot-2012.10 ELF file
6. zImage: A369 linux-3.4.12 + initramfs
7. README

If you want to re-build the image from scratch, 
try my Faraday Linux BSP/SDK: falinux.

1. Download the falinux

   $ git clone https://github.com/dantesu1218/falinux.git

2. Download all the required software packages:

   $ cd falinux
   $ make setup

3. Launch the menuconfig of falinux, and enable 'QEMU Support'

   $ make menuconfig

4. Re-build the packages

   $ make

5. Laungh the qemu-1.3.0 with the generated falinux images.

   $ make qemu

Changes for v8 -> v9:

    1. hw/arm: Update the file directory structure (Addressed by Peter.M)
       hw/arm: top level board models and things directly reference the CPU only
       hw: otherwise
    2. hw/arm: Rename SoC platform specific files:
       hw/arm/faraday_a369.c -> hw/arm/ftplat_a369.c
       hw/arm/faraday_a369_soc.c -> hw/arm/ftplat_a369soc.c
       hw/arm/faraday_a369_scu.c -> hw/arm/ftplat_a369scu.c
       hw/arm/faraday_a369_kpd.c -> hw/arm/ftplat_a369kpd.c
    3. hw: audio_codec.[ch] -> audio.[ch]
    4. hw/fti2c010: Drop .addr from Fti2c010State. (Addressed by Peter.C)
    5. hw/fti2c010: Add qemu_log_mask(LOG_UNIMP, ) to slave mode. (Addressed by Peter.C)
    6. hw/fti2c010: Update data type of Fti2c010State.recv from 'uint8_t' into 'bool'.
    7. hw/*.c: QOM Coding Conventions (Addressed by Peter.C)
    8. hw/*.h: Inlcudes bitops only if they need it. (Addressed by Peter.C)
    9. hw/ftplat_a369soc: Add a container memory region for remappable devices. (i.e. ROM, RAM)
       (Addressed by Peter.C and Peter.M)
    10. hw/faraday.h: Drop cpu, cpu_model, ahb_remapped, ddr_inited, rom_size, ram_size 
        scu, ahbc, ddrc from FaradaySoCState.
    11. hw/faraday.h: Replace FaradaySoCState.bi with FaradaySoCState.ram_boot
    12. hw/arm/ftplat_a369soc: Move customized system reset to ftplat_a369.c
    13. hw/faraday.h: Add faraday_soc_ahb_remap() and faraday_soc_ram_setup()
        for ftddrii030, ftahbc020, ftplat_a369soc and ftplat_a369.
    14. hw/ftahbc020, ftapbbrg020, ftddrii030, ftnandc021, ftssp010.c:
        Move object_property_add_link() to object::instance_init()

Changes for v7 -> v8:
    1. [hw/arm/ftintc020.c] (Addressed by Peter)
       'uint32_t mask = BIT(irq % 32);' -> 'uint32_t mask = BIT(irq & 0x1f);'
    2. [hw/arm/*.c] (Addressed by Peter)
       Drop '#include "faraday.h"', and define DPRINTF() at per-file basis
       exit(1) -> abort()
       SysBusDeviceClass::init() -> Device::realize()

Changes for v6 -> v7:

    1. hw/arm/ftwdt010: (Addressed by Paolo)
       Replace 'qemu_system_reset_request()' with 'watchdog_perform_action()'.
    2. hw/arm/ftrtc011: (Addressed by Paolo)
       Replace 'get_clock_realtime()' with 'qemu_get_clock_ns(rtc_clock)'.
       Replace 'qemu_new_timer_ms(rt_clock...)' with 'qemu_new_timer_ms(rtc_clock...)'
       Rename 'ftrtc011_timer_resync' -> 'ftrtc011_timer_rebase'
       Add ftrtc011_timer_resche()
       Add QTest support
    3. hw/arm/faraday_a369_soc: (Addressed by Paolo)
       Remove redundant device reset from a369soc_system_reset(...)
    4. include/qemu/bitops.h: (Addressed by Paolo)
       Update bitrev8() to use a kind of divide and conquer algorithm.
    5. hw/arm/ftahb020,ftddrii030,ftapbbrg020,faraday_a369_soc:
       Drop FARADAY_SOC_GET_CORE(), replaced with device to device QOM link.
    6. hw/wm8731,wm8750,marvell_88w8618_audio,spitz,z2:
       Add AudioCodecClass for wm87xx audio class abstration.
    7. hw/arm/ftnandc021:
       Drop nand_init(), replaced with device to device QOM link.
    8. hw/arm/ftsdc010:
       Bug fixed to write operation: remove sd_data_ready() in write path.
    9. hw/arm/ftgmac100,ftmac110,ftwdt010:
       rt-clock -> vm_clock
   10. hw/nand.c:
       Drop [hw/nand.c: correct the sense of the BUSY/READY], since it's already applied.
       Drop [hw/nand.c: bug fix to erase operation], since Peter has a better solution.

Changes for v5 -> v6:

   1. hw/arm/Makefile.objs: 
       Shift to the next line only after current line length is > 80 characters.
   2. hw/arm/faraday.h:
       Update debug macro format per discussions at the link bellow:
       http://thread.gmane.org/gmane.comp.emulators.qemu/195996/focus=196975
   3. hw/arm/faraday.h:
       TYPE_FARADAY_SOC: 'faraday/soc' -> 'faraday.soc'
   4. hw/arm/*.c:
       Replace '%llx' with '%HWADDR_PRIx'
   5. hw/arm/*.h:
       Add '#include "qemu/bitops.h"'
   6. hw/arm/faraday_a369_soc.c:
       Typo fix: salve4 -> slave4, salve6 -> slave6
   7. hw/arm/*.c:
       Drop FROM_SYSBUS().
   8. hw/arm/ftintc020.c:
       Re-write the source file with pl190.c as template.
   9. hw/arm/ftintc020.h:
       Rename the registers to improve readibility.
   10. hw/arm/faraday.h:
       Add dedicated spi flash pointer array (DeviceState *spi_fl[2]).
   11. hw/arm/faraday.h:
       Add 'qemu_irq pic[64]'.
   12. hw/arm/*c:
       Replace the complex conversion in XXX_REG32() with directly array indexing.
   13. hw/arm/*c:
       Update the error message output from 'hw_error(...)' into 'fprintf(stderr, ...)'
   14. hw/arm/faraday_a369.c, hw/arm/faraday_a369_soc.c:
       Move the external AHB device init from faraday_a369_soc.c to faraday_a369.c
   15. include/qemu/bitops.h:
       Move bitrev() from bitrev.[ch] into bitops.h
   16. hw/nand.c:
       Remove the clear/set SR[6] code in read/erase/write, because
       it's not possible to be observed by a guest.
   17. hw/arm/faraday_a369_kpd.c:
       Function prefix: 'ftkbc010' -> 'a369kpd' 
       Name of struct:  'FTKBC010State' -> 'A369KPDState'.
       QOM: 'FTKBC010' -> 'A369KPD'
   18. hw/arm/ftddrii030.c:
       'case REG_MCR ... 0x4c' -> 'case REG_MCR ... (CFG_REGSIZE - 1) * 4'
   19. hw/arm/ftahbc020.c:
       Add comments to fall-through path. (i.e. /* fall-through - skip slave6 */)
   20. hw/arm/faraday_a369.c:
       Update spi flash init code to use the new QOM classes of m25p80.

Changes for v3 -> v5:

   1. [cpu] Add the unapplied patches for faraday ARMv5TE cores.
   2. [nand] Add the unapplied patches for hw/nand.c.
   3. [audio] Add the unapplied patches for wm8731.
   4. [Global] Replace redundant statement in xxx_update_irq() with
            qemu_set_irq(s->irq, !!(xxxx));
   5. [Global] Introduce 'case xxx ... xxx' statement in switch { }
            to replace the 'if {} else {}' statement.
   6. [Global] Replace complex bitwise operation with functions defined in bitops.h
   7. [Global] Introduce array register caches (i.e. s->regs[]) implementation
            to fttsc010, ftrtc011, ftmac110, ftgmac100, ftddrii030
            faraday_a369_kpd and faraday_a369_soc.
   8. [Global] Add qemu_log_mask(LOG_GUEST_ERROR...) on undefined memory accesses.
   9. [Global] Add missing register access size constraint to
            faraday_a369_kpd, faraday_a369_soc, ftahbc020.c, ftddrii030.c,
            ftintc020.c, ftlcdc200.c, ftpwmtmr010.c, ftspi020.c, fttmr010.c
            and fttsc010.c.
  10. [Global] Add more register and bitmask descriptions to all the chips.
  11. [Global] Rename all the MemoryRegionOps variables to 'mmio_ops'
  12. [Global] Update all non-system header includes from '<>' into '""'
  13. [bitrev] Port bitrev.[ch] from linux kernel into QEMU.
  14. [ftapbbrg020] Replace cpu_physical_memory_read()/cpu_physical_memory_write()
            with dma_memory_read()/dma_memory_write()
  15. [ftdmac020] Replace cpu_physical_memory_read()/cpu_physical_memory_write()
            with dma_memory_read()/dma_memory_write()
  16. [ftgmac100/ftmac110] Replace cpu_physical_memory_read()/cpu_physical_memory_write()
            with dma_memory_read()/dma_memory_write()
  17. [ftgmac100/ftmac110] Add a 10ms timer for polling the owner bit of descriptors
              to call qemu_flush_queued_packets() when it's necessary.
  18. [ftgmac100/ftmac110] Replace hw_error() with qemu_log_mask(LOG_GUEST_ERROR, ) 
              for programmer errors.
  19. [ftgmac100/ftmac110] Update descriptor struct for big endian QEMU host support.
  20. [ftgmac100/ftmac110] Typo fix ('FTMAC_H' --> 'HW_ARM_FTMAC110_H') to the header files.
  21. [ftmac110] Update mdio phy response emulation from Marvell into Davicom PHY.
  22. [ftwdt010/fttsc010] Switch to a slower timer. (i.e. rt_clock)

  23. [faraday_a360] Removed. The ftmac110 and fttmr010 has been moved to 
              A369 as external AHB devices.  
  24. [faraday_a369] Add register caches for AHB/APB slave devices.
  25. [faraday_a369] Remember (arm_boot_info *) when launching in direct boot mode.
  26. [faraday_a369] Replace pointer to (mach *) with QOM APIs. (i.e. no more DEFINE_PROP_PTR())
  27. [faraday_a369] Add a error message for ROM mode when it failed to load image.
  28. [faraday_a369] Add the missing AHB remap process into direct boot mode.
  29. [faraday_a369] Bug fix to ROM and RAM memory overlap issue.
  30. [faraday_a369] Remove system reset order dependency from ftddrii030 and ftahbc020
  31. [faraday_a369] Separate the a369 platform implementation into 2 different parts:
              board=faraday_a369.c, and soc=faraday_a369_soc.c
  32. [faraday_a369] Copy all the un-used register values from read hardware into QEMU model.
  33. [ftssp010] Add address alignment checker.
  34. [ftssp010] Move the audio codec initialization code to faraday_a369.c.
  35. [ftspi020] Use a constant (CFG_NR_CSLINES) for the number of cs lines.
  36. [ftspi020] Replace 's->datacnt <= 0' with '!s->datacnt', because s->datacnt is unsigned.
  37. [ftspi020] Replace 's->datacnt' with 's->cmd[2]'
  38. [ftspi020] Remove redundant M25P80s command opcodes from the header file.
  39. [ftspi020] Replace 'g_new' with 'g_new0' for cs_lines allocation.
              This fix the bug which crash the QEMU when de-activating an unattached
              spi flash chip select. Thanks to Peter.

Changes for v3:
   
   1. [global] review all commit log make sure it correctly
               describe the QEMU model.
   2. [global] reformat the entire patch series to be compilable 
               on its own. Thanks to Andreas and Igor.
   3. [global] move all files from 'hw' into 'hw/arm', and 
               rename a36x.c to faraday_a36x.c
   4. [global] update lisence to GPLv2+
   5. [global] rename both struct and typedef names to CamelCase.
   6. [global] turn printfs into DPRINTF() which is controlled 
               by 'DEBUG_FARADAY'
   7. [global] remove disabled code.
   8. [global] add header files with descriptive defines to 
               ftintc020, ftrtc011, fttmr010 ... etc.
   9. [global] update all header file to have a 'HW_ARM_' prefix 
               to header guards.
  10. [faraday.h] add parameter names to ftmac110_init() and
                  ftgmac100_init()
  11. [a360/a369] remove printf("xxx %dMB ram.\n"...);
  12. [a360/a369] add 'arm926' as a fail-safe cpu model.
  13. [a360/a369] add DEFAULT_MACHINE_OPTIONS.
  14. [a360/a369] remove USB-EHCI support, it's now a standalone patch.
  15. [a360] replace ftlcdc200 init from sysbus_create_varargs() to
             sysbus_create_simple().
  16. [a360] make PMU a standalone file. (faraday_a360_pmu.c)
  17. [a369] make SCU a standalone file. (faraday_a369_pcu.c)
  18. [a369] make AHBC a standalone file. (ftahbc020.c)
  19. [a369] make DDRC a standalone file. (ftddrii030.c)
  20. [a369] rename ftkbc010 to faraday_a369_keypad.c
  21. [a369] replace ROM emulation with PFLASH.(rom.c is deleted)
  22. [ftsdc010] add sd_data_ready() to data R/W.
  23. [ftsdc010] check 'datacnt' at each read/write to make sure
             buffer would not overflow/underflow and also prevent
             'datacnt' from underflow by subtracted by a 4.
  24. [ftgmac100] add 802.1Q VLAN tag insertion/removal support.
  25. [ftgmac100] replace the dumb timer code with bottom half.
  26. [ftgmac100] rename the struct name of descriptors to CamelCase.
  27. [ftgmac100] replace 'void *' with 'Ftgmac100RXD *' and 
                  'Ftgmac100TXD *' for descriptor read/write.
  28. [ftgmac100] add buffer overflow check in ftgmac100_transmit()
  29. [ftgmac100] add buffer alignment check
  30. [ftmac110] replace the dumb timer code with bottom half.
  31. [ftmac110] rename the struct name of descriptors to CamelCase.
  32. [ftmac110] replace 'void *' with 'Ftmac110RXD *' and
                 'Ftmac110TXD *' for descriptor read/write.
  33. [ftmac110] add buffer overflow check in ftmac110_transmit()
  34. [ftmac110] add buffer alignment check
  35. [ftdmac020] replace the dumb timer code with bottom half.
  36. [ftdmac020] replace cpu_physical_memory_map() with 
                  cpu_physical_memory_read/write() for both simplicity
                  and security enhancement which caused by the un-checked
                  return mapped length from cpu_physical_memory_map().
  37. [ftapbbrg020] replace the dumb timer code with bottom half.
  38. [ftapbbrg020] replace cpu_physical_memory_map() with 
                  cpu_physical_memory_read/write() for both simplicity
                  and security enhancement which caused by the un-checked
                  return mapped length from cpu_physical_memory_map().
  39. [ftrtc011] switch to a slower timer with msec resolution. (i.e. rt_clock)
  40. [ftrtc011] re-calculate RTC counters from host timestamp upon register 
                 read/write and also vm save/restore.

Changes for v2:

   1. coding style fixes (verified with checkpatch.pl)
   2. add Faraday A360 support
   3. add Faraday USB 2.0 EHCI support
   4. merge a369_scu.c into a369.c
   5. introduce QOM coding style
   6. remove lowercase Macros: min(), max()
   7. name all struct as CamelCase style
   8. move function prototypes from .c to faraday.h
   9. use switch instead of if statement in a369_ahbc_write
  10. remove debug prints
  11. update all uarts in A36x to DEVICE_LITTLE_ENDIAN.
  12. move the variable definitions to the start of the function,
      instead of inside a do { } while(0)
  13. remove disabled and commented out code.
  14. use hw_error() and exit() upon pflash register failed.
  15. add const prior to TypeInfo (i.e. static *const*
      TypeInfo ftrtc011_info)
  16. add le32_to_cpu/cpu_to_le32 to the descriptor processing in
      FTGMAC100/FTMAC110.
  17. update the GPL license to GPL v2 (except for FTLCDC200,
      it's based on pl110.c which is LGPL.)
  18. add const to src_* in the DMA controllers (FTAPBBRG020/FTDMAC020)


Kuo-Jung Su (24):
  target-arm: add Faraday ARMv5TE processors support
  hw/arm: add Faraday a369 SoC platform support
  hw/arm: add FTINTC020 interrupt controller support
  hw/arm: add FTAHBC020 AHB controller support
  hw/arm: add FTDDRII030 DDRII controller support
  hw/arm: add FTPWMTMR010 timer support
  hw/arm: add FTWDT010 watchdog timer support
  hw/arm: add FTRTC011 RTC timer support
  tests: add QTest for FTRTC011
  hw/arm: add FTDMAC020 AHB DMA support
  hw/arm: add FTAPBBRG020 APB DMA support
  hw/arm: add FTNANDC021 nand flash controller support
  hw/arm: add FTI2C010 I2C controller support
  hw: Add AudioCodecClass for wm87xx audio class abstration.
  hw: add WM8731 audio codec support
  The FTSSP010 is a multi-function synchronous serial port interface   
     controller which supports SSP, SPI, I2S, AC97 and SPDIF.
  qemu/bitops.h: add the bit ordering reversal functions
  hw/arm: add FTGMAC100 1Gbps ethernet support
  hw/arm: add FTLCDC200 LCD controller support
  hw/arm: add FTTSC010 touchscreen controller support
  hw/arm: add FTSDC010 MMC/SD controller support
  hw/arm: add FTMAC110 10/100Mbps ethernet support
  hw/arm: add FTTMR010 timer support
  hw/arm: add FTSPI020 SPI flash controller support

 default-configs/arm-softmmu.mak |    1 +
 hw/Makefile.objs                |    2 +
 hw/arm/Makefile.objs            |    6 +
 hw/arm/ftplat_a369.c            |  180 ++++++++++
 hw/arm/ftplat_a369kpd.c         |  235 +++++++++++++
 hw/arm/ftplat_a369scu.c         |  186 ++++++++++
 hw/arm/ftplat_a369soc.c         |  294 ++++++++++++++++
 hw/arm/spitz.c                  |    9 +-
 hw/arm/z2.c                     |    9 +-
 hw/audio.c                      |   81 +++++
 hw/audio.h                      |   56 +++
 hw/faraday.h                    |  133 ++++++++
 hw/ftahbc020.c                  |  216 ++++++++++++
 hw/ftapbbrg020.c                |  478 ++++++++++++++++++++++++++
 hw/ftapbbrg020.h                |   44 +++
 hw/ftddrii030.c                 |  192 +++++++++++
 hw/ftdmac020.c                  |  599 ++++++++++++++++++++++++++++++++
 hw/ftdmac020.h                  |  107 ++++++
 hw/ftgmac100.c                  |  712 +++++++++++++++++++++++++++++++++++++++
 hw/ftgmac100.h                  |  237 +++++++++++++
 hw/fti2c010.c                   |  224 ++++++++++++
 hw/fti2c010.h                   |   74 ++++
 hw/ftintc020.c                  |  307 +++++++++++++++++
 hw/ftintc020.h                  |   55 +++
 hw/ftkbc010.h                   |   44 +++
 hw/ftlcdc200.c                  |  516 ++++++++++++++++++++++++++++
 hw/ftlcdc200.h                  |  110 ++++++
 hw/ftlcdc200_template.h         |  439 ++++++++++++++++++++++++
 hw/ftmac110.c                   |  665 ++++++++++++++++++++++++++++++++++++
 hw/ftmac110.h                   |  166 +++++++++
 hw/ftnandc021.c                 |  526 +++++++++++++++++++++++++++++
 hw/ftnandc021.h                 |   84 +++++
 hw/ftpwmtmr010.c                |  261 ++++++++++++++
 hw/ftpwmtmr010.h                |   31 ++
 hw/ftrtc011.c                   |  387 +++++++++++++++++++++
 hw/ftrtc011.h                   |   53 +++
 hw/ftsdc010.c                   |  359 ++++++++++++++++++++
 hw/ftsdc010.h                   |   88 +++++
 hw/ftspi020.c                   |  341 +++++++++++++++++++
 hw/ftspi020.h                   |   81 +++++
 hw/ftssp010.c                   |  504 +++++++++++++++++++++++++++
 hw/ftssp010.h                   |   96 ++++++
 hw/fttmr010.c                   |  449 ++++++++++++++++++++++++
 hw/fttmr010.h                   |   39 +++
 hw/fttsc010.c                   |  264 +++++++++++++++
 hw/fttsc010.h                   |   39 +++
 hw/ftwdt010.c                   |  213 ++++++++++++
 hw/ftwdt010.h                   |   35 ++
 hw/i2c.h                        |    9 -
 hw/marvell_88w8618_audio.c      |   23 +-
 hw/wm8731.c                     |  505 +++++++++++++++++++++++++++
 hw/wm8750.c                     |   91 ++---
 include/qemu/bitops.h           |   59 +++-
 target-arm/cpu.c                |   52 +++
 target-arm/cpu.h                |    6 +-
 target-arm/helper.c             |   84 +++++
 target-arm/machine.c            |    4 +
 tests/Makefile                  |    3 +
 tests/ftrtc011-test.c           |  410 ++++++++++++++++++++++
 59 files changed, 11403 insertions(+), 70 deletions(-)
 create mode 100644 hw/arm/ftplat_a369.c
 create mode 100644 hw/arm/ftplat_a369kpd.c
 create mode 100644 hw/arm/ftplat_a369scu.c
 create mode 100644 hw/arm/ftplat_a369soc.c
 create mode 100644 hw/audio.c
 create mode 100644 hw/audio.h
 create mode 100644 hw/faraday.h
 create mode 100644 hw/ftahbc020.c
 create mode 100644 hw/ftapbbrg020.c
 create mode 100644 hw/ftapbbrg020.h
 create mode 100644 hw/ftddrii030.c
 create mode 100644 hw/ftdmac020.c
 create mode 100644 hw/ftdmac020.h
 create mode 100644 hw/ftgmac100.c
 create mode 100644 hw/ftgmac100.h
 create mode 100644 hw/fti2c010.c
 create mode 100644 hw/fti2c010.h
 create mode 100644 hw/ftintc020.c
 create mode 100644 hw/ftintc020.h
 create mode 100644 hw/ftkbc010.h
 create mode 100644 hw/ftlcdc200.c
 create mode 100644 hw/ftlcdc200.h
 create mode 100644 hw/ftlcdc200_template.h
 create mode 100644 hw/ftmac110.c
 create mode 100644 hw/ftmac110.h
 create mode 100644 hw/ftnandc021.c
 create mode 100644 hw/ftnandc021.h
 create mode 100644 hw/ftpwmtmr010.c
 create mode 100644 hw/ftpwmtmr010.h
 create mode 100644 hw/ftrtc011.c
 create mode 100644 hw/ftrtc011.h
 create mode 100644 hw/ftsdc010.c
 create mode 100644 hw/ftsdc010.h
 create mode 100644 hw/ftspi020.c
 create mode 100644 hw/ftspi020.h
 create mode 100644 hw/ftssp010.c
 create mode 100644 hw/ftssp010.h
 create mode 100644 hw/fttmr010.c
 create mode 100644 hw/fttmr010.h
 create mode 100644 hw/fttsc010.c
 create mode 100644 hw/fttsc010.h
 create mode 100644 hw/ftwdt010.c
 create mode 100644 hw/ftwdt010.h
 create mode 100644 hw/wm8731.c
 create mode 100644 tests/ftrtc011-test.c

-- 
1.7.9.5

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH v9 01/24] target-arm: add Faraday ARMv5TE processors support
  2013-03-25 12:09 [Qemu-devel] [PATCH v9 00/24] hw/arm: add Faraday A369 SoC platform support Kuo-Jung Su
@ 2013-03-25 12:09 ` Kuo-Jung Su
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 02/24] hw/arm: add Faraday a369 SoC platform support Kuo-Jung Su
                   ` (22 subsequent siblings)
  23 siblings, 0 replies; 35+ messages in thread
From: Kuo-Jung Su @ 2013-03-25 12:09 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, i.mitsyanko, Blue Swirl, Paul Brook, Kuo-Jung Su,
	Andreas, fred.konrad

From: Kuo-Jung Su <dantesu@faraday-tech.com>

This patch includes the single core support to FA606TE, FA626TE,
FA616TE and FA726TE with CP15 Faraday extensions (AUX and I/D-Scratchpad).

Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
---
 target-arm/cpu.c     |   52 +++++++++++++++++++++++++++++++
 target-arm/cpu.h     |    6 +++-
 target-arm/helper.c  |   84 ++++++++++++++++++++++++++++++++++++++++++++++++++
 target-arm/machine.c |    4 +++
 4 files changed, 145 insertions(+), 1 deletion(-)

diff --git a/target-arm/cpu.c b/target-arm/cpu.c
index a1e9093..aed97b0 100644
--- a/target-arm/cpu.c
+++ b/target-arm/cpu.c
@@ -244,6 +244,54 @@ static void arm926_initfn(Object *obj)
     cpu->reset_sctlr = 0x00090078;
 }
 
+static void fa606te_initfn(Object *obj)
+{
+    ARMCPU *cpu = ARM_CPU(obj);
+    set_feature(&cpu->env, ARM_FEATURE_V5);
+    set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS);
+    set_feature(&cpu->env, ARM_FEATURE_MPU_FARADAY);
+    cpu->midr = 0x66056061; /* CR0-0 Identification Code Register (ID) */
+    cpu->ctr = 0x00000000;  /* CR0-1 Cache Type Register (CTR) */
+    cpu->reset_sctlr = 0x00000078;  /* CR1-0 Configuration Register (CFG) */
+}
+
+static void fa616te_initfn(Object *obj)
+{
+    ARMCPU *cpu = ARM_CPU(obj);
+    set_feature(&cpu->env, ARM_FEATURE_V5);
+    set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS);
+    set_feature(&cpu->env, ARM_FEATURE_CACHE_TEST_CLEAN);
+    set_feature(&cpu->env, ARM_FEATURE_TCM_FARADAY);
+    cpu->midr = 0x66056161; /* CR0-0 Identification Code Register (ID) */
+    cpu->ctr = 0x1d152152;  /* CR0-1 Cache Type Register (CTR) */
+    cpu->reset_sctlr = 0x00050078;  /* CR1-0 Configuration Register (CFG) */
+}
+
+static void fa626te_initfn(Object *obj)
+{
+    ARMCPU *cpu = ARM_CPU(obj);
+    set_feature(&cpu->env, ARM_FEATURE_V5);
+    set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS);
+    set_feature(&cpu->env, ARM_FEATURE_CACHE_TEST_CLEAN);
+    set_feature(&cpu->env, ARM_FEATURE_TCM_FARADAY);
+    set_feature(&cpu->env, ARM_FEATURE_AUXCR);
+    cpu->midr = 0x66056261; /* CR0-0 Identification Code Register (ID) */
+    cpu->ctr = 0x0f192192;  /* CR0-1 Cache Type Register (CTR) */
+    cpu->reset_sctlr = 0x00000078;  /* CR1-0 Configuration Register (CFG) */
+}
+
+static void fa726te_initfn(Object *obj)
+{
+    ARMCPU *cpu = ARM_CPU(obj);
+    set_feature(&cpu->env, ARM_FEATURE_V5);
+    set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS);
+    set_feature(&cpu->env, ARM_FEATURE_CACHE_TEST_CLEAN);
+    set_feature(&cpu->env, ARM_FEATURE_TCM_FARADAY);
+    cpu->midr = 0x66057261; /* CR0-0 Identification Code Register (ID) */
+    cpu->ctr = 0x1d192192;  /* CR0-1 Cache Type Register (CTR) */
+    cpu->reset_sctlr = 0x00050078;  /* CR1-0 Configuration Register (CFG) */
+}
+
 static void arm946_initfn(Object *obj)
 {
     ARMCPU *cpu = ARM_CPU(obj);
@@ -766,6 +814,10 @@ typedef struct ARMCPUInfo {
 
 static const ARMCPUInfo arm_cpus[] = {
     { .name = "arm926",      .initfn = arm926_initfn },
+    { .name = "fa606te",     .initfn = fa606te_initfn },
+    { .name = "fa616te",     .initfn = fa616te_initfn },
+    { .name = "fa626te",     .initfn = fa626te_initfn },
+    { .name = "fa726te",     .initfn = fa726te_initfn },
     { .name = "arm946",      .initfn = arm946_initfn },
     { .name = "arm1026",     .initfn = arm1026_initfn },
     /* What QEMU calls "arm1136-r2" is actually the 1136 r0p2, i.e. an
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index 2b97221..dd27e80 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -152,6 +152,8 @@ typedef struct CPUARMState {
         uint32_t c15_diagnostic; /* diagnostic register */
         uint32_t c15_power_diagnostic;
         uint32_t c15_power_control; /* power control */
+        uint32_t c15_tcm_data;      /* Data TCM region register */
+        uint32_t c15_tcm_inst;      /* Instruction TCM region register */
     } cp15;
 
     struct {
@@ -392,6 +394,8 @@ enum arm_features {
     ARM_FEATURE_MPIDR, /* has cp15 MPIDR */
     ARM_FEATURE_PXN, /* has Privileged Execute Never bit */
     ARM_FEATURE_LPAE, /* has Large Physical Address Extension */
+    ARM_FEATURE_TCM_FARADAY, /* Faraday Scratchpad(TCM) */
+    ARM_FEATURE_MPU_FARADAY, /* Faraday MPU */
 };
 
 static inline int arm_feature(CPUARMState *env, int feature)
@@ -640,7 +644,7 @@ static inline CPUARMState *cpu_init(const char *cpu_model)
 #define cpu_signal_handler cpu_arm_signal_handler
 #define cpu_list arm_cpu_list
 
-#define CPU_SAVE_VERSION 9
+#define CPU_SAVE_VERSION 10
 
 /* MMU modes definitions */
 #define MMU_MODE0_SUFFIX _kernel
diff --git a/target-arm/helper.c b/target-arm/helper.c
index fd055e8..337d8b8 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -1015,6 +1015,84 @@ static const ARMCPRegInfo lpae_cp_reginfo[] = {
     REGINFO_SENTINEL
 };
 
+static int faraday_pmsav5_ircfg_read(CPUARMState *env,
+                                     const ARMCPRegInfo *ri,
+                                     uint64_t *value)
+{
+    if (ri->crm >= 4) {
+        return EXCP_UDEF;
+    }
+    *value = env->cp15.c6_region[ri->crm];
+    return 0;
+}
+
+static int faraday_pmsav5_ircfg_write(CPUARMState *env,
+                                      const ARMCPRegInfo *ri,
+                                      uint64_t value)
+{
+    if (ri->crm >= 4) {
+        return EXCP_UDEF;
+    }
+    env->cp15.c6_region[ri->crm] = value;
+    return 0;
+}
+
+static int faraday_pmsav5_drcfg_read(CPUARMState *env,
+                                     const ARMCPRegInfo *ri,
+                                     uint64_t *value)
+{
+    if (ri->crm >= 4) {
+        return EXCP_UDEF;
+    }
+    *value = env->cp15.c6_region[ri->crm + 4];
+    return 0;
+}
+
+static int faraday_pmsav5_drcfg_write(CPUARMState *env,
+                                      const ARMCPRegInfo *ri,
+                                      uint64_t value)
+{
+    if (ri->crm >= 4) {
+        return EXCP_UDEF;
+    }
+    env->cp15.c6_region[ri->crm + 4] = value;
+    return 0;
+}
+
+static const ARMCPRegInfo faraday_pmsav5_cp_reginfo[] = {
+    /* Data region access permission */
+    { .name = "DATA_AP", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 0,
+      .access = PL1_RW,
+      .fieldoffset = offsetof(CPUARMState, cp15.c5_data), .resetvalue = 0,
+      .readfn = pmsav5_data_ap_read, .writefn = pmsav5_data_ap_write, },
+    /* Instruction region access permission */
+    { .name = "INSN_AP", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 1,
+      .access = PL1_RW,
+      .fieldoffset = offsetof(CPUARMState, cp15.c5_insn), .resetvalue = 0,
+      .readfn = pmsav5_insn_ap_read, .writefn = pmsav5_insn_ap_write, },
+    /* Data region base and size registers */
+    { .name = "DR_CFG", .cp = 15, .crn = 6, .crm = CP_ANY,
+      .opc1 = 0, .opc2 = 0, .access = PL1_RW,
+      .readfn = faraday_pmsav5_drcfg_read,
+      .writefn = faraday_pmsav5_drcfg_write, },
+    /* Instruction region base and size registers */
+    { .name = "IR_CFG", .cp = 15, .crn = 6, .crm = CP_ANY,
+      .opc1 = 0, .opc2 = 1, .access = PL1_RW,
+      .readfn = faraday_pmsav5_ircfg_read,
+      .writefn = faraday_pmsav5_ircfg_write, },
+    REGINFO_SENTINEL
+};
+
+static const ARMCPRegInfo faraday_tcm_cp_reginfo[] = {
+    { .name = "DTCMRR", .cp = 15, .crn = 9, .crm = 1,
+      .opc1 = 0, .opc2 = 0, .access = PL1_RW, .resetvalue = 0x0,
+      .fieldoffset = offsetof(CPUARMState, cp15.c15_tcm_data) },
+    { .name = "ITCMRR", .cp = 15, .crn = 9, .crm = 1,
+      .opc1 = 0, .opc2 = 1, .access = PL1_RW, .resetvalue = 0x0,
+      .fieldoffset = offsetof(CPUARMState, cp15.c15_tcm_inst) },
+    REGINFO_SENTINEL
+};
+
 static int sctlr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
 {
     env->cp15.c1_sys = value;
@@ -1163,6 +1241,12 @@ void register_cp_regs_for_features(ARMCPU *cpu)
     if (arm_feature(env, ARM_FEATURE_LPAE)) {
         define_arm_cp_regs(cpu, lpae_cp_reginfo);
     }
+    if (arm_feature(env, ARM_FEATURE_TCM_FARADAY)) {
+        define_arm_cp_regs(cpu, faraday_tcm_cp_reginfo);
+    }
+    if (arm_feature(env, ARM_FEATURE_MPU_FARADAY)) {
+        define_arm_cp_regs(cpu, faraday_pmsav5_cp_reginfo);
+    }
     /* Slightly awkwardly, the OMAP and StrongARM cores need all of
      * cp15 crn=0 to be writes-ignored, whereas for other cores they should
      * be read-only (ie write causes UNDEF exception).
diff --git a/target-arm/machine.c b/target-arm/machine.c
index 68dca7f..ed63614 100644
--- a/target-arm/machine.c
+++ b/target-arm/machine.c
@@ -62,6 +62,8 @@ void cpu_save(QEMUFile *f, void *opaque)
     qemu_put_be32(f, env->cp15.c15_power_control);
     qemu_put_be32(f, env->cp15.c15_diagnostic);
     qemu_put_be32(f, env->cp15.c15_power_diagnostic);
+    qemu_put_be32(f, env->cp15.c15_tcm_data);
+    qemu_put_be32(f, env->cp15.c15_tcm_inst);
 
     qemu_put_be64(f, env->features);
 
@@ -182,6 +184,8 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id)
     env->cp15.c15_power_control = qemu_get_be32(f);
     env->cp15.c15_diagnostic = qemu_get_be32(f);
     env->cp15.c15_power_diagnostic = qemu_get_be32(f);
+    env->cp15.c15_tcm_data = qemu_get_be32(f);
+    env->cp15.c15_tcm_inst = qemu_get_be32(f);
 
     env->features = qemu_get_be64(f);
 
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH v9 02/24] hw/arm: add Faraday a369 SoC platform support
  2013-03-25 12:09 [Qemu-devel] [PATCH v9 00/24] hw/arm: add Faraday A369 SoC platform support Kuo-Jung Su
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 01/24] target-arm: add Faraday ARMv5TE processors support Kuo-Jung Su
@ 2013-03-25 12:09 ` Kuo-Jung Su
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 03/24] hw/arm: add FTINTC020 interrupt controller support Kuo-Jung Su
                   ` (21 subsequent siblings)
  23 siblings, 0 replies; 35+ messages in thread
From: Kuo-Jung Su @ 2013-03-25 12:09 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, i.mitsyanko, Blue Swirl, Paul Brook, Kuo-Jung Su,
	Andreas, fred.konrad

From: Kuo-Jung Su <dantesu@faraday-tech.com>

The Faraday A369 EVB is a Faraday SoC platform evalution board used for
Faraday IP functional verification based on the well-known ARM AMBA 2.0
architecture.

Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
---
 hw/arm/Makefile.objs    |    2 +
 hw/arm/ftplat_a369.c    |   96 +++++++++++++++++++
 hw/arm/ftplat_a369kpd.c |  235 +++++++++++++++++++++++++++++++++++++++++++++++
 hw/arm/ftplat_a369scu.c |  186 +++++++++++++++++++++++++++++++++++++
 hw/arm/ftplat_a369soc.c |  155 +++++++++++++++++++++++++++++++
 hw/faraday.h            |  124 +++++++++++++++++++++++++
 hw/ftkbc010.h           |   44 +++++++++
 7 files changed, 842 insertions(+)
 create mode 100644 hw/arm/ftplat_a369.c
 create mode 100644 hw/arm/ftplat_a369kpd.c
 create mode 100644 hw/arm/ftplat_a369scu.c
 create mode 100644 hw/arm/ftplat_a369soc.c
 create mode 100644 hw/faraday.h
 create mode 100644 hw/ftkbc010.h

diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index f5f7d0e..09217c6 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -34,3 +34,5 @@ obj-y += tosa.o versatilepb.o vexpress.o xilinx_zynq.o z2.o
 
 obj-y += armv7m.o exynos4210.o pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o
 obj-y += omap1.o omap2.o
+
+obj-y += ftplat_a369.o ftplat_a369soc.o ftplat_a369scu.o ftplat_a369kpd.o
diff --git a/hw/arm/ftplat_a369.c b/hw/arm/ftplat_a369.c
new file mode 100644
index 0000000..6f00c82
--- /dev/null
+++ b/hw/arm/ftplat_a369.c
@@ -0,0 +1,96 @@
+/*
+ * Faraday A369 Evalution Board
+ *
+ * Copyright (c) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This code is licensed under GNU GPL v2+.
+ */
+
+#include "hw/sysbus.h"
+#include "hw/arm-misc.h"
+#include "hw/devices.h"
+#include "hw/i2c.h"
+#include "hw/boards.h"
+#include "hw/ssi.h"
+#include "net/net.h"
+#include "sysemu/sysemu.h"
+#include "exec/address-spaces.h"
+
+#include "hw/faraday.h"
+
+/* Board init.  */
+
+static void a369_board_init(QEMUMachineInitArgs *args)
+{
+    ARMCPU *cpu;
+    DeviceState *ds;
+    FaradaySoCState *s;
+
+    if (!args->cpu_model) {
+        args->cpu_model = "fa626te";
+    }
+
+    if (!args->ram_size) {
+        args->ram_size = 512 << 20;
+    }
+
+    /* CPU */
+    cpu = cpu_arm_init(args->cpu_model);
+    if (!cpu) {
+        fprintf(stderr, "a369: Unable to find CPU definition\n");
+        abort();
+    }
+
+    /* SoC */
+    ds = qdev_create(NULL, TYPE_FARADAY_SOC);
+    s = FARADAY_SOC(ds);
+    /* Setup QOM path for the SoC object (i.e. /machine/faraday.soc) */
+    object_property_add_child(qdev_get_machine(),
+                              TYPE_FARADAY_SOC,
+                              OBJECT(ds),
+                              NULL);
+    s->cpu_pic = arm_pic_init_cpu(cpu);
+    s->as  = get_system_memory();
+    s->ram = g_new0(MemoryRegion, 1);
+    memory_region_init_ram(s->ram, TYPE_FARADAY_SOC ".ram", args->ram_size);
+    vmstate_register_ram_global(s->ram);
+    qdev_init_nofail(ds);
+
+    /* System start-up */
+
+    if (args->kernel_filename) {
+        struct arm_boot_info *bi = g_new0(struct arm_boot_info, 1);
+
+        s->ram_boot = true;
+
+        faraday_soc_ram_setup(s, true);
+
+        faraday_soc_ahb_remap(s, true);
+
+        /* Boot Info */
+        bi->ram_size = args->ram_size;
+        bi->kernel_filename = args->kernel_filename;
+        bi->kernel_cmdline = args->kernel_cmdline;
+        bi->initrd_filename = args->initrd_filename;
+        bi->board_id = 0x3369;
+        arm_load_kernel(cpu, bi);
+    } else if (!drive_get(IF_PFLASH, 0, 0)) {
+        fprintf(stderr, "a369: Unable to load ROM image!\n");
+        abort();
+    }
+}
+
+static QEMUMachine a369_machine = {
+    .name = "a369",
+    .desc = "Faraday A369 (fa626te)",
+    .init = a369_board_init,
+    DEFAULT_MACHINE_OPTIONS,
+};
+
+static void a369_machine_init(void)
+{
+    qemu_register_machine(&a369_machine);
+}
+
+machine_init(a369_machine_init);
diff --git a/hw/arm/ftplat_a369kpd.c b/hw/arm/ftplat_a369kpd.c
new file mode 100644
index 0000000..6d42dfa
--- /dev/null
+++ b/hw/arm/ftplat_a369kpd.c
@@ -0,0 +1,235 @@
+/*
+ * Faraday FTKBC010 emulator for A369.
+ *
+ * Copyright (c) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * In A369 EVB, the FTKBC010 is configured as a keypad controller.
+ * It acts like a group of hard wired buttons on the board, each of them
+ * is monitored by the FTKBC010, and coordinated as (x, y).
+ * However there is a pinmux issue in A369 EVB, the Y-axis usually
+ * malfunctioned, so there are only 3 button emulated here.
+ *
+ * This code is licensed under GNU GPL v2+
+ */
+
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "hw/devices.h"
+#include "ui/console.h"
+#include "sysemu/sysemu.h"
+
+#include "hw/ftkbc010.h"
+
+#define CFG_REGSIZE     (0x3c / 4)
+
+/* Key codes */
+#define KEYCODE_ESC             1
+#define KEYCODE_BACKSPACE       14
+#define KEYCODE_ENTER           28
+#define KEYCODE_SPACE           57
+#define KEYCODE_MENU            139    /* Menu (show menu) */
+
+#define TYPE_A369KPD            "a369-kpd"
+
+typedef struct A369KPDState {
+    /*< private >*/
+    SysBusDevice parent_obj;
+
+    /*< public >*/
+    MemoryRegion iomem;
+    qemu_irq irq;
+
+    /* HW registers */
+    uint32_t regs[CFG_REGSIZE];
+} A369KPDState;
+
+#define A369KPD(obj) \
+    OBJECT_CHECK(A369KPDState, obj, TYPE_A369KPD)
+
+#define KBC_REG32(s, off) \
+    ((s)->regs[(off) / 4])
+
+static void a369kpd_update_irq(A369KPDState *s)
+{
+    uint32_t ier = 0;
+
+    /* keypad interrupt */
+    ier |= (KBC_REG32(s, REG_CR) & CR_KPDEN) ? ISR_KPDI : 0;
+    /* tx interrupt */
+    ier |= (KBC_REG32(s, REG_CR) & CR_TXIEN) ? ISR_TXI : 0;
+    /* rx interrupt */
+    ier |= (KBC_REG32(s, REG_CR) & CR_RXIEN) ? ISR_RXI : 0;
+
+    qemu_set_irq(s->irq, (ier & KBC_REG32(s, REG_ISR)) ? 1 : 0);
+}
+
+static uint64_t
+a369kpd_mem_read(void *opaque, hwaddr addr, unsigned size)
+{
+    A369KPDState *s = A369KPD(opaque);
+    uint64_t ret = 0;
+
+    switch (addr) {
+    case REG_CR ... REG_ASPR:
+        ret = s->regs[addr / 4];
+        break;
+    case REG_REVR:
+        ret = 0x00010403;  /* rev. = 1.4.3 */
+        break;
+    case REG_FEAR:
+        ret = 0x00000808;  /* 8x8 scan code for keypad */
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "a369kpd: undefined memory access@%#" HWADDR_PRIx "\n", addr);
+        break;
+    }
+
+    return ret;
+}
+
+static void
+a369kpd_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+    A369KPDState *s = A369KPD(opaque);
+
+    switch (addr) {
+    case REG_CR:
+        KBC_REG32(s, REG_CR) = (uint32_t)val;
+        /* if ftkbc010 enabled */
+        if (!(KBC_REG32(s, REG_CR) & CR_EN)) {
+            break;
+        }
+        /* if keypad interrupt cleared */
+        if (KBC_REG32(s, REG_CR) & CR_KPDIC) {
+            KBC_REG32(s, REG_CR) &= ~CR_KPDIC;
+            KBC_REG32(s, REG_ISR) &= ~ISR_KPDI;
+        }
+        /* if rx interrupt cleared */
+        if (KBC_REG32(s, REG_CR) & CR_RXICLR) {
+            KBC_REG32(s, REG_CR) &= ~CR_RXICLR;
+            KBC_REG32(s, REG_ISR) &= ~ISR_RXI;
+        }
+        /* if tx interrupt cleared */
+        if (KBC_REG32(s, REG_CR) & CR_TXICLR) {
+            KBC_REG32(s, REG_CR) &= ~CR_TXICLR;
+            KBC_REG32(s, REG_ISR) &= ~ISR_TXI;
+        }
+        a369kpd_update_irq(s);
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "a369kpd: undefined memory access@%#" HWADDR_PRIx "\n", addr);
+        break;
+    }
+}
+
+static const MemoryRegionOps mmio_ops = {
+    .read  = a369kpd_mem_read,
+    .write = a369kpd_mem_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    }
+};
+
+static void a369kpd_key_event(void *opaque, int scancode)
+{
+    A369KPDState *s = A369KPD(opaque);
+    int x, y, released = 0;
+
+    /* key release from qemu */
+    if (scancode & 0x80) {
+        released = 1;
+    }
+
+    /* strip qemu key release bit */
+    scancode &= ~0x80;
+
+    /* keypad interrupt */
+    if (!released && (KBC_REG32(s, REG_CR) & CR_KPDEN)) {
+        switch (scancode) {
+        case KEYCODE_ESC:
+        case KEYCODE_BACKSPACE:
+            x = 1;
+            break;
+        case KEYCODE_ENTER:
+        case KEYCODE_MENU:
+        case KEYCODE_SPACE:
+            x = 3;
+            break;
+        default:
+            x = 2;    /* KEY_HOME */
+            break;
+        }
+        y = 0;
+        KBC_REG32(s, REG_KPDXR) = ~BIT(x);
+        KBC_REG32(s, REG_KPDYR) = ~BIT(y);
+        KBC_REG32(s, REG_ISR)  |= ISR_KPDI;
+        a369kpd_update_irq(s);
+    }
+}
+
+static void a369kpd_reset(DeviceState *ds)
+{
+    A369KPDState *s = A369KPD(SYS_BUS_DEVICE(ds));
+
+    memset(s->regs, 0, sizeof(s->regs));
+    KBC_REG32(s, REG_KPDXR) = 0xffffffff;
+    KBC_REG32(s, REG_KPDYR) = 0xffffffff;
+
+    qemu_irq_lower(s->irq);
+}
+
+static void a369kpd_realize(DeviceState *dev, Error **errp)
+{
+    A369KPDState *s = A369KPD(dev);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+
+    memory_region_init_io(&s->iomem,
+                          &mmio_ops,
+                          s,
+                          TYPE_A369KPD,
+                          0x1000);
+    sysbus_init_mmio(sbd, &s->iomem);
+    sysbus_init_irq(sbd, &s->irq);
+
+    qemu_add_kbd_event_handler(a369kpd_key_event, s);
+}
+
+static const VMStateDescription vmstate_ftkbc010 = {
+    .name = TYPE_A369KPD,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(regs, A369KPDState, CFG_REGSIZE),
+        VMSTATE_END_OF_LIST(),
+    }
+};
+
+static void a369kpd_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->desc  = TYPE_A369KPD;
+    dc->vmsd  = &vmstate_ftkbc010;
+    dc->reset = a369kpd_reset;
+    dc->realize = a369kpd_realize;
+}
+
+static const TypeInfo a369kpd_info = {
+    .name          = TYPE_A369KPD,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(A369KPDState),
+    .class_init    = a369kpd_class_init,
+};
+
+static void a369kpd_register_types(void)
+{
+    type_register_static(&a369kpd_info);
+}
+
+type_init(a369kpd_register_types)
diff --git a/hw/arm/ftplat_a369scu.c b/hw/arm/ftplat_a369scu.c
new file mode 100644
index 0000000..de2e2be
--- /dev/null
+++ b/hw/arm/ftplat_a369scu.c
@@ -0,0 +1,186 @@
+/*
+ * Faraday A369 SCU
+ *
+ * Copyright (c) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * The system control unit (SCU) is responsible for
+ * power, clock and pinmux management. Since most of
+ * the features are useless to QEMU, only partial clock
+ * and pinmux management are implemented as a set of R/W values.
+ *
+ * This code is licensed under GNU GPL v2+
+ */
+
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "hw/devices.h"
+#include "sysemu/sysemu.h"
+
+#define REG_CHIPID      0x000   /* SoC chip id */
+#define REG_REVISON     0x004   /* SCU revision id */
+#define REG_HWCFG       0x008   /* HW configuration strap */
+#define REG_CPUMFCR     0x00C   /* CPUM (master) freq. control */
+#define REG_SCUCR       0x010   /* SCU control register */
+#define REG_SCUSR       0x014   /* SCU status register */
+#define REG_OSCCR       0x01C   /* OSC control register */
+#define REG_PLL1CR      0x020   /* PLL1 control register */
+#define REG_DLLCR       0x024   /* DLL control register */
+#define REG_SPR(n)      (0x100 + ((n) << 2)) /* Scratchpad register 0 - 15 */
+#define REG_GPINMUX     0x200   /* General PINMUX */
+#define REG_EXTHWCFG    0x204   /* Extended HW configuration strap */
+#define REG_CLKCFG0     0x228   /* Clock configuration 0 */
+#define REG_CLKCFG1     0x22C   /* Clock configuration 1 */
+#define REG_SCER        0x230   /* Special clock enable register */
+#define REG_MFPINMUX0   0x238   /* Multi-function pinmux 0 */
+#define REG_MFPINMUX1   0x23C   /* Multi-function pinmux 1 */
+#define REG_DCSRCR0     0x240   /* Driving cap. & Slew rate control 0 */
+#define REG_DCSRCR1     0x244   /* Driving cap. & Slew rate control 1 */
+#define REG_DCCR        0x254   /* Delay chain control register */
+#define REG_PCR         0x258   /* Power control register */
+
+#define TYPE_A369SCU    "a369-scu"
+#define CFG_REGSIZE     (0x260 / 4)
+
+typedef struct A369SCUState {
+    /*< private >*/
+    SysBusDevice parent_obj;
+
+    /*< public >*/
+    MemoryRegion iomem;
+
+    /* HW registers */
+    uint32_t regs[CFG_REGSIZE];
+} A369SCUState;
+
+#define A369SCU(obj) \
+    OBJECT_CHECK(A369SCUState, obj, TYPE_A369SCU)
+
+#define SCU_REG32(s, off) \
+    ((s)->regs[(off) / 4])
+
+static uint64_t
+a369scu_mem_read(void *opaque, hwaddr addr, unsigned size)
+{
+    A369SCUState *s = A369SCU(opaque);
+    uint64_t ret = 0;
+
+    switch (addr) {
+    case 0x000 ... (CFG_REGSIZE - 1) * 4:
+        ret = s->regs[addr / 4];
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "a369scu: undefined memory access@%#" HWADDR_PRIx "\n", addr);
+        break;
+    }
+
+    return ret;
+}
+
+static void
+a369scu_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+    A369SCUState *s = A369SCU(opaque);
+
+    switch (addr) {
+    case REG_GPINMUX:
+    case REG_CLKCFG0:
+    case REG_CLKCFG1:
+    case REG_MFPINMUX0:
+    case REG_MFPINMUX1:
+        s->regs[addr / 4] = (uint32_t)val;
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "a369scu: undefined memory access@%#" HWADDR_PRIx "\n", addr);
+        break;
+    }
+}
+
+static const MemoryRegionOps mmio_ops = {
+    .read  = a369scu_mem_read,
+    .write = a369scu_mem_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    }
+};
+
+static void a369scu_reset(DeviceState *ds)
+{
+    A369SCUState *s = A369SCU(SYS_BUS_DEVICE(ds));
+
+    memset(s->regs, 0, sizeof(s->regs));
+
+    SCU_REG32(s, REG_CHIPID)    = 0x00003369; /* A369 */
+    SCU_REG32(s, REG_REVISON)   = 0x00010000; /* Rev. = 1.0.0 */
+    SCU_REG32(s, REG_HWCFG)     = 0x00000c10; /* CPU = 4 * HCLK */
+    SCU_REG32(s, REG_CPUMFCR)   = 0x00000230; /* CPU = 4 * HCLK */
+    SCU_REG32(s, REG_SCUCR)     = 0x00000083; /* no low power detect */
+    SCU_REG32(s, REG_SCUSR)     = 0x00000100; /* CPU freq. stable */
+    SCU_REG32(s, REG_OSCCR)     = 0x00000003; /* OSCH disabled */
+    SCU_REG32(s, REG_PLL1CR)    = 0x20010003; /* PLL_NS = 32 */
+    SCU_REG32(s, REG_DLLCR)     = 0x00000003; /* DLL enabled & stable */
+    SCU_REG32(s, REG_GPINMUX)   = 0x00001078; /* Pinmux */
+    SCU_REG32(s, REG_EXTHWCFG)  = 0x00001cc8; /* NAND flash boot */
+    SCU_REG32(s, REG_CLKCFG0)   = 0x26877330; /* LCD = HCLK */
+    SCU_REG32(s, REG_CLKCFG1)   = 0x000a0a0a; /* SD = HCLK, SPI=PCLK */
+    SCU_REG32(s, REG_SCER)      = 0x00003fff; /* All clock enabled */
+    SCU_REG32(s, REG_MFPINMUX0) = 0x00000241; /* Pinmux */
+    SCU_REG32(s, REG_MFPINMUX1) = 0x00000000; /* Pinmux */
+    SCU_REG32(s, REG_DCSRCR0)   = 0x11111111; /* Slow slew rate */
+    SCU_REG32(s, REG_DCSRCR1)   = 0x11111111; /* Slow slew rate */
+    SCU_REG32(s, REG_DCCR)      = 0x00000303; /* All delay chain = 3 */
+    SCU_REG32(s, REG_PCR)       = 0x8000007f; /* High performance mode */
+}
+
+static void a369scu_realize(DeviceState *dev, Error **errp)
+{
+    A369SCUState *s = A369SCU(dev);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+
+    memory_region_init_io(&s->iomem,
+                          &mmio_ops,
+                          s,
+                          TYPE_A369SCU,
+                          0x1000);
+    sysbus_init_mmio(sbd, &s->iomem);
+}
+
+static const VMStateDescription vmstate_a369scu = {
+    .name = TYPE_A369SCU,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(regs, A369SCUState, CFG_REGSIZE),
+        VMSTATE_END_OF_LIST(),
+    }
+};
+
+static void a369scu_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->desc  = TYPE_A369SCU;
+    dc->vmsd  = &vmstate_a369scu;
+    dc->reset = a369scu_reset;
+    dc->realize = a369scu_realize;
+    dc->no_user = 1;
+}
+
+static const TypeInfo a369scu_info = {
+    .name          = TYPE_A369SCU,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(A369SCUState),
+    .class_init    = a369scu_class_init,
+};
+
+static void a369scu_register_types(void)
+{
+    type_register_static(&a369scu_info);
+}
+
+type_init(a369scu_register_types)
diff --git a/hw/arm/ftplat_a369soc.c b/hw/arm/ftplat_a369soc.c
new file mode 100644
index 0000000..624b549
--- /dev/null
+++ b/hw/arm/ftplat_a369soc.c
@@ -0,0 +1,155 @@
+/*
+ * Faraday A369 SoC
+ *
+ * Copyright (c) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This code is licensed under GNU GPL v2+.
+ */
+
+#include "hw/sysbus.h"
+#include "hw/arm-misc.h"
+#include "hw/devices.h"
+#include "hw/i2c.h"
+#include "hw/boards.h"
+#include "hw/flash.h"
+#include "hw/serial.h"
+#include "hw/ssi.h"
+#include "net/net.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/blockdev.h"
+
+#include "hw/faraday.h"
+
+static void a369soc_reset(DeviceState *ds)
+{
+    int i;
+    FaradaySoCState *s = FARADAY_SOC(SYS_BUS_DEVICE(ds));
+
+    /* AHB slave base & window configuration */
+    memset(s->ahb_slave, 0, sizeof(s->ahb_slave));
+    s->ahb_slave[0] = 0x94050000;
+    s->ahb_slave[1] = 0x96040000;
+    s->ahb_slave[2] = 0x90f00000;
+    s->ahb_slave[3] = 0x92050000; /* APB: base=0x92000000, size=32MB */
+    s->ahb_slave[5] = 0xc0080000;
+    s->ahb_slave[4] = 0x00080000; /* ROM: base=0x00000000, size=256MB */
+    s->ahb_slave[6] = 0x10090000; /* RAM: base=0x10000000, size=512MB */
+    for (i = 0; i < 15; ++i) {
+        s->ahb_slave[7 + i] = 0x90000000 + (i << 20);
+    }
+    s->ahb_slave[22] = 0x40080000;
+    s->ahb_slave[23] = 0x60080000;
+    s->ahb_slave[24] = 0xa0000000; /* SRAM: base=0xA0000000, size=1MB */
+
+    /* APB slave base & window configuration */
+    memset(s->apb_slave, 0, sizeof(s->apb_slave));
+    for (i = 0; i < 18; ++i) {
+        s->apb_slave[i] = 0x12000000 + (i << 20);
+    }
+}
+
+static void a369soc_chip_init(FaradaySoCState *s)
+{
+    DriveInfo *dinfo;
+
+    /* Remappable Memory Region Init */
+    s->rmr = g_new0(MemoryRegion, 1);
+    memory_region_init(s->rmr, "a369soc.rmr", 0x80000000);
+    memory_region_add_subregion(s->as, 0x00000000, s->rmr);
+
+    /* Embedded RAM Init */
+    s->sram = g_new0(MemoryRegion, 1);
+    memory_region_init_ram(s->sram, "a369soc.sram", 0x4000);
+    vmstate_register_ram_global(s->sram);
+    memory_region_add_subregion(s->as, 0xA0000000, s->sram);
+
+    /* Embedded ROM Init (Emulated with a parallel NOR flash) */
+    dinfo = drive_get_next(IF_PFLASH);
+    s->rom = pflash_cfi01_register(
+                    (hwaddr)-1,
+                    NULL,
+                    "a369soc.rom",
+                    6 << 10,
+                    dinfo ? dinfo->bdrv : NULL,
+                    1024,               /* 1 KB sector */
+                    6,                  /* sectors per chip */
+                    4,                  /* 32 bits */
+                    0, 0, 0, 0,         /* id */
+                    0                   /* Little Endian */);
+    if (!s->rom) {
+        fprintf(stderr, "a369soc: Unable to init ROM device.\n");
+        abort();
+    }
+    memory_region_add_subregion(s->rmr, s->rom_base,
+                sysbus_mmio_get_region(SYS_BUS_DEVICE(s->rom), 0));
+
+    /* Serial (FTUART010 which is 16550A compatible) */
+    if (serial_hds[0]) {
+        serial_mm_init(s->as,
+                       0x92b00000,
+                       2,
+                       s->pic[53],
+                       18432000,
+                       serial_hds[0],
+                       DEVICE_LITTLE_ENDIAN);
+    }
+    if (serial_hds[1]) {
+        serial_mm_init(s->as,
+                       0x92c00000,
+                       2,
+                       s->pic[54],
+                       18432000,
+                       serial_hds[1],
+                       DEVICE_LITTLE_ENDIAN);
+    }
+
+    /* ftscu010 */
+    sysbus_create_simple("a369-scu", 0x92000000, NULL);
+
+    /* ftkbc010 */
+    sysbus_create_simple("a369-kpd", 0x92f00000, s->pic[21]);
+}
+
+static void a369soc_realize(DeviceState *dev, Error **errp)
+{
+    FaradaySoCState *s = FARADAY_SOC(dev);
+
+    a369soc_reset(dev);
+    a369soc_chip_init(s);
+}
+
+static const VMStateDescription vmstate_a369soc = {
+    .name = TYPE_FARADAY_SOC,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_END_OF_LIST(),
+    }
+};
+
+static void a369soc_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->desc  = TYPE_FARADAY_SOC;
+    dc->vmsd  = &vmstate_a369soc;
+    dc->reset = a369soc_reset;
+    dc->realize = a369soc_realize;
+    dc->no_user = 1;
+}
+
+static const TypeInfo a369soc_info = {
+    .name          = TYPE_FARADAY_SOC,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(FaradaySoCState),
+    .class_init    = a369soc_class_init,
+};
+
+static void a369soc_register_types(void)
+{
+    type_register_static(&a369soc_info);
+}
+
+type_init(a369soc_register_types)
diff --git a/hw/faraday.h b/hw/faraday.h
new file mode 100644
index 0000000..7373ba0
--- /dev/null
+++ b/hw/faraday.h
@@ -0,0 +1,124 @@
+/*
+ * Faraday SoC platform support.
+ *
+ * Copyright (c) 2013 Faraday Technology
+ * Written by Kuo-Jung Su <dantesu@gmail.com>
+ *
+ * This code is licensed under the GNU GPL v2.
+ */
+#ifndef HW_ARM_FARADAY_H
+#define HW_ARM_FARADAY_H
+
+#include "hw/flash.h"
+#include "qemu/bitops.h"
+
+typedef struct FaradaySoCState {
+    /*< private >*/
+    SysBusDevice parent;
+
+    /*< public >*/
+    bool         ram_boot;
+    uint32_t     ahb_slave[32];
+    uint32_t     apb_slave[32];
+
+    hwaddr       rom_base;
+    hwaddr       ram_base;
+    qemu_irq     *cpu_pic;
+    qemu_irq     *pic;
+    DeviceState  *hdma[2];  /* AHB DMA */
+    DeviceState  *pdma[1];  /* APB DMA */
+    DeviceState  *spi[2];   /* Generic SPI bus */
+    DeviceState  *spi_fl[2];/* Dedicated SPI bus for flash memory */
+    DeviceState  *i2c[2];   /* Generic I2C bus */
+    DeviceState  *i2s[2];   /* Generic I2S bus */
+    DeviceState  *nandc[2]; /* NAND flash controller */
+
+    MemoryRegion *as;       /* address space */
+    MemoryRegion *ram;      /* external sdram */
+    pflash_t     *rom;      /* on-chip rom */
+    MemoryRegion *sram;     /* on-chip static ram */
+
+    MemoryRegion *rmr;      /* remappable memory region */
+    MemoryRegion *ram_visible;  /* ram alias controlled by DDR controller */
+} FaradaySoCState;
+
+/* SoC common APIs */
+#define TYPE_FARADAY_SOC    "faraday-soc"
+#define FARADAY_SOC(obj) \
+    OBJECT_CHECK(FaradaySoCState, obj, TYPE_FARADAY_SOC)
+
+static inline void faraday_soc_ram_setup(FaradaySoCState *s, bool visible)
+{
+    uint64_t size, ram_size = 0;
+
+    if (!s->as || !s->ram) {
+        return;
+    }
+
+    /* RAM size <= (1 << slave6.BIT[19-16]) MB */
+    size = (1 << extract32(s->ahb_slave[6], 16, 4)) << 20;
+    if (!ram_size || ram_size > size) {
+        ram_size = size;
+    }
+
+    if (visible) {
+        if (!s->ram_visible) {
+            s->ram_visible = g_new0(MemoryRegion, 1);
+            memory_region_init_alias(s->ram_visible,
+                                     TYPE_FARADAY_SOC ".ram_visible",
+                                     s->ram,
+                                     0,
+                                     ram_size);
+        }
+        memory_region_add_subregion(s->rmr, s->ram_base, s->ram_visible);
+    } else {
+        if (!s->ram_boot && s->ram_visible) {
+            memory_region_del_subregion(s->rmr, s->ram_visible);
+            g_free(s->ram_visible);
+            s->ram_visible = NULL;
+        }
+    }
+}
+
+/* Remap AHB slave 4 (ROM) & slave 6 (RAM) */
+static inline void faraday_soc_ahb_remap(FaradaySoCState *s, bool active)
+{
+    if (!s->ahb_slave[4] || !s->ahb_slave[6]) {
+        fprintf(stderr,
+                "faraday_soc_ahb_remap: "
+                "AHB slave 4 or 6 is not yet initialized!\n");
+        abort();
+    }
+
+    if (!active && s->ram_boot) {
+        return;
+    }
+
+    if (active) {
+        /* Remap RAM to base of ROM */
+        s->ram_base = s->ahb_slave[4] & 0xfff00000;
+        /* Remap ROM to base of ROM + size of RAM */
+        s->rom_base = s->ram_base
+                    + ((1 << extract32(s->ahb_slave[6], 16, 4)) << 20);
+    } else {
+        s->rom_base = s->ahb_slave[4] & 0xfff00000;
+        s->ram_base = s->ahb_slave[6] & 0xfff00000;
+    }
+
+    /* Update ROM/RAM Address */
+    if (s->as && s->ram_visible) {
+        memory_region_del_subregion(s->rmr, s->ram_visible);
+    }
+
+    if (s->rom) {
+        MemoryRegion *mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(s->rom), 0);
+        memory_region_del_subregion(s->rmr, mr);
+        memory_region_add_subregion(s->rmr, s->rom_base, mr);
+    }
+
+    if (s->as && s->ram_visible) {
+        memory_region_add_subregion(s->rmr, s->ram_base, s->ram_visible);
+    }
+}
+
+#endif
diff --git a/hw/ftkbc010.h b/hw/ftkbc010.h
new file mode 100644
index 0000000..43a77ad
--- /dev/null
+++ b/hw/ftkbc010.h
@@ -0,0 +1,44 @@
+/*
+ * Faraday FTKBC010 Keyboard/Keypad Controller
+ *
+ * Copyright (c) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This code is licensed under GNU GPL v2+
+ */
+#ifndef HW_ARM_FTKBC010_H
+#define HW_ARM_FTKBC010_H
+
+#include "qemu/bitops.h"
+
+#define REG_CR      0x00    /* control register */
+#define REG_SRDR    0x04    /* sample rate division register */
+#define REG_RSCR    0x08    /* request to send counter register */
+#define REG_SR      0x0C    /* status register */
+#define REG_ISR     0x10    /* interrupt status register */
+#define REG_KBDRR   0x14    /* keyboard receive register */
+#define REG_KBDTR   0x18    /* keyboard transmit register */
+#define REG_IMR     0x1C    /* interrupt mask register */
+#define REG_KPDXR   0x30    /* keypad X-Axis register */
+#define REG_KPDYR   0x34    /* keypad Y-Axis register */
+#define REG_ASPR    0x38    /* auto-scan period register */
+#define REG_REVR    0x50    /* revision register */
+#define REG_FEAR    0x54    /* feature register */
+
+#define CR_KPDIC    BIT(10) /* Write 1 to clear Keypad interupt */
+#define CR_KPDAS    BIT(9)  /* Keypad audo-scan enabled */
+#define CR_KPDEN    BIT(8)  /* Keypad function enabled */
+#define CR_RXICLR   BIT(7)  /* Write 1 to clear Keyboard/Mouse Rx interrupt */
+#define CR_TXICLR   BIT(6)  /* Write 1 to clear Keyboard/Mouse Tx interrupt */
+#define CR_NOLC     BIT(5)  /* No line control bit */
+#define CR_RXIEN    BIT(4)  /* Keyboard/Mouse Rx interrupt enabled */
+#define CR_TXIEN    BIT(3)  /* Keyboard/Mouse Tx interrupt enabled */
+#define CR_EN       BIT(2)  /* Chip enabled */
+#define CR_DATDN    BIT(1)  /* Data disabled */
+#define CR_CLKDN    BIT(0)  /* Clock disabled */
+
+#define ISR_KPDI    BIT(2)  /* Keypad interupt */
+#define ISR_TXI     BIT(1)  /* Keyboard/Mouse Tx interrupt enabled */
+#define ISR_RXI     BIT(0)  /* Keyboard/Mouse Rx interrupt enabled */
+
+#endif
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH v9 03/24] hw/arm: add FTINTC020 interrupt controller support
  2013-03-25 12:09 [Qemu-devel] [PATCH v9 00/24] hw/arm: add Faraday A369 SoC platform support Kuo-Jung Su
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 01/24] target-arm: add Faraday ARMv5TE processors support Kuo-Jung Su
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 02/24] hw/arm: add Faraday a369 SoC platform support Kuo-Jung Su
@ 2013-03-25 12:09 ` Kuo-Jung Su
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 04/24] hw/arm: add FTAHBC020 AHB " Kuo-Jung Su
                   ` (20 subsequent siblings)
  23 siblings, 0 replies; 35+ messages in thread
From: Kuo-Jung Su @ 2013-03-25 12:09 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, i.mitsyanko, Blue Swirl, Paul Brook, Kuo-Jung Su,
	Andreas, fred.konrad

From: Kuo-Jung Su <dantesu@faraday-tech.com>

The FTINTC020 interrupt controller supports both FIQ and IRQ signals
to the microprocessor.
It can handle up to 64 configurable IRQ sources and 64 FIQ sources.
The output signals to the microprocessor can be configured as
level-high/low active or edge-rising/falling triggered.

Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
---
 hw/arm/Makefile.objs    |    1 +
 hw/arm/ftplat_a369soc.c |   11 ++
 hw/ftintc020.c          |  307 +++++++++++++++++++++++++++++++++++++++++++++++
 hw/ftintc020.h          |   55 +++++++++
 4 files changed, 374 insertions(+)
 create mode 100644 hw/ftintc020.c
 create mode 100644 hw/ftintc020.h

diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 09217c6..7cdd831 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -24,6 +24,7 @@ obj-y += framebuffer.o
 obj-y += strongarm.o
 obj-y += imx_serial.o imx_ccm.o imx_timer.o imx_avic.o
 obj-$(CONFIG_KVM) += kvm/arm_gic.o
+obj-y += ftintc020.o
 
 obj-y := $(addprefix ../,$(obj-y))
 
diff --git a/hw/arm/ftplat_a369soc.c b/hw/arm/ftplat_a369soc.c
index 624b549..331ec2a 100644
--- a/hw/arm/ftplat_a369soc.c
+++ b/hw/arm/ftplat_a369soc.c
@@ -51,6 +51,8 @@ static void a369soc_reset(DeviceState *ds)
 
 static void a369soc_chip_init(FaradaySoCState *s)
 {
+    int i;
+    DeviceState *ds;
     DriveInfo *dinfo;
 
     /* Remappable Memory Region Init */
@@ -84,6 +86,15 @@ static void a369soc_chip_init(FaradaySoCState *s)
     memory_region_add_subregion(s->rmr, s->rom_base,
                 sysbus_mmio_get_region(SYS_BUS_DEVICE(s->rom), 0));
 
+    /* Interrupt Controller */
+    ds = sysbus_create_varargs("ftintc020", 0x90100000,
+                               s->cpu_pic[ARM_PIC_CPU_IRQ],
+                               s->cpu_pic[ARM_PIC_CPU_FIQ], NULL);
+    s->pic = g_new0(qemu_irq, 64);
+    for (i = 0; i < 64; ++i) {
+        s->pic[i] = qdev_get_gpio_in(ds, i);
+    }
+
     /* Serial (FTUART010 which is 16550A compatible) */
     if (serial_hds[0]) {
         serial_mm_init(s->as,
diff --git a/hw/ftintc020.c b/hw/ftintc020.c
new file mode 100644
index 0000000..9aaff1d
--- /dev/null
+++ b/hw/ftintc020.c
@@ -0,0 +1,307 @@
+/*
+ * Faraday FTINTC020 Programmable Interrupt Controller.
+ *
+ * Copyright (c) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This code is licensed under GNU GPL v2+.
+ */
+
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+
+#include "qemu/bitops.h"
+#include "hw/ftintc020.h"
+
+#define TYPE_FTINTC020  "ftintc020"
+
+#define CFG_REGSIZE     (0x100 / 4)
+
+typedef struct Ftintc020State {
+    /*< private >*/
+    SysBusDevice parent;
+
+    /*< public >*/
+    MemoryRegion iomem;
+
+    qemu_irq irq;
+    qemu_irq fiq;
+
+    uint32_t irq_ps[2]; /* IRQ pin state */
+    uint32_t fiq_ps[2]; /* FIQ pin state */
+
+    /* HW register caches */
+    uint32_t regs[CFG_REGSIZE];
+} Ftintc020State;
+
+#define FTINTC020(obj) \
+    OBJECT_CHECK(Ftintc020State, obj, TYPE_FTINTC020)
+
+#define PIC_REG32(s, off) \
+    ((s)->regs[(off) / 4])
+
+#define IRQ_REG32(s, n, off) \
+    ((s)->regs[(REG_IRQ(n) + ((off) & REG_MASK)) / 4])
+
+#define FIQ_REG32(s, n, off) \
+    ((s)->regs[(REG_FIQ(n) + ((off) & REG_MASK)) / 4])
+
+static void
+ftintc020_update_irq(Ftintc020State *s)
+{
+    uint32_t mask[2];
+
+    /* FIQ */
+    mask[0] = PIC_REG32(s, REG_FIQ32SRC) & PIC_REG32(s, REG_FIQ32ENA);
+    mask[1] = PIC_REG32(s, REG_FIQ64SRC) & PIC_REG32(s, REG_FIQ64ENA);
+    qemu_set_irq(s->fiq, !!(mask[0] || mask[1]));
+
+    /* IRQ */
+    mask[0] = PIC_REG32(s, REG_IRQ32SRC) & PIC_REG32(s, REG_IRQ32ENA);
+    mask[1] = PIC_REG32(s, REG_IRQ64SRC) & PIC_REG32(s, REG_IRQ64ENA);
+    qemu_set_irq(s->irq, !!(mask[0] || mask[1]));
+}
+
+/* Note: Here level means state of the signal on a pin */
+static void
+ftintc020_set_irq(void *opaque, int irq, int level)
+{
+    Ftintc020State *s = FTINTC020(opaque);
+    uint32_t i = irq / 32;
+    uint32_t mask = BIT(irq & 0x1f);
+
+    switch (irq) {
+    case 0  ... 63:
+        /* IRQ */
+        if (IRQ_REG32(s, irq, REG_MDR) & mask) {
+            /* Edge Triggered */
+            if (IRQ_REG32(s, irq, REG_LVR) & mask) {
+                /* Falling Active */
+                if ((s->irq_ps[i] & mask) && !level) {
+                    IRQ_REG32(s, irq, REG_SRC) |= mask;
+                }
+            } else {
+                /* Rising Active */
+                if (!(s->irq_ps[i] & mask) && level) {
+                    IRQ_REG32(s, irq, REG_SRC) |= mask;
+                }
+            }
+        } else {
+            /* Level Triggered */
+            if (IRQ_REG32(s, irq, REG_LVR) & mask) {
+                /* Low Active */
+                if (level) {
+                    IRQ_REG32(s, irq, REG_SRC) &= ~mask;
+                } else {
+                    IRQ_REG32(s, irq, REG_SRC) |= mask;
+                }
+            } else {
+                /* High Active */
+                if (level) {
+                    IRQ_REG32(s, irq, REG_SRC) |= mask;
+                } else {
+                    IRQ_REG32(s, irq, REG_SRC) &= ~mask;
+                }
+            }
+        }
+
+        /* FIQ */
+        if (FIQ_REG32(s, irq, REG_MDR) & mask) {
+            /* Edge Triggered */
+            if (FIQ_REG32(s, irq, REG_LVR) & mask) {
+                /* Falling Active */
+                if ((s->fiq_ps[i] & mask) && !level) {
+                    FIQ_REG32(s, irq, REG_SRC) |= mask;
+                }
+            } else {
+                /* Rising Active */
+                if (!(s->fiq_ps[i] & mask) && level) {
+                    FIQ_REG32(s, irq, REG_SRC) |= mask;
+                }
+            }
+        } else {
+            /* Level Triggered */
+            if (FIQ_REG32(s, irq, REG_LVR) & mask) {
+                /* Low Active */
+                if (level) {
+                    FIQ_REG32(s, irq, REG_SRC) &= ~mask;
+                } else {
+                    FIQ_REG32(s, irq, REG_SRC) |= mask;
+                }
+            } else {
+                /* High Active */
+                if (level) {
+                    FIQ_REG32(s, irq, REG_SRC) |= mask;
+                } else {
+                    FIQ_REG32(s, irq, REG_SRC) &= ~mask;
+                }
+            }
+        }
+        /* update IRQ/FIQ pin states */
+        if (level) {
+            s->irq_ps[i] |= mask;
+            s->fiq_ps[i] |= mask;
+        } else {
+            s->irq_ps[i] &= ~mask;
+            s->fiq_ps[i] &= ~mask;
+        }
+        break;
+
+    default:
+        fprintf(stderr, "ftintc020: undefined irq=%d\n", irq);
+        abort();
+    }
+
+    ftintc020_update_irq(s);
+}
+
+static uint64_t
+ftintc020_mem_read(void *opaque, hwaddr addr, unsigned size)
+{
+    Ftintc020State *s = FTINTC020(opaque);
+    uint32_t ret = 0;
+
+    if (addr > REG_FIQ64SR) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "ftintc020: undefined memory access@%#" HWADDR_PRIx "\n", addr);
+        return ret;
+    }
+
+    switch (addr) {
+    case REG_IRQ32SR:
+        ret = PIC_REG32(s, REG_IRQ32SRC) & PIC_REG32(s, REG_IRQ32ENA);
+        break;
+    case REG_IRQ64SR:
+        ret = PIC_REG32(s, REG_IRQ64SRC) & PIC_REG32(s, REG_IRQ64ENA);
+        break;
+    case REG_FIQ32SR:
+        ret = PIC_REG32(s, REG_FIQ32SRC) & PIC_REG32(s, REG_FIQ32ENA);
+        break;
+    case REG_FIQ64SR:
+        ret = PIC_REG32(s, REG_FIQ64SRC) & PIC_REG32(s, REG_FIQ64ENA);
+        break;
+    default:
+        ret = s->regs[addr / 4];
+        break;
+    }
+
+    return ret;
+}
+
+static void
+ftintc020_mem_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
+{
+    Ftintc020State *s = FTINTC020(opaque);
+
+    if (addr > REG_FIQ64SR) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "ftintc020: undefined memory access@%#" HWADDR_PRIx "\n", addr);
+        return;
+    }
+
+    switch (addr) {
+    case REG_IRQ32SCR:
+        /* clear edge triggered interrupts only */
+        value = ~(value & PIC_REG32(s, REG_IRQ32MDR));
+        PIC_REG32(s, REG_IRQ32SRC) &= (uint32_t)value;
+        break;
+    case REG_IRQ64SCR:
+        /* clear edge triggered interrupts only */
+        value = ~(value & PIC_REG32(s, REG_IRQ64MDR));
+        PIC_REG32(s, REG_IRQ64SRC) &= (uint32_t)value;
+        break;
+    case REG_FIQ32SCR:
+        /* clear edge triggered interrupts only */
+        value = ~(value & PIC_REG32(s, REG_FIQ32MDR));
+        PIC_REG32(s, REG_FIQ32SRC) &= (uint32_t)value;
+        break;
+    case REG_FIQ64SCR:
+        /* clear edge triggered interrupts only */
+        value = ~(value & PIC_REG32(s, REG_FIQ64MDR));
+        PIC_REG32(s, REG_FIQ64SRC) &= (uint32_t)value;
+        break;
+    default:
+        s->regs[addr / 4] = (uint32_t)value;
+        break;
+    }
+
+    ftintc020_update_irq(s);
+}
+
+static const MemoryRegionOps mmio_ops = {
+    .read  = ftintc020_mem_read,
+    .write = ftintc020_mem_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    }
+};
+
+static void ftintc020_reset(DeviceState *ds)
+{
+    Ftintc020State *s = FTINTC020(SYS_BUS_DEVICE(ds));
+
+    s->irq_ps[0] = 0;
+    s->irq_ps[1] = 0;
+    s->fiq_ps[0] = 0;
+    s->fiq_ps[1] = 0;
+    memset(s->regs, 0, sizeof(s->regs));
+
+    ftintc020_update_irq(s);
+}
+
+static VMStateDescription vmstate_ftintc020 = {
+    .name = TYPE_FTINTC020,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(regs, Ftintc020State, CFG_REGSIZE),
+        VMSTATE_UINT32_ARRAY(irq_ps, Ftintc020State, 2),
+        VMSTATE_UINT32_ARRAY(fiq_ps, Ftintc020State, 2),
+        VMSTATE_END_OF_LIST(),
+    },
+};
+
+static void ftintc020_realize(DeviceState *dev, Error **errp)
+{
+    Ftintc020State *s = FTINTC020(dev);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+
+    /* Enable IC memory-mapped registers access.  */
+    memory_region_init_io(&s->iomem,
+                          &mmio_ops,
+                          s,
+                          TYPE_FTINTC020,
+                          0x1000);
+    sysbus_init_mmio(sbd, &s->iomem);
+
+    qdev_init_gpio_in(&sbd->qdev, ftintc020_set_irq, 64);
+    sysbus_init_irq(sbd, &s->irq);
+    sysbus_init_irq(sbd, &s->fiq);
+}
+
+static void ftintc020_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->vmsd    = &vmstate_ftintc020;
+    dc->reset   = ftintc020_reset;
+    dc->realize = ftintc020_realize;
+    dc->no_user = 1;
+}
+
+static const TypeInfo ftintc020_info = {
+    .name          = TYPE_FTINTC020,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(Ftintc020State),
+    .class_init    = ftintc020_class_init,
+};
+
+static void ftintc020_register_types(void)
+{
+    type_register_static(&ftintc020_info);
+}
+
+type_init(ftintc020_register_types)
diff --git a/hw/ftintc020.h b/hw/ftintc020.h
new file mode 100644
index 0000000..e354497
--- /dev/null
+++ b/hw/ftintc020.h
@@ -0,0 +1,55 @@
+/*
+ * Faraday FTINTC020 Programmable Interrupt Controller.
+ *
+ * Copyright (c) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This code is licensed under GNU GPL v2+.
+ */
+
+#ifndef HW_ARM_FTINTC020_H
+#define HW_ARM_FTINTC020_H
+
+/* IRQ/FIO: 0 ~ 31 */
+#define REG_IRQ32SRC    0x00    /* IRQ source register */
+#define REG_IRQ32ENA    0x04    /* IRQ enable register */
+#define REG_IRQ32SCR    0x08    /* IRQ status clear register */
+#define REG_IRQ32MDR    0x0C    /* IRQ trigger mode register */
+#define REG_IRQ32LVR    0x10    /* IRQ trigger level register */
+#define REG_IRQ32SR     0x14    /* IRQ status register */
+
+#define REG_FIQ32SRC    0x20    /* FIQ source register */
+#define REG_FIQ32ENA    0x24    /* FIQ enable register */
+#define REG_FIQ32SCR    0x28    /* FIQ status clear register */
+#define REG_FIQ32MDR    0x2C    /* FIQ trigger mode register */
+#define REG_FIQ32LVR    0x30    /* FIQ trigger level register */
+#define REG_FIQ32SR     0x34    /* FIQ status register */
+
+/* Extended IRQ/FIO: 32 ~ 63 */
+#define REG_IRQ64SRC    0x60    /* Extended IRQ source register */
+#define REG_IRQ64ENA    0x64    /* Extended IRQ enable register */
+#define REG_IRQ64SCR    0x68    /* Extended IRQ status clear register */
+#define REG_IRQ64MDR    0x6C    /* Extended IRQ trigger mode register */
+#define REG_IRQ64LVR    0x70    /* Extended IRQ trigger level register */
+#define REG_IRQ64SR     0x74    /* Extended IRQ status register */
+
+#define REG_FIQ64SRC    0x80    /* Extended FIQ source register */
+#define REG_FIQ64ENA    0x84    /* Extended FIQ enable register */
+#define REG_FIQ64SCR    0x88    /* Extended FIQ status clear register */
+#define REG_FIQ64MDR    0x8C    /* Extended FIQ trigger mode register */
+#define REG_FIQ64LVR    0x90    /* Extended FIQ trigger level register */
+#define REG_FIQ64SR     0x94    /* Extended FIQ status register */
+
+/* Common register offset to IRQ/FIQ */
+#define REG_SRC         0x00    /* Source register */
+#define REG_ENA         0x04    /* Enable register */
+#define REG_SCR         0x08    /* Status clear register */
+#define REG_MDR         0x0C    /* Trigger mode register */
+#define REG_LVR         0x10    /* Trigger level register */
+#define REG_SR          0x14    /* Status register */
+#define REG_MASK        0x1f
+
+#define REG_IRQ(n)      (((n) < 32) ? REG_IRQ32SRC : REG_IRQ64SRC)
+#define REG_FIQ(n)      (((n) < 32) ? REG_FIQ32SRC : REG_FIQ64SRC)
+
+#endif
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH v9 04/24] hw/arm: add FTAHBC020 AHB controller support
  2013-03-25 12:09 [Qemu-devel] [PATCH v9 00/24] hw/arm: add Faraday A369 SoC platform support Kuo-Jung Su
                   ` (2 preceding siblings ...)
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 03/24] hw/arm: add FTINTC020 interrupt controller support Kuo-Jung Su
@ 2013-03-25 12:09 ` Kuo-Jung Su
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 05/24] hw/arm: add FTDDRII030 DDRII " Kuo-Jung Su
                   ` (19 subsequent siblings)
  23 siblings, 0 replies; 35+ messages in thread
From: Kuo-Jung Su @ 2013-03-25 12:09 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, i.mitsyanko, Blue Swirl, Paul Brook, Kuo-Jung Su,
	Andreas, fred.konrad

From: Kuo-Jung Su <dantesu@faraday-tech.com>

It's used to manage AHB slave devices
and also the AHB remap function for slave4 & slave6.

Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
---
 hw/arm/Makefile.objs    |    2 +-
 hw/arm/ftplat_a369soc.c |    9 ++
 hw/ftahbc020.c          |  216 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 226 insertions(+), 1 deletion(-)
 create mode 100644 hw/ftahbc020.c

diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 7cdd831..b2fa20f 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -24,7 +24,7 @@ obj-y += framebuffer.o
 obj-y += strongarm.o
 obj-y += imx_serial.o imx_ccm.o imx_timer.o imx_avic.o
 obj-$(CONFIG_KVM) += kvm/arm_gic.o
-obj-y += ftintc020.o
+obj-y += ftintc020.o ftahbc020.o
 
 obj-y := $(addprefix ../,$(obj-y))
 
diff --git a/hw/arm/ftplat_a369soc.c b/hw/arm/ftplat_a369soc.c
index 331ec2a..7f222cb 100644
--- a/hw/arm/ftplat_a369soc.c
+++ b/hw/arm/ftplat_a369soc.c
@@ -54,6 +54,7 @@ static void a369soc_chip_init(FaradaySoCState *s)
     int i;
     DeviceState *ds;
     DriveInfo *dinfo;
+    Error *local_errp = NULL;
 
     /* Remappable Memory Region Init */
     s->rmr = g_new0(MemoryRegion, 1);
@@ -120,6 +121,14 @@ static void a369soc_chip_init(FaradaySoCState *s)
 
     /* ftkbc010 */
     sysbus_create_simple("a369-kpd", 0x92f00000, s->pic[21]);
+
+    /* ftahbc020 */
+    ds = sysbus_create_simple("ftahbc020", 0x94000000, NULL);
+    object_property_set_link(OBJECT(ds), OBJECT(s), "soc", &local_errp);
+    if (local_errp) {
+        fprintf(stderr, "a369soc: Unable to set soc link for FTAHBC020\n");
+        abort();
+    }
 }
 
 static void a369soc_realize(DeviceState *dev, Error **errp)
diff --git a/hw/ftahbc020.c b/hw/ftahbc020.c
new file mode 100644
index 0000000..76e8b75
--- /dev/null
+++ b/hw/ftahbc020.c
@@ -0,0 +1,216 @@
+/*
+ * Faraday AHB controller
+ *
+ * Copyright (c) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This code is licensed under GNU GPL v2+
+ */
+
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "hw/devices.h"
+#include "sysemu/sysemu.h"
+
+#include "hw/faraday.h"
+
+#define REG_SLAVE(n)    ((n) * 4)   /* Slave config (base & size) */
+#define REG_PRIR        0x80        /* Priority register */
+#define REG_IDLECR      0x84        /* IDLE count register */
+#define REG_CR          0x88        /* Control register */
+#define REG_REVR        0x8c        /* Revision register */
+
+#define CR_REMAP        0x01        /* Enable AHB remap for slave 4 & 6 */
+
+#define TYPE_FTAHBC020  "ftahbc020"
+
+typedef struct Ftahbc020State {
+    /*< private >*/
+    SysBusDevice parent;
+
+    /*< public >*/
+    MemoryRegion iomem;
+
+    FaradaySoCState *soc;
+
+    /* HW register cache */
+    uint32_t prir;  /* Priority register */
+    uint32_t cr;    /* Control register */
+} Ftahbc020State;
+
+#define FTAHBC020(obj) \
+    OBJECT_CHECK(Ftahbc020State, obj, TYPE_FTAHBC020)
+
+static uint64_t
+ftahbc020_mem_read(void *opaque, hwaddr addr, unsigned size)
+{
+    Ftahbc020State *s = FTAHBC020(opaque);
+    FaradaySoCState *soc = s->soc;
+    bool remapped = (soc->ram_base != (soc->ahb_slave[6] & 0xfff00000));
+    uint64_t ret = 0;
+
+    switch (addr) {
+    /* slave address & window configuration */
+    case REG_SLAVE(0) ... REG_SLAVE(3):
+    /* fall-through - skip slave4 */
+    case REG_SLAVE(5):
+    /* fall-through - skip slave6 */
+    case REG_SLAVE(7) ... REG_SLAVE(31):
+        ret = soc->ahb_slave[addr / 4];
+        break;
+    case REG_SLAVE(4):
+        ret = soc->rom_base | (soc->ahb_slave[4] & 0x000f0000);
+        break;
+    case REG_SLAVE(6):
+        ret = soc->ram_base | (soc->ahb_slave[6] & 0x000f0000);
+        break;
+    /* priority register */
+    case REG_PRIR:
+        ret = s->prir;
+        break;
+    /* idle counter register */
+    case REG_IDLECR:
+        break;
+    /* control register */
+    case REG_CR:
+        if (remapped) {
+            s->cr |= CR_REMAP;
+        }
+        ret = s->cr;
+        break;
+    /* revision register */
+    case REG_REVR:
+        ret = 0x00010301;   /* rev. 1.3.1 */
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "ftahbc020: undefined memory access@%#" HWADDR_PRIx "\n", addr);
+        break;
+    }
+
+    return ret;
+}
+
+static void
+ftahbc020_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+    Ftahbc020State *s = FTAHBC020(opaque);
+    FaradaySoCState *soc = s->soc;
+    bool remapped = (soc->ram_base != (soc->ahb_slave[6] & 0xfff00000));
+
+    switch (addr) {
+    case REG_CR:    /* control register */
+        s->cr = (uint32_t)val;
+        if (remapped && !(s->cr & CR_REMAP)) {
+            fprintf(stderr,
+                    "ftahbc020: "
+                    "AHB remap could only be disabled via system reset!\n");
+            abort();
+        }
+        if (!remapped && (s->cr & CR_REMAP)) {
+            faraday_soc_ahb_remap(soc, true);
+        }
+        break;
+    case REG_PRIR:
+        s->prir = (uint32_t)val;
+        break;
+    case REG_SLAVE(0) ... REG_SLAVE(31):
+    case REG_IDLECR:
+    case REG_REVR:
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "ftahbc020: undefined memory access@%#" HWADDR_PRIx "\n", addr);
+        break;
+    }
+}
+
+static const MemoryRegionOps mmio_ops = {
+    .read  = ftahbc020_mem_read,
+    .write = ftahbc020_mem_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    }
+};
+
+static void ftahbc020_reset(DeviceState *ds)
+{
+    Ftahbc020State *s = FTAHBC020(SYS_BUS_DEVICE(ds));
+    Error *local_errp = NULL;
+
+    s->soc = FARADAY_SOC(object_property_get_link(OBJECT(s),
+                                                  "soc",
+                                                  &local_errp));
+    if (local_errp) {
+        fprintf(stderr, "ftahbc020: Unable to get soc link\n");
+        abort();
+    }
+
+    s->cr = 0;
+    s->prir = 0;
+    faraday_soc_ahb_remap(s->soc, false);
+}
+
+static void ftahbc020_realize(DeviceState *dev, Error **errp)
+{
+    Ftahbc020State *s = FTAHBC020(dev);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+
+    memory_region_init_io(&s->iomem,
+                          &mmio_ops,
+                          s,
+                          TYPE_FTAHBC020,
+                          0x1000);
+    sysbus_init_mmio(sbd, &s->iomem);
+}
+
+static const VMStateDescription vmstate_ftahbc020 = {
+    .name = TYPE_FTAHBC020,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(prir, Ftahbc020State),
+        VMSTATE_UINT32(cr, Ftahbc020State),
+        VMSTATE_END_OF_LIST(),
+    }
+};
+
+static void ftahbc020_instance_init(Object *obj)
+{
+    Ftahbc020State *s = FTAHBC020(obj);
+
+    object_property_add_link(obj,
+                             "soc",
+                             TYPE_FARADAY_SOC,
+                             (Object **) &s->soc,
+                             NULL);
+}
+
+static void ftahbc020_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->desc  = TYPE_FTAHBC020;
+    dc->vmsd  = &vmstate_ftahbc020;
+    dc->reset = ftahbc020_reset;
+    dc->realize = ftahbc020_realize;
+    dc->no_user = 1;
+}
+
+static const TypeInfo ftahbc020_info = {
+    .name          = TYPE_FTAHBC020,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(Ftahbc020State),
+    .instance_init = ftahbc020_instance_init,
+    .class_init    = ftahbc020_class_init,
+};
+
+static void ftahbc020_register_types(void)
+{
+    type_register_static(&ftahbc020_info);
+}
+
+type_init(ftahbc020_register_types)
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH v9 05/24] hw/arm: add FTDDRII030 DDRII controller support
  2013-03-25 12:09 [Qemu-devel] [PATCH v9 00/24] hw/arm: add Faraday A369 SoC platform support Kuo-Jung Su
                   ` (3 preceding siblings ...)
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 04/24] hw/arm: add FTAHBC020 AHB " Kuo-Jung Su
@ 2013-03-25 12:09 ` Kuo-Jung Su
  2013-03-28  0:09   ` Peter Crosthwaite
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 06/24] hw/arm: add FTPWMTMR010 timer support Kuo-Jung Su
                   ` (18 subsequent siblings)
  23 siblings, 1 reply; 35+ messages in thread
From: Kuo-Jung Su @ 2013-03-25 12:09 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, i.mitsyanko, Blue Swirl, Paul Brook, Kuo-Jung Su,
	Andreas, fred.konrad

From: Kuo-Jung Su <dantesu@faraday-tech.com>

The FTDDRII030 is a DDRII SDRAM controller which is responsible for
SDRAM initialization.

Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
---
 hw/arm/Makefile.objs    |    2 +-
 hw/arm/ftplat_a369soc.c |    8 ++
 hw/ftddrii030.c         |  192 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 201 insertions(+), 1 deletion(-)
 create mode 100644 hw/ftddrii030.c

diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index b2fa20f..e774962 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -24,7 +24,7 @@ obj-y += framebuffer.o
 obj-y += strongarm.o
 obj-y += imx_serial.o imx_ccm.o imx_timer.o imx_avic.o
 obj-$(CONFIG_KVM) += kvm/arm_gic.o
-obj-y += ftintc020.o ftahbc020.o
+obj-y += ftintc020.o ftahbc020.o ftddrii030.o
 
 obj-y := $(addprefix ../,$(obj-y))
 
diff --git a/hw/arm/ftplat_a369soc.c b/hw/arm/ftplat_a369soc.c
index 7f222cb..b2da582 100644
--- a/hw/arm/ftplat_a369soc.c
+++ b/hw/arm/ftplat_a369soc.c
@@ -129,6 +129,14 @@ static void a369soc_chip_init(FaradaySoCState *s)
         fprintf(stderr, "a369soc: Unable to set soc link for FTAHBC020\n");
         abort();
     }
+
+    /* ftddrii030 */
+    ds = sysbus_create_simple("ftddrii030", 0x93100000, NULL);
+    object_property_set_link(OBJECT(ds), OBJECT(s), "soc", &local_errp);
+    if (local_errp) {
+        fprintf(stderr, "a369soc: Unable to set soc link for FTDDRII030\n");
+        abort();
+    }
 }
 
 static void a369soc_realize(DeviceState *dev, Error **errp)
diff --git a/hw/ftddrii030.c b/hw/ftddrii030.c
new file mode 100644
index 0000000..158db1f
--- /dev/null
+++ b/hw/ftddrii030.c
@@ -0,0 +1,192 @@
+/*
+ * Faraday DDRII controller
+ *
+ * Copyright (c) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This code is licensed under GNU GPL v2+
+ */
+
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "hw/devices.h"
+#include "sysemu/sysemu.h"
+
+#include "hw/faraday.h"
+
+#define REG_MCR             0x00    /* memory configuration register */
+#define REG_MSR             0x04    /* memory status register */
+#define REG_REVR            0x50    /* revision register */
+
+#define MSR_INIT_OK         BIT(8)  /* initialization finished */
+#define MSR_CMD_MRS         BIT(0)  /* start MRS command (init. seq.) */
+
+#define CFG_REGSIZE         (REG_REVR / 4)
+
+#define TYPE_FTDDRII030     "ftddrii030"
+
+typedef struct Ftddrii030State {
+    /*< private >*/
+    SysBusDevice parent;
+
+    /*< public >*/
+    MemoryRegion iomem;
+
+    FaradaySoCState *soc;
+
+    /* HW register cache */
+    uint32_t regs[CFG_REGSIZE];
+} Ftddrii030State;
+
+#define FTDDRII030(obj) \
+    OBJECT_CHECK(Ftddrii030State, obj, TYPE_FTDDRII030)
+
+#define DDR_REG32(s, off) \
+    ((s)->regs[(off) / 4])
+
+static uint64_t
+ftddrii030_mem_read(void *opaque, hwaddr addr, unsigned size)
+{
+    Ftddrii030State *s = FTDDRII030(opaque);
+    FaradaySoCState *soc = s->soc;
+    uint64_t ret = 0;
+
+    if (soc->ram_visible) {
+        DDR_REG32(s, REG_MSR) |= MSR_INIT_OK;
+    } else {
+        DDR_REG32(s, REG_MSR) &= ~MSR_INIT_OK;
+    }
+
+    switch (addr) {
+    case REG_MCR ... REG_REVR - 4:
+        ret = DDR_REG32(s, addr);
+        break;
+    case REG_REVR:
+        ret = 0x100;    /* rev. = 0.1.0 */
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "ftddrii030: undefined memory access@%#" HWADDR_PRIx "\n", addr);
+        break;
+    }
+
+    return ret;
+}
+
+static void
+ftddrii030_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+    Ftddrii030State *s = FTDDRII030(opaque);
+    FaradaySoCState *soc = s->soc;
+
+    switch (addr) {
+    case REG_MCR:
+        DDR_REG32(s, addr) = (uint32_t)val & 0xffff;
+        break;
+    case REG_MSR:
+        if (!soc->ram_visible && (val & MSR_CMD_MRS)) {
+            val &= ~MSR_CMD_MRS;
+            faraday_soc_ram_setup(soc, true);
+        }
+        DDR_REG32(s, addr) = (uint32_t)val;
+        break;
+    /* SDRAM Timing, ECC ...etc. */
+    case REG_MSR + 4 ... REG_REVR - 4:
+        DDR_REG32(s, addr) = (uint32_t)val;
+        break;
+    case REG_REVR:
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "ftddrii030: undefined memory access@%#" HWADDR_PRIx "\n", addr);
+        break;
+    }
+}
+
+static const MemoryRegionOps mmio_ops = {
+    .read  = ftddrii030_mem_read,
+    .write = ftddrii030_mem_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    }
+};
+
+static void ftddrii030_reset(DeviceState *ds)
+{
+    Ftddrii030State *s = FTDDRII030(SYS_BUS_DEVICE(ds));
+    Error *local_errp = NULL;
+
+    s->soc = FARADAY_SOC(object_property_get_link(OBJECT(s),
+                                                  "soc",
+                                                  &local_errp));
+    if (local_errp) {
+        fprintf(stderr, "ftahbc020: Unable to get soc link\n");
+        abort();
+    }
+
+    faraday_soc_ram_setup(s->soc, false);
+    memset(s->regs, 0, sizeof(s->regs));
+}
+
+static void ftddrii030_realize(DeviceState *dev, Error **errp)
+{
+    Ftddrii030State *s = FTDDRII030(dev);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+
+    memory_region_init_io(&s->iomem,
+                          &mmio_ops,
+                          s,
+                          TYPE_FTDDRII030,
+                          0x1000);
+    sysbus_init_mmio(sbd, &s->iomem);
+}
+
+static const VMStateDescription vmstate_ftddrii030 = {
+    .name = TYPE_FTDDRII030,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(regs, Ftddrii030State, CFG_REGSIZE),
+        VMSTATE_END_OF_LIST(),
+    }
+};
+
+static void ftddrii030_instance_init(Object *obj)
+{
+    Ftddrii030State *s = FTDDRII030(obj);
+
+    object_property_add_link(obj,
+                             "soc",
+                             TYPE_FARADAY_SOC,
+                             (Object **) &s->soc,
+                             NULL);
+}
+
+static void ftddrii030_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->desc  = TYPE_FTDDRII030;
+    dc->vmsd  = &vmstate_ftddrii030;
+    dc->reset = ftddrii030_reset;
+    dc->realize = ftddrii030_realize;
+    dc->no_user = 1;
+}
+
+static const TypeInfo ftddrii030_info = {
+    .name          = TYPE_FTDDRII030,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(Ftddrii030State),
+    .instance_init = ftddrii030_instance_init,
+    .class_init    = ftddrii030_class_init,
+};
+
+static void ftddrii030_register_types(void)
+{
+    type_register_static(&ftddrii030_info);
+}
+
+type_init(ftddrii030_register_types)
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH v9 06/24] hw/arm: add FTPWMTMR010 timer support
  2013-03-25 12:09 [Qemu-devel] [PATCH v9 00/24] hw/arm: add Faraday A369 SoC platform support Kuo-Jung Su
                   ` (4 preceding siblings ...)
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 05/24] hw/arm: add FTDDRII030 DDRII " Kuo-Jung Su
@ 2013-03-25 12:09 ` Kuo-Jung Su
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 07/24] hw/arm: add FTWDT010 watchdog " Kuo-Jung Su
                   ` (17 subsequent siblings)
  23 siblings, 0 replies; 35+ messages in thread
From: Kuo-Jung Su @ 2013-03-25 12:09 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, i.mitsyanko, Blue Swirl, Paul Brook, Kuo-Jung Su,
	Andreas, fred.konrad

From: Kuo-Jung Su <dantesu@faraday-tech.com>

The FTPWMTMR010 is an APB device which provides up to 8 independent timers.

Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
---
 hw/arm/Makefile.objs    |    2 +-
 hw/arm/ftplat_a369soc.c |   10 ++
 hw/ftpwmtmr010.c        |  261 +++++++++++++++++++++++++++++++++++++++++++++++
 hw/ftpwmtmr010.h        |   31 ++++++
 4 files changed, 303 insertions(+), 1 deletion(-)
 create mode 100644 hw/ftpwmtmr010.c
 create mode 100644 hw/ftpwmtmr010.h

diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index e774962..4fe0222 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -24,7 +24,7 @@ obj-y += framebuffer.o
 obj-y += strongarm.o
 obj-y += imx_serial.o imx_ccm.o imx_timer.o imx_avic.o
 obj-$(CONFIG_KVM) += kvm/arm_gic.o
-obj-y += ftintc020.o ftahbc020.o ftddrii030.o
+obj-y += ftintc020.o ftahbc020.o ftddrii030.o ftpwmtmr010.o
 
 obj-y := $(addprefix ../,$(obj-y))
 
diff --git a/hw/arm/ftplat_a369soc.c b/hw/arm/ftplat_a369soc.c
index b2da582..6e2ea65 100644
--- a/hw/arm/ftplat_a369soc.c
+++ b/hw/arm/ftplat_a369soc.c
@@ -137,6 +137,16 @@ static void a369soc_chip_init(FaradaySoCState *s)
         fprintf(stderr, "a369soc: Unable to set soc link for FTDDRII030\n");
         abort();
     }
+
+    /* Timer */
+    ds = qdev_create(NULL, "ftpwmtmr010");
+    qdev_prop_set_uint32(ds, "freq", 66 * 1000000);
+    qdev_init_nofail(ds);
+    sysbus_mmio_map(SYS_BUS_DEVICE(ds), 0, 0x92300000);
+    sysbus_connect_irq(SYS_BUS_DEVICE(ds), 0, s->pic[8]);
+    sysbus_connect_irq(SYS_BUS_DEVICE(ds), 1, s->pic[9]);
+    sysbus_connect_irq(SYS_BUS_DEVICE(ds), 2, s->pic[10]);
+    sysbus_connect_irq(SYS_BUS_DEVICE(ds), 3, s->pic[11]);
 }
 
 static void a369soc_realize(DeviceState *dev, Error **errp)
diff --git a/hw/ftpwmtmr010.c b/hw/ftpwmtmr010.c
new file mode 100644
index 0000000..d08eaa6
--- /dev/null
+++ b/hw/ftpwmtmr010.c
@@ -0,0 +1,261 @@
+/*
+ * Faraday FTPWMTMR010 Timer.
+ *
+ * Copyright (c) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This code is licensed under GNU GPL v2+.
+ */
+
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "qemu/timer.h"
+#include "sysemu/sysemu.h"
+
+#include "hw/ftpwmtmr010.h"
+
+#define TYPE_FTPWMTMR010        "ftpwmtmr010"
+#define TYPE_FTPWMTMR010_TIMER  "ftpwmtmr010_timer"
+
+typedef struct Ftpwmtmr010State Ftpwmtmr010State;
+
+typedef struct Ftpwmtmr010Timer {
+    uint32_t ctrl;
+    uint32_t cntb;
+    int id;
+    uint64_t timeout;
+    uint64_t countdown;
+    qemu_irq irq;
+    QEMUTimer *qtimer;
+    Ftpwmtmr010State *chip;
+} Ftpwmtmr010Timer;
+
+struct Ftpwmtmr010State {
+    /*< private >*/
+    SysBusDevice parent;
+
+    /*< public >*/
+    MemoryRegion iomem;
+    Ftpwmtmr010Timer timer[8];
+    uint32_t freq;        /* desired source clock */
+    uint64_t step;        /* get_ticks_per_sec() / freq */
+    uint32_t stat;
+};
+
+#define FTPWMTMR010(obj) \
+    OBJECT_CHECK(Ftpwmtmr010State, obj, TYPE_FTPWMTMR010)
+
+static uint64_t
+ftpwmtmr010_mem_read(void *opaque, hwaddr addr, unsigned size)
+{
+    Ftpwmtmr010State *s = FTPWMTMR010(opaque);
+    Ftpwmtmr010Timer *t;
+    uint64_t now = qemu_get_clock_ns(vm_clock);
+    uint64_t ret = 0;
+
+    switch (addr) {
+    case REG_SR:
+        ret = s->stat;
+        break;
+    case REG_REVR:
+        ret = 0x00000000;   /* Rev. 0.0.0 (no rev. id) */
+        break;
+    case REG_TIMER_BASE(1) ... REG_TIMER_BASE(8) + 0x0C:
+        t = s->timer + (addr >> 4) - 1;
+        switch (addr & 0x0f) {
+        case REG_TIMER_CTRL:
+            return t->ctrl;
+        case REG_TIMER_CNTB:
+            return t->cntb;
+        case REG_TIMER_CNTO:
+            if ((t->ctrl & TIMER_CTRL_START) && (t->timeout > now)) {
+                ret = (t->timeout - now) / s->step;
+            }
+            break;
+        }
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "ftpwmtmr010: undefined memory access@%#" HWADDR_PRIx "\n", addr);
+        break;
+    }
+
+    return ret;
+}
+
+static void
+ftpwmtmr010_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+    Ftpwmtmr010State *s = FTPWMTMR010(opaque);
+    Ftpwmtmr010Timer *t;
+    int i;
+
+    switch (addr) {
+    case REG_SR:
+        s->stat &= ~((uint32_t)val);
+        for (i = 0; i < 8; ++i) {
+            if (val & BIT(i)) {
+                qemu_irq_lower(s->timer[i].irq);
+            }
+        }
+        break;
+    case REG_TIMER_BASE(1) ... REG_TIMER_BASE(8) + 0x0C:
+        t = s->timer + (addr >> 4) - 1;
+        switch (addr & 0x0f) {
+        case REG_TIMER_CTRL:
+            t->ctrl = (uint32_t)val;
+            if (t->ctrl & TIMER_CTRL_UPDATE) {
+                t->countdown = (uint64_t)t->cntb * s->step;
+            }
+            if (t->ctrl & TIMER_CTRL_START) {
+                t->timeout = t->countdown + qemu_get_clock_ns(vm_clock);
+                qemu_mod_timer(t->qtimer, t->timeout);
+            }
+            break;
+        case REG_TIMER_CNTB:
+            t->cntb = (uint32_t)val;
+            break;
+        }
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "ftpwmtmr010: undefined memory access@%#" HWADDR_PRIx "\n", addr);
+        break;
+    }
+}
+
+static const MemoryRegionOps mmio_ops = {
+    .read  = ftpwmtmr010_mem_read,
+    .write = ftpwmtmr010_mem_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    }
+};
+
+static void ftpwmtmr010_timer_tick(void *opaque)
+{
+    Ftpwmtmr010Timer *t = opaque;
+    Ftpwmtmr010State *s = t->chip;
+
+    /* if the timer has been enabled/started */
+    if (!(t->ctrl & TIMER_CTRL_START)) {
+        return;
+    }
+
+    /* if the interrupt enabled */
+    if (t->ctrl & TIMER_CTRL_INTR) {
+        s->stat |= BIT(t->id);
+        if (t->ctrl & TIMER_CTRL_INTR_EDGE) {
+            qemu_irq_pulse(t->irq);
+        } else {
+            qemu_irq_raise(t->irq);
+        }
+    }
+
+    /* if auto-reload is enabled */
+    if (t->ctrl & TIMER_CTRL_AUTORELOAD) {
+        t->timeout = t->countdown + qemu_get_clock_ns(vm_clock);
+        qemu_mod_timer(t->qtimer, t->timeout);
+    } else {
+        t->ctrl &= ~TIMER_CTRL_START;
+    }
+}
+
+static void ftpwmtmr010_reset(DeviceState *ds)
+{
+    Ftpwmtmr010State *s = FTPWMTMR010(SYS_BUS_DEVICE(ds));
+    int i;
+
+    s->stat = 0;
+
+    for (i = 0; i < 8; ++i) {
+        s->timer[i].ctrl = 0;
+        s->timer[i].cntb = 0;
+        s->timer[i].timeout = 0;
+        qemu_irq_lower(s->timer[i].irq);
+        qemu_del_timer(s->timer[i].qtimer);
+    }
+}
+
+static void ftpwmtmr010_realize(DeviceState *dev, Error **errp)
+{
+    Ftpwmtmr010State *s = FTPWMTMR010(dev);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+    int i;
+
+    s->step = (uint64_t)get_ticks_per_sec() / (uint64_t)s->freq;
+
+    memory_region_init_io(&s->iomem,
+                          &mmio_ops,
+                          s,
+                          TYPE_FTPWMTMR010,
+                          0x1000);
+    sysbus_init_mmio(sbd, &s->iomem);
+    for (i = 0; i < 8; ++i) {
+        s->timer[i].id = i;
+        s->timer[i].chip = s;
+        s->timer[i].qtimer = qemu_new_timer_ns(vm_clock,
+                                               ftpwmtmr010_timer_tick,
+                                               &s->timer[i]);
+        sysbus_init_irq(sbd, &s->timer[i].irq);
+    }
+}
+
+static const VMStateDescription vmstate_ftpwmtmr010_timer = {
+    .name = TYPE_FTPWMTMR010_TIMER,
+    .version_id = 2,
+    .minimum_version_id = 2,
+    .minimum_version_id_old = 2,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(ctrl, Ftpwmtmr010Timer),
+        VMSTATE_UINT32(cntb, Ftpwmtmr010Timer),
+        VMSTATE_END_OF_LIST(),
+    },
+};
+
+static const VMStateDescription vmstate_ftpwmtmr010 = {
+    .name = TYPE_FTPWMTMR010,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(stat, Ftpwmtmr010State),
+        VMSTATE_UINT32(freq, Ftpwmtmr010State),
+        VMSTATE_UINT64(step, Ftpwmtmr010State),
+        VMSTATE_STRUCT_ARRAY(timer, Ftpwmtmr010State, 8, 1,
+                        vmstate_ftpwmtmr010_timer, Ftpwmtmr010Timer),
+        VMSTATE_END_OF_LIST(),
+    }
+};
+
+static Property ftpwmtmr010_properties[] = {
+    DEFINE_PROP_UINT32("freq", Ftpwmtmr010State, freq, 66000000),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void ftpwmtmr010_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->vmsd    = &vmstate_ftpwmtmr010;
+    dc->props   = ftpwmtmr010_properties;
+    dc->reset   = ftpwmtmr010_reset;
+    dc->realize = ftpwmtmr010_realize;
+    dc->no_user = 1;
+}
+
+static const TypeInfo ftpwmtmr010_info = {
+    .name          = TYPE_FTPWMTMR010,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(Ftpwmtmr010State),
+    .class_init    = ftpwmtmr010_class_init,
+};
+
+static void ftpwmtmr010_register_types(void)
+{
+    type_register_static(&ftpwmtmr010_info);
+}
+
+type_init(ftpwmtmr010_register_types)
diff --git a/hw/ftpwmtmr010.h b/hw/ftpwmtmr010.h
new file mode 100644
index 0000000..a435a70
--- /dev/null
+++ b/hw/ftpwmtmr010.h
@@ -0,0 +1,31 @@
+/*
+ * Faraday FTPWMTMR010 Timer.
+ *
+ * Copyright (c) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This code is licensed under GNU GPL v2+.
+ */
+
+#ifndef HW_ARM_FTPWMTMR010_H
+#define HW_ARM_FTPWMTMR010_H
+
+#include "qemu/bitops.h"
+
+#define REG_SR              0x00    /* status register */
+#define REG_REVR            0x90    /* revision register */
+
+#define REG_TIMER_BASE(id)  (0x00 + ((id) << 4))
+#define REG_TIMER_CTRL      0x00
+#define REG_TIMER_CNTB      0x04
+#define REG_TIMER_CMPB      0x08
+#define REG_TIMER_CNTO      0x0C
+
+#define TIMER_CTRL_EXTCK        BIT(0)  /* external clock */
+#define TIMER_CTRL_START        BIT(1)
+#define TIMER_CTRL_UPDATE       BIT(2)
+#define TIMER_CTRL_AUTORELOAD   BIT(4)
+#define TIMER_CTRL_INTR         BIT(5)  /* interrupt enabled */
+#define TIMER_CTRL_INTR_EDGE    BIT(6)  /* interrupt type: 1=edge 0=level */
+
+#endif
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH v9 07/24] hw/arm: add FTWDT010 watchdog timer support
  2013-03-25 12:09 [Qemu-devel] [PATCH v9 00/24] hw/arm: add Faraday A369 SoC platform support Kuo-Jung Su
                   ` (5 preceding siblings ...)
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 06/24] hw/arm: add FTPWMTMR010 timer support Kuo-Jung Su
@ 2013-03-25 12:09 ` Kuo-Jung Su
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 08/24] hw/arm: add FTRTC011 RTC " Kuo-Jung Su
                   ` (16 subsequent siblings)
  23 siblings, 0 replies; 35+ messages in thread
From: Kuo-Jung Su @ 2013-03-25 12:09 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, i.mitsyanko, Wei-Ren Chen, Blue Swirl, Paul Brook,
	Kuo-Jung Su, Paolo Bonzini, Andreas, fred.konrad

From: Kuo-Jung Su <dantesu@faraday-tech.com>

The FTWDT010 is used to prevent system from infinite loop
while software gets trapped in the deadlock.

Under the normal operation, users should restart FTWDT010
at the regular intervals before counter counts down to 0.

If the counter does reach 0, FTWDT010 will try to reset
the system by generating one or a combination of signals,
system reset, system interrupt, and external interrupt.

Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
---
 hw/arm/Makefile.objs    |    2 +-
 hw/arm/ftplat_a369.c    |    8 ++
 hw/arm/ftplat_a369soc.c |    3 +
 hw/ftwdt010.c           |  213 +++++++++++++++++++++++++++++++++++++++++++++++
 hw/ftwdt010.h           |   35 ++++++++
 5 files changed, 260 insertions(+), 1 deletion(-)
 create mode 100644 hw/ftwdt010.c
 create mode 100644 hw/ftwdt010.h

diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 4fe0222..22f0c64 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -24,7 +24,7 @@ obj-y += framebuffer.o
 obj-y += strongarm.o
 obj-y += imx_serial.o imx_ccm.o imx_timer.o imx_avic.o
 obj-$(CONFIG_KVM) += kvm/arm_gic.o
-obj-y += ftintc020.o ftahbc020.o ftddrii030.o ftpwmtmr010.o
+obj-y += ftintc020.o ftahbc020.o ftddrii030.o ftpwmtmr010.o ftwdt010.o
 
 obj-y := $(addprefix ../,$(obj-y))
 
diff --git a/hw/arm/ftplat_a369.c b/hw/arm/ftplat_a369.c
index 6f00c82..45f0846 100644
--- a/hw/arm/ftplat_a369.c
+++ b/hw/arm/ftplat_a369.c
@@ -19,6 +19,11 @@
 
 #include "hw/faraday.h"
 
+static void a369_system_reset(void *opaque)
+{
+    cpu_reset(CPU(opaque));
+}
+
 /* Board init.  */
 
 static void a369_board_init(QEMUMachineInitArgs *args)
@@ -57,6 +62,9 @@ static void a369_board_init(QEMUMachineInitArgs *args)
     vmstate_register_ram_global(s->ram);
     qdev_init_nofail(ds);
 
+    /* Customized system reset */
+    qemu_register_reset(a369_system_reset, cpu);
+
     /* System start-up */
 
     if (args->kernel_filename) {
diff --git a/hw/arm/ftplat_a369soc.c b/hw/arm/ftplat_a369soc.c
index 6e2ea65..56f0920 100644
--- a/hw/arm/ftplat_a369soc.c
+++ b/hw/arm/ftplat_a369soc.c
@@ -147,6 +147,9 @@ static void a369soc_chip_init(FaradaySoCState *s)
     sysbus_connect_irq(SYS_BUS_DEVICE(ds), 1, s->pic[9]);
     sysbus_connect_irq(SYS_BUS_DEVICE(ds), 2, s->pic[10]);
     sysbus_connect_irq(SYS_BUS_DEVICE(ds), 3, s->pic[11]);
+
+    /* ftwdt010 */
+    sysbus_create_simple("ftwdt010", 0x92200000, s->pic[46]);
 }
 
 static void a369soc_realize(DeviceState *dev, Error **errp)
diff --git a/hw/ftwdt010.c b/hw/ftwdt010.c
new file mode 100644
index 0000000..cdbe2f3
--- /dev/null
+++ b/hw/ftwdt010.c
@@ -0,0 +1,213 @@
+/*
+ * QEMU model of the FTWDT010 WatchDog Timer
+ *
+ * Copyright (C) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This file is licensed under GNU GPL v2+.
+ */
+
+#include "hw/sysbus.h"
+#include "hw/watchdog.h"
+#include "sysemu/sysemu.h"
+#include "qemu/timer.h"
+
+#include "hw/ftwdt010.h"
+
+#define TYPE_FTWDT010   "ftwdt010"
+
+typedef struct Ftwdt010State {
+    /*< private >*/
+    SysBusDevice parent;
+
+    /*< public >*/
+    MemoryRegion mmio;
+
+    qemu_irq irq;
+
+    QEMUTimer *qtimer;
+
+    uint64_t timeout;
+    uint64_t freq;        /* desired source clock */
+    uint64_t step;        /* get_ticks_per_sec() / freq */
+    bool running;
+
+    /* HW register cache */
+    uint32_t load;
+    uint32_t cr;
+    uint32_t sr;
+} Ftwdt010State;
+
+#define FTWDT010(obj) \
+    OBJECT_CHECK(Ftwdt010State, obj, TYPE_FTWDT010)
+
+static uint64_t
+ftwdt010_mem_read(void *opaque, hwaddr addr, unsigned size)
+{
+    Ftwdt010State *s = FTWDT010(opaque);
+    uint32_t ret = 0;
+
+    switch (addr) {
+    case REG_COUNTER:
+        if (s->cr & CR_EN) {
+            ret = s->timeout - qemu_get_clock_ms(rt_clock);
+            ret = MIN(s->load, ret * 1000000ULL / s->step);
+        } else {
+            ret = s->load;
+        }
+        break;
+    case REG_LOAD:
+        return s->load;
+    case REG_CR:
+        return s->cr;
+    case REG_SR:
+        return s->sr;
+    case REG_REVR:
+        return 0x00010601;  /* rev. 1.6.1 */
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "ftwdt010: undefined memory access@%#" HWADDR_PRIx "\n", addr);
+        break;
+    }
+
+    return ret;
+}
+
+static void
+ftwdt010_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+    Ftwdt010State *s = FTWDT010(opaque);
+
+    switch (addr) {
+    case REG_LOAD:
+        s->load = (uint32_t)val;
+        break;
+    case REG_RESTART:
+        if ((s->cr & CR_EN) && (val == WDT_MAGIC)) {
+            s->timeout = (s->step * (uint64_t)s->load) / 1000000ULL;
+            s->timeout = qemu_get_clock_ms(rt_clock) + MAX(s->timeout, 1);
+            qemu_mod_timer(s->qtimer, s->timeout);
+        }
+        break;
+    case REG_CR:
+        s->cr = (uint32_t)val;
+        if (s->cr & CR_EN) {
+            if (s->running) {
+                break;
+            }
+            s->running = true;
+            s->timeout = (s->step * (uint64_t)s->load) / 1000000ULL;
+            s->timeout = qemu_get_clock_ms(rt_clock) + MAX(s->timeout, 1);
+            qemu_mod_timer(s->qtimer, s->timeout);
+        } else {
+            s->running = false;
+            qemu_del_timer(s->qtimer);
+        }
+        break;
+    case REG_SCR:
+        s->sr &= ~(uint32_t)val;
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "ftwdt010: undefined memory access@%#" HWADDR_PRIx "\n", addr);
+        break;
+    }
+}
+
+static const MemoryRegionOps mmio_ops = {
+    .read  = ftwdt010_mem_read,
+    .write = ftwdt010_mem_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4
+    }
+};
+
+static void ftwdt010_timer_tick(void *opaque)
+{
+    Ftwdt010State *s = FTWDT010(opaque);
+
+    s->sr = SR_SRST;
+
+    /* send interrupt signal */
+    qemu_set_irq(s->irq, (s->cr & CR_INTR) ? 1 : 0);
+
+    /* send system reset */
+    if (s->cr & CR_SRST) {
+        watchdog_perform_action();
+    }
+}
+
+static void ftwdt010_reset(DeviceState *ds)
+{
+    Ftwdt010State *s = FTWDT010(SYS_BUS_DEVICE(ds));
+
+    s->cr      = 0;
+    s->sr      = 0;
+    s->load    = 0x3ef1480;
+    s->timeout = 0;
+}
+
+static void ftwdt010_realize(DeviceState *dev, Error **errp)
+{
+    Ftwdt010State *s = FTWDT010(dev);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+
+    s->step = (uint64_t)get_ticks_per_sec() / s->freq;
+    s->qtimer = qemu_new_timer_ms(rt_clock, ftwdt010_timer_tick, s);
+
+    memory_region_init_io(&s->mmio,
+                          &mmio_ops,
+                          s,
+                          TYPE_FTWDT010,
+                          0x1000);
+    sysbus_init_mmio(sbd, &s->mmio);
+    sysbus_init_irq(sbd, &s->irq);
+}
+
+static const VMStateDescription vmstate_ftwdt010 = {
+    .name = TYPE_FTWDT010,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT64(timeout, Ftwdt010State),
+        VMSTATE_UINT64(freq, Ftwdt010State),
+        VMSTATE_UINT64(step, Ftwdt010State),
+        VMSTATE_UINT32(load, Ftwdt010State),
+        VMSTATE_UINT32(cr, Ftwdt010State),
+        VMSTATE_UINT32(sr, Ftwdt010State),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static Property ftwdt010_properties[] = {
+    DEFINE_PROP_UINT64("freq", Ftwdt010State, freq, 66000000ULL),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void ftwdt010_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->vmsd    = &vmstate_ftwdt010;
+    dc->props   = ftwdt010_properties;
+    dc->reset   = ftwdt010_reset;
+    dc->realize = ftwdt010_realize;
+    dc->no_user = 1;
+}
+
+static const TypeInfo ftwdt010_info = {
+    .name           = TYPE_FTWDT010,
+    .parent         = TYPE_SYS_BUS_DEVICE,
+    .instance_size  = sizeof(Ftwdt010State),
+    .class_init     = ftwdt010_class_init,
+};
+
+static void ftwdt010_register_types(void)
+{
+    type_register_static(&ftwdt010_info);
+}
+
+type_init(ftwdt010_register_types)
diff --git a/hw/ftwdt010.h b/hw/ftwdt010.h
new file mode 100644
index 0000000..46c0871
--- /dev/null
+++ b/hw/ftwdt010.h
@@ -0,0 +1,35 @@
+/*
+ * QEMU model of the FTWDT010 WatchDog Timer
+ *
+ * Copyright (C) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This file is licensed under GNU GPL v2+.
+ */
+
+#ifndef HW_ARM_FTWDT010_H
+#define HW_ARM_FTWDT010_H
+
+#include "qemu/bitops.h"
+
+/* Hardware registers */
+#define REG_COUNTER     0x00    /* counter register */
+#define REG_LOAD        0x04    /* (re)load register */
+#define REG_RESTART     0x08    /* restart register */
+#define REG_CR          0x0C    /* control register */
+#define REG_SR          0x10    /* status register */
+#define REG_SCR         0x14    /* status clear register */
+#define REG_INTR_LEN    0x18    /* interrupt length register */
+#define REG_REVR        0x1C    /* revision register */
+
+#define CR_CLKS         BIT(4)  /* clock source */
+#define CR_ESIG         BIT(3)  /* external signal enabled */
+#define CR_INTR         BIT(2)  /* system reset interrupt enabled */
+#define CR_SRST         BIT(1)  /* system reset enabled */
+#define CR_EN           BIT(0)  /* chip enabled */
+
+#define SR_SRST         BIT(1)  /* system reset */
+
+#define WDT_MAGIC       0x5ab9  /* magic for watchdog restart */
+
+#endif
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH v9 08/24] hw/arm: add FTRTC011 RTC timer support
  2013-03-25 12:09 [Qemu-devel] [PATCH v9 00/24] hw/arm: add Faraday A369 SoC platform support Kuo-Jung Su
                   ` (6 preceding siblings ...)
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 07/24] hw/arm: add FTWDT010 watchdog " Kuo-Jung Su
@ 2013-03-25 12:09 ` Kuo-Jung Su
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 09/24] tests: add QTest for FTRTC011 Kuo-Jung Su
                   ` (15 subsequent siblings)
  23 siblings, 0 replies; 35+ messages in thread
From: Kuo-Jung Su @ 2013-03-25 12:09 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, i.mitsyanko, Wei-Ren Chen, Blue Swirl, Paul Brook,
	Kuo-Jung Su, Paolo Bonzini, Andreas, fred.konrad

From: Kuo-Jung Su <dantesu@faraday-tech.com>

It provides separate second, minute, hour, and day counters. The second
counter is toggled each second, the minute counter is toggled each minute,
the hour counter is toggled each hour, and the day counter is toggled each day.

The FTRTC011 provides a programmable auto-alarm function. When the second
auto-alarm function is turned on, the RTC will automatically trigger an
interrupt each second. The automatic minute and hour alarms can be turned on
as well.

Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
---
 hw/arm/Makefile.objs    |    3 +-
 hw/arm/ftplat_a369soc.c |   18 +++
 hw/ftrtc011.c           |  387 +++++++++++++++++++++++++++++++++++++++++++++++
 hw/ftrtc011.h           |   53 +++++++
 4 files changed, 460 insertions(+), 1 deletion(-)
 create mode 100644 hw/ftrtc011.c
 create mode 100644 hw/ftrtc011.h

diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 22f0c64..6a41b21 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -24,7 +24,8 @@ obj-y += framebuffer.o
 obj-y += strongarm.o
 obj-y += imx_serial.o imx_ccm.o imx_timer.o imx_avic.o
 obj-$(CONFIG_KVM) += kvm/arm_gic.o
-obj-y += ftintc020.o ftahbc020.o ftddrii030.o ftpwmtmr010.o ftwdt010.o
+obj-y += ftintc020.o ftahbc020.o ftddrii030.o ftpwmtmr010.o ftwdt010.o \
+                ftrtc011.o
 
 obj-y := $(addprefix ../,$(obj-y))
 
diff --git a/hw/arm/ftplat_a369soc.c b/hw/arm/ftplat_a369soc.c
index 56f0920..bd696c4 100644
--- a/hw/arm/ftplat_a369soc.c
+++ b/hw/arm/ftplat_a369soc.c
@@ -150,6 +150,24 @@ static void a369soc_chip_init(FaradaySoCState *s)
 
     /* ftwdt010 */
     sysbus_create_simple("ftwdt010", 0x92200000, s->pic[46]);
+
+    /* ftrtc011 */
+    ds = qdev_create(NULL, "ftrtc011");
+    /* Setup QOM path for QTest */
+    object_property_add_child(OBJECT(s),
+                              "ftrtc011",
+                              OBJECT(ds),
+                              NULL);
+    qdev_init_nofail(ds);
+    sysbus_mmio_map(SYS_BUS_DEVICE(ds), 0, 0x92100000);
+    /* Alarm (Edge) */
+    sysbus_connect_irq(SYS_BUS_DEVICE(ds), 1, s->pic[42]);
+    /* Second (Edge) */
+    sysbus_connect_irq(SYS_BUS_DEVICE(ds), 2, s->pic[43]);
+    /* Minute (Edge) */
+    sysbus_connect_irq(SYS_BUS_DEVICE(ds), 3, s->pic[44]);
+    /* Hour (Edge) */
+    sysbus_connect_irq(SYS_BUS_DEVICE(ds), 4, s->pic[45]);
 }
 
 static void a369soc_realize(DeviceState *dev, Error **errp)
diff --git a/hw/ftrtc011.c b/hw/ftrtc011.c
new file mode 100644
index 0000000..79b9021
--- /dev/null
+++ b/hw/ftrtc011.c
@@ -0,0 +1,387 @@
+/*
+ * QEMU model of the FTRTC011 RTC Timer
+ *
+ * Copyright (C) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This file is licensed under GNU GPL v2+.
+ */
+
+#include "hw/sysbus.h"
+#include "qemu/timer.h"
+#include "sysemu/sysemu.h"
+
+#include "hw/ftrtc011.h"
+
+enum ftrtc011_irqpin {
+    IRQ_ALARM_LEVEL = 0,
+    IRQ_ALARM_EDGE,
+    IRQ_SEC,
+    IRQ_MIN,
+    IRQ_HOUR,
+    IRQ_DAY,
+};
+
+#define TYPE_FTRTC011   "ftrtc011"
+
+#define CFG_REGSIZE     (0x3c / 4)
+
+typedef struct Ftrtc011State {
+    /*< private >*/
+    SysBusDevice parent;
+
+    /*< public >*/
+    MemoryRegion mmio;
+
+    qemu_irq irq[6];
+
+    QEMUTimer *qtimer;
+    int64_t rtc_base;
+    int64_t rtc_start;
+
+    /* HW register caches */
+    uint32_t regs[CFG_REGSIZE];
+} Ftrtc011State;
+
+#define FTRTC011(obj) \
+    OBJECT_CHECK(Ftrtc011State, obj, TYPE_FTRTC011)
+
+#define RTC_REG32(s, off) \
+    ((s)->regs[(off) / 4])
+
+/* Update interrupts.  */
+static void ftrtc011_update_irq(Ftrtc011State *s)
+{
+    uint32_t mask = extract32(RTC_REG32(s, REG_CR), 1, 5)
+                  & RTC_REG32(s, REG_ISR);
+
+    qemu_set_irq(s->irq[IRQ_ALARM_LEVEL], !!(mask & ISR_ALARM));
+
+    if (mask) {
+        if (mask & ISR_SEC) {
+            qemu_irq_pulse(s->irq[IRQ_SEC]);
+        }
+        if (mask & ISR_MIN) {
+            qemu_irq_pulse(s->irq[IRQ_MIN]);
+        }
+        if (mask & ISR_HOUR) {
+            qemu_irq_pulse(s->irq[IRQ_HOUR]);
+        }
+        if (mask & ISR_DAY) {
+            qemu_irq_pulse(s->irq[IRQ_DAY]);
+        }
+        if (mask & ISR_ALARM) {
+            qemu_irq_pulse(s->irq[IRQ_ALARM_EDGE]);
+        }
+    }
+}
+
+static void ftrtc011_timer_rebase(Ftrtc011State *s)
+{
+    int64_t ticks = get_ticks_per_sec();
+    int64_t elapsed = RTC_REG32(s, REG_SEC)
+                    + (60LL * RTC_REG32(s, REG_MIN))
+                    + (3600LL * RTC_REG32(s, REG_HOUR))
+                    + (86400LL * RTC_REG32(s, REG_DAY));
+
+    s->rtc_base  = elapsed;
+    s->rtc_start = qemu_get_clock_ns(rtc_clock);
+    /* adjust to the beginning of the current second */
+    s->rtc_start = s->rtc_start - (s->rtc_start % ticks);
+}
+
+static void ftrtc011_timer_update(Ftrtc011State *s)
+{
+    int64_t elapsed;
+    uint8_t sec, min, hr;
+    uint32_t day;
+
+    /* check if RTC is enabled */
+    if (!(RTC_REG32(s, REG_CR) & CR_EN)) {
+        return;
+    }
+
+    /*
+     * 2013.03.11 Kuo-Jung Su
+     * Under QTest, in the very beginning of test,
+     * qemu_get_clock_ns(rtc_clock) would somehow jumps
+     * from 0 sec into 436474 sec.
+     * So I have to apply this simple work-aroud to make it work.
+     */
+    if (!s->rtc_start) {
+        ftrtc011_timer_rebase(s);
+    }
+
+    /*
+     * Although the timer is supposed to tick per second,
+     * there is no guarantee that the tick interval is exactly
+     * 1 and only 1 second.
+     */
+    elapsed = s->rtc_base
+            + (qemu_get_clock_ns(rtc_clock) - s->rtc_start) / 1000000000LL;
+    day = (uint32_t)(elapsed / 86400LL);
+    elapsed %= 86400LL;
+    hr = (uint8_t)(elapsed / 3600LL);
+    elapsed %= 3600LL;
+    min = (uint8_t)(elapsed / 60LL);
+    elapsed %= 60LL;
+    sec = elapsed;
+
+    /* sec interrupt */
+    if ((RTC_REG32(s, REG_SEC) != sec)
+        && (RTC_REG32(s, REG_CR) & CR_INTR_SEC)) {
+        RTC_REG32(s, REG_ISR) |= ISR_SEC;
+    }
+    /* min interrupt */
+    if ((RTC_REG32(s, REG_MIN) != min)
+        && (RTC_REG32(s, REG_CR) & CR_INTR_MIN)) {
+        RTC_REG32(s, REG_ISR) |= ISR_MIN;
+    }
+    /* hr interrupt */
+    if ((RTC_REG32(s, REG_HOUR) != hr)
+        && (RTC_REG32(s, REG_CR) & CR_INTR_HOUR)) {
+        RTC_REG32(s, REG_ISR) |= ISR_HOUR;
+    }
+    /* day interrupt */
+    if ((RTC_REG32(s, REG_DAY) != day)
+        && (RTC_REG32(s, REG_CR) & CR_INTR_DAY)) {
+        RTC_REG32(s, REG_ISR) |= ISR_DAY;
+    }
+
+    /* update RTC registers */
+    RTC_REG32(s, REG_SEC) = (uint8_t)sec;
+    RTC_REG32(s, REG_MIN) = (uint8_t)min;
+    RTC_REG32(s, REG_HOUR) = (uint8_t)hr;
+    RTC_REG32(s, REG_DAY) = (uint16_t)day;
+
+    /* alarm interrupt */
+    if (RTC_REG32(s, REG_CR) & CR_INTR_ALARM) {
+        if ((RTC_REG32(s, REG_SEC)
+            | (RTC_REG32(s, REG_MIN) << 8)
+            | (RTC_REG32(s, REG_HOUR) << 16)) ==
+                (RTC_REG32(s, REG_ALARM_SEC)
+                | (RTC_REG32(s, REG_ALARM_MIN) << 8)
+                | (RTC_REG32(s, REG_ALARM_HOUR) << 16))) {
+            RTC_REG32(s, REG_ISR) |= ISR_ALARM;
+        }
+    }
+
+    /* set interrupt signal */
+    ftrtc011_update_irq(s);
+}
+
+static void ftrtc011_timer_resche(Ftrtc011State *s)
+{
+    uint32_t cr = RTC_REG32(s, REG_CR);
+    int64_t  ticks = get_ticks_per_sec();
+    int64_t  timeout = 0;
+
+    if ((cr & CR_EN) && (cr & CR_INTR_MASK)) {
+        timeout = qemu_get_clock_ns(rtc_clock) + ticks;
+    }
+
+    if (timeout > 0) {
+        qemu_mod_timer(s->qtimer, timeout);
+    }
+}
+
+static uint64_t
+ftrtc011_mem_read(void *opaque, hwaddr addr, unsigned size)
+{
+    Ftrtc011State *s = FTRTC011(opaque);
+    uint32_t ret = 0;
+
+    switch (addr) {
+    case REG_SEC ... REG_DAY:
+        ftrtc011_timer_update(s);
+        /* fall-through */
+    case REG_ALARM_SEC ... REG_ISR:
+        ret = s->regs[addr / 4];
+        break;
+    case REG_REVR:
+        ret = 0x00010000;  /* rev. 1.0.0 */
+        break;
+    case REG_CURRENT:
+        ftrtc011_timer_update(s);
+        ret |= RTC_REG32(s, REG_DAY) << 17;
+        ret |= RTC_REG32(s, REG_HOUR) << 12;
+        ret |= RTC_REG32(s, REG_MIN) << 6;
+        ret |= RTC_REG32(s, REG_SEC);
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "ftrtc011: undefined memory access@%#" HWADDR_PRIx "\n", addr);
+        break;
+    }
+
+    return ret;
+}
+
+static void
+ftrtc011_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+    Ftrtc011State *s = FTRTC011(opaque);
+
+    switch (addr) {
+    case REG_ALARM_SEC ... REG_ALARM_HOUR:
+        /* fall-through */
+    case REG_WSEC ... REG_WHOUR:
+        s->regs[addr / 4] = (uint32_t)val & 0xff;
+        break;
+    case REG_WDAY:
+        s->regs[addr / 4] = (uint32_t)val & 0xffff;
+        break;
+    case REG_CR:
+        /* check if RTC is enabled */
+        if (val & CR_EN) {
+            /* update the RTC counter with the user supplied values */
+            if (val & CR_LOAD) {
+                RTC_REG32(s, REG_SEC) = RTC_REG32(s, REG_WSEC);
+                RTC_REG32(s, REG_MIN) = RTC_REG32(s, REG_WMIN);
+                RTC_REG32(s, REG_HOUR) = RTC_REG32(s, REG_WHOUR);
+                RTC_REG32(s, REG_DAY) = RTC_REG32(s, REG_WDAY);
+                val &= ~CR_LOAD;
+                ftrtc011_timer_rebase(s);
+            } else if (!(RTC_REG32(s, REG_CR) & CR_EN)) {
+                ftrtc011_timer_rebase(s);
+            }
+        } else {
+            qemu_del_timer(s->qtimer);
+        }
+        RTC_REG32(s, REG_CR) = (uint32_t)val;
+        /* reschedule a RTC timer update */
+        ftrtc011_timer_resche(s);
+        break;
+    case REG_ISR:
+        RTC_REG32(s, REG_ISR) &= ~((uint32_t)val);
+        ftrtc011_update_irq(s);
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "ftrtc011: undefined memory access@%#" HWADDR_PRIx "\n", addr);
+        break;
+    }
+
+    if (RTC_REG32(s, REG_ALARM_SEC) > 59
+        || RTC_REG32(s, REG_ALARM_MIN) > 59
+        || RTC_REG32(s, REG_ALARM_HOUR) > 23
+        || RTC_REG32(s, REG_WSEC) > 59
+        || RTC_REG32(s, REG_WMIN) > 59
+        || RTC_REG32(s, REG_WHOUR) > 23) {
+        goto werr;
+    }
+
+    return;
+
+werr:
+    qemu_log_mask(LOG_GUEST_ERROR,
+            "ftrtc011: %u is an invalid value to reg@0x%02x.\n",
+            (uint32_t)val, (uint32_t)addr);
+}
+
+static const MemoryRegionOps mmio_ops = {
+    .read  = ftrtc011_mem_read,
+    .write = ftrtc011_mem_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4
+    }
+};
+
+static void ftrtc011_timer_tick(void *opaque)
+{
+    Ftrtc011State *s = FTRTC011(opaque);
+    ftrtc011_timer_update(s);
+    ftrtc011_timer_resche(s);
+}
+
+static void ftrtc011_reset(DeviceState *ds)
+{
+    Ftrtc011State *s = FTRTC011(SYS_BUS_DEVICE(ds));
+
+    qemu_del_timer(s->qtimer);
+    memset(s->regs, 0, sizeof(s->regs));
+    ftrtc011_update_irq(s);
+}
+
+static void ftrtc011_save(QEMUFile *f, void *opaque)
+{
+    int i;
+    Ftrtc011State *s = FTRTC011(opaque);
+
+    for (i = 0; i < sizeof(s->regs) / 4; ++i) {
+        qemu_put_be32(f, s->regs[i]);
+    }
+}
+
+static int ftrtc011_load(QEMUFile *f, void *opaque, int version_id)
+{
+    int i;
+    Ftrtc011State *s = FTRTC011(opaque);
+
+    for (i = 0; i < sizeof(s->regs) / 4; ++i) {
+        s->regs[i] = qemu_get_be32(f);
+    }
+
+    return 0;
+}
+
+static void ftrtc011_realize(DeviceState *dev, Error **errp)
+{
+    Ftrtc011State *s = FTRTC011(dev);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+
+    s->qtimer = qemu_new_timer_ns(rtc_clock, ftrtc011_timer_tick, s);
+
+    memory_region_init_io(&s->mmio,
+                          &mmio_ops,
+                          s,
+                          TYPE_FTRTC011,
+                          0x1000);
+    sysbus_init_mmio(sbd, &s->mmio);
+    sysbus_init_irq(sbd, &s->irq[IRQ_ALARM_LEVEL]);
+    sysbus_init_irq(sbd, &s->irq[IRQ_ALARM_EDGE]);
+    sysbus_init_irq(sbd, &s->irq[IRQ_SEC]);
+    sysbus_init_irq(sbd, &s->irq[IRQ_MIN]);
+    sysbus_init_irq(sbd, &s->irq[IRQ_HOUR]);
+    sysbus_init_irq(sbd, &s->irq[IRQ_DAY]);
+
+    register_savevm(&sbd->qdev, TYPE_FTRTC011, -1, 0,
+                    ftrtc011_save, ftrtc011_load, s);
+}
+
+static const VMStateDescription vmstate_ftrtc011 = {
+    .name = TYPE_FTRTC011,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(regs, Ftrtc011State, CFG_REGSIZE),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void ftrtc011_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->vmsd    = &vmstate_ftrtc011;
+    dc->reset   = ftrtc011_reset;
+    dc->realize = ftrtc011_realize;
+    dc->no_user = 1;
+}
+
+static const TypeInfo ftrtc011_info = {
+    .name           = TYPE_FTRTC011,
+    .parent         = TYPE_SYS_BUS_DEVICE,
+    .instance_size  = sizeof(Ftrtc011State),
+    .class_init     = ftrtc011_class_init,
+};
+
+static void ftrtc011_register_types(void)
+{
+    type_register_static(&ftrtc011_info);
+}
+
+type_init(ftrtc011_register_types)
diff --git a/hw/ftrtc011.h b/hw/ftrtc011.h
new file mode 100644
index 0000000..6e23d5e
--- /dev/null
+++ b/hw/ftrtc011.h
@@ -0,0 +1,53 @@
+/*
+ * QEMU model of the FTRTC011 RTC Timer
+ *
+ * Copyright (C) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This file is licensed under GNU GPL v2+.
+ */
+#ifndef HW_ARM_FTRTC011_H
+#define HW_ARM_FTRTC011_H
+
+#include "qemu/bitops.h"
+
+/* Hardware registers */
+#define REG_SEC         0x00
+#define REG_MIN         0x04
+#define REG_HOUR        0x08
+#define REG_DAY         0x0C
+
+#define REG_ALARM_SEC   0x10    /* Alarm: sec */
+#define REG_ALARM_MIN   0x14    /* Alarm: min */
+#define REG_ALARM_HOUR  0x18    /* Alarm: hour */
+
+#define REG_CR          0x20    /* Control Register */
+#define REG_WSEC        0x24    /* Write SEC Register */
+#define REG_WMIN        0x28    /* Write MIN Register */
+#define REG_WHOUR       0x2C    /* Write HOUR Register */
+#define REG_WDAY        0x30    /* Write DAY Register */
+#define REG_ISR         0x34    /* Interrupt Status Register */
+
+#define REG_REVR        0x3C    /* Revision Register */
+#define REG_CURRENT     0x44    /* Group-up day/hour/min/sec as a register */
+
+#define CR_LOAD         BIT(6)  /* Update counters by Wxxx registers */
+#define CR_INTR_ALARM   BIT(5)  /* Alarm interrupt enabled */
+#define CR_INTR_DAY     BIT(4)  /* DDAY interrupt enabled */
+#define CR_INTR_HOUR    BIT(3)  /* HOUR interrupt enabled */
+#define CR_INTR_MIN     BIT(2)  /* MIN interrupt enabled */
+#define CR_INTR_SEC     BIT(1)  /* SEC interrupt enabled */
+#define CR_INTR_MASK    (CR_INTR_SEC | CR_INTR_MIN \
+                        | CR_INTR_HOUR | CR_INTR_DAY | CR_INTR_ALARM)
+#define CR_EN           BIT(0)  /* RTC enabled */
+
+#define ISR_LOAD        BIT(5)  /* CR_LOAD finished (no interrupt occurs) */
+#define ISR_ALARM       BIT(4)
+#define ISR_DAY         BIT(3)
+#define ISR_HOUR        BIT(2)
+#define ISR_MIN         BIT(1)
+#define ISR_SEC         BIT(0)
+#define ISR_MASK        (ISR_SEC | ISR_MIN \
+                        | ISR_HOUR | ISR_DAY | ISR_ALARM | ISR_LOAD)
+
+#endif
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH v9 09/24] tests: add QTest for FTRTC011
  2013-03-25 12:09 [Qemu-devel] [PATCH v9 00/24] hw/arm: add Faraday A369 SoC platform support Kuo-Jung Su
                   ` (7 preceding siblings ...)
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 08/24] hw/arm: add FTRTC011 RTC " Kuo-Jung Su
@ 2013-03-25 12:09 ` Kuo-Jung Su
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 10/24] hw/arm: add FTDMAC020 AHB DMA support Kuo-Jung Su
                   ` (14 subsequent siblings)
  23 siblings, 0 replies; 35+ messages in thread
From: Kuo-Jung Su @ 2013-03-25 12:09 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, i.mitsyanko, Wei-Ren Chen, Blue Swirl, Paul Brook,
	Kuo-Jung Su, Paolo Bonzini, Andreas, fred.konrad

From: Kuo-Jung Su <dantesu@faraday-tech.com>

The FTRTC011 QEMU model is implemented without calender functions.
It acts in counter mode only, all the time & timezone conversion
relies on the c runtime library. (i.e. mktime(), localtime() ...etc)

Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
---
 tests/Makefile        |    3 +
 tests/ftrtc011-test.c |  410 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 413 insertions(+)
 create mode 100644 tests/ftrtc011-test.c

diff --git a/tests/Makefile b/tests/Makefile
index 567e36e..bc75f2b 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -75,6 +75,8 @@ gcov-files-sparc-y += hw/m48t59.c
 gcov-files-sparc64-y += hw/m48t59.c
 check-qtest-arm-y = tests/tmp105-test$(EXESUF)
 gcov-files-arm-y += hw/tmp105.c
+check-qtest-arm-y = tests/ftrtc011-test$(EXESUF)
+gcov-files-arm-y += hw/ftrtc011.c
 
 GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h tests/test-qmp-commands.h
 
@@ -133,6 +135,7 @@ tests/m48t59-test$(EXESUF): tests/m48t59-test.o
 tests/fdc-test$(EXESUF): tests/fdc-test.o
 tests/hd-geo-test$(EXESUF): tests/hd-geo-test.o
 tests/tmp105-test$(EXESUF): tests/tmp105-test.o
+tests/ftrtc011-test$(EXESUF): tests/ftrtc011-test.o
 
 # QTest rules
 
diff --git a/tests/ftrtc011-test.c b/tests/ftrtc011-test.c
new file mode 100644
index 0000000..15b4f0d
--- /dev/null
+++ b/tests/ftrtc011-test.c
@@ -0,0 +1,410 @@
+/*
+ * QTest testcase for the FTRTC011 real-time clock
+ *
+ * Copyright (c) 2013 Kuo-Jung Su
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+#include "libqtest.h"
+#include "hw/ftrtc011.h"
+
+#include <glib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#define A369_FTRTC011_BASE      0x92100000
+#define A369_FTRTC011_IRQ_ALARM 42  /* edge triggered */
+#define A369_FTRTC011_IRQ_SEC   43  /* edge triggered */
+#define A369_FTRTC011_IRQ_MIN   44  /* edge triggered */
+#define A369_FTRTC011_IRQ_HOUR  45  /* edge triggered */
+
+#define CFG_BASEYEAR            2010
+
+static time_t ts_base;
+
+static uint32_t rtc_read(uint32_t reg)
+{
+    return readl(A369_FTRTC011_BASE + reg);
+}
+
+static void rtc_write(uint32_t reg, uint32_t val)
+{
+    writel(A369_FTRTC011_BASE + reg, val);
+}
+
+static int rtc_get_irq(int irq)
+{
+#if 0   /* It looks to me that get_irq() doesn't work well
+         * with edge interrupts.
+         */
+    return get_irq(irq);
+#else
+    switch (irq) {
+    case A369_FTRTC011_IRQ_ALARM:
+        return !!(rtc_read(REG_ISR) & ISR_ALARM);
+    case A369_FTRTC011_IRQ_SEC:
+        return !!(rtc_read(REG_ISR) & ISR_SEC);
+    case A369_FTRTC011_IRQ_MIN:
+        return !!(rtc_read(REG_ISR) & ISR_MIN);
+    case A369_FTRTC011_IRQ_HOUR:
+        return !!(rtc_read(REG_ISR) & ISR_HOUR);
+    default:
+        return 0;
+    }
+#endif
+}
+
+static int tm_cmp(struct tm *lhs, struct tm *rhs)
+{
+    time_t a, b;
+    struct tm d1, d2;
+
+    memcpy(&d1, lhs, sizeof(d1));
+    memcpy(&d2, rhs, sizeof(d2));
+
+    a = mktime(&d1);
+    b = mktime(&d2);
+
+    if (a < b) {
+        return -1;
+    } else if (a > b) {
+        return 1;
+    }
+
+    return 0;
+}
+
+static void rtc_start(void)
+{
+    time_t ts;
+    struct tm base;
+
+    if (!ts_base) {
+        base.tm_isdst = -1;
+        base.tm_year  = CFG_BASEYEAR - 1900;
+        base.tm_mon   = 0;
+        base.tm_mday  = 1;
+        base.tm_hour  = 0;
+        base.tm_min   = 0;
+        base.tm_sec   = 0;
+        ts_base = mktime(&base);
+    }
+
+    ts = time(NULL) - ts_base;
+    rtc_write(REG_WDAY, ts / 86400LL);
+    ts %= 86400LL;
+    rtc_write(REG_WHOUR, ts / 3600LL);
+    ts %= 3600LL;
+    rtc_write(REG_WMIN, ts / 60LL);
+    ts %= 60LL;
+    rtc_write(REG_WSEC, ts);
+
+    rtc_write(REG_ISR, ISR_MASK);
+    rtc_write(REG_CR, CR_EN | CR_LOAD | CR_INTR_MASK);
+}
+
+static void rtc_get_datetime(struct tm *date)
+{
+    time_t ts;
+    int64_t sec, min, hour, day;
+
+    if (!ts_base) {
+        fprintf(stderr, "ts_base is not yet initialized!\n");
+        exit(1);
+    }
+
+    sec  = rtc_read(REG_SEC);
+    min  = rtc_read(REG_MIN);
+    hour = rtc_read(REG_HOUR);
+    day  = rtc_read(REG_DAY);
+    ts   = ts_base + (86400LL * day) + (hour * 3600LL) + (min * 60LL) + sec;
+
+    localtime_r(&ts, date);
+}
+
+static void rtc_test_check_time(int wiggle)
+{
+    struct tm start, date[4], end;
+    struct tm *datep;
+    time_t ts;
+
+    rtc_start();
+
+    /*
+     * This check assumes a few things.
+     * First, we cannot guarantee that we get a consistent reading
+     * from the wall clock because we may hit an edge of the clock
+     * while reading.
+     * To work around this, we read four clock readings such that
+     * at least two of them should match.  We need to assume that one
+     * reading is corrupt so we need four readings to ensure that we have at
+     * least two consecutive identical readings
+     *
+     * It's also possible that we'll cross an edge reading the host clock so
+     * simply check to make sure that the clock reading is within the period of
+     * when we expect it to be.
+     */
+
+    ts = time(NULL);
+    localtime_r(&ts, &start);
+
+    rtc_get_datetime(&date[0]);
+    rtc_get_datetime(&date[1]);
+    rtc_get_datetime(&date[2]);
+    rtc_get_datetime(&date[3]);
+
+    ts = time(NULL);
+    localtime_r(&ts, &end);
+
+    if (tm_cmp(&date[0], &date[1]) == 0) {
+        datep = &date[0];
+    } else if (tm_cmp(&date[1], &date[2]) == 0) {
+        datep = &date[1];
+    } else if (tm_cmp(&date[2], &date[3]) == 0) {
+        datep = &date[2];
+    } else {
+        g_assert_not_reached();
+    }
+
+    if (!(tm_cmp(&start, datep) <= 0 && tm_cmp(datep, &end) <= 0)) {
+        time_t t, s;
+
+        start.tm_isdst = datep->tm_isdst;
+
+        t = mktime(datep);
+        s = mktime(&start);
+        if (t < s) {
+            g_test_message("RTC is %ld second(s) behind wall-clock\n",
+                           (s - t));
+        } else {
+            g_test_message("RTC is %ld second(s) ahead of wall-clock\n",
+                           (t - s));
+        }
+
+        g_assert_cmpint(ABS(t - s), <=, wiggle);
+    }
+}
+
+static int wiggle = 2;
+
+static void rtc_test_intr_alarm(void)
+{
+    time_t ts;
+    int i;
+
+    rtc_start();
+
+    g_assert(!rtc_get_irq(A369_FTRTC011_IRQ_ALARM));
+
+    /* schedule an alarm at 2 sec later */
+    ts = time(NULL) + 2 - ts_base;
+    ts %= 86400LL;
+    rtc_write(REG_ALARM_HOUR, ts / 3600LL);
+    ts %= 3600LL;
+    rtc_write(REG_ALARM_MIN,  ts / 60LL);
+    ts %= 60LL;
+    rtc_write(REG_ALARM_SEC,  ts);
+
+    for (i = 0; i < 2 + wiggle; i++) {
+        /*
+         * Since it's an edge interrupt (pulse),
+         * is it always possible to get the high level signal observed?
+         */
+        if (rtc_get_irq(A369_FTRTC011_IRQ_ALARM)) {
+            break;
+        }
+
+        clock_step(1000000000LL);
+    }
+
+    g_assert(i < 2 + wiggle);
+}
+
+static void rtc_test_intr_sec(void)
+{
+    int i;
+
+    rtc_start();
+
+    g_assert(!rtc_get_irq(A369_FTRTC011_IRQ_SEC));
+
+    for (i = 2; i > 0; i--) {
+
+        if (rtc_get_irq(A369_FTRTC011_IRQ_SEC)) {
+            break;
+        }
+
+        clock_step(1000000000);
+    }
+
+    g_assert(i > 0);
+}
+
+static void rtc_test_intr_min(void)
+{
+    int i;
+
+    rtc_start();
+
+    g_assert(!get_irq(A369_FTRTC011_IRQ_MIN));
+
+    for (i = 60 + 2; i > 0; i--) {
+
+        if (rtc_get_irq(A369_FTRTC011_IRQ_MIN)) {
+            break;
+        }
+
+        clock_step(1000000000);
+    }
+
+    g_assert(i > 0);
+}
+
+static void rtc_test_intr_hour(void)
+{
+    int i;
+
+    rtc_start();
+
+    g_assert(!get_irq(A369_FTRTC011_IRQ_HOUR));
+
+    for (i = 3600 + 2; i > 0; i--) {
+
+        if (rtc_get_irq(A369_FTRTC011_IRQ_HOUR)) {
+            break;
+        }
+
+        clock_step(1000000000);
+    }
+
+    g_assert(i > 0);
+}
+
+static void set_time(int h, int m, int s)
+{
+    rtc_write(REG_WDAY, rtc_read(REG_DAY));
+    rtc_write(REG_WHOUR, h);
+    rtc_write(REG_WMIN, m);
+    rtc_write(REG_WSEC, s);
+
+    /* update rtc counter */
+    rtc_write(REG_CR, rtc_read(REG_CR) | CR_LOAD);
+}
+
+#define assert_time(h, m, s) \
+    do { \
+        g_assert_cmpint(rtc_read(REG_HOUR), ==, h); \
+        g_assert_cmpint(rtc_read(REG_MIN), ==, m); \
+        g_assert_cmpint(rtc_read(REG_SEC), ==, s); \
+    } while (0)
+
+static void rtc_test_basic_24h(void)
+{
+    rtc_start();
+
+    /* set decimal 24 hour mode */
+    set_time(9, 59, 0);
+    clock_step(1000000000LL);
+    assert_time(9, 59, 1);
+    clock_step(59000000000LL);
+    assert_time(10, 0, 0);
+
+    /* test hour wraparound */
+    set_time(9, 59, 0);
+    clock_step(60000000000LL);
+    assert_time(10, 0, 0);
+
+    /* test day wraparound */
+    set_time(23, 59, 0);
+    clock_step(60000000000LL);
+    assert_time(0, 0, 0);
+}
+
+static void rtc_test_set_year(void)
+{
+    char t[256];
+    int i, year;
+    time_t ts;
+    struct tm date;
+
+    rtc_start();
+
+    for (i = 0; i < 1000; ++i) {
+        year = CFG_BASEYEAR + g_test_rand_int_range(0, 20);
+
+        date.tm_isdst = -1;
+        date.tm_year  = year - 1900;
+        date.tm_mon   = g_test_rand_int_range(0, 11);
+        date.tm_mday  = g_test_rand_int_range(1, 25);
+        date.tm_hour  = g_test_rand_int_range(0, 23);
+        date.tm_min   = g_test_rand_int_range(0, 59);
+        date.tm_sec   = g_test_rand_int_range(0, 59);
+
+        asctime_r(&date, t);
+
+        ts = mktime(&date) - ts_base;
+        rtc_write(REG_WDAY, ts / 86400LL);
+        ts %= 86400LL;
+        rtc_write(REG_WHOUR, ts / 3600LL);
+        ts %= 3600LL;
+        rtc_write(REG_WMIN, ts / 60LL);
+        ts %= 60LL;
+        rtc_write(REG_WSEC, ts);
+
+        /* update rtc counter */
+        rtc_write(REG_CR, rtc_read(REG_CR) | CR_LOAD);
+
+        rtc_get_datetime(&date);
+
+        asctime_r(&date, t);
+
+        g_assert(date.tm_year == year - 1900);
+    }
+}
+
+/* success if no crash or abort */
+static void rtc_test_fuzz(void)
+{
+    unsigned int i;
+
+    rtc_start();
+
+    for (i = 0; i < 1000; i++) {
+        uint8_t reg, val;
+
+        reg = (uint8_t)g_test_rand_int_range(0x00, 0x34) & 0xfc;
+        val = (uint8_t)g_test_rand_int_range(0, 256);
+
+        rtc_write(reg, val);
+        rtc_read(reg);
+    }
+}
+
+int main(int argc, char **argv)
+{
+    QTestState *s = NULL;
+    int ret;
+
+    g_test_init(&argc, &argv, NULL);
+
+    s = qtest_start("-M a369 -nographic -kernel /dev/zero -rtc clock=vm");
+    qtest_irq_intercept_in(s, "ftrtc011");
+    qtest_add_func("/rtc/check-time", rtc_test_check_time);
+    qtest_add_func("/rtc/interrupt/alarm", rtc_test_intr_alarm);
+    qtest_add_func("/rtc/interrupt/sec", rtc_test_intr_sec);
+    qtest_add_func("/rtc/interrupt/min", rtc_test_intr_min);
+    qtest_add_func("/rtc/interrupt/hour", rtc_test_intr_hour);
+    qtest_add_func("/rtc/basic-24h", rtc_test_basic_24h);
+    qtest_add_func("/rtc/set-year", rtc_test_set_year);
+    qtest_add_func("/rtc/fuzz-registers", rtc_test_fuzz);
+    ret = g_test_run();
+
+    if (s) {
+        qtest_quit(s);
+    }
+
+    return ret;
+}
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH v9 10/24] hw/arm: add FTDMAC020 AHB DMA support
  2013-03-25 12:09 [Qemu-devel] [PATCH v9 00/24] hw/arm: add Faraday A369 SoC platform support Kuo-Jung Su
                   ` (8 preceding siblings ...)
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 09/24] tests: add QTest for FTRTC011 Kuo-Jung Su
@ 2013-03-25 12:09 ` Kuo-Jung Su
  2013-03-29  0:22   ` Peter Crosthwaite
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 11/24] hw/arm: add FTAPBBRG020 APB " Kuo-Jung Su
                   ` (13 subsequent siblings)
  23 siblings, 1 reply; 35+ messages in thread
From: Kuo-Jung Su @ 2013-03-25 12:09 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, i.mitsyanko, Blue Swirl, Paul Brook, Kuo-Jung Su,
	Andreas, fred.konrad

From: Kuo-Jung Su <dantesu@faraday-tech.com>

The Faraday FTDMAC020 provides eight configurable
channels for the memory-to-memory, memory-to-peripheral,
peripheral-to-peripheral, and peripheral-to-memory transfers.

Each DMA channel supports chain transfer and can be programmed
to one of the 16 handshaking channels in the hardware handshake mode.

The main function of the hardware handshake mode is to provide an
indication of the device status. Users can also disable the hardware
handshake mode by programming the register when a DMA transfer is not
necessary of referring to the handshaking channels.

Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
---
 hw/arm/Makefile.objs    |    2 +-
 hw/arm/ftplat_a369soc.c |   14 ++
 hw/ftdmac020.c          |  599 +++++++++++++++++++++++++++++++++++++++++++++++
 hw/ftdmac020.h          |  107 +++++++++
 4 files changed, 721 insertions(+), 1 deletion(-)
 create mode 100644 hw/ftdmac020.c
 create mode 100644 hw/ftdmac020.h

diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 6a41b21..6510c51 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -25,7 +25,7 @@ obj-y += strongarm.o
 obj-y += imx_serial.o imx_ccm.o imx_timer.o imx_avic.o
 obj-$(CONFIG_KVM) += kvm/arm_gic.o
 obj-y += ftintc020.o ftahbc020.o ftddrii030.o ftpwmtmr010.o ftwdt010.o \
-                ftrtc011.o
+                ftrtc011.o ftdmac020.o
 
 obj-y := $(addprefix ../,$(obj-y))
 
diff --git a/hw/arm/ftplat_a369soc.c b/hw/arm/ftplat_a369soc.c
index bd696c4..59e2c61 100644
--- a/hw/arm/ftplat_a369soc.c
+++ b/hw/arm/ftplat_a369soc.c
@@ -168,6 +168,20 @@ static void a369soc_chip_init(FaradaySoCState *s)
     sysbus_connect_irq(SYS_BUS_DEVICE(ds), 3, s->pic[44]);
     /* Hour (Edge) */
     sysbus_connect_irq(SYS_BUS_DEVICE(ds), 4, s->pic[45]);
+
+    /* ftdmac020 */
+    s->hdma[0] = sysbus_create_varargs("ftdmac020",
+                                       0x90300000,
+                                       s->pic[0],  /* ALL (NC in A369) */
+                                       s->pic[15], /* TC */
+                                       s->pic[16], /* ERR */
+                                       NULL);
+    s->hdma[1] = sysbus_create_varargs("ftdmac020",
+                                       0x96100000,
+                                       s->pic[0],  /* ALL (NC in A369) */
+                                       s->pic[17], /* TC */
+                                       s->pic[18], /* ERR */
+                                       NULL);
 }
 
 static void a369soc_realize(DeviceState *dev, Error **errp)
diff --git a/hw/ftdmac020.c b/hw/ftdmac020.c
new file mode 100644
index 0000000..81b49b2
--- /dev/null
+++ b/hw/ftdmac020.c
@@ -0,0 +1,599 @@
+/*
+ * QEMU model of the FTDMAC020 DMA Controller
+ *
+ * Copyright (C) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This file is licensed under GNU GPL v2+.
+ *
+ * Note: The FTDMAC020 descending address mode is not implemented.
+ */
+
+#include "hw/sysbus.h"
+#include "sysemu/dma.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/blockdev.h"
+
+#include "hw/ftdmac020.h"
+
+#define TYPE_FTDMAC020    "ftdmac020"
+
+enum ftdmac020_irqpin {
+    IRQ_ALL = 0,
+    IRQ_TC,
+    IRQ_ERR,
+};
+
+typedef struct Ftdmac020State Ftdmac020State;
+
+/**
+ * struct Ftdmac020LLD - hardware link list descriptor.
+ * @src: source physical address
+ * @dst: destination physical addr
+ * @next: phsical address to the next link list descriptor
+ * @ctrl: control field
+ * @size: transfer size
+ *
+ * should be word aligned
+ */
+typedef struct Ftdmac020LLD {
+    uint32_t src;
+    uint32_t dst;
+    uint32_t next;
+    uint32_t ctrl;
+    uint32_t size;
+} Ftdmac020LLD;
+
+typedef struct Ftdmac020Chan {
+    Ftdmac020State *chip;
+
+    int id;
+    int burst;
+    int llp_cnt;
+    int src_bw;
+    int src_stride;
+    int dst_bw;
+    int dst_stride;
+
+    /* HW register cache */
+    uint32_t ccr;
+    uint32_t cfg;
+    uint32_t src;
+    uint32_t dst;
+    uint32_t llp;
+    uint32_t len;
+} Ftdmac020Chan;
+
+typedef struct Ftdmac020State {
+    /*< private >*/
+    SysBusDevice parent;
+
+    /*< public >*/
+    MemoryRegion iomem;
+    qemu_irq irq[3];
+
+    Ftdmac020Chan chan[8];
+    qemu_irq      ack[16];
+    uint32_t      req;
+
+    int busy;    /* Busy Channel ID */
+    int bh_owner;
+    QEMUBH *bh;
+    DMAContext *dma;
+
+    /* HW register cache */
+    uint32_t tcisr;
+    uint32_t eaisr;
+    uint32_t tcsr;
+    uint32_t easr;
+    uint32_t cesr;
+    uint32_t cbsr;
+    uint32_t csr;
+    uint32_t sync;
+} Ftdmac020State;
+
+#define FTDMAC020(obj) \
+    OBJECT_CHECK(Ftdmac020State, obj, TYPE_FTDMAC020)
+
+static void ftdmac020_update_irq(Ftdmac020State *s)
+{
+    uint32_t tc, err;
+
+    /* 1. Checking TC interrupts */
+    tc = s->tcisr & 0xff;
+    qemu_set_irq(s->irq[IRQ_TC], tc ? 1 : 0);
+
+    /* 2. Checking Error/Abort interrupts */
+    err = s->eaisr & 0x00ff00ff;
+    qemu_set_irq(s->irq[IRQ_ERR], err ? 1 : 0);
+
+    /* 3. Checking interrupt summary (TC | Error | Abort) */
+    qemu_set_irq(s->irq[IRQ_ALL], (tc || err) ? 1 : 0);
+}
+
+static void ftdmac020_chan_ccr_decode(Ftdmac020Chan *c)
+{
+    uint32_t tmp;
+
+    /* 1. decode burst size */
+    tmp = extract32(c->ccr, 16, 3);
+    c->burst  = 1 << (tmp ? tmp + 1 : 0);
+
+    /* 2. decode source width */
+    tmp = extract32(c->ccr, 11, 2);
+    c->src_bw = 8 << tmp;
+
+    /* 3. decode destination width */
+    tmp = extract32(c->ccr, 8, 2);
+    c->dst_bw = 8 << tmp;
+
+    /* 4. decode source address stride */
+    tmp = extract32(c->ccr, 5, 2);
+    if (tmp == 2) {
+        c->src_stride = 0;
+    } else {
+        c->src_stride = c->src_bw >> 3;
+    }
+
+    /* 5. decode destination address stride */
+    tmp = extract32(c->ccr, 3, 2);
+    if (tmp == 2) {
+        c->dst_stride = 0;
+    } else {
+        c->dst_stride = c->dst_bw >> 3;
+    }
+}
+
+static void ftdmac020_chan_start(Ftdmac020Chan *c)
+{
+    Ftdmac020State *s = c->chip;
+    Ftdmac020LLD desc;
+    hwaddr src, dst;
+    uint8_t buf[4096] __attribute__ ((aligned (8)));
+    int i, len, stride, src_hs, dst_hs;
+
+    if (!(c->ccr & CCR_START)) {
+        return;
+    }
+
+    s->busy = c->id;
+
+    /* DMA src/dst address */
+    src = c->src;
+    dst = c->dst;
+
+    /* DMA hardware handshake id */
+    src_hs = (c->cfg & CFG_SRC_HANDSHAKE_EN) ? extract32(c->cfg, 3, 4) : -1;
+    dst_hs = (c->cfg & CFG_DST_HANDSHAKE_EN) ? extract32(c->cfg, 9, 4) : -1;
+
+    /* DMA src/dst sanity check */
+    if (cpu_physical_memory_is_io(src) && c->src_stride) {
+        fprintf(stderr,
+            "ftdmac020: src is an I/O device with non-fixed address?\n");
+        abort();
+    }
+    if (cpu_physical_memory_is_io(dst) && c->dst_stride) {
+        fprintf(stderr,
+            "ftdmac020: dst is an I/O device with non-fixed address?\n");
+        abort();
+    }
+
+    while (c->len > 0) {
+        /*
+         * Postpone this DMA action
+         * if the corresponding dma request is not asserted
+         */
+        if ((src_hs >= 0) && !(s->req & BIT(src_hs))) {
+            break;
+        }
+        if ((dst_hs >= 0) && !(s->req & BIT(dst_hs))) {
+            break;
+        }
+
+        len = MIN(sizeof(buf), c->burst * (c->src_bw >> 3));
+
+        /* load data from source into local buffer */
+        if (c->src_stride) {
+            dma_memory_read(s->dma, src, buf, len);
+            src += len;
+        } else {
+            stride = c->src_bw >> 3;
+            for (i = 0; i < len; i += stride) {
+                dma_memory_read(s->dma, src, buf + i, stride);
+            }
+        }
+
+        /* DMA Hardware Handshake */
+        if (src_hs >= 0) {
+            qemu_set_irq(s->ack[src_hs], 1);
+        }
+
+        /* store data into destination from local buffer */
+        if (c->dst_stride) {
+            dma_memory_write(s->dma, dst, buf, len);
+            dst += len;
+        } else {
+            stride = c->dst_bw >> 3;
+            for (i = 0; i < len; i += stride) {
+                dma_memory_write(s->dma, dst, buf + i, stride);
+            }
+        }
+
+        /* DMA Hardware Handshake */
+        if (dst_hs >= 0) {
+            qemu_set_irq(s->ack[dst_hs], 1);
+        }
+
+        /* update the channel transfer size */
+        c->len -= len / (c->src_bw >> 3);
+
+        if (c->len == 0) {
+            /* update the channel transfer status */
+            if (!(c->ccr & CCR_MASK_TC)) {
+                s->tcsr |= BIT(c->id);
+                if (!(c->cfg & CFG_MASK_TCI)) {
+                    s->tcisr |= BIT(c->id);
+                }
+                ftdmac020_update_irq(s);
+            }
+            /* try to load next lld */
+            if (c->llp) {
+                c->llp_cnt += 1;
+                dma_memory_read(s->dma, c->llp, &desc, sizeof(desc));
+
+                desc.src  = le32_to_cpu(desc.src);
+                desc.dst  = le32_to_cpu(desc.dst);
+                desc.next = le32_to_cpu(desc.next);
+                desc.size = le32_to_cpu(desc.size);
+                desc.ctrl = le32_to_cpu(desc.ctrl);
+
+                c->src = desc.src;
+                c->dst = desc.dst;
+                c->llp = desc.next & 0xfffffffc;
+                c->len = desc.size & 0x003fffff;
+                c->ccr = (c->ccr & 0x78f8c081)
+                       | (extract32(desc.ctrl, 29, 3) << 24)
+                       | ((desc.ctrl & BIT(28)) ? CCR_MASK_TC : 0)
+                       | (extract32(desc.ctrl, 25, 3) << 11)
+                       | (extract32(desc.ctrl, 22, 3) << 8)
+                       | (extract32(desc.ctrl, 20, 2) << 5)
+                       | (extract32(desc.ctrl, 18, 2) << 3)
+                       | (extract32(desc.ctrl, 16, 2) << 1);
+                ftdmac020_chan_ccr_decode(c);
+
+                src = c->src;
+                dst = c->dst;
+            } else {
+                /* clear dma start bit */
+                c->ccr &= ~CCR_START;
+            }
+        }
+    }
+
+    /* update dma src/dst address */
+    c->src = src;
+    c->dst = dst;
+
+    s->busy = -1;
+}
+
+static void ftdmac020_chan_reset(Ftdmac020Chan *c)
+{
+    c->ccr = 0;
+    c->cfg = 0;
+    c->src = 0;
+    c->dst = 0;
+    c->llp = 0;
+    c->len = 0;
+}
+
+static void ftdmac020_bh(void *opaque)
+{
+    Ftdmac020State *s = FTDMAC020(opaque);
+    Ftdmac020Chan  *c = NULL;
+    int i, jobs, done;
+
+    ++s->bh_owner;
+    jobs = 0;
+    done = 0;
+    for (i = 0; i < 8; ++i) {
+        c = s->chan + i;
+        if (c->ccr & CCR_START) {
+            ++jobs;
+            ftdmac020_chan_start(c);
+            if (!(c->ccr & CCR_START)) {
+                ++done;
+            }
+        }
+    }
+    --s->bh_owner;
+
+    /*
+     * Devices those with an infinite FIFO (always ready for R/W)
+     * would trigger a new DMA handshake transaction here.
+     * (i.e. ftnandc021, ftsdc010)
+     */
+    if ((jobs - done) && s->req) {
+        qemu_bh_schedule(s->bh);
+    }
+}
+
+static void ftdmac020_handle_req(void *opaque, int line, int level)
+{
+    Ftdmac020State *s = FTDMAC020(opaque);
+
+    if (level) {
+        /*
+         * Devices those wait for data from externaI/O
+         * would trigger a new DMA handshake transaction here.
+         * (i.e. ftssp010)
+         */
+        if (!(s->req & BIT(line))) {
+            /* a simple workaround for BH reentry issue */
+            if (!s->bh_owner) {
+                qemu_bh_schedule(s->bh);
+            }
+        }
+        s->req |= BIT(line);
+    } else {
+        s->req &= ~BIT(line);
+        qemu_set_irq(s->ack[line], 0);
+    }
+}
+
+static void ftdmac020_chip_reset(Ftdmac020State *s)
+{
+    int i;
+
+    s->tcisr = 0;
+    s->eaisr = 0;
+    s->tcsr = 0;
+    s->easr = 0;
+    s->cesr = 0;
+    s->cbsr = 0;
+    s->csr  = 0;
+    s->sync = 0;
+
+    for (i = 0; i < 8; ++i) {
+        ftdmac020_chan_reset(s->chan + i);
+    }
+
+    /* We can assume our GPIO have been wired up now */
+    for (i = 0; i < 16; ++i) {
+        qemu_set_irq(s->ack[i], 0);
+    }
+    s->req = 0;
+}
+
+static uint64_t ftdmac020_mem_read(void *opaque, hwaddr addr, unsigned size)
+{
+    Ftdmac020State *s = FTDMAC020(opaque);
+    Ftdmac020Chan  *c = NULL;
+    uint32_t i, ret = 0;
+
+    switch (addr) {
+    case REG_ISR:
+        /* 1. Checking TC interrupts */
+        ret |= s->tcisr & 0xff;
+        /* 2. Checking Error interrupts */
+        ret |= s->eaisr & 0xff;
+        /* 3. Checking Abort interrupts */
+        ret |= (s->eaisr >> 16) & 0xff;
+        break;
+    case REG_TCISR:
+        return s->tcisr;
+    case REG_EAISR:
+        return s->eaisr;
+    case REG_TCSR:
+        return s->tcsr;
+    case REG_EASR:
+        return s->easr;
+    case REG_CESR:
+        for (i = 0; i < 8; ++i) {
+            c = s->chan + i;
+            ret |= (c->ccr & CCR_START) ? BIT(i) : 0;
+        }
+        break;
+    case REG_CBSR:
+        return (s->busy > 0) ? BIT(s->busy) : 0;
+    case REG_CSR:
+        return s->csr;
+    case REG_SYNC:
+        return s->sync;
+    case REG_REVISION:
+        /* rev. = 1.13.0 */
+        return 0x00011300;
+    case REG_FEATURE:
+        /* fifo = 32 bytes, support linked list, 8 channels, AHB0 only */
+        return 0x00008105;
+    case REG_CHAN_BASE(0) ... REG_CHAN_BASE(7) + 0x14:
+        c = s->chan + REG_CHAN_ID(addr);
+        switch (addr & 0x1f) {
+        case REG_CHAN_CCR:
+            return c->ccr;
+        case REG_CHAN_CFG:
+            ret = c->cfg;
+            ret |= (s->busy == c->id) ? (1 << 8) : 0;
+            ret |= (c->llp_cnt & 0x0f) << 16;
+            break;
+        case REG_CHAN_SRC:
+            return c->src;
+        case REG_CHAN_DST:
+            return c->dst;
+        case REG_CHAN_LLP:
+            return c->llp;
+        case REG_CHAN_LEN:
+            return c->len;
+        default:
+            qemu_log_mask(LOG_GUEST_ERROR,
+                "ftdmac020: undefined memory access@%#" HWADDR_PRIx "\n",
+                addr);
+            break;
+        }
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "ftdmac020: undefined memory access@%#" HWADDR_PRIx "\n", addr);
+        break;
+    }
+
+    return ret;
+}
+
+static void ftdmac020_mem_write(void    *opaque,
+                                hwaddr   addr,
+                                uint64_t val,
+                                unsigned size)
+{
+    Ftdmac020State *s = FTDMAC020(opaque);
+    Ftdmac020Chan  *c = NULL;
+
+    switch (addr) {
+    case REG_TCCLR:
+        s->tcisr &= ~((uint32_t)val);
+        s->tcsr &= ~((uint32_t)val);
+        ftdmac020_update_irq(s);
+        break;
+    case REG_EACLR:
+        s->eaisr &= ~((uint32_t)val);
+        s->easr &= ~((uint32_t)val);
+        ftdmac020_update_irq(s);
+        break;
+    case REG_CSR:
+        s->csr = (uint32_t)val;
+        break;
+    case REG_SYNC:
+        /* In QEMU, devices are all in the same clock domain
+         * so there is nothing needs to be done.
+         */
+        s->sync = (uint32_t)val;
+        break;
+    case REG_CHAN_BASE(0) ... REG_CHAN_BASE(7) + 0x14:
+        c = s->chan + REG_CHAN_ID(addr);
+        switch (addr & 0x1f) {
+        case REG_CHAN_CCR:
+            if (!(c->ccr & CCR_START) && (val & CCR_START)) {
+                c->llp_cnt = 0;
+            }
+            c->ccr = (uint32_t)val & 0x87FFBFFF;
+            if (c->ccr & CCR_START) {
+                ftdmac020_chan_ccr_decode(c);
+                /* kick-off DMA engine */
+                qemu_bh_schedule(s->bh);
+            }
+            break;
+        case REG_CHAN_CFG:
+            c->cfg = (uint32_t)val & 0x3eff;
+            break;
+        case REG_CHAN_SRC:
+            c->src = (uint32_t)val;
+            break;
+        case REG_CHAN_DST:
+            c->dst = (uint32_t)val;
+            break;
+        case REG_CHAN_LLP:
+            c->llp = (uint32_t)val & 0xfffffffc;
+            break;
+        case REG_CHAN_LEN:
+            c->len = (uint32_t)val & 0x003fffff;
+            break;
+        default:
+            qemu_log_mask(LOG_GUEST_ERROR,
+                "ftdmac020: undefined memory access@%#" HWADDR_PRIx "\n",
+                addr);
+            break;
+        }
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "ftdmac020: undefined memory access@%#" HWADDR_PRIx "\n", addr);
+        break;
+    }
+}
+
+static const MemoryRegionOps mmio_ops = {
+    .read  = ftdmac020_mem_read,
+    .write = ftdmac020_mem_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4
+    }
+};
+
+static void ftdmac020_reset(DeviceState *ds)
+{
+    Ftdmac020State *s = FTDMAC020(SYS_BUS_DEVICE(ds));
+
+    ftdmac020_chip_reset(s);
+}
+
+static void ftdmac020_realize(DeviceState *dev, Error **errp)
+{
+    Ftdmac020State *s = FTDMAC020(dev);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+    int i;
+
+    memory_region_init_io(&s->iomem,
+                          &mmio_ops,
+                          s,
+                          TYPE_FTDMAC020,
+                          0x1000);
+    sysbus_init_mmio(sbd, &s->iomem);
+    for (i = 0; i < 3; ++i) {
+        sysbus_init_irq(sbd, &s->irq[i]);
+    }
+    qdev_init_gpio_in(&sbd->qdev, ftdmac020_handle_req, 16);
+    qdev_init_gpio_out(&sbd->qdev, s->ack, 16);
+
+    s->busy = -1;
+    s->dma = &dma_context_memory;
+    s->bh = qemu_bh_new(ftdmac020_bh, s);
+    for (i = 0; i < 8; ++i) {
+        Ftdmac020Chan *c = s->chan + i;
+        c->id   = i;
+        c->chip = s;
+    }
+}
+
+static const VMStateDescription vmstate_ftdmac020 = {
+    .name = TYPE_FTDMAC020,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(tcisr, Ftdmac020State),
+        VMSTATE_UINT32(eaisr, Ftdmac020State),
+        VMSTATE_UINT32(tcsr, Ftdmac020State),
+        VMSTATE_UINT32(easr, Ftdmac020State),
+        VMSTATE_UINT32(cesr, Ftdmac020State),
+        VMSTATE_UINT32(cbsr, Ftdmac020State),
+        VMSTATE_UINT32(csr, Ftdmac020State),
+        VMSTATE_UINT32(sync, Ftdmac020State),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void ftdmac020_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->vmsd    = &vmstate_ftdmac020;
+    dc->reset   = ftdmac020_reset;
+    dc->realize = ftdmac020_realize;
+    dc->no_user = 1;
+}
+
+static const TypeInfo ftdmac020_info = {
+    .name           = TYPE_FTDMAC020,
+    .parent         = TYPE_SYS_BUS_DEVICE,
+    .instance_size  = sizeof(Ftdmac020State),
+    .class_init     = ftdmac020_class_init,
+};
+
+static void ftdmac020_register_types(void)
+{
+    type_register_static(&ftdmac020_info);
+}
+
+type_init(ftdmac020_register_types)
diff --git a/hw/ftdmac020.h b/hw/ftdmac020.h
new file mode 100644
index 0000000..86ee58c
--- /dev/null
+++ b/hw/ftdmac020.h
@@ -0,0 +1,107 @@
+/*
+ * QEMU model of the FTDMAC020 DMA Controller
+ *
+ * Copyright (C) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This file is licensed under GNU GPL v2+.
+ *
+ * Note: The FTDMAC020 decreasing address mode is not implemented.
+ */
+
+#ifndef HW_ARM_FTDMAC020_H
+#define HW_ARM_FTDMAC020_H
+
+#include "qemu/bitops.h"
+
+#define REG_ISR         0x00    /* Interrupt Status Register */
+#define REG_TCISR       0x04    /* Terminal Count Interrupt Status Register */
+#define REG_TCCLR       0x08    /* Terminal Count Status Clear Register */
+#define REG_EAISR       0x0c    /* Error/Abort Interrupt Status Register */
+#define REG_EACLR       0x10    /* Error/Abort Status Clear Register */
+#define REG_TCSR        0x14    /* Terminal Count Status Register */
+#define REG_EASR        0x18    /* Error/Abort Status Register */
+#define REG_CESR        0x1c    /* Channel Enable Status Register */
+#define REG_CBSR        0x20    /* Channel Busy Status Register */
+#define REG_CSR         0x24    /* Configuration Status Register */
+#define REG_SYNC        0x28    /* Synchronization Register */
+#define REG_REVISION    0x30
+#define REG_FEATURE     0x34
+
+#define REG_CHAN_ID(addr)   (((addr) - 0x100) >> 5)
+#define REG_CHAN_BASE(ch)   (0x100 + ((ch) << 5))
+
+#define REG_CHAN_CCR        0x00
+#define REG_CHAN_CFG        0x04
+#define REG_CHAN_SRC        0x08
+#define REG_CHAN_DST        0x0C
+#define REG_CHAN_LLP        0x10
+#define REG_CHAN_LEN        0x14
+
+/*
+ * Feature register
+ */
+#define FEATURE_NCHAN(f)    (((f) >> 12) & 0xF)
+#define FEATURE_BRIDGE(f)   ((f) & BIT(10))
+#define FEATURE_DUALBUS(f)  ((f) & BIT(9))
+#define FEATURE_LLP(f)      ((f) & BIT(8))
+#define FEATURE_FIFOAW(f)   ((f) & 0xF)
+
+/*
+ * Channel control register
+ */
+#define CCR_START           BIT(0)
+#define CCR_DST_M1          BIT(1)  /* dst is a slave device of AHB1 */
+#define CCR_SRC_M1          BIT(2)  /* src is a slave device of AHB1 */
+#define CCR_DST_INC         (0 << 3)/* dst addr: next = curr++ */
+#define CCR_DST_DEC         (1 << 3)/* dst addr: next = curr-- */
+#define CCR_DST_FIXED       (2 << 3)
+#define CCR_SRC_INC         (0 << 5)/* src addr: next = curr++ */
+#define CCR_SRC_DEC         (1 << 5)/* src addr: next = curr-- */
+#define CCR_SRC_FIXED       (2 << 5)
+#define CCR_HANDSHAKE       BIT(7)  /* DMA HW handshake enabled */
+#define CCR_DST_WIDTH_8     (0 << 8)
+#define CCR_DST_WIDTH_16    (1 << 8)
+#define CCR_DST_WIDTH_32    (2 << 8)
+#define CCR_DST_WIDTH_64    (3 << 8)
+#define CCR_SRC_WIDTH_8     (0 << 11)
+#define CCR_SRC_WIDTH_16    (1 << 11)
+#define CCR_SRC_WIDTH_32    (2 << 11)
+#define CCR_SRC_WIDTH_64    (3 << 11)
+#define CCR_ABORT           BIT(15)
+#define CCR_BURST_1         (0 << 16)
+#define CCR_BURST_4         (1 << 16)
+#define CCR_BURST_8         (2 << 16)
+#define CCR_BURST_16        (3 << 16)
+#define CCR_BURST_32        (4 << 16)
+#define CCR_BURST_64        (5 << 16)
+#define CCR_BURST_128       (6 << 16)
+#define CCR_BURST_256       (7 << 16)
+#define CCR_PRIVILEGED      BIT(19)
+#define CCR_BUFFERABLE      BIT(20)
+#define CCR_CACHEABLE       BIT(21)
+#define CCR_PRIO_0          (0 << 22)
+#define CCR_PRIO_1          (1 << 22)
+#define CCR_PRIO_2          (2 << 22)
+#define CCR_PRIO_3          (3 << 22)
+#define CCR_FIFOTH_1        (0 << 24)
+#define CCR_FIFOTH_2        (1 << 24)
+#define CCR_FIFOTH_4        (2 << 24)
+#define CCR_FIFOTH_8        (3 << 24)
+#define CCR_FIFOTH_16       (4 << 24)
+#define CCR_MASK_TC         BIT(31) /* DMA Transfer terminated(finished) */
+
+/*
+ * Channel configuration register
+ */
+#define CFG_MASK_TCI            BIT(0)    /* disable tc interrupt */
+#define CFG_MASK_EI             BIT(1)    /* disable error interrupt */
+#define CFG_MASK_AI             BIT(2)    /* disable abort interrupt */
+#define CFG_SRC_HANDSHAKE(x)    (((x) & 0xf) << 3)
+#define CFG_SRC_HANDSHAKE_EN    BIT(7)
+#define CFG_BUSY                BIT(8)
+#define CFG_DST_HANDSHAKE(x)    (((x) & 0xf) << 9)
+#define CFG_DST_HANDSHAKE_EN    BIT(13)
+#define CFG_LLP_CNT(cfg)        (((cfg) >> 16) & 0xf)
+
+#endif    /* HW_ARM_FTDMAC020_H */
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH v9 11/24] hw/arm: add FTAPBBRG020 APB DMA support
  2013-03-25 12:09 [Qemu-devel] [PATCH v9 00/24] hw/arm: add Faraday A369 SoC platform support Kuo-Jung Su
                   ` (9 preceding siblings ...)
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 10/24] hw/arm: add FTDMAC020 AHB DMA support Kuo-Jung Su
@ 2013-03-25 12:09 ` Kuo-Jung Su
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 12/24] hw/arm: add FTNANDC021 nand flash controller support Kuo-Jung Su
                   ` (12 subsequent siblings)
  23 siblings, 0 replies; 35+ messages in thread
From: Kuo-Jung Su @ 2013-03-25 12:09 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, i.mitsyanko, Blue Swirl, Paul Brook, Kuo-Jung Su,
	Andreas, fred.konrad

From: Kuo-Jung Su <dantesu@faraday-tech.com>

The FTAPBBRG020 supports the DMA functions for the AHB-to-AHB,
AHB-to-APB, APB-to-AHB, and APB-to-APB transactions.

The DMA engine can support up to 4 DMA channels (A, B, C, and D)
and 15 handshaking channels. A DMA channel granted by the arbiter
block is the only channel starts transfers. Each DMA channel can
be programmed to one of the 15 handshaking channels in the hardware
handshake mode to act as the source device or act as the destination
device.

The main function of the hardware handshake mode is to provide an
indication of the device status. Users can also disable the hardware
handshake mode by programming the register when a DMA transfer is not
necessary of referring to the handshaking channels.

Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
---
 hw/arm/Makefile.objs    |    2 +-
 hw/arm/ftplat_a369soc.c |    9 +
 hw/ftapbbrg020.c        |  478 +++++++++++++++++++++++++++++++++++++++++++++++
 hw/ftapbbrg020.h        |   44 +++++
 4 files changed, 532 insertions(+), 1 deletion(-)
 create mode 100644 hw/ftapbbrg020.c
 create mode 100644 hw/ftapbbrg020.h

diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 6510c51..34e2939 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -25,7 +25,7 @@ obj-y += strongarm.o
 obj-y += imx_serial.o imx_ccm.o imx_timer.o imx_avic.o
 obj-$(CONFIG_KVM) += kvm/arm_gic.o
 obj-y += ftintc020.o ftahbc020.o ftddrii030.o ftpwmtmr010.o ftwdt010.o \
-                ftrtc011.o ftdmac020.o
+                ftrtc011.o ftdmac020.o ftapbbrg020.o
 
 obj-y := $(addprefix ../,$(obj-y))
 
diff --git a/hw/arm/ftplat_a369soc.c b/hw/arm/ftplat_a369soc.c
index 59e2c61..99eb428 100644
--- a/hw/arm/ftplat_a369soc.c
+++ b/hw/arm/ftplat_a369soc.c
@@ -182,6 +182,15 @@ static void a369soc_chip_init(FaradaySoCState *s)
                                        s->pic[17], /* TC */
                                        s->pic[18], /* ERR */
                                        NULL);
+
+    /* ftapbbrg020 */
+    ds = sysbus_create_simple("ftapbbrg020", 0x90f00000, s->pic[14]);
+    s->pdma[0] = ds;
+    object_property_set_link(OBJECT(ds), OBJECT(s), "soc", &local_errp);
+    if (local_errp) {
+        fprintf(stderr, "a369soc: Unable to set soc link for FTAPBBRG020\n");
+        abort();
+    }
 }
 
 static void a369soc_realize(DeviceState *dev, Error **errp)
diff --git a/hw/ftapbbrg020.c b/hw/ftapbbrg020.c
new file mode 100644
index 0000000..efc8376
--- /dev/null
+++ b/hw/ftapbbrg020.c
@@ -0,0 +1,478 @@
+/*
+ * QEMU model of the FTAPBBRG020 DMA Controller
+ *
+ * Copyright (C) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This file is licensed under GNU GPL v2+.
+ *
+ * Note: The FTAPBBRG020 DMA descending address mode is not implemented.
+ */
+
+#include "hw/sysbus.h"
+#include "sysemu/dma.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/blockdev.h"
+
+#include "hw/faraday.h"
+#include "hw/ftapbbrg020.h"
+
+#define TYPE_FTAPBBRG020    "ftapbbrg020"
+
+typedef struct Ftapbbrg020State Ftapbbrg020State;
+
+typedef struct Ftapbbrg020Chan {
+    Ftapbbrg020State *chip;
+
+    int id;
+    int burst;
+    int src_bw;
+    int src_stride;
+    int dst_bw;
+    int dst_stride;
+
+    /* HW register caches */
+    uint32_t src;
+    uint32_t dst;
+    uint32_t len;
+    uint32_t cmd;
+} Ftapbbrg020Chan;
+
+typedef struct Ftapbbrg020State {
+    /*< private >*/
+    SysBusDevice parent;
+
+    /*< public >*/
+    MemoryRegion iomem;
+    qemu_irq irq;
+
+    FaradaySoCState *soc;
+    Ftapbbrg020Chan chan[4];
+    qemu_irq ack[16];
+    uint32_t req;
+
+    int busy;    /* Busy Channel ID */
+    int bh_owner;
+    QEMUBH *bh;
+    DMAContext *dma;
+} Ftapbbrg020State;
+
+#define FTAPBBRG020(obj) \
+    OBJECT_CHECK(Ftapbbrg020State, obj, TYPE_FTAPBBRG020)
+
+static uint32_t ftapbbrg020_get_isr(Ftapbbrg020State *s)
+{
+    int i;
+    uint32_t isr = 0;
+    Ftapbbrg020Chan *chan;
+
+    for (i = 0; i < 4; ++i) {
+        chan = s->chan + i;
+        isr |= (chan->cmd & CMD_INTR_STATUS);
+    }
+
+    return isr;
+}
+
+static void ftapbbrg020_update_irq(Ftapbbrg020State *s)
+{
+    uint32_t isr = ftapbbrg020_get_isr(s);
+
+    qemu_set_irq(s->irq, isr ? 1 : 0);
+}
+
+static void ftapbbrg020_chan_cmd_decode(Ftapbbrg020Chan *c)
+{
+    uint32_t tmp;
+
+    /* 1. decode burst size */
+    c->burst = (c->cmd & CMD_BURST4) ? 4 : 1;
+
+    /* 2. decode source/destination width */
+    tmp = extract32(c->cmd, 20, 2);
+    if (tmp > 2) {
+        tmp = 2;
+    }
+    c->src_bw = c->dst_bw = 8 << (2 - tmp);
+
+    /* 3. decode source address stride */
+    switch (extract32(c->cmd, 8, 2)) {
+    case 0:
+        c->src_stride = 0;
+        break;
+    case 1:
+        c->src_stride = c->src_bw >> 3;
+        break;
+    case 2:
+        c->src_stride = 2 * (c->src_bw >> 3);
+        break;
+    case 3:
+        c->src_stride = 4 * (c->src_bw >> 3);
+        break;
+    }
+
+    /* 4. decode destination address stride */
+    switch (extract32(c->cmd, 12, 2)) {
+    case 0:
+        c->dst_stride = 0;
+        break;
+    case 1:
+        c->dst_stride = c->dst_bw >> 3;
+        break;
+    case 2:
+        c->dst_stride = 2 * (c->dst_bw >> 3);
+        break;
+    case 3:
+        c->dst_stride = 4 * (c->dst_bw >> 3);
+        break;
+    }
+}
+
+static void ftapbbrg020_chan_start(Ftapbbrg020Chan *c)
+{
+    Ftapbbrg020State *s = c->chip;
+    hwaddr src, dst;
+    uint8_t buf[4096] __attribute__ ((aligned (8)));
+    int i, len, stride, src_hs, dst_hs;
+
+    if (!(c->cmd & CMD_START)) {
+        return;
+    }
+
+    s->busy = c->id;
+
+    /* DMA src/dst address */
+    src = c->src;
+    dst = c->dst;
+
+    /* DMA hardware handshake id */
+    src_hs = extract32(c->cmd, 24, 4);
+    dst_hs = extract32(c->cmd, 16, 4);
+
+    /* DMA src/dst sanity check */
+    if (cpu_physical_memory_is_io(src) && c->src_stride) {
+        fprintf(stderr,
+            "ftapbbrg020: src is an I/O device with non-fixed address?\n");
+        abort();
+    }
+    if (cpu_physical_memory_is_io(dst) && c->dst_stride) {
+        fprintf(stderr,
+            "ftapbbrg020: dst is an I/O device with non-fixed address?\n");
+        abort();
+    }
+
+    while (c->len > 0) {
+        /*
+         * Postpone this DMA action
+         * if the corresponding dma request is not asserted
+         */
+        if (src_hs && !(s->req & BIT(src_hs))) {
+            break;
+        }
+        if (dst_hs && !(s->req & BIT(dst_hs))) {
+            break;
+        }
+
+        len = MIN(sizeof(buf), c->burst * (c->src_bw >> 3));
+
+        /* load data from source into local buffer */
+        if (c->src_stride) {
+            dma_memory_read(s->dma, src, buf, len);
+            src += len;
+        } else {
+            stride = c->src_bw >> 3;
+            for (i = 0; i < len; i += stride) {
+                dma_memory_read(s->dma, src, buf + i, stride);
+            }
+        }
+
+        /* DMA Hardware Handshake */
+        if (src_hs) {
+            qemu_set_irq(s->ack[src_hs], 1);
+        }
+
+        /* store data into destination from local buffer */
+        if (c->dst_stride) {
+            dma_memory_write(s->dma, dst, buf, len);
+            dst += len;
+        } else {
+            stride = c->dst_bw >> 3;
+            for (i = 0; i < len; i += stride) {
+                dma_memory_write(s->dma, dst, buf + i, stride);
+            }
+        }
+
+        /* DMA Hardware Handshake */
+        if (dst_hs) {
+            qemu_set_irq(s->ack[dst_hs], 1);
+        }
+
+        /* update the channel transfer size */
+        c->len -= len / (c->src_bw >> 3);
+
+        if (c->len == 0) {
+            /* update the channel transfer status */
+            if (c->cmd & CMD_INTR_FIN_EN) {
+                c->cmd |= CMD_INTR_FIN;
+                ftapbbrg020_update_irq(s);
+            }
+            /* clear start bit */
+            c->cmd &= ~CMD_START;
+        }
+    }
+
+    /* update src/dst address */
+    c->src = src;
+    c->dst = dst;
+
+    s->busy = -1;
+}
+
+static void ftapbbrg020_chan_reset(Ftapbbrg020Chan *c)
+{
+    c->cmd = 0;
+    c->src = 0;
+    c->dst = 0;
+    c->len = 0;
+}
+
+static void ftapbbrg020_bh(void *opaque)
+{
+    Ftapbbrg020State *s = FTAPBBRG020(opaque);
+    Ftapbbrg020Chan  *c = NULL;
+    int i, jobs, done;
+
+    ++s->bh_owner;
+    jobs = 0;
+    done = 0;
+    for (i = 0; i < 4; ++i) {
+        c = s->chan + i;
+        if (c->cmd & CMD_START) {
+            ++jobs;
+            ftapbbrg020_chan_start(c);
+            if (!(c->cmd & CMD_START)) {
+                ++done;
+            }
+        }
+    }
+    --s->bh_owner;
+
+    /*
+     * Devices those with an infinite FIFO (always ready for R/W)
+     * would trigger a new DMA handshake transaction here.
+     * (i.e. ftnandc021, ftsdc010)
+     */
+    if ((jobs - done) && s->req) {
+        qemu_bh_schedule(s->bh);
+    }
+}
+
+static void ftapbbrg020_handle_req(void *opaque, int line, int level)
+{
+    Ftapbbrg020State *s = FTAPBBRG020(opaque);
+
+    if (level) {
+        /*
+         * Devices those wait for data from external I/O
+         * would trigger a new DMA handshake transaction here.
+         * (i.e. ftssp010)
+         */
+        if (!(s->req & BIT(line))) {
+            /* a simple workaround for BH reentry issue */
+            if (!s->bh_owner) {
+                qemu_bh_schedule(s->bh);
+            }
+        }
+        s->req |= BIT(line);
+    } else {
+        s->req &= ~BIT(line);
+        qemu_set_irq(s->ack[line], 0);
+    }
+}
+
+static void ftapbbrg020_chip_reset(Ftapbbrg020State *s)
+{
+    int i;
+
+    for (i = 0; i < 4; ++i) {
+        ftapbbrg020_chan_reset(s->chan + i);
+    }
+
+    /* We can assume our GPIO have been wired up now */
+    for (i = 0; i < 16; ++i) {
+        qemu_set_irq(s->ack[i], 0);
+    }
+    s->req = 0;
+}
+
+static uint64_t
+ftapbbrg020_mem_read(void *opaque, hwaddr addr, unsigned size)
+{
+    Ftapbbrg020State *s = FTAPBBRG020(opaque);
+    Ftapbbrg020Chan  *c = NULL;
+    uint32_t ret = 0;
+
+    switch (addr) {
+    case REG_SLAVE(0) ... REG_SLAVE(31):
+        ret = s->soc->apb_slave[addr / 4];
+        break;
+    case REG_CHAN_BASE(0) ... REG_CHAN_BASE(3) + 0x0C:
+        c = s->chan + REG_CHAN_ID(addr);
+        switch (addr & 0x0f) {
+        case REG_CHAN_CMD:
+            return c->cmd;
+        case REG_CHAN_SRC:
+            return c->src;
+        case REG_CHAN_DST:
+            return c->dst;
+        case REG_CHAN_CYC:
+            return c->len;
+        }
+        break;
+    case REG_REVISION:
+        return 0x00010800;  /* rev 1.8.0 */
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "ftapbbrg020: undefined memory access@%#" HWADDR_PRIx "\n", addr);
+        break;
+    }
+
+    return ret;
+}
+
+static void
+ftapbbrg020_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+    Ftapbbrg020State *s = FTAPBBRG020(opaque);
+    Ftapbbrg020Chan  *c = NULL;
+
+    switch (addr) {
+    case REG_CHAN_BASE(0) ... REG_CHAN_BASE(3) + 0x0C:
+        c = s->chan + REG_CHAN_ID(addr);
+        switch (addr & 0x0f) {
+        case REG_CHAN_CMD:
+            c->cmd = (uint32_t)val;
+            ftapbbrg020_update_irq(s);
+            if (c->cmd & CMD_START) {
+                ftapbbrg020_chan_cmd_decode(c);
+                /* kick-off DMA engine */
+                qemu_bh_schedule(s->bh);
+            }
+            break;
+        case REG_CHAN_SRC:
+            c->src = (uint32_t)val;
+            break;
+        case REG_CHAN_DST:
+            c->dst = (uint32_t)val;
+            break;
+        case REG_CHAN_CYC:
+            c->len = (uint32_t)val & 0x00ffffff;
+            break;
+        }
+        break;
+
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "ftapbbrg020: undefined memory access@%#" HWADDR_PRIx "\n", addr);
+        break;
+    }
+}
+
+static const MemoryRegionOps mmio_ops = {
+    .read  = ftapbbrg020_mem_read,
+    .write = ftapbbrg020_mem_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4
+    }
+};
+
+static void ftapbbrg020_reset(DeviceState *ds)
+{
+    Ftapbbrg020State *s = FTAPBBRG020(SYS_BUS_DEVICE(ds));
+    Error *local_errp = NULL;
+
+    s->soc = FARADAY_SOC(object_property_get_link(OBJECT(s),
+                                                  "soc",
+                                                  &local_errp));
+    if (local_errp) {
+        fprintf(stderr, "ftapbbrg020: Unable to get soc link\n");
+        abort();
+    }
+
+    ftapbbrg020_chip_reset(s);
+}
+
+static void ftapbbrg020_realize(DeviceState *dev, Error **errp)
+{
+    Ftapbbrg020State *s = FTAPBBRG020(dev);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+    Ftapbbrg020Chan *c;
+    int i;
+
+    memory_region_init_io(&s->iomem,
+                          &mmio_ops,
+                          s,
+                          TYPE_FTAPBBRG020,
+                          0x1000);
+    sysbus_init_mmio(sbd, &s->iomem);
+    sysbus_init_irq(sbd, &s->irq);
+    qdev_init_gpio_in(&sbd->qdev, ftapbbrg020_handle_req, 16);
+    qdev_init_gpio_out(&sbd->qdev, s->ack, 16);
+
+    s->busy = -1;
+    s->dma = &dma_context_memory;
+    s->bh = qemu_bh_new(ftapbbrg020_bh, s);
+    for (i = 0; i < 4; ++i) {
+        c = s->chan + i;
+        c->id   = i;
+        c->chip = s;
+    }
+}
+
+static const VMStateDescription vmstate_ftapbbrg020 = {
+    .name = TYPE_FTAPBBRG020,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void ftapbbrg020_instance_init(Object *obj)
+{
+    Ftapbbrg020State *s = FTAPBBRG020(obj);
+
+    object_property_add_link(obj,
+                             "soc",
+                             TYPE_FARADAY_SOC,
+                             (Object **) &s->soc,
+                             NULL);
+}
+
+static void ftapbbrg020_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->vmsd    = &vmstate_ftapbbrg020;
+    dc->reset   = ftapbbrg020_reset;
+    dc->realize = ftapbbrg020_realize;
+    dc->no_user = 1;
+}
+
+static const TypeInfo ftapbbrg020_info = {
+    .name          = TYPE_FTAPBBRG020,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(Ftapbbrg020State),
+    .instance_init = ftapbbrg020_instance_init,
+    .class_init    = ftapbbrg020_class_init,
+};
+
+static void ftapbbrg020_register_types(void)
+{
+    type_register_static(&ftapbbrg020_info);
+}
+
+type_init(ftapbbrg020_register_types)
diff --git a/hw/ftapbbrg020.h b/hw/ftapbbrg020.h
new file mode 100644
index 0000000..fc060dc
--- /dev/null
+++ b/hw/ftapbbrg020.h
@@ -0,0 +1,44 @@
+/*
+ * QEMU model of the FTAPBBRG020 DMA Controller
+ *
+ * Copyright (C) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This file is licensed under GNU GPL v2+.
+ *
+ * Note: The FTAPBBRG020 DMA decreasing address mode is not implemented.
+ */
+
+#ifndef HW_ARM_FTAPBBRG020_H
+#define HW_ARM_FTAPBBRG020_H
+
+#include "qemu/bitops.h"
+
+#define REG_SLAVE(n)        ((n) * 4) /* Slave config (base & size) */
+#define REG_REVISION        0xC8
+
+/*
+ * Channel base address
+ * @ch: channle id (0 <= id <= 3)
+ *      i.e. 0: Channel A
+ *           1: Channel B
+ *           2: Channel C
+ *           3: Channel D
+ */
+#define REG_CHAN_ID(off)    (((off) - 0x80) >> 4)
+#define REG_CHAN_BASE(ch)   (0x80 + ((ch) << 4))
+
+#define REG_CHAN_SRC        0x00
+#define REG_CHAN_DST        0x04
+#define REG_CHAN_CYC        0x08
+#define REG_CHAN_CMD        0x0C
+
+#define CMD_START           BIT(0)
+#define CMD_INTR_FIN        BIT(1)
+#define CMD_INTR_FIN_EN     BIT(2)
+#define CMD_BURST4          BIT(3)
+#define CMD_INTR_ERR        BIT(4)
+#define CMD_INTR_ERR_EN     BIT(5)
+#define CMD_INTR_STATUS     (CMD_INTR_FIN | CMD_INTR_ERR)
+
+#endif    /* HW_ARM_FTAPBB020_H */
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH v9 12/24] hw/arm: add FTNANDC021 nand flash controller support
  2013-03-25 12:09 [Qemu-devel] [PATCH v9 00/24] hw/arm: add Faraday A369 SoC platform support Kuo-Jung Su
                   ` (10 preceding siblings ...)
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 11/24] hw/arm: add FTAPBBRG020 APB " Kuo-Jung Su
@ 2013-03-25 12:09 ` Kuo-Jung Su
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 13/24] hw/arm: add FTI2C010 I2C " Kuo-Jung Su
                   ` (11 subsequent siblings)
  23 siblings, 0 replies; 35+ messages in thread
From: Kuo-Jung Su @ 2013-03-25 12:09 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, i.mitsyanko, Blue Swirl, Paul Brook, Kuo-Jung Su,
	Andreas, fred.konrad

From: Kuo-Jung Su <dantesu@faraday-tech.com>

The FTNANDC021 is an integrated NAND flash controller which
re-pack the NAND flash command set with a shorter built-in opcode.
It also provides a register base interface for user to easily
access the underlying NAND flash chips, and also supports HW ECC.

However the optional hardware ECC function is not implemented.

Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
---
 hw/arm/Makefile.objs    |    2 +-
 hw/arm/ftplat_a369.c    |   14 ++
 hw/arm/ftplat_a369soc.c |    9 +
 hw/ftnandc021.c         |  526 +++++++++++++++++++++++++++++++++++++++++++++++
 hw/ftnandc021.h         |   84 ++++++++
 5 files changed, 634 insertions(+), 1 deletion(-)
 create mode 100644 hw/ftnandc021.c
 create mode 100644 hw/ftnandc021.h

diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 34e2939..8fe0f67 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -25,7 +25,7 @@ obj-y += strongarm.o
 obj-y += imx_serial.o imx_ccm.o imx_timer.o imx_avic.o
 obj-$(CONFIG_KVM) += kvm/arm_gic.o
 obj-y += ftintc020.o ftahbc020.o ftddrii030.o ftpwmtmr010.o ftwdt010.o \
-                ftrtc011.o ftdmac020.o ftapbbrg020.o
+                ftrtc011.o ftdmac020.o ftapbbrg020.o ftnandc021.o
 
 obj-y := $(addprefix ../,$(obj-y))
 
diff --git a/hw/arm/ftplat_a369.c b/hw/arm/ftplat_a369.c
index 45f0846..827c58a 100644
--- a/hw/arm/ftplat_a369.c
+++ b/hw/arm/ftplat_a369.c
@@ -31,6 +31,8 @@ static void a369_board_init(QEMUMachineInitArgs *args)
     ARMCPU *cpu;
     DeviceState *ds;
     FaradaySoCState *s;
+    DriveInfo *dinfo;
+    Error *local_errp = NULL;
 
     if (!args->cpu_model) {
         args->cpu_model = "fa626te";
@@ -65,6 +67,18 @@ static void a369_board_init(QEMUMachineInitArgs *args)
     /* Customized system reset */
     qemu_register_reset(a369_system_reset, cpu);
 
+    /* Attach the nand flash to ftnandc021 */
+    dinfo = drive_get_next(IF_MTD);
+    ds = nand_init(dinfo ? dinfo->bdrv : NULL, NAND_MFR_SAMSUNG, 0xda);
+    object_property_set_link(OBJECT(s->nandc[0]),
+                             OBJECT(ds),
+                             "flash",
+                             &local_errp);
+    if (local_errp) {
+        fprintf(stderr, "a369: Unable to set flash link for FTNANDC021\n");
+        abort();
+    }
+
     /* System start-up */
 
     if (args->kernel_filename) {
diff --git a/hw/arm/ftplat_a369soc.c b/hw/arm/ftplat_a369soc.c
index 99eb428..e057629 100644
--- a/hw/arm/ftplat_a369soc.c
+++ b/hw/arm/ftplat_a369soc.c
@@ -54,6 +54,7 @@ static void a369soc_chip_init(FaradaySoCState *s)
     int i;
     DeviceState *ds;
     DriveInfo *dinfo;
+    qemu_irq ack, req;
     Error *local_errp = NULL;
 
     /* Remappable Memory Region Init */
@@ -191,6 +192,14 @@ static void a369soc_chip_init(FaradaySoCState *s)
         fprintf(stderr, "a369soc: Unable to set soc link for FTAPBBRG020\n");
         abort();
     }
+
+    /* ftnandc021 */
+    ds = sysbus_create_simple("ftnandc021", 0x90200000, s->pic[30]);
+    s->nandc[0] = ds;
+    ack = qdev_get_gpio_in(ds, 0);
+    req = qdev_get_gpio_in(s->hdma[0], 15);
+    qdev_connect_gpio_out(s->hdma[0], 15, ack);
+    qdev_connect_gpio_out(ds, 0, req);
 }
 
 static void a369soc_realize(DeviceState *dev, Error **errp)
diff --git a/hw/ftnandc021.c b/hw/ftnandc021.c
new file mode 100644
index 0000000..fef92aa
--- /dev/null
+++ b/hw/ftnandc021.c
@@ -0,0 +1,526 @@
+/*
+ * QEMU model of the FTNANDC021 NAND Flash Controller
+ *
+ * Copyright (C) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This file is licensed under GNU GPL v2+.
+ */
+
+#include "hw/sysbus.h"
+#include "hw/devices.h"
+#include "hw/flash.h"
+#include "sysemu/blockdev.h"
+
+#include "hw/ftnandc021.h"
+
+#define TYPE_FTNANDC021 "ftnandc021"
+
+typedef struct Ftnandc021State {
+    /*< private >*/
+    SysBusDevice parent;
+
+    /*< public >*/
+    MemoryRegion mmio;
+
+    qemu_irq irq;
+    DeviceState *flash;
+
+    /* DMA hardware handshake */
+    qemu_irq req;
+
+    uint8_t  manf_id, chip_id;
+
+    int      cmd;
+    int      len;    /* buffer length for page read/write */
+    int      pi;    /* page index */
+    int      bw;    /* bus width (8-bits, 16-bits) */
+
+    uint64_t size;    /* flash size (maximum access range) */
+    uint32_t pgsz;    /* page size (Bytes) */
+    uint32_t bksz;    /* block size (Bytes) */
+    uint32_t alen;    /* address length (cycle) */
+
+    uint32_t id[2];
+    uint8_t  oob[8];/* 5 bytes for 512/2048 page; 7 bytes for 4096 page */
+
+    /* HW register caches */
+    uint32_t sr;
+    uint32_t fcr;
+    uint32_t mcr;
+    uint32_t ier;
+    uint32_t bcr;
+} Ftnandc021State;
+
+#define FTNANDC021(obj) \
+    OBJECT_CHECK(Ftnandc021State, obj, TYPE_FTNANDC021)
+
+static void ftnandc021_update_irq(Ftnandc021State *s)
+{
+    if (s->ier & IER_ENA) {
+        if ((s->ier & 0x0f) & (s->sr >> 2)) {
+            qemu_set_irq(s->irq, 1);
+        } else {
+            qemu_set_irq(s->irq, 0);
+        }
+    }
+}
+
+static void ftnandc021_set_idle(Ftnandc021State *s)
+{
+    /* CLE=0, ALE=0, CS=1 */
+    nand_setpins(s->flash, 0, 0, 1, 1, 0);
+
+    /* Set command compelete */
+    s->sr |= SR_CMD;
+
+    /* Update IRQ signal */
+    ftnandc021_update_irq(s);
+}
+
+static void ftnandc021_set_cmd(Ftnandc021State *s, uint8_t cmd)
+{
+    /* CLE=1, ALE=0, CS=0 */
+    nand_setpins(s->flash, 1, 0, 0, 1, 0);
+
+    /* Write out command code */
+    nand_setio(s->flash, cmd);
+}
+
+static void ftnandc021_set_addr(Ftnandc021State *s, int col, int row)
+{
+    /* CLE=0, ALE=1, CS=0 */
+    nand_setpins(s->flash, 0, 1, 0, 1, 0);
+
+    if (col < 0 && row < 0) {
+        /* special case for READ_ID (0x90) */
+        nand_setio(s->flash, 0);
+    } else {
+        /* column address */
+        if (col >= 0) {
+            nand_setio(s->flash, extract32(col, 0, 8));
+            nand_setio(s->flash, extract32(col, 8, 8));
+        }
+        /* row address */
+        if (row >= 0) {
+            nand_setio(s->flash, extract32(row, 0, 8));
+            if (s->alen >= 4) {
+                nand_setio(s->flash, extract32(row, 8, 8));
+            }
+            if (s->alen >= 5) {
+                nand_setio(s->flash, extract32(row, 16, 8));
+            }
+        }
+    }
+}
+
+static void ftnandc021_handle_ack(void *opaque, int line, int level)
+{
+    Ftnandc021State *s = FTNANDC021(opaque);
+
+    if (!s->bcr) {
+        return;
+    }
+
+    if (level) {
+        qemu_set_irq(s->req, 0);
+    } else if (s->len > 0) {
+        qemu_set_irq(s->req, 1);
+    }
+}
+
+static void ftnandc021_command(Ftnandc021State *s, uint32_t cmd)
+{
+    int i;
+
+    s->sr &= ~SR_CMD;
+    s->cmd = cmd;
+
+    switch (cmd) {
+    case FTNANDC021_CMD_RDID:    /* read id */
+        ftnandc021_set_cmd(s, 0x90);
+        ftnandc021_set_addr(s, -1, -1);
+        nand_setpins(s->flash, 0, 0, 0, 1, 0);
+        if (s->bw == 8) {
+            s->id[0] = (nand_getio(s->flash) << 0)
+                     | (nand_getio(s->flash) << 8)
+                     | (nand_getio(s->flash) << 16)
+                     | (nand_getio(s->flash) << 24);
+            s->id[1] = (nand_getio(s->flash) << 0);
+        } else {
+            s->id[0] = (nand_getio(s->flash) << 0)
+                     | (nand_getio(s->flash) << 16);
+            s->id[1] = (nand_getio(s->flash) << 0);
+        }
+        break;
+    case FTNANDC021_CMD_RESET:    /* reset */
+        ftnandc021_set_cmd(s, 0xff);
+        break;
+    case FTNANDC021_CMD_RDST:    /* read status */
+        ftnandc021_set_cmd(s, 0x70);
+        nand_setpins(s->flash, 0, 0, 0, 1, 0);
+        s->id[1] = (nand_getio(s->flash) << 0);
+        break;
+    case FTNANDC021_CMD_RDPG:    /* read page */
+        ftnandc021_set_cmd(s, 0x00);
+        ftnandc021_set_addr(s, 0, s->pi);
+        ftnandc021_set_cmd(s, 0x30);
+        nand_setpins(s->flash, 0, 0, 0, 1, 0);
+        s->len = s->pgsz;
+        break;
+    case FTNANDC021_CMD_RDOOB:    /* read oob */
+        ftnandc021_set_cmd(s, 0x00);
+        ftnandc021_set_addr(s, s->pgsz, s->pi);
+        ftnandc021_set_cmd(s, 0x30);
+        nand_setpins(s->flash, 0, 0, 0, 1, 0);
+        for (i = 0; i < 16 * (s->pgsz / 512); ) {
+            if (s->bw == 8) {
+                if (i < 7) {
+                    s->oob[i] = (uint8_t)nand_getio(s->flash);
+                } else {
+                    (void)nand_getio(s->flash);
+                }
+                i += 1;
+            } else {
+                if (i < 7) {
+                    *(uint16_t *)(s->oob + i) = (uint16_t)nand_getio(s->flash);
+                } else {
+                    (void)nand_getio(s->flash);
+                }
+                i += 2;
+            }
+        }
+        break;
+    case FTNANDC021_CMD_WRPG:    /* write page + read status */
+        ftnandc021_set_cmd(s, 0x80);
+        ftnandc021_set_addr(s, 0, s->pi);
+        /* data phase */
+        nand_setpins(s->flash, 0, 0, 0, 1, 0);
+        s->len = s->pgsz;
+        break;
+    case FTNANDC021_CMD_ERBLK:    /* erase block + read status */
+        ftnandc021_set_cmd(s, 0x60);
+        ftnandc021_set_addr(s, -1, s->pi);
+        ftnandc021_set_cmd(s, 0xd0);
+        /* read status */
+        ftnandc021_command(s, 0x04);
+        break;
+    case FTNANDC021_CMD_WROOB:    /* write oob + read status */
+        ftnandc021_set_cmd(s, 0x80);
+        ftnandc021_set_addr(s, s->pgsz, s->pi);
+        /* data phase */
+        nand_setpins(s->flash, 0, 0, 0, 1, 0);
+        for (i = 0; i < 16 * (s->pgsz / 512); ) {
+            if (s->bw == 8) {
+                if (i <= 7) {
+                    nand_setio(s->flash, s->oob[i]);
+                } else {
+                    nand_setio(s->flash, 0xffffffff);
+                }
+                i += 1;
+            } else {
+                if (i <= 7) {
+                    nand_setio(s->flash, s->oob[i] | (s->oob[i + 1] << 8));
+                } else {
+                    nand_setio(s->flash, 0xffffffff);
+                }
+                i += 2;
+            }
+        }
+        ftnandc021_set_cmd(s, 0x10);
+        /* read status */
+        ftnandc021_command(s, 0x04);
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "ftnandc021: unknow command=0x%02x\n", cmd);
+        break;
+    }
+
+    /* if cmd is not page read/write, then return to idle mode */
+    switch (s->cmd) {
+    case FTNANDC021_CMD_RDPG:
+    case FTNANDC021_CMD_WRPG:
+        if (s->bcr && (s->len > 0)) {
+            qemu_set_irq(s->req, 1);
+        }
+        break;
+    default:
+        ftnandc021_set_idle(s);
+        break;
+    }
+}
+
+static uint64_t
+ftnandc021_mem_read(void *opaque, hwaddr addr, unsigned size)
+{
+    uint32_t i, ret = 0;
+    Ftnandc021State *s = FTNANDC021(opaque);
+
+    switch (addr) {
+    case REG_DR:
+        if ((s->cmd == FTNANDC021_CMD_RDPG) && (s->len > 0)) {
+            if (s->bw == 8) {
+                for (i = 0; i < 4 && s->len > 0; i++, s->len--) {
+                    ret = deposit32(ret, i * 8, 8, nand_getio(s->flash));
+                }
+            } else {
+                for (i = 0; i < 2 && s->len > 1; i++, s->len -= 2) {
+                    ret = deposit32(ret, i * 16, 16, nand_getio(s->flash));
+                }
+            }
+            if (s->len <= 0) {
+                ftnandc021_set_idle(s);
+            }
+        }
+        break;
+    case REG_SR:
+        return s->sr;
+    case REG_ACR:
+        return s->cmd << 8;
+    case REG_RDBR:
+        return s->oob[0];
+    case REG_RDLSN:
+        return s->oob[1] | (s->oob[2] << 8);
+    case REG_RDCRC:
+        if (s->pgsz > 2048) {
+            return s->oob[3] | (s->oob[4] << 8)
+                   | (s->oob[5] << 16) | (s->oob[6] << 24);
+        } else {
+            return s->oob[3] | (s->oob[4] << 8);
+        }
+    case REG_FCR:
+        return s->fcr;
+    case REG_PIR:
+        return s->pi;
+    case REG_PCR:
+        return 1;           /* page count = 1 */
+    case REG_MCR:
+        return s->mcr;
+    case REG_IDRL:
+        return s->id[0];
+    case REG_IDRH:
+        return s->id[1];
+    case REG_IER:
+        return s->ier;
+    case REG_BCR:
+        return s->bcr;
+    case REG_ATR1:
+        return 0x02240264;  /* AC Timing */
+    case REG_ATR2:
+        return 0x42054209;  /* AC Timing */
+    case REG_PRR:
+        return 0x00000001;  /* Always ready for I/O */
+    case REG_REVR:
+        return 0x00010100;  /* Rev. 1.1.0 */
+    case REG_CFGR:
+        return 0x00081601;  /* 8-bit BCH, 16 bit flash, 1 chip */
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "ftnandc021: undefined memory access@%#" HWADDR_PRIx "\n", addr);
+        break;
+    }
+
+    return ret;
+}
+
+static void
+ftnandc021_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+    uint32_t i;
+    Ftnandc021State *s = FTNANDC021(opaque);
+
+    switch (addr) {
+    case REG_DR:
+        if (s->cmd == FTNANDC021_CMD_WRPG && s->len > 0) {
+            if (s->bw == 8) {
+                for (i = 0; i < 4 && s->len > 0; i++, s->len--) {
+                    nand_setio(s->flash,
+                               extract32((uint32_t)val, i * 8, 8));
+                }
+            } else {
+                for (i = 0; i < 2 && s->len > 1; i++, s->len -= 2) {
+                    nand_setio(s->flash,
+                               extract32((uint32_t)val, i * 16, 16));
+                }
+            }
+            if (s->len <= 0) {
+                ftnandc021_set_cmd(s, 0x10);
+                /* read status */
+                ftnandc021_command(s, 0x04);
+            }
+        }
+        break;
+    case REG_ACR:
+        if (!(val & ACR_START)) {
+            break;
+        }
+        ftnandc021_command(s, extract32((uint32_t)val, 8, 5));
+        break;
+    case REG_WRBR:
+        s->oob[0] = (uint32_t)val & 0xff;
+        break;
+    case REG_WRLSN:
+        s->oob[1] = ((uint32_t)val >> 0) & 0xff;
+        s->oob[2] = ((uint32_t)val >> 8) & 0xff;
+        break;
+    case REG_WRCRC:
+        s->oob[3] = ((uint32_t)val >> 0) & 0xff;
+        s->oob[4] = ((uint32_t)val >> 8) & 0xff;
+        if (s->pgsz > 2048) {
+            s->oob[5] = ((uint32_t)val >> 16) & 0xff;
+            s->oob[6] = ((uint32_t)val >> 24) & 0xff;
+        }
+        break;
+    case REG_FCR:
+        s->fcr = (uint32_t)val;
+        if (s->fcr & FCR_16BIT) {
+            s->bw = 16;
+        } else {
+            s->bw = 8;
+        }
+        break;
+    case REG_PIR:
+        s->pi = (uint32_t)val & 0x03ffffff;
+        break;
+    case REG_MCR:
+        s->mcr = (uint32_t)val;
+        /* page size */
+        switch (extract32(s->mcr, 8, 2)) {
+        case 0:
+            s->pgsz = 512;
+            break;
+        case 2:
+            s->pgsz = 4096;
+            break;
+        default:
+            s->pgsz = 2048;
+            break;
+        }
+        /* block size */
+        s->bksz = s->pgsz * (1 << (4 + extract32(s->mcr, 16, 2)));
+        /* address length (cycle) */
+        s->alen = 3 + extract32(s->mcr, 10, 2);
+        /* flash size */
+        s->size = 1ULL << (24 + extract32(s->mcr, 4, 4));
+        break;
+    case REG_IER:
+        s->ier = (uint32_t)val & 0x8f;
+        ftnandc021_update_irq(s);
+        break;
+    case REG_ISCR:
+        s->sr &= ~(((uint32_t)val & 0x0f) << 2);
+        ftnandc021_update_irq(s);
+        break;
+    case REG_BCR:
+        s->bcr = (uint32_t)val;
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "ftnandc021: undefined memory access@%#" HWADDR_PRIx "\n", addr);
+        break;
+    }
+}
+
+static const MemoryRegionOps mmio_ops = {
+    .read  = ftnandc021_mem_read,
+    .write = ftnandc021_mem_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4
+    }
+};
+
+static void ftnandc021_reset(DeviceState *ds)
+{
+    Ftnandc021State *s = FTNANDC021(SYS_BUS_DEVICE(ds));
+    Error *local_errp = NULL;
+
+    s->flash = DEVICE(object_property_get_link(OBJECT(s),
+                                               "flash",
+                                               &local_errp));
+    if (local_errp) {
+        fprintf(stderr, "ftnandc021: Unable to get flash link\n");
+        abort();
+    }
+
+    s->sr    = 0;
+    s->fcr   = 0;
+    s->mcr   = 0;
+    s->ier   = 0;
+    s->bcr   = 0;
+    s->id[0] = 0;
+    s->id[1] = 0;
+
+    /* We can assume our GPIO outputs have been wired up now */
+    qemu_set_irq(s->req, 0);
+}
+
+static void ftnandc021_realize(DeviceState *dev, Error **errp)
+{
+    Ftnandc021State *s = FTNANDC021(dev);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+
+    memory_region_init_io(&s->mmio,
+                          &mmio_ops,
+                          s,
+                          TYPE_FTNANDC021,
+                          0x1000);
+    sysbus_init_mmio(sbd, &s->mmio);
+    sysbus_init_irq(sbd, &s->irq);
+
+    qdev_init_gpio_in(&sbd->qdev, ftnandc021_handle_ack, 1);
+    qdev_init_gpio_out(&sbd->qdev, &s->req, 1);
+}
+
+static const VMStateDescription vmstate_ftnandc021 = {
+    .name = TYPE_FTNANDC021,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(sr, Ftnandc021State),
+        VMSTATE_UINT32(fcr, Ftnandc021State),
+        VMSTATE_UINT32(mcr, Ftnandc021State),
+        VMSTATE_UINT32(ier, Ftnandc021State),
+        VMSTATE_UINT32(bcr, Ftnandc021State),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void ftnandc021_instance_init(Object *obj)
+{
+    Ftnandc021State *s = FTNANDC021(obj);
+
+    object_property_add_link(obj,
+                             "flash",
+                             TYPE_DEVICE,
+                             (Object **) &s->flash,
+                             NULL);
+}
+
+static void ftnandc021_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->vmsd    = &vmstate_ftnandc021;
+    dc->reset   = ftnandc021_reset;
+    dc->realize = ftnandc021_realize;
+    dc->no_user = 1;
+}
+
+static const TypeInfo ftnandc021_info = {
+    .name          = TYPE_FTNANDC021,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(Ftnandc021State),
+    .instance_init = ftnandc021_instance_init,
+    .class_init    = ftnandc021_class_init,
+};
+
+static void ftnandc021_register_types(void)
+{
+    type_register_static(&ftnandc021_info);
+}
+
+type_init(ftnandc021_register_types)
diff --git a/hw/ftnandc021.h b/hw/ftnandc021.h
new file mode 100644
index 0000000..199de95
--- /dev/null
+++ b/hw/ftnandc021.h
@@ -0,0 +1,84 @@
+/*
+ * QEMU model of the FTNANDC021 NAND Flash Controller
+ *
+ * Copyright (C) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This file is licensed under GNU GPL v2+.
+ */
+
+#ifndef HW_ARM_FTNANDC021_H
+#define HW_ARM_FTNANDC021_H
+
+#include "qemu/bitops.h"
+
+/* NANDC control registers */
+#define REG_SR                  0x100    /* Status Register */
+#define REG_ACR                 0x104    /* Access Control Register */
+#define REG_FCR                 0x108    /* Flow Control Register */
+#define REG_PIR                 0x10C    /* Page Index Register */
+#define REG_MCR                 0x110    /* Memory Configuration Register */
+#define REG_ATR1                0x114    /* AC Timing Register 1 */
+#define REG_ATR2                0x118    /* AC Timing Register 2 */
+#define REG_IDRL                0x120    /* ID Register LSB */
+#define REG_IDRH                0x124    /* ID Register MSB */
+#define REG_IER                 0x128    /* Interrupt Enable Register */
+#define REG_ISCR                0x12C    /* Interrupt Status Clear Register */
+#define REG_WRBR                0x140    /* Write Bad Block Register */
+#define REG_WRLSN               0x144    /* Write LSN Register */
+#define REG_WRCRC               0x148    /* Write LSN CRC Register */
+#define REG_RDBR                0x150    /* Read Bad Block Register */
+#define REG_RDLSN               0x154    /* Read LSN Register */
+#define REG_RDCRC               0x158    /* Read LSN CRC Register */
+
+/* BMC control registers */
+#define REG_PRR                 0x208    /* BMC PIO Ready Register */
+#define REG_BCR                 0x20C    /* BMC Burst Control Register */
+
+/** MISC register **/
+#define REG_DR                  0x300    /* Data Register */
+#define REG_PCR                 0x308    /* Page Count Register */
+#define REG_RSTR                0x30C    /* MLC Reset Register */
+#define REG_REVR                0x3F8    /* Revision Register */
+#define REG_CFGR                0x3FC    /* Configuration Register */
+
+
+/*
+ * Register BITMASK
+ */
+#define SR_BLANK                BIT(7)  /* blanking check failed */
+#define SR_ECC                  BIT(6)  /* ecc failed */
+#define SR_STS                  BIT(4)  /* status error */
+#define SR_CRC                  BIT(3)  /* crc error */
+#define SR_CMD                  BIT(2)  /* command finished */
+#define SR_BUSY                 BIT(1)  /* chip busy */
+#define SR_ENA                  BIT(0)  /* chip enabled */
+
+#define ACR_CMD(x)              (((x) & 0x1f) << 8) /* command code */
+#define ACR_START               BIT(7)  /* command start */
+
+#define FCR_16BIT               BIT(4)  /* 16 bit data bus */
+#define FCR_WPROT               BIT(3)  /* write protected */
+#define FCR_NOSC                BIT(2)  /* bypass status check error */
+#define FCR_MICRON              BIT(1)  /* Micron 2-plane command */
+#define FCR_NOBC                BIT(0)  /* skip blanking check error */
+
+#define IER_ENA                 BIT(7)  /* interrupt enabled */
+#define IER_ECC                 BIT(3)  /* ecc error timeout */
+#define IER_STS                 BIT(2)  /* status error */
+#define IER_CRC                 BIT(1)  /* crc error */
+#define IER_CMD                 BIT(0)  /* command finished */
+
+/*
+ * FTNANDC021 integrated command set
+ */
+#define FTNANDC021_CMD_RDID     0x01    /* read id */
+#define FTNANDC021_CMD_RESET    0x02
+#define FTNANDC021_CMD_RDST     0x04    /* read status */
+#define FTNANDC021_CMD_RDPG     0x05    /* read page (data + oob) */
+#define FTNANDC021_CMD_RDOOB    0x06    /* read oob */
+#define FTNANDC021_CMD_WRPG     0x10    /* write page (data + oob) */
+#define FTNANDC021_CMD_ERBLK    0x11    /* erase block */
+#define FTNANDC021_CMD_WROOB    0x13    /* write oob */
+
+#endif
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH v9 13/24] hw/arm: add FTI2C010 I2C controller support
  2013-03-25 12:09 [Qemu-devel] [PATCH v9 00/24] hw/arm: add Faraday A369 SoC platform support Kuo-Jung Su
                   ` (11 preceding siblings ...)
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 12/24] hw/arm: add FTNANDC021 nand flash controller support Kuo-Jung Su
@ 2013-03-25 12:09 ` Kuo-Jung Su
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 14/24] hw: Add AudioCodecClass for wm87xx audio class abstration Kuo-Jung Su
                   ` (10 subsequent siblings)
  23 siblings, 0 replies; 35+ messages in thread
From: Kuo-Jung Su @ 2013-03-25 12:09 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, i.mitsyanko, Blue Swirl, Paul Brook, Kuo-Jung Su,
	Andreas, fred.konrad

From: Kuo-Jung Su <dantesu@faraday-tech.com>

The FTI2C010 is a simple I2C master controller.

Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
---
 hw/arm/Makefile.objs    |    2 +-
 hw/arm/ftplat_a369soc.c |    6 ++
 hw/fti2c010.c           |  224 +++++++++++++++++++++++++++++++++++++++++++++++
 hw/fti2c010.h           |   74 ++++++++++++++++
 4 files changed, 305 insertions(+), 1 deletion(-)
 create mode 100644 hw/fti2c010.c
 create mode 100644 hw/fti2c010.h

diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 8fe0f67..2bb67f7 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -25,7 +25,7 @@ obj-y += strongarm.o
 obj-y += imx_serial.o imx_ccm.o imx_timer.o imx_avic.o
 obj-$(CONFIG_KVM) += kvm/arm_gic.o
 obj-y += ftintc020.o ftahbc020.o ftddrii030.o ftpwmtmr010.o ftwdt010.o \
-                ftrtc011.o ftdmac020.o ftapbbrg020.o ftnandc021.o
+                ftrtc011.o ftdmac020.o ftapbbrg020.o ftnandc021.o fti2c010.o
 
 obj-y := $(addprefix ../,$(obj-y))
 
diff --git a/hw/arm/ftplat_a369soc.c b/hw/arm/ftplat_a369soc.c
index e057629..b6e82ad 100644
--- a/hw/arm/ftplat_a369soc.c
+++ b/hw/arm/ftplat_a369soc.c
@@ -200,6 +200,12 @@ static void a369soc_chip_init(FaradaySoCState *s)
     req = qdev_get_gpio_in(s->hdma[0], 15);
     qdev_connect_gpio_out(s->hdma[0], 15, ack);
     qdev_connect_gpio_out(ds, 0, req);
+
+    /* fti2c010 */
+    ds = sysbus_create_simple("fti2c010", 0x92900000, s->pic[51]);
+    s->i2c[0] = ds;
+    ds = sysbus_create_simple("fti2c010", 0x92A00000, s->pic[52]);
+    s->i2c[1] = ds;
 }
 
 static void a369soc_realize(DeviceState *dev, Error **errp)
diff --git a/hw/fti2c010.c b/hw/fti2c010.c
new file mode 100644
index 0000000..5b5606b
--- /dev/null
+++ b/hw/fti2c010.c
@@ -0,0 +1,224 @@
+/*
+ * QEMU model of the FTI2C010 Controller
+ *
+ * Copyright (C) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This file is licensed under GNU GPL v2+.
+ */
+
+#include "hw/sysbus.h"
+#include "hw/i2c.h"
+#include "sysemu/sysemu.h"
+
+#include "hw/fti2c010.h"
+
+#define I2C_RD  1
+#define I2C_WR  0
+
+#define TYPE_FTI2C010   "fti2c010"
+
+typedef struct Fti2c010State {
+    /*< private >*/
+    SysBusDevice parent;
+
+    /*< public >*/
+    MemoryRegion mmio;
+
+    qemu_irq irq;
+    i2c_bus *bus;
+
+    bool     recv;    /* I2C RD = true; I2C WR = false */
+
+    /* HW register cache */
+    uint32_t cr;
+    uint32_t sr;
+    uint32_t cdr;
+    uint32_t dr;
+    uint32_t tgsr;
+} Fti2c010State;
+
+#define FTI2C010(obj) \
+    OBJECT_CHECK(Fti2c010State, obj, TYPE_FTI2C010)
+
+static void
+fti2c010_update_irq(Fti2c010State *s)
+{
+    uint32_t sr = extract32(s->sr, 4, 8);
+    uint32_t cr = extract32(s->cr, 8, 8);
+    qemu_set_irq(s->irq, (sr & cr) ? 1 : 0);
+}
+
+static uint64_t
+fti2c010_mem_read(void *opaque, hwaddr addr, unsigned size)
+{
+    Fti2c010State *s = FTI2C010(opaque);
+    uint32_t ret = 0;
+
+    switch (addr) {
+    case REG_CR:
+        return s->cr;
+    case REG_SR:
+        ret = s->sr | (i2c_bus_busy(s->bus) ? SR_BB : 0);
+        s->sr &= 0xfffff00f;    /* clear RC status bits */
+        fti2c010_update_irq(s);
+        break;
+    case REG_CDR:
+        return s->cdr;
+    case REG_DR:
+        return s->dr;
+    case REG_TGSR:
+        return s->tgsr;
+    case REG_BMR:
+        return 0x00000003;  /* Slave mode: SCL=1, SDA=1 */
+    case REG_REVR:
+        return 0x00011000;  /* REV. 1.10.0 */
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "fti2c010: undefined memory access@%#" HWADDR_PRIx "\n", addr);
+        break;
+    }
+
+    return ret;
+}
+
+static void
+fti2c010_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+    Fti2c010State *s = FTI2C010(opaque);
+
+    switch (addr) {
+    case REG_CR:
+        s->cr = (uint32_t)val;
+        if (s->cr & CR_SLAVE_MASK) {
+            qemu_log_mask(LOG_UNIMP,
+                "fti2c010: slave mask deteced!\n");
+            s->cr &= ~CR_SLAVE_MASK;
+        }
+        if (s->cr & CR_I2CRST) {
+            s->dr = 0;
+            s->sr = 0;
+        } else if ((s->cr & CR_MASTER_EN) && (s->cr & CR_TBEN)) {
+            s->sr &= ~SR_ACK;
+            if (s->cr & CR_START) {
+                s->recv = !!(s->dr & I2C_RD);
+                if (!i2c_start_transfer(s->bus,
+                                        extract32(s->dr, 1, 7),
+                                        s->recv)) {
+                    s->sr |= SR_DT | SR_ACK;
+                } else {
+                    s->sr &= ~SR_DT;
+                }
+            } else {
+                if (s->recv) {
+                    s->dr = i2c_recv(s->bus);
+                    s->sr |= SR_DR;
+                } else {
+                    i2c_send(s->bus, (uint8_t)s->dr);
+                    s->sr |= SR_DT;
+                }
+                if (s->cr & CR_NACK) {
+                    i2c_nack(s->bus);
+                }
+                s->sr |= SR_ACK;
+                if (s->cr & CR_STOP) {
+                    i2c_end_transfer(s->bus);
+                }
+            }
+        }
+        s->cr &= ~(CR_TBEN | CR_I2CRST);
+        fti2c010_update_irq(s);
+        break;
+    case REG_CDR:
+        s->cdr = (uint32_t)val & 0x3ffff;
+        break;
+    case REG_DR:
+        s->dr  = (uint32_t)val & 0xff;
+        break;
+    case REG_TGSR:
+        s->tgsr = (uint32_t)val & 0x1fff;
+        break;
+        /* slave mode is useless to QEMU, ignore it. */
+    case REG_SAR:
+        qemu_log_mask(LOG_UNIMP,
+            "fti2c010: undefined memory access@%#" HWADDR_PRIx "\n", addr);
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "fti2c010: undefined memory access@%#" HWADDR_PRIx "\n", addr);
+        break;
+    }
+}
+
+static const MemoryRegionOps mmio_ops = {
+    .read  = fti2c010_mem_read,
+    .write = fti2c010_mem_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4
+    }
+};
+
+static void fti2c010_reset(DeviceState *ds)
+{
+    Fti2c010State *s = FTI2C010(SYS_BUS_DEVICE(ds));
+
+    s->cr   = 0;
+    s->sr   = 0;
+    s->cdr  = 0;
+    s->tgsr = TGSR_TSR(1) | TGSR_GSR(1);
+
+    qemu_set_irq(s->irq, 0);
+}
+
+static void fti2c010_realize(DeviceState *dev, Error **errp)
+{
+    Fti2c010State *s = FTI2C010(dev);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+
+    s->bus = i2c_init_bus(&sbd->qdev, "i2c");
+
+    memory_region_init_io(&s->mmio, &mmio_ops, s, TYPE_FTI2C010, 0x1000);
+    sysbus_init_mmio(sbd, &s->mmio);
+    sysbus_init_irq(sbd, &s->irq);
+}
+
+static const VMStateDescription vmstate_fti2c010 = {
+    .name = TYPE_FTI2C010,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(cr, Fti2c010State),
+        VMSTATE_UINT32(sr, Fti2c010State),
+        VMSTATE_UINT32(cdr, Fti2c010State),
+        VMSTATE_UINT32(dr, Fti2c010State),
+        VMSTATE_UINT32(tgsr, Fti2c010State),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void fti2c010_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->vmsd    = &vmstate_fti2c010;
+    dc->reset   = fti2c010_reset;
+    dc->realize = fti2c010_realize;
+    dc->no_user = 1;
+}
+
+static const TypeInfo fti2c010_info = {
+    .name           = TYPE_FTI2C010,
+    .parent         = TYPE_SYS_BUS_DEVICE,
+    .instance_size  = sizeof(Fti2c010State),
+    .class_init     = fti2c010_class_init,
+};
+
+static void fti2c010_register_types(void)
+{
+    type_register_static(&fti2c010_info);
+}
+
+type_init(fti2c010_register_types)
diff --git a/hw/fti2c010.h b/hw/fti2c010.h
new file mode 100644
index 0000000..1c69eb2
--- /dev/null
+++ b/hw/fti2c010.h
@@ -0,0 +1,74 @@
+/*
+ * QEMU model of the FTI2C010 Controller
+ *
+ * Copyright (C) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This file is licensed under GNU GPL v2+.
+ */
+
+#ifndef HW_ARM_FTI2C010_H
+#define HW_ARM_FTI2C010_H
+
+#include "qemu/bitops.h"
+
+/*
+ * FTI2C010 registers
+ */
+#define REG_CR              0x00    /* control register */
+#define REG_SR              0x04    /* status register */
+#define REG_CDR             0x08    /* clock division register */
+#define REG_DR              0x0C    /* data register */
+#define REG_SAR             0x10    /* slave address register */
+#define REG_TGSR            0x14    /* time & glitch suppression register */
+#define REG_BMR             0x18    /* bus monitor register */
+#define REG_REVR            0x30    /* revision register */
+
+/*
+ * REG_CR
+ */
+#define CR_STARTIEN         0x4000  /* start condition (slave only) */
+#define CR_ALIEN            0x2000  /* Arbitration lose (master only) */
+#define CR_SAMIEN           0x1000  /* slave address match (slave only) */
+#define CR_STOPIEN          0x800   /* stop condition (slave only) */
+#define CR_BERRIEN          0x400   /* NACK response (master only) */
+#define CR_DRIEN            0x200   /* data receive */
+#define CR_DTIEN            0x100   /* data transmit */
+#define CR_TBEN             0x80    /* transfer byte enable */
+#define CR_NACK             0x40    /* acknowledge mode: 0=ACK, 1=NACK */
+#define CR_STOP             0x20    /* stop (master only) */
+#define CR_START            0x10    /* start (master only) */
+#define CR_GCEN             0x8     /* general call message (slave only) */
+#define CR_SCLEN            0x4     /* enable clock (master only) */
+#define CR_I2CEN            0x2     /* enable I2C */
+#define CR_I2CRST           0x1     /* reset I2C */
+
+#define CR_SLAVE_MASK       (CR_GCEN | CR_SAMIEN | CR_STOPIEN | CR_STARTIEN)
+
+#define CR_MASTER_INTR      (CR_ALIEN | CR_BERRIEN | CR_DRIEN | CR_DTIEN)
+#define CR_MASTER_EN        (CR_SCLEN | CR_I2CEN)
+#define CR_MASTER_MODE      (CR_MASTER_INTR | CR_MASTER_EN)
+
+/*
+ * REG_SR
+ */
+#define SR_START            0x800
+#define SR_AL               0x400
+#define SR_GC               0x200
+#define SR_SAM              0x100
+#define SR_STOP             0x80
+#define SR_BERR             0x40
+#define SR_DR               0x20    /* received one new data byte */
+#define SR_DT               0x10    /* trandmitted one data byte */
+#define SR_BB               0x8     /* set when i2c bus is busy */
+#define SR_I2CB             0x4     /* set when fti2c010 is busy */
+#define SR_ACK              0x2
+#define SR_RW               0x1
+
+/*
+ * REG_TGSR
+ */
+#define TGSR_TSR(x)         ((x) & 0x3f)            /* setup/hold time */
+#define TGSR_GSR(x)         (((x) & 0x07) << 10)    /* glitch suppression */
+
+#endif
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH v9 14/24] hw: Add AudioCodecClass for wm87xx audio class abstration.
  2013-03-25 12:09 [Qemu-devel] [PATCH v9 00/24] hw/arm: add Faraday A369 SoC platform support Kuo-Jung Su
                   ` (12 preceding siblings ...)
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 13/24] hw/arm: add FTI2C010 I2C " Kuo-Jung Su
@ 2013-03-25 12:09 ` Kuo-Jung Su
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 15/24] hw: add WM8731 audio codec support Kuo-Jung Su
                   ` (9 subsequent siblings)
  23 siblings, 0 replies; 35+ messages in thread
From: Kuo-Jung Su @ 2013-03-25 12:09 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, i.mitsyanko, Blue Swirl, Paul Brook, Kuo-Jung Su,
	Andreas, fred.konrad

From: Kuo-Jung Su <dantesu@faraday-tech.com>

Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
---
 hw/Makefile.objs           |    1 +
 hw/arm/spitz.c             |    9 +++--
 hw/arm/z2.c                |    9 +++--
 hw/audio.c                 |   81 +++++++++++++++++++++++++++++++++++++++
 hw/audio.h                 |   56 +++++++++++++++++++++++++++
 hw/i2c.h                   |    9 -----
 hw/marvell_88w8618_audio.c |   23 +++++++----
 hw/wm8750.c                |   91 ++++++++++++++++++++++----------------------
 8 files changed, 211 insertions(+), 68 deletions(-)
 create mode 100644 hw/audio.c
 create mode 100644 hw/audio.h

diff --git a/hw/Makefile.objs b/hw/Makefile.objs
index 11812c6..808e2d0 100644
--- a/hw/Makefile.objs
+++ b/hw/Makefile.objs
@@ -168,6 +168,7 @@ common-obj-$(CONFIG_REALLY_VIRTFS) += 9pfs/
 common-obj-y += usb/
 common-obj-$(CONFIG_PTIMER) += ptimer.o
 common-obj-$(CONFIG_MAX7310) += max7310.o
+common-obj-y += audio.o
 common-obj-$(CONFIG_WM8750) += wm8750.o
 common-obj-$(CONFIG_TWL92230) += twl92230.o
 common-obj-$(CONFIG_TSC2005) += tsc2005.o
diff --git a/hw/arm/spitz.c b/hw/arm/spitz.c
index f5832be..a07dfe3 100644
--- a/hw/arm/spitz.c
+++ b/hw/arm/spitz.c
@@ -24,6 +24,7 @@
 #include "ui/console.h"
 #include "block/block.h"
 #include "audio/audio.h"
+#include "hw/audio.h"
 #include "hw/boards.h"
 #include "sysemu/blockdev.h"
 #include "hw/sysbus.h"
@@ -739,9 +740,11 @@ static void spitz_i2c_setup(PXA2xxState *cpu)
                     qemu_allocate_irqs(spitz_wm8750_addr, wm, 1)[0]);
     /* .. and to the sound interface.  */
     cpu->i2s->opaque = wm;
-    cpu->i2s->codec_out = wm8750_dac_dat;
-    cpu->i2s->codec_in = wm8750_adc_dat;
-    wm8750_data_req_set(wm, cpu->i2s->data_req, cpu->i2s);
+    cpu->i2s->codec_out = audio_codec_dac_dat;
+    cpu->i2s->codec_in = audio_codec_adc_dat;
+    audio_codec_data_req_set(DEVICE(wm),
+                             cpu->i2s->data_req,
+                             cpu->i2s);
 }
 
 static void spitz_akita_i2c_setup(PXA2xxState *cpu)
diff --git a/hw/arm/z2.c b/hw/arm/z2.c
index cbb6d80..1fecdc5 100644
--- a/hw/arm/z2.c
+++ b/hw/arm/z2.c
@@ -23,6 +23,7 @@
 #include "sysemu/blockdev.h"
 #include "ui/console.h"
 #include "audio/audio.h"
+#include "hw/audio.h"
 #include "exec/address-spaces.h"
 
 #ifdef DEBUG_Z2
@@ -353,9 +354,11 @@ static void z2_init(QEMUMachineInitArgs *args)
     i2c_create_slave(bus, "aer915", 0x55);
     wm = i2c_create_slave(bus, "wm8750", 0x1b);
     mpu->i2s->opaque = wm;
-    mpu->i2s->codec_out = wm8750_dac_dat;
-    mpu->i2s->codec_in = wm8750_adc_dat;
-    wm8750_data_req_set(wm, mpu->i2s->data_req, mpu->i2s);
+    mpu->i2s->codec_out = audio_codec_dac_dat;
+    mpu->i2s->codec_in = audio_codec_adc_dat;
+    audio_codec_data_req_set(DEVICE(wm),
+                             mpu->i2s->data_req,
+                             mpu->i2s);
 
     qdev_connect_gpio_out(mpu->gpio, Z2_GPIO_LCD_CS,
         qemu_allocate_irqs(z2_lcd_cs, z2_lcd, 1)[0]);
diff --git a/hw/audio.c b/hw/audio.c
new file mode 100644
index 0000000..35f99b9
--- /dev/null
+++ b/hw/audio.c
@@ -0,0 +1,81 @@
+/*
+ * Audio Codec Class
+ *
+ * Copyright (c) 2013 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This file is licensed under GNU GPL v2+.
+ */
+
+#include "hw/qdev.h"
+#include "hw/i2c.h"
+#include "hw/audio.h"
+
+void audio_codec_data_req_set(DeviceState *dev,
+                              void (*data_req)(void *, int, int),
+                              void *opaque)
+{
+    AudioCodecClass *k = AUDIO_CODEC_GET_CLASS(dev);
+    if (k->data_req_set) {
+        k->data_req_set(dev, data_req, opaque);
+    }
+}
+
+void audio_codec_dac_dat(void *opaque, uint32_t sample)
+{
+    AudioCodecClass *k = AUDIO_CODEC_GET_CLASS(opaque);
+    if (k->dac_dat) {
+        k->dac_dat(opaque, sample);
+    }
+}
+
+uint32_t audio_codec_adc_dat(void *opaque)
+{
+    AudioCodecClass *k = AUDIO_CODEC_GET_CLASS(opaque);
+    uint32_t ret = 0;
+    if (k->adc_dat) {
+        ret = k->adc_dat(opaque);
+    }
+    return ret;
+}
+
+void *audio_codec_dac_buffer(void *opaque, int samples)
+{
+    AudioCodecClass *k = AUDIO_CODEC_GET_CLASS(opaque);
+    void *ret = NULL;
+    if (k->dac_buffer) {
+        ret = k->dac_buffer(opaque, samples);
+    }
+    return ret;
+}
+
+void audio_codec_dac_commit(void *opaque)
+{
+    AudioCodecClass *k = AUDIO_CODEC_GET_CLASS(opaque);
+    if (k->dac_commit) {
+        k->dac_commit(opaque);
+    }
+}
+
+void audio_codec_set_bclk_in(void *opaque, int new_hz)
+{
+    AudioCodecClass *k = AUDIO_CODEC_GET_CLASS(opaque);
+    if (k->set_bclk_in) {
+        k->set_bclk_in(opaque, new_hz);
+    }
+}
+
+static const TypeInfo audio_codec_info = {
+    .name          = TYPE_AUDIO_CODEC,
+    .parent        = TYPE_I2C_SLAVE,
+    .instance_size = sizeof(AudioCodecState),
+    .abstract      = true,
+    .class_size    = sizeof(AudioCodecClass),
+};
+
+static void audio_codec_register_types(void)
+{
+    type_register_static(&audio_codec_info);
+}
+
+type_init(audio_codec_register_types)
diff --git a/hw/audio.h b/hw/audio.h
new file mode 100644
index 0000000..25a2743
--- /dev/null
+++ b/hw/audio.h
@@ -0,0 +1,56 @@
+/*
+ * Audio Codec Class
+ *
+ * Copyright (c) 2013 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This file is licensed under GNU GPL v2+.
+ */
+
+#ifndef QEMU_AUDIO_CODEC_H
+#define QEMU_AUDIO_CODEC_H
+
+#include "qdev.h"
+#include "i2c.h"
+
+typedef I2CSlave    AudioCodecState;
+
+#define TYPE_AUDIO_CODEC "audio-codec"
+#define AUDIO_CODEC(obj) \
+     OBJECT_CHECK(AudioCodecState, (obj), TYPE_AUDIO_CODEC)
+
+typedef struct AudioCodecClass {
+    /*< private >*/
+    I2CSlaveClass parent;
+
+    /*< public >*/
+    void     (*data_req_set)(DeviceState *dev,
+                             void (*data_req)(void *, int, int),
+                             void *opaque);
+    void     (*dac_dat)(void *opaque, uint32_t sample);
+    uint32_t (*adc_dat)(void *opaque);
+    void    *(*dac_buffer)(void *opaque, int samples);
+    void     (*dac_commit)(void *opaque);
+    void     (*set_bclk_in)(void *opaque, int new_hz);
+} AudioCodecClass;
+
+#define AUDIO_CODEC_CLASS(klass) \
+     OBJECT_CLASS_CHECK(AudioCodecClass, (klass), TYPE_AUDIO_CODEC)
+#define AUDIO_CODEC_GET_CLASS(obj) \
+     OBJECT_GET_CLASS(AudioCodecClass, (obj), TYPE_AUDIO_CODEC)
+
+void audio_codec_data_req_set(DeviceState *dev,
+                              void (*data_req)(void *, int, int),
+                              void *opaque);
+
+void audio_codec_dac_dat(void *opaque, uint32_t sample);
+
+uint32_t audio_codec_adc_dat(void *opaque);
+
+void *audio_codec_dac_buffer(void *opaque, int samples);
+
+void audio_codec_dac_commit(void *opaque);
+
+void audio_codec_set_bclk_in(void *opaque, int new_hz);
+
+#endif
diff --git a/hw/i2c.h b/hw/i2c.h
index 461392f..39566b5 100644
--- a/hw/i2c.h
+++ b/hw/i2c.h
@@ -63,15 +63,6 @@ int i2c_recv(i2c_bus *bus);
 
 DeviceState *i2c_create_slave(i2c_bus *bus, const char *name, uint8_t addr);
 
-/* wm8750.c */
-void wm8750_data_req_set(DeviceState *dev,
-                void (*data_req)(void *, int, int), void *opaque);
-void wm8750_dac_dat(void *opaque, uint32_t sample);
-uint32_t wm8750_adc_dat(void *opaque);
-void *wm8750_dac_buffer(void *opaque, int samples);
-void wm8750_dac_commit(void *opaque);
-void wm8750_set_bclk_in(void *opaque, int new_hz);
-
 /* lm832x.c */
 void lm832x_key_event(DeviceState *dev, int key, int state);
 
diff --git a/hw/marvell_88w8618_audio.c b/hw/marvell_88w8618_audio.c
index e042046..f6b9d4e 100644
--- a/hw/marvell_88w8618_audio.c
+++ b/hw/marvell_88w8618_audio.c
@@ -13,6 +13,7 @@
 #include "hw/hw.h"
 #include "hw/i2c.h"
 #include "hw/sysbus.h"
+#include "hw/audio.h"
 #include "audio/audio.h"
 
 #define MP_AUDIO_SIZE           0x00001000
@@ -82,32 +83,36 @@ static void mv88w8618_audio_callback(void *opaque, int free_out, int free_in)
     mem_buffer = buf;
     if (s->playback_mode & MP_AUDIO_16BIT_SAMPLE) {
         if (s->playback_mode & MP_AUDIO_MONO) {
-            codec_buffer = wm8750_dac_buffer(s->wm, block_size >> 1);
+            codec_buffer = audio_codec_dac_buffer(AUDIO_CODEC(s->wm),
+                                                  block_size >> 1);
             for (pos = 0; pos < block_size; pos += 2) {
                 *codec_buffer++ = *(int16_t *)mem_buffer;
                 *codec_buffer++ = *(int16_t *)mem_buffer;
                 mem_buffer += 2;
             }
         } else {
-            memcpy(wm8750_dac_buffer(s->wm, block_size >> 2),
-                   (uint32_t *)mem_buffer, block_size);
+            memcpy(audio_codec_dac_buffer(AUDIO_CODEC(s->wm),
+                                          block_size >> 2),
+                   (uint32_t *)mem_buffer,
+                   block_size);
         }
     } else {
         if (s->playback_mode & MP_AUDIO_MONO) {
-            codec_buffer = wm8750_dac_buffer(s->wm, block_size);
+            codec_buffer = audio_codec_dac_buffer(s->wm, block_size);
             for (pos = 0; pos < block_size; pos++) {
                 *codec_buffer++ = cpu_to_le16(256 * *mem_buffer);
                 *codec_buffer++ = cpu_to_le16(256 * *mem_buffer++);
             }
         } else {
-            codec_buffer = wm8750_dac_buffer(s->wm, block_size >> 1);
+            codec_buffer = audio_codec_dac_buffer(AUDIO_CODEC(s->wm),
+                                                  block_size >> 1);
             for (pos = 0; pos < block_size; pos += 2) {
                 *codec_buffer++ = cpu_to_le16(256 * *mem_buffer++);
                 *codec_buffer++ = cpu_to_le16(256 * *mem_buffer++);
             }
         }
     }
-    wm8750_dac_commit(s->wm);
+    audio_codec_dac_commit(AUDIO_CODEC(s->wm));
 
     s->last_free = free_out - block_size;
 
@@ -135,7 +140,7 @@ static void mv88w8618_audio_clock_update(mv88w8618_audio_state *s)
     }
     rate /= ((s->clock_div >> 8) & 0xff) + 1;
 
-    wm8750_set_bclk_in(s->wm, rate);
+    audio_codec_set_bclk_in(AUDIO_CODEC(s->wm), rate);
 }
 
 static uint64_t mv88w8618_audio_read(void *opaque, hwaddr offset,
@@ -244,7 +249,9 @@ static int mv88w8618_audio_init(SysBusDevice *dev)
 
     sysbus_init_irq(dev, &s->irq);
 
-    wm8750_data_req_set(s->wm, mv88w8618_audio_callback, s);
+    audio_codec_data_req_set(DEVICE(s->wm),
+                             mv88w8618_audio_callback,
+                             s);
 
     memory_region_init_io(&s->iomem, &mv88w8618_audio_ops, s,
                           "audio", MP_AUDIO_SIZE);
diff --git a/hw/wm8750.c b/hw/wm8750.c
index 0904cf4..1fffe55 100644
--- a/hw/wm8750.c
+++ b/hw/wm8750.c
@@ -9,12 +9,13 @@
 
 #include "hw/hw.h"
 #include "hw/i2c.h"
+#include "hw/audio.h"
 #include "audio/audio.h"
 
 #define IN_PORT_N	3
 #define OUT_PORT_N	3
 
-#define CODEC		"wm8750"
+#define TYPE_WM8750 "wm8750"
 
 typedef struct {
     int adc;
@@ -24,7 +25,7 @@ typedef struct {
 } WMRate;
 
 typedef struct {
-    I2CSlave i2c;
+    AudioCodecState parent;
     uint8_t i2c_data[2];
     int i2c_len;
     QEMUSoundCard card;
@@ -50,6 +51,9 @@ typedef struct {
     int adc_hz, dac_hz, ext_adc_hz, ext_dac_hz, master;
 } WM8750State;
 
+#define WM8750(obj) \
+    OBJECT_CHECK(WM8750State, obj, TYPE_WM8750)
+
 /* pow(10.0, -i / 20.0) * 255, i = 0..42 */
 static const uint8_t wm8750_vol_db_table[] = {
     255, 227, 203, 181, 161, 143, 128, 114, 102, 90, 81, 72, 64, 57, 51, 45,
@@ -80,14 +84,14 @@ static inline void wm8750_out_flush(WM8750State *s)
 
 static void wm8750_audio_in_cb(void *opaque, int avail_b)
 {
-    WM8750State *s = (WM8750State *) opaque;
+    WM8750State *s = WM8750(opaque);
     s->req_in = avail_b;
     s->data_req(s->opaque, s->req_out >> 2, avail_b >> 2);
 }
 
 static void wm8750_audio_out_cb(void *opaque, int free_b)
 {
-    WM8750State *s = (WM8750State *) opaque;
+    WM8750State *s = WM8750(opaque);
 
     if (s->idx_out >= free_b) {
         s->idx_out = free_b;
@@ -200,11 +204,11 @@ static void wm8750_set_format(WM8750State *s)
     in_fmt.fmt = AUD_FMT_S16;
 
     s->adc_voice[0] = AUD_open_in(&s->card, s->adc_voice[0],
-                    CODEC ".input1", s, wm8750_audio_in_cb, &in_fmt);
+                    TYPE_WM8750 ".input1", s, wm8750_audio_in_cb, &in_fmt);
     s->adc_voice[1] = AUD_open_in(&s->card, s->adc_voice[1],
-                    CODEC ".input2", s, wm8750_audio_in_cb, &in_fmt);
+                    TYPE_WM8750 ".input2", s, wm8750_audio_in_cb, &in_fmt);
     s->adc_voice[2] = AUD_open_in(&s->card, s->adc_voice[2],
-                    CODEC ".input3", s, wm8750_audio_in_cb, &in_fmt);
+                    TYPE_WM8750 ".input3", s, wm8750_audio_in_cb, &in_fmt);
 
     /* Setup output */
     out_fmt.endianness = 0;
@@ -213,12 +217,12 @@ static void wm8750_set_format(WM8750State *s)
     out_fmt.fmt = AUD_FMT_S16;
 
     s->dac_voice[0] = AUD_open_out(&s->card, s->dac_voice[0],
-                    CODEC ".speaker", s, wm8750_audio_out_cb, &out_fmt);
+                    TYPE_WM8750 ".speaker", s, wm8750_audio_out_cb, &out_fmt);
     s->dac_voice[1] = AUD_open_out(&s->card, s->dac_voice[1],
-                    CODEC ".headphone", s, wm8750_audio_out_cb, &out_fmt);
+                    TYPE_WM8750 ".headphone", s, wm8750_audio_out_cb, &out_fmt);
     /* MONOMIX is also in stereo for simplicity */
     s->dac_voice[2] = AUD_open_out(&s->card, s->dac_voice[2],
-                    CODEC ".monomix", s, wm8750_audio_out_cb, &out_fmt);
+                    TYPE_WM8750 ".monomix", s, wm8750_audio_out_cb, &out_fmt);
     /* no sense emulating OUT3 which is a mix of other outputs */
 
     wm8750_vol_update(s);
@@ -256,7 +260,7 @@ static void wm8750_clk_update(WM8750State *s, int ext)
 
 static void wm8750_reset(I2CSlave *i2c)
 {
-    WM8750State *s = (WM8750State *) i2c;
+    WM8750State *s = WM8750(i2c);
     s->rate = &wm_rate_table[0];
     s->enable = 0;
     wm8750_clk_update(s, 1);
@@ -299,7 +303,7 @@ static void wm8750_reset(I2CSlave *i2c)
 
 static void wm8750_event(I2CSlave *i2c, enum i2c_event event)
 {
-    WM8750State *s = (WM8750State *) i2c;
+    WM8750State *s = WM8750(i2c);
 
     switch (event) {
     case I2C_START_SEND:
@@ -356,7 +360,7 @@ static void wm8750_event(I2CSlave *i2c, enum i2c_event event)
 
 static int wm8750_tx(I2CSlave *i2c, uint8_t data)
 {
-    WM8750State *s = (WM8750State *) i2c;
+    WM8750State *s = WM8750(i2c);
     uint8_t cmd;
     uint16_t value;
 
@@ -542,7 +546,7 @@ static int wm8750_tx(I2CSlave *i2c, uint8_t data)
         break;
 
     case WM8750_RESET:	/* Reset */
-        wm8750_reset(&s->i2c);
+        wm8750_reset(I2C_SLAVE(&s->parent));
         break;
 
 #ifdef VERBOSE
@@ -561,21 +565,21 @@ static int wm8750_rx(I2CSlave *i2c)
 
 static void wm8750_pre_save(void *opaque)
 {
-    WM8750State *s = opaque;
+    WM8750State *s = WM8750(opaque);
 
     s->rate_vmstate = s->rate - wm_rate_table;
 }
 
 static int wm8750_post_load(void *opaque, int version_id)
 {
-    WM8750State *s = opaque;
+    WM8750State *s = WM8750(opaque);
 
     s->rate = &wm_rate_table[s->rate_vmstate & 0x1f];
     return 0;
 }
 
 static const VMStateDescription vmstate_wm8750 = {
-    .name = CODEC,
+    .name = TYPE_WM8750,
     .version_id = 0,
     .minimum_version_id = 0,
     .minimum_version_id_old = 0,
@@ -604,42 +608,31 @@ static const VMStateDescription vmstate_wm8750 = {
         VMSTATE_UINT8(format, WM8750State),
         VMSTATE_UINT8(power, WM8750State),
         VMSTATE_UINT8(rate_vmstate, WM8750State),
-        VMSTATE_I2C_SLAVE(i2c, WM8750State),
         VMSTATE_END_OF_LIST()
     }
 };
 
 static int wm8750_init(I2CSlave *i2c)
 {
-    WM8750State *s = FROM_I2C_SLAVE(WM8750State, i2c);
+    WM8750State *s = WM8750(i2c);
 
-    AUD_register_card(CODEC, &s->card);
-    wm8750_reset(&s->i2c);
+    AUD_register_card(TYPE_WM8750, &s->card);
+    wm8750_reset(I2C_SLAVE(&s->parent));
 
     return 0;
 }
 
-#if 0
-static void wm8750_fini(I2CSlave *i2c)
-{
-    WM8750State *s = (WM8750State *) i2c;
-    wm8750_reset(&s->i2c);
-    AUD_remove_card(&s->card);
-    g_free(s);
-}
-#endif
-
-void wm8750_data_req_set(DeviceState *dev,
+static void wm8750_data_req_set(DeviceState *dev,
                 void (*data_req)(void *, int, int), void *opaque)
 {
-    WM8750State *s = FROM_I2C_SLAVE(WM8750State, I2C_SLAVE(dev));
+    WM8750State *s = WM8750(dev);
     s->data_req = data_req;
     s->opaque = opaque;
 }
 
-void wm8750_dac_dat(void *opaque, uint32_t sample)
+static void wm8750_dac_dat(void *opaque, uint32_t sample)
 {
-    WM8750State *s = (WM8750State *) opaque;
+    WM8750State *s = WM8750(opaque);
 
     *(uint32_t *) &s->data_out[s->idx_out] = sample;
     s->req_out -= 4;
@@ -648,9 +641,9 @@ void wm8750_dac_dat(void *opaque, uint32_t sample)
         wm8750_out_flush(s);
 }
 
-void *wm8750_dac_buffer(void *opaque, int samples)
+static void *wm8750_dac_buffer(void *opaque, int samples)
 {
-    WM8750State *s = (WM8750State *) opaque;
+    WM8750State *s = WM8750(opaque);
     /* XXX: Should check if there are <i>samples</i> free samples available */
     void *ret = s->data_out + s->idx_out;
 
@@ -659,16 +652,16 @@ void *wm8750_dac_buffer(void *opaque, int samples)
     return ret;
 }
 
-void wm8750_dac_commit(void *opaque)
+static void wm8750_dac_commit(void *opaque)
 {
-    WM8750State *s = (WM8750State *) opaque;
+    WM8750State *s = WM8750(opaque);
 
     wm8750_out_flush(s);
 }
 
-uint32_t wm8750_adc_dat(void *opaque)
+static uint32_t wm8750_adc_dat(void *opaque)
 {
-    WM8750State *s = (WM8750State *) opaque;
+    WM8750State *s = WM8750(opaque);
     uint32_t *data;
 
     if (s->idx_in >= sizeof(s->data_in))
@@ -680,9 +673,9 @@ uint32_t wm8750_adc_dat(void *opaque)
     return *data;
 }
 
-void wm8750_set_bclk_in(void *opaque, int new_hz)
+static void wm8750_set_bclk_in(void *opaque, int new_hz)
 {
-    WM8750State *s = (WM8750State *) opaque;
+    WM8750State *s = WM8750(opaque);
 
     s->ext_adc_hz = new_hz;
     s->ext_dac_hz = new_hz;
@@ -693,6 +686,14 @@ static void wm8750_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
     I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass);
+    AudioCodecClass *ac = AUDIO_CODEC_CLASS(klass);
+
+    ac->data_req_set = wm8750_data_req_set;
+    ac->dac_dat = wm8750_dac_dat;
+    ac->adc_dat = wm8750_adc_dat;
+    ac->dac_buffer = wm8750_dac_buffer;
+    ac->dac_commit = wm8750_dac_commit;
+    ac->set_bclk_in = wm8750_set_bclk_in;
 
     sc->init = wm8750_init;
     sc->event = wm8750_event;
@@ -702,8 +703,8 @@ static void wm8750_class_init(ObjectClass *klass, void *data)
 }
 
 static const TypeInfo wm8750_info = {
-    .name          = "wm8750",
-    .parent        = TYPE_I2C_SLAVE,
+    .name          = TYPE_WM8750,
+    .parent        = TYPE_AUDIO_CODEC,
     .instance_size = sizeof(WM8750State),
     .class_init    = wm8750_class_init,
 };
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH v9 15/24] hw: add WM8731 audio codec support
  2013-03-25 12:09 [Qemu-devel] [PATCH v9 00/24] hw/arm: add Faraday A369 SoC platform support Kuo-Jung Su
                   ` (13 preceding siblings ...)
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 14/24] hw: Add AudioCodecClass for wm87xx audio class abstration Kuo-Jung Su
@ 2013-03-25 12:09 ` Kuo-Jung Su
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 16/24] The FTSSP010 is a multi-function synchronous serial port interface controller which supports SSP, SPI, I2S, AC97 and SPDIF Kuo-Jung Su
                   ` (8 subsequent siblings)
  23 siblings, 0 replies; 35+ messages in thread
From: Kuo-Jung Su @ 2013-03-25 12:09 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, i.mitsyanko, Blue Swirl, Paul Brook, Kuo-Jung Su,
	Andreas, fred.konrad

From: Kuo-Jung Su <dantesu@faraday-tech.com>

Wolfson WM8731 is a simple audio codec for embedded systems.
It has 2 input and 1 output ports:

** Input **
    1. Linue-In
    2. Microphone

** Output **
    1. Headphone out

BTW it's based on hw/wm8750.c with 16-bit I2S support by default.

Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
---
 default-configs/arm-softmmu.mak |    1 +
 hw/Makefile.objs                |    1 +
 hw/wm8731.c                     |  505 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 507 insertions(+)
 create mode 100644 hw/wm8731.c

diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
index ab87035..b682c24 100644
--- a/default-configs/arm-softmmu.mak
+++ b/default-configs/arm-softmmu.mak
@@ -11,6 +11,7 @@ CONFIG_SERIAL=y
 CONFIG_PTIMER=y
 CONFIG_SD=y
 CONFIG_MAX7310=y
+CONFIG_WM8731=y
 CONFIG_WM8750=y
 CONFIG_TWL92230=y
 CONFIG_TSC2005=y
diff --git a/hw/Makefile.objs b/hw/Makefile.objs
index 808e2d0..5b4bf20 100644
--- a/hw/Makefile.objs
+++ b/hw/Makefile.objs
@@ -169,6 +169,7 @@ common-obj-y += usb/
 common-obj-$(CONFIG_PTIMER) += ptimer.o
 common-obj-$(CONFIG_MAX7310) += max7310.o
 common-obj-y += audio.o
+common-obj-$(CONFIG_WM8731) += wm8731.o
 common-obj-$(CONFIG_WM8750) += wm8750.o
 common-obj-$(CONFIG_TWL92230) += twl92230.o
 common-obj-$(CONFIG_TSC2005) += tsc2005.o
diff --git a/hw/wm8731.c b/hw/wm8731.c
new file mode 100644
index 0000000..820341f
--- /dev/null
+++ b/hw/wm8731.c
@@ -0,0 +1,505 @@
+/*
+ * WM8731 audio codec.
+ *
+ * base is wm8750.c
+ *
+ * Copyright (c) 2013 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This file is licensed under GNU GPL v2+.
+ */
+
+#include "hw/hw.h"
+#include "hw/i2c.h"
+#include "hw/audio.h"
+#include "audio/audio.h"
+
+#define IN_PORT_N   2
+#define OUT_PORT_N  1
+
+#define TYPE_WM8731 "wm8731"
+
+typedef struct WMRate {
+    int adc;
+    int adc_hz;
+    int dac;
+    int dac_hz;
+} WMRate;
+
+typedef struct WM8731State {
+    AudioCodecState parent;
+
+    uint8_t i2c_data[2];
+    int i2c_len;
+    QEMUSoundCard card;
+    SWVoiceIn *adc_voice[IN_PORT_N];
+    SWVoiceOut *dac_voice[OUT_PORT_N];
+    void (*data_req)(void *, int, int);
+    void *opaque;
+    uint8_t data_in[4096];
+    uint8_t data_out[4096];
+    int idx_in, req_in;
+    int idx_out, req_out;
+
+    SWVoiceOut **out[2];
+    uint8_t outvol[2];
+    SWVoiceIn **in[2];
+    uint8_t invol[2], inmute[2], mutemic;
+
+    uint8_t mute;
+    uint8_t power, format, active;
+    const WMRate *rate;
+    uint8_t rate_vmstate;
+    int adc_hz, dac_hz, ext_adc_hz, ext_dac_hz, master;
+} WM8731State;
+
+#define WM8731(obj) \
+    OBJECT_CHECK(WM8731State, obj, TYPE_WM8731)
+
+#define WM8731_OUTVOL_TRANSFORM(x)  (x << 1)
+#define WM8731_INVOL_TRANSFORM(x)   (x << 3)
+
+static inline void wm8731_in_load(WM8731State *s)
+{
+    if (s->idx_in + s->req_in <= sizeof(s->data_in)) {
+        return;
+    }
+    s->idx_in = audio_MAX(0, (int) sizeof(s->data_in) - s->req_in);
+    AUD_read(*s->in[0], s->data_in + s->idx_in,
+             sizeof(s->data_in) - s->idx_in);
+}
+
+static inline void wm8731_out_flush(WM8731State *s)
+{
+    int sent = 0;
+    while (sent < s->idx_out) {
+        sent += AUD_write(*s->out[0], s->data_out + sent, s->idx_out - sent)
+                ? 0 : s->idx_out;
+    }
+    s->idx_out = 0;
+}
+
+static void wm8731_audio_in_cb(void *opaque, int avail_b)
+{
+    WM8731State *s = WM8731(opaque);
+    s->req_in = avail_b;
+    /* 16 bit samples */
+    s->data_req(s->opaque, s->req_out >> 1, avail_b >> 1);
+}
+
+static void wm8731_audio_out_cb(void *opaque, int free_b)
+{
+    WM8731State *s = WM8731(opaque);
+
+    if (s->idx_out >= free_b) {
+        s->idx_out = free_b;
+        s->req_out = 0;
+        wm8731_out_flush(s);
+    } else {
+        s->req_out = free_b - s->idx_out;
+    }
+    /* 16 bit samples */
+    s->data_req(s->opaque, s->req_out >> 1, s->req_in >> 1);
+}
+
+static const WMRate wm_rate_table[] = {
+    {  256, 48000,  256, 48000 },    /* SR: 0000, BOSR: 0 */
+    {  384, 48000,  384, 48000 },    /* SR: 0000, BOSR: 1 */
+    {  256, 48000,  256,  8000 },    /* SR: 0001, BOSR: 0 */
+    {  384, 48000,  384,  8000 },    /* SR: 0001, BOSR: 1 */
+    {  256,  8000,  256, 48000 },    /* SR: 0010, BOSR: 0 */
+    {  384,  8000,  384, 48000 },    /* SR: 0010, BOSR: 1 */
+    {  256,  8000,  256,  8000 },    /* SR: 0011, BOSR: 0 */
+    {  384,  8000,  384,  8000 },    /* SR: 0011, BOSR: 1 */
+    {  256, 32000,  256, 32000 },    /* SR: 0110, BOSR: 0 */
+    {  384, 32000,  384, 32000 },    /* SR: 0110, BOSR: 1 */
+    {  128, 96000,  128, 96000 },    /* SR: 0111, BOSR: 0 */
+    {  192, 96000,  192, 96000 },    /* SR: 0111, BOSR: 1 */
+    {  256, 44100,  256, 44100 },    /* SR: 1000, BOSR: 0 */
+    {  384, 44100,  384, 44100 },    /* SR: 1000, BOSR: 1 */
+    {  256, 44100,  256,  8000 },    /* SR: 1001, BOSR: 0 */
+    {  384, 44100,  384,  8000 },    /* SR: 1001, BOSR: 1 */
+    {  256,  8000,  256, 44100 },    /* SR: 1010, BOSR: 0 */
+    {  384,  8000,  384, 44100 },    /* SR: 1010, BOSR: 1 */
+    {  256,  8000,  256,  8000 },    /* SR: 1011, BOSR: 0 */
+    {  384,  8000,  384,  8000 },    /* SR: 1011, BOSR: 1 */
+    {  128, 88200,  128, 88200 },    /* SR: 1011, BOSR: 0 */
+    {  192, 88200,  192, 88200 },    /* SR: 1011, BOSR: 1 */
+};
+
+static void wm8731_vol_update(WM8731State *s)
+{
+    AUD_set_volume_in(s->adc_voice[0], s->mute,
+                    s->inmute[0] ? 0 : WM8731_INVOL_TRANSFORM(s->invol[0]),
+                    s->inmute[1] ? 0 : WM8731_INVOL_TRANSFORM(s->invol[1]));
+    AUD_set_volume_in(s->adc_voice[1], s->mute,
+                    s->mutemic ? 0 : WM8731_INVOL_TRANSFORM(s->invol[0]),
+                    s->mutemic ? 0 : WM8731_INVOL_TRANSFORM(s->invol[1]));
+
+    /* Headphone: LOUT1VOL ROUT1VOL */
+    AUD_set_volume_out(s->dac_voice[0], s->mute,
+                    WM8731_OUTVOL_TRANSFORM(s->outvol[0]),
+                    WM8731_OUTVOL_TRANSFORM(s->outvol[1]));
+}
+
+static void wm8731_set_format(WM8731State *s)
+{
+    int i;
+    struct audsettings in_fmt;
+    struct audsettings out_fmt;
+
+    wm8731_out_flush(s);
+
+    if (s->in[0] && *s->in[0]) {
+        AUD_set_active_in(*s->in[0], 0);
+    }
+    if (s->out[0] && *s->out[0]) {
+        AUD_set_active_out(*s->out[0], 0);
+    }
+    for (i = 0; i < IN_PORT_N; ++i) {
+        if (s->adc_voice[i]) {
+            AUD_close_in(&s->card, s->adc_voice[i]);
+            s->adc_voice[i] = NULL;
+        }
+    }
+    for (i = 0; i < OUT_PORT_N; ++i) {
+        if (s->dac_voice[i]) {
+            AUD_close_out(&s->card, s->dac_voice[i]);
+            s->dac_voice[i] = NULL;
+        }
+    }
+
+    /* Setup input */
+    in_fmt.endianness = 0;
+    in_fmt.nchannels = 2;
+    in_fmt.freq = s->adc_hz;
+    in_fmt.fmt = AUD_FMT_S16;
+
+    s->adc_voice[0] = AUD_open_in(&s->card,
+                                  s->adc_voice[0],
+                                  TYPE_WM8731 ".line-in",
+                                  s,
+                                  wm8731_audio_in_cb,
+                                  &in_fmt);
+    s->adc_voice[1] = AUD_open_in(&s->card,
+                                  s->adc_voice[1],
+                                  TYPE_WM8731 ".microphone",
+                                  s,
+                                  wm8731_audio_in_cb,
+                                  &in_fmt);
+
+    /* Setup output */
+    out_fmt.endianness = 0;
+    out_fmt.nchannels = 2;
+    out_fmt.freq = s->dac_hz;
+    out_fmt.fmt = AUD_FMT_S16;
+
+    s->dac_voice[0] = AUD_open_out(&s->card,
+                                   s->dac_voice[0],
+                                   TYPE_WM8731 ".headphone",
+                                   s,
+                                   wm8731_audio_out_cb,
+                                   &out_fmt);
+
+    wm8731_vol_update(s);
+
+    /* We should connect the left and right channels to their
+     * respective inputs/outputs but we have completely no need
+     * for mixing or combining paths to different ports, so we
+     * connect both channels to where the left channel is routed.  */
+    if (s->in[0] && *s->in[0]) {
+        AUD_set_active_in(*s->in[0], 1);
+    }
+    if (s->out[0] && *s->out[0]) {
+        AUD_set_active_out(*s->out[0], 1);
+    }
+}
+
+static void wm8731_clk_update(WM8731State *s, int ext)
+{
+    if (s->master || !s->ext_dac_hz) {
+        s->dac_hz = s->rate->dac_hz;
+    } else {
+        s->dac_hz = s->ext_dac_hz;
+    }
+    if (s->master || !s->ext_adc_hz) {
+        s->adc_hz = s->rate->adc_hz;
+    } else {
+        s->adc_hz = s->ext_adc_hz;
+    }
+    if (s->master || (!s->ext_dac_hz && !s->ext_adc_hz)) {
+        if (!ext) {
+            wm8731_set_format(s);
+        }
+    } else {
+        if (ext) {
+            wm8731_set_format(s);
+        }
+    }
+}
+
+static void wm8731_reset(I2CSlave *i2c)
+{
+    WM8731State *s = WM8731(i2c);
+    s->rate = &wm_rate_table[0];
+    wm8731_clk_update(s, 1);
+    s->in[0] = &s->adc_voice[0];
+    s->invol[0] = 0x17;
+    s->invol[1] = 0x17;
+    s->out[0] = &s->dac_voice[0];
+    s->outvol[0] = 0x39;
+    s->outvol[1] = 0x39;
+    s->inmute[0] = 0;
+    s->inmute[1] = 0;
+    s->mutemic = 1;
+    s->mute = 1;
+    s->power = 0x9f;
+    s->format = 0x02;    /* I2S, 16-bits */
+    s->active = 0;
+    s->idx_in = sizeof(s->data_in);
+    s->req_in = 0;
+    s->idx_out = 0;
+    s->req_out = 0;
+    wm8731_vol_update(s);
+    s->i2c_len = 0;
+}
+
+static void wm8731_event(I2CSlave *i2c, enum i2c_event event)
+{
+    WM8731State *s = WM8731(i2c);
+
+    switch (event) {
+    case I2C_START_SEND:
+        s->i2c_len = 0;
+        break;
+    case I2C_FINISH:
+#ifdef VERBOSE
+        if (s->i2c_len < 2) {
+            printf("wm8731_event: message too short (%i bytes)\n",
+                    s->i2c_len);
+        }
+#endif
+        break;
+    default:
+        break;
+    }
+}
+
+#define WM8731_LINVOL   0x00
+#define WM8731_RINVOL   0x01
+#define WM8731_LOUT1V   0x02
+#define WM8731_ROUT1V   0x03
+#define WM8731_APANA    0x04
+#define WM8731_APDIGI   0x05
+#define WM8731_PWR      0x06
+#define WM8731_IFACE    0x07
+#define WM8731_SRATE    0x08
+#define WM8731_ACTIVE   0x09
+#define WM8731_RESET    0x0f
+
+static int wm8731_tx(I2CSlave *i2c, uint8_t data)
+{
+    WM8731State *s = WM8731(i2c);
+    uint8_t cmd;
+    uint16_t value;
+
+    if (s->i2c_len >= 2) {
+#ifdef VERBOSE
+        printf("wm8731_tx: long message (%i bytes)\n", s->i2c_len);
+#endif
+        return 1;
+    }
+    s->i2c_data[s->i2c_len++] = data;
+    if (s->i2c_len != 2) {
+        return 0;
+    }
+
+    cmd = s->i2c_data[0] >> 1;
+    value = ((s->i2c_data[0] << 8) | s->i2c_data[1]) & 0x1ff;
+
+    switch (cmd) {
+    case WM8731_LINVOL:
+        s->invol[0] = value & 0x1f;         /* LINVOL */
+        s->inmute[0] = (value >> 7) & 1;    /* LINMUTE */
+        wm8731_vol_update(s);
+        break;
+    case WM8731_RINVOL:
+        s->invol[1] = value & 0x1f;         /* RINVOL */
+        s->inmute[1] = (value >> 7) & 1;    /* RINMUTE */
+        wm8731_vol_update(s);
+        break;
+    case WM8731_LOUT1V:
+        s->outvol[0] = value & 0x7f;        /* LHPVOL */
+        wm8731_vol_update(s);
+        break;
+    case WM8731_ROUT1V:
+        s->outvol[1] = value & 0x7f;        /* RHPVOL */
+        wm8731_vol_update(s);
+        break;
+    case WM8731_APANA:
+        s->mutemic = (value >> 1) & 1;      /* MUTEMIC */
+        if (value & 0x04) {
+            s->in[0] = &s->adc_voice[1];    /* MIC */
+        } else {
+            s->in[0] = &s->adc_voice[0];    /* LINE-IN */
+        }
+        break;
+    case WM8731_APDIGI:
+        if (s->mute != ((value >> 3) & 1)) {
+            s->mute = (value >> 3) & 1;     /* DACMU */
+            wm8731_vol_update(s);
+        }
+        break;
+    case WM8731_PWR:
+        s->power = (uint8_t)(value & 0xff);
+        wm8731_set_format(s);
+        break;
+    case WM8731_IFACE:
+        s->format = value;
+        s->master = (value >> 6) & 1;       /* MS */
+        wm8731_clk_update(s, s->master);
+        break;
+    case WM8731_SRATE:
+        s->rate = &wm_rate_table[(value >> 1) & 0x1f];
+        wm8731_clk_update(s, 0);
+        break;
+    case WM8731_ACTIVE:
+        s->active = (uint8_t)(value & 1);
+        break;
+    case WM8731_RESET:    /* Reset */
+        if (value == 0) {
+            wm8731_reset(I2C_SLAVE(&s->parent));
+        }
+        break;
+
+#ifdef VERBOSE
+    default:
+        printf("wm8731_tx: unknown register %02x\n", cmd);
+#endif
+    }
+
+    return 0;
+}
+
+static int wm8731_rx(I2CSlave *i2c)
+{
+    return 0x00;
+}
+
+static void wm8731_pre_save(void *opaque)
+{
+    WM8731State *s = WM8731(opaque);
+
+    s->rate_vmstate = s->rate - wm_rate_table;
+}
+
+static int wm8731_post_load(void *opaque, int version_id)
+{
+    WM8731State *s = WM8731(opaque);
+
+    s->rate = &wm_rate_table[s->rate_vmstate & 0x1f];
+    return 0;
+}
+
+static const VMStateDescription vmstate_wm8731 = {
+    .name = TYPE_WM8731,
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .minimum_version_id_old = 0,
+    .pre_save = wm8731_pre_save,
+    .post_load = wm8731_post_load,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT8_ARRAY(i2c_data, WM8731State, 2),
+        VMSTATE_INT32(i2c_len, WM8731State),
+        VMSTATE_INT32(idx_in, WM8731State),
+        VMSTATE_INT32(req_in, WM8731State),
+        VMSTATE_INT32(idx_out, WM8731State),
+        VMSTATE_INT32(req_out, WM8731State),
+        VMSTATE_UINT8_ARRAY(outvol, WM8731State, 2),
+        VMSTATE_UINT8_ARRAY(invol, WM8731State, 2),
+        VMSTATE_UINT8_ARRAY(inmute, WM8731State, 2),
+        VMSTATE_UINT8(mutemic, WM8731State),
+        VMSTATE_UINT8(mute, WM8731State),
+        VMSTATE_UINT8(format, WM8731State),
+        VMSTATE_UINT8(power, WM8731State),
+        VMSTATE_UINT8(active, WM8731State),
+        VMSTATE_UINT8(rate_vmstate, WM8731State),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static int wm8731_init(I2CSlave *i2c)
+{
+    WM8731State *s = WM8731(i2c);
+
+    AUD_register_card(TYPE_WM8731, &s->card);
+    wm8731_reset(I2C_SLAVE(&s->parent));
+
+    return 0;
+}
+
+static void wm8731_data_req_set(DeviceState *dev,
+                void (*data_req)(void *, int, int), void *opaque)
+{
+    WM8731State *s = WM8731(dev);
+    s->data_req = data_req;
+    s->opaque = opaque;
+}
+
+static void wm8731_dac_dat(void *opaque, uint32_t sample)
+{
+    WM8731State *s = WM8731(opaque);
+    /* 16-bit samples */
+    *(uint16_t *) &s->data_out[s->idx_out] = (uint16_t)sample;
+    s->req_out -= 2;
+    s->idx_out += 2;
+    if (s->idx_out >= sizeof(s->data_out) || s->req_out <= 0) {
+        wm8731_out_flush(s);
+    }
+}
+
+static uint32_t wm8731_adc_dat(void *opaque)
+{
+    WM8731State *s = WM8731(opaque);
+    uint16_t sample;
+
+    if (s->idx_in >= sizeof(s->data_in)) {
+        wm8731_in_load(s);
+    }
+
+    sample = *(uint16_t *) &s->data_in[s->idx_in];
+    s->req_in -= 2;
+    s->idx_in += 2;
+    return sample;
+}
+
+static void wm8731_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass);
+    AudioCodecClass *ac = AUDIO_CODEC_CLASS(klass);
+
+    ac->data_req_set = wm8731_data_req_set;
+    ac->dac_dat = wm8731_dac_dat;
+    ac->adc_dat = wm8731_adc_dat;
+
+    sc->init  = wm8731_init;
+    sc->event = wm8731_event;
+    sc->recv  = wm8731_rx;
+    sc->send  = wm8731_tx;
+    dc->vmsd  = &vmstate_wm8731;
+}
+
+static const TypeInfo wm8731_info = {
+    .name          = TYPE_WM8731,
+    .parent        = TYPE_AUDIO_CODEC,
+    .instance_size = sizeof(WM8731State),
+    .class_init    = wm8731_class_init,
+};
+
+static void wm8731_register_types(void)
+{
+    type_register_static(&wm8731_info);
+}
+
+type_init(wm8731_register_types)
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH v9 16/24] The FTSSP010 is a multi-function synchronous serial port interface controller which supports SSP, SPI, I2S, AC97 and SPDIF.
  2013-03-25 12:09 [Qemu-devel] [PATCH v9 00/24] hw/arm: add Faraday A369 SoC platform support Kuo-Jung Su
                   ` (14 preceding siblings ...)
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 15/24] hw: add WM8731 audio codec support Kuo-Jung Su
@ 2013-03-25 12:09 ` Kuo-Jung Su
  2013-03-31  2:39   ` Peter Crosthwaite
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 17/24] qemu/bitops.h: add the bit ordering reversal functions Kuo-Jung Su
                   ` (7 subsequent siblings)
  23 siblings, 1 reply; 35+ messages in thread
From: Kuo-Jung Su @ 2013-03-25 12:09 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, i.mitsyanko, Blue Swirl, Paul Brook, Kuo-Jung Su,
	Andreas, fred.konrad

From: Kuo-Jung Su <dantesu@faraday-tech.com>

Only I2S and SPI protocol have been implemented in this patch.

Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
---
 hw/arm/Makefile.objs    |    3 +-
 hw/arm/ftplat_a369.c    |   31 +++
 hw/arm/ftplat_a369soc.c |   17 ++
 hw/faraday.h            |    3 +
 hw/ftssp010.c           |  504 +++++++++++++++++++++++++++++++++++++++++++++++
 hw/ftssp010.h           |   96 +++++++++
 6 files changed, 653 insertions(+), 1 deletion(-)
 create mode 100644 hw/ftssp010.c
 create mode 100644 hw/ftssp010.h

diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 2bb67f7..42c8472 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -25,7 +25,8 @@ obj-y += strongarm.o
 obj-y += imx_serial.o imx_ccm.o imx_timer.o imx_avic.o
 obj-$(CONFIG_KVM) += kvm/arm_gic.o
 obj-y += ftintc020.o ftahbc020.o ftddrii030.o ftpwmtmr010.o ftwdt010.o \
-                ftrtc011.o ftdmac020.o ftapbbrg020.o ftnandc021.o fti2c010.o
+                ftrtc011.o ftdmac020.o ftapbbrg020.o ftnandc021.o fti2c010.o \
+                ftssp010.o
 
 obj-y := $(addprefix ../,$(obj-y))
 
diff --git a/hw/arm/ftplat_a369.c b/hw/arm/ftplat_a369.c
index 827c58a..922fb55 100644
--- a/hw/arm/ftplat_a369.c
+++ b/hw/arm/ftplat_a369.c
@@ -11,6 +11,7 @@
 #include "hw/arm-misc.h"
 #include "hw/devices.h"
 #include "hw/i2c.h"
+#include "hw/audio.h"
 #include "hw/boards.h"
 #include "hw/ssi.h"
 #include "net/net.h"
@@ -28,6 +29,7 @@ static void a369_system_reset(void *opaque)
 
 static void a369_board_init(QEMUMachineInitArgs *args)
 {
+    int i, nr_flash;
     ARMCPU *cpu;
     DeviceState *ds;
     FaradaySoCState *s;
@@ -79,6 +81,35 @@ static void a369_board_init(QEMUMachineInitArgs *args)
         abort();
     }
 
+    /* Attach the spi flash to ftssp010.0 */
+    nr_flash = 1;
+    for (i = 0; i < nr_flash; i++) {
+        SSIBus *ssi = (SSIBus *)qdev_get_child_bus(s->spi[0], "spi");
+        DeviceState *fl = ssi_create_slave_no_init(ssi, "w25q64");
+        qemu_irq cs_line;
+
+        qdev_init_nofail(fl);
+        cs_line = qdev_get_gpio_in(fl, 0);
+        sysbus_connect_irq(SYS_BUS_DEVICE(s->spi[0]), i + 1, cs_line);
+    }
+
+    /* Attach the wm8731 to fti2c010.0 & ftssp010.0 */
+    for (i = 0; i < 1; ++i) {
+        i2c_bus *i2c = (i2c_bus *)qdev_get_child_bus(s->i2c[0], "i2c");
+        ds = i2c_create_slave(i2c, "wm8731", 0x1B);
+        object_property_set_link(OBJECT(s->i2s[0]),
+                                 OBJECT(ds),
+                                 "codec",
+                                 &local_errp);
+        if (local_errp) {
+            fprintf(stderr, "a369: Unable to set codec link for FTSSP010\n");
+            abort();
+        }
+        audio_codec_data_req_set(ds,
+                                 ftssp010_i2s_data_req,
+                                 s->i2s[0]);
+    }
+
     /* System start-up */
 
     if (args->kernel_filename) {
diff --git a/hw/arm/ftplat_a369soc.c b/hw/arm/ftplat_a369soc.c
index b6e82ad..9391764 100644
--- a/hw/arm/ftplat_a369soc.c
+++ b/hw/arm/ftplat_a369soc.c
@@ -206,6 +206,23 @@ static void a369soc_chip_init(FaradaySoCState *s)
     s->i2c[0] = ds;
     ds = sysbus_create_simple("fti2c010", 0x92A00000, s->pic[52]);
     s->i2c[1] = ds;
+
+    /* ftssp010 */
+    ds = sysbus_create_simple("ftssp010", 0x92700000, s->pic[49]);
+    s->spi[0] = ds;
+    s->i2s[0] = ds;
+
+    /* ftssp010 - DMA (Tx) */
+    ack = qdev_get_gpio_in(ds, 0);
+    req = qdev_get_gpio_in(s->pdma[0], 7);
+    qdev_connect_gpio_out(s->pdma[0], 7, ack);
+    qdev_connect_gpio_out(ds, 0, req);
+
+    /* ftssp010 - DMA (Rx) */
+    ack = qdev_get_gpio_in(ds, 1);
+    req = qdev_get_gpio_in(s->pdma[0], 8);
+    qdev_connect_gpio_out(s->pdma[0], 8, ack);
+    qdev_connect_gpio_out(ds, 1, req);
 }
 
 static void a369soc_realize(DeviceState *dev, Error **errp)
diff --git a/hw/faraday.h b/hw/faraday.h
index 7373ba0..39a608c 100644
--- a/hw/faraday.h
+++ b/hw/faraday.h
@@ -121,4 +121,7 @@ static inline void faraday_soc_ahb_remap(FaradaySoCState *s, bool active)
     }
 }
 
+/* ftssp010.c */
+void ftssp010_i2s_data_req(void *opaque, int tx, int rx);
+
 #endif
diff --git a/hw/ftssp010.c b/hw/ftssp010.c
new file mode 100644
index 0000000..fe1ddbb
--- /dev/null
+++ b/hw/ftssp010.c
@@ -0,0 +1,504 @@
+/*
+ * QEMU model of the FTSSP010 Controller
+ *
+ * Copyright (C) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This file is licensed under GNU GPL v2+.
+ */
+
+#include "hw/sysbus.h"
+#include "hw/i2c.h"
+#include "hw/ssi.h"
+#include "hw/audio.h"
+#include "sysemu/sysemu.h"
+#include "qemu/fifo8.h"
+
+#include "hw/faraday.h"
+#include "hw/ftssp010.h"
+
+#define CFG_FIFO_DEPTH  16
+
+#define TYPE_FTSSP010   "ftssp010"
+
+typedef struct Ftssp010State {
+    /*< private >*/
+    SysBusDevice parent;
+
+    /*< public >*/
+    MemoryRegion mmio;
+
+    qemu_irq irq;
+    SSIBus *spi;
+    AudioCodecState *codec;
+
+    uint8_t num_cs;
+    qemu_irq *cs_lines;
+
+    Fifo8 rx_fifo;
+    Fifo8 tx_fifo;
+
+    uint8_t tx_thres;
+    uint8_t rx_thres;
+
+    int busy;
+    uint8_t bw;
+
+    /* DMA hardware handshake */
+    qemu_irq req[2];    /* 0 - Tx, 1 - Rx */
+
+    /* HW register caches */
+
+    uint32_t cr0;
+    uint32_t cr1;
+    uint32_t cr2;
+    uint32_t icr;    /* interrupt control register */
+    uint32_t isr;    /* interrupt status register */
+
+} Ftssp010State;
+
+#define FTSSP010(obj) \
+    OBJECT_CHECK(Ftssp010State, obj, TYPE_FTSSP010)
+
+/* Update interrupts.  */
+static void ftssp010_update(Ftssp010State *s)
+{
+    if (!(s->cr2 & CR2_SSPEN)) {
+        return;
+    }
+
+    /* tx fifo status update */
+    if ((s->tx_fifo.num / (s->bw >> 3)) <= s->tx_thres) {
+        s->isr |=  ISR_TFTHI;
+        if (s->icr & ICR_TFDMA) {
+            qemu_set_irq(s->req[0], 1);
+        }
+    } else {
+        s->isr &= ~ISR_TFTHI;
+    }
+
+    /* rx fifo status update */
+    switch (s->cr0 & CR0_FFMT_MASK) {
+    case CR0_FFMT_SPI:
+        s->isr |=  ISR_RFTHI;
+        if (s->icr & ICR_RFDMA) {
+            qemu_set_irq(s->req[1], 1);
+        }
+        break;
+    default:
+        if ((s->rx_fifo.num / (s->bw >> 3)) >= s->rx_thres) {
+            s->isr |=  ISR_RFTHI;
+            if (s->icr & ICR_RFDMA) {
+                qemu_set_irq(s->req[1], 1);
+            }
+        } else {
+            s->isr &= ~ISR_RFTHI;
+        }
+        break;
+    }
+
+    /* update the interrupt signal */
+    qemu_set_irq(s->irq, (s->icr & s->isr) ? 1 : 0);
+}
+
+static void ftssp010_handle_ack(void *opaque, int line, int level)
+{
+    Ftssp010State *s = FTSSP010(opaque);
+
+    switch (line) {
+    case 0:    /* Tx */
+        if (s->icr & ICR_TFDMA) {
+            if (level) {
+                qemu_set_irq(s->req[0], 0);
+            } else if ((s->tx_fifo.num / (s->bw >> 3)) <= s->tx_thres) {
+                qemu_set_irq(s->req[0], 1);
+            }
+        }
+        break;
+    case 1:    /* Rx */
+        if (s->icr & ICR_RFDMA) {
+            if (level) {
+                qemu_set_irq(s->req[1], 0);
+            } else {
+                switch (s->cr0 & CR0_FFMT_MASK) {
+                case CR0_FFMT_SPI:
+                    qemu_set_irq(s->req[1], 1);
+                    break;
+                default:
+                    if ((s->rx_fifo.num / (s->bw >> 3)) >= s->rx_thres) {
+                        qemu_set_irq(s->req[1], 1);
+                    }
+                    break;
+                }
+            }
+        }
+        break;
+    default:
+        break;
+    }
+}
+
+void ftssp010_i2s_data_req(void *opaque, int tx, int rx)
+{
+    int i, len;
+    uint32_t sample;
+    Ftssp010State *s = FTSSP010(opaque);
+
+    if (!(s->cr2 & CR2_SSPEN)) {
+        return;
+    }
+
+    if ((s->cr0 & CR0_FFMT_MASK) != CR0_FFMT_I2S) {
+        return;
+    }
+
+    s->busy = 1;
+
+    if ((s->cr2 & CR2_TXEN) && (s->cr2 & CR2_TXDOE)) {
+        len = tx * (s->bw / 8);
+        while (!fifo8_is_empty(&s->tx_fifo) && len > 0) {
+            sample = 0;
+            for (i = 0; i < (s->bw >> 3) && len > 0; i++, len--) {
+                sample = deposit32(sample, i * 8, 8, fifo8_pop(&s->tx_fifo));
+            }
+            audio_codec_dac_dat(s->codec, sample);
+        }
+
+        if (fifo8_is_empty(&s->tx_fifo) && len > 0) {
+            s->isr |= ISR_TFURI;
+        }
+    }
+
+    if (s->cr2 & CR2_RXEN) {
+        len = rx * (s->bw / 8);
+        while (!fifo8_is_full(&s->rx_fifo) && len > 0) {
+            sample = audio_codec_adc_dat(s->codec);
+            for (i = 0; i < (s->bw >> 3) && len > 0; i++, len--) {
+                fifo8_push(&s->rx_fifo, extract32(sample, i * 8, 8));
+            }
+        }
+
+        if (fifo8_is_full(&s->rx_fifo) && len > 0) {
+            s->isr |= ISR_RFORI;
+        }
+    }
+
+    s->busy = 0;
+
+    ftssp010_update(s);
+}
+
+static void ftssp010_spi_tx(Ftssp010State *s)
+{
+    if (!(s->cr2 & CR2_TXEN)) {
+        return;
+    }
+
+    s->busy = 1;
+
+    if (fifo8_is_empty(&s->tx_fifo)) {
+        s->isr |= ISR_TFURI;
+    }
+
+    while (!fifo8_is_empty(&s->tx_fifo)) {
+        ssi_transfer(s->spi, fifo8_pop(&s->tx_fifo));
+    }
+
+    s->busy = 0;
+}
+
+static uint64_t
+ftssp010_mem_read(void *opaque, hwaddr addr, unsigned size)
+{
+    Ftssp010State *s = FTSSP010(opaque);
+    uint32_t i, ret = 0;
+
+    if (addr & 0x03) {
+        fprintf(stderr, "ftssp010: "
+                 "Although ftssp010 supports byte/half-word access, "
+                 "the target address still needs to be word aligned\n");
+        abort();
+    }
+
+    switch (addr) {
+    case REG_CR0:    /* Control Register 0 */
+        return s->cr0;
+    case REG_CR1:    /* Control Register 1 */
+        return s->cr1;
+    case REG_CR2:    /* Control Register 2 */
+        return s->cr2;
+    case REG_SR:    /* Status Register */
+        ret |= s->busy ? SR_BUSY : 0;
+        /* tx fifo status */
+        ret |= SR_TFVE(s->tx_fifo.num / (s->bw >> 3));
+        if (!fifo8_is_full(&s->tx_fifo)) {
+            ret |= SR_TFNF;
+        }
+        /* rx fifo status */
+        switch (s->cr0 & CR0_FFMT_MASK) {
+        case CR0_FFMT_SPI:
+            ret |= SR_RFF | SR_RFVE(CFG_FIFO_DEPTH);
+            break;
+        case CR0_FFMT_I2S:
+            ret |= SR_RFVE(s->rx_fifo.num / (s->bw >> 3));
+            if (fifo8_is_full(&s->rx_fifo)) {
+                ret |= SR_RFF;
+            }
+            break;
+        default:
+            break;
+        }
+        break;
+    case REG_ICR:    /* Interrupt Control Register */
+        return s->icr;
+    case REG_ISR:    /* Interrupt Status Register */
+        ret = s->isr;
+        s->isr &= ~ISR_RCMASK;
+        ftssp010_update(s);
+        break;
+    case REG_DR:    /* Data Register */
+        if (!(s->cr2 & CR2_SSPEN)) {
+            break;
+        }
+        if (!(s->cr2 & CR2_RXEN)) {
+            break;
+        }
+        switch (s->cr0 & CR0_FFMT_MASK) {
+        case CR0_FFMT_SPI:
+            for (i = 0; i < (s->bw >> 3); i++) {
+                ret = deposit32(ret, i * 8, 8, ssi_transfer(s->spi, 0));
+            }
+            break;
+        case CR0_FFMT_I2S:
+            for (i = 0; i < (s->bw >> 3); i++) {
+                if (fifo8_is_empty(&s->rx_fifo)) {
+                    break;
+                }
+                ret = deposit32(ret, i * 8, 8, fifo8_pop(&s->rx_fifo));
+            }
+            break;
+        default:
+            break;
+        }
+        ftssp010_update(s);
+        break;
+    case REG_REVR:
+        return 0x00011901;    /* ver. 1.19.1 */
+    case REG_FEAR:
+        return 0x660f0f1f;    /* SPI+I2S, FIFO=16 */
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "ftssp010: undefined memory access@%#" HWADDR_PRIx "\n", addr);
+        break;
+    }
+
+    return ret;
+}
+
+static void
+ftssp010_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+    int i;
+    Ftssp010State *s = FTSSP010(opaque);
+
+    if (addr & 0x03) {
+        fprintf(stderr, "ftssp010: "
+                 "Although ftssp010 supports byte/half-word access, "
+                 "the target address still needs to be word aligned\n");
+        abort();
+    }
+
+    switch (addr) {
+    case REG_CR0:    /* Control Register 0 */
+        s->cr0 = (uint32_t)val;
+        break;
+    case REG_CR1:    /* Control Register 1 */
+        s->cr1 = (uint32_t)val;
+        s->bw  = extract32(s->cr1, 16, 7) + 1;
+        break;
+    case REG_CR2:    /* Control Register 2 */
+        s->cr2 = (uint32_t)val;
+        if (s->cr2 & CR2_SSPRST) {
+            fifo8_reset(&s->tx_fifo);
+            fifo8_reset(&s->rx_fifo);
+            s->busy = 0;
+            s->cr2 &= ~(CR2_SSPRST | CR2_TXFCLR | CR2_RXFCLR);
+            if (s->cr0 & CR0_FLASH) {
+                int cs = extract32(s->cr2, 10, 2);
+                qemu_set_irq(s->cs_lines[cs], 1);
+                s->cr2 |= CR2_FS;
+            };
+        }
+        if (s->cr2 & CR2_TXFCLR) {
+            fifo8_reset(&s->tx_fifo);
+            s->cr2 &= ~CR2_TXFCLR;
+        }
+        if (s->cr2 & CR2_RXFCLR) {
+            fifo8_reset(&s->rx_fifo);
+            s->cr2 &= ~CR2_RXFCLR;
+        }
+        if (s->cr0 & CR0_FLASH) {
+            int cs = extract32(s->cr2, 10, 2);
+            qemu_set_irq(s->cs_lines[cs], (s->cr2 & CR2_FS) ? 1 : 0);
+        }
+        if (s->cr2 & CR2_SSPEN) {
+            switch (s->cr0 & CR0_FFMT_MASK) {
+            case CR0_FFMT_SPI:
+                ftssp010_spi_tx(s);
+                break;
+            default:
+                break;
+            }
+        }
+        ftssp010_update(s);
+        break;
+    case REG_ICR:    /* Interrupt Control Register */
+        s->icr = (uint32_t)val;
+        s->tx_thres = extract32(s->icr, 12, 5);
+        s->rx_thres = extract32(s->icr,  7, 5);
+        break;
+    case REG_DR:    /* Data Register */
+        if (!(s->cr2 & CR2_SSPEN)) {
+            break;
+        }
+        for (i = 0; i < (s->bw >> 3) && !fifo8_is_full(&s->tx_fifo); i++) {
+            fifo8_push(&s->tx_fifo, extract32((uint32_t)val, i * 8, 8));
+        }
+        switch (s->cr0 & CR0_FFMT_MASK) {
+        case CR0_FFMT_SPI:
+            ftssp010_spi_tx(s);
+            break;
+        default:
+            break;
+        }
+        ftssp010_update(s);
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "ftssp010: undefined memory access@%#" HWADDR_PRIx "\n", addr);
+        break;
+    }
+}
+
+static const MemoryRegionOps mmio_ops = {
+    .read  = ftssp010_mem_read,
+    .write = ftssp010_mem_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 1,
+        .max_access_size = 4
+    }
+};
+
+static void ftssp010_reset(DeviceState *ds)
+{
+    Ftssp010State *s = FTSSP010(SYS_BUS_DEVICE(ds));
+    Error *local_errp = NULL;
+
+    s->codec = AUDIO_CODEC(object_property_get_link(OBJECT(s),
+                            "codec", &local_errp));
+    if (local_errp) {
+        fprintf(stderr, "ftssp010: Unable to get codec link\n");
+        abort();
+    }
+
+    s->busy = 0;
+    s->bw   = 8;    /* 8-bit */
+
+    s->cr0 = 0;
+    s->cr1 = 0;
+    s->cr2 = 0;
+    s->tx_thres = 4;
+    s->rx_thres = 4;
+    s->icr = ICR_TFTHOD(4) | ICR_RFTHOD(4);
+    s->isr = ISR_TFTHI;
+
+    fifo8_reset(&s->tx_fifo);
+    fifo8_reset(&s->rx_fifo);
+
+    ftssp010_update(s);
+}
+
+static void ftssp010_realize(DeviceState *dev, Error **errp)
+{
+    Ftssp010State *s = FTSSP010(dev);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+    int i;
+
+    s->spi = ssi_create_bus(&sbd->qdev, "spi");
+
+    fifo8_create(&s->tx_fifo, CFG_FIFO_DEPTH * 4);
+    fifo8_create(&s->rx_fifo, CFG_FIFO_DEPTH * 4);
+
+    memory_region_init_io(&s->mmio,
+                          &mmio_ops,
+                          s,
+                          TYPE_FTSSP010,
+                          0x1000);
+    sysbus_init_mmio(sbd, &s->mmio);
+    sysbus_init_irq(sbd, &s->irq);
+
+    s->num_cs = 4;
+    s->cs_lines = g_new(qemu_irq, s->num_cs);
+    ssi_auto_connect_slaves(dev, s->cs_lines, s->spi);
+    for (i = 0; i < s->num_cs; ++i) {
+        sysbus_init_irq(sbd, &s->cs_lines[i]);
+    }
+
+    /* DMA hardware handshake */
+    qdev_init_gpio_in(&sbd->qdev, ftssp010_handle_ack, 2);
+    qdev_init_gpio_out(&sbd->qdev, s->req, 2);
+}
+
+static const VMStateDescription vmstate_ftssp010 = {
+    .name = TYPE_FTSSP010,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(cr0, Ftssp010State),
+        VMSTATE_UINT32(cr1, Ftssp010State),
+        VMSTATE_UINT32(cr2, Ftssp010State),
+        VMSTATE_UINT32(icr, Ftssp010State),
+        VMSTATE_UINT32(isr, Ftssp010State),
+        VMSTATE_FIFO8(tx_fifo, Ftssp010State),
+        VMSTATE_FIFO8(rx_fifo, Ftssp010State),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void ftssp010_instance_init(Object *obj)
+{
+    Ftssp010State *s = FTSSP010(obj);
+
+    object_property_add_link(obj,
+                             "codec",
+                             TYPE_AUDIO_CODEC,
+                             (Object **) &s->codec,
+                             NULL);
+}
+
+static void ftssp010_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->vmsd    = &vmstate_ftssp010;
+    dc->reset   = ftssp010_reset;
+    dc->realize = ftssp010_realize;
+    dc->no_user = 1;
+}
+
+static const TypeInfo ftssp010_info = {
+    .name          = TYPE_FTSSP010,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(Ftssp010State),
+    .instance_init = ftssp010_instance_init,
+    .class_init    = ftssp010_class_init,
+};
+
+static void ftssp010_register_types(void)
+{
+    type_register_static(&ftssp010_info);
+}
+
+type_init(ftssp010_register_types)
diff --git a/hw/ftssp010.h b/hw/ftssp010.h
new file mode 100644
index 0000000..e3d3e7a
--- /dev/null
+++ b/hw/ftssp010.h
@@ -0,0 +1,96 @@
+/*
+ * QEMU model of the FTSSP010 Controller
+ *
+ * Copyright (C) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This file is licensed under GNU GPL v2+.
+ */
+
+#ifndef HW_ARM_FTSSP010_H
+#define HW_ARM_FTSSP010_H
+
+/* FTSSP010: Registers */
+#define REG_CR0             0x00    /* control register 0 */
+#define REG_CR1             0x04    /* control register 1 */
+#define REG_CR2             0x08    /* control register 2 */
+#define REG_SR              0x0C    /* status register */
+#define REG_ICR             0X10    /* interrupt control register */
+#define REG_ISR             0x14    /* interrupt statis register */
+#define REG_DR              0x18    /* data register */
+#define REG_REVR            0x60    /* revision register */
+#define REG_FEAR            0x64    /* 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_LBM             (1 << 7)  /* Loopback mode */
+#define CR0_LSB             (1 << 6)  /* LSB first */
+#define CR0_FSPO            (1 << 5)  /* Frame sync atcive low */
+#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)  /* SCLK Remain HIGH */
+#define CR0_SCLKPH          (1 << 0)  /* Half CLK cycle */
+
+/* Control Register 1 */
+
+/* padding data length */
+#define CR1_PDL(x)          (((x) & 0xff) << 24)
+/* serial data length(actual data length-1) */
+#define CR1_SDL(x)          ((((x) - 1) & 0x1f) << 16)
+/*  clk divider */
+#define CR1_CLKDIV(x)       ((x) & 0xffff)
+
+/* Control Register 2 */
+#define CR2_FSOS(x)         (((x) & 0x03) << 10)        /* FS/CS Select */
+#define CR2_FS              (1 << 9)    /* FS/CS Signal Level */
+#define CR2_TXEN            (1 << 8)    /* Tx Enable */
+#define CR2_RXEN            (1 << 7)    /* Rx Enable */
+#define CR2_SSPRST          (1 << 6)    /* SSP reset */
+#define CR2_TXFCLR          (1 << 3)    /* TX FIFO Clear */
+#define CR2_RXFCLR          (1 << 2)    /* RX FIFO Clear */
+#define CR2_TXDOE           (1 << 1)    /* TX Data Output Enable */
+#define CR2_SSPEN           (1 << 0)    /* SSP Enable */
+
+/*
+ * Status Register
+ */
+#define SR_TFVE(reg)        (((reg) & 0x1F) << 12)
+#define SR_RFVE(reg)        (((reg) & 0x1F) << 4)
+#define SR_BUSY             (1 << 2)
+#define SR_TFNF             (1 << 1)    /* Tx FIFO Not Full */
+#define SR_RFF              (1 << 0)    /* Rx FIFO Full */
+
+/* Interrupr Control Register */
+#define ICR_TFTHOD(x)       (((x) & 0x1f) << 12)/* TX FIFO Threshold */
+#define ICR_RFTHOD(x)       (((x) & 0x1f) << 7) /* RX FIFO Threshold */
+#define ICR_AC97I           0x40      /* AC97 intr enable */
+#define ICR_TFDMA           0x20      /* TX DMA enable */
+#define ICR_RFDMA           0x10      /* RX DMA enable */
+#define ICR_TFTHI           0x08      /* TX FIFO intr enable */
+#define ICR_RFTHI           0x04      /* RX FIFO intr enable */
+#define ICR_TFURI           0x02      /* TX FIFO Underrun intr enable */
+#define ICR_RFORI           0x01      /* RX FIFO Overrun intr enable */
+
+/* Interrupr Status Register */
+#define ISR_AC97I           0x10      /* AC97 interrupt */
+#define ISR_TFTHI           0x08      /* TX FIFO interrupt */
+#define ISR_RFTHI           0x04      /* RX FIFO interrupt */
+#define ISR_TFURI           0x02      /* TX FIFO Underrun interrupt */
+#define ISR_RFORI           0x01      /* RX FIFO Overrun interrupt */
+/* Read-Clear status mask */
+#define ISR_RCMASK          (ISR_RFORI | ISR_TFURI | ISR_AC97I)
+
+#endif
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH v9 17/24] qemu/bitops.h: add the bit ordering reversal functions
  2013-03-25 12:09 [Qemu-devel] [PATCH v9 00/24] hw/arm: add Faraday A369 SoC platform support Kuo-Jung Su
                   ` (15 preceding siblings ...)
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 16/24] The FTSSP010 is a multi-function synchronous serial port interface controller which supports SSP, SPI, I2S, AC97 and SPDIF Kuo-Jung Su
@ 2013-03-25 12:09 ` Kuo-Jung Su
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 18/24] hw/arm: add FTGMAC100 1Gbps ethernet support Kuo-Jung Su
                   ` (6 subsequent siblings)
  23 siblings, 0 replies; 35+ messages in thread
From: Kuo-Jung Su @ 2013-03-25 12:09 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, i.mitsyanko, Blue Swirl, Paul Brook, Kuo-Jung Su,
	Andreas, fred.konrad

From: Kuo-Jung Su <dantesu@faraday-tech.com>

Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
---
 include/qemu/bitops.h |   59 ++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 58 insertions(+), 1 deletion(-)

diff --git a/include/qemu/bitops.h b/include/qemu/bitops.h
index affcc96..64cbb04 100644
--- a/include/qemu/bitops.h
+++ b/include/qemu/bitops.h
@@ -3,7 +3,8 @@
  *
  * Copyright (C) 2010 Corentin Chary <corentin.chary@gmail.com>
  *
- * Mostly inspired by (stolen from) linux/bitmap.h and linux/bitops.h
+ * Mostly inspired by (stolen from) linux/bitmap.h, linux/bitops.h
+ * and linux/bitrev.h
  *
  * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
  * See the COPYING.LIB file in the top-level directory.
@@ -273,4 +274,60 @@ static inline uint64_t deposit64(uint64_t value, int start, int length,
     return (value & ~mask) | ((fieldval << start) & mask);
 }
 
+/**
+ * bitrev8:
+ * @value: the value to reverse bit ordering from
+ *
+ * Reverse the 8 bit input @value
+ *
+ * Returns: the input @value with reversed bit ordering
+ */
+static inline uint8_t bitrev8(uint8_t value)
+{
+    value = (value & 0xF0) >> 4 | (value & 0x0F) << 4;
+    value = (value & 0xCC) >> 2 | (value & 0x33) << 2;
+    value = (value & 0xAA) >> 1 | (value & 0x55) << 1;
+    return value;
+}
+
+/**
+ * bitrev16:
+ * @value: the value to reverse bit ordering from
+ *
+ * Reverse the 16 bit input @value
+ *
+ * Returns: the input @value with reversed bit ordering
+ */
+static inline uint16_t bitrev16(uint16_t value)
+{
+    return (bitrev8(value & 0xff) << 8) | bitrev8(value >> 8);
+}
+
+/**
+ * bitrev32:
+ * @value: the value to reverse bit ordering from
+ *
+ * Reverse the 32 bit input @value
+ *
+ * Returns: the input @value with reversed bit ordering
+ */
+static inline uint32_t bitrev32(uint32_t value)
+{
+    return (bitrev16(value & 0xffff) << 16) | bitrev16(value >> 16);
+}
+
+/**
+ * bitrev64:
+ * @value: the value to reverse bit ordering from
+ *
+ * Reverse the 64 bit input @value
+ *
+ * Returns: the input @value with reversed bit ordering
+ */
+static inline uint64_t bitrev64(uint64_t value)
+{
+    return ((uint64_t)bitrev32(value & 0xffffffffULL) << 32)
+        | (uint64_t)bitrev32(value >> 32);
+}
+
 #endif
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH v9 18/24] hw/arm: add FTGMAC100 1Gbps ethernet support
  2013-03-25 12:09 [Qemu-devel] [PATCH v9 00/24] hw/arm: add Faraday A369 SoC platform support Kuo-Jung Su
                   ` (16 preceding siblings ...)
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 17/24] qemu/bitops.h: add the bit ordering reversal functions Kuo-Jung Su
@ 2013-03-25 12:09 ` Kuo-Jung Su
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 19/24] hw/arm: add FTLCDC200 LCD controller support Kuo-Jung Su
                   ` (5 subsequent siblings)
  23 siblings, 0 replies; 35+ messages in thread
From: Kuo-Jung Su @ 2013-03-25 12:09 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, i.mitsyanko, Blue Swirl, Paul Brook, Kuo-Jung Su,
	Andreas, fred.konrad

From: Kuo-Jung Su <dantesu@faraday-tech.com>

The FTGMAC100 Ethernet controller has a DMA engine which handles
all data transfers between the system memory and on-chip memories.
Its DMA engine supports both 16-bits and 32-bits alignment,
and thus make it possible to support zero-copy transfer at both
Linux and WINCE.

It also has 802.1Q VLAN tagging support for both insertion and removal.

Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
---
 hw/arm/Makefile.objs    |    2 +-
 hw/arm/ftplat_a369soc.c |    5 +
 hw/faraday.h            |    3 +
 hw/ftgmac100.c          |  712 +++++++++++++++++++++++++++++++++++++++++++++++
 hw/ftgmac100.h          |  237 ++++++++++++++++
 5 files changed, 958 insertions(+), 1 deletion(-)
 create mode 100644 hw/ftgmac100.c
 create mode 100644 hw/ftgmac100.h

diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 42c8472..62c823d 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -26,7 +26,7 @@ obj-y += imx_serial.o imx_ccm.o imx_timer.o imx_avic.o
 obj-$(CONFIG_KVM) += kvm/arm_gic.o
 obj-y += ftintc020.o ftahbc020.o ftddrii030.o ftpwmtmr010.o ftwdt010.o \
                 ftrtc011.o ftdmac020.o ftapbbrg020.o ftnandc021.o fti2c010.o \
-                ftssp010.o
+                ftssp010.o ftgmac100.o
 
 obj-y := $(addprefix ../,$(obj-y))
 
diff --git a/hw/arm/ftplat_a369soc.c b/hw/arm/ftplat_a369soc.c
index 9391764..622b1db 100644
--- a/hw/arm/ftplat_a369soc.c
+++ b/hw/arm/ftplat_a369soc.c
@@ -223,6 +223,11 @@ static void a369soc_chip_init(FaradaySoCState *s)
     req = qdev_get_gpio_in(s->pdma[0], 8);
     qdev_connect_gpio_out(s->pdma[0], 8, ack);
     qdev_connect_gpio_out(ds, 1, req);
+
+    /* ftgmac100 */
+    if (nb_nics > 0) {
+        ftgmac100_init(&nd_table[0], 0x90c00000, s->pic[32]);
+    }
 }
 
 static void a369soc_realize(DeviceState *dev, Error **errp)
diff --git a/hw/faraday.h b/hw/faraday.h
index 39a608c..068b799 100644
--- a/hw/faraday.h
+++ b/hw/faraday.h
@@ -124,4 +124,7 @@ static inline void faraday_soc_ahb_remap(FaradaySoCState *s, bool active)
 /* ftssp010.c */
 void ftssp010_i2s_data_req(void *opaque, int tx, int rx);
 
+/* ftgmac100.c */
+void ftgmac100_init(NICInfo *nd, uint32_t base, qemu_irq irq);
+
 #endif
diff --git a/hw/ftgmac100.c b/hw/ftgmac100.c
new file mode 100644
index 0000000..45f8a5b
--- /dev/null
+++ b/hw/ftgmac100.c
@@ -0,0 +1,712 @@
+/*
+ * QEMU model of the FTGMAC100 Controller
+ *
+ * Copyright (C) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This file is licensed under GNU GPL v2+.
+ */
+
+#include "hw/sysbus.h"
+#include "qemu/timer.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/dma.h"
+#include "net/net.h"
+
+#include "hw/faraday.h"
+#include "hw/ftgmac100.h"
+
+#ifndef DEBUG
+#define DEBUG   0
+#endif
+
+#define DPRINTF(fmt, ...) \
+    do { \
+        if (DEBUG) { \
+            fprintf(stderr, fmt , ## __VA_ARGS__); \
+        } \
+    } while (0)
+
+#define TYPE_FTGMAC100  "ftgmac100"
+
+#define CFG_MAXFRMLEN   9220    /* Max. frame length */
+#define CFG_REGSIZE     (0x100 / 4)
+
+typedef struct Ftgmac100State {
+    /*< private >*/
+    SysBusDevice parent;
+
+    /*< public >*/
+    MemoryRegion mmio;
+
+    QEMUBH *bh;
+    qemu_irq irq;
+    NICState *nic;
+    NICConf conf;
+    DMAContext *dma;
+    QEMUTimer *qtimer;
+
+    bool phycr_rd;
+
+    struct {
+        uint8_t  buf[CFG_MAXFRMLEN];
+        uint32_t len;
+    } txbuff;
+
+    uint32_t hptx_idx;
+    uint32_t tx_idx;
+    uint32_t rx_idx;
+
+    /* HW register cache */
+    uint32_t regs[CFG_REGSIZE];
+} Ftgmac100State;
+
+#define FTGMAC100(obj) \
+    OBJECT_CHECK(Ftgmac100State, obj, TYPE_FTGMAC100)
+
+#define MAC_REG32(s, off) \
+    ((s)->regs[(off) / 4])
+
+static int ftgmac100_mcast_hash(Ftgmac100State *s, const uint8_t *data)
+{
+#define CRCPOLY_BE    0x04c11db7
+    int i, len;
+    uint32_t crc = 0xFFFFFFFF;
+
+    len = (MAC_REG32(s, REG_MACCR) & MACCR_GMODE) ? 5 : 6;
+
+    while (len--) {
+        uint32_t c = *(data++);
+        for (i = 0; i < 8; ++i) {
+            crc = (crc << 1) ^ ((((crc >> 31) ^ c) & 0x01) ? CRCPOLY_BE : 0);
+            c >>= 1;
+        }
+    }
+    crc = ~crc;
+
+    /* Reverse CRC32 and return MSB 6 bits only */
+    return bitrev8(crc >> 24) >> 2;
+}
+
+static void
+ftgmac100_read_txdesc(Ftgmac100State *s, hwaddr addr, Ftgmac100TXD *desc)
+{
+    int i;
+    uint32_t *p = (uint32_t *)desc;
+
+    if (addr & 0x0f) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                 "ftgmac100: Tx desc is not 16-byte aligned!\n"
+                 "It's fine in QEMU but the real HW would panic.\n");
+    }
+
+    dma_memory_read(s->dma, addr, desc, sizeof(*desc));
+
+    for (i = 0; i < sizeof(*desc); i += 4) {
+        *p = le32_to_cpu(*p);
+    }
+
+    if (desc->buf & 0x01) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                 "ftgmac100: tx buffer is not 16-bit aligned!\n");
+    }
+}
+
+static void
+ftgmac100_write_txdesc(Ftgmac100State *s, hwaddr addr, Ftgmac100TXD *desc)
+{
+    int i;
+    uint32_t *p = (uint32_t *)desc;
+
+    for (i = 0; i < sizeof(*desc); i += 4) {
+        *p = cpu_to_le32(*p);
+    }
+
+    dma_memory_write(s->dma, addr, desc, sizeof(*desc));
+}
+
+static void
+ftgmac100_read_rxdesc(Ftgmac100State *s, hwaddr addr, Ftgmac100RXD *desc)
+{
+    int i;
+    uint32_t *p = (uint32_t *)desc;
+
+    if (addr & 0x0f) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                 "ftgmac100: Rx desc is not 16-byte aligned!\n"
+                 "It's fine in QEMU but the real HW would panic.\n");
+    }
+
+    dma_memory_read(s->dma, addr, desc, sizeof(*desc));
+
+    for (i = 0; i < sizeof(*desc); i += 4) {
+        *p = le32_to_cpu(*p);
+    }
+
+    if (desc->buf & 0x01) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                 "ftgmac100: rx buffer is not 16-bit aligned!\n");
+    }
+}
+
+static void
+ftgmac100_write_rxdesc(Ftgmac100State *s, hwaddr addr, Ftgmac100RXD *desc)
+{
+    int i;
+    uint32_t *p = (uint32_t *)desc;
+
+    for (i = 0; i < sizeof(*desc); i += 4) {
+        *p = cpu_to_le32(*p);
+    }
+
+    dma_memory_write(s->dma, addr, desc, sizeof(*desc));
+}
+
+static void ftgmac100_update_irq(Ftgmac100State *s)
+{
+    qemu_set_irq(s->irq, !!(MAC_REG32(s, REG_ISR) & MAC_REG32(s, REG_IMR)));
+}
+
+static int ftgmac100_can_receive(NetClientState *nc)
+{
+    int ret = 0;
+    uint32_t val;
+    Ftgmac100State *s = qemu_get_nic_opaque(nc);
+    Ftgmac100RXD rxd;
+    hwaddr off = MAC_REG32(s, REG_RXBAR) + s->rx_idx * sizeof(rxd);
+
+    val = MAC_REG32(s, REG_MACCR);
+    if ((val & MACCR_RCV_EN) && (val & MACCR_RDMA_EN)) {
+        ftgmac100_read_rxdesc(s, off, &rxd);
+        ret = !rxd.owner;
+        if (!ret) {
+            qemu_mod_timer(s->qtimer, qemu_get_clock_ms(vm_clock) + 10);
+        }
+    }
+
+    return ret;
+}
+
+static ssize_t ftgmac100_receive(NetClientState *nc,
+                                 const uint8_t  *buf,
+                                 size_t          size)
+{
+    const uint8_t *ptr = buf;
+    hwaddr off;
+    size_t len;
+    Ftgmac100RXD rxd;
+    Ftgmac100State *s = qemu_get_nic_opaque(nc);
+    int bcst, mcst, ftl, proto;
+
+    MAC_REG32(s, REG_RXPKT) += 1;
+
+    /*
+     * Check if it's a long frame. (CRC32 is excluded)
+     */
+    ftl = 0;
+    proto = (buf[12] << 8) | buf[13];
+    if (MAC_REG32(s, REG_MACCR) & MACCR_JUMBO_LF) {
+        if (proto == 0x8100) {  /* 802.1Q VLAN */
+            ftl = (size > 9216) ? 1 : 0;
+        } else {
+            ftl = (size > 9212) ? 1 : 0;
+        }
+    } else {
+        if (proto == 0x8100) {  /* 802.1Q VLAN */
+            ftl = (size > 1518) ? 1 : 0;
+        } else {
+            ftl = (size > 1514) ? 1 : 0;
+        }
+    }
+    if (ftl) {
+        MAC_REG32(s, REG_RXCRCFTL) = (MAC_REG32(s, REG_RXCRCFTL) + 1) & 0xffff;
+        DPRINTF("ftgmac100_receive: frame too long, drop it\n");
+        return -1;
+    }
+
+    /* if it's a broadcast */
+    if ((buf[0] == 0xff) && (buf[1] == 0xff) && (buf[2] == 0xff)
+            && (buf[3] == 0xff) && (buf[4] == 0xff) && (buf[5] == 0xff)) {
+        bcst = 1;
+        MAC_REG32(s, REG_RXBCST) += 1;
+        if (!(MAC_REG32(s, REG_MACCR) & MACCR_RCV_ALL)
+            && !(MAC_REG32(s, REG_MACCR) & MACCR_RX_BROADPKT)) {
+            DPRINTF("ftgmac100_receive: bcst filtered\n");
+            return -1;
+        }
+    } else {
+        bcst = 0;
+    }
+
+    /* if it's a multicast */
+    if ((buf[0] == 0x01) && (buf[1] == 0x00) && (buf[2] == 0x5e)
+        && (buf[3] <= 0x7f)) {
+        mcst = 1;
+        MAC_REG32(s, REG_RXMCST) += 1;
+        if (!(MAC_REG32(s, REG_MACCR) & MACCR_RCV_ALL)
+            && !(MAC_REG32(s, REG_MACCR) & MACCR_RX_MULTIPKT)) {
+            int hash, id;
+            if (!(MAC_REG32(s, REG_MACCR) & MACCR_HT_MULTI_EN)) {
+                DPRINTF("ftgmac100_receive: mcst filtered\n");
+                return -1;
+            }
+            hash = ftgmac100_mcast_hash(s, buf);
+            id = (hash >= 32) ? 1 : 0;
+            if (!(MAC_REG32(s, REG_MHASH0 + id * 4) & BIT(hash % 32))) {
+                DPRINTF("ftgmac100_receive: mcst filtered\n");
+                return -1;
+            }
+        }
+    } else {
+        mcst = 0;
+    }
+
+    /* check if the destination matches NIC mac address */
+    if (!(MAC_REG32(s, REG_MACCR) & MACCR_RCV_ALL) && !bcst && !mcst) {
+        if (memcmp(s->conf.macaddr.a, buf, 6)) {
+            return -1;
+        }
+    }
+
+    while (size > 0) {
+        off = MAC_REG32(s, REG_RXBAR) + s->rx_idx * sizeof(rxd);
+        ftgmac100_read_rxdesc(s, off, &rxd);
+        if (rxd.owner) {
+            MAC_REG32(s, REG_ISR) |= ISR_NORXBUF;
+            DPRINTF("ftgmac100: out of rxd!?\n");
+            return -1;
+        }
+        len = size > rxd.len ? rxd.len : size;
+        rxd.frs = (ptr == buf) ? 1 : 0;
+
+        if (rxd.frs && proto == 0x8100 && len >= 18) {
+            rxd.vlan = 1;
+            rxd.vlantag = (buf[14] << 8) | buf[15];
+            if (MAC_REG32(s, REG_MACCR) & MACCR_VLAN_RM) {
+                /* copy src/dst mac address */
+                dma_memory_write(s->dma, rxd.buf, ptr, 12);
+                /* copy proto + payload, the VLAN tag is ignored */
+                dma_memory_write(s->dma, rxd.buf + 12, ptr + 16, len - 16);
+            } else {
+                dma_memory_write(s->dma, rxd.buf, ptr, len);
+            }
+        } else {
+            rxd.vlan = 0;
+            rxd.vlantag = 0;
+            dma_memory_write(s->dma, rxd.buf, ptr, len);
+        }
+        ptr  += len;
+        size -= len;
+
+        rxd.lrs = (size <= 0) ? 1 : 0;
+        rxd.len = len;
+        rxd.bcast = bcst;
+        rxd.mcast = mcst;
+        rxd.owner = 1;
+
+        /* write-back the rx descriptor */
+        ftgmac100_write_rxdesc(s, off, &rxd);
+
+        if (rxd.end) {
+            s->rx_idx = 0;
+        } else {
+            s->rx_idx += 1;
+        }
+    }
+
+    /* update interrupt signal */
+    MAC_REG32(s, REG_ISR) |= ISR_RPKT_OK | ISR_RPKT_FINISH;
+    ftgmac100_update_irq(s);
+
+    return (ssize_t)((uint32_t)ptr - (uint32_t)buf);
+}
+
+static void
+ftgmac100_transmit(Ftgmac100State *s, uint32_t bar, uint32_t *idx)
+{
+    hwaddr off;
+    uint8_t *buf;
+    int ftl, proto;
+    Ftgmac100TXD txd;
+
+    if ((MAC_REG32(s, REG_MACCR) & (MACCR_XMT_EN | MACCR_XDMA_EN))
+            != (MACCR_XMT_EN | MACCR_XDMA_EN)) {
+        return;
+    }
+
+    do {
+        off = bar + (*idx) * sizeof(txd);
+        ftgmac100_read_txdesc(s, off, &txd);
+        if (!txd.owner) {
+            MAC_REG32(s, REG_ISR) |= ISR_NOTXBUF;
+            break;
+        }
+        if (txd.fts) {
+            s->txbuff.len = 0;
+        }
+        if (txd.len + s->txbuff.len > sizeof(s->txbuff.buf)) {
+            fprintf(stderr, "ftgmac100: tx buffer overflow!\n");
+            abort();
+        }
+        buf = s->txbuff.buf + s->txbuff.len;
+        dma_memory_read(s->dma, txd.buf, (uint8_t *)buf, txd.len);
+        s->txbuff.len += txd.len;
+        proto = (s->txbuff.buf[12] << 8) | s->txbuff.buf[13];
+        /* Insert VLAN Tag */
+        if (txd.fts && txd.vlan && proto != 0x8100) {
+            if (s->txbuff.len + 4 > sizeof(s->txbuff.buf)) {
+                fprintf(stderr, "ftgmac100: tx buffer overflow!\n");
+                abort();
+            }
+            memmove(s->txbuff.buf + 16,
+                    s->txbuff.buf + 12,
+                    s->txbuff.len - 12);
+            s->txbuff.buf[12] = 0x81;
+            s->txbuff.buf[13] = 0x00;
+            s->txbuff.buf[14] = (uint8_t)(txd.vlantag >> 8);
+            s->txbuff.buf[15] = (uint8_t)(txd.vlantag >> 0);
+            s->txbuff.len += 4;
+            proto = 0x8100;
+        }
+        /* Check if it's a long frame. (CRC32 is excluded) */
+        if (MAC_REG32(s, REG_MACCR) & MACCR_JUMBO_LF) {
+            if (proto == 0x8100) {
+                ftl = (s->txbuff.len > 9216) ? 1 : 0;
+            } else {
+                ftl = (s->txbuff.len > 9212) ? 1 : 0;
+            }
+        } else {
+            if (proto == 0x8100) {
+                ftl = (s->txbuff.len > 1518) ? 1 : 0;
+            } else {
+                ftl = (s->txbuff.len > 1514) ? 1 : 0;
+            }
+        }
+        if (ftl) {
+            fprintf(stderr, "ftgmac100_transmit: frame too long\n");
+            abort();
+        }
+        if (txd.lts) {
+            MAC_REG32(s, REG_TXPKT) += 1;
+            if (MAC_REG32(s, REG_MACCR) & MACCR_LOOP_EN) {
+                ftgmac100_receive(qemu_get_queue(s->nic),
+                                  s->txbuff.buf,
+                                  s->txbuff.len);
+            } else {
+                qemu_send_packet(qemu_get_queue(s->nic),
+                                 s->txbuff.buf,
+                                 s->txbuff.len);
+            }
+        }
+        if (txd.end) {
+            *idx = 0;
+        } else {
+            *idx += 1;
+        }
+        if (txd.tx2fic) {
+            MAC_REG32(s, REG_ISR) |= ISR_XPKT_OK;
+        }
+        if (txd.txic) {
+            MAC_REG32(s, REG_ISR) |= ISR_XPKT_FINISH;
+        }
+        txd.owner = 0;
+        ftgmac100_write_txdesc(s, off, &txd);
+    } while (1);
+}
+
+static void ftgmac100_bh(void *opaque)
+{
+    Ftgmac100State *s = FTGMAC100(opaque);
+
+    /* 1. process high priority tx ring */
+    if (MAC_REG32(s, REG_HPTXBAR)
+        && (MAC_REG32(s, REG_MACCR) & MACCR_HPTXR_EN)) {
+        ftgmac100_transmit(s, MAC_REG32(s, REG_HPTXBAR), &s->hptx_idx);
+    }
+
+    /* 2. process normal priority tx ring */
+    if (MAC_REG32(s, REG_TXBAR)) {
+        ftgmac100_transmit(s, MAC_REG32(s, REG_TXBAR), &s->tx_idx);
+    }
+
+    /* 3. update interrupt signal */
+    ftgmac100_update_irq(s);
+}
+
+static void ftgmac100_chip_reset(Ftgmac100State *s)
+{
+    s->phycr_rd = false;
+    s->txbuff.len = 0;
+    s->hptx_idx = 0;
+    s->tx_idx = 0;
+    s->rx_idx = 0;
+    memset(s->regs, 0, sizeof(s->regs));
+
+    MAC_REG32(s, REG_APTC) = 0x00000001; /* timing control */
+    MAC_REG32(s, REG_DBLAC) = 0x00022f72; /* dma burst, arbitration */
+    MAC_REG32(s, REG_DMAFIFO) = 0x0c000000; /* tx/rx fifo empty */
+    MAC_REG32(s, REG_REVR) = 0x00000600; /* rev. = 0.6.0 */
+    MAC_REG32(s, REG_FEAR) = 0x0000001b; /* fifo = 16KB */
+    MAC_REG32(s, REG_TPAFCR) = 0x000000f1; /* tx priority, arbitration */
+    MAC_REG32(s, REG_RBSR) = 0x00000640; /* rx buffer size */
+
+    if (s->bh) {
+        qemu_bh_cancel(s->bh);
+    }
+
+    if (s->qtimer) {
+        qemu_del_timer(s->qtimer);
+    }
+
+    ftgmac100_update_irq(s);
+}
+
+static uint64_t
+ftgmac100_mem_read(void *opaque, hwaddr addr, unsigned size)
+{
+    Ftgmac100State *s = FTGMAC100(opaque);
+    uint32_t dev, reg, ret = 0;
+
+    if (addr > REG_RXNOBCOL) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "ftgmac100: undefined memory access@%#" HWADDR_PRIx "\n", addr);
+        return ret;
+    }
+
+    switch (addr) {
+    case REG_HMAC:
+        ret = s->conf.macaddr.a[1] | (s->conf.macaddr.a[0] << 8);
+        break;
+    case REG_LMAC:
+        ret = s->conf.macaddr.a[5] | (s->conf.macaddr.a[4] << 8)
+            | (s->conf.macaddr.a[3] << 16) | (s->conf.macaddr.a[2] << 24);
+        break;
+    case REG_PHYDR:
+        dev = extract32(MAC_REG32(s, REG_PHYCR), 16, 5);
+        reg = extract32(MAC_REG32(s, REG_PHYCR), 21, 5);
+        /* Emulating a Marvell 88E1111 with 1Gbps link state */
+        if (!dev && s->phycr_rd) {
+            switch (reg) {
+            case 0:    /* PHY control register */
+                return 0x1140 << 16;
+            case 1:    /* PHY status register */
+                return 0x796d << 16;
+            case 2:    /* PHY ID 1 register */
+                return 0x0141 << 16;
+            case 3:    /* PHY ID 2 register */
+                return 0x0cc2 << 16;
+            case 4:    /* Autonegotiation advertisement register */
+                return 0x0de1 << 16;
+            case 5:    /* Autonegotiation partner abilities register */
+                return 0x45e1 << 16;
+            case 17:/* Marvell 88E1111 */
+                ret = (2 << 14) | (1 << 13) | (1 << 11) | (1 << 10);
+                return ret << 16;
+            }
+        }
+        break;
+    case REG_RXPTR:
+        ret = s->rx_idx;
+        break;
+    case REG_TXPTR:
+        ret = s->tx_idx;
+        break;
+    case REG_HPTXPTR:
+        ret = s->hptx_idx;
+        break;
+    default:
+        ret = s->regs[addr / 4];
+        break;
+    }
+
+    return ret;
+}
+
+static void
+ftgmac100_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+    Ftgmac100State *s = FTGMAC100(opaque);
+
+    if (addr >= REG_TXPTR) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "ftgmac100: undefined memory access@%#" HWADDR_PRIx "\n", addr);
+        return;
+    }
+
+    switch (addr) {
+    case REG_ISR:
+        MAC_REG32(s, REG_ISR) &= ~((uint32_t)val);
+        ftgmac100_update_irq(s);
+        break;
+    case REG_IMR:
+        MAC_REG32(s, REG_IMR) = (uint32_t)val;
+        ftgmac100_update_irq(s);
+        break;
+    case REG_HMAC:
+        s->conf.macaddr.a[1] = extract32((uint32_t)val, 0, 8);
+        s->conf.macaddr.a[0] = extract32((uint32_t)val, 8, 8);
+        break;
+    case REG_LMAC:
+        s->conf.macaddr.a[5] = extract32((uint32_t)val, 0, 8);
+        s->conf.macaddr.a[4] = extract32((uint32_t)val, 8, 8);
+        s->conf.macaddr.a[3] = extract32((uint32_t)val, 16, 8);
+        s->conf.macaddr.a[2] = extract32((uint32_t)val, 24, 8);
+        break;
+    case REG_MACCR:
+        MAC_REG32(s, REG_MACCR) = (uint32_t)val;
+        if (val & MACCR_SW_RST) {
+            ftgmac100_chip_reset(s);
+            MAC_REG32(s, REG_MACCR) &= ~MACCR_SW_RST;
+        }
+        if ((val & MACCR_RCV_EN) && (val & MACCR_RDMA_EN)) {
+            if (ftgmac100_can_receive(qemu_get_queue(s->nic))) {
+                qemu_flush_queued_packets(qemu_get_queue(s->nic));
+            }
+        } else {
+            qemu_del_timer(s->qtimer);
+        }
+        break;
+    case REG_MACSR:
+        MAC_REG32(s, REG_MACSR) &= ~((uint32_t)val);
+        break;
+    case REG_PHYCR:
+        s->phycr_rd = (val & PHYCR_MDIORD) ? true : false;
+        MAC_REG32(s, REG_PHYCR) = (uint32_t)val
+                                & (~(PHYCR_MDIOWR | PHYCR_MDIORD));
+        break;
+    case REG_TXPD:
+    case REG_HPTXPD:
+        qemu_bh_schedule(s->bh);
+        break;
+    case REG_RXPD:
+    case REG_DMAFIFO:
+    case REG_REVR:
+    case REG_FEAR:
+        break;
+    default:
+        s->regs[addr / 4] = (uint32_t)val;
+        break;
+    }
+}
+
+static const MemoryRegionOps mmio_ops = {
+    .read  = ftgmac100_mem_read,
+    .write = ftgmac100_mem_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4
+    }
+};
+
+static void ftgmac100_cleanup(NetClientState *nc)
+{
+    Ftgmac100State *s = qemu_get_nic_opaque(nc);
+
+    s->nic = NULL;
+}
+
+static NetClientInfo net_ftgmac100_info = {
+    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .size = sizeof(NICState),
+    .can_receive = ftgmac100_can_receive,
+    .receive = ftgmac100_receive,
+    .cleanup = ftgmac100_cleanup,
+};
+
+static void ftgmac100_reset(DeviceState *ds)
+{
+    Ftgmac100State *s = FTGMAC100(SYS_BUS_DEVICE(ds));
+
+    ftgmac100_chip_reset(s);
+}
+
+static void ftgmac100_watchdog(void *opaque)
+{
+    Ftgmac100State *s = FTGMAC100(opaque);
+
+    if (ftgmac100_can_receive(qemu_get_queue(s->nic))) {
+        qemu_flush_queued_packets(qemu_get_queue(s->nic));
+    }
+}
+
+static void ftgmac100_realize(DeviceState *dev, Error **errp)
+{
+    Ftgmac100State *s = FTGMAC100(dev);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+
+    memory_region_init_io(&s->mmio, &mmio_ops, s, TYPE_FTGMAC100, 0x1000);
+    sysbus_init_mmio(sbd, &s->mmio);
+    sysbus_init_irq(sbd, &s->irq);
+
+    qemu_macaddr_default_if_unset(&s->conf.macaddr);
+    s->nic = qemu_new_nic(&net_ftgmac100_info,
+                          &s->conf,
+                          object_get_typename(OBJECT(dev)),
+                          sbd->qdev.id,
+                          s);
+    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+
+    s->qtimer = qemu_new_timer_ms(vm_clock, ftgmac100_watchdog, s);
+    s->dma = &dma_context_memory;
+    s->bh = qemu_bh_new(ftgmac100_bh, s);
+
+    ftgmac100_chip_reset(s);
+}
+
+static const VMStateDescription vmstate_ftgmac100 = {
+    .name = TYPE_FTGMAC100,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(regs, Ftgmac100State, CFG_REGSIZE),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static Property ftgmac100_properties[] = {
+    DEFINE_NIC_PROPERTIES(Ftgmac100State, conf),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void ftgmac100_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->vmsd  = &vmstate_ftgmac100;
+    dc->props = ftgmac100_properties;
+    dc->reset = ftgmac100_reset;
+    dc->realize = ftgmac100_realize;
+    dc->no_user = 1;
+}
+
+static const TypeInfo ftgmac100_info = {
+    .name           = TYPE_FTGMAC100,
+    .parent         = TYPE_SYS_BUS_DEVICE,
+    .instance_size  = sizeof(Ftgmac100State),
+    .class_init     = ftgmac100_class_init,
+};
+
+static void ftgmac100_register_types(void)
+{
+    type_register_static(&ftgmac100_info);
+}
+
+/* Legacy helper function.  Should go away when machine config files are
+   implemented.  */
+void ftgmac100_init(NICInfo *nd, uint32_t base, qemu_irq irq)
+{
+    DeviceState *dev;
+    SysBusDevice *s;
+
+    qemu_check_nic_model(nd, TYPE_FTGMAC100);
+    dev = qdev_create(NULL, TYPE_FTGMAC100);
+    qdev_set_nic_properties(dev, nd);
+    qdev_init_nofail(dev);
+    s = SYS_BUS_DEVICE(dev);
+    sysbus_mmio_map(s, 0, base);
+    sysbus_connect_irq(s, 0, irq);
+}
+
+type_init(ftgmac100_register_types)
diff --git a/hw/ftgmac100.h b/hw/ftgmac100.h
new file mode 100644
index 0000000..4030017
--- /dev/null
+++ b/hw/ftgmac100.h
@@ -0,0 +1,237 @@
+/*
+ * QEMU model of the FTGMAC100 Controller
+ *
+ * Copyright (C) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This file is licensed under GNU GPL v2+.
+ */
+
+#ifndef HW_ARM_FTGMAC100_H
+#define HW_ARM_FTGMAC100_H
+
+#define REG_ISR             0x00    /* interrupt status */
+#define REG_IMR             0x04    /* interrupt mask */
+#define REG_HMAC            0x08    /* mac address */
+#define REG_LMAC            0x0c
+#define REG_MHASH0          0x10    /* multicast hash table */
+#define REG_MHASH1          0x14
+#define REG_TXPD            0x18    /* kick tx dma */
+#define REG_RXPD            0x1c    /* kick rx dma */
+#define REG_TXBAR           0x20    /* tx list/ring base address */
+#define REG_RXBAR           0x24    /* rx list/ring base address */
+#define REG_HPTXPD          0x28    /* kick high priority tx dma */
+#define REG_HPTXBAR         0x2C    /* high priority tx list/ring base */
+#define REG_ITC             0x30    /* interrupt timer control */
+#define REG_APTC            0x34    /* auto polling timer control */
+#define REG_DBLAC           0x38    /* dma burst length, arbitration control */
+#define REG_DMAFIFO         0x3C    /* dma fifo */
+#define REG_REVR            0x40    /* revision */
+#define REG_FEAR            0x44    /* feature */
+#define REG_TPAFCR          0x48    /* tx priority arbitration&fifo control */
+#define REG_RBSR            0x4C    /* rx buffer size register */
+#define REG_MACCR           0x50    /* mac control register */
+#define REG_MACSR           0x54    /* mac status register */
+#define REG_TSTMODE         0x58    /* test mode register */
+#define REG_PHYCR           0x60    /* phy control register */
+#define REG_PHYDR           0x64    /* phy data register */
+#define REG_FCR             0x68    /* flow control register */
+#define REG_BPR             0x6c    /* back pressure register */
+#define REG_WOLCR           0x70    /* wake-on-lan control */
+#define REG_WOLSR           0x74    /* wake-on-lan status */
+#define REG_WFCRC           0x78    /* wake frame crc */
+#define REG_WFBM1           0x80    /* wake frame byte mask */
+#define REG_WFBM2           0x84
+#define REG_WFBM3           0x88
+#define REG_WFBM4           0x8c
+#define REG_TXPTR           0x90    /* tx pointer */
+#define REG_HPTXPTR         0x94    /* high priority tx pointer */
+#define REG_RXPTR           0x98    /* rx pointer */
+#define REG_TXPKT           0xa0    /* tx counter */
+#define REG_TXERR0          0xa4    /* tx error */
+#define REG_TXERR1          0xa8
+#define REG_TXERR2          0xac
+#define REG_RXPKT           0xb0    /* rx counter */
+#define REG_RXBCST          0xb4    /* rx bcst counter */
+#define REG_RXMCST          0xb8    /* rx mcst counter */
+#define REG_RXRUNT          0xc0    /* rx err: runt */
+#define REG_RXCRCFTL        0xc4    /* rx err: BIT[31-16]#crc; BIT[15-0]#ftl */
+#define REG_RXNOBCOL        0xc8    /* rx err: BIT[31-16]#nob; BIT[15-0]#col */
+
+/* interrupt status register */
+#define ISR_NOHTXB          (1UL<<10)   /* out of high priority tx buffer */
+#define ISR_PHYSTS_CHG      (1UL<<9)    /* phy status change */
+#define ISR_AHB_ERR         (1UL<<8)    /* AHB error */
+#define ISR_XPKT_LOST       (1UL<<7)    /* tx packet lost */
+#define ISR_NOTXBUF         (1UL<<6)    /* out of tx buffer */
+#define ISR_XPKT_OK         (1UL<<5)    /* tx packet ok (fifo) */
+#define ISR_XPKT_FINISH     (1UL<<4)    /* tx packet finished (phy) */
+#define ISR_RPKT_LOST       (1UL<<3)    /* rx packet lost */
+#define ISR_NORXBUF         (1UL<<2)    /* out of rx buffer */
+#define ISR_RPKT_OK         (1UL<<1)    /* rx packet ok (fifo) */
+#define ISR_RPKT_FINISH     (1UL<<0)    /* rx packet finished (ram) */
+
+/* MAC control register */
+#define MACCR_SW_RST            (1UL<<31)   /* software reset */
+#define MACCR_100M              (1UL<<19)   /* 100Mbps */
+#define MACCR_CRC_DIS           (1UL<<18)   /* discard crc error */
+#define MACCR_RX_BROADPKT       (1UL<<17)   /* recv all broadcast packets */
+#define MACCR_RX_MULTIPKT       (1UL<<16)   /* recv all multicast packets */
+#define MACCR_HT_MULTI_EN       (1UL<<15)   /* recv multicast by hash */
+#define MACCR_RCV_ALL           (1UL<<14)   /* recv all packets */
+#define MACCR_JUMBO_LF          (1UL<<13)   /* support jumbo frame */
+#define MACCR_RX_RUNT           (1UL<<12)   /* recv runt packets */
+#define MACCR_CRC_APD           (1UL<<10)   /* crc append */
+#define MACCR_GMODE             (1UL<<9)    /* giga mode */
+#define MACCR_FULLDUP           (1UL<<8)    /* full duplex */
+#define MACCR_ENRX_IN_HALFTX    (1UL<<7)    /* rx while tx in half duplex */
+#define MACCR_LOOP_EN           (1UL<<6)    /* loopback */
+#define MACCR_HPTXR_EN          (1UL<<5)    /* high priority tx enabled */
+#define MACCR_VLAN_RM           (1UL<<4)    /* vlan removal enabled */
+#define MACCR_RCV_EN            (1UL<<3)    /* rx enabled */
+#define MACCR_XMT_EN            (1UL<<2)    /* tx enabled */
+#define MACCR_RDMA_EN           (1UL<<1)    /* rx dma enabled */
+#define MACCR_XDMA_EN           (1UL<<0)    /* tx dma enabled */
+
+/*
+ * MDIO
+ */
+#define PHYCR_MDIOWR            (1 << 27)   /* mdio write */
+#define PHYCR_MDIORD            (1 << 26)   /* mdio read */
+
+/*
+ * Tx/Rx Descriptors
+ */
+typedef struct Ftgmac100RXD {
+    /* RXDES0 */
+#ifdef HOST_WORDS_BIGENDIAN
+    uint32_t owner:1;   /* BIT31: owner - 1:SW, 0: HW */
+    uint32_t rsvd3:1;
+    uint32_t frs:1;     /* first rx segment */
+    uint32_t lrs:1;     /* last rx segment */
+    uint32_t rsvd2:2;
+    uint32_t pausefrm:1;/* pause frame received */
+    uint32_t pauseopc:1;/* pause frame OP code */
+    uint32_t fifofull:1;/* packet dropped due to FIFO full */
+    uint32_t oddnb:1;   /* odd nibbles */
+    uint32_t runt:1;    /* runt packet (< 64 bytes) */
+    uint32_t ftl:1;     /* frame too long */
+    uint32_t crcerr:1;  /* crc error */
+    uint32_t rxerr:1;   /* rx error */
+    uint32_t bcast:1;   /* broadcast */
+    uint32_t mcast:1;   /* multicast */
+    uint32_t end:1;     /* end of descriptor list/ring */
+    uint32_t rsvd1:1;
+    uint32_t len:14;    /* max./received packet length */
+#else
+    uint32_t len:14;
+    uint32_t rsvd1:1;
+    uint32_t end:1;
+    uint32_t mcast:1;
+    uint32_t bcast:1;
+    uint32_t rxerr:1;
+    uint32_t crcerr:1;
+    uint32_t ftl:1;
+    uint32_t runt:1;
+    uint32_t oddnb:1;
+    uint32_t fifofull:1;
+    uint32_t pauseopc:1;
+    uint32_t pausefrm:1;
+    uint32_t rsvd2:2;
+    uint32_t lrs:1;
+    uint32_t frs:1;
+    uint32_t rsvd3:1;
+    uint32_t owner:1;
+#endif  /* #ifdef HOST_WORDS_BIGENDIAN */
+
+    /* RXDES1 */
+#ifdef HOST_WORDS_BIGENDIAN
+    uint32_t rsvd5:4;
+    uint32_t ipcs:1;    /* IPv4 checksum failed */
+    uint32_t udpcs:1;   /* IPv4 UDP checksum failed */
+    uint32_t tcpcs:1;   /* IPv4 TCP checksum failed */
+    uint32_t vlan:1;    /* VLAN tag detected */
+    uint32_t nofrag:1;  /* Not Fragmented */
+    uint32_t llc:1;     /* LLC packet detected */
+    uint32_t proto:2;   /* Protocol Type */
+    uint32_t rsvd4:4;
+    uint32_t vlantag:16;/* VLAN tag */
+#else
+    uint32_t vlantag:16;
+    uint32_t rsvd4:4;
+    uint32_t proto:2;
+    uint32_t llc:1;
+    uint32_t nofrag:1;
+    uint32_t vlan:1;
+    uint32_t tcpcs:1;
+    uint32_t udpcs:1;
+    uint32_t ipcs:1;
+    uint32_t rsvd5:4;
+#endif  /* #ifdef HOST_WORDS_BIGENDIAN */
+
+    /* RXDES2 */
+    void    *skb;
+
+    /* RXDES3 */
+    uint32_t buf;
+} __attribute__ ((aligned (16))) Ftgmac100RXD;
+
+typedef struct Ftgmac100TXD {
+    /* TXDES0 */
+#ifdef HOST_WORDS_BIGENDIAN
+    uint32_t owner:1;   /* BIT31: owner - 1:HW, 0: SW */
+    uint32_t rsvd4:1;
+    uint32_t fts:1;     /* first tx segment */
+    uint32_t lts:1;     /* last tx segment */
+    uint32_t rsvd3:8;
+    uint32_t crcerr:1;  /* crc error */
+    uint32_t rsvd2:3;
+    uint32_t end:1;     /* end of descriptor list/ring */
+    uint32_t rsvd1:1;
+    uint32_t len:14;    /* packet length */
+#else
+    uint32_t len:14;
+    uint32_t rsvd1:1;
+    uint32_t end:1;
+    uint32_t rsvd2:3;
+    uint32_t crcerr:1;
+    uint32_t rsvd3:8;
+    uint32_t lts:1;
+    uint32_t fts:1;
+    uint32_t rsvd4:1;
+    uint32_t owner:1;
+#endif  /* #ifdef HOST_WORDS_BIGENDIAN */
+
+    /* TXDES1 */
+#ifdef HOST_WORDS_BIGENDIAN
+    uint32_t txic:1;        /* interrupt when data has been copied to phy */
+    uint32_t tx2fic:1;      /* interrupt when data has been copied to fifo */
+    uint32_t rsvd6:7;
+    uint32_t llc:1;         /* is a LLC packet */
+    uint32_t rsvd5:2;
+    uint32_t ipcs:1;        /* Enable IPv4 checksum offload */
+    uint32_t udpcs:1;       /* Enable IPv4 UDP checksum offload */
+    uint32_t tcpcs:1;       /* Enable IPv4 TCP checksum offload */
+    uint32_t vlan:1;        /* Enable VLAN tag insertion */
+    uint32_t vlantag:16;    /* VLAN tag */
+#else
+    uint32_t vlantag:16;
+    uint32_t vlan:1;
+    uint32_t tcpcs:1;
+    uint32_t udpcs:1;
+    uint32_t ipcs:1;
+    uint32_t rsvd5:2;
+    uint32_t llc:1;
+    uint32_t rsvd6:7;
+    uint32_t tx2fic:1;
+    uint32_t txic:1;
+#endif  /* #ifdef HOST_WORDS_BIGENDIAN */
+
+    /* TXDES2 */
+    void    *skb;
+
+    /* TXDES3 */
+    uint32_t buf;
+} __attribute__ ((aligned (16))) Ftgmac100TXD;
+
+#endif    /* #ifndef HW_ARM_FTGMAC100_H */
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH v9 19/24] hw/arm: add FTLCDC200 LCD controller support
  2013-03-25 12:09 [Qemu-devel] [PATCH v9 00/24] hw/arm: add Faraday A369 SoC platform support Kuo-Jung Su
                   ` (17 preceding siblings ...)
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 18/24] hw/arm: add FTGMAC100 1Gbps ethernet support Kuo-Jung Su
@ 2013-03-25 12:09 ` Kuo-Jung Su
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 20/24] hw/arm: add FTTSC010 touchscreen " Kuo-Jung Su
                   ` (4 subsequent siblings)
  23 siblings, 0 replies; 35+ messages in thread
From: Kuo-Jung Su @ 2013-03-25 12:09 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, i.mitsyanko, Blue Swirl, Paul Brook, Kuo-Jung Su,
	Andreas, fred.konrad

From: Kuo-Jung Su <dantesu@faraday-tech.com>

The FTLCDC200 Color LCD controller performs translation of
pixel-coded data into the required formats and timings to
drive a variety of single/dual mono and color LCDs.

Depending on the LCD type and mode, the unpacked data can represent:
   1. an actual true display gray or color value
   2. an address to a 256 x 16 bit wide palette RAM gray or color value.

The FTLCDC200 generates 4 individual interrupts for:
   1. DMA FIFO underflow
   2. base address update
   3. vertical status
   4. bus error.

There is also a single combined interrupt that is raised when any of
the individual interrupts become active.

Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
---
 hw/arm/Makefile.objs    |    2 +-
 hw/arm/ftplat_a369soc.c |   10 +
 hw/ftlcdc200.c          |  516 +++++++++++++++++++++++++++++++++++++++++++++++
 hw/ftlcdc200.h          |  110 ++++++++++
 hw/ftlcdc200_template.h |  439 ++++++++++++++++++++++++++++++++++++++++
 5 files changed, 1076 insertions(+), 1 deletion(-)
 create mode 100644 hw/ftlcdc200.c
 create mode 100644 hw/ftlcdc200.h
 create mode 100644 hw/ftlcdc200_template.h

diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 62c823d..f6b947e 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -26,7 +26,7 @@ obj-y += imx_serial.o imx_ccm.o imx_timer.o imx_avic.o
 obj-$(CONFIG_KVM) += kvm/arm_gic.o
 obj-y += ftintc020.o ftahbc020.o ftddrii030.o ftpwmtmr010.o ftwdt010.o \
                 ftrtc011.o ftdmac020.o ftapbbrg020.o ftnandc021.o fti2c010.o \
-                ftssp010.o ftgmac100.o
+                ftssp010.o ftgmac100.o ftlcdc200.o
 
 obj-y := $(addprefix ../,$(obj-y))
 
diff --git a/hw/arm/ftplat_a369soc.c b/hw/arm/ftplat_a369soc.c
index 622b1db..cdc6d4a 100644
--- a/hw/arm/ftplat_a369soc.c
+++ b/hw/arm/ftplat_a369soc.c
@@ -228,6 +228,16 @@ static void a369soc_chip_init(FaradaySoCState *s)
     if (nb_nics > 0) {
         ftgmac100_init(&nd_table[0], 0x90c00000, s->pic[32]);
     }
+
+    /* ftlcdc200 */
+    sysbus_create_varargs("ftlcdc200",
+                          0x94a00000,
+                          s->pic[0],  /* ALL (NC in A369) */
+                          s->pic[25], /* VSTATUS */
+                          s->pic[24], /* Base Address Update */
+                          s->pic[23], /* FIFO Under-Run */
+                          s->pic[22], /* AHB Bus Error */
+                          NULL);
 }
 
 static void a369soc_realize(DeviceState *dev, Error **errp)
diff --git a/hw/ftlcdc200.c b/hw/ftlcdc200.c
new file mode 100644
index 0000000..2e25372
--- /dev/null
+++ b/hw/ftlcdc200.c
@@ -0,0 +1,516 @@
+/*
+ * Faraday FTLCDC200 Color LCD Controller
+ *
+ * base is pl110.c
+ *
+ * Copyright (c) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This code is licensed under the GNU LGPL
+ */
+
+#include "hw/sysbus.h"
+#include "hw/framebuffer.h"
+#include "ui/console.h"
+#include "ui/pixel_ops.h"
+
+#include "qemu/bitops.h"
+#include "hw/ftlcdc200.h"
+
+enum ftlcdc200_irqpin {
+    IRQ_ALL = 0,
+    IRQ_VSTATUS,
+    IRQ_BASEUPT,
+    IRQ_FIFOUR,
+    IRQ_BUSERR,
+};
+
+enum ftlcdc200_bppmode {
+    BPP_1 = 0,
+    BPP_2,
+    BPP_4,
+    BPP_8,
+    BPP_16,
+    BPP_32,
+    BPP_16_565,
+    BPP_12,
+};
+
+#define TYPE_FTLCDC200  "ftlcdc200"
+
+typedef struct Ftlcdc200State {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    QemuConsole *con;
+
+    qemu_irq irq[5];
+    int cols;
+    int rows;
+    enum ftlcdc200_bppmode bpp;
+    int invalidate;
+    uint32_t palette[256];
+    uint32_t raw_palette[128];
+
+    /* hw register caches */
+    uint32_t fer;   /* function enable register */
+    uint32_t ppr;   /* panel pixel register */
+    uint32_t ier;   /* interrupt enable register */
+    uint32_t isr;   /* interrupt status register */
+    uint32_t sppr;  /* serail panel pixel register */
+
+    uint32_t fb[4]; /* frame buffer base address register */
+    uint32_t ht;    /* horizontal timing control register */
+    uint32_t vt0;   /* vertital timing control register 0 */
+    uint32_t vt1;   /* vertital timing control register 1 */
+    uint32_t pol;   /* polarity */
+
+} Ftlcdc200State;
+
+#define FTLCDC200(obj) \
+    OBJECT_CHECK(Ftlcdc200State, obj, TYPE_FTLCDC200)
+
+static const VMStateDescription vmstate_ftlcdc200 = {
+    .name = TYPE_FTLCDC200,
+    .version_id = 2,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_INT32(cols, Ftlcdc200State),
+        VMSTATE_INT32(rows, Ftlcdc200State),
+        VMSTATE_UINT32(bpp, Ftlcdc200State),
+        VMSTATE_INT32(invalidate, Ftlcdc200State),
+        VMSTATE_UINT32_ARRAY(palette, Ftlcdc200State, 256),
+        VMSTATE_UINT32_ARRAY(raw_palette, Ftlcdc200State, 128),
+        VMSTATE_UINT32(fer, Ftlcdc200State),
+        VMSTATE_UINT32(ppr, Ftlcdc200State),
+        VMSTATE_UINT32(ier, Ftlcdc200State),
+        VMSTATE_UINT32(isr, Ftlcdc200State),
+        VMSTATE_UINT32(sppr, Ftlcdc200State),
+        VMSTATE_UINT32_ARRAY(fb, Ftlcdc200State, 4),
+        VMSTATE_UINT32(ht, Ftlcdc200State),
+        VMSTATE_UINT32(vt0, Ftlcdc200State),
+        VMSTATE_UINT32(vt1, Ftlcdc200State),
+        VMSTATE_UINT32(pol, Ftlcdc200State),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+#define BITS 8
+#include "ftlcdc200_template.h"
+#define BITS 15
+#include "ftlcdc200_template.h"
+#define BITS 16
+#include "ftlcdc200_template.h"
+#define BITS 24
+#include "ftlcdc200_template.h"
+#define BITS 32
+#include "ftlcdc200_template.h"
+
+static int ftlcdc200_enabled(Ftlcdc200State *s)
+{
+    uint32_t mask = FER_EN | FER_ON;
+    return ((s->fer & mask) == mask)
+            && s->bpp && s->cols && s->rows && s->fb[0];
+}
+
+/* Update interrupts.  */
+static void ftlcdc200_update_irq(Ftlcdc200State *s)
+{
+    uint32_t mask = s->ier & s->isr;
+
+    if (mask) {
+        qemu_irq_raise(s->irq[IRQ_ALL]);
+        qemu_set_irq(s->irq[IRQ_FIFOUR],  (mask & ISR_FIFOUR) ? 1 : 0);
+        qemu_set_irq(s->irq[IRQ_BASEUPT], (mask & ISR_NEXTFB) ? 1 : 0);
+        qemu_set_irq(s->irq[IRQ_VSTATUS], (mask & ISR_VCOMP) ? 1 : 0);
+        qemu_set_irq(s->irq[IRQ_BUSERR],  (mask & ISR_BUSERR) ? 1 : 0);
+    } else {
+        qemu_irq_lower(s->irq[IRQ_ALL]);
+        qemu_irq_lower(s->irq[IRQ_VSTATUS]);
+        qemu_irq_lower(s->irq[IRQ_BASEUPT]);
+        qemu_irq_lower(s->irq[IRQ_FIFOUR]);
+        qemu_irq_lower(s->irq[IRQ_BUSERR]);
+    }
+}
+
+static void ftlcdc200_update_display(void *opaque)
+{
+    Ftlcdc200State *s = FTLCDC200(opaque);
+    DisplaySurface *surface = qemu_console_surface(s->con);
+    drawfn *fntable;
+    drawfn fn;
+    int dest_width;
+    int src_width;
+    int bpp_offset;
+    int first;
+    int last;
+
+    if (!ftlcdc200_enabled(s)) {
+        return;
+    }
+
+    switch (surface_bits_per_pixel(surface)) {
+    case 0:
+        return;
+    case 8:
+        fntable = ftlcdc200_draw_fn_8;
+        dest_width = 1;
+        break;
+    case 15:
+        fntable = ftlcdc200_draw_fn_15;
+        dest_width = 2;
+        break;
+    case 16:
+        fntable = ftlcdc200_draw_fn_16;
+        dest_width = 2;
+        break;
+    case 24:
+        fntable = ftlcdc200_draw_fn_24;
+        dest_width = 3;
+        break;
+    case 32:
+        fntable = ftlcdc200_draw_fn_32;
+        dest_width = 4;
+        break;
+    default:
+        fprintf(stderr, "ftlcdc200: Bad color depth\n");
+        abort();
+    }
+
+    bpp_offset = 0;
+    fn = fntable[s->bpp + bpp_offset];
+
+    src_width = s->cols;
+    switch (s->bpp) {
+    case BPP_1:
+        src_width >>= 3;
+        break;
+    case BPP_2:
+        src_width >>= 2;
+        break;
+    case BPP_4:
+        src_width >>= 1;
+        break;
+    case BPP_8:
+        break;
+    case BPP_16:
+    case BPP_16_565:
+    case BPP_12:
+        src_width <<= 1;
+        break;
+    case BPP_32:
+        src_width <<= 2;
+        break;
+    }
+    dest_width *= s->cols;
+    first = 0;
+    framebuffer_update_display(surface,
+                               sysbus_address_space(&s->busdev),
+                               s->fb[0], s->cols, s->rows,
+                               src_width, dest_width, 0,
+                               s->invalidate,
+                               fn, s->palette,
+                               &first, &last);
+    if (s->ier & (IER_VCOMP | IER_NEXTFB)) {
+        s->isr |= (IER_VCOMP | IER_NEXTFB);
+        ftlcdc200_update_irq(s);
+    }
+    if (first >= 0) {
+        dpy_gfx_update(s->con, 0, first, s->cols, last - first + 1);
+    }
+    s->invalidate = 0;
+}
+
+static void ftlcdc200_invalidate_display(void *opaque)
+{
+    Ftlcdc200State *s = FTLCDC200(opaque);
+    s->invalidate = 1;
+    if (ftlcdc200_enabled(s)) {
+        qemu_console_resize(s->con, s->cols, s->rows);
+    }
+}
+
+static void ftlcdc200_update_palette(Ftlcdc200State *s, int n)
+{
+    DisplaySurface *surface = qemu_console_surface(s->con);
+    int i;
+    uint32_t raw;
+    unsigned int r, g, b;
+
+    raw = s->raw_palette[n];
+    n <<= 1;
+    for (i = 0; i < 2; i++) {
+        r = extract32(raw,  0, 5) << 3;
+        g = extract32(raw,  5, 5) << 3;
+        b = extract32(raw, 10, 5) << 3;
+        /* The I bit is ignored.  */
+        raw >>= 6;
+        switch (surface_bits_per_pixel(surface)) {
+        case 8:
+            s->palette[n] = rgb_to_pixel8(r, g, b);
+            break;
+        case 15:
+            s->palette[n] = rgb_to_pixel15(r, g, b);
+            break;
+        case 16:
+            s->palette[n] = rgb_to_pixel16(r, g, b);
+            break;
+        case 24:
+        case 32:
+            s->palette[n] = rgb_to_pixel32(r, g, b);
+            break;
+        }
+        n++;
+    }
+}
+
+static void ftlcdc200_resize(Ftlcdc200State *s, int width, int height)
+{
+    if (width != s->cols || height != s->rows) {
+        if (ftlcdc200_enabled(s)) {
+            qemu_console_resize(s->con, width, height);
+        }
+    }
+    s->cols = width;
+    s->rows = height;
+}
+
+static uint64_t
+ftlcdc200_mem_read(void *opaque, hwaddr addr, unsigned size)
+{
+    Ftlcdc200State *s = FTLCDC200(opaque);
+
+    switch (addr) {
+    case REG_FER:
+        return s->fer;
+    case REG_PPR:
+        return s->ppr;
+    case REG_IER:
+        return s->ier;
+    case REG_ISR:
+        return s->isr;
+    case REG_FB0:
+        return s->fb[0];
+    case REG_FB1:
+        return s->fb[1];
+    case REG_FB2:
+        return s->fb[2];
+    case REG_FB3:
+        return s->fb[3];
+    case REG_HT:
+        return s->ht;
+    case REG_VT0:
+        return s->vt0;
+    case REG_VT1:
+        return s->vt1;
+    case REG_POL:
+        return s->pol;
+    case REG_SPPR:
+        return s->sppr;
+    case 0xA00 ... 0xBFC:   /* palette.  */
+        return s->raw_palette[(addr - 0xA00) >> 2];
+    /* we don't care */
+    case REG_CCIR:
+    case 0x300 ... 0x310:   /* image parameters */
+    case 0x400 ... 0x40C:   /* color management */
+    case 0x600 ... 0x8FC:   /* gamma correction */
+    case 0xC00 ... 0xD3C:   /* cstn parameters */
+    case 0x1100 ... 0x112C: /* scalar control */
+    case 0x2000 ... 0xBFFC: /* osd control */
+        return 0;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "ftlcdc200: undefined memory access@%#" HWADDR_PRIx "\n", addr);
+        return 0;
+    }
+}
+
+static void
+ftlcdc200_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+    Ftlcdc200State *s = FTLCDC200(opaque);
+    int n;
+
+    /* For simplicity invalidate the display whenever a control register
+       is written to.  */
+    s->invalidate = 1;
+
+    switch (addr) {
+    case REG_FER:
+        s->fer = (uint32_t)val;
+        if (ftlcdc200_enabled(s)) {
+            qemu_console_resize(s->con, s->cols, s->rows);
+        }
+        break;
+    case REG_PPR:
+        s->ppr = (uint32_t)val;
+        switch (s->ppr & PPR_RGB_MASK) {
+        case PPR_RGB1:
+            s->bpp = BPP_1;
+            break;
+        case PPR_RGB2:
+            s->bpp = BPP_2;
+            break;
+        case PPR_RGB4:
+            s->bpp = BPP_4;
+            break;
+        case PPR_RGB8:
+            s->bpp = BPP_8;
+            break;
+        case PPR_RGB12:
+            s->bpp = BPP_12;
+            break;
+        case PPR_RGB16_555:
+            s->bpp = BPP_16;
+            break;
+        case PPR_RGB16_565:
+            s->bpp = BPP_16_565;
+            break;
+        case PPR_RGB24:
+        default:
+            s->bpp = BPP_32;
+            break;
+        }
+        if (ftlcdc200_enabled(s)) {
+            qemu_console_resize(s->con, s->cols, s->rows);
+        }
+        break;
+    case REG_IER:
+        s->ier = (uint32_t)val;
+        ftlcdc200_update_irq(s);
+        break;
+    case REG_ISCR:
+        s->isr &= ~((uint32_t)val);
+        ftlcdc200_update_irq(s);
+        break;
+    case REG_FB0:
+        s->fb[0] = (uint32_t)val;
+        break;
+    case REG_FB1:
+        s->fb[1] = (uint32_t)val;
+        break;
+    case REG_FB2:
+        s->fb[2] = (uint32_t)val;
+        break;
+    case REG_FB3:
+        s->fb[3] = (uint32_t)val;
+        break;
+    case REG_HT:
+        s->ht = (uint32_t)val;
+        n = ((s->ht & 0xff) + 1) << 4;
+        ftlcdc200_resize(s, n, s->rows);
+        break;
+    case REG_VT0:
+        s->vt0 = (uint32_t)val;
+        n = (s->vt0 & 0xfff) + 1;
+        ftlcdc200_resize(s, s->cols, n);
+        break;
+    case REG_VT1:
+        s->vt1 = (uint32_t)val;
+        break;
+    case REG_POL:
+        s->pol = (uint32_t)val;
+        break;
+    case REG_SPPR:
+        s->sppr = (uint32_t)val;
+        break;
+    case 0xA00 ... 0xBFC:   /* palette.  */
+        n = (addr - 0xA00) >> 2;
+        s->raw_palette[(addr - 0xA00) >> 2] = val;
+        ftlcdc200_update_palette(s, n);
+        break;
+    case 0x300 ... 0x310:   /* image parameters */
+    case 0x400 ... 0x40C:   /* color management */
+    case 0x600 ... 0x8FC:   /* gamma correction */
+    case 0xC00 ... 0xD3C:   /* cstn parameters */
+    case 0x1100 ... 0x112C: /* scalar control */
+    case 0x2000 ... 0xBFFC: /* osd control */
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "ftlcdc200: undefined memory access@%#" HWADDR_PRIx "\n", addr);
+        break;
+    }
+}
+
+static const MemoryRegionOps mmio_ops = {
+    .read  = ftlcdc200_mem_read,
+    .write = ftlcdc200_mem_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    }
+};
+
+static void ftlcdc200_reset(DeviceState *ds)
+{
+    Ftlcdc200State *s = FTLCDC200(SYS_BUS_DEVICE(ds));
+
+    s->fer   = 0;
+    s->ppr   = 0;
+    s->ier   = 0;
+    s->isr   = 0;
+    s->sppr  = 0;
+    s->fb[0] = 0;
+    s->fb[1] = 0;
+    s->fb[2] = 0;
+    s->fb[3] = 0;
+    s->ht    = 0;
+    s->vt0   = 0;
+    s->vt1   = 0;
+    s->pol   = 0;
+    s->cols  = 0;
+    s->rows  = 0;
+    s->bpp   = 0;
+    s->invalidate = 1;
+
+    memset(s->raw_palette, 0, sizeof(s->raw_palette));
+
+    /* Make sure we redraw, and at the right size */
+    ftlcdc200_invalidate_display(s);
+}
+
+static void ftlcdc200_realize(DeviceState *dev, Error **errp)
+{
+    Ftlcdc200State *s = FTLCDC200(dev);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+
+    memory_region_init_io(&s->iomem,
+                          &mmio_ops,
+                          s,
+                          TYPE_FTLCDC200,
+                          0x10000);
+    sysbus_init_mmio(sbd, &s->iomem);
+    sysbus_init_irq(sbd, &s->irq[IRQ_ALL]);
+    sysbus_init_irq(sbd, &s->irq[IRQ_VSTATUS]);
+    sysbus_init_irq(sbd, &s->irq[IRQ_BASEUPT]);
+    sysbus_init_irq(sbd, &s->irq[IRQ_FIFOUR]);
+    sysbus_init_irq(sbd, &s->irq[IRQ_BUSERR]);
+    s->con = graphic_console_init(ftlcdc200_update_display,
+                                  ftlcdc200_invalidate_display,
+                                  NULL, NULL, s);
+}
+
+static void ftlcdc200_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->reset   = ftlcdc200_reset;
+    dc->vmsd    = &vmstate_ftlcdc200;
+    dc->realize = ftlcdc200_realize;
+    dc->no_user = 1;
+}
+
+static const TypeInfo ftlcdc200_info = {
+    .name          = TYPE_FTLCDC200,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(Ftlcdc200State),
+    .class_init    = ftlcdc200_class_init,
+};
+
+static void ftlcdc200_register_types(void)
+{
+    type_register_static(&ftlcdc200_info);
+}
+
+type_init(ftlcdc200_register_types)
diff --git a/hw/ftlcdc200.h b/hw/ftlcdc200.h
new file mode 100644
index 0000000..53917e1
--- /dev/null
+++ b/hw/ftlcdc200.h
@@ -0,0 +1,110 @@
+/*
+ * Faraday FTLCDC200 Color LCD Controller
+ *
+ * Copyright (c) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This code is licensed under the GNU LGPL
+ */
+
+#ifndef HW_ARM_FTLCDC2XX_H
+#define HW_ARM_FTLCDC2XX_H
+
+/* HW Registers */
+
+#define REG_FER         0x00000 /* LCD Function Enable Register */
+#define REG_PPR         0x00004 /* LCD Panel Pixel Register */
+#define REG_IER         0x00008 /* LCD Interrupt Enable Register */
+#define REG_ISCR        0x0000C /* LCD Interrupt Status Clear Register */
+#define REG_ISR         0x00010 /* LCD Interrupt Status Register */
+#define REG_FB0         0x00018 /* LCD Framebuffer Base Register 0 */
+#define REG_FB1         0x00024 /* LCD Framebuffer Base Register 1 */
+#define REG_FB2         0x00030 /* LCD Framebuffer Base Register 2 */
+#define REG_FB3         0x0003C /* LCD Framebuffer Base Register 3 */
+
+#define REG_HT          0x00100 /* LCD Horizontal Timing Control Register */
+#define REG_VT0         0x00104 /* LCD Vertical Timing Control Register 0 */
+#define REG_VT1         0x00108 /* LCD Vertical Timing Control Register 1 */
+#define REG_POL         0x0010C /* LCD Polarity Control Register */
+
+#define REG_SPPR        0x00200 /* LCD Serial Panel Pixel Register */
+#define REG_CCIR        0x00204 /* LCD CCIR565 Register */
+
+/* LCD Function Enable Register */
+#define FER_EN          (1 << 0)    /* chip enabled */
+#define FER_ON          (1 << 1)    /* screen on */
+#define FER_YUV420      (3 << 2)
+#define FER_YUV422      (2 << 2)
+#define FER_YUV         (1 << 3)    /* 1:YUV, 0:RGB */
+
+/* LCD Panel Pixel Register */
+#define PPR_BPP_1       (0 << 0)
+#define PPR_BPP_2       (1 << 0)
+#define PPR_BPP_4       (2 << 0)
+#define PPR_BPP_8       (3 << 0)
+#define PPR_BPP_16      (4 << 0)
+#define PPR_BPP_24      (5 << 0)
+#define PPR_BPP_MASK    (7 << 0)
+#define PPR_PWROFF      (1 << 3)
+#define PPR_BGR         (1 << 4)
+#define PPR_ENDIAN_LBLP (0 << 5)
+#define PPR_ENDIAN_BBBP (1 << 5)
+#define PPR_ENDIAN_LBBP (2 << 5)
+#define PPR_ENDIAN_MASK (3 << 5)
+#define PPR_RGB1        (PPR_BPP_1)
+#define PPR_RGB2        (PPR_BPP_2)
+#define PPR_RGB4        (PPR_BPP_4)
+#define PPR_RGB8        (PPR_BPP_8)
+#define PPR_RGB12       (PPR_BPP_16 | (2 << 7))
+#define PPR_RGB16_555   (PPR_BPP_16 | (1 << 7))
+#define PPR_RGB16_565   (PPR_BPP_16 | (0 << 7))
+#define PPR_RGB24       (PPR_BPP_24)
+#define PPR_RGB32       (PPR_BPP_24)
+#define PPR_RGB_MASK    (PPR_BPP_MASK | (3 << 7))
+#define PPR_VCOMP_VSYNC (0 << 9)
+#define PPR_VCOMP_VBP   (1 << 9)
+#define PPR_VCOMP_VAIMG (2 << 9)
+#define PPR_VCOMP_VFP   (3 << 9)
+#define PPR_VCOMP_MASK  (3 << 9)
+
+/* LCD Interrupt Enable Register */
+#define IER_FIFOUR      (1 << 0)
+#define IER_NEXTFB      (1 << 1)
+#define IER_VCOMP       (1 << 2)
+#define IER_BUSERR      (1 << 3)
+
+/* LCD Interrupt Status Register */
+#define ISR_FIFOUR      (1 << 0)
+#define ISR_NEXTFB      (1 << 1)
+#define ISR_VCOMP       (1 << 2)
+#define ISR_BUSERR      (1 << 3)
+
+/* LCD Horizontal Timing Control Register */
+#define HT_HBP(x)       ((((x) - 1) & 0xff) << 24)
+#define HT_HFP(x)       ((((x) - 1) & 0xff) << 16)
+#define HT_HSYNC(x)     ((((x) - 1) & 0xff) << 8)
+#define HT_PL(x)        (((x >> 4) - 1) & 0xff)
+
+/* LCD Vertical Timing Control Register 0 */
+#define VT0_VFP(x)      (((x) & 0xff) << 24)
+#define VT0_VSYNC(x)    ((((x) - 1) & 0x3f) << 16)
+#define VT0_LF(x)       (((x) - 1) & 0xfff)
+
+/* LCD Polarity Control Register */
+#define POL_IVS         (1 << 0)
+#define POL_IHS         (1 << 1)
+#define POL_ICK         (1 << 2)
+#define POL_IDE         (1 << 3)
+#define POL_IPWR        (1 << 4)
+#define POL_DIV(x)      ((((x) - 1) & 0x7f) << 8)
+
+/* LCD Serial Panel Pixel Register */
+#define SPPR_SERIAL     (1 << 0)
+#define SPPR_DELTA      (1 << 1)
+#define SPPR_CS_RGB     (0 << 2)
+#define SPPR_CS_BRG     (1 << 2)
+#define SPPR_CS_GBR     (2 << 2)
+#define SPPR_LSR        (1 << 4)
+#define SPPR_AUO052     (1 << 5)
+
+#endif /* HW_ARM_FTLCDC2XX_H */
diff --git a/hw/ftlcdc200_template.h b/hw/ftlcdc200_template.h
new file mode 100644
index 0000000..f2785fc
--- /dev/null
+++ b/hw/ftlcdc200_template.h
@@ -0,0 +1,439 @@
+/*
+ * Faraday FTLCDC200 Color LCD Controller
+ *
+ * Copyright (c) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This code is licensed under the GNU LGPL
+ *
+ * Framebuffer format conversion routines.
+ */
+
+#ifndef ORDER
+
+#if BITS == 8
+#define COPY_PIXEL(to, from) \
+    do { *((to)++) = (from); } while (0)
+#elif BITS == 15 || BITS == 16
+#define COPY_PIXEL(to, from) \
+    do { \
+        *(uint16_t *)to = from;\
+        to += 2;\
+    } while (0)
+#elif BITS == 24
+#define COPY_PIXEL(to, from) \
+    do {\
+        *(to++) = from;\
+        *(to++) = (from) >> 8;\
+        *(to++) = (from) >> 16;\
+    } while (0)
+#elif BITS == 32
+#define COPY_PIXEL(to, from) \
+    do {\
+        *(uint32_t *)to = from;\
+        to += 4;\
+    } while (0)
+#else
+#error unknown bit depth
+#endif
+
+#undef RGB
+#define BORDER bgr
+#define ORDER 0
+#include "ftlcdc200_template.h"
+#define ORDER 1
+#include "ftlcdc200_template.h"
+#define ORDER 2
+#include "ftlcdc200_template.h"
+#undef BORDER
+#define RGB
+#define BORDER rgb
+#define ORDER 0
+#include "ftlcdc200_template.h"
+#define ORDER 1
+#include "ftlcdc200_template.h"
+#define ORDER 2
+#include "ftlcdc200_template.h"
+#undef BORDER
+
+static drawfn glue(ftlcdc200_draw_fn_, BITS)[48] = {
+    glue(ftlcdc200_draw_line1_lblp_bgr, BITS),
+    glue(ftlcdc200_draw_line2_lblp_bgr, BITS),
+    glue(ftlcdc200_draw_line4_lblp_bgr, BITS),
+    glue(ftlcdc200_draw_line8_lblp_bgr, BITS),
+    glue(ftlcdc200_draw_line16_555_lblp_bgr, BITS),
+    glue(ftlcdc200_draw_line32_lblp_bgr, BITS),
+    glue(ftlcdc200_draw_line16_lblp_bgr, BITS),
+    glue(ftlcdc200_draw_line12_lblp_bgr, BITS),
+
+    glue(ftlcdc200_draw_line1_bbbp_bgr, BITS),
+    glue(ftlcdc200_draw_line2_bbbp_bgr, BITS),
+    glue(ftlcdc200_draw_line4_bbbp_bgr, BITS),
+    glue(ftlcdc200_draw_line8_bbbp_bgr, BITS),
+    glue(ftlcdc200_draw_line16_555_bbbp_bgr, BITS),
+    glue(ftlcdc200_draw_line32_bbbp_bgr, BITS),
+    glue(ftlcdc200_draw_line16_bbbp_bgr, BITS),
+    glue(ftlcdc200_draw_line12_bbbp_bgr, BITS),
+
+    glue(ftlcdc200_draw_line1_lbbp_bgr, BITS),
+    glue(ftlcdc200_draw_line2_lbbp_bgr, BITS),
+    glue(ftlcdc200_draw_line4_lbbp_bgr, BITS),
+    glue(ftlcdc200_draw_line8_lbbp_bgr, BITS),
+    glue(ftlcdc200_draw_line16_555_lbbp_bgr, BITS),
+    glue(ftlcdc200_draw_line32_lbbp_bgr, BITS),
+    glue(ftlcdc200_draw_line16_lbbp_bgr, BITS),
+    glue(ftlcdc200_draw_line12_lbbp_bgr, BITS),
+
+    glue(ftlcdc200_draw_line1_lblp_rgb, BITS),
+    glue(ftlcdc200_draw_line2_lblp_rgb, BITS),
+    glue(ftlcdc200_draw_line4_lblp_rgb, BITS),
+    glue(ftlcdc200_draw_line8_lblp_rgb, BITS),
+    glue(ftlcdc200_draw_line16_555_lblp_rgb, BITS),
+    glue(ftlcdc200_draw_line32_lblp_rgb, BITS),
+    glue(ftlcdc200_draw_line16_lblp_rgb, BITS),
+    glue(ftlcdc200_draw_line12_lblp_rgb, BITS),
+
+    glue(ftlcdc200_draw_line1_bbbp_rgb, BITS),
+    glue(ftlcdc200_draw_line2_bbbp_rgb, BITS),
+    glue(ftlcdc200_draw_line4_bbbp_rgb, BITS),
+    glue(ftlcdc200_draw_line8_bbbp_rgb, BITS),
+    glue(ftlcdc200_draw_line16_555_bbbp_rgb, BITS),
+    glue(ftlcdc200_draw_line32_bbbp_rgb, BITS),
+    glue(ftlcdc200_draw_line16_bbbp_rgb, BITS),
+    glue(ftlcdc200_draw_line12_bbbp_rgb, BITS),
+
+    glue(ftlcdc200_draw_line1_lbbp_rgb, BITS),
+    glue(ftlcdc200_draw_line2_lbbp_rgb, BITS),
+    glue(ftlcdc200_draw_line4_lbbp_rgb, BITS),
+    glue(ftlcdc200_draw_line8_lbbp_rgb, BITS),
+    glue(ftlcdc200_draw_line16_555_lbbp_rgb, BITS),
+    glue(ftlcdc200_draw_line32_lbbp_rgb, BITS),
+    glue(ftlcdc200_draw_line16_lbbp_rgb, BITS),
+    glue(ftlcdc200_draw_line12_lbbp_rgb, BITS),
+};
+
+#undef BITS
+#undef COPY_PIXEL
+
+#else
+
+#if ORDER == 0
+#define NAME glue(glue(lblp_, BORDER), BITS)
+#ifdef HOST_WORDS_BIGENDIAN
+#define SWAP_WORDS 1
+#endif
+#elif ORDER == 1
+#define NAME glue(glue(bbbp_, BORDER), BITS)
+#ifndef HOST_WORDS_BIGENDIAN
+#define SWAP_WORDS 1
+#endif
+#else
+#define SWAP_PIXELS 1
+#define NAME glue(glue(lbbp_, BORDER), BITS)
+#ifdef HOST_WORDS_BIGENDIAN
+#define SWAP_WORDS 1
+#endif
+#endif
+
+#define FN_2(x, y) FN(x, y) FN(x + 1, y)
+#define FN_4(x, y) FN_2(x, y) FN_2(x + 2, y)
+#define FN_8(y) FN_4(0, y) FN_4(4, y)
+
+static void glue(ftlcdc200_draw_line1_, NAME)(void          *opaque,
+                                              uint8_t       *d,
+                                              const uint8_t *src,
+                                              int            width,
+                                              int            deststep)
+{
+    uint32_t *palette = opaque;
+    uint32_t data;
+    while (width > 0) {
+        data = *(uint32_t *)src;
+#ifdef SWAP_PIXELS
+#define FN(x, y) COPY_PIXEL(d, palette[(data >> (y + 7 - (x))) & 1]);
+#else
+#define FN(x, y) COPY_PIXEL(d, palette[(data >> ((x) + y)) & 1]);
+#endif
+#ifdef SWAP_WORDS
+        FN_8(24)
+        FN_8(16)
+        FN_8(8)
+        FN_8(0)
+#else
+        FN_8(0)
+        FN_8(8)
+        FN_8(16)
+        FN_8(24)
+#endif
+#undef FN
+        width -= 32;
+        src += 4;
+    }
+}
+
+static void glue(ftlcdc200_draw_line2_, NAME)(void          *opaque,
+                                              uint8_t       *d,
+                                              const uint8_t *src,
+                                              int            width,
+                                              int            deststep)
+{
+    uint32_t *palette = opaque;
+    uint32_t data;
+    while (width > 0) {
+        data = *(uint32_t *)src;
+#ifdef SWAP_PIXELS
+#define FN(x, y) COPY_PIXEL(d, palette[(data >> (y + 6 - (x) * 2)) & 3]);
+#else
+#define FN(x, y) COPY_PIXEL(d, palette[(data >> ((x) * 2 + y)) & 3]);
+#endif
+#ifdef SWAP_WORDS
+        FN_4(0, 24)
+        FN_4(0, 16)
+        FN_4(0, 8)
+        FN_4(0, 0)
+#else
+        FN_4(0, 0)
+        FN_4(0, 8)
+        FN_4(0, 16)
+        FN_4(0, 24)
+#endif
+#undef FN
+        width -= 16;
+        src += 4;
+    }
+}
+
+static void glue(ftlcdc200_draw_line4_, NAME)(void          *opaque,
+                                              uint8_t       *d,
+                                              const uint8_t *src,
+                                              int            width,
+                                              int            deststep)
+{
+    uint32_t *palette = opaque;
+    uint32_t data;
+    while (width > 0) {
+        data = *(uint32_t *)src;
+#ifdef SWAP_PIXELS
+#define FN(x, y) COPY_PIXEL(d, palette[(data >> (y + 4 - (x) * 4)) & 0xf]);
+#else
+#define FN(x, y) COPY_PIXEL(d, palette[(data >> ((x) * 4 + y)) & 0xf]);
+#endif
+#ifdef SWAP_WORDS
+        FN_2(0, 24)
+        FN_2(0, 16)
+        FN_2(0, 8)
+        FN_2(0, 0)
+#else
+        FN_2(0, 0)
+        FN_2(0, 8)
+        FN_2(0, 16)
+        FN_2(0, 24)
+#endif
+#undef FN
+        width -= 8;
+        src += 4;
+    }
+}
+
+static void glue(ftlcdc200_draw_line8_, NAME)(void          *opaque,
+                                              uint8_t       *d,
+                                              const uint8_t *src,
+                                              int            width,
+                                              int            deststep)
+{
+    uint32_t *palette = opaque;
+    uint32_t data;
+    while (width > 0) {
+        data = *(uint32_t *)src;
+#define FN(x) COPY_PIXEL(d, palette[(data >> (x)) & 0xff]);
+#ifdef SWAP_WORDS
+        FN(24)
+        FN(16)
+        FN(8)
+        FN(0)
+#else
+        FN(0)
+        FN(8)
+        FN(16)
+        FN(24)
+#endif
+#undef FN
+        width -= 4;
+        src += 4;
+    }
+}
+
+static void glue(ftlcdc200_draw_line16_, NAME)(void          *opaque,
+                                               uint8_t       *d,
+                                               const uint8_t *src,
+                                               int            width,
+                                               int            deststep)
+{
+    uint32_t data;
+    unsigned int r, g, b;
+    while (width > 0) {
+        data = *(uint32_t *)src;
+#ifdef SWAP_WORDS
+        data = bswap32(data);
+#endif
+#ifdef RGB
+#define LSB r
+#define MSB b
+#else
+#define LSB b
+#define MSB r
+#endif
+#if 0
+        LSB = data & 0x1f;
+        data >>= 5;
+        g = data & 0x3f;
+        data >>= 6;
+        MSB = data & 0x1f;
+        data >>= 5;
+#else
+        LSB = (data & 0x1f) << 3;
+        data >>= 5;
+        g = (data & 0x3f) << 2;
+        data >>= 6;
+        MSB = (data & 0x1f) << 3;
+        data >>= 5;
+#endif
+        COPY_PIXEL(d, glue(rgb_to_pixel, BITS)(r, g, b));
+        LSB = (data & 0x1f) << 3;
+        data >>= 5;
+        g = (data & 0x3f) << 2;
+        data >>= 6;
+        MSB = (data & 0x1f) << 3;
+        data >>= 5;
+        COPY_PIXEL(d, glue(rgb_to_pixel, BITS)(r, g, b));
+#undef MSB
+#undef LSB
+        width -= 2;
+        src += 4;
+    }
+}
+
+static void glue(ftlcdc200_draw_line32_, NAME)(void          *opaque,
+                                               uint8_t       *d,
+                                               const uint8_t *src,
+                                               int            width,
+                                               int            deststep)
+{
+    uint32_t data;
+    unsigned int r, g, b;
+    while (width > 0) {
+        data = *(uint32_t *)src;
+#ifdef RGB
+#define LSB r
+#define MSB b
+#else
+#define LSB b
+#define MSB r
+#endif
+#ifndef SWAP_WORDS
+        LSB = data & 0xff;
+        g = (data >> 8) & 0xff;
+        MSB = (data >> 16) & 0xff;
+#else
+        LSB = (data >> 24) & 0xff;
+        g = (data >> 16) & 0xff;
+        MSB = (data >> 8) & 0xff;
+#endif
+        COPY_PIXEL(d, glue(rgb_to_pixel, BITS)(r, g, b));
+#undef MSB
+#undef LSB
+        width--;
+        src += 4;
+    }
+}
+
+static void glue(ftlcdc200_draw_line16_555_, NAME)(void          *opaque,
+                                                   uint8_t       *d,
+                                                   const uint8_t *src,
+                                                   int            width,
+                                                   int            deststep)
+{
+    /* RGB 555 plus an intensity bit (which we ignore) */
+    uint32_t data;
+    unsigned int r, g, b;
+    while (width > 0) {
+        data = *(uint32_t *)src;
+#ifdef SWAP_WORDS
+        data = bswap32(data);
+#endif
+#ifdef RGB
+#define LSB r
+#define MSB b
+#else
+#define LSB b
+#define MSB r
+#endif
+        LSB = (data & 0x1f) << 3;
+        data >>= 5;
+        g = (data & 0x1f) << 3;
+        data >>= 5;
+        MSB = (data & 0x1f) << 3;
+        data >>= 5;
+        COPY_PIXEL(d, glue(rgb_to_pixel, BITS)(r, g, b));
+        LSB = (data & 0x1f) << 3;
+        data >>= 5;
+        g = (data & 0x1f) << 3;
+        data >>= 5;
+        MSB = (data & 0x1f) << 3;
+        data >>= 6;
+        COPY_PIXEL(d, glue(rgb_to_pixel, BITS)(r, g, b));
+#undef MSB
+#undef LSB
+        width -= 2;
+        src += 4;
+    }
+}
+
+static void glue(ftlcdc200_draw_line12_, NAME)(void          *opaque,
+                                               uint8_t       *d,
+                                               const uint8_t *src,
+                                               int            width,
+                                               int            deststep)
+{
+    /* RGB 444 with 4 bits of zeroes at the top of each halfword */
+    uint32_t data;
+    unsigned int r, g, b;
+    while (width > 0) {
+        data = *(uint32_t *)src;
+#ifdef SWAP_WORDS
+        data = bswap32(data);
+#endif
+#ifdef RGB
+#define LSB r
+#define MSB b
+#else
+#define LSB b
+#define MSB r
+#endif
+        LSB = (data & 0xf) << 4;
+        data >>= 4;
+        g = (data & 0xf) << 4;
+        data >>= 4;
+        MSB = (data & 0xf) << 4;
+        data >>= 8;
+        COPY_PIXEL(d, glue(rgb_to_pixel, BITS)(r, g, b));
+        LSB = (data & 0xf) << 4;
+        data >>= 4;
+        g = (data & 0xf) << 4;
+        data >>= 4;
+        MSB = (data & 0xf) << 4;
+        data >>= 8;
+        COPY_PIXEL(d, glue(rgb_to_pixel, BITS)(r, g, b));
+#undef MSB
+#undef LSB
+        width -= 2;
+        src += 4;
+    }
+}
+
+#undef SWAP_PIXELS
+#undef NAME
+#undef SWAP_WORDS
+#undef ORDER
+
+#endif
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH v9 20/24] hw/arm: add FTTSC010 touchscreen controller support
  2013-03-25 12:09 [Qemu-devel] [PATCH v9 00/24] hw/arm: add Faraday A369 SoC platform support Kuo-Jung Su
                   ` (18 preceding siblings ...)
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 19/24] hw/arm: add FTLCDC200 LCD controller support Kuo-Jung Su
@ 2013-03-25 12:09 ` Kuo-Jung Su
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 21/24] hw/arm: add FTSDC010 MMC/SD " Kuo-Jung Su
                   ` (3 subsequent siblings)
  23 siblings, 0 replies; 35+ messages in thread
From: Kuo-Jung Su @ 2013-03-25 12:09 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, i.mitsyanko, Blue Swirl, Paul Brook, Kuo-Jung Su,
	Andreas, fred.konrad

From: Kuo-Jung Su <dantesu@faraday-tech.com>

The FTTSC010 provides two operation modes to sample
the analog input voltage.

  1. The manual operation mode needs to program
     and control the panel drivers by software
     step-by-step for the x-y position measurement.

  2. The auto-scan mode provides a periodic sampling
     method to convert the analog input.

This patch only implements the auto-scan mode.

Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
---
 hw/arm/Makefile.objs    |    2 +-
 hw/arm/ftplat_a369soc.c |    3 +
 hw/fttsc010.c           |  264 +++++++++++++++++++++++++++++++++++++++++++++++
 hw/fttsc010.h           |   39 +++++++
 4 files changed, 307 insertions(+), 1 deletion(-)
 create mode 100644 hw/fttsc010.c
 create mode 100644 hw/fttsc010.h

diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index f6b947e..d163239 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -26,7 +26,7 @@ obj-y += imx_serial.o imx_ccm.o imx_timer.o imx_avic.o
 obj-$(CONFIG_KVM) += kvm/arm_gic.o
 obj-y += ftintc020.o ftahbc020.o ftddrii030.o ftpwmtmr010.o ftwdt010.o \
                 ftrtc011.o ftdmac020.o ftapbbrg020.o ftnandc021.o fti2c010.o \
-                ftssp010.o ftgmac100.o ftlcdc200.o
+                ftssp010.o ftgmac100.o ftlcdc200.o fttsc010.o
 
 obj-y := $(addprefix ../,$(obj-y))
 
diff --git a/hw/arm/ftplat_a369soc.c b/hw/arm/ftplat_a369soc.c
index cdc6d4a..231ea1e 100644
--- a/hw/arm/ftplat_a369soc.c
+++ b/hw/arm/ftplat_a369soc.c
@@ -238,6 +238,9 @@ static void a369soc_chip_init(FaradaySoCState *s)
                           s->pic[23], /* FIFO Under-Run */
                           s->pic[22], /* AHB Bus Error */
                           NULL);
+
+    /* fttsc010 */
+    sysbus_create_simple("fttsc010", 0x92400000, s->pic[19]);
 }
 
 static void a369soc_realize(DeviceState *dev, Error **errp)
diff --git a/hw/fttsc010.c b/hw/fttsc010.c
new file mode 100644
index 0000000..02b7ec2
--- /dev/null
+++ b/hw/fttsc010.c
@@ -0,0 +1,264 @@
+/*
+ * Faraday FTTSC010 emulator.
+ *
+ * Copyright (c) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This code is licensed under GNU GPL v2+.
+ */
+
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "hw/devices.h"
+#include "ui/console.h"
+#include "qemu/timer.h"
+#include "sysemu/sysemu.h"
+
+#include "hw/fttsc010.h"
+
+#define X_AXIS_DMAX     3470
+#define X_AXIS_MIN      290
+#define Y_AXIS_DMAX     3450
+#define Y_AXIS_MIN      200
+
+#define ADS_XPOS(x, y)  \
+    (X_AXIS_MIN + ((X_AXIS_DMAX * (x)) >> 15))
+#define ADS_YPOS(x, y)  \
+    (Y_AXIS_MIN + ((Y_AXIS_DMAX * (y)) >> 15))
+#define ADS_Z1POS(x, y) \
+    (8)
+#define ADS_Z2POS(x, y) \
+    ((1600 + ADS_XPOS(x, y)) * ADS_Z1POS(x, y) / ADS_XPOS(x, y))
+
+#define TYPE_FTTSC010   "fttsc010"
+
+#define CFG_REGSIZE     (0x3c / 4)
+
+typedef struct Fttsc010State {
+    /*< private >*/
+    SysBusDevice parent;
+
+    /*< public >*/
+    MemoryRegion iomem;
+    qemu_irq irq;
+
+    uint64_t interval;
+    QEMUTimer *qtimer;
+
+    int x, y;
+    int z1, z2;
+    uint32_t freq;
+
+    /* HW registers */
+    uint32_t regs[CFG_REGSIZE];
+} Fttsc010State;
+
+#define FTTSC010(obj) \
+    OBJECT_CHECK(Fttsc010State, obj, TYPE_FTTSC010)
+
+#define TSC_REG32(s, off) \
+    ((s)->regs[(off) / 4])
+
+static void fttsc010_update_irq(Fttsc010State *s)
+{
+    qemu_set_irq(s->irq, !!(TSC_REG32(s, REG_IMR) & TSC_REG32(s, REG_ISR)));
+}
+
+static uint64_t
+fttsc010_mem_read(void *opaque, hwaddr addr, unsigned size)
+{
+    uint32_t ret = 0;
+    Fttsc010State *s = FTTSC010(opaque);
+
+    switch (addr) {
+    case REG_CR ... REG_DCR:
+        ret = s->regs[addr / 4];
+        break;
+    case REG_XYR:
+        ret = deposit32(ret,  0, 12, s->x);
+        ret = deposit32(ret, 16, 12, s->y);
+        break;
+    case REG_ZR:
+        ret = deposit32(ret,  0, 12, s->z1);
+        ret = deposit32(ret, 16, 12, s->z2);
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "fttsc010: undefined memory access@%#" HWADDR_PRIx "\n", addr);
+        break;
+    }
+
+    return ret;
+}
+
+static void
+fttsc010_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+    uint32_t dly, sdiv, mdiv;
+    Fttsc010State *s = FTTSC010(opaque);
+
+    switch (addr) {
+    case REG_CR:
+        TSC_REG32(s, REG_CR) = (uint32_t)val;
+        if (TSC_REG32(s, REG_CR) & (CR_AS | CR_RD1)) {
+            /* ADC conversion delay with frame number */
+            dly = extract32(TSC_REG32(s, REG_DCR), 0, 16);
+            /* ADC sample clock divider */
+            sdiv = extract32(TSC_REG32(s, REG_CSR), 8, 8);
+            /* ADC main clock divider */
+            mdiv = extract32(TSC_REG32(s, REG_CSR), 0, 8);
+            /* Calculate sample rate/timer interval */
+            s->interval = s->freq / ((mdiv + 1) * (sdiv + 1) * (dly + 1) * 64);
+            s->interval = MAX(1ULL, s->interval);
+            qemu_mod_timer(s->qtimer,
+                s->interval + qemu_get_clock_ms(vm_clock));
+        } else {
+            qemu_del_timer(s->qtimer);
+        }
+        break;
+    case REG_ISR:
+        TSC_REG32(s, REG_ISR) &= ~((uint32_t)val);
+        fttsc010_update_irq(s);
+        break;
+    case REG_IMR:
+        TSC_REG32(s, REG_IMR) = (uint32_t)val;
+        fttsc010_update_irq(s);
+        break;
+    case REG_CSR ... REG_DCR:
+        s->regs[addr / 4] = (uint32_t)val;
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "fttsc010: undefined memory access@%#" HWADDR_PRIx "\n", addr);
+        break;
+    }
+}
+
+static const MemoryRegionOps mmio_ops = {
+    .read  = fttsc010_mem_read,
+    .write = fttsc010_mem_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    }
+};
+
+static void fttsc010_timer_tick(void *opaque)
+{
+    Fttsc010State *s = FTTSC010(opaque);
+
+    /* update isr for auto-scan */
+    if (TSC_REG32(s, REG_CR) & CR_AS) {
+        TSC_REG32(s, REG_ISR) |= ISR_AS;
+    }
+
+    /* turn it off, when it's under one-shot mode */
+    TSC_REG32(s, REG_CR) &= ~CR_RD1;
+
+    /* update irq signal */
+    fttsc010_update_irq(s);
+
+    /* reschedule for next auto-scan */
+    if (TSC_REG32(s, REG_CR) & CR_AS) {
+        qemu_mod_timer(s->qtimer, s->interval + qemu_get_clock_ms(vm_clock));
+    }
+}
+
+static void
+fttsc010_touchscreen_event(void *opaque, int x, int y, int z, int bt)
+{
+    Fttsc010State *s = FTTSC010(opaque);
+
+    if (bt) {
+        /* button pressed */
+        x = 0x7fff - x;
+        s->x  = ADS_XPOS(x, y);
+        s->y  = ADS_YPOS(x, y);
+        s->z1 = ADS_Z1POS(x, y);
+        s->z2 = ADS_Z2POS(x, y);
+    } else {
+        /* button released */
+        s->z1 = 0;
+        s->z2 = 0;
+    }
+}
+
+static void fttsc010_reset(DeviceState *ds)
+{
+    Fttsc010State *s = FTTSC010(SYS_BUS_DEVICE(ds));
+
+    memset(s->regs, 0, sizeof(s->regs));
+    TSC_REG32(s, REG_REVR) = 0x00010000;    /* rev. 1.0.0 */
+
+    /* initialize touch sample interval to 10 ms */
+    TSC_REG32(s, REG_CSR) = CSR_SDIV(16) | CSR_MDIV(5);
+    TSC_REG32(s, REG_DCR) = DCR_DLY(100);
+
+    s->x  = 0;
+    s->y  = 0;
+    s->z1 = 0;
+    s->z2 = 0;
+
+    qemu_set_irq(s->irq, 0);
+}
+
+static void fttsc010_realize(DeviceState *dev, Error **errp)
+{
+    Fttsc010State *s = FTTSC010(dev);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+
+    s->qtimer   = qemu_new_timer_ms(vm_clock, fttsc010_timer_tick, s);
+
+    memory_region_init_io(&s->iomem,
+                          &mmio_ops,
+                          s,
+                          TYPE_FTTSC010,
+                          0x1000);
+    sysbus_init_mmio(sbd, &s->iomem);
+    sysbus_init_irq(sbd, &s->irq);
+
+    qemu_add_mouse_event_handler(fttsc010_touchscreen_event, s, 1,
+                    "QEMU FTTSC010-driven Touchscreen");
+}
+
+static const VMStateDescription vmstate_fttsc010 = {
+    .name = TYPE_FTTSC010,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(regs, Fttsc010State, CFG_REGSIZE),
+        VMSTATE_END_OF_LIST(),
+    }
+};
+
+static Property properties_fttsc010[] = {
+    DEFINE_PROP_UINT32("freq", Fttsc010State, freq, 66000000),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void fttsc010_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->reset   = fttsc010_reset;
+    dc->realize = fttsc010_realize;
+    dc->vmsd    = &vmstate_fttsc010;
+    dc->props   = properties_fttsc010;
+    dc->no_user = 1;
+}
+
+static const TypeInfo fttsc010_info = {
+    .name          = TYPE_FTTSC010,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(Fttsc010State),
+    .class_init    = fttsc010_class_init,
+};
+
+static void fttsc010_register_types(void)
+{
+    type_register_static(&fttsc010_info);
+}
+
+type_init(fttsc010_register_types)
diff --git a/hw/fttsc010.h b/hw/fttsc010.h
new file mode 100644
index 0000000..b600645
--- /dev/null
+++ b/hw/fttsc010.h
@@ -0,0 +1,39 @@
+/*
+ * Faraday FTTSC010 touchscreen driver
+ *
+ * Copyright (C) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This file is licensed under GNU GPL v2+.
+ */
+
+#ifndef HW_ARM_FTTSC010_H
+#define HW_ARM_FTTSC010_H
+
+#include "qemu/bitops.h"
+
+#define REG_CR      0x00  /* Control Register */
+#define REG_ISR     0x04  /* Interrupt Status Register */
+#define REG_IMR     0x08  /* Interrupt Mask Register */
+#define REG_REVR    0x0C  /* Revision Register */
+#define REG_CSR     0x30  /* Clock & Sample Rate Register */
+#define REG_PFR     0x34  /* Panel Function Register */
+#define REG_DCR     0x38  /* Delay Control Register */
+#define REG_XYR     0x3C  /* Touchscreen X,Y-Axis Register */
+#define REG_ZR      0x4C  /* Touchscreen Z-Axis (Pressure) Register */
+
+#define CR_AS       BIT(31) /* Auto-scan enable */
+#define CR_RD1      BIT(30) /* Single read enabled */
+#define CR_CHDLY(x) BIT(16 + (x % 7)) /* ADC channel x delay enabled */
+#define CR_APWRDN   BIT(9)  /* ADC auto power down mode */
+#define CR_PWRDN    BIT(8)  /* ADC and IPGA power down */
+#define CR_ICS(x)   ((x) & 0xf) /* ADC analog input channel select */
+
+#define CSR_SDIV(x) (((x) & 0xff) << 8) /* ADC sample clock divider */
+#define CSR_MDIV(x) (((x) & 0xff) << 0) /* ADC main clock divider */
+
+#define DCR_DLY(x)  ((x) & 0xffff)      /* ADC conversion delay */
+
+#define ISR_AS      BIT(10) /* Auto-scan cpmplete */
+
+#endif
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH v9 21/24] hw/arm: add FTSDC010 MMC/SD controller support
  2013-03-25 12:09 [Qemu-devel] [PATCH v9 00/24] hw/arm: add Faraday A369 SoC platform support Kuo-Jung Su
                   ` (19 preceding siblings ...)
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 20/24] hw/arm: add FTTSC010 touchscreen " Kuo-Jung Su
@ 2013-03-25 12:09 ` Kuo-Jung Su
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 22/24] hw/arm: add FTMAC110 10/100Mbps ethernet support Kuo-Jung Su
                   ` (2 subsequent siblings)
  23 siblings, 0 replies; 35+ messages in thread
From: Kuo-Jung Su @ 2013-03-25 12:09 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, i.mitsyanko, Blue Swirl, Paul Brook, Kuo-Jung Su,
	Andreas, fred.konrad

From: Kuo-Jung Su <dantesu@faraday-tech.com>

The FTSDC010 is a simple MMC/SD host controller and
many of its registers are similar to Arm PrimeCell PL181.

Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
---
 hw/arm/Makefile.objs    |    2 +-
 hw/arm/ftplat_a369soc.c |    7 +
 hw/ftsdc010.c           |  359 +++++++++++++++++++++++++++++++++++++++++++++++
 hw/ftsdc010.h           |   88 ++++++++++++
 4 files changed, 455 insertions(+), 1 deletion(-)
 create mode 100644 hw/ftsdc010.c
 create mode 100644 hw/ftsdc010.h

diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index d163239..93e94db 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -26,7 +26,7 @@ obj-y += imx_serial.o imx_ccm.o imx_timer.o imx_avic.o
 obj-$(CONFIG_KVM) += kvm/arm_gic.o
 obj-y += ftintc020.o ftahbc020.o ftddrii030.o ftpwmtmr010.o ftwdt010.o \
                 ftrtc011.o ftdmac020.o ftapbbrg020.o ftnandc021.o fti2c010.o \
-                ftssp010.o ftgmac100.o ftlcdc200.o fttsc010.o
+                ftssp010.o ftgmac100.o ftlcdc200.o fttsc010.o ftsdc010.o
 
 obj-y := $(addprefix ../,$(obj-y))
 
diff --git a/hw/arm/ftplat_a369soc.c b/hw/arm/ftplat_a369soc.c
index 231ea1e..21387df 100644
--- a/hw/arm/ftplat_a369soc.c
+++ b/hw/arm/ftplat_a369soc.c
@@ -241,6 +241,13 @@ static void a369soc_chip_init(FaradaySoCState *s)
 
     /* fttsc010 */
     sysbus_create_simple("fttsc010", 0x92400000, s->pic[19]);
+
+    /* ftsdc010 */
+    ds = sysbus_create_simple("ftsdc010", 0x90600000, s->pic[39]);
+    ack = qdev_get_gpio_in(ds, 0);
+    req = qdev_get_gpio_in(s->hdma[0], 13);
+    qdev_connect_gpio_out(s->hdma[0], 13, ack);
+    qdev_connect_gpio_out(ds, 0, req);
 }
 
 static void a369soc_realize(DeviceState *dev, Error **errp)
diff --git a/hw/ftsdc010.c b/hw/ftsdc010.c
new file mode 100644
index 0000000..8b93fa6
--- /dev/null
+++ b/hw/ftsdc010.c
@@ -0,0 +1,359 @@
+/*
+ * QEMU model of the FTSDC010 MMC/SD Host Controller
+ *
+ * Copyright (C) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This file is licensed under GNU GPL v2+.
+ */
+
+#include "hw/sysbus.h"
+#include "hw/sd.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/blockdev.h"
+
+#include "qemu/bitops.h"
+#include "hw/ftsdc010.h"
+
+#define TYPE_FTSDC010   "ftsdc010"
+
+typedef struct Ftsdc010State {
+    /*< private >*/
+    SysBusDevice parent;
+
+    /*< public >*/
+    MemoryRegion iomem;
+    SDState *card;
+    qemu_irq irq;
+
+    /* DMA hardware handshake */
+    qemu_irq req;
+
+    uint32_t datacnt;
+
+    /* HW register cache */
+    uint32_t cmd;
+    uint32_t arg;
+    uint32_t rsp[4];
+    uint32_t rspcmd;
+    uint32_t dcr;
+    uint32_t dtr;
+    uint32_t dlr;
+    uint32_t status;
+    uint32_t ier;
+    uint32_t pwr;
+    uint32_t clk;
+} Ftsdc010State;
+
+#define FTSDC010(obj) \
+    OBJECT_CHECK(Ftsdc010State, obj, TYPE_FTSDC010)
+
+static void ftsdc010_update_irq(Ftsdc010State *s)
+{
+    qemu_set_irq(s->irq, !!(s->ier & s->status));
+}
+
+static void ftsdc010_handle_ack(void *opaque, int line, int level)
+{
+    Ftsdc010State *s = FTSDC010(opaque);
+
+    if (!(s->dcr & DCR_DMA)) {
+        return;
+    }
+
+    if (level) {
+        qemu_set_irq(s->req, 0);
+    } else if (s->datacnt) {
+        qemu_set_irq(s->req, 1);
+    }
+}
+
+static void ftsdc010_send_command(Ftsdc010State *s)
+{
+    SDRequest request;
+    uint8_t response[16];
+    int rlen;
+
+    request.cmd = s->cmd & CMD_IDX;
+    request.arg = s->arg;
+
+    rlen = sd_do_command(s->card, &request, response);
+    if (rlen < 0) {
+        goto error;
+    }
+    if (s->cmd & CMD_WAIT_RSP) {
+#define RWORD(n) ((response[n] << 24) | (response[n + 1] << 16) \
+                  | (response[n + 2] << 8) | response[n + 3])
+        if (rlen == 0 || (rlen == 4 && (s->cmd & CMD_LONG_RSP))) {
+            goto error;
+        }
+        if (rlen != 4 && rlen != 16) {
+            goto error;
+        }
+        if (rlen == 4) {
+            s->rsp[0] = RWORD(0);
+            s->rsp[1] = s->rsp[2] = s->rsp[3] = 0;
+        } else {
+            s->rsp[3] = RWORD(0);
+            s->rsp[2] = RWORD(4);
+            s->rsp[1] = RWORD(8);
+            s->rsp[0] = RWORD(12) & ~1;
+        }
+        s->rspcmd  = (s->cmd & CMD_IDX);
+        s->rspcmd |= (s->cmd & CMD_APP) ? RSP_CMDAPP : 0;
+        s->status |= SR_RSP;
+#undef RWORD
+    } else {
+        s->status |= SR_CMD;
+    }
+
+    if ((s->dcr & DCR_DMA) && s->datacnt) {
+        qemu_set_irq(s->req, 1);
+    }
+
+    return;
+
+error:
+    s->status |= SR_RSP_TIMEOUT;
+}
+
+static void ftsdc010_chip_reset(Ftsdc010State *s)
+{
+    s->cmd = 0;
+    s->arg = 0;
+    s->rsp[0] = 0;
+    s->rsp[1] = 0;
+    s->rsp[2] = 0;
+    s->rsp[3] = 0;
+    s->rspcmd = 0;
+    s->dcr = 0;
+    s->dtr = 0;
+    s->dlr = 0;
+    s->datacnt = 0;
+    s->status &= ~(SR_CARD_REMOVED | SR_WPROT);
+    s->status |= SR_TXRDY | SR_RXRDY;
+    s->ier = 0;
+    s->pwr = 0;
+    s->clk = 0;
+}
+
+static uint64_t ftsdc010_mem_read(void *opaque, hwaddr addr, unsigned size)
+{
+    Ftsdc010State *s = FTSDC010(opaque);
+    uint32_t i, ret = 0;
+
+    switch (addr) {
+    case REG_SR:
+        return s->status;
+    case REG_DR:
+        if (!(s->dcr & DCR_WR) && s->datacnt && sd_data_ready(s->card)) {
+            for (i = 0; i < 4 && s->datacnt; i++, s->datacnt--) {
+                ret = deposit32(ret, i * 8, 8, sd_read_data(s->card));
+            }
+            if (!s->datacnt) {
+                s->status |= SR_DAT_END;
+            }
+            s->status |= SR_DAT;
+        }
+        break;
+    case REG_DCR:
+        return s->dcr;
+    case REG_DTR:
+        return s->dtr;
+    case REG_DLR:
+        return s->dlr;
+    case REG_CMD:
+        return s->cmd;
+    case REG_ARG:
+        return s->arg;
+    case REG_RSP0:
+        return s->rsp[0];
+    case REG_RSP1:
+        return s->rsp[1];
+    case REG_RSP2:
+        return s->rsp[2];
+    case REG_RSP3:
+        return s->rsp[3];
+    case REG_RSPCMD:
+        return s->rspcmd;
+    case REG_IER:
+        return s->ier;
+    case REG_PWR:
+        return s->pwr;
+    case REG_CLK:
+        return s->clk;
+    case REG_BUS:
+        return 0x00000009;  /* support 1-bit/4-bit bus */
+    case REG_FEAR:
+        return 0x00000010;  /* fifo = 16 */
+    case REG_REVR:
+        return 0x00030300;  /* rev. 3.3.0 */
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "ftsdc010: undefined memory access@%#" HWADDR_PRIx "\n", addr);
+        break;
+    }
+
+    return ret;
+}
+
+static void
+ftsdc010_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+    int i;
+    Ftsdc010State *s = FTSDC010(opaque);
+
+    switch (addr) {
+    case REG_DR:
+        if ((s->dcr & DCR_WR) && s->datacnt) {
+            for (i = 0; i < 4 && s->datacnt; i++, s->datacnt--) {
+                sd_write_data(s->card, extract32((uint32_t)val, i * 8, 8));
+            }
+            if (!s->datacnt) {
+                s->status |= SR_DAT_END;
+            }
+            s->status |= SR_DAT;
+        }
+        break;
+    case REG_CMD:
+        s->cmd = (uint32_t)val;
+        if (s->cmd & CMD_RST) {
+            ftsdc010_chip_reset(s);
+        } else if (s->cmd & CMD_EN) {
+            s->cmd &= ~CMD_EN;
+            s->status &= ~(SR_CMD | SR_RSP | SR_RSP_ERR);
+            ftsdc010_send_command(s);
+        }
+        break;
+    case REG_ARG:
+        s->arg = (uint32_t)val;
+        break;
+    case REG_DCR:
+        s->dcr = (uint32_t)val;
+        if (s->dcr & DCR_EN) {
+            s->dcr &= ~(DCR_EN);
+            s->status &= ~(SR_DAT | SR_DAT_END | SR_DAT_ERR);
+            s->datacnt = s->dlr;
+        }
+        break;
+    case REG_DTR:
+        s->dtr = (uint32_t)val;
+        break;
+    case REG_DLR:
+        s->dlr = (uint32_t)val;
+        break;
+    case REG_SCR:
+        s->status &= ~((uint32_t)val & 0x14ff);
+        ftsdc010_update_irq(s);
+        break;
+    case REG_IER:
+        s->ier = (uint32_t)val;
+        ftsdc010_update_irq(s);
+        break;
+    case REG_PWR:
+        s->pwr = (uint32_t)val;
+        break;
+    case REG_CLK:
+        s->clk = (uint32_t)val;
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "ftsdc010: undefined memory access@%#" HWADDR_PRIx "\n", addr);
+        break;
+    }
+}
+
+static const MemoryRegionOps mmio_ops = {
+    .read  = ftsdc010_mem_read,
+    .write = ftsdc010_mem_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4
+    }
+};
+
+static void ftsdc010_reset(DeviceState *ds)
+{
+    Ftsdc010State *s = FTSDC010(SYS_BUS_DEVICE(ds));
+
+    ftsdc010_chip_reset(s);
+
+    sd_set_cb(s->card, NULL, NULL);
+
+    /* We can assume our GPIO outputs have been wired up now */
+    qemu_set_irq(s->req, 0);
+}
+
+static void ftsdc010_realize(DeviceState *dev, Error **errp)
+{
+    Ftsdc010State *s = FTSDC010(dev);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+    DriveInfo *dinfo;
+
+    memory_region_init_io(&s->iomem, &mmio_ops, s, TYPE_FTSDC010, 0x1000);
+    sysbus_init_mmio(sbd, &s->iomem);
+    sysbus_init_irq(sbd, &s->irq);
+
+    qdev_init_gpio_in(&sbd->qdev, ftsdc010_handle_ack, 1);
+    qdev_init_gpio_out(&sbd->qdev, &s->req, 1);
+
+    dinfo = drive_get_next(IF_SD);
+    s->card = sd_init(dinfo ? dinfo->bdrv : NULL, 0);
+
+    s->status = SR_CARD_REMOVED;
+    if (dinfo) {
+        if (bdrv_is_read_only(dinfo->bdrv)) {
+            s->status |= SR_WPROT;
+        }
+        if (bdrv_is_inserted(dinfo->bdrv)) {
+            s->status &= ~SR_CARD_REMOVED;
+        }
+    }
+}
+
+static const VMStateDescription vmstate_ftsdc010 = {
+    .name = TYPE_FTSDC010,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(cmd, Ftsdc010State),
+        VMSTATE_UINT32(arg, Ftsdc010State),
+        VMSTATE_UINT32_ARRAY(rsp, Ftsdc010State, 4),
+        VMSTATE_UINT32(rspcmd, Ftsdc010State),
+        VMSTATE_UINT32(dcr, Ftsdc010State),
+        VMSTATE_UINT32(dtr, Ftsdc010State),
+        VMSTATE_UINT32(dlr, Ftsdc010State),
+        VMSTATE_UINT32(datacnt, Ftsdc010State),
+        VMSTATE_UINT32(status, Ftsdc010State),
+        VMSTATE_UINT32(ier, Ftsdc010State),
+        VMSTATE_UINT32(pwr, Ftsdc010State),
+        VMSTATE_UINT32(clk, Ftsdc010State),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void ftsdc010_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->vmsd    = &vmstate_ftsdc010;
+    dc->reset   = ftsdc010_reset;
+    dc->realize = ftsdc010_realize;
+    dc->no_user = 1;
+}
+
+static const TypeInfo ftsdc010_info = {
+    .name           = TYPE_FTSDC010,
+    .parent         = TYPE_SYS_BUS_DEVICE,
+    .instance_size  = sizeof(Ftsdc010State),
+    .class_init     = ftsdc010_class_init,
+};
+
+static void ftsdc010_register_types(void)
+{
+    type_register_static(&ftsdc010_info);
+}
+
+type_init(ftsdc010_register_types)
diff --git a/hw/ftsdc010.h b/hw/ftsdc010.h
new file mode 100644
index 0000000..6157e5b
--- /dev/null
+++ b/hw/ftsdc010.h
@@ -0,0 +1,88 @@
+/*
+ * QEMU model of the FTSDC010 MMC/SD Host Controller
+ *
+ * Copyright (C) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This file is licensed under GNU GPL v2+.
+ */
+
+#ifndef HW_ARM_FTSDC010_H
+#define HW_ARM_FTSDC010_H
+
+/* sd controller register */
+#define REG_CMD                0x0000
+#define REG_ARG                0x0004
+#define REG_RSP0               0x0008    /* response */
+#define REG_RSP1               0x000C
+#define REG_RSP2               0x0010
+#define REG_RSP3               0x0014
+#define REG_RSPCMD             0x0018    /* responsed command */
+#define REG_DCR                0x001C    /* data control */
+#define REG_DTR                0x0020    /* data timeout */
+#define REG_DLR                0x0024    /* data length */
+#define REG_SR                 0x0028    /* status register */
+#define REG_SCR                0x002C    /* status clear register */
+#define REG_IER                0x0030    /* interrupt mask/enable register */
+#define REG_PWR                0x0034    /* power control */
+#define REG_CLK                0x0038    /* clock control */
+#define REG_BUS                0x003C    /* bus width */
+#define REG_DR                 0x0040    /* data register */
+#define REG_GPOR               0x0048    /* general purpose output register */
+#define REG_FEAR               0x009C    /* feature register */
+#define REG_REVR               0x00A0    /* revision register */
+
+/* bit mapping of command register */
+#define CMD_IDX                0x0000003F   /* command code */
+#define CMD_WAIT_RSP           0x00000040   /* 32-bit response */
+#define CMD_LONG_RSP           0x00000080   /* 128-bit response */
+#define CMD_APP                0x00000100   /* command is a ACMD */
+#define CMD_EN                 0x00000200   /* command enabled */
+#define CMD_RST                0x00000400   /* software reset */
+
+/* bit mapping of response command register */
+#define RSP_CMDIDX             0x0000003F
+#define RSP_CMDAPP             0x00000040
+
+/* bit mapping of data control register */
+#define DCR_BKSZ               0x0000000F
+#define DCR_WR                 0x00000010
+#define DCR_RD                 0x00000000
+#define DCR_DMA                0x00000020
+#define DCR_EN                 0x00000040
+#define DCR_THRES              0x00000080
+#define DCR_BURST1             0x00000000
+#define DCR_BURST4             0x00000100
+#define DCR_BURST8             0x00000200
+#define DCR_FIFO_RESET         0x00000400
+
+/* bit mapping of status register */
+#define SR_RSP_CRC            0x00000001
+#define SR_DAT_CRC            0x00000002
+#define SR_RSP_TIMEOUT        0x00000004
+#define SR_DAT_TIMEOUT        0x00000008
+#define SR_RSP_ERR            (SR_RSP_CRC | SR_RSP_TIMEOUT)
+#define SR_DAT_ERR            (SR_DAT_CRC | SR_DAT_TIMEOUT)
+#define SR_RSP                0x00000010
+#define SR_DAT                0x00000020
+#define SR_CMD                0x00000040
+#define SR_DAT_END            0x00000080
+#define SR_TXRDY              0x00000100
+#define SR_RXRDY              0x00000200
+#define SR_CARD_CHANGE        0x00000400
+#define SR_CARD_REMOVED       0x00000800
+#define SR_WPROT              0x00001000
+#define SR_SDIO               0x00010000
+#define SR_DAT0               0x00020000
+
+/* bit mapping of clock register */
+#define CLK_HISPD             0x00000200
+#define CLK_OFF               0x00000100
+#define CLK_SD                0x00000080
+
+/* bit mapping of bus register */
+#define BUS_CARD_DATA3        0x00000020
+#define BUS_4BITS_SUPP        0x00000008
+#define BUS_8BITS_SUPP        0x00000010
+
+#endif
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH v9 22/24] hw/arm: add FTMAC110 10/100Mbps ethernet support
  2013-03-25 12:09 [Qemu-devel] [PATCH v9 00/24] hw/arm: add Faraday A369 SoC platform support Kuo-Jung Su
                   ` (20 preceding siblings ...)
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 21/24] hw/arm: add FTSDC010 MMC/SD " Kuo-Jung Su
@ 2013-03-25 12:09 ` Kuo-Jung Su
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 23/24] hw/arm: add FTTMR010 timer support Kuo-Jung Su
  2013-03-25 12:10 ` [Qemu-devel] [PATCH v9 24/24] hw/arm: add FTSPI020 SPI flash controller support Kuo-Jung Su
  23 siblings, 0 replies; 35+ messages in thread
From: Kuo-Jung Su @ 2013-03-25 12:09 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, i.mitsyanko, Blue Swirl, Paul Brook, Kuo-Jung Su,
	Andreas, fred.konrad

From: Kuo-Jung Su <dantesu@faraday-tech.com>

The FTMAC110 is an Ethernet controller that provides AHB master capability
and is in full compliance with the IEEE 802.3 10/100 Mbps specifications.
Its DMA controller handles all data transfers between system memory
and on-chip memories.

It supports half-word data transfer for Linux. However it has a weird DMA
alignment issue:

(1) Tx DMA Buffer Address:
    1 bytes aligned: Invalid
    2 bytes aligned: O.K
    4 bytes aligned: O.K

(2) Rx DMA Buffer Address:
    1 bytes aligned: Invalid
    2 bytes aligned: O.K
    4 bytes aligned: Invalid (It means 0x0, 0x4, 0x8, 0xC are invalid)

Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
---
 hw/arm/Makefile.objs |    3 +-
 hw/arm/ftplat_a369.c |    7 +
 hw/faraday.h         |    3 +
 hw/ftmac110.c        |  665 ++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/ftmac110.h        |  166 +++++++++++++
 5 files changed, 843 insertions(+), 1 deletion(-)
 create mode 100644 hw/ftmac110.c
 create mode 100644 hw/ftmac110.h

diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 93e94db..4697a76 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -26,7 +26,8 @@ obj-y += imx_serial.o imx_ccm.o imx_timer.o imx_avic.o
 obj-$(CONFIG_KVM) += kvm/arm_gic.o
 obj-y += ftintc020.o ftahbc020.o ftddrii030.o ftpwmtmr010.o ftwdt010.o \
                 ftrtc011.o ftdmac020.o ftapbbrg020.o ftnandc021.o fti2c010.o \
-                ftssp010.o ftgmac100.o ftlcdc200.o fttsc010.o ftsdc010.o
+                ftssp010.o ftgmac100.o ftlcdc200.o fttsc010.o ftsdc010.o \
+                ftmac110.o
 
 obj-y := $(addprefix ../,$(obj-y))
 
diff --git a/hw/arm/ftplat_a369.c b/hw/arm/ftplat_a369.c
index 922fb55..1b3e3cd 100644
--- a/hw/arm/ftplat_a369.c
+++ b/hw/arm/ftplat_a369.c
@@ -110,6 +110,13 @@ static void a369_board_init(QEMUMachineInitArgs *args)
                                  s->i2s[0]);
     }
 
+    /* External AHB devices */
+
+    /* Ethernet: FTMAC110 */
+    if (nb_nics > 1) {
+        ftmac110_init(&nd_table[1], 0xC0100000, s->pic[5]);
+    }
+
     /* System start-up */
 
     if (args->kernel_filename) {
diff --git a/hw/faraday.h b/hw/faraday.h
index 068b799..ba7ea0e 100644
--- a/hw/faraday.h
+++ b/hw/faraday.h
@@ -127,4 +127,7 @@ void ftssp010_i2s_data_req(void *opaque, int tx, int rx);
 /* ftgmac100.c */
 void ftgmac100_init(NICInfo *nd, uint32_t base, qemu_irq irq);
 
+/* ftmac110.c */
+void ftmac110_init(NICInfo *nd, uint32_t base, qemu_irq irq);
+
 #endif
diff --git a/hw/ftmac110.c b/hw/ftmac110.c
new file mode 100644
index 0000000..de1164d
--- /dev/null
+++ b/hw/ftmac110.c
@@ -0,0 +1,665 @@
+/*
+ * QEMU model of the FTMAC110 Controller
+ *
+ * Copyright (C) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This file is licensed under GNU GPL v2+.
+ */
+
+/*******************************************************************/
+/*               FTMAC110 DMA design issue                         */
+/*                                             Dante Su 2010.02.03 */
+/*                                                                 */
+/* The DMA engine has a weird restriction that its Rx DMA engine   */
+/* accepts only 16-bits aligned address, 32-bits aligned is still  */
+/* invalid. 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                                    */
+/*******************************************************************/
+
+#include "hw/sysbus.h"
+#include "qemu/timer.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/dma.h"
+#include "net/net.h"
+
+#include "hw/faraday.h"
+#include "hw/ftmac110.h"
+
+#ifndef DEBUG
+#define DEBUG   0
+#endif
+
+#define DPRINTF(fmt, ...) \
+    do { \
+        if (DEBUG) { \
+            fprintf(stderr, fmt , ## __VA_ARGS__); \
+        } \
+    } while (0)
+
+#define TYPE_FTMAC110   "ftmac110"
+
+#define CFG_MAXFRMLEN   1536    /* Max. frame length */
+#define CFG_REGSIZE     (0x100 / 4)
+
+typedef struct Ftmac110State {
+    /*< private >*/
+    SysBusDevice parent;
+
+    /*< public >*/
+    MemoryRegion mmio;
+
+    QEMUBH *bh;
+    qemu_irq irq;
+    NICState *nic;
+    NICConf conf;
+    DMAContext *dma;
+    QEMUTimer *qtimer;
+
+    bool phycr_rd;
+
+    struct {
+        uint8_t  buf[CFG_MAXFRMLEN];
+        uint32_t len;
+    } txbuff;
+
+    uint32_t tx_idx;
+    uint32_t rx_idx;
+
+    /* HW register cache */
+    uint32_t regs[CFG_REGSIZE];
+} Ftmac110State;
+
+#define FTMAC110(obj) \
+    OBJECT_CHECK(Ftmac110State, obj, TYPE_FTMAC110)
+
+#define MAC_REG32(s, off) \
+    ((s)->regs[(off) / 4])
+
+static int ftmac110_mcast_hash(int len, const uint8_t *p)
+{
+#define CRCPOLY_LE 0xedb88320
+    int i;
+    uint32_t crc = 0xFFFFFFFF;
+
+    while (len--) {
+        crc ^= *p++;
+        for (i = 0; i < 8; i++) {
+            crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0);
+        }
+    }
+
+    /* Reverse CRC32 and return MSB 6 bits only */
+    return bitrev8(crc >> 24) >> 2;
+}
+
+static void
+ftmac110_read_rxdesc(Ftmac110State *s, hwaddr addr, Ftmac110RXD *desc)
+{
+    int i;
+    uint32_t *p = (uint32_t *)desc;
+
+    if (addr & 0x0f) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                 "ftmac110: Rx desc is not 16-byte aligned!\n"
+                 "It's fine in QEMU but the real HW would panic.\n");
+    }
+
+    dma_memory_read(s->dma, addr, desc, sizeof(*desc));
+
+    for (i = 0; i < sizeof(*desc); i += 4) {
+        *p = le32_to_cpu(*p);
+    }
+
+    if ((desc->buf & 0x1) || !(desc->buf % 4)) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                 "ftmac110: rx buffer is not exactly 16-bit aligned.\n");
+    }
+}
+
+static void
+ftmac110_write_rxdesc(Ftmac110State *s, hwaddr addr, Ftmac110RXD *desc)
+{
+    int i;
+    uint32_t *p = (uint32_t *)desc;
+
+    for (i = 0; i < sizeof(*desc); i += 4) {
+        *p = cpu_to_le32(*p);
+    }
+
+    dma_memory_write(s->dma, addr, desc, sizeof(*desc));
+}
+
+static void
+ftmac110_read_txdesc(Ftmac110State *s, hwaddr addr, Ftmac110TXD *desc)
+{
+    int i;
+    uint32_t *p = (uint32_t *)desc;
+
+    if (addr & 0x0f) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                 "ftmac110: Tx desc is not 16-byte aligned!\n"
+                 "It's fine in QEMU but the real HW would panic.\n");
+    }
+
+    dma_memory_read(s->dma, addr, desc, sizeof(*desc));
+
+    for (i = 0; i < sizeof(*desc); i += 4) {
+        *p = le32_to_cpu(*p);
+    }
+
+    if (desc->buf & 0x1) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                 "ftmac110: tx buffer is not 16-bit aligned.\n");
+    }
+}
+
+static void
+ftmac110_write_txdesc(Ftmac110State *s, hwaddr addr, Ftmac110TXD *desc)
+{
+    int i;
+    uint32_t *p = (uint32_t *)desc;
+
+    for (i = 0; i < sizeof(*desc); i += 4) {
+        *p = cpu_to_le32(*p);
+    }
+
+    dma_memory_write(s->dma, addr, desc, sizeof(*desc));
+}
+
+static void ftmac110_update_irq(Ftmac110State *s)
+{
+    qemu_set_irq(s->irq, !!(MAC_REG32(s, REG_ISR) & MAC_REG32(s, REG_IMR)));
+}
+
+static int ftmac110_can_receive(NetClientState *nc)
+{
+    int ret = 0;
+    uint32_t val;
+    Ftmac110State *s = qemu_get_nic_opaque(nc);
+    Ftmac110RXD rxd;
+    hwaddr off = MAC_REG32(s, REG_RXBAR) + s->rx_idx * sizeof(rxd);
+
+    val = MAC_REG32(s, REG_MACCR);
+    if ((val & MACCR_RCV_EN) && (val & MACCR_RDMA_EN)) {
+        ftmac110_read_rxdesc(s, off, &rxd);
+        ret = rxd.owner;
+        if (!ret) {
+            qemu_mod_timer(s->qtimer, qemu_get_clock_ms(vm_clock) + 10);
+        }
+    }
+
+    return ret;
+}
+
+static ssize_t ftmac110_receive(NetClientState *nc,
+                                const uint8_t  *buf,
+                                size_t          size)
+{
+    const uint8_t *ptr = buf;
+    hwaddr off;
+    size_t len;
+    Ftmac110RXD rxd;
+    Ftmac110State *s = qemu_get_nic_opaque(nc);
+    int bcst, mcst, ftl, proto;
+
+    MAC_REG32(s, REG_RXPKT) += 1;
+
+    /*
+     * Check if it's a long frame. (CRC32 is excluded)
+     */
+    proto = (buf[12] << 8) | buf[13];
+    if (proto == 0x8100) {  /* 802.1Q VLAN */
+        ftl = (size > 1518) ? 1 : 0;
+    } else {
+        ftl = (size > 1514) ? 1 : 0;
+    }
+    if (ftl) {
+        MAC_REG32(s, REG_RXCRCFTL) = (MAC_REG32(s, REG_RXCRCFTL) + 1) & 0xffff;
+        DPRINTF("ftmac110_receive: frame too long, drop it\n");
+        return -1;
+    }
+
+    /* if it's a broadcast */
+    if ((buf[0] == 0xff) && (buf[1] == 0xff) && (buf[2] == 0xff)
+            && (buf[3] == 0xff) && (buf[4] == 0xff) && (buf[5] == 0xff)) {
+        bcst = 1;
+        MAC_REG32(s, REG_RXBCST) += 1;
+        if (!(MAC_REG32(s, REG_MACCR) & MACCR_RCV_ALL)
+            && !(MAC_REG32(s, REG_MACCR) & MACCR_RX_BROADPKT)) {
+            DPRINTF("ftmac110_receive: bcst filtered\n");
+            return -1;
+        }
+    } else {
+        bcst = 0;
+    }
+
+    /* if it's a multicast */
+    if ((buf[0] == 0x01) && (buf[1] == 0x00) && (buf[2] == 0x5e)
+        && (buf[3] <= 0x7f)) {
+        mcst = 1;
+        MAC_REG32(s, REG_RXMCST) += 1;
+        if (!(MAC_REG32(s, REG_MACCR) & MACCR_RCV_ALL)
+            && !(MAC_REG32(s, REG_MACCR) & MACCR_RX_MULTIPKT)) {
+            int hash, id;
+            if (!(MAC_REG32(s, REG_MACCR) & MACCR_HT_MULTI_EN)) {
+                DPRINTF("ftmac110_receive: mcst filtered\n");
+                return -1;
+            }
+            hash = ftmac110_mcast_hash(6, buf);
+            id = (hash >= 32) ? 1 : 0;
+            if (!(MAC_REG32(s, REG_MHASH0 + id * 4) & BIT(hash % 32))) {
+                DPRINTF("ftmac110_receive: mcst filtered\n");
+                return -1;
+            }
+        }
+    } else {
+        mcst = 0;
+    }
+
+    /* check if the destination matches NIC mac address */
+    if (!(MAC_REG32(s, REG_MACCR) & MACCR_RCV_ALL) && !bcst && !mcst) {
+        if (memcmp(s->conf.macaddr.a, buf, 6)) {
+            return -1;
+        }
+    }
+
+    while (size > 0) {
+        off = MAC_REG32(s, REG_RXBAR) + s->rx_idx * sizeof(rxd);
+        ftmac110_read_rxdesc(s, off, &rxd);
+        if (!rxd.owner) {
+            MAC_REG32(s, REG_ISR) |= ISR_NORXBUF;
+            DPRINTF("ftmac110: out of rxd!?\n");
+            return -1;
+        }
+
+        if (ptr == buf) {
+            rxd.frs = 1;
+        } else {
+            rxd.frs = 0;
+        }
+
+        len = size > rxd.bufsz ? rxd.bufsz : size;
+        dma_memory_write(s->dma, rxd.buf, (uint8_t *)ptr, len);
+        ptr  += len;
+        size -= len;
+
+        if (size <= 0) {
+            rxd.lrs = 1;
+        } else {
+            rxd.lrs = 0;
+        }
+
+        rxd.len = len;
+        rxd.bcast = bcst;
+        rxd.mcast = mcst;
+        rxd.owner = 0;
+
+        /* write-back the rx descriptor */
+        ftmac110_write_rxdesc(s, off, &rxd);
+
+        if (rxd.end) {
+            s->rx_idx = 0;
+        } else {
+            s->rx_idx += 1;
+        }
+    }
+
+    /* update interrupt signal */
+    MAC_REG32(s, REG_ISR) |= ISR_RPKT_OK | ISR_RPKT_FINISH;
+    ftmac110_update_irq(s);
+
+    return (ssize_t)((uint32_t)ptr - (uint32_t)buf);
+}
+
+static void ftmac110_transmit(Ftmac110State *s, uint32_t bar, uint32_t *idx)
+{
+    hwaddr off;
+    uint8_t *buf;
+    int ftl, proto;
+    Ftmac110TXD txd;
+
+    if ((MAC_REG32(s, REG_MACCR) & (MACCR_XMT_EN | MACCR_XDMA_EN))
+            != (MACCR_XMT_EN | MACCR_XDMA_EN)) {
+        return;
+    }
+
+    do {
+        off = bar + (*idx) * sizeof(txd);
+        ftmac110_read_txdesc(s, off, &txd);
+        if (!txd.owner) {
+            MAC_REG32(s, REG_ISR) |= ISR_NOTXBUF;
+            break;
+        }
+        if (txd.fts) {
+            s->txbuff.len = 0;
+        }
+        if (txd.len + s->txbuff.len > sizeof(s->txbuff.buf)) {
+            fprintf(stderr, "ftmac110: tx buffer overflow!\n");
+            abort();
+        }
+        buf = s->txbuff.buf + s->txbuff.len;
+        dma_memory_read(s->dma, txd.buf, (uint8_t *)buf, txd.len);
+        s->txbuff.len += txd.len;
+        /* Check if it's a long frame. (CRC32 is excluded) */
+        proto = (s->txbuff.buf[12] << 8) | s->txbuff.buf[13];
+        if (proto == 0x8100) {
+            ftl = (s->txbuff.len > 1518) ? 1 : 0;
+        } else {
+            ftl = (s->txbuff.len > 1514) ? 1 : 0;
+        }
+        if (ftl) {
+            fprintf(stderr, "ftmac110_transmit: frame too long\n");
+            abort();
+        }
+        if (txd.lts) {
+            MAC_REG32(s, REG_TXPKT) += 1;
+            if (MAC_REG32(s, REG_MACCR) & MACCR_LOOP_EN) {
+                ftmac110_receive(qemu_get_queue(s->nic),
+                                 s->txbuff.buf,
+                                 s->txbuff.len);
+            } else {
+                qemu_send_packet(qemu_get_queue(s->nic),
+                                 s->txbuff.buf,
+                                 s->txbuff.len);
+            }
+        }
+        if (txd.end) {
+            *idx = 0;
+        } else {
+            *idx += 1;
+        }
+        if (txd.tx2fic) {
+            MAC_REG32(s, REG_ISR) |= ISR_XPKT_OK;
+        }
+        if (txd.txic) {
+            MAC_REG32(s, REG_ISR) |= ISR_XPKT_FINISH;
+        }
+        txd.owner = 0;
+        ftmac110_write_txdesc(s, off, &txd);
+    } while (1);
+}
+
+static void ftmac110_bh(void *opaque)
+{
+    Ftmac110State *s = FTMAC110(opaque);
+
+    if (MAC_REG32(s, REG_TXBAR)) {
+        ftmac110_transmit(s, MAC_REG32(s, REG_TXBAR), &s->tx_idx);
+    }
+
+    ftmac110_update_irq(s);
+}
+
+static void ftmac110_chip_reset(Ftmac110State *s)
+{
+    s->phycr_rd = false;
+    s->txbuff.len = 0;
+    s->tx_idx = 0;
+    s->rx_idx = 0;
+    memset(s->regs, 0, sizeof(s->regs));
+
+    MAC_REG32(s, REG_APTC) = 0x00000001; /* timing control */
+    MAC_REG32(s, REG_DBLAC) = 0x00022f72; /* dma burst, arbitration */
+    MAC_REG32(s, REG_REVR) = 0x00000700; /* rev. = 0.7.0 */
+    MAC_REG32(s, REG_FEAR) = 0x00000006; /* support full/half duplex */
+
+    if (s->bh) {
+        qemu_bh_cancel(s->bh);
+    }
+
+    if (s->qtimer) {
+        qemu_del_timer(s->qtimer);
+    }
+
+    ftmac110_update_irq(s);
+}
+
+static uint64_t
+ftmac110_mem_read(void *opaque, hwaddr addr, unsigned size)
+{
+    Ftmac110State *s = FTMAC110(opaque);
+    uint32_t dev, reg, ret = 0;
+
+    if (addr > REG_RXCRCFTL) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "ftmac110: undefined memory access@%#" HWADDR_PRIx "\n", addr);
+        return ret;
+    }
+
+    switch (addr) {
+    case REG_ISR:
+        ret = MAC_REG32(s, REG_ISR);
+        MAC_REG32(s, REG_ISR) = 0;
+        ftmac110_update_irq(s);
+        break;
+    case REG_IMR:
+        return MAC_REG32(s, REG_IMR);
+    case REG_HMAC:
+        return s->conf.macaddr.a[1]
+               | (s->conf.macaddr.a[0] << 8);
+    case REG_LMAC:
+        return s->conf.macaddr.a[5]
+               | (s->conf.macaddr.a[4] << 8)
+               | (s->conf.macaddr.a[3] << 16)
+               | (s->conf.macaddr.a[2] << 24);
+    case REG_MACSR:
+        ret = MAC_REG32(s, REG_MACSR);
+        MAC_REG32(s, REG_MACSR) = 0;
+        break;
+    case REG_PHYCR:
+        dev = extract32(MAC_REG32(s, REG_PHYCR), 16, 5);
+        reg = extract32(MAC_REG32(s, REG_PHYCR), 21, 5);
+        if (!dev && s->phycr_rd) {
+            /* Emulating a Davicom PHY with 100Mbps link state */
+            switch (reg) {
+            case 0:    /* PHY control register */
+                return MAC_REG32(s, REG_PHYCR) | 0x3100;
+            case 1:    /* PHY status register */
+                return MAC_REG32(s, REG_PHYCR) | 0x786d;
+            case 2:    /* PHY ID 1 register */
+                return MAC_REG32(s, REG_PHYCR) | 0x0181;
+            case 3:    /* PHY ID 2 register */
+                return MAC_REG32(s, REG_PHYCR) | 0xb8a0;
+            case 4:    /* Autonegotiation advertisement register */
+                return MAC_REG32(s, REG_PHYCR) | 0x01e1;
+            case 5:    /* Autonegotiation partner abilities register */
+                return MAC_REG32(s, REG_PHYCR) | 0x45e1;
+            }
+        }
+        break;
+    default:
+        ret = s->regs[addr / 4];
+        break;
+    }
+
+    return ret;
+}
+
+static void
+ftmac110_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+    Ftmac110State *s = FTMAC110(opaque);
+
+    if (addr > REG_BPR) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "ftmac110: undefined memory access@%#" HWADDR_PRIx "\n", addr);
+        return;
+    }
+
+    switch (addr) {
+    case REG_IMR:
+        MAC_REG32(s, REG_IMR) = (uint32_t)val;
+        ftmac110_update_irq(s);
+        break;
+    case REG_HMAC:
+        s->conf.macaddr.a[1] = extract32((uint32_t)val, 0, 8);
+        s->conf.macaddr.a[0] = extract32((uint32_t)val, 8, 8);
+        break;
+    case REG_LMAC:
+        s->conf.macaddr.a[5] = extract32((uint32_t)val, 0, 8);
+        s->conf.macaddr.a[4] = extract32((uint32_t)val, 8, 8);
+        s->conf.macaddr.a[3] = extract32((uint32_t)val, 16, 8);
+        s->conf.macaddr.a[2] = extract32((uint32_t)val, 24, 8);
+        break;
+    case REG_MACCR:
+        MAC_REG32(s, REG_MACCR) = (uint32_t)val;
+        if (val & MACCR_SW_RST) {
+            ftmac110_chip_reset(s);
+            MAC_REG32(s, REG_MACCR) &= ~MACCR_SW_RST;
+        }
+        if ((val & MACCR_RCV_EN) && (val & MACCR_RDMA_EN)) {
+            if (ftmac110_can_receive(qemu_get_queue(s->nic))) {
+                qemu_flush_queued_packets(qemu_get_queue(s->nic));
+            }
+        } else {
+            qemu_del_timer(s->qtimer);
+        }
+        break;
+    case REG_PHYCR:
+        s->phycr_rd = (val & PHYCR_MDIORD) ? true : false;
+        MAC_REG32(s, REG_PHYCR) = (uint32_t)val
+                                & ~(PHYCR_MDIOWR | PHYCR_MDIORD);
+        break;
+    case REG_TXPD:
+        qemu_bh_schedule(s->bh);
+        break;
+    case REG_RXPD:
+    case REG_REVR:
+    case REG_FEAR:
+        break;
+    default:
+        s->regs[addr / 4] = (uint32_t)val;
+        break;
+    }
+}
+
+static const MemoryRegionOps mmio_ops = {
+    .read  = ftmac110_mem_read,
+    .write = ftmac110_mem_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4
+    }
+};
+
+static void ftmac110_cleanup(NetClientState *nc)
+{
+    Ftmac110State *s = qemu_get_nic_opaque(nc);
+
+    s->nic = NULL;
+}
+
+static NetClientInfo net_ftmac110_info = {
+    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .size = sizeof(NICState),
+    .can_receive = ftmac110_can_receive,
+    .receive = ftmac110_receive,
+    .cleanup = ftmac110_cleanup,
+};
+
+static void ftmac110_reset(DeviceState *ds)
+{
+    Ftmac110State *s = FTMAC110(SYS_BUS_DEVICE(ds));
+
+    ftmac110_chip_reset(s);
+}
+
+static void ftmac110_watchdog(void *opaque)
+{
+    Ftmac110State *s = FTMAC110(opaque);
+
+    if (ftmac110_can_receive(qemu_get_queue(s->nic))) {
+        qemu_flush_queued_packets(qemu_get_queue(s->nic));
+    }
+}
+
+static void ftmac110_realize(DeviceState *dev, Error **errp)
+{
+    Ftmac110State *s = FTMAC110(dev);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+
+    memory_region_init_io(&s->mmio, &mmio_ops, s, TYPE_FTMAC110, 0x1000);
+    sysbus_init_mmio(sbd, &s->mmio);
+    sysbus_init_irq(sbd, &s->irq);
+
+    qemu_macaddr_default_if_unset(&s->conf.macaddr);
+    s->nic = qemu_new_nic(&net_ftmac110_info,
+                          &s->conf,
+                          object_get_typename(OBJECT(dev)),
+                          sbd->qdev.id,
+                          s);
+    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+
+    s->qtimer = qemu_new_timer_ms(vm_clock, ftmac110_watchdog, s);
+    s->dma = &dma_context_memory;
+    s->bh = qemu_bh_new(ftmac110_bh, s);
+
+    ftmac110_chip_reset(s);
+}
+
+static const VMStateDescription vmstate_ftmac110 = {
+    .name = TYPE_FTMAC110,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(regs, Ftmac110State, CFG_REGSIZE),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static Property ftmac110_properties[] = {
+    DEFINE_NIC_PROPERTIES(Ftmac110State, conf),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void ftmac110_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->vmsd  = &vmstate_ftmac110;
+    dc->props = ftmac110_properties;
+    dc->reset = ftmac110_reset;
+    dc->realize = ftmac110_realize;
+    dc->no_user = 1;
+}
+
+static const TypeInfo ftmac110_info = {
+    .name           = TYPE_FTMAC110,
+    .parent         = TYPE_SYS_BUS_DEVICE,
+    .instance_size  = sizeof(Ftmac110State),
+    .class_init     = ftmac110_class_init,
+};
+
+static void ftmac110_register_types(void)
+{
+    type_register_static(&ftmac110_info);
+}
+
+/* Legacy helper function.  Should go away when machine config files are
+   implemented.  */
+void ftmac110_init(NICInfo *nd, uint32_t base, qemu_irq irq)
+{
+    DeviceState *dev;
+    SysBusDevice *s;
+
+    qemu_check_nic_model(nd, TYPE_FTMAC110);
+    dev = qdev_create(NULL, TYPE_FTMAC110);
+    qdev_set_nic_properties(dev, nd);
+    qdev_init_nofail(dev);
+    s = SYS_BUS_DEVICE(dev);
+    sysbus_mmio_map(s, 0, base);
+    sysbus_connect_irq(s, 0, irq);
+}
+
+type_init(ftmac110_register_types)
diff --git a/hw/ftmac110.h b/hw/ftmac110.h
new file mode 100644
index 0000000..710d32d
--- /dev/null
+++ b/hw/ftmac110.h
@@ -0,0 +1,166 @@
+/*
+ * QEMU model of the FTMAC110 Controller
+ *
+ * Copyright (C) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This file is licensed under GNU GPL v2+.
+ */
+
+#ifndef HW_ARM_FTMAC110_H
+#define HW_ARM_FTMAC110_H
+
+#define REG_ISR             0x00    /* interrupt status */
+#define REG_IMR             0x04    /* interrupt mask */
+#define REG_HMAC            0x08    /* mac address */
+#define REG_LMAC            0x0c
+#define REG_MHASH0          0x10    /* multicast hash table */
+#define REG_MHASH1          0x14
+#define REG_TXPD            0x18    /* kick tx dma */
+#define REG_RXPD            0x1c    /* kick rx dma */
+#define REG_TXBAR           0x20    /* tx list/ring base address */
+#define REG_RXBAR           0x24    /* rx list/ring base address */
+#define REG_ITC             0x28    /* interrupt timer control */
+#define REG_APTC            0x2C    /* auto polling timer control */
+#define REG_DBLAC           0x30    /* dma burst length, arbitration control */
+#define REG_REVR            0x34    /* revision */
+#define REG_FEAR            0x38    /* feature */
+
+#define REG_MACCR           0x88    /* mac control register */
+#define REG_MACSR           0x8C    /* mac status register */
+#define REG_PHYCR           0x90    /* phy control register */
+#define REG_PHYDR           0x94    /* phy data register */
+#define REG_FCR             0x98    /* flow control register */
+#define REG_BPR             0x9c    /* back pressure register */
+
+#define REG_TXPKT           0xf8    /* tx packet counter */
+#define REG_RXPKT           0xf4    /* rx packet counter */
+#define REG_RXBCST          0xec    /* rx broadcast packet counter */
+#define REG_RXMCST          0xf0    /* rx multicast packet counter */
+#define REG_RXRUNT          0xe0    /* rx err: runt */
+#define REG_RXCRCFTL        0xc4    /* rx err: BIT[31-16]#crc; BIT[15-0]#ftl */
+
+/* interrupt status register */
+#define ISR_PHYSTS_CHG      (1UL<<9)/* phy status change */
+#define ISR_AHB_ERR         (1UL<<8)/* AHB error */
+#define ISR_RPKT_LOST       (1UL<<7)/* rx packet lost */
+#define ISR_RPKT_OK         (1UL<<6)/* rx packet ok (fifo) */
+#define ISR_XPKT_LOST       (1UL<<5)/* tx packet lost */
+#define ISR_XPKT_OK         (1UL<<4)/* tx packet ok (fifo) */
+#define ISR_NOTXBUF         (1UL<<3)/* out of tx buffer */
+#define ISR_XPKT_FINISH     (1UL<<2)/* tx packet finished (phy) */
+#define ISR_NORXBUF         (1UL<<1)/* out of rx buffer */
+#define ISR_RPKT_FINISH     (1UL<<0)/* rx packet finished (ram) */
+
+/* MAC control register */
+#define MACCR_100M              (1UL<<18)/* 100Mbps */
+#define MACCR_RX_BROADPKT       (1UL<<17)/* recv all broadcast packets */
+#define MACCR_RX_MULTIPKT       (1UL<<16)/* recv all multicast packets */
+#define MACCR_FULLDUP           (1UL<<15)/* full duplex */
+#define MACCR_CRC_APD           (1UL<<14)/* crc append */
+#define MACCR_RCV_ALL           (1UL<<12)/* recv all packets */
+#define MACCR_RX_FTL            (1UL<<11)/* recv ftl packets */
+#define MACCR_RX_RUNT           (1UL<<10)/* recv runt packets */
+#define MACCR_HT_MULTI_EN       (1UL<<9) /* recv multicast by hash */
+#define MACCR_RCV_EN            (1UL<<8) /* rx enabled */
+#define MACCR_ENRX_IN_HALFTX    (1UL<<6) /* rx while tx in half duplex */
+#define MACCR_XMT_EN            (1UL<<5) /* tx enabled */
+#define MACCR_CRC_DIS           (1UL<<4) /* discard crc erro */
+#define MACCR_LOOP_EN           (1UL<<3) /* loopback */
+#define MACCR_SW_RST            (1UL<<2) /* software reset */
+#define MACCR_RDMA_EN           (1UL<<1) /* rx dma enabled */
+#define MACCR_XDMA_EN           (1UL<<0) /* tx dma enabled */
+
+/*
+ * MDIO
+ */
+#define PHYCR_MDIOWR            (1 << 27)/* mdio write */
+#define PHYCR_MDIORD            (1 << 26)/* mdio read */
+
+/*
+ * Tx/Rx descriptors
+ */
+typedef struct Ftmac110RXD {
+    /* RXDES0 */
+#ifdef HOST_WORDS_BIGENDIAN
+    uint32_t owner:1;   /* BIT31: owner - 1:HW, 0: SW */
+    uint32_t rsvd3:1;
+    uint32_t frs:1;     /* first rx segment */
+    uint32_t lrs:1;     /* last rx segment */
+    uint32_t rsvd2:5;
+    uint32_t error:5;   /* rx error */
+    uint32_t bcast:1;   /* broadcast */
+    uint32_t mcast:1;   /* multicast */
+    uint32_t rsvd1:5;
+    uint32_t len:11;    /* received packet length */
+#else
+    uint32_t len:11;
+    uint32_t rsvd1:5;
+    uint32_t mcast:1;
+    uint32_t bcast:1;
+    uint32_t error:5;
+    uint32_t rsvd2:5;
+    uint32_t lrs:1;
+    uint32_t frs:1;
+    uint32_t rsvd3:1;
+    uint32_t owner:1;
+#endif  /* #ifdef HOST_WORDS_BIGENDIAN */
+
+    /* RXDES1 */
+#ifdef HOST_WORDS_BIGENDIAN
+    uint32_t end:1;     /* end of descriptor list/ring */
+    uint32_t rsvd4:20;
+    uint32_t bufsz:11;  /* max. packet length */
+#else
+    uint32_t bufsz:11;
+    uint32_t rsvd4:20;
+    uint32_t end:1;
+#endif  /* #ifdef HOST_WORDS_BIGENDIAN */
+
+    /* RXDES2 */
+    uint32_t buf;
+
+    /* RXDES3 */
+    void     *skb;
+} __attribute__ ((aligned (16))) Ftmac110RXD;
+
+typedef struct Ftmac110TXD {
+    /* TXDES0 */
+#ifdef HOST_WORDS_BIGENDIAN
+    uint32_t owner:1;   /* BIT31: owner - 1:HW, 0: SW */
+    uint32_t rsvd1:29;
+    uint32_t error:2;
+#else
+    uint32_t error:2;
+    uint32_t rsvd1:29;
+    uint32_t owner:1;
+#endif  /* #ifdef HOST_WORDS_BIGENDIAN */
+
+    /* TXDES1 */
+#ifdef HOST_WORDS_BIGENDIAN
+    uint32_t end:1;     /* end of descriptor list/ring */
+    uint32_t txic:1;    /* interrupt when data has been copied to phy */
+    uint32_t tx2fic:1;  /* interrupt when data has been copied to fifo */
+    uint32_t fts:1;     /* first tx segment */
+    uint32_t lts:1;     /* last tx segment */
+    uint32_t rsvd2:16;
+    uint32_t len:11;    /* packet length */
+#else
+    uint32_t len:11;
+    uint32_t rsvd2:16;
+    uint32_t lts:1;
+    uint32_t fts:1;
+    uint32_t tx2fic:1;
+    uint32_t txic:1;
+    uint32_t end:1;
+#endif  /* #ifdef HOST_WORDS_BIGENDIAN */
+
+    /* TXDES2 */
+    uint32_t buf;
+
+    /* TXDES3 */
+    void     *skb;
+
+} __attribute__ ((aligned (16))) Ftmac110TXD;
+
+#endif  /* HW_ARM_FTMAC110_H */
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH v9 23/24] hw/arm: add FTTMR010 timer support
  2013-03-25 12:09 [Qemu-devel] [PATCH v9 00/24] hw/arm: add Faraday A369 SoC platform support Kuo-Jung Su
                   ` (21 preceding siblings ...)
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 22/24] hw/arm: add FTMAC110 10/100Mbps ethernet support Kuo-Jung Su
@ 2013-03-25 12:09 ` Kuo-Jung Su
  2013-03-25 12:10 ` [Qemu-devel] [PATCH v9 24/24] hw/arm: add FTSPI020 SPI flash controller support Kuo-Jung Su
  23 siblings, 0 replies; 35+ messages in thread
From: Kuo-Jung Su @ 2013-03-25 12:09 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, i.mitsyanko, Blue Swirl, Paul Brook, Kuo-Jung Su,
	Andreas, fred.konrad

From: Kuo-Jung Su <dantesu@faraday-tech.com>

The FTTMR010 provides three independent sets of sub-timers.
Two match registers are provided for each sub-timer, whenever
the value of the match registers equals any one value of the
sub-timers, the timer interrupt will be immediately triggered.
And it would also issue an interrupt when an overflow occurs.

Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
---
 hw/arm/Makefile.objs |    2 +-
 hw/arm/ftplat_a369.c |    8 +
 hw/fttmr010.c        |  449 ++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/fttmr010.h        |   39 +++++
 4 files changed, 497 insertions(+), 1 deletion(-)
 create mode 100644 hw/fttmr010.c
 create mode 100644 hw/fttmr010.h

diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 4697a76..bcfb70a 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -27,7 +27,7 @@ obj-$(CONFIG_KVM) += kvm/arm_gic.o
 obj-y += ftintc020.o ftahbc020.o ftddrii030.o ftpwmtmr010.o ftwdt010.o \
                 ftrtc011.o ftdmac020.o ftapbbrg020.o ftnandc021.o fti2c010.o \
                 ftssp010.o ftgmac100.o ftlcdc200.o fttsc010.o ftsdc010.o \
-                ftmac110.o
+                ftmac110.o fttmr010.o
 
 obj-y := $(addprefix ../,$(obj-y))
 
diff --git a/hw/arm/ftplat_a369.c b/hw/arm/ftplat_a369.c
index 1b3e3cd..f22e2ca 100644
--- a/hw/arm/ftplat_a369.c
+++ b/hw/arm/ftplat_a369.c
@@ -117,6 +117,14 @@ static void a369_board_init(QEMUMachineInitArgs *args)
         ftmac110_init(&nd_table[1], 0xC0100000, s->pic[5]);
     }
 
+    /* Timer: FTTMR010 */
+    ds = qdev_create(NULL, "fttmr010");
+    qdev_prop_set_uint32(ds, "freq", 33 * 1000000);
+    qdev_init_nofail(ds);
+    sysbus_mmio_map(SYS_BUS_DEVICE(ds), 0, 0xC0200000);
+    sysbus_connect_irq(SYS_BUS_DEVICE(ds), 1, s->pic[6]);
+    sysbus_connect_irq(SYS_BUS_DEVICE(ds), 2, s->pic[7]);
+
     /* System start-up */
 
     if (args->kernel_filename) {
diff --git a/hw/fttmr010.c b/hw/fttmr010.c
new file mode 100644
index 0000000..ccb0c6a
--- /dev/null
+++ b/hw/fttmr010.c
@@ -0,0 +1,449 @@
+/*
+ * Faraday FTTMR010 Timer.
+ *
+ * Copyright (c) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This code is licensed under GNU GPL v2+.
+ */
+
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "qemu/timer.h"
+#include "sysemu/sysemu.h"
+
+#include "hw/fttmr010.h"
+
+#define TYPE_FTTMR010       "fttmr010"
+#define TYPE_FTTMR010_TIMER "fttmr010_timer"
+
+typedef struct Fttmr010State Fttmr010State;
+
+typedef struct Fttmr010Timer {
+    int id;
+    int up;
+    Fttmr010State *chip;
+    qemu_irq irq;
+    QEMUTimer *qtimer;
+    uint64_t start;
+    uint32_t intr_match1:1;
+    uint32_t intr_match2:1;
+
+    /* HW register caches */
+    uint64_t counter;
+    uint64_t reload;
+    uint32_t match1;
+    uint32_t match2;
+
+} Fttmr010Timer;
+
+struct Fttmr010State {
+    /*< private >*/
+    SysBusDevice parent;
+
+    /*< public >*/
+    MemoryRegion iomem;
+    qemu_irq irq;
+    Fttmr010Timer timer[3];
+    uint32_t freq;        /* desired source clock */
+    uint64_t step;        /* get_ticks_per_sec() / freq */
+
+    /* HW register caches */
+    uint32_t cr;
+    uint32_t isr;
+    uint32_t imr;
+};
+
+#define FTTMR010(obj) \
+    OBJECT_CHECK(Fttmr010State, obj, TYPE_FTTMR010)
+
+static void fttmr010_timer_restart(Fttmr010Timer *t)
+{
+    Fttmr010State *s = t->chip;
+    uint64_t interval;
+    int pending = 0;
+
+    t->intr_match1 = 0;
+    t->intr_match2 = 0;
+
+    /* check match1 */
+    if (t->up && t->match1 <= t->counter) {
+        t->intr_match1 = 1;
+    }
+    if (!t->up && t->match1 >= t->counter) {
+        t->intr_match1 = 1;
+    }
+    if (t->match1 == t->counter) {
+        s->isr |= ISR_MATCH1(t->id);
+        ++pending;
+    }
+
+    /* check match2 */
+    if (t->up && t->match2 <= t->counter) {
+        t->intr_match2 = 1;
+    }
+    if (!t->up && t->match2 >= t->counter) {
+        t->intr_match2 = 1;
+    }
+    if (t->match2 == t->counter) {
+        s->isr |= ISR_MATCH2(t->id);
+        ++pending;
+    }
+
+    /* determine delay interval */
+    if (t->up) {
+        if ((t->match1 > t->counter) && (t->match2 > t->counter)) {
+            interval = MIN(t->match1, t->match2) - t->counter;
+        } else if (t->match1 > t->counter) {
+            interval = t->match1 - t->counter;
+        } else if (t->match2 > t->reload) {
+            interval = t->match2 - t->counter;
+        } else {
+            interval = 0xffffffffULL - t->counter;
+        }
+    } else {
+        if ((t->match1 < t->counter) && (t->match2 < t->counter)) {
+            interval = t->counter - MAX(t->match1, t->match2);
+        } else if (t->match1 < t->reload) {
+            interval = t->counter - t->match1;
+        } else if (t->match2 < t->reload) {
+            interval = t->counter - t->match2;
+        } else {
+            interval = t->counter;
+        }
+    }
+
+    if (pending) {
+        qemu_irq_pulse(s->irq);
+        qemu_irq_pulse(t->irq);
+    }
+    t->start = qemu_get_clock_ns(vm_clock);
+    qemu_mod_timer(t->qtimer, t->start + interval * s->step);
+}
+
+static uint64_t fttmr010_update_counter(Fttmr010Timer *t)
+{
+    Fttmr010State *s = t->chip;
+    uint64_t now = qemu_get_clock_ns(vm_clock);
+    uint64_t elapsed;
+    int pending = 0;
+
+    if (s->cr & CR_TMR_EN(t->id)) {
+        /* get elapsed time */
+        elapsed = (now - t->start) / s->step;
+
+        /* convert to count-up/count-down value */
+        if (t->up) {
+            t->counter = t->counter + elapsed;
+        } else {
+            if (t->counter > elapsed) {
+                t->counter -= elapsed;
+            } else {
+                t->counter = 0;
+            }
+        }
+        t->start = now;
+
+        /* check match1 */
+        if (!t->intr_match1) {
+            if (t->up && t->match1 <= t->counter) {
+                t->intr_match1 = 1;
+                s->isr |= ISR_MATCH1(t->id);
+                ++pending;
+            }
+            if (!t->up && t->match1 >= t->counter) {
+                t->intr_match1 = 1;
+                s->isr |= ISR_MATCH1(t->id);
+                ++pending;
+            }
+        }
+
+        /* check match2 */
+        if (!t->intr_match2) {
+            if (t->up && t->match2 <= t->counter) {
+                t->intr_match2 = 1;
+                s->isr |= ISR_MATCH2(t->id);
+                ++pending;
+            }
+            if (!t->up && t->match2 >= t->counter) {
+                t->intr_match2 = 1;
+                s->isr |= ISR_MATCH2(t->id);
+                ++pending;
+            }
+        }
+
+        /* check overflow/underflow */
+        if (t->up && t->counter >= 0xffffffffULL) {
+            if (s->cr & CR_TMR_OFEN(t->id)) {
+                s->isr |= ISR_OF(t->id);
+                ++pending;
+            }
+            t->counter = t->reload;
+            fttmr010_timer_restart(t);
+        }
+        if (!t->up && t->counter == 0) {
+            if (s->cr & CR_TMR_OFEN(t->id)) {
+                s->isr |= ISR_OF(t->id);
+                ++pending;
+            }
+            t->counter = t->reload;
+            fttmr010_timer_restart(t);
+        }
+    }
+
+    if (pending) {
+        qemu_irq_pulse(s->irq);
+        qemu_irq_pulse(t->irq);
+    }
+
+    return t->counter;
+}
+
+static uint64_t
+fttmr010_mem_read(void *opaque, hwaddr addr, unsigned size)
+{
+    Fttmr010State *s = FTTMR010(opaque);
+    Fttmr010Timer *t;
+    uint64_t ret = 0;
+
+    switch (addr) {
+    case REG_TMR_BASE(0) ... REG_TMR_BASE(2) + 0x0C:
+        t = s->timer + REG_TMR_ID(addr);
+        switch (addr & 0x0f) {
+        case REG_TMR_COUNTER:
+            return fttmr010_update_counter(t);
+        case REG_TMR_RELOAD:
+            return t->reload;
+        case REG_TMR_MATCH1:
+            return t->match1;
+        case REG_TMR_MATCH2:
+            return t->match2;
+        }
+        break;
+    case REG_CR:
+        return s->cr;
+    case REG_ISR:
+        return s->isr;
+    case REG_IMR:
+        return s->imr;
+    case REG_REVR:
+        return 0x00010801;  /* rev. 1.8.1 */
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "fttmr010: undefined memory access@%#" HWADDR_PRIx "\n", addr);
+        break;
+    }
+
+    return ret;
+}
+
+static void
+fttmr010_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+    Fttmr010State *s = FTTMR010(opaque);
+    Fttmr010Timer *t;
+    int i;
+
+    switch (addr) {
+    case REG_TMR_BASE(0) ... REG_TMR_BASE(2) + 0x0C:
+        t = s->timer + REG_TMR_ID(addr);
+        switch (addr & 0x0f) {
+        case REG_TMR_COUNTER:
+            t->counter = (uint32_t)val;
+            break;
+        case REG_TMR_RELOAD:
+            t->reload = (uint32_t)val;
+            break;
+        case REG_TMR_MATCH1:
+            t->match1 = (uint32_t)val;
+            break;
+        case REG_TMR_MATCH2:
+            t->match2 = (uint32_t)val;
+            break;
+        }
+        break;
+    case REG_CR:
+        s->cr = (uint32_t)val;
+        for (i = 0; i < 3; ++i) {
+            t = s->timer + i;
+            if (s->cr & CR_TMR_COUNTUP(t->id)) {
+                t->up = 1;
+            } else {
+                t->up = 0;
+            }
+            if (s->cr & CR_TMR_EN(t->id)) {
+                fttmr010_timer_restart(t);
+            } else {
+                qemu_del_timer(t->qtimer);
+            }
+        }
+        break;
+    case REG_ISR:
+        s->isr &= ~((uint32_t)val);
+        break;
+    case REG_IMR:
+        s->imr = (uint32_t)val;
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "fttmr010: undefined memory access@%#" HWADDR_PRIx "\n", addr);
+        break;
+    }
+}
+
+static const MemoryRegionOps mmio_ops = {
+    .read  = fttmr010_mem_read,
+    .write = fttmr010_mem_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    }
+};
+
+static void fttmr010_timer_tick(void *opaque)
+{
+    Fttmr010Timer *t = opaque;
+    Fttmr010State *s = t->chip;
+    uint64_t now;
+
+    /* if the timer has been enabled/started */
+    if (!(s->cr & CR_TMR_EN(t->id))) {
+        return;
+    }
+
+    fttmr010_update_counter(t);
+
+    if (t->reload == t->counter) {
+        return;
+    }
+
+    now = qemu_get_clock_ns(vm_clock);
+
+    if (t->up) {
+        if (!t->intr_match1 && t->match1 > t->counter) {
+            qemu_mod_timer(t->qtimer,
+                now + (t->match1 - t->counter) * s->step);
+        } else if (!t->intr_match2 && t->match2 > t->counter) {
+            qemu_mod_timer(t->qtimer,
+                now + (t->match2 - t->counter) * s->step);
+        } else {
+            qemu_mod_timer(t->qtimer,
+                now + (0xffffffffULL - t->counter) * s->step);
+        }
+    } else {
+        if (!t->intr_match1 && t->match1 < t->counter) {
+            qemu_mod_timer(t->qtimer,
+                now + (t->counter - t->match1) * s->step);
+        } else if (!t->intr_match2 && t->match2 < t->counter) {
+            qemu_mod_timer(t->qtimer,
+                now + (t->counter - t->match2) * s->step);
+        } else {
+            qemu_mod_timer(t->qtimer,
+                now + t->counter * s->step);
+        }
+    }
+}
+
+static void fttmr010_reset(DeviceState *ds)
+{
+    Fttmr010State *s = FTTMR010(SYS_BUS_DEVICE(ds));
+    int i;
+
+    s->cr  = 0;
+    s->isr = 0;
+    s->imr = 0;
+    qemu_irq_lower(s->irq);
+
+    for (i = 0; i < 3; ++i) {
+        s->timer[i].counter = 0;
+        s->timer[i].reload  = 0;
+        s->timer[i].match1  = 0;
+        s->timer[i].match2  = 0;
+        qemu_irq_lower(s->timer[i].irq);
+        qemu_del_timer(s->timer[i].qtimer);
+    }
+}
+
+static void fttmr010_realize(DeviceState *dev, Error **errp)
+{
+    Fttmr010State *s = FTTMR010(dev);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+    int i;
+
+    s->step = (uint64_t)get_ticks_per_sec() / (uint64_t)s->freq;
+
+    memory_region_init_io(&s->iomem,
+                          &mmio_ops,
+                          s,
+                          TYPE_FTTMR010,
+                          0x1000);
+    sysbus_init_mmio(sbd, &s->iomem);
+    sysbus_init_irq(sbd, &s->irq);
+    for (i = 0; i < 3; ++i) {
+        s->timer[i].id = i;
+        s->timer[i].chip = s;
+        s->timer[i].qtimer = qemu_new_timer_ns(vm_clock,
+                                        fttmr010_timer_tick, &s->timer[i]);
+        sysbus_init_irq(sbd, &s->timer[i].irq);
+    }
+}
+
+static const VMStateDescription vmstate_fttmr010_timer = {
+    .name = TYPE_FTTMR010_TIMER,
+    .version_id = 2,
+    .minimum_version_id = 2,
+    .minimum_version_id_old = 2,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT64(counter, Fttmr010Timer),
+        VMSTATE_UINT64(reload, Fttmr010Timer),
+        VMSTATE_UINT32(match1, Fttmr010Timer),
+        VMSTATE_UINT32(match2, Fttmr010Timer),
+        VMSTATE_END_OF_LIST(),
+    },
+};
+
+static const VMStateDescription vmstate_fttmr010 = {
+    .name = TYPE_FTTMR010,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(cr, Fttmr010State),
+        VMSTATE_UINT32(isr, Fttmr010State),
+        VMSTATE_UINT32(imr, Fttmr010State),
+        VMSTATE_STRUCT_ARRAY(timer, Fttmr010State, 3, 1,
+                        vmstate_fttmr010_timer, Fttmr010Timer),
+        VMSTATE_END_OF_LIST(),
+    }
+};
+
+static Property fttmr010_properties[] = {
+    DEFINE_PROP_UINT32("freq", Fttmr010State, freq, 66000000),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void fttmr010_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->vmsd    = &vmstate_fttmr010;
+    dc->props   = fttmr010_properties;
+    dc->reset   = fttmr010_reset;
+    dc->realize = fttmr010_realize;
+    dc->no_user = 1;
+}
+
+static const TypeInfo fttmr010_info = {
+    .name          = TYPE_FTTMR010,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(Fttmr010State),
+    .class_init    = fttmr010_class_init,
+};
+
+static void fttmr010_register_types(void)
+{
+    type_register_static(&fttmr010_info);
+}
+
+type_init(fttmr010_register_types)
diff --git a/hw/fttmr010.h b/hw/fttmr010.h
new file mode 100644
index 0000000..7ae20cb
--- /dev/null
+++ b/hw/fttmr010.h
@@ -0,0 +1,39 @@
+/*
+ * Faraday FTTMR010 Timer.
+ *
+ * Copyright (c) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This code is licensed under GNU GPL v2+.
+ */
+
+#ifndef HW_ARM_FTTMR010_H
+#define HW_ARM_FTTMR010_H
+
+#define REG_TMR_ID(off)     ((off) >> 4)
+#define REG_TMR_BASE(id)    (0x00 + ((id) << 4))
+#define REG_TMR_COUNTER     0x00
+#define REG_TMR_RELOAD      0x04
+#define REG_TMR_MATCH1      0x08
+#define REG_TMR_MATCH2      0x0C
+
+#define REG_CR              0x30    /* control register */
+#define REG_ISR             0x34    /* interrupt status register */
+#define REG_IMR             0x38    /* interrupt mask register */
+#define REG_REVR            0x3C    /* revision register */
+
+/* timer enable */
+#define CR_TMR_EN(id)       (0x01 << ((id) * 3))
+/* timer overflow interrupt enable */
+#define CR_TMR_OFEN(id)     (0x04 << ((id) * 3))
+/* timer count-up mode */
+#define CR_TMR_COUNTUP(id)  (0x01 << (9 + (id)))
+
+/* timer match 1 */
+#define ISR_MATCH1(id)      (0x01 << ((id) * 3))
+/* timer match 2 */
+#define ISR_MATCH2(id)      (0x02 << ((id) * 3))
+/* timer overflow */
+#define ISR_OF(id)          (0x04 << ((id) * 3))
+
+#endif
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH v9 24/24] hw/arm: add FTSPI020 SPI flash controller support
  2013-03-25 12:09 [Qemu-devel] [PATCH v9 00/24] hw/arm: add Faraday A369 SoC platform support Kuo-Jung Su
                   ` (22 preceding siblings ...)
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 23/24] hw/arm: add FTTMR010 timer support Kuo-Jung Su
@ 2013-03-25 12:10 ` Kuo-Jung Su
  2013-03-29  0:02   ` Peter Crosthwaite
  23 siblings, 1 reply; 35+ messages in thread
From: Kuo-Jung Su @ 2013-03-25 12:10 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, i.mitsyanko, Blue Swirl, Paul Brook, Kuo-Jung Su,
	Andreas, fred.konrad

From: Kuo-Jung Su <dantesu@faraday-tech.com>

The FTSPI020 is an integrated SPI Flash controller
which supports up to 4 flash chips.

Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
---
 hw/arm/Makefile.objs |    2 +-
 hw/arm/ftplat_a369.c |   16 +++
 hw/ftspi020.c        |  341 ++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/ftspi020.h        |   81 ++++++++++++
 4 files changed, 439 insertions(+), 1 deletion(-)
 create mode 100644 hw/ftspi020.c
 create mode 100644 hw/ftspi020.h

diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index bcfb70a..a34ca41 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -27,7 +27,7 @@ obj-$(CONFIG_KVM) += kvm/arm_gic.o
 obj-y += ftintc020.o ftahbc020.o ftddrii030.o ftpwmtmr010.o ftwdt010.o \
                 ftrtc011.o ftdmac020.o ftapbbrg020.o ftnandc021.o fti2c010.o \
                 ftssp010.o ftgmac100.o ftlcdc200.o fttsc010.o ftsdc010.o \
-                ftmac110.o fttmr010.o
+                ftmac110.o fttmr010.o ftspi020.o
 
 obj-y := $(addprefix ../,$(obj-y))
 
diff --git a/hw/arm/ftplat_a369.c b/hw/arm/ftplat_a369.c
index f22e2ca..77cd44d 100644
--- a/hw/arm/ftplat_a369.c
+++ b/hw/arm/ftplat_a369.c
@@ -125,6 +125,22 @@ static void a369_board_init(QEMUMachineInitArgs *args)
     sysbus_connect_irq(SYS_BUS_DEVICE(ds), 1, s->pic[6]);
     sysbus_connect_irq(SYS_BUS_DEVICE(ds), 2, s->pic[7]);
 
+    /* SPI: FTSPI020 */
+    ds = sysbus_create_simple("ftspi020", 0xC0000000, s->pic[4]);
+    s->spi_fl[0] = ds;
+
+    /* Attach the spi flash to ftspi020.0 */
+    nr_flash = 1;
+    for (i = 0; i < nr_flash; i++) {
+        SSIBus *ssi = (SSIBus *)qdev_get_child_bus(s->spi_fl[0], "spi");
+        DeviceState *fl = ssi_create_slave_no_init(ssi, "w25q64");
+        qemu_irq cs_line;
+
+        qdev_init_nofail(fl);
+        cs_line = qdev_get_gpio_in(fl, 0);
+        sysbus_connect_irq(SYS_BUS_DEVICE(s->spi_fl[0]), i + 1, cs_line);
+    }
+
     /* System start-up */
 
     if (args->kernel_filename) {
diff --git a/hw/ftspi020.c b/hw/ftspi020.c
new file mode 100644
index 0000000..a7253bd
--- /dev/null
+++ b/hw/ftspi020.c
@@ -0,0 +1,341 @@
+/*
+ * Faraday FTSPI020 Flash Controller
+ *
+ * Copyright (c) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This code is licensed under GNU GPL v2+.
+ */
+
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "hw/ssi.h"
+#include "sysemu/sysemu.h"
+
+#include "hw/ftspi020.h"
+
+#define TYPE_FTSPI020   "ftspi020"
+
+typedef struct Ftspi020State {
+    /*< private >*/
+    SysBusDevice parent;
+
+    /*< public >*/
+    MemoryRegion iomem;
+    qemu_irq irq;
+
+    /* DMA hardware handshake */
+    qemu_irq req;
+
+    SSIBus *spi;
+    qemu_irq *cs_lines;
+
+    int wip;    /* SPI Flash Status: Write In Progress BIT shift */
+
+    /* HW register caches */
+    uint32_t cmd[4];
+    uint32_t ctrl;
+    uint32_t timing;
+    uint32_t icr;
+    uint32_t isr;
+    uint32_t rdsr;
+} Ftspi020State;
+
+#define FTSPI020(obj) \
+    OBJECT_CHECK(Ftspi020State, obj, TYPE_FTSPI020)
+
+static void ftspi020_update_irq(Ftspi020State *s)
+{
+    qemu_set_irq(s->irq, s->isr ? 1 : 0);
+}
+
+static void ftspi020_handle_ack(void *opaque, int line, int level)
+{
+    Ftspi020State *s = FTSPI020(opaque);
+
+    if (!(s->icr & ICR_DMA)) {
+        return;
+    }
+
+    if (level) {
+        qemu_set_irq(s->req, 0);
+    } else if (s->cmd[2]) {
+        qemu_set_irq(s->req, 1);
+    }
+}
+
+static int ftspi020_do_command(Ftspi020State *s)
+{
+    uint32_t cs   = extract32(s->cmd[3],  8, 2);
+    uint32_t cmd  = extract32(s->cmd[3], 24, 8);
+    uint32_t ilen = extract32(s->cmd[1], 24, 2);
+    uint32_t alen = extract32(s->cmd[1],  0, 3);
+    uint32_t dcyc = extract32(s->cmd[1], 16, 8);
+
+    if (dcyc % 8) {
+        fprintf(stderr, "ftspi020: bad dummy clock (%u) to QEMU\n", dcyc);
+        abort();
+    }
+
+    /* activate the spi flash */
+    qemu_set_irq(s->cs_lines[cs], 0);
+
+    /* if it's a SPI flash READ_STATUS command */
+    if ((s->cmd[3] & (CMD3_RDSR | CMD3_WRITE)) == CMD3_RDSR) {
+        uint32_t rdsr;
+
+        ssi_transfer(s->spi, cmd);
+        do {
+            rdsr = ssi_transfer(s->spi, 0x00);
+            if (s->cmd[3] & CMD3_RDSR_SW) {
+                break;
+            }
+        } while (rdsr & (1 << s->wip));
+        s->rdsr = rdsr;
+    } else {
+    /* otherwise */
+        int i;
+
+        ilen = MIN(ilen, 2);
+        alen = MIN(alen, 4);
+
+        /* command cycles */
+        for (i = 0; i < ilen; ++i) {
+            ssi_transfer(s->spi, cmd);
+        }
+        /* address cycles */
+        for (i = alen - 1; i >= 0; --i) {
+            ssi_transfer(s->spi, extract32(s->cmd[0], i * 8, 8));
+        }
+        /* dummy cycles */
+        for (i = 0; i < (dcyc >> 3); ++i) {
+            ssi_transfer(s->spi, 0x00);
+        }
+    }
+
+    if (!s->cmd[2]) {
+        qemu_set_irq(s->cs_lines[cs], 1);
+    } else if (s->icr & ICR_DMA) {
+        qemu_set_irq(s->req, 1);
+    }
+
+    if (s->cmd[3] & CMD3_INTR) {
+        s->isr |= ISR_CMDFIN;
+    }
+    ftspi020_update_irq(s);
+
+    return 0;
+}
+
+static void ftspi020_chip_reset(Ftspi020State *s)
+{
+    int i;
+
+    for (i = 0; i < 4; ++i) {
+        s->cmd[i] = 0;
+    }
+    s->wip = 0;
+    s->ctrl = 0;
+    s->timing = 0;
+    s->icr = 0;
+    s->isr = 0;
+    s->rdsr = 0;
+
+    qemu_set_irq(s->irq, 0);
+
+    for (i = 0; i < CFG_NR_CSLINES; ++i) {
+        /* de-activate the spi flash */
+        qemu_set_irq(s->cs_lines[i], 1);
+    }
+}
+
+static uint64_t
+ftspi020_mem_read(void *opaque, hwaddr addr, unsigned size)
+{
+    Ftspi020State *s = FTSPI020(opaque);
+    uint64_t ret = 0;
+
+    switch (addr) {
+    case REG_CMD0 ... REG_CMD3:
+        return s->cmd[(addr - REG_CMD0) / 4];
+    case REG_CR:
+        return s->ctrl;
+    case REG_TR:
+        return s->timing;
+    case REG_SR:
+        /* In QEMU, the data fifo is always ready for read/write */
+        return SR_RX_READY | SR_TX_READY;
+    case REG_ISR:
+        return s->isr;
+    case REG_ICR:
+        return s->icr;
+    case REG_RDSR:
+        return s->rdsr;
+    case REG_REVR:
+        return 0x00010001;  /* rev. 1.0.1 */
+    case REG_FEAR:
+        return FEAR_CLKM_SYNC
+            | FEAR_CMDQ(2) | FEAR_RXFIFO(32) | FEAR_TXFIFO(32);
+    case REG_DR:
+        if (!(s->cmd[3] & CMD3_WRITE)) {
+            int i;
+            uint32_t cs = extract32(s->cmd[3], 8, 2);
+            for (i = 0; i < 4 && s->cmd[2]; i++, s->cmd[2]--) {
+                ret = deposit32(ret, i * 8, 8,
+                    ssi_transfer(s->spi, 0x00) & 0xff);
+            }
+            if (!s->cmd[2]) {
+                qemu_set_irq(s->cs_lines[cs], 1);
+                if (s->cmd[3] & CMD3_INTR) {
+                    s->isr |= ISR_CMDFIN;
+                }
+                ftspi020_update_irq(s);
+            }
+        }
+        break;
+        /* we don't care */
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "ftspi020: undefined memory access@%#" HWADDR_PRIx "\n", addr);
+        break;
+    }
+
+    return ret;
+}
+
+static void
+ftspi020_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+    Ftspi020State *s = FTSPI020(opaque);
+
+    switch (addr) {
+    case REG_CMD0 ... REG_CMD2:
+        s->cmd[(addr - REG_CMD0) / 4] = (uint32_t)val;
+        break;
+    case REG_CMD3:
+        s->cmd[3] = (uint32_t)val;
+        ftspi020_do_command(s);
+        break;
+    case REG_CR:
+        if (val & CR_ABORT) {
+            ftspi020_chip_reset(s);
+            val &= ~CR_ABORT;
+        }
+        s->ctrl = (uint32_t)val;
+        s->wip  = extract32(val, 16, 3);
+        break;
+    case REG_TR:
+        s->timing = (uint32_t)val;
+        break;
+    case REG_DR:
+        if (s->cmd[3] & CMD3_WRITE) {
+            int i;
+            uint32_t cs = extract32(s->cmd[3], 8, 2);
+            for (i = 0; i < 4 && s->cmd[2]; i++, s->cmd[2]--) {
+                ssi_transfer(s->spi, extract32((uint32_t)val, i * 8, 8));
+            }
+            if (!s->cmd[2]) {
+                qemu_set_irq(s->cs_lines[cs], 1);
+                if (s->cmd[3] & CMD3_INTR) {
+                    s->isr |= ISR_CMDFIN;
+                }
+                ftspi020_update_irq(s);
+            }
+        }
+        break;
+    case REG_ISR:
+        s->isr &= ~((uint32_t)val);
+        ftspi020_update_irq(s);
+        break;
+    case REG_ICR:
+        s->icr = (uint32_t)val;
+        break;
+        /* we don't care */
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "ftspi020: undefined memory access@%#" HWADDR_PRIx "\n", addr);
+        break;
+    }
+}
+
+static const MemoryRegionOps mmio_ops = {
+    .read  = ftspi020_mem_read,
+    .write = ftspi020_mem_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    }
+};
+
+static void ftspi020_reset(DeviceState *ds)
+{
+    Ftspi020State *s = FTSPI020(SYS_BUS_DEVICE(ds));
+
+    ftspi020_chip_reset(s);
+}
+
+static void ftspi020_realize(DeviceState *dev, Error **errp)
+{
+    Ftspi020State *s = FTSPI020(dev);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+    int i;
+
+    memory_region_init_io(&s->iomem,
+                          &mmio_ops,
+                          s,
+                          TYPE_FTSPI020,
+                          0x1000);
+    sysbus_init_mmio(sbd, &s->iomem);
+    sysbus_init_irq(sbd, &s->irq);
+
+    s->spi = ssi_create_bus(&sbd->qdev, "spi");
+    s->cs_lines = g_new0(qemu_irq, CFG_NR_CSLINES);
+    ssi_auto_connect_slaves(dev, s->cs_lines, s->spi);
+    for (i = 0; i < CFG_NR_CSLINES; ++i) {
+        sysbus_init_irq(sbd, &s->cs_lines[i]);
+    }
+
+    qdev_init_gpio_in(&sbd->qdev, ftspi020_handle_ack, 1);
+    qdev_init_gpio_out(&sbd->qdev, &s->req, 1);
+}
+
+static const VMStateDescription vmstate_ftspi020 = {
+    .name = TYPE_FTSPI020,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(cmd, Ftspi020State, 4),
+        VMSTATE_UINT32(ctrl, Ftspi020State),
+        VMSTATE_UINT32(timing, Ftspi020State),
+        VMSTATE_UINT32(icr, Ftspi020State),
+        VMSTATE_UINT32(isr, Ftspi020State),
+        VMSTATE_UINT32(rdsr, Ftspi020State),
+        VMSTATE_END_OF_LIST(),
+    }
+};
+
+static void ftspi020_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->vmsd    = &vmstate_ftspi020;
+    dc->reset   = ftspi020_reset;
+    dc->realize = ftspi020_realize;
+    dc->no_user = 1;
+}
+
+static const TypeInfo ftspi020_info = {
+    .name          = TYPE_FTSPI020,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(Ftspi020State),
+    .class_init    = ftspi020_class_init,
+};
+
+static void ftspi020_register_types(void)
+{
+    type_register_static(&ftspi020_info);
+}
+
+type_init(ftspi020_register_types)
diff --git a/hw/ftspi020.h b/hw/ftspi020.h
new file mode 100644
index 0000000..47b5d2e
--- /dev/null
+++ b/hw/ftspi020.h
@@ -0,0 +1,81 @@
+/*
+ * Faraday FTSPI020 Flash Controller
+ *
+ * Copyright (c) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This code is licensed under GNU GPL v2+.
+ */
+
+#ifndef HW_ARM_FTSPI020_H
+#define HW_ARM_FTSPI020_H
+
+#include "qemu/bitops.h"
+
+/* Number of CS lines */
+#define CFG_NR_CSLINES      4
+
+/******************************************************************************
+ * FTSPI020 registers
+ *****************************************************************************/
+#define REG_CMD0            0x00    /* Flash address */
+#define REG_CMD1            0x04
+#define REG_CMD2            0x08    /* Flash data counter */
+#define REG_CMD3            0x0c
+#define REG_CR              0x10    /* Control Register */
+#define REG_TR              0x14    /* AC Timing Register */
+#define REG_SR              0x18    /* Status Register */
+#define REG_ICR             0x20    /* Interrupt Control Register */
+#define REG_ISR             0x24    /* Interrupt Status Register */
+#define REG_RDSR            0x28    /* Read Status Register */
+#define REG_REVR            0x50    /* Revision Register */
+#define REG_FEAR            0x54    /* Feature Register */
+#define REG_DR              0x100   /* Data Register */
+
+#define CMD1_CTRD           BIT(28) /* Enable 1 byte continuous read */
+#define CMD1_INST_LEN(x)    (((x) & 0x03) << 24)/* instruction length */
+#define CMD1_DCLK_LEN(x)    (((x) & 0xff) << 16)/* dummy clock length */
+#define CMD1_ADDR_LEN(x)    (((x) & 0x07) << 0) /* address length */
+
+#define CMD3_INST_OPC(x)    (((x) & 0xff) << 24)/* instruction op code */
+#define CMD3_CTRD_OPC(x)    (((x) & 0xff) << 16)/* cont. read op code */
+#define CMD3_CS(x)          (((x) & 0x0f) << 8) /* chip select */
+#define CMD3_OPM_STD        (0)         /* standard 1-bit serial mode */
+#define CMD3_OPM_DUAL       (1 << 5)    /* fast read dual */
+#define CMD3_OPM_QUAD       (2 << 5)    /* fast read quad */
+#define CMD3_OPM_DUALIO     (3 << 5)    /* fast read dual io */
+#define CMD3_OPM_QUADIO     (4 << 5)    /* fast read quad io */
+#define CMD3_DTR            BIT(4)  /* Enable double transfer rate */
+#define CMD3_RDSR_HW        (0)     /* Enable HW polling RDSR */
+#define CMD3_RDSR_SW        BIT(3)  /* Disable HW polling RDSR */
+#define CMD3_RDSR           BIT(2)  /* Indicate it's a RDSR command */
+#define CMD3_READ           0       /* Indicate it's a read command */
+#define CMD3_WRITE          BIT(1)  /* Indicate it's a write command */
+#define CMD3_INTR           BIT(0)  /* Enable interrupt and status update */
+
+#define CR_BUSYBIT(x)       (((x) & 0x07) << 16) /* Busy bit in the RDSR */
+#define CR_ABORT            BIT(8)
+#define CR_MODE0            0       /* SPI MODE0 */
+#define CR_MODE3            BIT(4)  /* SPI MODE3 */
+#define CR_CLKDIV(n)        ((n) & 0x03)    /* Clock divider = 2 * (n + 1) */
+
+#define TR_TRACE(x)         (((x) & 0x0f) << 4) /* trace delay */
+#define TR_CS(x)            (((x) & 0x0f) << 0) /* cs delay */
+
+#define SR_RX_READY         BIT(1)  /* Rx Ready */
+#define SR_TX_READY         BIT(0)  /* Tx Ready */
+
+#define ICR_RX_THRES(x)     (((x) & 0x03) << 12)/* rx interrupt threshold */
+#define ICR_TX_THRES(x)     (((x) & 0x03) << 8) /* tx interrupt threshold */
+#define ICR_DMA             BIT(0)  /* Enable DMA HW handshake */
+
+#define ISR_CMDFIN          BIT(0)  /* Command finished interrupt */
+
+#define FEAR_CLKM_BYPORT    0       /* clock mode = byport */
+#define FEAR_CLKM_SYNC      BIT(25) /* clock mode = sync */
+#define FEAR_SUPP_DTR       BIT(24) /* support double transfer rate */
+#define FEAR_CMDQ(x)        (((x) & 0x07) << 16)    /* cmd queue depth */
+#define FEAR_RXFIFO(x)      (((x) & 0xff) << 8)     /* rx fifo depth */
+#define FEAR_TXFIFO(x)      (((x) & 0xff) << 0)     /* tx fifo depth */
+
+#endif  /* HW_ARM_FTSPI020_H */
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* Re: [Qemu-devel] [PATCH v9 05/24] hw/arm: add FTDDRII030 DDRII controller support
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 05/24] hw/arm: add FTDDRII030 DDRII " Kuo-Jung Su
@ 2013-03-28  0:09   ` Peter Crosthwaite
  2013-03-28  3:24     ` Kuo-Jung Su
  0 siblings, 1 reply; 35+ messages in thread
From: Peter Crosthwaite @ 2013-03-28  0:09 UTC (permalink / raw)
  To: Kuo-Jung Su
  Cc: Peter Maydell, i.mitsyanko, qemu-devel, Blue Swirl, Paul Brook,
	Kuo-Jung Su, Andreas, fred.konrad

Hi Kuo-Jung,

On Mon, Mar 25, 2013 at 10:09 PM, Kuo-Jung Su <dantesu@gmail.com> wrote:
> From: Kuo-Jung Su <dantesu@faraday-tech.com>
>
> The FTDDRII030 is a DDRII SDRAM controller which is responsible for
> SDRAM initialization.
>
> Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
> ---
>  hw/arm/Makefile.objs    |    2 +-
>  hw/arm/ftplat_a369soc.c |    8 ++
>  hw/ftddrii030.c         |  192 +++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 201 insertions(+), 1 deletion(-)
>  create mode 100644 hw/ftddrii030.c
>
> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
> index b2fa20f..e774962 100644
> --- a/hw/arm/Makefile.objs
> +++ b/hw/arm/Makefile.objs
> @@ -24,7 +24,7 @@ obj-y += framebuffer.o
>  obj-y += strongarm.o
>  obj-y += imx_serial.o imx_ccm.o imx_timer.o imx_avic.o
>  obj-$(CONFIG_KVM) += kvm/arm_gic.o
> -obj-y += ftintc020.o ftahbc020.o
> +obj-y += ftintc020.o ftahbc020.o ftddrii030.o
>
>  obj-y := $(addprefix ../,$(obj-y))
>
> diff --git a/hw/arm/ftplat_a369soc.c b/hw/arm/ftplat_a369soc.c
> index 7f222cb..b2da582 100644
> --- a/hw/arm/ftplat_a369soc.c
> +++ b/hw/arm/ftplat_a369soc.c
> @@ -129,6 +129,14 @@ static void a369soc_chip_init(FaradaySoCState *s)
>          fprintf(stderr, "a369soc: Unable to set soc link for FTAHBC020\n");
>          abort();
>      }
> +
> +    /* ftddrii030 */
> +    ds = sysbus_create_simple("ftddrii030", 0x93100000, NULL);
> +    object_property_set_link(OBJECT(ds), OBJECT(s), "soc", &local_errp);
> +    if (local_errp) {
> +        fprintf(stderr, "a369soc: Unable to set soc link for FTDDRII030\n");
> +        abort();
> +    }
>  }
>
>  static void a369soc_realize(DeviceState *dev, Error **errp)
> diff --git a/hw/ftddrii030.c b/hw/ftddrii030.c
> new file mode 100644
> index 0000000..158db1f
> --- /dev/null
> +++ b/hw/ftddrii030.c
> @@ -0,0 +1,192 @@
> +/*
> + * Faraday DDRII controller
> + *
> + * Copyright (c) 2012 Faraday Technology
> + * Written by Dante Su <dantesu@faraday-tech.com>
> + *
> + * This code is licensed under GNU GPL v2+
> + */
> +
> +#include "hw/hw.h"
> +#include "hw/sysbus.h"
> +#include "hw/devices.h"
> +#include "sysemu/sysemu.h"
> +
> +#include "hw/faraday.h"
> +
> +#define REG_MCR             0x00    /* memory configuration register */
> +#define REG_MSR             0x04    /* memory status register */
> +#define REG_REVR            0x50    /* revision register */
> +
> +#define MSR_INIT_OK         BIT(8)  /* initialization finished */
> +#define MSR_CMD_MRS         BIT(0)  /* start MRS command (init. seq.) */
> +
> +#define CFG_REGSIZE         (REG_REVR / 4)
> +
> +#define TYPE_FTDDRII030     "ftddrii030"
> +
> +typedef struct Ftddrii030State {
> +    /*< private >*/
> +    SysBusDevice parent;
> +
> +    /*< public >*/
> +    MemoryRegion iomem;
> +
> +    FaradaySoCState *soc;
> +

This new implementation still has many of the same encapsulation
issues discussed in v8. If you go with the suggestion myself and Peter
came up with you could make this device completely self contained - it
should have no awareness that it is part of the Faraday SoC. If you
model as a second MemoryRegion you can push all the Faraday specific
foo up to the SoC. Interdiff would go something like this:

-    FaradaySoCState *soc;
+    MemoryRegion ram;

> +    /* HW register cache */
> +    uint32_t regs[CFG_REGSIZE];
> +} Ftddrii030State;
> +
> +#define FTDDRII030(obj) \
> +    OBJECT_CHECK(Ftddrii030State, obj, TYPE_FTDDRII030)
> +
> +#define DDR_REG32(s, off) \
> +    ((s)->regs[(off) / 4])
> +
> +static uint64_t
> +ftddrii030_mem_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    Ftddrii030State *s = FTDDRII030(opaque);
> +    FaradaySoCState *soc = s->soc;
> +    uint64_t ret = 0;
> +
> +    if (soc->ram_visible) {
> +        DDR_REG32(s, REG_MSR) |= MSR_INIT_OK;
> +    } else {
> +        DDR_REG32(s, REG_MSR) &= ~MSR_INIT_OK;
> +    }
> +
> +    switch (addr) {
> +    case REG_MCR ... REG_REVR - 4:
> +        ret = DDR_REG32(s, addr);
> +        break;
> +    case REG_REVR:
> +        ret = 0x100;    /* rev. = 0.1.0 */
> +        break;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +            "ftddrii030: undefined memory access@%#" HWADDR_PRIx "\n", addr);
> +        break;
> +    }
> +
> +    return ret;
> +}
> +
> +static void
> +ftddrii030_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
> +{
> +    Ftddrii030State *s = FTDDRII030(opaque);
> +    FaradaySoCState *soc = s->soc;
> +
> +    switch (addr) {
> +    case REG_MCR:
> +        DDR_REG32(s, addr) = (uint32_t)val & 0xffff;
> +        break;
> +    case REG_MSR:
> +        if (!soc->ram_visible && (val & MSR_CMD_MRS)) {
> +            val &= ~MSR_CMD_MRS;
> +            faraday_soc_ram_setup(soc, true);

This function (added earlier in this series) then becomes local to
this device model and the memory_region_add_subregion() happens with
the new ram Memory region instead. AFAICT that function only requires
the ram size which strikes me as something that should be a QOM
property to the device.

> +        }
> +        DDR_REG32(s, addr) = (uint32_t)val;
> +        break;
> +    /* SDRAM Timing, ECC ...etc. */
> +    case REG_MSR + 4 ... REG_REVR - 4:
> +        DDR_REG32(s, addr) = (uint32_t)val;
> +        break;
> +    case REG_REVR:
> +        break;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +            "ftddrii030: undefined memory access@%#" HWADDR_PRIx "\n", addr);
> +        break;
> +    }
> +}
> +
> +static const MemoryRegionOps mmio_ops = {
> +    .read  = ftddrii030_mem_read,
> +    .write = ftddrii030_mem_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .valid = {
> +        .min_access_size = 4,
> +        .max_access_size = 4,
> +    }
> +};
> +
> +static void ftddrii030_reset(DeviceState *ds)
> +{
> +    Ftddrii030State *s = FTDDRII030(SYS_BUS_DEVICE(ds));
> +    Error *local_errp = NULL;
> +
> +    s->soc = FARADAY_SOC(object_property_get_link(OBJECT(s),
> +                                                  "soc",
> +                                                  &local_errp));
> +    if (local_errp) {
> +        fprintf(stderr, "ftahbc020: Unable to get soc link\n");
> +        abort();
> +    }
> +
> +    faraday_soc_ram_setup(s->soc, false);
> +    memset(s->regs, 0, sizeof(s->regs));
> +}
> +
> +static void ftddrii030_realize(DeviceState *dev, Error **errp)
> +{
> +    Ftddrii030State *s = FTDDRII030(dev);
> +    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
> +
> +    memory_region_init_io(&s->iomem,
> +                          &mmio_ops,
> +                          s,
> +                          TYPE_FTDDRII030,
> +                          0x1000);
> +    sysbus_init_mmio(sbd, &s->iomem);

Init the new ram memory region here.

I could use a second opinion on all this QOM stuff though - you are on
the bleeding edge with the QOM SoC stuff. I suggest giving it some
list time for others to reply before a remake.

Regards,
Peter

> +}
> +
> +static const VMStateDescription vmstate_ftddrii030 = {
> +    .name = TYPE_FTDDRII030,
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .minimum_version_id_old = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_UINT32_ARRAY(regs, Ftddrii030State, CFG_REGSIZE),
> +        VMSTATE_END_OF_LIST(),
> +    }
> +};
> +
> +static void ftddrii030_instance_init(Object *obj)
> +{
> +    Ftddrii030State *s = FTDDRII030(obj);
> +
> +    object_property_add_link(obj,
> +                             "soc",
> +                             TYPE_FARADAY_SOC,
> +                             (Object **) &s->soc,
> +                             NULL);
> +}
> +
> +static void ftddrii030_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    dc->desc  = TYPE_FTDDRII030;
> +    dc->vmsd  = &vmstate_ftddrii030;
> +    dc->reset = ftddrii030_reset;
> +    dc->realize = ftddrii030_realize;
> +    dc->no_user = 1;
> +}
> +
> +static const TypeInfo ftddrii030_info = {
> +    .name          = TYPE_FTDDRII030,
> +    .parent        = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(Ftddrii030State),
> +    .instance_init = ftddrii030_instance_init,
> +    .class_init    = ftddrii030_class_init,
> +};
> +
> +static void ftddrii030_register_types(void)
> +{
> +    type_register_static(&ftddrii030_info);
> +}
> +
> +type_init(ftddrii030_register_types)
> --
> 1.7.9.5
>
>

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Qemu-devel] [PATCH v9 05/24] hw/arm: add FTDDRII030 DDRII controller support
  2013-03-28  0:09   ` Peter Crosthwaite
@ 2013-03-28  3:24     ` Kuo-Jung Su
  2013-03-28  3:58       ` Peter Crosthwaite
  0 siblings, 1 reply; 35+ messages in thread
From: Kuo-Jung Su @ 2013-03-28  3:24 UTC (permalink / raw)
  To: Peter Crosthwaite
  Cc: Peter Maydell, Igor Mitsyanko, qemu-devel, Blue Swirl,
	Paul Brook, Kuo-Jung Su, Andreas, fred konrad

2013/3/28 Peter Crosthwaite <peter.crosthwaite@xilinx.com>:
> Hi Kuo-Jung,
>
> On Mon, Mar 25, 2013 at 10:09 PM, Kuo-Jung Su <dantesu@gmail.com> wrote:
>> From: Kuo-Jung Su <dantesu@faraday-tech.com>
>>
>> The FTDDRII030 is a DDRII SDRAM controller which is responsible for
>> SDRAM initialization.
>>
>> Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
>> ---
>>  hw/arm/Makefile.objs    |    2 +-
>>  hw/arm/ftplat_a369soc.c |    8 ++
>>  hw/ftddrii030.c         |  192 +++++++++++++++++++++++++++++++++++++++++++++++
>>  3 files changed, 201 insertions(+), 1 deletion(-)
>>  create mode 100644 hw/ftddrii030.c
>>
>> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
>> index b2fa20f..e774962 100644
>> --- a/hw/arm/Makefile.objs
>> +++ b/hw/arm/Makefile.objs
>> @@ -24,7 +24,7 @@ obj-y += framebuffer.o
>>  obj-y += strongarm.o
>>  obj-y += imx_serial.o imx_ccm.o imx_timer.o imx_avic.o
>>  obj-$(CONFIG_KVM) += kvm/arm_gic.o
>> -obj-y += ftintc020.o ftahbc020.o
>> +obj-y += ftintc020.o ftahbc020.o ftddrii030.o
>>
>>  obj-y := $(addprefix ../,$(obj-y))
>>
>> diff --git a/hw/arm/ftplat_a369soc.c b/hw/arm/ftplat_a369soc.c
>> index 7f222cb..b2da582 100644
>> --- a/hw/arm/ftplat_a369soc.c
>> +++ b/hw/arm/ftplat_a369soc.c
>> @@ -129,6 +129,14 @@ static void a369soc_chip_init(FaradaySoCState *s)
>>          fprintf(stderr, "a369soc: Unable to set soc link for FTAHBC020\n");
>>          abort();
>>      }
>> +
>> +    /* ftddrii030 */
>> +    ds = sysbus_create_simple("ftddrii030", 0x93100000, NULL);
>> +    object_property_set_link(OBJECT(ds), OBJECT(s), "soc", &local_errp);
>> +    if (local_errp) {
>> +        fprintf(stderr, "a369soc: Unable to set soc link for FTDDRII030\n");
>> +        abort();
>> +    }
>>  }
>>
>>  static void a369soc_realize(DeviceState *dev, Error **errp)
>> diff --git a/hw/ftddrii030.c b/hw/ftddrii030.c
>> new file mode 100644
>> index 0000000..158db1f
>> --- /dev/null
>> +++ b/hw/ftddrii030.c
>> @@ -0,0 +1,192 @@
>> +/*
>> + * Faraday DDRII controller
>> + *
>> + * Copyright (c) 2012 Faraday Technology
>> + * Written by Dante Su <dantesu@faraday-tech.com>
>> + *
>> + * This code is licensed under GNU GPL v2+
>> + */
>> +
>> +#include "hw/hw.h"
>> +#include "hw/sysbus.h"
>> +#include "hw/devices.h"
>> +#include "sysemu/sysemu.h"
>> +
>> +#include "hw/faraday.h"
>> +
>> +#define REG_MCR             0x00    /* memory configuration register */
>> +#define REG_MSR             0x04    /* memory status register */
>> +#define REG_REVR            0x50    /* revision register */
>> +
>> +#define MSR_INIT_OK         BIT(8)  /* initialization finished */
>> +#define MSR_CMD_MRS         BIT(0)  /* start MRS command (init. seq.) */
>> +
>> +#define CFG_REGSIZE         (REG_REVR / 4)
>> +
>> +#define TYPE_FTDDRII030     "ftddrii030"
>> +
>> +typedef struct Ftddrii030State {
>> +    /*< private >*/
>> +    SysBusDevice parent;
>> +
>> +    /*< public >*/
>> +    MemoryRegion iomem;
>> +
>> +    FaradaySoCState *soc;
>> +
>
> This new implementation still has many of the same encapsulation
> issues discussed in v8. If you go with the suggestion myself and Peter
> came up with you could make this device completely self contained - it
> should have no awareness that it is part of the Faraday SoC. If you
> model as a second MemoryRegion you can push all the Faraday specific
> foo up to the SoC. Interdiff would go something like this:
>
> -    FaradaySoCState *soc;
> +    MemoryRegion ram;
>

Sorry, but I think it might not work in that way.
Because there is an AHB remap function is Faraday SoC,
which would alter the base address of both ROM and RAM.
So the ram instance should never be a local variable to FTDDRII030 only.

The first thing solution spring up in my mind is to add a QOM link in
FTDDRII030, and then pass the ram instance as a QOM link from device model.
However it turns out that it's also not possible, since the ram
instance (MemoryRegion) is not a Object *.

So I turn out to implement both the 'AHB remap' and 'RAM
initialization' as common routines
to Faraday SoC, and also create a remappable memory region for both ROM and RAM.

>> +    /* HW register cache */
>> +    uint32_t regs[CFG_REGSIZE];
>> +} Ftddrii030State;
>> +
>> +#define FTDDRII030(obj) \
>> +    OBJECT_CHECK(Ftddrii030State, obj, TYPE_FTDDRII030)
>> +
>> +#define DDR_REG32(s, off) \
>> +    ((s)->regs[(off) / 4])
>> +
>> +static uint64_t
>> +ftddrii030_mem_read(void *opaque, hwaddr addr, unsigned size)
>> +{
>> +    Ftddrii030State *s = FTDDRII030(opaque);
>> +    FaradaySoCState *soc = s->soc;
>> +    uint64_t ret = 0;
>> +
>> +    if (soc->ram_visible) {
>> +        DDR_REG32(s, REG_MSR) |= MSR_INIT_OK;
>> +    } else {
>> +        DDR_REG32(s, REG_MSR) &= ~MSR_INIT_OK;
>> +    }
>> +
>> +    switch (addr) {
>> +    case REG_MCR ... REG_REVR - 4:
>> +        ret = DDR_REG32(s, addr);
>> +        break;
>> +    case REG_REVR:
>> +        ret = 0x100;    /* rev. = 0.1.0 */
>> +        break;
>> +    default:
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +            "ftddrii030: undefined memory access@%#" HWADDR_PRIx "\n", addr);
>> +        break;
>> +    }
>> +
>> +    return ret;
>> +}
>> +
>> +static void
>> +ftddrii030_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
>> +{
>> +    Ftddrii030State *s = FTDDRII030(opaque);
>> +    FaradaySoCState *soc = s->soc;
>> +
>> +    switch (addr) {
>> +    case REG_MCR:
>> +        DDR_REG32(s, addr) = (uint32_t)val & 0xffff;
>> +        break;
>> +    case REG_MSR:
>> +        if (!soc->ram_visible && (val & MSR_CMD_MRS)) {
>> +            val &= ~MSR_CMD_MRS;
>> +            faraday_soc_ram_setup(soc, true);
>
> This function (added earlier in this series) then becomes local to
> this device model and the memory_region_add_subregion() happens with
> the new ram Memory region instead. AFAICT that function only requires
> the ram size which strikes me as something that should be a QOM
> property to the device.
>

Please see the comments above.

>> +        }
>> +        DDR_REG32(s, addr) = (uint32_t)val;
>> +        break;
>> +    /* SDRAM Timing, ECC ...etc. */
>> +    case REG_MSR + 4 ... REG_REVR - 4:
>> +        DDR_REG32(s, addr) = (uint32_t)val;
>> +        break;
>> +    case REG_REVR:
>> +        break;
>> +    default:
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +            "ftddrii030: undefined memory access@%#" HWADDR_PRIx "\n", addr);
>> +        break;
>> +    }
>> +}
>> +
>> +static const MemoryRegionOps mmio_ops = {
>> +    .read  = ftddrii030_mem_read,
>> +    .write = ftddrii030_mem_write,
>> +    .endianness = DEVICE_LITTLE_ENDIAN,
>> +    .valid = {
>> +        .min_access_size = 4,
>> +        .max_access_size = 4,
>> +    }
>> +};
>> +
>> +static void ftddrii030_reset(DeviceState *ds)
>> +{
>> +    Ftddrii030State *s = FTDDRII030(SYS_BUS_DEVICE(ds));
>> +    Error *local_errp = NULL;
>> +
>> +    s->soc = FARADAY_SOC(object_property_get_link(OBJECT(s),
>> +                                                  "soc",
>> +                                                  &local_errp));
>> +    if (local_errp) {
>> +        fprintf(stderr, "ftahbc020: Unable to get soc link\n");
>> +        abort();
>> +    }
>> +
>> +    faraday_soc_ram_setup(s->soc, false);
>> +    memset(s->regs, 0, sizeof(s->regs));
>> +}
>> +
>> +static void ftddrii030_realize(DeviceState *dev, Error **errp)
>> +{
>> +    Ftddrii030State *s = FTDDRII030(dev);
>> +    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
>> +
>> +    memory_region_init_io(&s->iomem,
>> +                          &mmio_ops,
>> +                          s,
>> +                          TYPE_FTDDRII030,
>> +                          0x1000);
>> +    sysbus_init_mmio(sbd, &s->iomem);
>
> Init the new ram memory region here.
>

Please see the comments above.

> I could use a second opinion on all this QOM stuff though - you are on
> the bleeding edge with the QOM SoC stuff. I suggest giving it some
> list time for others to reply before a remake.
>

Got it, thanks.

> Regards,
> Peter
>
>> +}
>> +
>> +static const VMStateDescription vmstate_ftddrii030 = {
>> +    .name = TYPE_FTDDRII030,
>> +    .version_id = 1,
>> +    .minimum_version_id = 1,
>> +    .minimum_version_id_old = 1,
>> +    .fields = (VMStateField[]) {
>> +        VMSTATE_UINT32_ARRAY(regs, Ftddrii030State, CFG_REGSIZE),
>> +        VMSTATE_END_OF_LIST(),
>> +    }
>> +};
>> +
>> +static void ftddrii030_instance_init(Object *obj)
>> +{
>> +    Ftddrii030State *s = FTDDRII030(obj);
>> +
>> +    object_property_add_link(obj,
>> +                             "soc",
>> +                             TYPE_FARADAY_SOC,
>> +                             (Object **) &s->soc,
>> +                             NULL);
>> +}
>> +
>> +static void ftddrii030_class_init(ObjectClass *klass, void *data)
>> +{
>> +    DeviceClass *dc = DEVICE_CLASS(klass);
>> +
>> +    dc->desc  = TYPE_FTDDRII030;
>> +    dc->vmsd  = &vmstate_ftddrii030;
>> +    dc->reset = ftddrii030_reset;
>> +    dc->realize = ftddrii030_realize;
>> +    dc->no_user = 1;
>> +}
>> +
>> +static const TypeInfo ftddrii030_info = {
>> +    .name          = TYPE_FTDDRII030,
>> +    .parent        = TYPE_SYS_BUS_DEVICE,
>> +    .instance_size = sizeof(Ftddrii030State),
>> +    .instance_init = ftddrii030_instance_init,
>> +    .class_init    = ftddrii030_class_init,
>> +};
>> +
>> +static void ftddrii030_register_types(void)
>> +{
>> +    type_register_static(&ftddrii030_info);
>> +}
>> +
>> +type_init(ftddrii030_register_types)
>> --
>> 1.7.9.5
>>
>>



--
Best wishes,
Kuo-Jung Su

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Qemu-devel] [PATCH v9 05/24] hw/arm: add FTDDRII030 DDRII controller support
  2013-03-28  3:24     ` Kuo-Jung Su
@ 2013-03-28  3:58       ` Peter Crosthwaite
  2013-03-28  5:28         ` Kuo-Jung Su
  0 siblings, 1 reply; 35+ messages in thread
From: Peter Crosthwaite @ 2013-03-28  3:58 UTC (permalink / raw)
  To: Kuo-Jung Su
  Cc: Peter Maydell, Igor Mitsyanko, qemu-devel, Blue Swirl,
	Paul Brook, Kuo-Jung Su, Andreas, fred konrad

Hi Kuo Jung,

On Thu, Mar 28, 2013 at 1:24 PM, Kuo-Jung Su <dantesu@gmail.com> wrote:
> 2013/3/28 Peter Crosthwaite <peter.crosthwaite@xilinx.com>:
>> Hi Kuo-Jung,
>>
>> On Mon, Mar 25, 2013 at 10:09 PM, Kuo-Jung Su <dantesu@gmail.com> wrote:
>>> From: Kuo-Jung Su <dantesu@faraday-tech.com>
>>>
>>> The FTDDRII030 is a DDRII SDRAM controller which is responsible for
>>> SDRAM initialization.
>>>
>>> Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
>>> ---
>>>  hw/arm/Makefile.objs    |    2 +-
>>>  hw/arm/ftplat_a369soc.c |    8 ++
>>>  hw/ftddrii030.c         |  192 +++++++++++++++++++++++++++++++++++++++++++++++
>>>  3 files changed, 201 insertions(+), 1 deletion(-)
>>>  create mode 100644 hw/ftddrii030.c
>>>
>>> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
>>> index b2fa20f..e774962 100644
>>> --- a/hw/arm/Makefile.objs
>>> +++ b/hw/arm/Makefile.objs
>>> @@ -24,7 +24,7 @@ obj-y += framebuffer.o
>>>  obj-y += strongarm.o
>>>  obj-y += imx_serial.o imx_ccm.o imx_timer.o imx_avic.o
>>>  obj-$(CONFIG_KVM) += kvm/arm_gic.o
>>> -obj-y += ftintc020.o ftahbc020.o
>>> +obj-y += ftintc020.o ftahbc020.o ftddrii030.o
>>>
>>>  obj-y := $(addprefix ../,$(obj-y))
>>>
>>> diff --git a/hw/arm/ftplat_a369soc.c b/hw/arm/ftplat_a369soc.c
>>> index 7f222cb..b2da582 100644
>>> --- a/hw/arm/ftplat_a369soc.c
>>> +++ b/hw/arm/ftplat_a369soc.c
>>> @@ -129,6 +129,14 @@ static void a369soc_chip_init(FaradaySoCState *s)
>>>          fprintf(stderr, "a369soc: Unable to set soc link for FTAHBC020\n");
>>>          abort();
>>>      }
>>> +
>>> +    /* ftddrii030 */
>>> +    ds = sysbus_create_simple("ftddrii030", 0x93100000, NULL);
>>> +    object_property_set_link(OBJECT(ds), OBJECT(s), "soc", &local_errp);
>>> +    if (local_errp) {
>>> +        fprintf(stderr, "a369soc: Unable to set soc link for FTDDRII030\n");
>>> +        abort();
>>> +    }
>>>  }
>>>
>>>  static void a369soc_realize(DeviceState *dev, Error **errp)
>>> diff --git a/hw/ftddrii030.c b/hw/ftddrii030.c
>>> new file mode 100644
>>> index 0000000..158db1f
>>> --- /dev/null
>>> +++ b/hw/ftddrii030.c
>>> @@ -0,0 +1,192 @@
>>> +/*
>>> + * Faraday DDRII controller
>>> + *
>>> + * Copyright (c) 2012 Faraday Technology
>>> + * Written by Dante Su <dantesu@faraday-tech.com>
>>> + *
>>> + * This code is licensed under GNU GPL v2+
>>> + */
>>> +
>>> +#include "hw/hw.h"
>>> +#include "hw/sysbus.h"
>>> +#include "hw/devices.h"
>>> +#include "sysemu/sysemu.h"
>>> +
>>> +#include "hw/faraday.h"
>>> +
>>> +#define REG_MCR             0x00    /* memory configuration register */
>>> +#define REG_MSR             0x04    /* memory status register */
>>> +#define REG_REVR            0x50    /* revision register */
>>> +
>>> +#define MSR_INIT_OK         BIT(8)  /* initialization finished */
>>> +#define MSR_CMD_MRS         BIT(0)  /* start MRS command (init. seq.) */
>>> +
>>> +#define CFG_REGSIZE         (REG_REVR / 4)
>>> +
>>> +#define TYPE_FTDDRII030     "ftddrii030"
>>> +
>>> +typedef struct Ftddrii030State {
>>> +    /*< private >*/
>>> +    SysBusDevice parent;
>>> +
>>> +    /*< public >*/
>>> +    MemoryRegion iomem;
>>> +
>>> +    FaradaySoCState *soc;
>>> +
>>
>> This new implementation still has many of the same encapsulation
>> issues discussed in v8. If you go with the suggestion myself and Peter
>> came up with you could make this device completely self contained - it
>> should have no awareness that it is part of the Faraday SoC. If you
>> model as a second MemoryRegion you can push all the Faraday specific
>> foo up to the SoC. Interdiff would go something like this:
>>
>> -    FaradaySoCState *soc;
>> +    MemoryRegion ram;
>>
>
> Sorry, but I think it might not work in that way.
> Because there is an AHB remap function is Faraday SoC,
> which would alter the base address of both ROM and RAM.
> So the ram instance should never be a local variable to FTDDRII030 only.
>

The new RAM region would have no awareness of its base address. It is
just a remappable container. The SoC can still do the remapping part.
This device model is then oblivous to the fact that the SoC and other
devices are remapping its base address.

> The first thing solution spring up in my mind is to add a QOM link in
> FTDDRII030, and then pass the ram instance as a QOM link from device model.
> However it turns out that it's also not possible, since the ram
> instance (MemoryRegion) is not a Object *.
>

calling sysbus_mmio_get_region() on the SoC level should do the trick.
If you init the container region as proposed, this function can be
used by the SoC to get a handle to it.

I think we are on similar timezones (+10:00 here) if you want to try
and clarify on the IRC sometime today as well.

Regards,
Peter

> So I turn out to implement both the 'AHB remap' and 'RAM
> initialization' as common routines
> to Faraday SoC, and also create a remappable memory region for both ROM and RAM.
>
>>> +    /* HW register cache */
>>> +    uint32_t regs[CFG_REGSIZE];
>>> +} Ftddrii030State;
>>> +
>>> +#define FTDDRII030(obj) \
>>> +    OBJECT_CHECK(Ftddrii030State, obj, TYPE_FTDDRII030)
>>> +
>>> +#define DDR_REG32(s, off) \
>>> +    ((s)->regs[(off) / 4])
>>> +
>>> +static uint64_t
>>> +ftddrii030_mem_read(void *opaque, hwaddr addr, unsigned size)
>>> +{
>>> +    Ftddrii030State *s = FTDDRII030(opaque);
>>> +    FaradaySoCState *soc = s->soc;
>>> +    uint64_t ret = 0;
>>> +
>>> +    if (soc->ram_visible) {
>>> +        DDR_REG32(s, REG_MSR) |= MSR_INIT_OK;
>>> +    } else {
>>> +        DDR_REG32(s, REG_MSR) &= ~MSR_INIT_OK;
>>> +    }
>>> +
>>> +    switch (addr) {
>>> +    case REG_MCR ... REG_REVR - 4:
>>> +        ret = DDR_REG32(s, addr);
>>> +        break;
>>> +    case REG_REVR:
>>> +        ret = 0x100;    /* rev. = 0.1.0 */
>>> +        break;
>>> +    default:
>>> +        qemu_log_mask(LOG_GUEST_ERROR,
>>> +            "ftddrii030: undefined memory access@%#" HWADDR_PRIx "\n", addr);
>>> +        break;
>>> +    }
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static void
>>> +ftddrii030_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
>>> +{
>>> +    Ftddrii030State *s = FTDDRII030(opaque);
>>> +    FaradaySoCState *soc = s->soc;
>>> +
>>> +    switch (addr) {
>>> +    case REG_MCR:
>>> +        DDR_REG32(s, addr) = (uint32_t)val & 0xffff;
>>> +        break;
>>> +    case REG_MSR:
>>> +        if (!soc->ram_visible && (val & MSR_CMD_MRS)) {
>>> +            val &= ~MSR_CMD_MRS;
>>> +            faraday_soc_ram_setup(soc, true);
>>
>> This function (added earlier in this series) then becomes local to
>> this device model and the memory_region_add_subregion() happens with
>> the new ram Memory region instead. AFAICT that function only requires
>> the ram size which strikes me as something that should be a QOM
>> property to the device.
>>
>
> Please see the comments above.
>
>>> +        }
>>> +        DDR_REG32(s, addr) = (uint32_t)val;
>>> +        break;
>>> +    /* SDRAM Timing, ECC ...etc. */
>>> +    case REG_MSR + 4 ... REG_REVR - 4:
>>> +        DDR_REG32(s, addr) = (uint32_t)val;
>>> +        break;
>>> +    case REG_REVR:
>>> +        break;
>>> +    default:
>>> +        qemu_log_mask(LOG_GUEST_ERROR,
>>> +            "ftddrii030: undefined memory access@%#" HWADDR_PRIx "\n", addr);
>>> +        break;
>>> +    }
>>> +}
>>> +
>>> +static const MemoryRegionOps mmio_ops = {
>>> +    .read  = ftddrii030_mem_read,
>>> +    .write = ftddrii030_mem_write,
>>> +    .endianness = DEVICE_LITTLE_ENDIAN,
>>> +    .valid = {
>>> +        .min_access_size = 4,
>>> +        .max_access_size = 4,
>>> +    }
>>> +};
>>> +
>>> +static void ftddrii030_reset(DeviceState *ds)
>>> +{
>>> +    Ftddrii030State *s = FTDDRII030(SYS_BUS_DEVICE(ds));
>>> +    Error *local_errp = NULL;
>>> +
>>> +    s->soc = FARADAY_SOC(object_property_get_link(OBJECT(s),
>>> +                                                  "soc",
>>> +                                                  &local_errp));
>>> +    if (local_errp) {
>>> +        fprintf(stderr, "ftahbc020: Unable to get soc link\n");
>>> +        abort();
>>> +    }
>>> +
>>> +    faraday_soc_ram_setup(s->soc, false);
>>> +    memset(s->regs, 0, sizeof(s->regs));
>>> +}
>>> +
>>> +static void ftddrii030_realize(DeviceState *dev, Error **errp)
>>> +{
>>> +    Ftddrii030State *s = FTDDRII030(dev);
>>> +    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
>>> +
>>> +    memory_region_init_io(&s->iomem,
>>> +                          &mmio_ops,
>>> +                          s,
>>> +                          TYPE_FTDDRII030,
>>> +                          0x1000);
>>> +    sysbus_init_mmio(sbd, &s->iomem);
>>
>> Init the new ram memory region here.
>>
>
> Please see the comments above.
>
>> I could use a second opinion on all this QOM stuff though - you are on
>> the bleeding edge with the QOM SoC stuff. I suggest giving it some
>> list time for others to reply before a remake.
>>
>
> Got it, thanks.
>
>> Regards,
>> Peter
>>
>>> +}
>>> +
>>> +static const VMStateDescription vmstate_ftddrii030 = {
>>> +    .name = TYPE_FTDDRII030,
>>> +    .version_id = 1,
>>> +    .minimum_version_id = 1,
>>> +    .minimum_version_id_old = 1,
>>> +    .fields = (VMStateField[]) {
>>> +        VMSTATE_UINT32_ARRAY(regs, Ftddrii030State, CFG_REGSIZE),
>>> +        VMSTATE_END_OF_LIST(),
>>> +    }
>>> +};
>>> +
>>> +static void ftddrii030_instance_init(Object *obj)
>>> +{
>>> +    Ftddrii030State *s = FTDDRII030(obj);
>>> +
>>> +    object_property_add_link(obj,
>>> +                             "soc",
>>> +                             TYPE_FARADAY_SOC,
>>> +                             (Object **) &s->soc,
>>> +                             NULL);
>>> +}
>>> +
>>> +static void ftddrii030_class_init(ObjectClass *klass, void *data)
>>> +{
>>> +    DeviceClass *dc = DEVICE_CLASS(klass);
>>> +
>>> +    dc->desc  = TYPE_FTDDRII030;
>>> +    dc->vmsd  = &vmstate_ftddrii030;
>>> +    dc->reset = ftddrii030_reset;
>>> +    dc->realize = ftddrii030_realize;
>>> +    dc->no_user = 1;
>>> +}
>>> +
>>> +static const TypeInfo ftddrii030_info = {
>>> +    .name          = TYPE_FTDDRII030,
>>> +    .parent        = TYPE_SYS_BUS_DEVICE,
>>> +    .instance_size = sizeof(Ftddrii030State),
>>> +    .instance_init = ftddrii030_instance_init,
>>> +    .class_init    = ftddrii030_class_init,
>>> +};
>>> +
>>> +static void ftddrii030_register_types(void)
>>> +{
>>> +    type_register_static(&ftddrii030_info);
>>> +}
>>> +
>>> +type_init(ftddrii030_register_types)
>>> --
>>> 1.7.9.5
>>>
>>>
>
>
>
> --
> Best wishes,
> Kuo-Jung Su
>

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Qemu-devel] [PATCH v9 05/24] hw/arm: add FTDDRII030 DDRII controller support
  2013-03-28  3:58       ` Peter Crosthwaite
@ 2013-03-28  5:28         ` Kuo-Jung Su
  0 siblings, 0 replies; 35+ messages in thread
From: Kuo-Jung Su @ 2013-03-28  5:28 UTC (permalink / raw)
  To: Peter Crosthwaite
  Cc: Peter Maydell, Igor Mitsyanko, qemu-devel, Blue Swirl,
	Paul Brook, Kuo-Jung Su, Andreas, fred konrad

2013/3/28 Peter Crosthwaite <peter.crosthwaite@xilinx.com>:
> Hi Kuo Jung,
>
> On Thu, Mar 28, 2013 at 1:24 PM, Kuo-Jung Su <dantesu@gmail.com> wrote:
>> 2013/3/28 Peter Crosthwaite <peter.crosthwaite@xilinx.com>:
>>> Hi Kuo-Jung,
>>>
>>> On Mon, Mar 25, 2013 at 10:09 PM, Kuo-Jung Su <dantesu@gmail.com> wrote:
>>>> From: Kuo-Jung Su <dantesu@faraday-tech.com>
>>>>
>>>> The FTDDRII030 is a DDRII SDRAM controller which is responsible for
>>>> SDRAM initialization.
>>>>
>>>> Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
>>>> ---
>>>>  hw/arm/Makefile.objs    |    2 +-
>>>>  hw/arm/ftplat_a369soc.c |    8 ++
>>>>  hw/ftddrii030.c         |  192 +++++++++++++++++++++++++++++++++++++++++++++++
>>>>  3 files changed, 201 insertions(+), 1 deletion(-)
>>>>  create mode 100644 hw/ftddrii030.c
>>>>
>>>> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
>>>> index b2fa20f..e774962 100644
>>>> --- a/hw/arm/Makefile.objs
>>>> +++ b/hw/arm/Makefile.objs
>>>> @@ -24,7 +24,7 @@ obj-y += framebuffer.o
>>>>  obj-y += strongarm.o
>>>>  obj-y += imx_serial.o imx_ccm.o imx_timer.o imx_avic.o
>>>>  obj-$(CONFIG_KVM) += kvm/arm_gic.o
>>>> -obj-y += ftintc020.o ftahbc020.o
>>>> +obj-y += ftintc020.o ftahbc020.o ftddrii030.o
>>>>
>>>>  obj-y := $(addprefix ../,$(obj-y))
>>>>
>>>> diff --git a/hw/arm/ftplat_a369soc.c b/hw/arm/ftplat_a369soc.c
>>>> index 7f222cb..b2da582 100644
>>>> --- a/hw/arm/ftplat_a369soc.c
>>>> +++ b/hw/arm/ftplat_a369soc.c
>>>> @@ -129,6 +129,14 @@ static void a369soc_chip_init(FaradaySoCState *s)
>>>>          fprintf(stderr, "a369soc: Unable to set soc link for FTAHBC020\n");
>>>>          abort();
>>>>      }
>>>> +
>>>> +    /* ftddrii030 */
>>>> +    ds = sysbus_create_simple("ftddrii030", 0x93100000, NULL);
>>>> +    object_property_set_link(OBJECT(ds), OBJECT(s), "soc", &local_errp);
>>>> +    if (local_errp) {
>>>> +        fprintf(stderr, "a369soc: Unable to set soc link for FTDDRII030\n");
>>>> +        abort();
>>>> +    }
>>>>  }
>>>>
>>>>  static void a369soc_realize(DeviceState *dev, Error **errp)
>>>> diff --git a/hw/ftddrii030.c b/hw/ftddrii030.c
>>>> new file mode 100644
>>>> index 0000000..158db1f
>>>> --- /dev/null
>>>> +++ b/hw/ftddrii030.c
>>>> @@ -0,0 +1,192 @@
>>>> +/*
>>>> + * Faraday DDRII controller
>>>> + *
>>>> + * Copyright (c) 2012 Faraday Technology
>>>> + * Written by Dante Su <dantesu@faraday-tech.com>
>>>> + *
>>>> + * This code is licensed under GNU GPL v2+
>>>> + */
>>>> +
>>>> +#include "hw/hw.h"
>>>> +#include "hw/sysbus.h"
>>>> +#include "hw/devices.h"
>>>> +#include "sysemu/sysemu.h"
>>>> +
>>>> +#include "hw/faraday.h"
>>>> +
>>>> +#define REG_MCR             0x00    /* memory configuration register */
>>>> +#define REG_MSR             0x04    /* memory status register */
>>>> +#define REG_REVR            0x50    /* revision register */
>>>> +
>>>> +#define MSR_INIT_OK         BIT(8)  /* initialization finished */
>>>> +#define MSR_CMD_MRS         BIT(0)  /* start MRS command (init. seq.) */
>>>> +
>>>> +#define CFG_REGSIZE         (REG_REVR / 4)
>>>> +
>>>> +#define TYPE_FTDDRII030     "ftddrii030"
>>>> +
>>>> +typedef struct Ftddrii030State {
>>>> +    /*< private >*/
>>>> +    SysBusDevice parent;
>>>> +
>>>> +    /*< public >*/
>>>> +    MemoryRegion iomem;
>>>> +
>>>> +    FaradaySoCState *soc;
>>>> +
>>>
>>> This new implementation still has many of the same encapsulation
>>> issues discussed in v8. If you go with the suggestion myself and Peter
>>> came up with you could make this device completely self contained - it
>>> should have no awareness that it is part of the Faraday SoC. If you
>>> model as a second MemoryRegion you can push all the Faraday specific
>>> foo up to the SoC. Interdiff would go something like this:
>>>
>>> -    FaradaySoCState *soc;
>>> +    MemoryRegion ram;
>>>
>>
>> Sorry, but I think it might not work in that way.
>> Because there is an AHB remap function is Faraday SoC,
>> which would alter the base address of both ROM and RAM.
>> So the ram instance should never be a local variable to FTDDRII030 only.
>>
>
> The new RAM region would have no awareness of its base address. It is
> just a remappable container. The SoC can still do the remapping part.
> This device model is then oblivous to the fact that the SoC and other
> devices are remapping its base address.
>
>> The first thing solution spring up in my mind is to add a QOM link in
>> FTDDRII030, and then pass the ram instance as a QOM link from device model.
>> However it turns out that it's also not possible, since the ram
>> instance (MemoryRegion) is not a Object *.
>>
>
> calling sysbus_mmio_get_region() on the SoC level should do the trick.
> If you init the container region as proposed, this function can be
> used by the SoC to get a handle to it.
>

It sounds like pretty good idea to me, I think I know what to do now.
Thanks

> I think we are on similar timezones (+10:00 here) if you want to try
> and clarify on the IRC sometime today as well.
>

Thanks for the invitation, but the IRQ is banned in my company...

> Regards,
> Peter
>
>> So I turn out to implement both the 'AHB remap' and 'RAM
>> initialization' as common routines
>> to Faraday SoC, and also create a remappable memory region for both ROM and RAM.
>>
>>>> +    /* HW register cache */
>>>> +    uint32_t regs[CFG_REGSIZE];
>>>> +} Ftddrii030State;
>>>> +
>>>> +#define FTDDRII030(obj) \
>>>> +    OBJECT_CHECK(Ftddrii030State, obj, TYPE_FTDDRII030)
>>>> +
>>>> +#define DDR_REG32(s, off) \
>>>> +    ((s)->regs[(off) / 4])
>>>> +
>>>> +static uint64_t
>>>> +ftddrii030_mem_read(void *opaque, hwaddr addr, unsigned size)
>>>> +{
>>>> +    Ftddrii030State *s = FTDDRII030(opaque);
>>>> +    FaradaySoCState *soc = s->soc;
>>>> +    uint64_t ret = 0;
>>>> +
>>>> +    if (soc->ram_visible) {
>>>> +        DDR_REG32(s, REG_MSR) |= MSR_INIT_OK;
>>>> +    } else {
>>>> +        DDR_REG32(s, REG_MSR) &= ~MSR_INIT_OK;
>>>> +    }
>>>> +
>>>> +    switch (addr) {
>>>> +    case REG_MCR ... REG_REVR - 4:
>>>> +        ret = DDR_REG32(s, addr);
>>>> +        break;
>>>> +    case REG_REVR:
>>>> +        ret = 0x100;    /* rev. = 0.1.0 */
>>>> +        break;
>>>> +    default:
>>>> +        qemu_log_mask(LOG_GUEST_ERROR,
>>>> +            "ftddrii030: undefined memory access@%#" HWADDR_PRIx "\n", addr);
>>>> +        break;
>>>> +    }
>>>> +
>>>> +    return ret;
>>>> +}
>>>> +
>>>> +static void
>>>> +ftddrii030_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
>>>> +{
>>>> +    Ftddrii030State *s = FTDDRII030(opaque);
>>>> +    FaradaySoCState *soc = s->soc;
>>>> +
>>>> +    switch (addr) {
>>>> +    case REG_MCR:
>>>> +        DDR_REG32(s, addr) = (uint32_t)val & 0xffff;
>>>> +        break;
>>>> +    case REG_MSR:
>>>> +        if (!soc->ram_visible && (val & MSR_CMD_MRS)) {
>>>> +            val &= ~MSR_CMD_MRS;
>>>> +            faraday_soc_ram_setup(soc, true);
>>>
>>> This function (added earlier in this series) then becomes local to
>>> this device model and the memory_region_add_subregion() happens with
>>> the new ram Memory region instead. AFAICT that function only requires
>>> the ram size which strikes me as something that should be a QOM
>>> property to the device.
>>>
>>
>> Please see the comments above.
>>
>>>> +        }
>>>> +        DDR_REG32(s, addr) = (uint32_t)val;
>>>> +        break;
>>>> +    /* SDRAM Timing, ECC ...etc. */
>>>> +    case REG_MSR + 4 ... REG_REVR - 4:
>>>> +        DDR_REG32(s, addr) = (uint32_t)val;
>>>> +        break;
>>>> +    case REG_REVR:
>>>> +        break;
>>>> +    default:
>>>> +        qemu_log_mask(LOG_GUEST_ERROR,
>>>> +            "ftddrii030: undefined memory access@%#" HWADDR_PRIx "\n", addr);
>>>> +        break;
>>>> +    }
>>>> +}
>>>> +
>>>> +static const MemoryRegionOps mmio_ops = {
>>>> +    .read  = ftddrii030_mem_read,
>>>> +    .write = ftddrii030_mem_write,
>>>> +    .endianness = DEVICE_LITTLE_ENDIAN,
>>>> +    .valid = {
>>>> +        .min_access_size = 4,
>>>> +        .max_access_size = 4,
>>>> +    }
>>>> +};
>>>> +
>>>> +static void ftddrii030_reset(DeviceState *ds)
>>>> +{
>>>> +    Ftddrii030State *s = FTDDRII030(SYS_BUS_DEVICE(ds));
>>>> +    Error *local_errp = NULL;
>>>> +
>>>> +    s->soc = FARADAY_SOC(object_property_get_link(OBJECT(s),
>>>> +                                                  "soc",
>>>> +                                                  &local_errp));
>>>> +    if (local_errp) {
>>>> +        fprintf(stderr, "ftahbc020: Unable to get soc link\n");
>>>> +        abort();
>>>> +    }
>>>> +
>>>> +    faraday_soc_ram_setup(s->soc, false);
>>>> +    memset(s->regs, 0, sizeof(s->regs));
>>>> +}
>>>> +
>>>> +static void ftddrii030_realize(DeviceState *dev, Error **errp)
>>>> +{
>>>> +    Ftddrii030State *s = FTDDRII030(dev);
>>>> +    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
>>>> +
>>>> +    memory_region_init_io(&s->iomem,
>>>> +                          &mmio_ops,
>>>> +                          s,
>>>> +                          TYPE_FTDDRII030,
>>>> +                          0x1000);
>>>> +    sysbus_init_mmio(sbd, &s->iomem);
>>>
>>> Init the new ram memory region here.
>>>
>>
>> Please see the comments above.
>>
>>> I could use a second opinion on all this QOM stuff though - you are on
>>> the bleeding edge with the QOM SoC stuff. I suggest giving it some
>>> list time for others to reply before a remake.
>>>
>>
>> Got it, thanks.
>>
>>> Regards,
>>> Peter
>>>
>>>> +}
>>>> +
>>>> +static const VMStateDescription vmstate_ftddrii030 = {
>>>> +    .name = TYPE_FTDDRII030,
>>>> +    .version_id = 1,
>>>> +    .minimum_version_id = 1,
>>>> +    .minimum_version_id_old = 1,
>>>> +    .fields = (VMStateField[]) {
>>>> +        VMSTATE_UINT32_ARRAY(regs, Ftddrii030State, CFG_REGSIZE),
>>>> +        VMSTATE_END_OF_LIST(),
>>>> +    }
>>>> +};
>>>> +
>>>> +static void ftddrii030_instance_init(Object *obj)
>>>> +{
>>>> +    Ftddrii030State *s = FTDDRII030(obj);
>>>> +
>>>> +    object_property_add_link(obj,
>>>> +                             "soc",
>>>> +                             TYPE_FARADAY_SOC,
>>>> +                             (Object **) &s->soc,
>>>> +                             NULL);
>>>> +}
>>>> +
>>>> +static void ftddrii030_class_init(ObjectClass *klass, void *data)
>>>> +{
>>>> +    DeviceClass *dc = DEVICE_CLASS(klass);
>>>> +
>>>> +    dc->desc  = TYPE_FTDDRII030;
>>>> +    dc->vmsd  = &vmstate_ftddrii030;
>>>> +    dc->reset = ftddrii030_reset;
>>>> +    dc->realize = ftddrii030_realize;
>>>> +    dc->no_user = 1;
>>>> +}
>>>> +
>>>> +static const TypeInfo ftddrii030_info = {
>>>> +    .name          = TYPE_FTDDRII030,
>>>> +    .parent        = TYPE_SYS_BUS_DEVICE,
>>>> +    .instance_size = sizeof(Ftddrii030State),
>>>> +    .instance_init = ftddrii030_instance_init,
>>>> +    .class_init    = ftddrii030_class_init,
>>>> +};
>>>> +
>>>> +static void ftddrii030_register_types(void)
>>>> +{
>>>> +    type_register_static(&ftddrii030_info);
>>>> +}
>>>> +
>>>> +type_init(ftddrii030_register_types)
>>>> --
>>>> 1.7.9.5
>>>>
>>>>
>>
>>
>>
>> --
>> Best wishes,
>> Kuo-Jung Su
>>



--
Best wishes,
Kuo-Jung Su

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Qemu-devel] [PATCH v9 24/24] hw/arm: add FTSPI020 SPI flash controller support
  2013-03-25 12:10 ` [Qemu-devel] [PATCH v9 24/24] hw/arm: add FTSPI020 SPI flash controller support Kuo-Jung Su
@ 2013-03-29  0:02   ` Peter Crosthwaite
  2013-03-29  7:15     ` Kuo-Jung Su
  0 siblings, 1 reply; 35+ messages in thread
From: Peter Crosthwaite @ 2013-03-29  0:02 UTC (permalink / raw)
  To: Kuo-Jung Su
  Cc: Peter Maydell, i.mitsyanko, qemu-devel, Blue Swirl, Paul Brook,
	Kuo-Jung Su, Andreas, fred.konrad

Hi Kuo-Jung,

On Mon, Mar 25, 2013 at 10:10 PM, Kuo-Jung Su <dantesu@gmail.com> wrote:
> From: Kuo-Jung Su <dantesu@faraday-tech.com>
>
> The FTSPI020 is an integrated SPI Flash controller
> which supports up to 4 flash chips.
>
> Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
> ---
>  hw/arm/Makefile.objs |    2 +-
>  hw/arm/ftplat_a369.c |   16 +++
>  hw/ftspi020.c        |  341 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  hw/ftspi020.h        |   81 ++++++++++++
>  4 files changed, 439 insertions(+), 1 deletion(-)
>  create mode 100644 hw/ftspi020.c
>  create mode 100644 hw/ftspi020.h
>
> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
> index bcfb70a..a34ca41 100644
> --- a/hw/arm/Makefile.objs
> +++ b/hw/arm/Makefile.objs
> @@ -27,7 +27,7 @@ obj-$(CONFIG_KVM) += kvm/arm_gic.o
>  obj-y += ftintc020.o ftahbc020.o ftddrii030.o ftpwmtmr010.o ftwdt010.o \
>                  ftrtc011.o ftdmac020.o ftapbbrg020.o ftnandc021.o fti2c010.o \
>                  ftssp010.o ftgmac100.o ftlcdc200.o fttsc010.o ftsdc010.o \
> -                ftmac110.o fttmr010.o
> +                ftmac110.o fttmr010.o ftspi020.o
>
>  obj-y := $(addprefix ../,$(obj-y))
>
> diff --git a/hw/arm/ftplat_a369.c b/hw/arm/ftplat_a369.c
> index f22e2ca..77cd44d 100644
> --- a/hw/arm/ftplat_a369.c
> +++ b/hw/arm/ftplat_a369.c
> @@ -125,6 +125,22 @@ static void a369_board_init(QEMUMachineInitArgs *args)
>      sysbus_connect_irq(SYS_BUS_DEVICE(ds), 1, s->pic[6]);
>      sysbus_connect_irq(SYS_BUS_DEVICE(ds), 2, s->pic[7]);
>
> +    /* SPI: FTSPI020 */
> +    ds = sysbus_create_simple("ftspi020", 0xC0000000, s->pic[4]);
> +    s->spi_fl[0] = ds;
> +
> +    /* Attach the spi flash to ftspi020.0 */
> +    nr_flash = 1;
> +    for (i = 0; i < nr_flash; i++) {
> +        SSIBus *ssi = (SSIBus *)qdev_get_child_bus(s->spi_fl[0], "spi");
> +        DeviceState *fl = ssi_create_slave_no_init(ssi, "w25q64");

You qdev_init straight away, so you can just call ssi_create_slave.
This is just a hangover from my recent m25p80 cleanup which made the
original reason for create_slave_no_init redundant. I'll have to send
some cleanup patches for my own machine models with the equivalent
change. s/ssi_create_slave_no_init/ssi_create_slave and ...

> +        qemu_irq cs_line;
> +
> +        qdev_init_nofail(fl);

... dump this.

Regards,
Peter

> +        cs_line = qdev_get_gpio_in(fl, 0);
> +        sysbus_connect_irq(SYS_BUS_DEVICE(s->spi_fl[0]), i + 1, cs_line);
> +    }
> +
>      /* System start-up */
>
>      if (args->kernel_filename) {
> diff --git a/hw/ftspi020.c b/hw/ftspi020.c
> new file mode 100644
> index 0000000..a7253bd
> --- /dev/null
> +++ b/hw/ftspi020.c
> @@ -0,0 +1,341 @@
> +/*
> + * Faraday FTSPI020 Flash Controller
> + *
> + * Copyright (c) 2012 Faraday Technology
> + * Written by Dante Su <dantesu@faraday-tech.com>
> + *
> + * This code is licensed under GNU GPL v2+.
> + */
> +
> +#include "hw/hw.h"
> +#include "hw/sysbus.h"
> +#include "hw/ssi.h"
> +#include "sysemu/sysemu.h"
> +
> +#include "hw/ftspi020.h"
> +
> +#define TYPE_FTSPI020   "ftspi020"
> +
> +typedef struct Ftspi020State {
> +    /*< private >*/
> +    SysBusDevice parent;
> +
> +    /*< public >*/
> +    MemoryRegion iomem;
> +    qemu_irq irq;
> +
> +    /* DMA hardware handshake */
> +    qemu_irq req;
> +
> +    SSIBus *spi;
> +    qemu_irq *cs_lines;
> +
> +    int wip;    /* SPI Flash Status: Write In Progress BIT shift */
> +
> +    /* HW register caches */
> +    uint32_t cmd[4];
> +    uint32_t ctrl;
> +    uint32_t timing;
> +    uint32_t icr;
> +    uint32_t isr;
> +    uint32_t rdsr;
> +} Ftspi020State;
> +
> +#define FTSPI020(obj) \
> +    OBJECT_CHECK(Ftspi020State, obj, TYPE_FTSPI020)
> +
> +static void ftspi020_update_irq(Ftspi020State *s)
> +{
> +    qemu_set_irq(s->irq, s->isr ? 1 : 0);
> +}
> +
> +static void ftspi020_handle_ack(void *opaque, int line, int level)
> +{
> +    Ftspi020State *s = FTSPI020(opaque);
> +
> +    if (!(s->icr & ICR_DMA)) {
> +        return;
> +    }
> +
> +    if (level) {
> +        qemu_set_irq(s->req, 0);
> +    } else if (s->cmd[2]) {
> +        qemu_set_irq(s->req, 1);
> +    }
> +}
> +
> +static int ftspi020_do_command(Ftspi020State *s)
> +{
> +    uint32_t cs   = extract32(s->cmd[3],  8, 2);
> +    uint32_t cmd  = extract32(s->cmd[3], 24, 8);
> +    uint32_t ilen = extract32(s->cmd[1], 24, 2);
> +    uint32_t alen = extract32(s->cmd[1],  0, 3);
> +    uint32_t dcyc = extract32(s->cmd[1], 16, 8);
> +
> +    if (dcyc % 8) {
> +        fprintf(stderr, "ftspi020: bad dummy clock (%u) to QEMU\n", dcyc);
> +        abort();
> +    }
> +
> +    /* activate the spi flash */
> +    qemu_set_irq(s->cs_lines[cs], 0);
> +
> +    /* if it's a SPI flash READ_STATUS command */
> +    if ((s->cmd[3] & (CMD3_RDSR | CMD3_WRITE)) == CMD3_RDSR) {
> +        uint32_t rdsr;
> +
> +        ssi_transfer(s->spi, cmd);
> +        do {
> +            rdsr = ssi_transfer(s->spi, 0x00);
> +            if (s->cmd[3] & CMD3_RDSR_SW) {
> +                break;
> +            }
> +        } while (rdsr & (1 << s->wip));
> +        s->rdsr = rdsr;
> +    } else {
> +    /* otherwise */
> +        int i;
> +
> +        ilen = MIN(ilen, 2);
> +        alen = MIN(alen, 4);
> +
> +        /* command cycles */
> +        for (i = 0; i < ilen; ++i) {
> +            ssi_transfer(s->spi, cmd);
> +        }
> +        /* address cycles */
> +        for (i = alen - 1; i >= 0; --i) {
> +            ssi_transfer(s->spi, extract32(s->cmd[0], i * 8, 8));
> +        }
> +        /* dummy cycles */
> +        for (i = 0; i < (dcyc >> 3); ++i) {
> +            ssi_transfer(s->spi, 0x00);
> +        }
> +    }
> +
> +    if (!s->cmd[2]) {
> +        qemu_set_irq(s->cs_lines[cs], 1);
> +    } else if (s->icr & ICR_DMA) {
> +        qemu_set_irq(s->req, 1);
> +    }
> +
> +    if (s->cmd[3] & CMD3_INTR) {
> +        s->isr |= ISR_CMDFIN;
> +    }
> +    ftspi020_update_irq(s);
> +
> +    return 0;
> +}
> +
> +static void ftspi020_chip_reset(Ftspi020State *s)
> +{
> +    int i;
> +
> +    for (i = 0; i < 4; ++i) {
> +        s->cmd[i] = 0;
> +    }
> +    s->wip = 0;
> +    s->ctrl = 0;
> +    s->timing = 0;
> +    s->icr = 0;
> +    s->isr = 0;
> +    s->rdsr = 0;
> +
> +    qemu_set_irq(s->irq, 0);
> +
> +    for (i = 0; i < CFG_NR_CSLINES; ++i) {
> +        /* de-activate the spi flash */
> +        qemu_set_irq(s->cs_lines[i], 1);
> +    }
> +}
> +
> +static uint64_t
> +ftspi020_mem_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    Ftspi020State *s = FTSPI020(opaque);
> +    uint64_t ret = 0;
> +
> +    switch (addr) {
> +    case REG_CMD0 ... REG_CMD3:
> +        return s->cmd[(addr - REG_CMD0) / 4];
> +    case REG_CR:
> +        return s->ctrl;
> +    case REG_TR:
> +        return s->timing;
> +    case REG_SR:
> +        /* In QEMU, the data fifo is always ready for read/write */
> +        return SR_RX_READY | SR_TX_READY;
> +    case REG_ISR:
> +        return s->isr;
> +    case REG_ICR:
> +        return s->icr;
> +    case REG_RDSR:
> +        return s->rdsr;
> +    case REG_REVR:
> +        return 0x00010001;  /* rev. 1.0.1 */
> +    case REG_FEAR:
> +        return FEAR_CLKM_SYNC
> +            | FEAR_CMDQ(2) | FEAR_RXFIFO(32) | FEAR_TXFIFO(32);
> +    case REG_DR:
> +        if (!(s->cmd[3] & CMD3_WRITE)) {
> +            int i;
> +            uint32_t cs = extract32(s->cmd[3], 8, 2);
> +            for (i = 0; i < 4 && s->cmd[2]; i++, s->cmd[2]--) {
> +                ret = deposit32(ret, i * 8, 8,
> +                    ssi_transfer(s->spi, 0x00) & 0xff);
> +            }
> +            if (!s->cmd[2]) {
> +                qemu_set_irq(s->cs_lines[cs], 1);
> +                if (s->cmd[3] & CMD3_INTR) {
> +                    s->isr |= ISR_CMDFIN;
> +                }
> +                ftspi020_update_irq(s);
> +            }
> +        }
> +        break;
> +        /* we don't care */
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +            "ftspi020: undefined memory access@%#" HWADDR_PRIx "\n", addr);
> +        break;
> +    }
> +
> +    return ret;
> +}
> +
> +static void
> +ftspi020_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
> +{
> +    Ftspi020State *s = FTSPI020(opaque);
> +
> +    switch (addr) {
> +    case REG_CMD0 ... REG_CMD2:
> +        s->cmd[(addr - REG_CMD0) / 4] = (uint32_t)val;
> +        break;
> +    case REG_CMD3:
> +        s->cmd[3] = (uint32_t)val;
> +        ftspi020_do_command(s);
> +        break;
> +    case REG_CR:
> +        if (val & CR_ABORT) {
> +            ftspi020_chip_reset(s);
> +            val &= ~CR_ABORT;
> +        }
> +        s->ctrl = (uint32_t)val;
> +        s->wip  = extract32(val, 16, 3);
> +        break;
> +    case REG_TR:
> +        s->timing = (uint32_t)val;
> +        break;
> +    case REG_DR:
> +        if (s->cmd[3] & CMD3_WRITE) {
> +            int i;
> +            uint32_t cs = extract32(s->cmd[3], 8, 2);
> +            for (i = 0; i < 4 && s->cmd[2]; i++, s->cmd[2]--) {
> +                ssi_transfer(s->spi, extract32((uint32_t)val, i * 8, 8));
> +            }
> +            if (!s->cmd[2]) {
> +                qemu_set_irq(s->cs_lines[cs], 1);
> +                if (s->cmd[3] & CMD3_INTR) {
> +                    s->isr |= ISR_CMDFIN;
> +                }
> +                ftspi020_update_irq(s);
> +            }
> +        }
> +        break;
> +    case REG_ISR:
> +        s->isr &= ~((uint32_t)val);
> +        ftspi020_update_irq(s);
> +        break;
> +    case REG_ICR:
> +        s->icr = (uint32_t)val;
> +        break;
> +        /* we don't care */
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +            "ftspi020: undefined memory access@%#" HWADDR_PRIx "\n", addr);
> +        break;
> +    }
> +}
> +
> +static const MemoryRegionOps mmio_ops = {
> +    .read  = ftspi020_mem_read,
> +    .write = ftspi020_mem_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .valid = {
> +        .min_access_size = 4,
> +        .max_access_size = 4,
> +    }
> +};
> +
> +static void ftspi020_reset(DeviceState *ds)
> +{
> +    Ftspi020State *s = FTSPI020(SYS_BUS_DEVICE(ds));
> +
> +    ftspi020_chip_reset(s);
> +}
> +
> +static void ftspi020_realize(DeviceState *dev, Error **errp)
> +{
> +    Ftspi020State *s = FTSPI020(dev);
> +    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
> +    int i;
> +
> +    memory_region_init_io(&s->iomem,
> +                          &mmio_ops,
> +                          s,
> +                          TYPE_FTSPI020,
> +                          0x1000);
> +    sysbus_init_mmio(sbd, &s->iomem);
> +    sysbus_init_irq(sbd, &s->irq);
> +
> +    s->spi = ssi_create_bus(&sbd->qdev, "spi");
> +    s->cs_lines = g_new0(qemu_irq, CFG_NR_CSLINES);
> +    ssi_auto_connect_slaves(dev, s->cs_lines, s->spi);
> +    for (i = 0; i < CFG_NR_CSLINES; ++i) {
> +        sysbus_init_irq(sbd, &s->cs_lines[i]);
> +    }
> +
> +    qdev_init_gpio_in(&sbd->qdev, ftspi020_handle_ack, 1);
> +    qdev_init_gpio_out(&sbd->qdev, &s->req, 1);
> +}
> +
> +static const VMStateDescription vmstate_ftspi020 = {
> +    .name = TYPE_FTSPI020,
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .minimum_version_id_old = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_UINT32_ARRAY(cmd, Ftspi020State, 4),
> +        VMSTATE_UINT32(ctrl, Ftspi020State),
> +        VMSTATE_UINT32(timing, Ftspi020State),
> +        VMSTATE_UINT32(icr, Ftspi020State),
> +        VMSTATE_UINT32(isr, Ftspi020State),
> +        VMSTATE_UINT32(rdsr, Ftspi020State),
> +        VMSTATE_END_OF_LIST(),
> +    }
> +};
> +
> +static void ftspi020_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    dc->vmsd    = &vmstate_ftspi020;
> +    dc->reset   = ftspi020_reset;
> +    dc->realize = ftspi020_realize;
> +    dc->no_user = 1;
> +}
> +
> +static const TypeInfo ftspi020_info = {
> +    .name          = TYPE_FTSPI020,
> +    .parent        = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(Ftspi020State),
> +    .class_init    = ftspi020_class_init,
> +};
> +
> +static void ftspi020_register_types(void)
> +{
> +    type_register_static(&ftspi020_info);
> +}
> +
> +type_init(ftspi020_register_types)
> diff --git a/hw/ftspi020.h b/hw/ftspi020.h
> new file mode 100644
> index 0000000..47b5d2e
> --- /dev/null
> +++ b/hw/ftspi020.h
> @@ -0,0 +1,81 @@
> +/*
> + * Faraday FTSPI020 Flash Controller
> + *
> + * Copyright (c) 2012 Faraday Technology
> + * Written by Dante Su <dantesu@faraday-tech.com>
> + *
> + * This code is licensed under GNU GPL v2+.
> + */
> +
> +#ifndef HW_ARM_FTSPI020_H
> +#define HW_ARM_FTSPI020_H
> +
> +#include "qemu/bitops.h"
> +
> +/* Number of CS lines */
> +#define CFG_NR_CSLINES      4
> +
> +/******************************************************************************
> + * FTSPI020 registers
> + *****************************************************************************/
> +#define REG_CMD0            0x00    /* Flash address */
> +#define REG_CMD1            0x04
> +#define REG_CMD2            0x08    /* Flash data counter */
> +#define REG_CMD3            0x0c
> +#define REG_CR              0x10    /* Control Register */
> +#define REG_TR              0x14    /* AC Timing Register */
> +#define REG_SR              0x18    /* Status Register */
> +#define REG_ICR             0x20    /* Interrupt Control Register */
> +#define REG_ISR             0x24    /* Interrupt Status Register */
> +#define REG_RDSR            0x28    /* Read Status Register */
> +#define REG_REVR            0x50    /* Revision Register */
> +#define REG_FEAR            0x54    /* Feature Register */
> +#define REG_DR              0x100   /* Data Register */
> +
> +#define CMD1_CTRD           BIT(28) /* Enable 1 byte continuous read */
> +#define CMD1_INST_LEN(x)    (((x) & 0x03) << 24)/* instruction length */
> +#define CMD1_DCLK_LEN(x)    (((x) & 0xff) << 16)/* dummy clock length */
> +#define CMD1_ADDR_LEN(x)    (((x) & 0x07) << 0) /* address length */
> +
> +#define CMD3_INST_OPC(x)    (((x) & 0xff) << 24)/* instruction op code */
> +#define CMD3_CTRD_OPC(x)    (((x) & 0xff) << 16)/* cont. read op code */
> +#define CMD3_CS(x)          (((x) & 0x0f) << 8) /* chip select */
> +#define CMD3_OPM_STD        (0)         /* standard 1-bit serial mode */
> +#define CMD3_OPM_DUAL       (1 << 5)    /* fast read dual */
> +#define CMD3_OPM_QUAD       (2 << 5)    /* fast read quad */
> +#define CMD3_OPM_DUALIO     (3 << 5)    /* fast read dual io */
> +#define CMD3_OPM_QUADIO     (4 << 5)    /* fast read quad io */
> +#define CMD3_DTR            BIT(4)  /* Enable double transfer rate */
> +#define CMD3_RDSR_HW        (0)     /* Enable HW polling RDSR */
> +#define CMD3_RDSR_SW        BIT(3)  /* Disable HW polling RDSR */
> +#define CMD3_RDSR           BIT(2)  /* Indicate it's a RDSR command */
> +#define CMD3_READ           0       /* Indicate it's a read command */
> +#define CMD3_WRITE          BIT(1)  /* Indicate it's a write command */
> +#define CMD3_INTR           BIT(0)  /* Enable interrupt and status update */
> +
> +#define CR_BUSYBIT(x)       (((x) & 0x07) << 16) /* Busy bit in the RDSR */
> +#define CR_ABORT            BIT(8)
> +#define CR_MODE0            0       /* SPI MODE0 */
> +#define CR_MODE3            BIT(4)  /* SPI MODE3 */
> +#define CR_CLKDIV(n)        ((n) & 0x03)    /* Clock divider = 2 * (n + 1) */
> +
> +#define TR_TRACE(x)         (((x) & 0x0f) << 4) /* trace delay */
> +#define TR_CS(x)            (((x) & 0x0f) << 0) /* cs delay */
> +
> +#define SR_RX_READY         BIT(1)  /* Rx Ready */
> +#define SR_TX_READY         BIT(0)  /* Tx Ready */
> +
> +#define ICR_RX_THRES(x)     (((x) & 0x03) << 12)/* rx interrupt threshold */
> +#define ICR_TX_THRES(x)     (((x) & 0x03) << 8) /* tx interrupt threshold */
> +#define ICR_DMA             BIT(0)  /* Enable DMA HW handshake */
> +
> +#define ISR_CMDFIN          BIT(0)  /* Command finished interrupt */
> +
> +#define FEAR_CLKM_BYPORT    0       /* clock mode = byport */
> +#define FEAR_CLKM_SYNC      BIT(25) /* clock mode = sync */
> +#define FEAR_SUPP_DTR       BIT(24) /* support double transfer rate */
> +#define FEAR_CMDQ(x)        (((x) & 0x07) << 16)    /* cmd queue depth */
> +#define FEAR_RXFIFO(x)      (((x) & 0xff) << 8)     /* rx fifo depth */
> +#define FEAR_TXFIFO(x)      (((x) & 0xff) << 0)     /* tx fifo depth */
> +
> +#endif  /* HW_ARM_FTSPI020_H */
> --
> 1.7.9.5
>
>

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Qemu-devel] [PATCH v9 10/24] hw/arm: add FTDMAC020 AHB DMA support
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 10/24] hw/arm: add FTDMAC020 AHB DMA support Kuo-Jung Su
@ 2013-03-29  0:22   ` Peter Crosthwaite
  2013-03-29  7:23     ` Kuo-Jung Su
  0 siblings, 1 reply; 35+ messages in thread
From: Peter Crosthwaite @ 2013-03-29  0:22 UTC (permalink / raw)
  To: Kuo-Jung Su
  Cc: Peter Maydell, i.mitsyanko, qemu-devel, Blue Swirl, Paul Brook,
	Kuo-Jung Su, Andreas, fred.konrad

Hi Kuo-Jung,

On Mon, Mar 25, 2013 at 10:09 PM, Kuo-Jung Su <dantesu@gmail.com> wrote:
> From: Kuo-Jung Su <dantesu@faraday-tech.com>
>
> The Faraday FTDMAC020 provides eight configurable
> channels for the memory-to-memory, memory-to-peripheral,
> peripheral-to-peripheral, and peripheral-to-memory transfers.
>
> Each DMA channel supports chain transfer and can be programmed
> to one of the 16 handshaking channels in the hardware handshake mode.
>
> The main function of the hardware handshake mode is to provide an
> indication of the device status. Users can also disable the hardware
> handshake mode by programming the register when a DMA transfer is not
> necessary of referring to the handshaking channels.
>
> Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
> ---
>  hw/arm/Makefile.objs    |    2 +-
>  hw/arm/ftplat_a369soc.c |   14 ++
>  hw/ftdmac020.c          |  599 +++++++++++++++++++++++++++++++++++++++++++++++
>  hw/ftdmac020.h          |  107 +++++++++
>  4 files changed, 721 insertions(+), 1 deletion(-)
>  create mode 100644 hw/ftdmac020.c
>  create mode 100644 hw/ftdmac020.h
>
> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
> index 6a41b21..6510c51 100644
> --- a/hw/arm/Makefile.objs
> +++ b/hw/arm/Makefile.objs
> @@ -25,7 +25,7 @@ obj-y += strongarm.o
>  obj-y += imx_serial.o imx_ccm.o imx_timer.o imx_avic.o
>  obj-$(CONFIG_KVM) += kvm/arm_gic.o
>  obj-y += ftintc020.o ftahbc020.o ftddrii030.o ftpwmtmr010.o ftwdt010.o \
> -                ftrtc011.o
> +                ftrtc011.o ftdmac020.o
>
>  obj-y := $(addprefix ../,$(obj-y))
>
> diff --git a/hw/arm/ftplat_a369soc.c b/hw/arm/ftplat_a369soc.c
> index bd696c4..59e2c61 100644
> --- a/hw/arm/ftplat_a369soc.c
> +++ b/hw/arm/ftplat_a369soc.c
> @@ -168,6 +168,20 @@ static void a369soc_chip_init(FaradaySoCState *s)
>      sysbus_connect_irq(SYS_BUS_DEVICE(ds), 3, s->pic[44]);
>      /* Hour (Edge) */
>      sysbus_connect_irq(SYS_BUS_DEVICE(ds), 4, s->pic[45]);
> +
> +    /* ftdmac020 */
> +    s->hdma[0] = sysbus_create_varargs("ftdmac020",
> +                                       0x90300000,
> +                                       s->pic[0],  /* ALL (NC in A369) */
> +                                       s->pic[15], /* TC */
> +                                       s->pic[16], /* ERR */
> +                                       NULL);
> +    s->hdma[1] = sysbus_create_varargs("ftdmac020",
> +                                       0x96100000,
> +                                       s->pic[0],  /* ALL (NC in A369) */
> +                                       s->pic[17], /* TC */
> +                                       s->pic[18], /* ERR */
> +                                       NULL);
>  }
>
>  static void a369soc_realize(DeviceState *dev, Error **errp)
> diff --git a/hw/ftdmac020.c b/hw/ftdmac020.c
> new file mode 100644
> index 0000000..81b49b2
> --- /dev/null
> +++ b/hw/ftdmac020.c
> @@ -0,0 +1,599 @@
> +/*
> + * QEMU model of the FTDMAC020 DMA Controller
> + *
> + * Copyright (C) 2012 Faraday Technology
> + * Written by Dante Su <dantesu@faraday-tech.com>
> + *
> + * This file is licensed under GNU GPL v2+.
> + *
> + * Note: The FTDMAC020 descending address mode is not implemented.
> + */
> +
> +#include "hw/sysbus.h"
> +#include "sysemu/dma.h"
> +#include "sysemu/sysemu.h"
> +#include "sysemu/blockdev.h"
> +
> +#include "hw/ftdmac020.h"
> +
> +#define TYPE_FTDMAC020    "ftdmac020"
> +
> +enum ftdmac020_irqpin {
> +    IRQ_ALL = 0,
> +    IRQ_TC,
> +    IRQ_ERR,
> +};
> +
> +typedef struct Ftdmac020State Ftdmac020State;
> +
> +/**
> + * struct Ftdmac020LLD - hardware link list descriptor.
> + * @src: source physical address
> + * @dst: destination physical addr
> + * @next: phsical address to the next link list descriptor
> + * @ctrl: control field
> + * @size: transfer size
> + *
> + * should be word aligned
> + */
> +typedef struct Ftdmac020LLD {
> +    uint32_t src;
> +    uint32_t dst;
> +    uint32_t next;
> +    uint32_t ctrl;
> +    uint32_t size;
> +} Ftdmac020LLD;
> +
> +typedef struct Ftdmac020Chan {
> +    Ftdmac020State *chip;
> +
> +    int id;
> +    int burst;
> +    int llp_cnt;
> +    int src_bw;
> +    int src_stride;
> +    int dst_bw;
> +    int dst_stride;
> +
> +    /* HW register cache */
> +    uint32_t ccr;
> +    uint32_t cfg;
> +    uint32_t src;
> +    uint32_t dst;
> +    uint32_t llp;
> +    uint32_t len;
> +} Ftdmac020Chan;
> +
> +typedef struct Ftdmac020State {
> +    /*< private >*/
> +    SysBusDevice parent;
> +
> +    /*< public >*/
> +    MemoryRegion iomem;
> +    qemu_irq irq[3];
> +
> +    Ftdmac020Chan chan[8];
> +    qemu_irq      ack[16];
> +    uint32_t      req;
> +
> +    int busy;    /* Busy Channel ID */
> +    int bh_owner;
> +    QEMUBH *bh;
> +    DMAContext *dma;
> +
> +    /* HW register cache */
> +    uint32_t tcisr;
> +    uint32_t eaisr;
> +    uint32_t tcsr;
> +    uint32_t easr;
> +    uint32_t cesr;
> +    uint32_t cbsr;
> +    uint32_t csr;
> +    uint32_t sync;
> +} Ftdmac020State;
> +
> +#define FTDMAC020(obj) \
> +    OBJECT_CHECK(Ftdmac020State, obj, TYPE_FTDMAC020)
> +
> +static void ftdmac020_update_irq(Ftdmac020State *s)
> +{
> +    uint32_t tc, err;
> +
> +    /* 1. Checking TC interrupts */
> +    tc = s->tcisr & 0xff;
> +    qemu_set_irq(s->irq[IRQ_TC], tc ? 1 : 0);
> +
> +    /* 2. Checking Error/Abort interrupts */
> +    err = s->eaisr & 0x00ff00ff;
> +    qemu_set_irq(s->irq[IRQ_ERR], err ? 1 : 0);
> +
> +    /* 3. Checking interrupt summary (TC | Error | Abort) */
> +    qemu_set_irq(s->irq[IRQ_ALL], (tc || err) ? 1 : 0);
> +}
> +
> +static void ftdmac020_chan_ccr_decode(Ftdmac020Chan *c)
> +{
> +    uint32_t tmp;
> +
> +    /* 1. decode burst size */
> +    tmp = extract32(c->ccr, 16, 3);
> +    c->burst  = 1 << (tmp ? tmp + 1 : 0);
> +
> +    /* 2. decode source width */
> +    tmp = extract32(c->ccr, 11, 2);
> +    c->src_bw = 8 << tmp;
> +
> +    /* 3. decode destination width */
> +    tmp = extract32(c->ccr, 8, 2);
> +    c->dst_bw = 8 << tmp;
> +
> +    /* 4. decode source address stride */
> +    tmp = extract32(c->ccr, 5, 2);
> +    if (tmp == 2) {
> +        c->src_stride = 0;
> +    } else {
> +        c->src_stride = c->src_bw >> 3;
> +    }
> +
> +    /* 5. decode destination address stride */
> +    tmp = extract32(c->ccr, 3, 2);
> +    if (tmp == 2) {
> +        c->dst_stride = 0;
> +    } else {
> +        c->dst_stride = c->dst_bw >> 3;
> +    }
> +}
> +
> +static void ftdmac020_chan_start(Ftdmac020Chan *c)
> +{
> +    Ftdmac020State *s = c->chip;
> +    Ftdmac020LLD desc;
> +    hwaddr src, dst;
> +    uint8_t buf[4096] __attribute__ ((aligned (8)));
> +    int i, len, stride, src_hs, dst_hs;
> +
> +    if (!(c->ccr & CCR_START)) {
> +        return;
> +    }
> +
> +    s->busy = c->id;
> +
> +    /* DMA src/dst address */
> +    src = c->src;
> +    dst = c->dst;
> +
> +    /* DMA hardware handshake id */
> +    src_hs = (c->cfg & CFG_SRC_HANDSHAKE_EN) ? extract32(c->cfg, 3, 4) : -1;
> +    dst_hs = (c->cfg & CFG_DST_HANDSHAKE_EN) ? extract32(c->cfg, 9, 4) : -1;
> +
> +    /* DMA src/dst sanity check */
> +    if (cpu_physical_memory_is_io(src) && c->src_stride) {
> +        fprintf(stderr,
> +            "ftdmac020: src is an I/O device with non-fixed address?\n");
> +        abort();
> +    }

This doesn't look like a QEMU fatal to me. Its seems like software
usage policy of the DMA. Does the real hardware actually do any
preventative action on striding io accesses? If not then I would
either remove this assertion or at least demote it to a
LOG_GUEST_ERROR. There may be corner case applications where this is
valid. I think its also worthwhile trying to avoid use of
cpu_physical_foo() functions from device land.

> +    if (cpu_physical_memory_is_io(dst) && c->dst_stride) {
> +        fprintf(stderr,
> +            "ftdmac020: dst is an I/O device with non-fixed address?\n");
> +        abort();
> +    }
> +
> +    while (c->len > 0) {
> +        /*
> +         * Postpone this DMA action
> +         * if the corresponding dma request is not asserted
> +         */
> +        if ((src_hs >= 0) && !(s->req & BIT(src_hs))) {
> +            break;
> +        }
> +        if ((dst_hs >= 0) && !(s->req & BIT(dst_hs))) {
> +            break;
> +        }
> +
> +        len = MIN(sizeof(buf), c->burst * (c->src_bw >> 3));
> +
> +        /* load data from source into local buffer */
> +        if (c->src_stride) {
> +            dma_memory_read(s->dma, src, buf, len);
> +            src += len;
> +        } else {
> +            stride = c->src_bw >> 3;
> +            for (i = 0; i < len; i += stride) {
> +                dma_memory_read(s->dma, src, buf + i, stride);
> +            }
> +        }
> +
> +        /* DMA Hardware Handshake */
> +        if (src_hs >= 0) {
> +            qemu_set_irq(s->ack[src_hs], 1);
> +        }
> +
> +        /* store data into destination from local buffer */
> +        if (c->dst_stride) {
> +            dma_memory_write(s->dma, dst, buf, len);
> +            dst += len;
> +        } else {
> +            stride = c->dst_bw >> 3;
> +            for (i = 0; i < len; i += stride) {
> +                dma_memory_write(s->dma, dst, buf + i, stride);
> +            }
> +        }
> +
> +        /* DMA Hardware Handshake */
> +        if (dst_hs >= 0) {
> +            qemu_set_irq(s->ack[dst_hs], 1);
> +        }
> +
> +        /* update the channel transfer size */
> +        c->len -= len / (c->src_bw >> 3);
> +
> +        if (c->len == 0) {
> +            /* update the channel transfer status */
> +            if (!(c->ccr & CCR_MASK_TC)) {
> +                s->tcsr |= BIT(c->id);
> +                if (!(c->cfg & CFG_MASK_TCI)) {
> +                    s->tcisr |= BIT(c->id);
> +                }
> +                ftdmac020_update_irq(s);
> +            }
> +            /* try to load next lld */
> +            if (c->llp) {
> +                c->llp_cnt += 1;
> +                dma_memory_read(s->dma, c->llp, &desc, sizeof(desc));
> +
> +                desc.src  = le32_to_cpu(desc.src);
> +                desc.dst  = le32_to_cpu(desc.dst);
> +                desc.next = le32_to_cpu(desc.next);
> +                desc.size = le32_to_cpu(desc.size);
> +                desc.ctrl = le32_to_cpu(desc.ctrl);
> +
> +                c->src = desc.src;
> +                c->dst = desc.dst;
> +                c->llp = desc.next & 0xfffffffc;
> +                c->len = desc.size & 0x003fffff;
> +                c->ccr = (c->ccr & 0x78f8c081)
> +                       | (extract32(desc.ctrl, 29, 3) << 24)
> +                       | ((desc.ctrl & BIT(28)) ? CCR_MASK_TC : 0)
> +                       | (extract32(desc.ctrl, 25, 3) << 11)
> +                       | (extract32(desc.ctrl, 22, 3) << 8)
> +                       | (extract32(desc.ctrl, 20, 2) << 5)
> +                       | (extract32(desc.ctrl, 18, 2) << 3)
> +                       | (extract32(desc.ctrl, 16, 2) << 1);
> +                ftdmac020_chan_ccr_decode(c);
> +
> +                src = c->src;
> +                dst = c->dst;
> +            } else {
> +                /* clear dma start bit */
> +                c->ccr &= ~CCR_START;
> +            }
> +        }
> +    }
> +
> +    /* update dma src/dst address */
> +    c->src = src;
> +    c->dst = dst;
> +
> +    s->busy = -1;
> +}
> +
> +static void ftdmac020_chan_reset(Ftdmac020Chan *c)
> +{
> +    c->ccr = 0;
> +    c->cfg = 0;
> +    c->src = 0;
> +    c->dst = 0;
> +    c->llp = 0;
> +    c->len = 0;
> +}
> +
> +static void ftdmac020_bh(void *opaque)
> +{
> +    Ftdmac020State *s = FTDMAC020(opaque);
> +    Ftdmac020Chan  *c = NULL;
> +    int i, jobs, done;
> +
> +    ++s->bh_owner;
> +    jobs = 0;
> +    done = 0;
> +    for (i = 0; i < 8; ++i) {
> +        c = s->chan + i;
> +        if (c->ccr & CCR_START) {
> +            ++jobs;
> +            ftdmac020_chan_start(c);
> +            if (!(c->ccr & CCR_START)) {
> +                ++done;
> +            }
> +        }
> +    }
> +    --s->bh_owner;
> +
> +    /*
> +     * Devices those with an infinite FIFO (always ready for R/W)

Grammar (- the "those"?).

> +     * would trigger a new DMA handshake transaction here.
> +     * (i.e. ftnandc021, ftsdc010)
> +     */
> +    if ((jobs - done) && s->req) {
> +        qemu_bh_schedule(s->bh);
> +    }
> +}
> +
> +static void ftdmac020_handle_req(void *opaque, int line, int level)
> +{
> +    Ftdmac020State *s = FTDMAC020(opaque);
> +
> +    if (level) {
> +        /*
> +         * Devices those wait for data from externaI/O

s/those/that

> +         * would trigger a new DMA handshake transaction here.
> +         * (i.e. ftssp010)

A nitpick, but do you mean e.g. instead of i.e.?

Regards,
Peter

> +         */
> +        if (!(s->req & BIT(line))) {
> +            /* a simple workaround for BH reentry issue */
> +            if (!s->bh_owner) {
> +                qemu_bh_schedule(s->bh);
> +            }
> +        }
> +        s->req |= BIT(line);
> +    } else {
> +        s->req &= ~BIT(line);
> +        qemu_set_irq(s->ack[line], 0);
> +    }
> +}
> +
> +static void ftdmac020_chip_reset(Ftdmac020State *s)
> +{
> +    int i;
> +
> +    s->tcisr = 0;
> +    s->eaisr = 0;
> +    s->tcsr = 0;
> +    s->easr = 0;
> +    s->cesr = 0;
> +    s->cbsr = 0;
> +    s->csr  = 0;
> +    s->sync = 0;
> +
> +    for (i = 0; i < 8; ++i) {
> +        ftdmac020_chan_reset(s->chan + i);
> +    }
> +
> +    /* We can assume our GPIO have been wired up now */
> +    for (i = 0; i < 16; ++i) {
> +        qemu_set_irq(s->ack[i], 0);
> +    }
> +    s->req = 0;
> +}
> +
> +static uint64_t ftdmac020_mem_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    Ftdmac020State *s = FTDMAC020(opaque);
> +    Ftdmac020Chan  *c = NULL;
> +    uint32_t i, ret = 0;
> +
> +    switch (addr) {
> +    case REG_ISR:
> +        /* 1. Checking TC interrupts */
> +        ret |= s->tcisr & 0xff;
> +        /* 2. Checking Error interrupts */
> +        ret |= s->eaisr & 0xff;
> +        /* 3. Checking Abort interrupts */
> +        ret |= (s->eaisr >> 16) & 0xff;
> +        break;
> +    case REG_TCISR:
> +        return s->tcisr;
> +    case REG_EAISR:
> +        return s->eaisr;
> +    case REG_TCSR:
> +        return s->tcsr;
> +    case REG_EASR:
> +        return s->easr;
> +    case REG_CESR:
> +        for (i = 0; i < 8; ++i) {
> +            c = s->chan + i;
> +            ret |= (c->ccr & CCR_START) ? BIT(i) : 0;
> +        }
> +        break;
> +    case REG_CBSR:
> +        return (s->busy > 0) ? BIT(s->busy) : 0;
> +    case REG_CSR:
> +        return s->csr;
> +    case REG_SYNC:
> +        return s->sync;
> +    case REG_REVISION:
> +        /* rev. = 1.13.0 */
> +        return 0x00011300;
> +    case REG_FEATURE:
> +        /* fifo = 32 bytes, support linked list, 8 channels, AHB0 only */
> +        return 0x00008105;
> +    case REG_CHAN_BASE(0) ... REG_CHAN_BASE(7) + 0x14:
> +        c = s->chan + REG_CHAN_ID(addr);
> +        switch (addr & 0x1f) {
> +        case REG_CHAN_CCR:
> +            return c->ccr;
> +        case REG_CHAN_CFG:
> +            ret = c->cfg;
> +            ret |= (s->busy == c->id) ? (1 << 8) : 0;
> +            ret |= (c->llp_cnt & 0x0f) << 16;
> +            break;
> +        case REG_CHAN_SRC:
> +            return c->src;
> +        case REG_CHAN_DST:
> +            return c->dst;
> +        case REG_CHAN_LLP:
> +            return c->llp;
> +        case REG_CHAN_LEN:
> +            return c->len;
> +        default:
> +            qemu_log_mask(LOG_GUEST_ERROR,
> +                "ftdmac020: undefined memory access@%#" HWADDR_PRIx "\n",
> +                addr);
> +            break;
> +        }
> +        break;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +            "ftdmac020: undefined memory access@%#" HWADDR_PRIx "\n", addr);
> +        break;
> +    }
> +
> +    return ret;
> +}
> +
> +static void ftdmac020_mem_write(void    *opaque,
> +                                hwaddr   addr,
> +                                uint64_t val,
> +                                unsigned size)
> +{
> +    Ftdmac020State *s = FTDMAC020(opaque);
> +    Ftdmac020Chan  *c = NULL;
> +
> +    switch (addr) {
> +    case REG_TCCLR:
> +        s->tcisr &= ~((uint32_t)val);
> +        s->tcsr &= ~((uint32_t)val);
> +        ftdmac020_update_irq(s);
> +        break;
> +    case REG_EACLR:
> +        s->eaisr &= ~((uint32_t)val);
> +        s->easr &= ~((uint32_t)val);
> +        ftdmac020_update_irq(s);
> +        break;
> +    case REG_CSR:
> +        s->csr = (uint32_t)val;
> +        break;
> +    case REG_SYNC:
> +        /* In QEMU, devices are all in the same clock domain
> +         * so there is nothing needs to be done.
> +         */
> +        s->sync = (uint32_t)val;
> +        break;
> +    case REG_CHAN_BASE(0) ... REG_CHAN_BASE(7) + 0x14:
> +        c = s->chan + REG_CHAN_ID(addr);
> +        switch (addr & 0x1f) {
> +        case REG_CHAN_CCR:
> +            if (!(c->ccr & CCR_START) && (val & CCR_START)) {
> +                c->llp_cnt = 0;
> +            }
> +            c->ccr = (uint32_t)val & 0x87FFBFFF;
> +            if (c->ccr & CCR_START) {
> +                ftdmac020_chan_ccr_decode(c);
> +                /* kick-off DMA engine */
> +                qemu_bh_schedule(s->bh);
> +            }
> +            break;
> +        case REG_CHAN_CFG:
> +            c->cfg = (uint32_t)val & 0x3eff;
> +            break;
> +        case REG_CHAN_SRC:
> +            c->src = (uint32_t)val;
> +            break;
> +        case REG_CHAN_DST:
> +            c->dst = (uint32_t)val;
> +            break;
> +        case REG_CHAN_LLP:
> +            c->llp = (uint32_t)val & 0xfffffffc;
> +            break;
> +        case REG_CHAN_LEN:
> +            c->len = (uint32_t)val & 0x003fffff;
> +            break;
> +        default:
> +            qemu_log_mask(LOG_GUEST_ERROR,
> +                "ftdmac020: undefined memory access@%#" HWADDR_PRIx "\n",
> +                addr);
> +            break;
> +        }
> +        break;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +            "ftdmac020: undefined memory access@%#" HWADDR_PRIx "\n", addr);
> +        break;
> +    }
> +}
> +
> +static const MemoryRegionOps mmio_ops = {
> +    .read  = ftdmac020_mem_read,
> +    .write = ftdmac020_mem_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .valid = {
> +        .min_access_size = 4,
> +        .max_access_size = 4
> +    }
> +};
> +
> +static void ftdmac020_reset(DeviceState *ds)
> +{
> +    Ftdmac020State *s = FTDMAC020(SYS_BUS_DEVICE(ds));
> +
> +    ftdmac020_chip_reset(s);
> +}
> +
> +static void ftdmac020_realize(DeviceState *dev, Error **errp)
> +{
> +    Ftdmac020State *s = FTDMAC020(dev);
> +    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
> +    int i;
> +
> +    memory_region_init_io(&s->iomem,
> +                          &mmio_ops,
> +                          s,
> +                          TYPE_FTDMAC020,
> +                          0x1000);
> +    sysbus_init_mmio(sbd, &s->iomem);
> +    for (i = 0; i < 3; ++i) {
> +        sysbus_init_irq(sbd, &s->irq[i]);
> +    }
> +    qdev_init_gpio_in(&sbd->qdev, ftdmac020_handle_req, 16);
> +    qdev_init_gpio_out(&sbd->qdev, s->ack, 16);
> +
> +    s->busy = -1;
> +    s->dma = &dma_context_memory;
> +    s->bh = qemu_bh_new(ftdmac020_bh, s);
> +    for (i = 0; i < 8; ++i) {
> +        Ftdmac020Chan *c = s->chan + i;
> +        c->id   = i;
> +        c->chip = s;
> +    }
> +}
> +
> +static const VMStateDescription vmstate_ftdmac020 = {
> +    .name = TYPE_FTDMAC020,
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .minimum_version_id_old = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_UINT32(tcisr, Ftdmac020State),
> +        VMSTATE_UINT32(eaisr, Ftdmac020State),
> +        VMSTATE_UINT32(tcsr, Ftdmac020State),
> +        VMSTATE_UINT32(easr, Ftdmac020State),
> +        VMSTATE_UINT32(cesr, Ftdmac020State),
> +        VMSTATE_UINT32(cbsr, Ftdmac020State),
> +        VMSTATE_UINT32(csr, Ftdmac020State),
> +        VMSTATE_UINT32(sync, Ftdmac020State),
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> +
> +static void ftdmac020_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    dc->vmsd    = &vmstate_ftdmac020;
> +    dc->reset   = ftdmac020_reset;
> +    dc->realize = ftdmac020_realize;
> +    dc->no_user = 1;
> +}
> +
> +static const TypeInfo ftdmac020_info = {
> +    .name           = TYPE_FTDMAC020,
> +    .parent         = TYPE_SYS_BUS_DEVICE,
> +    .instance_size  = sizeof(Ftdmac020State),
> +    .class_init     = ftdmac020_class_init,
> +};
> +
> +static void ftdmac020_register_types(void)
> +{
> +    type_register_static(&ftdmac020_info);
> +}
> +
> +type_init(ftdmac020_register_types)
> diff --git a/hw/ftdmac020.h b/hw/ftdmac020.h
> new file mode 100644
> index 0000000..86ee58c
> --- /dev/null
> +++ b/hw/ftdmac020.h
> @@ -0,0 +1,107 @@
> +/*
> + * QEMU model of the FTDMAC020 DMA Controller
> + *
> + * Copyright (C) 2012 Faraday Technology
> + * Written by Dante Su <dantesu@faraday-tech.com>
> + *
> + * This file is licensed under GNU GPL v2+.
> + *
> + * Note: The FTDMAC020 decreasing address mode is not implemented.
> + */
> +
> +#ifndef HW_ARM_FTDMAC020_H
> +#define HW_ARM_FTDMAC020_H
> +
> +#include "qemu/bitops.h"
> +
> +#define REG_ISR         0x00    /* Interrupt Status Register */
> +#define REG_TCISR       0x04    /* Terminal Count Interrupt Status Register */
> +#define REG_TCCLR       0x08    /* Terminal Count Status Clear Register */
> +#define REG_EAISR       0x0c    /* Error/Abort Interrupt Status Register */
> +#define REG_EACLR       0x10    /* Error/Abort Status Clear Register */
> +#define REG_TCSR        0x14    /* Terminal Count Status Register */
> +#define REG_EASR        0x18    /* Error/Abort Status Register */
> +#define REG_CESR        0x1c    /* Channel Enable Status Register */
> +#define REG_CBSR        0x20    /* Channel Busy Status Register */
> +#define REG_CSR         0x24    /* Configuration Status Register */
> +#define REG_SYNC        0x28    /* Synchronization Register */
> +#define REG_REVISION    0x30
> +#define REG_FEATURE     0x34
> +
> +#define REG_CHAN_ID(addr)   (((addr) - 0x100) >> 5)
> +#define REG_CHAN_BASE(ch)   (0x100 + ((ch) << 5))
> +
> +#define REG_CHAN_CCR        0x00
> +#define REG_CHAN_CFG        0x04
> +#define REG_CHAN_SRC        0x08
> +#define REG_CHAN_DST        0x0C
> +#define REG_CHAN_LLP        0x10
> +#define REG_CHAN_LEN        0x14
> +
> +/*
> + * Feature register
> + */
> +#define FEATURE_NCHAN(f)    (((f) >> 12) & 0xF)
> +#define FEATURE_BRIDGE(f)   ((f) & BIT(10))
> +#define FEATURE_DUALBUS(f)  ((f) & BIT(9))
> +#define FEATURE_LLP(f)      ((f) & BIT(8))
> +#define FEATURE_FIFOAW(f)   ((f) & 0xF)
> +
> +/*
> + * Channel control register
> + */
> +#define CCR_START           BIT(0)
> +#define CCR_DST_M1          BIT(1)  /* dst is a slave device of AHB1 */
> +#define CCR_SRC_M1          BIT(2)  /* src is a slave device of AHB1 */
> +#define CCR_DST_INC         (0 << 3)/* dst addr: next = curr++ */
> +#define CCR_DST_DEC         (1 << 3)/* dst addr: next = curr-- */
> +#define CCR_DST_FIXED       (2 << 3)
> +#define CCR_SRC_INC         (0 << 5)/* src addr: next = curr++ */
> +#define CCR_SRC_DEC         (1 << 5)/* src addr: next = curr-- */
> +#define CCR_SRC_FIXED       (2 << 5)
> +#define CCR_HANDSHAKE       BIT(7)  /* DMA HW handshake enabled */
> +#define CCR_DST_WIDTH_8     (0 << 8)
> +#define CCR_DST_WIDTH_16    (1 << 8)
> +#define CCR_DST_WIDTH_32    (2 << 8)
> +#define CCR_DST_WIDTH_64    (3 << 8)
> +#define CCR_SRC_WIDTH_8     (0 << 11)
> +#define CCR_SRC_WIDTH_16    (1 << 11)
> +#define CCR_SRC_WIDTH_32    (2 << 11)
> +#define CCR_SRC_WIDTH_64    (3 << 11)
> +#define CCR_ABORT           BIT(15)
> +#define CCR_BURST_1         (0 << 16)
> +#define CCR_BURST_4         (1 << 16)
> +#define CCR_BURST_8         (2 << 16)
> +#define CCR_BURST_16        (3 << 16)
> +#define CCR_BURST_32        (4 << 16)
> +#define CCR_BURST_64        (5 << 16)
> +#define CCR_BURST_128       (6 << 16)
> +#define CCR_BURST_256       (7 << 16)
> +#define CCR_PRIVILEGED      BIT(19)
> +#define CCR_BUFFERABLE      BIT(20)
> +#define CCR_CACHEABLE       BIT(21)
> +#define CCR_PRIO_0          (0 << 22)
> +#define CCR_PRIO_1          (1 << 22)
> +#define CCR_PRIO_2          (2 << 22)
> +#define CCR_PRIO_3          (3 << 22)
> +#define CCR_FIFOTH_1        (0 << 24)
> +#define CCR_FIFOTH_2        (1 << 24)
> +#define CCR_FIFOTH_4        (2 << 24)
> +#define CCR_FIFOTH_8        (3 << 24)
> +#define CCR_FIFOTH_16       (4 << 24)
> +#define CCR_MASK_TC         BIT(31) /* DMA Transfer terminated(finished) */
> +
> +/*
> + * Channel configuration register
> + */
> +#define CFG_MASK_TCI            BIT(0)    /* disable tc interrupt */
> +#define CFG_MASK_EI             BIT(1)    /* disable error interrupt */
> +#define CFG_MASK_AI             BIT(2)    /* disable abort interrupt */
> +#define CFG_SRC_HANDSHAKE(x)    (((x) & 0xf) << 3)
> +#define CFG_SRC_HANDSHAKE_EN    BIT(7)
> +#define CFG_BUSY                BIT(8)
> +#define CFG_DST_HANDSHAKE(x)    (((x) & 0xf) << 9)
> +#define CFG_DST_HANDSHAKE_EN    BIT(13)
> +#define CFG_LLP_CNT(cfg)        (((cfg) >> 16) & 0xf)
> +
> +#endif    /* HW_ARM_FTDMAC020_H */
> --
> 1.7.9.5
>
>

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Qemu-devel] [PATCH v9 24/24] hw/arm: add FTSPI020 SPI flash controller support
  2013-03-29  0:02   ` Peter Crosthwaite
@ 2013-03-29  7:15     ` Kuo-Jung Su
  0 siblings, 0 replies; 35+ messages in thread
From: Kuo-Jung Su @ 2013-03-29  7:15 UTC (permalink / raw)
  To: Peter Crosthwaite
  Cc: Peter Maydell, Igor Mitsyanko, qemu-devel, Blue Swirl,
	Paul Brook, Kuo-Jung Su, Andreas, fred konrad

2013/3/29 Peter Crosthwaite <peter.crosthwaite@xilinx.com>:
> Hi Kuo-Jung,
>
> On Mon, Mar 25, 2013 at 10:10 PM, Kuo-Jung Su <dantesu@gmail.com> wrote:
>> From: Kuo-Jung Su <dantesu@faraday-tech.com>
>>
>> The FTSPI020 is an integrated SPI Flash controller
>> which supports up to 4 flash chips.
>>
>> Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
>> ---
>>  hw/arm/Makefile.objs |    2 +-
>>  hw/arm/ftplat_a369.c |   16 +++
>>  hw/ftspi020.c        |  341 ++++++++++++++++++++++++++++++++++++++++++++++++++
>>  hw/ftspi020.h        |   81 ++++++++++++
>>  4 files changed, 439 insertions(+), 1 deletion(-)
>>  create mode 100644 hw/ftspi020.c
>>  create mode 100644 hw/ftspi020.h
>>
>> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
>> index bcfb70a..a34ca41 100644
>> --- a/hw/arm/Makefile.objs
>> +++ b/hw/arm/Makefile.objs
>> @@ -27,7 +27,7 @@ obj-$(CONFIG_KVM) += kvm/arm_gic.o
>>  obj-y += ftintc020.o ftahbc020.o ftddrii030.o ftpwmtmr010.o ftwdt010.o \
>>                  ftrtc011.o ftdmac020.o ftapbbrg020.o ftnandc021.o fti2c010.o \
>>                  ftssp010.o ftgmac100.o ftlcdc200.o fttsc010.o ftsdc010.o \
>> -                ftmac110.o fttmr010.o
>> +                ftmac110.o fttmr010.o ftspi020.o
>>
>>  obj-y := $(addprefix ../,$(obj-y))
>>
>> diff --git a/hw/arm/ftplat_a369.c b/hw/arm/ftplat_a369.c
>> index f22e2ca..77cd44d 100644
>> --- a/hw/arm/ftplat_a369.c
>> +++ b/hw/arm/ftplat_a369.c
>> @@ -125,6 +125,22 @@ static void a369_board_init(QEMUMachineInitArgs *args)
>>      sysbus_connect_irq(SYS_BUS_DEVICE(ds), 1, s->pic[6]);
>>      sysbus_connect_irq(SYS_BUS_DEVICE(ds), 2, s->pic[7]);
>>
>> +    /* SPI: FTSPI020 */
>> +    ds = sysbus_create_simple("ftspi020", 0xC0000000, s->pic[4]);
>> +    s->spi_fl[0] = ds;
>> +
>> +    /* Attach the spi flash to ftspi020.0 */
>> +    nr_flash = 1;
>> +    for (i = 0; i < nr_flash; i++) {
>> +        SSIBus *ssi = (SSIBus *)qdev_get_child_bus(s->spi_fl[0], "spi");
>> +        DeviceState *fl = ssi_create_slave_no_init(ssi, "w25q64");
>
> You qdev_init straight away, so you can just call ssi_create_slave.
> This is just a hangover from my recent m25p80 cleanup which made the
> original reason for create_slave_no_init redundant. I'll have to send
> some cleanup patches for my own machine models with the equivalent
> change. s/ssi_create_slave_no_init/ssi_create_slave and ...
>

Got it, thanks

>> +        qemu_irq cs_line;
>> +
>> +        qdev_init_nofail(fl);
>
> ... dump this.
>
> Regards,
> Peter
>
>> +        cs_line = qdev_get_gpio_in(fl, 0);
>> +        sysbus_connect_irq(SYS_BUS_DEVICE(s->spi_fl[0]), i + 1, cs_line);
>> +    }
>> +
>>      /* System start-up */
>>
>>      if (args->kernel_filename) {
>> diff --git a/hw/ftspi020.c b/hw/ftspi020.c
>> new file mode 100644
>> index 0000000..a7253bd
>> --- /dev/null
>> +++ b/hw/ftspi020.c
>> @@ -0,0 +1,341 @@
>> +/*
>> + * Faraday FTSPI020 Flash Controller
>> + *
>> + * Copyright (c) 2012 Faraday Technology
>> + * Written by Dante Su <dantesu@faraday-tech.com>
>> + *
>> + * This code is licensed under GNU GPL v2+.
>> + */
>> +
>> +#include "hw/hw.h"
>> +#include "hw/sysbus.h"
>> +#include "hw/ssi.h"
>> +#include "sysemu/sysemu.h"
>> +
>> +#include "hw/ftspi020.h"
>> +
>> +#define TYPE_FTSPI020   "ftspi020"
>> +
>> +typedef struct Ftspi020State {
>> +    /*< private >*/
>> +    SysBusDevice parent;
>> +
>> +    /*< public >*/
>> +    MemoryRegion iomem;
>> +    qemu_irq irq;
>> +
>> +    /* DMA hardware handshake */
>> +    qemu_irq req;
>> +
>> +    SSIBus *spi;
>> +    qemu_irq *cs_lines;
>> +
>> +    int wip;    /* SPI Flash Status: Write In Progress BIT shift */
>> +
>> +    /* HW register caches */
>> +    uint32_t cmd[4];
>> +    uint32_t ctrl;
>> +    uint32_t timing;
>> +    uint32_t icr;
>> +    uint32_t isr;
>> +    uint32_t rdsr;
>> +} Ftspi020State;
>> +
>> +#define FTSPI020(obj) \
>> +    OBJECT_CHECK(Ftspi020State, obj, TYPE_FTSPI020)
>> +
>> +static void ftspi020_update_irq(Ftspi020State *s)
>> +{
>> +    qemu_set_irq(s->irq, s->isr ? 1 : 0);
>> +}
>> +
>> +static void ftspi020_handle_ack(void *opaque, int line, int level)
>> +{
>> +    Ftspi020State *s = FTSPI020(opaque);
>> +
>> +    if (!(s->icr & ICR_DMA)) {
>> +        return;
>> +    }
>> +
>> +    if (level) {
>> +        qemu_set_irq(s->req, 0);
>> +    } else if (s->cmd[2]) {
>> +        qemu_set_irq(s->req, 1);
>> +    }
>> +}
>> +
>> +static int ftspi020_do_command(Ftspi020State *s)
>> +{
>> +    uint32_t cs   = extract32(s->cmd[3],  8, 2);
>> +    uint32_t cmd  = extract32(s->cmd[3], 24, 8);
>> +    uint32_t ilen = extract32(s->cmd[1], 24, 2);
>> +    uint32_t alen = extract32(s->cmd[1],  0, 3);
>> +    uint32_t dcyc = extract32(s->cmd[1], 16, 8);
>> +
>> +    if (dcyc % 8) {
>> +        fprintf(stderr, "ftspi020: bad dummy clock (%u) to QEMU\n", dcyc);
>> +        abort();
>> +    }
>> +
>> +    /* activate the spi flash */
>> +    qemu_set_irq(s->cs_lines[cs], 0);
>> +
>> +    /* if it's a SPI flash READ_STATUS command */
>> +    if ((s->cmd[3] & (CMD3_RDSR | CMD3_WRITE)) == CMD3_RDSR) {
>> +        uint32_t rdsr;
>> +
>> +        ssi_transfer(s->spi, cmd);
>> +        do {
>> +            rdsr = ssi_transfer(s->spi, 0x00);
>> +            if (s->cmd[3] & CMD3_RDSR_SW) {
>> +                break;
>> +            }
>> +        } while (rdsr & (1 << s->wip));
>> +        s->rdsr = rdsr;
>> +    } else {
>> +    /* otherwise */
>> +        int i;
>> +
>> +        ilen = MIN(ilen, 2);
>> +        alen = MIN(alen, 4);
>> +
>> +        /* command cycles */
>> +        for (i = 0; i < ilen; ++i) {
>> +            ssi_transfer(s->spi, cmd);
>> +        }
>> +        /* address cycles */
>> +        for (i = alen - 1; i >= 0; --i) {
>> +            ssi_transfer(s->spi, extract32(s->cmd[0], i * 8, 8));
>> +        }
>> +        /* dummy cycles */
>> +        for (i = 0; i < (dcyc >> 3); ++i) {
>> +            ssi_transfer(s->spi, 0x00);
>> +        }
>> +    }
>> +
>> +    if (!s->cmd[2]) {
>> +        qemu_set_irq(s->cs_lines[cs], 1);
>> +    } else if (s->icr & ICR_DMA) {
>> +        qemu_set_irq(s->req, 1);
>> +    }
>> +
>> +    if (s->cmd[3] & CMD3_INTR) {
>> +        s->isr |= ISR_CMDFIN;
>> +    }
>> +    ftspi020_update_irq(s);
>> +
>> +    return 0;
>> +}
>> +
>> +static void ftspi020_chip_reset(Ftspi020State *s)
>> +{
>> +    int i;
>> +
>> +    for (i = 0; i < 4; ++i) {
>> +        s->cmd[i] = 0;
>> +    }
>> +    s->wip = 0;
>> +    s->ctrl = 0;
>> +    s->timing = 0;
>> +    s->icr = 0;
>> +    s->isr = 0;
>> +    s->rdsr = 0;
>> +
>> +    qemu_set_irq(s->irq, 0);
>> +
>> +    for (i = 0; i < CFG_NR_CSLINES; ++i) {
>> +        /* de-activate the spi flash */
>> +        qemu_set_irq(s->cs_lines[i], 1);
>> +    }
>> +}
>> +
>> +static uint64_t
>> +ftspi020_mem_read(void *opaque, hwaddr addr, unsigned size)
>> +{
>> +    Ftspi020State *s = FTSPI020(opaque);
>> +    uint64_t ret = 0;
>> +
>> +    switch (addr) {
>> +    case REG_CMD0 ... REG_CMD3:
>> +        return s->cmd[(addr - REG_CMD0) / 4];
>> +    case REG_CR:
>> +        return s->ctrl;
>> +    case REG_TR:
>> +        return s->timing;
>> +    case REG_SR:
>> +        /* In QEMU, the data fifo is always ready for read/write */
>> +        return SR_RX_READY | SR_TX_READY;
>> +    case REG_ISR:
>> +        return s->isr;
>> +    case REG_ICR:
>> +        return s->icr;
>> +    case REG_RDSR:
>> +        return s->rdsr;
>> +    case REG_REVR:
>> +        return 0x00010001;  /* rev. 1.0.1 */
>> +    case REG_FEAR:
>> +        return FEAR_CLKM_SYNC
>> +            | FEAR_CMDQ(2) | FEAR_RXFIFO(32) | FEAR_TXFIFO(32);
>> +    case REG_DR:
>> +        if (!(s->cmd[3] & CMD3_WRITE)) {
>> +            int i;
>> +            uint32_t cs = extract32(s->cmd[3], 8, 2);
>> +            for (i = 0; i < 4 && s->cmd[2]; i++, s->cmd[2]--) {
>> +                ret = deposit32(ret, i * 8, 8,
>> +                    ssi_transfer(s->spi, 0x00) & 0xff);
>> +            }
>> +            if (!s->cmd[2]) {
>> +                qemu_set_irq(s->cs_lines[cs], 1);
>> +                if (s->cmd[3] & CMD3_INTR) {
>> +                    s->isr |= ISR_CMDFIN;
>> +                }
>> +                ftspi020_update_irq(s);
>> +            }
>> +        }
>> +        break;
>> +        /* we don't care */
>> +    default:
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +            "ftspi020: undefined memory access@%#" HWADDR_PRIx "\n", addr);
>> +        break;
>> +    }
>> +
>> +    return ret;
>> +}
>> +
>> +static void
>> +ftspi020_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
>> +{
>> +    Ftspi020State *s = FTSPI020(opaque);
>> +
>> +    switch (addr) {
>> +    case REG_CMD0 ... REG_CMD2:
>> +        s->cmd[(addr - REG_CMD0) / 4] = (uint32_t)val;
>> +        break;
>> +    case REG_CMD3:
>> +        s->cmd[3] = (uint32_t)val;
>> +        ftspi020_do_command(s);
>> +        break;
>> +    case REG_CR:
>> +        if (val & CR_ABORT) {
>> +            ftspi020_chip_reset(s);
>> +            val &= ~CR_ABORT;
>> +        }
>> +        s->ctrl = (uint32_t)val;
>> +        s->wip  = extract32(val, 16, 3);
>> +        break;
>> +    case REG_TR:
>> +        s->timing = (uint32_t)val;
>> +        break;
>> +    case REG_DR:
>> +        if (s->cmd[3] & CMD3_WRITE) {
>> +            int i;
>> +            uint32_t cs = extract32(s->cmd[3], 8, 2);
>> +            for (i = 0; i < 4 && s->cmd[2]; i++, s->cmd[2]--) {
>> +                ssi_transfer(s->spi, extract32((uint32_t)val, i * 8, 8));
>> +            }
>> +            if (!s->cmd[2]) {
>> +                qemu_set_irq(s->cs_lines[cs], 1);
>> +                if (s->cmd[3] & CMD3_INTR) {
>> +                    s->isr |= ISR_CMDFIN;
>> +                }
>> +                ftspi020_update_irq(s);
>> +            }
>> +        }
>> +        break;
>> +    case REG_ISR:
>> +        s->isr &= ~((uint32_t)val);
>> +        ftspi020_update_irq(s);
>> +        break;
>> +    case REG_ICR:
>> +        s->icr = (uint32_t)val;
>> +        break;
>> +        /* we don't care */
>> +    default:
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +            "ftspi020: undefined memory access@%#" HWADDR_PRIx "\n", addr);
>> +        break;
>> +    }
>> +}
>> +
>> +static const MemoryRegionOps mmio_ops = {
>> +    .read  = ftspi020_mem_read,
>> +    .write = ftspi020_mem_write,
>> +    .endianness = DEVICE_LITTLE_ENDIAN,
>> +    .valid = {
>> +        .min_access_size = 4,
>> +        .max_access_size = 4,
>> +    }
>> +};
>> +
>> +static void ftspi020_reset(DeviceState *ds)
>> +{
>> +    Ftspi020State *s = FTSPI020(SYS_BUS_DEVICE(ds));
>> +
>> +    ftspi020_chip_reset(s);
>> +}
>> +
>> +static void ftspi020_realize(DeviceState *dev, Error **errp)
>> +{
>> +    Ftspi020State *s = FTSPI020(dev);
>> +    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
>> +    int i;
>> +
>> +    memory_region_init_io(&s->iomem,
>> +                          &mmio_ops,
>> +                          s,
>> +                          TYPE_FTSPI020,
>> +                          0x1000);
>> +    sysbus_init_mmio(sbd, &s->iomem);
>> +    sysbus_init_irq(sbd, &s->irq);
>> +
>> +    s->spi = ssi_create_bus(&sbd->qdev, "spi");
>> +    s->cs_lines = g_new0(qemu_irq, CFG_NR_CSLINES);
>> +    ssi_auto_connect_slaves(dev, s->cs_lines, s->spi);
>> +    for (i = 0; i < CFG_NR_CSLINES; ++i) {
>> +        sysbus_init_irq(sbd, &s->cs_lines[i]);
>> +    }
>> +
>> +    qdev_init_gpio_in(&sbd->qdev, ftspi020_handle_ack, 1);
>> +    qdev_init_gpio_out(&sbd->qdev, &s->req, 1);
>> +}
>> +
>> +static const VMStateDescription vmstate_ftspi020 = {
>> +    .name = TYPE_FTSPI020,
>> +    .version_id = 1,
>> +    .minimum_version_id = 1,
>> +    .minimum_version_id_old = 1,
>> +    .fields = (VMStateField[]) {
>> +        VMSTATE_UINT32_ARRAY(cmd, Ftspi020State, 4),
>> +        VMSTATE_UINT32(ctrl, Ftspi020State),
>> +        VMSTATE_UINT32(timing, Ftspi020State),
>> +        VMSTATE_UINT32(icr, Ftspi020State),
>> +        VMSTATE_UINT32(isr, Ftspi020State),
>> +        VMSTATE_UINT32(rdsr, Ftspi020State),
>> +        VMSTATE_END_OF_LIST(),
>> +    }
>> +};
>> +
>> +static void ftspi020_class_init(ObjectClass *klass, void *data)
>> +{
>> +    DeviceClass *dc = DEVICE_CLASS(klass);
>> +
>> +    dc->vmsd    = &vmstate_ftspi020;
>> +    dc->reset   = ftspi020_reset;
>> +    dc->realize = ftspi020_realize;
>> +    dc->no_user = 1;
>> +}
>> +
>> +static const TypeInfo ftspi020_info = {
>> +    .name          = TYPE_FTSPI020,
>> +    .parent        = TYPE_SYS_BUS_DEVICE,
>> +    .instance_size = sizeof(Ftspi020State),
>> +    .class_init    = ftspi020_class_init,
>> +};
>> +
>> +static void ftspi020_register_types(void)
>> +{
>> +    type_register_static(&ftspi020_info);
>> +}
>> +
>> +type_init(ftspi020_register_types)
>> diff --git a/hw/ftspi020.h b/hw/ftspi020.h
>> new file mode 100644
>> index 0000000..47b5d2e
>> --- /dev/null
>> +++ b/hw/ftspi020.h
>> @@ -0,0 +1,81 @@
>> +/*
>> + * Faraday FTSPI020 Flash Controller
>> + *
>> + * Copyright (c) 2012 Faraday Technology
>> + * Written by Dante Su <dantesu@faraday-tech.com>
>> + *
>> + * This code is licensed under GNU GPL v2+.
>> + */
>> +
>> +#ifndef HW_ARM_FTSPI020_H
>> +#define HW_ARM_FTSPI020_H
>> +
>> +#include "qemu/bitops.h"
>> +
>> +/* Number of CS lines */
>> +#define CFG_NR_CSLINES      4
>> +
>> +/******************************************************************************
>> + * FTSPI020 registers
>> + *****************************************************************************/
>> +#define REG_CMD0            0x00    /* Flash address */
>> +#define REG_CMD1            0x04
>> +#define REG_CMD2            0x08    /* Flash data counter */
>> +#define REG_CMD3            0x0c
>> +#define REG_CR              0x10    /* Control Register */
>> +#define REG_TR              0x14    /* AC Timing Register */
>> +#define REG_SR              0x18    /* Status Register */
>> +#define REG_ICR             0x20    /* Interrupt Control Register */
>> +#define REG_ISR             0x24    /* Interrupt Status Register */
>> +#define REG_RDSR            0x28    /* Read Status Register */
>> +#define REG_REVR            0x50    /* Revision Register */
>> +#define REG_FEAR            0x54    /* Feature Register */
>> +#define REG_DR              0x100   /* Data Register */
>> +
>> +#define CMD1_CTRD           BIT(28) /* Enable 1 byte continuous read */
>> +#define CMD1_INST_LEN(x)    (((x) & 0x03) << 24)/* instruction length */
>> +#define CMD1_DCLK_LEN(x)    (((x) & 0xff) << 16)/* dummy clock length */
>> +#define CMD1_ADDR_LEN(x)    (((x) & 0x07) << 0) /* address length */
>> +
>> +#define CMD3_INST_OPC(x)    (((x) & 0xff) << 24)/* instruction op code */
>> +#define CMD3_CTRD_OPC(x)    (((x) & 0xff) << 16)/* cont. read op code */
>> +#define CMD3_CS(x)          (((x) & 0x0f) << 8) /* chip select */
>> +#define CMD3_OPM_STD        (0)         /* standard 1-bit serial mode */
>> +#define CMD3_OPM_DUAL       (1 << 5)    /* fast read dual */
>> +#define CMD3_OPM_QUAD       (2 << 5)    /* fast read quad */
>> +#define CMD3_OPM_DUALIO     (3 << 5)    /* fast read dual io */
>> +#define CMD3_OPM_QUADIO     (4 << 5)    /* fast read quad io */
>> +#define CMD3_DTR            BIT(4)  /* Enable double transfer rate */
>> +#define CMD3_RDSR_HW        (0)     /* Enable HW polling RDSR */
>> +#define CMD3_RDSR_SW        BIT(3)  /* Disable HW polling RDSR */
>> +#define CMD3_RDSR           BIT(2)  /* Indicate it's a RDSR command */
>> +#define CMD3_READ           0       /* Indicate it's a read command */
>> +#define CMD3_WRITE          BIT(1)  /* Indicate it's a write command */
>> +#define CMD3_INTR           BIT(0)  /* Enable interrupt and status update */
>> +
>> +#define CR_BUSYBIT(x)       (((x) & 0x07) << 16) /* Busy bit in the RDSR */
>> +#define CR_ABORT            BIT(8)
>> +#define CR_MODE0            0       /* SPI MODE0 */
>> +#define CR_MODE3            BIT(4)  /* SPI MODE3 */
>> +#define CR_CLKDIV(n)        ((n) & 0x03)    /* Clock divider = 2 * (n + 1) */
>> +
>> +#define TR_TRACE(x)         (((x) & 0x0f) << 4) /* trace delay */
>> +#define TR_CS(x)            (((x) & 0x0f) << 0) /* cs delay */
>> +
>> +#define SR_RX_READY         BIT(1)  /* Rx Ready */
>> +#define SR_TX_READY         BIT(0)  /* Tx Ready */
>> +
>> +#define ICR_RX_THRES(x)     (((x) & 0x03) << 12)/* rx interrupt threshold */
>> +#define ICR_TX_THRES(x)     (((x) & 0x03) << 8) /* tx interrupt threshold */
>> +#define ICR_DMA             BIT(0)  /* Enable DMA HW handshake */
>> +
>> +#define ISR_CMDFIN          BIT(0)  /* Command finished interrupt */
>> +
>> +#define FEAR_CLKM_BYPORT    0       /* clock mode = byport */
>> +#define FEAR_CLKM_SYNC      BIT(25) /* clock mode = sync */
>> +#define FEAR_SUPP_DTR       BIT(24) /* support double transfer rate */
>> +#define FEAR_CMDQ(x)        (((x) & 0x07) << 16)    /* cmd queue depth */
>> +#define FEAR_RXFIFO(x)      (((x) & 0xff) << 8)     /* rx fifo depth */
>> +#define FEAR_TXFIFO(x)      (((x) & 0xff) << 0)     /* tx fifo depth */
>> +
>> +#endif  /* HW_ARM_FTSPI020_H */
>> --
>> 1.7.9.5
>>
>>



--
Best wishes,
Kuo-Jung Su

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Qemu-devel] [PATCH v9 10/24] hw/arm: add FTDMAC020 AHB DMA support
  2013-03-29  0:22   ` Peter Crosthwaite
@ 2013-03-29  7:23     ` Kuo-Jung Su
  0 siblings, 0 replies; 35+ messages in thread
From: Kuo-Jung Su @ 2013-03-29  7:23 UTC (permalink / raw)
  To: Peter Crosthwaite
  Cc: Peter Maydell, Igor Mitsyanko, qemu-devel, Blue Swirl,
	Paul Brook, Kuo-Jung Su, Andreas, fred konrad

2013/3/29 Peter Crosthwaite <peter.crosthwaite@xilinx.com>:
> Hi Kuo-Jung,
>
> On Mon, Mar 25, 2013 at 10:09 PM, Kuo-Jung Su <dantesu@gmail.com> wrote:
>> From: Kuo-Jung Su <dantesu@faraday-tech.com>
>>
>> The Faraday FTDMAC020 provides eight configurable
>> channels for the memory-to-memory, memory-to-peripheral,
>> peripheral-to-peripheral, and peripheral-to-memory transfers.
>>
>> Each DMA channel supports chain transfer and can be programmed
>> to one of the 16 handshaking channels in the hardware handshake mode.
>>
>> The main function of the hardware handshake mode is to provide an
>> indication of the device status. Users can also disable the hardware
>> handshake mode by programming the register when a DMA transfer is not
>> necessary of referring to the handshaking channels.
>>
>> Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
>> ---
>>  hw/arm/Makefile.objs    |    2 +-
>>  hw/arm/ftplat_a369soc.c |   14 ++
>>  hw/ftdmac020.c          |  599 +++++++++++++++++++++++++++++++++++++++++++++++
>>  hw/ftdmac020.h          |  107 +++++++++
>>  4 files changed, 721 insertions(+), 1 deletion(-)
>>  create mode 100644 hw/ftdmac020.c
>>  create mode 100644 hw/ftdmac020.h
>>
>> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
>> index 6a41b21..6510c51 100644
>> --- a/hw/arm/Makefile.objs
>> +++ b/hw/arm/Makefile.objs
>> @@ -25,7 +25,7 @@ obj-y += strongarm.o
>>  obj-y += imx_serial.o imx_ccm.o imx_timer.o imx_avic.o
>>  obj-$(CONFIG_KVM) += kvm/arm_gic.o
>>  obj-y += ftintc020.o ftahbc020.o ftddrii030.o ftpwmtmr010.o ftwdt010.o \
>> -                ftrtc011.o
>> +                ftrtc011.o ftdmac020.o
>>
>>  obj-y := $(addprefix ../,$(obj-y))
>>
>> diff --git a/hw/arm/ftplat_a369soc.c b/hw/arm/ftplat_a369soc.c
>> index bd696c4..59e2c61 100644
>> --- a/hw/arm/ftplat_a369soc.c
>> +++ b/hw/arm/ftplat_a369soc.c
>> @@ -168,6 +168,20 @@ static void a369soc_chip_init(FaradaySoCState *s)
>>      sysbus_connect_irq(SYS_BUS_DEVICE(ds), 3, s->pic[44]);
>>      /* Hour (Edge) */
>>      sysbus_connect_irq(SYS_BUS_DEVICE(ds), 4, s->pic[45]);
>> +
>> +    /* ftdmac020 */
>> +    s->hdma[0] = sysbus_create_varargs("ftdmac020",
>> +                                       0x90300000,
>> +                                       s->pic[0],  /* ALL (NC in A369) */
>> +                                       s->pic[15], /* TC */
>> +                                       s->pic[16], /* ERR */
>> +                                       NULL);
>> +    s->hdma[1] = sysbus_create_varargs("ftdmac020",
>> +                                       0x96100000,
>> +                                       s->pic[0],  /* ALL (NC in A369) */
>> +                                       s->pic[17], /* TC */
>> +                                       s->pic[18], /* ERR */
>> +                                       NULL);
>>  }
>>
>>  static void a369soc_realize(DeviceState *dev, Error **errp)
>> diff --git a/hw/ftdmac020.c b/hw/ftdmac020.c
>> new file mode 100644
>> index 0000000..81b49b2
>> --- /dev/null
>> +++ b/hw/ftdmac020.c
>> @@ -0,0 +1,599 @@
>> +/*
>> + * QEMU model of the FTDMAC020 DMA Controller
>> + *
>> + * Copyright (C) 2012 Faraday Technology
>> + * Written by Dante Su <dantesu@faraday-tech.com>
>> + *
>> + * This file is licensed under GNU GPL v2+.
>> + *
>> + * Note: The FTDMAC020 descending address mode is not implemented.
>> + */
>> +
>> +#include "hw/sysbus.h"
>> +#include "sysemu/dma.h"
>> +#include "sysemu/sysemu.h"
>> +#include "sysemu/blockdev.h"
>> +
>> +#include "hw/ftdmac020.h"
>> +
>> +#define TYPE_FTDMAC020    "ftdmac020"
>> +
>> +enum ftdmac020_irqpin {
>> +    IRQ_ALL = 0,
>> +    IRQ_TC,
>> +    IRQ_ERR,
>> +};
>> +
>> +typedef struct Ftdmac020State Ftdmac020State;
>> +
>> +/**
>> + * struct Ftdmac020LLD - hardware link list descriptor.
>> + * @src: source physical address
>> + * @dst: destination physical addr
>> + * @next: phsical address to the next link list descriptor
>> + * @ctrl: control field
>> + * @size: transfer size
>> + *
>> + * should be word aligned
>> + */
>> +typedef struct Ftdmac020LLD {
>> +    uint32_t src;
>> +    uint32_t dst;
>> +    uint32_t next;
>> +    uint32_t ctrl;
>> +    uint32_t size;
>> +} Ftdmac020LLD;
>> +
>> +typedef struct Ftdmac020Chan {
>> +    Ftdmac020State *chip;
>> +
>> +    int id;
>> +    int burst;
>> +    int llp_cnt;
>> +    int src_bw;
>> +    int src_stride;
>> +    int dst_bw;
>> +    int dst_stride;
>> +
>> +    /* HW register cache */
>> +    uint32_t ccr;
>> +    uint32_t cfg;
>> +    uint32_t src;
>> +    uint32_t dst;
>> +    uint32_t llp;
>> +    uint32_t len;
>> +} Ftdmac020Chan;
>> +
>> +typedef struct Ftdmac020State {
>> +    /*< private >*/
>> +    SysBusDevice parent;
>> +
>> +    /*< public >*/
>> +    MemoryRegion iomem;
>> +    qemu_irq irq[3];
>> +
>> +    Ftdmac020Chan chan[8];
>> +    qemu_irq      ack[16];
>> +    uint32_t      req;
>> +
>> +    int busy;    /* Busy Channel ID */
>> +    int bh_owner;
>> +    QEMUBH *bh;
>> +    DMAContext *dma;
>> +
>> +    /* HW register cache */
>> +    uint32_t tcisr;
>> +    uint32_t eaisr;
>> +    uint32_t tcsr;
>> +    uint32_t easr;
>> +    uint32_t cesr;
>> +    uint32_t cbsr;
>> +    uint32_t csr;
>> +    uint32_t sync;
>> +} Ftdmac020State;
>> +
>> +#define FTDMAC020(obj) \
>> +    OBJECT_CHECK(Ftdmac020State, obj, TYPE_FTDMAC020)
>> +
>> +static void ftdmac020_update_irq(Ftdmac020State *s)
>> +{
>> +    uint32_t tc, err;
>> +
>> +    /* 1. Checking TC interrupts */
>> +    tc = s->tcisr & 0xff;
>> +    qemu_set_irq(s->irq[IRQ_TC], tc ? 1 : 0);
>> +
>> +    /* 2. Checking Error/Abort interrupts */
>> +    err = s->eaisr & 0x00ff00ff;
>> +    qemu_set_irq(s->irq[IRQ_ERR], err ? 1 : 0);
>> +
>> +    /* 3. Checking interrupt summary (TC | Error | Abort) */
>> +    qemu_set_irq(s->irq[IRQ_ALL], (tc || err) ? 1 : 0);
>> +}
>> +
>> +static void ftdmac020_chan_ccr_decode(Ftdmac020Chan *c)
>> +{
>> +    uint32_t tmp;
>> +
>> +    /* 1. decode burst size */
>> +    tmp = extract32(c->ccr, 16, 3);
>> +    c->burst  = 1 << (tmp ? tmp + 1 : 0);
>> +
>> +    /* 2. decode source width */
>> +    tmp = extract32(c->ccr, 11, 2);
>> +    c->src_bw = 8 << tmp;
>> +
>> +    /* 3. decode destination width */
>> +    tmp = extract32(c->ccr, 8, 2);
>> +    c->dst_bw = 8 << tmp;
>> +
>> +    /* 4. decode source address stride */
>> +    tmp = extract32(c->ccr, 5, 2);
>> +    if (tmp == 2) {
>> +        c->src_stride = 0;
>> +    } else {
>> +        c->src_stride = c->src_bw >> 3;
>> +    }
>> +
>> +    /* 5. decode destination address stride */
>> +    tmp = extract32(c->ccr, 3, 2);
>> +    if (tmp == 2) {
>> +        c->dst_stride = 0;
>> +    } else {
>> +        c->dst_stride = c->dst_bw >> 3;
>> +    }
>> +}
>> +
>> +static void ftdmac020_chan_start(Ftdmac020Chan *c)
>> +{
>> +    Ftdmac020State *s = c->chip;
>> +    Ftdmac020LLD desc;
>> +    hwaddr src, dst;
>> +    uint8_t buf[4096] __attribute__ ((aligned (8)));
>> +    int i, len, stride, src_hs, dst_hs;
>> +
>> +    if (!(c->ccr & CCR_START)) {
>> +        return;
>> +    }
>> +
>> +    s->busy = c->id;
>> +
>> +    /* DMA src/dst address */
>> +    src = c->src;
>> +    dst = c->dst;
>> +
>> +    /* DMA hardware handshake id */
>> +    src_hs = (c->cfg & CFG_SRC_HANDSHAKE_EN) ? extract32(c->cfg, 3, 4) : -1;
>> +    dst_hs = (c->cfg & CFG_DST_HANDSHAKE_EN) ? extract32(c->cfg, 9, 4) : -1;
>> +
>> +    /* DMA src/dst sanity check */
>> +    if (cpu_physical_memory_is_io(src) && c->src_stride) {
>> +        fprintf(stderr,
>> +            "ftdmac020: src is an I/O device with non-fixed address?\n");
>> +        abort();
>> +    }
>
> This doesn't look like a QEMU fatal to me. Its seems like software
> usage policy of the DMA. Does the real hardware actually do any
> preventative action on striding io accesses? If not then I would
> either remove this assertion or at least demote it to a
> LOG_GUEST_ERROR. There may be corner case applications where this is
> valid. I think its also worthwhile trying to avoid use of
> cpu_physical_foo() functions from device land.
>

It's a programming error, which is used to identify possible bug in driver.
And I'll remove this sanity check in next release.

,>> +    if (cpu_physical_memory_is_io(dst) && c->dst_stride) {
>> +        fprintf(stderr,
>> +            "ftdmac020: dst is an I/O device with non-fixed address?\n");
>> +        abort();
>> +    }
>> +
>> +    while (c->len > 0) {
>> +        /*
>> +         * Postpone this DMA action
>> +         * if the corresponding dma request is not asserted
>> +         */
>> +        if ((src_hs >= 0) && !(s->req & BIT(src_hs))) {
>> +            break;
>> +        }
>> +        if ((dst_hs >= 0) && !(s->req & BIT(dst_hs))) {
>> +            break;
>> +        }
>> +
>> +        len = MIN(sizeof(buf), c->burst * (c->src_bw >> 3));
>> +
>> +        /* load data from source into local buffer */
>> +        if (c->src_stride) {
>> +            dma_memory_read(s->dma, src, buf, len);
>> +            src += len;
>> +        } else {
>> +            stride = c->src_bw >> 3;
>> +            for (i = 0; i < len; i += stride) {
>> +                dma_memory_read(s->dma, src, buf + i, stride);
>> +            }
>> +        }
>> +
>> +        /* DMA Hardware Handshake */
>> +        if (src_hs >= 0) {
>> +            qemu_set_irq(s->ack[src_hs], 1);
>> +        }
>> +
>> +        /* store data into destination from local buffer */
>> +        if (c->dst_stride) {
>> +            dma_memory_write(s->dma, dst, buf, len);
>> +            dst += len;
>> +        } else {
>> +            stride = c->dst_bw >> 3;
>> +            for (i = 0; i < len; i += stride) {
>> +                dma_memory_write(s->dma, dst, buf + i, stride);
>> +            }
>> +        }
>> +
>> +        /* DMA Hardware Handshake */
>> +        if (dst_hs >= 0) {
>> +            qemu_set_irq(s->ack[dst_hs], 1);
>> +        }
>> +
>> +        /* update the channel transfer size */
>> +        c->len -= len / (c->src_bw >> 3);
>> +
>> +        if (c->len == 0) {
>> +            /* update the channel transfer status */
>> +            if (!(c->ccr & CCR_MASK_TC)) {
>> +                s->tcsr |= BIT(c->id);
>> +                if (!(c->cfg & CFG_MASK_TCI)) {
>> +                    s->tcisr |= BIT(c->id);
>> +                }
>> +                ftdmac020_update_irq(s);
>> +            }
>> +            /* try to load next lld */
>> +            if (c->llp) {
>> +                c->llp_cnt += 1;
>> +                dma_memory_read(s->dma, c->llp, &desc, sizeof(desc));
>> +
>> +                desc.src  = le32_to_cpu(desc.src);
>> +                desc.dst  = le32_to_cpu(desc.dst);
>> +                desc.next = le32_to_cpu(desc.next);
>> +                desc.size = le32_to_cpu(desc.size);
>> +                desc.ctrl = le32_to_cpu(desc.ctrl);
>> +
>> +                c->src = desc.src;
>> +                c->dst = desc.dst;
>> +                c->llp = desc.next & 0xfffffffc;
>> +                c->len = desc.size & 0x003fffff;
>> +                c->ccr = (c->ccr & 0x78f8c081)
>> +                       | (extract32(desc.ctrl, 29, 3) << 24)
>> +                       | ((desc.ctrl & BIT(28)) ? CCR_MASK_TC : 0)
>> +                       | (extract32(desc.ctrl, 25, 3) << 11)
>> +                       | (extract32(desc.ctrl, 22, 3) << 8)
>> +                       | (extract32(desc.ctrl, 20, 2) << 5)
>> +                       | (extract32(desc.ctrl, 18, 2) << 3)
>> +                       | (extract32(desc.ctrl, 16, 2) << 1);
>> +                ftdmac020_chan_ccr_decode(c);
>> +
>> +                src = c->src;
>> +                dst = c->dst;
>> +            } else {
>> +                /* clear dma start bit */
>> +                c->ccr &= ~CCR_START;
>> +            }
>> +        }
>> +    }
>> +
>> +    /* update dma src/dst address */
>> +    c->src = src;
>> +    c->dst = dst;
>> +
>> +    s->busy = -1;
>> +}
>> +
>> +static void ftdmac020_chan_reset(Ftdmac020Chan *c)
>> +{
>> +    c->ccr = 0;
>> +    c->cfg = 0;
>> +    c->src = 0;
>> +    c->dst = 0;
>> +    c->llp = 0;
>> +    c->len = 0;
>> +}
>> +
>> +static void ftdmac020_bh(void *opaque)
>> +{
>> +    Ftdmac020State *s = FTDMAC020(opaque);
>> +    Ftdmac020Chan  *c = NULL;
>> +    int i, jobs, done;
>> +
>> +    ++s->bh_owner;
>> +    jobs = 0;
>> +    done = 0;
>> +    for (i = 0; i < 8; ++i) {
>> +        c = s->chan + i;
>> +        if (c->ccr & CCR_START) {
>> +            ++jobs;
>> +            ftdmac020_chan_start(c);
>> +            if (!(c->ccr & CCR_START)) {
>> +                ++done;
>> +            }
>> +        }
>> +    }
>> +    --s->bh_owner;
>> +
>> +    /*
>> +     * Devices those with an infinite FIFO (always ready for R/W)
>
> Grammar (- the "those"?).
>

Awkward, now you know my English really sucks. :P

>> +     * would trigger a new DMA handshake transaction here.
>> +     * (i.e. ftnandc021, ftsdc010)
>> +     */
>> +    if ((jobs - done) && s->req) {
>> +        qemu_bh_schedule(s->bh);
>> +    }
>> +}
>> +
>> +static void ftdmac020_handle_req(void *opaque, int line, int level)
>> +{
>> +    Ftdmac020State *s = FTDMAC020(opaque);
>> +
>> +    if (level) {
>> +        /*
>> +         * Devices those wait for data from externaI/O
>
> s/those/that
>
>> +         * would trigger a new DMA handshake transaction here.
>> +         * (i.e. ftssp010)
>
> A nitpick, but do you mean e.g. instead of i.e.?
>

Both, because FTSSP010 is the only one would trigger DMA in that way.

> Regards,
> Peter
>
>> +         */
>> +        if (!(s->req & BIT(line))) {
>> +            /* a simple workaround for BH reentry issue */
>> +            if (!s->bh_owner) {
>> +                qemu_bh_schedule(s->bh);
>> +            }
>> +        }
>> +        s->req |= BIT(line);
>> +    } else {
>> +        s->req &= ~BIT(line);
>> +        qemu_set_irq(s->ack[line], 0);
>> +    }
>> +}
>> +
>> +static void ftdmac020_chip_reset(Ftdmac020State *s)
>> +{
>> +    int i;
>> +
>> +    s->tcisr = 0;
>> +    s->eaisr = 0;
>> +    s->tcsr = 0;
>> +    s->easr = 0;
>> +    s->cesr = 0;
>> +    s->cbsr = 0;
>> +    s->csr  = 0;
>> +    s->sync = 0;
>> +
>> +    for (i = 0; i < 8; ++i) {
>> +        ftdmac020_chan_reset(s->chan + i);
>> +    }
>> +
>> +    /* We can assume our GPIO have been wired up now */
>> +    for (i = 0; i < 16; ++i) {
>> +        qemu_set_irq(s->ack[i], 0);
>> +    }
>> +    s->req = 0;
>> +}
>> +
>> +static uint64_t ftdmac020_mem_read(void *opaque, hwaddr addr, unsigned size)
>> +{
>> +    Ftdmac020State *s = FTDMAC020(opaque);
>> +    Ftdmac020Chan  *c = NULL;
>> +    uint32_t i, ret = 0;
>> +
>> +    switch (addr) {
>> +    case REG_ISR:
>> +        /* 1. Checking TC interrupts */
>> +        ret |= s->tcisr & 0xff;
>> +        /* 2. Checking Error interrupts */
>> +        ret |= s->eaisr & 0xff;
>> +        /* 3. Checking Abort interrupts */
>> +        ret |= (s->eaisr >> 16) & 0xff;
>> +        break;
>> +    case REG_TCISR:
>> +        return s->tcisr;
>> +    case REG_EAISR:
>> +        return s->eaisr;
>> +    case REG_TCSR:
>> +        return s->tcsr;
>> +    case REG_EASR:
>> +        return s->easr;
>> +    case REG_CESR:
>> +        for (i = 0; i < 8; ++i) {
>> +            c = s->chan + i;
>> +            ret |= (c->ccr & CCR_START) ? BIT(i) : 0;
>> +        }
>> +        break;
>> +    case REG_CBSR:
>> +        return (s->busy > 0) ? BIT(s->busy) : 0;
>> +    case REG_CSR:
>> +        return s->csr;
>> +    case REG_SYNC:
>> +        return s->sync;
>> +    case REG_REVISION:
>> +        /* rev. = 1.13.0 */
>> +        return 0x00011300;
>> +    case REG_FEATURE:
>> +        /* fifo = 32 bytes, support linked list, 8 channels, AHB0 only */
>> +        return 0x00008105;
>> +    case REG_CHAN_BASE(0) ... REG_CHAN_BASE(7) + 0x14:
>> +        c = s->chan + REG_CHAN_ID(addr);
>> +        switch (addr & 0x1f) {
>> +        case REG_CHAN_CCR:
>> +            return c->ccr;
>> +        case REG_CHAN_CFG:
>> +            ret = c->cfg;
>> +            ret |= (s->busy == c->id) ? (1 << 8) : 0;
>> +            ret |= (c->llp_cnt & 0x0f) << 16;
>> +            break;
>> +        case REG_CHAN_SRC:
>> +            return c->src;
>> +        case REG_CHAN_DST:
>> +            return c->dst;
>> +        case REG_CHAN_LLP:
>> +            return c->llp;
>> +        case REG_CHAN_LEN:
>> +            return c->len;
>> +        default:
>> +            qemu_log_mask(LOG_GUEST_ERROR,
>> +                "ftdmac020: undefined memory access@%#" HWADDR_PRIx "\n",
>> +                addr);
>> +            break;
>> +        }
>> +        break;
>> +    default:
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +            "ftdmac020: undefined memory access@%#" HWADDR_PRIx "\n", addr);
>> +        break;
>> +    }
>> +
>> +    return ret;
>> +}
>> +
>> +static void ftdmac020_mem_write(void    *opaque,
>> +                                hwaddr   addr,
>> +                                uint64_t val,
>> +                                unsigned size)
>> +{
>> +    Ftdmac020State *s = FTDMAC020(opaque);
>> +    Ftdmac020Chan  *c = NULL;
>> +
>> +    switch (addr) {
>> +    case REG_TCCLR:
>> +        s->tcisr &= ~((uint32_t)val);
>> +        s->tcsr &= ~((uint32_t)val);
>> +        ftdmac020_update_irq(s);
>> +        break;
>> +    case REG_EACLR:
>> +        s->eaisr &= ~((uint32_t)val);
>> +        s->easr &= ~((uint32_t)val);
>> +        ftdmac020_update_irq(s);
>> +        break;
>> +    case REG_CSR:
>> +        s->csr = (uint32_t)val;
>> +        break;
>> +    case REG_SYNC:
>> +        /* In QEMU, devices are all in the same clock domain
>> +         * so there is nothing needs to be done.
>> +         */
>> +        s->sync = (uint32_t)val;
>> +        break;
>> +    case REG_CHAN_BASE(0) ... REG_CHAN_BASE(7) + 0x14:
>> +        c = s->chan + REG_CHAN_ID(addr);
>> +        switch (addr & 0x1f) {
>> +        case REG_CHAN_CCR:
>> +            if (!(c->ccr & CCR_START) && (val & CCR_START)) {
>> +                c->llp_cnt = 0;
>> +            }
>> +            c->ccr = (uint32_t)val & 0x87FFBFFF;
>> +            if (c->ccr & CCR_START) {
>> +                ftdmac020_chan_ccr_decode(c);
>> +                /* kick-off DMA engine */
>> +                qemu_bh_schedule(s->bh);
>> +            }
>> +            break;
>> +        case REG_CHAN_CFG:
>> +            c->cfg = (uint32_t)val & 0x3eff;
>> +            break;
>> +        case REG_CHAN_SRC:
>> +            c->src = (uint32_t)val;
>> +            break;
>> +        case REG_CHAN_DST:
>> +            c->dst = (uint32_t)val;
>> +            break;
>> +        case REG_CHAN_LLP:
>> +            c->llp = (uint32_t)val & 0xfffffffc;
>> +            break;
>> +        case REG_CHAN_LEN:
>> +            c->len = (uint32_t)val & 0x003fffff;
>> +            break;
>> +        default:
>> +            qemu_log_mask(LOG_GUEST_ERROR,
>> +                "ftdmac020: undefined memory access@%#" HWADDR_PRIx "\n",
>> +                addr);
>> +            break;
>> +        }
>> +        break;
>> +    default:
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +            "ftdmac020: undefined memory access@%#" HWADDR_PRIx "\n", addr);
>> +        break;
>> +    }
>> +}
>> +
>> +static const MemoryRegionOps mmio_ops = {
>> +    .read  = ftdmac020_mem_read,
>> +    .write = ftdmac020_mem_write,
>> +    .endianness = DEVICE_LITTLE_ENDIAN,
>> +    .valid = {
>> +        .min_access_size = 4,
>> +        .max_access_size = 4
>> +    }
>> +};
>> +
>> +static void ftdmac020_reset(DeviceState *ds)
>> +{
>> +    Ftdmac020State *s = FTDMAC020(SYS_BUS_DEVICE(ds));
>> +
>> +    ftdmac020_chip_reset(s);
>> +}
>> +
>> +static void ftdmac020_realize(DeviceState *dev, Error **errp)
>> +{
>> +    Ftdmac020State *s = FTDMAC020(dev);
>> +    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
>> +    int i;
>> +
>> +    memory_region_init_io(&s->iomem,
>> +                          &mmio_ops,
>> +                          s,
>> +                          TYPE_FTDMAC020,
>> +                          0x1000);
>> +    sysbus_init_mmio(sbd, &s->iomem);
>> +    for (i = 0; i < 3; ++i) {
>> +        sysbus_init_irq(sbd, &s->irq[i]);
>> +    }
>> +    qdev_init_gpio_in(&sbd->qdev, ftdmac020_handle_req, 16);
>> +    qdev_init_gpio_out(&sbd->qdev, s->ack, 16);
>> +
>> +    s->busy = -1;
>> +    s->dma = &dma_context_memory;
>> +    s->bh = qemu_bh_new(ftdmac020_bh, s);
>> +    for (i = 0; i < 8; ++i) {
>> +        Ftdmac020Chan *c = s->chan + i;
>> +        c->id   = i;
>> +        c->chip = s;
>> +    }
>> +}
>> +
>> +static const VMStateDescription vmstate_ftdmac020 = {
>> +    .name = TYPE_FTDMAC020,
>> +    .version_id = 1,
>> +    .minimum_version_id = 1,
>> +    .minimum_version_id_old = 1,
>> +    .fields = (VMStateField[]) {
>> +        VMSTATE_UINT32(tcisr, Ftdmac020State),
>> +        VMSTATE_UINT32(eaisr, Ftdmac020State),
>> +        VMSTATE_UINT32(tcsr, Ftdmac020State),
>> +        VMSTATE_UINT32(easr, Ftdmac020State),
>> +        VMSTATE_UINT32(cesr, Ftdmac020State),
>> +        VMSTATE_UINT32(cbsr, Ftdmac020State),
>> +        VMSTATE_UINT32(csr, Ftdmac020State),
>> +        VMSTATE_UINT32(sync, Ftdmac020State),
>> +        VMSTATE_END_OF_LIST()
>> +    }
>> +};
>> +
>> +static void ftdmac020_class_init(ObjectClass *klass, void *data)
>> +{
>> +    DeviceClass *dc = DEVICE_CLASS(klass);
>> +
>> +    dc->vmsd    = &vmstate_ftdmac020;
>> +    dc->reset   = ftdmac020_reset;
>> +    dc->realize = ftdmac020_realize;
>> +    dc->no_user = 1;
>> +}
>> +
>> +static const TypeInfo ftdmac020_info = {
>> +    .name           = TYPE_FTDMAC020,
>> +    .parent         = TYPE_SYS_BUS_DEVICE,
>> +    .instance_size  = sizeof(Ftdmac020State),
>> +    .class_init     = ftdmac020_class_init,
>> +};
>> +
>> +static void ftdmac020_register_types(void)
>> +{
>> +    type_register_static(&ftdmac020_info);
>> +}
>> +
>> +type_init(ftdmac020_register_types)
>> diff --git a/hw/ftdmac020.h b/hw/ftdmac020.h
>> new file mode 100644
>> index 0000000..86ee58c
>> --- /dev/null
>> +++ b/hw/ftdmac020.h
>> @@ -0,0 +1,107 @@
>> +/*
>> + * QEMU model of the FTDMAC020 DMA Controller
>> + *
>> + * Copyright (C) 2012 Faraday Technology
>> + * Written by Dante Su <dantesu@faraday-tech.com>
>> + *
>> + * This file is licensed under GNU GPL v2+.
>> + *
>> + * Note: The FTDMAC020 decreasing address mode is not implemented.
>> + */
>> +
>> +#ifndef HW_ARM_FTDMAC020_H
>> +#define HW_ARM_FTDMAC020_H
>> +
>> +#include "qemu/bitops.h"
>> +
>> +#define REG_ISR         0x00    /* Interrupt Status Register */
>> +#define REG_TCISR       0x04    /* Terminal Count Interrupt Status Register */
>> +#define REG_TCCLR       0x08    /* Terminal Count Status Clear Register */
>> +#define REG_EAISR       0x0c    /* Error/Abort Interrupt Status Register */
>> +#define REG_EACLR       0x10    /* Error/Abort Status Clear Register */
>> +#define REG_TCSR        0x14    /* Terminal Count Status Register */
>> +#define REG_EASR        0x18    /* Error/Abort Status Register */
>> +#define REG_CESR        0x1c    /* Channel Enable Status Register */
>> +#define REG_CBSR        0x20    /* Channel Busy Status Register */
>> +#define REG_CSR         0x24    /* Configuration Status Register */
>> +#define REG_SYNC        0x28    /* Synchronization Register */
>> +#define REG_REVISION    0x30
>> +#define REG_FEATURE     0x34
>> +
>> +#define REG_CHAN_ID(addr)   (((addr) - 0x100) >> 5)
>> +#define REG_CHAN_BASE(ch)   (0x100 + ((ch) << 5))
>> +
>> +#define REG_CHAN_CCR        0x00
>> +#define REG_CHAN_CFG        0x04
>> +#define REG_CHAN_SRC        0x08
>> +#define REG_CHAN_DST        0x0C
>> +#define REG_CHAN_LLP        0x10
>> +#define REG_CHAN_LEN        0x14
>> +
>> +/*
>> + * Feature register
>> + */
>> +#define FEATURE_NCHAN(f)    (((f) >> 12) & 0xF)
>> +#define FEATURE_BRIDGE(f)   ((f) & BIT(10))
>> +#define FEATURE_DUALBUS(f)  ((f) & BIT(9))
>> +#define FEATURE_LLP(f)      ((f) & BIT(8))
>> +#define FEATURE_FIFOAW(f)   ((f) & 0xF)
>> +
>> +/*
>> + * Channel control register
>> + */
>> +#define CCR_START           BIT(0)
>> +#define CCR_DST_M1          BIT(1)  /* dst is a slave device of AHB1 */
>> +#define CCR_SRC_M1          BIT(2)  /* src is a slave device of AHB1 */
>> +#define CCR_DST_INC         (0 << 3)/* dst addr: next = curr++ */
>> +#define CCR_DST_DEC         (1 << 3)/* dst addr: next = curr-- */
>> +#define CCR_DST_FIXED       (2 << 3)
>> +#define CCR_SRC_INC         (0 << 5)/* src addr: next = curr++ */
>> +#define CCR_SRC_DEC         (1 << 5)/* src addr: next = curr-- */
>> +#define CCR_SRC_FIXED       (2 << 5)
>> +#define CCR_HANDSHAKE       BIT(7)  /* DMA HW handshake enabled */
>> +#define CCR_DST_WIDTH_8     (0 << 8)
>> +#define CCR_DST_WIDTH_16    (1 << 8)
>> +#define CCR_DST_WIDTH_32    (2 << 8)
>> +#define CCR_DST_WIDTH_64    (3 << 8)
>> +#define CCR_SRC_WIDTH_8     (0 << 11)
>> +#define CCR_SRC_WIDTH_16    (1 << 11)
>> +#define CCR_SRC_WIDTH_32    (2 << 11)
>> +#define CCR_SRC_WIDTH_64    (3 << 11)
>> +#define CCR_ABORT           BIT(15)
>> +#define CCR_BURST_1         (0 << 16)
>> +#define CCR_BURST_4         (1 << 16)
>> +#define CCR_BURST_8         (2 << 16)
>> +#define CCR_BURST_16        (3 << 16)
>> +#define CCR_BURST_32        (4 << 16)
>> +#define CCR_BURST_64        (5 << 16)
>> +#define CCR_BURST_128       (6 << 16)
>> +#define CCR_BURST_256       (7 << 16)
>> +#define CCR_PRIVILEGED      BIT(19)
>> +#define CCR_BUFFERABLE      BIT(20)
>> +#define CCR_CACHEABLE       BIT(21)
>> +#define CCR_PRIO_0          (0 << 22)
>> +#define CCR_PRIO_1          (1 << 22)
>> +#define CCR_PRIO_2          (2 << 22)
>> +#define CCR_PRIO_3          (3 << 22)
>> +#define CCR_FIFOTH_1        (0 << 24)
>> +#define CCR_FIFOTH_2        (1 << 24)
>> +#define CCR_FIFOTH_4        (2 << 24)
>> +#define CCR_FIFOTH_8        (3 << 24)
>> +#define CCR_FIFOTH_16       (4 << 24)
>> +#define CCR_MASK_TC         BIT(31) /* DMA Transfer terminated(finished) */
>> +
>> +/*
>> + * Channel configuration register
>> + */
>> +#define CFG_MASK_TCI            BIT(0)    /* disable tc interrupt */
>> +#define CFG_MASK_EI             BIT(1)    /* disable error interrupt */
>> +#define CFG_MASK_AI             BIT(2)    /* disable abort interrupt */
>> +#define CFG_SRC_HANDSHAKE(x)    (((x) & 0xf) << 3)
>> +#define CFG_SRC_HANDSHAKE_EN    BIT(7)
>> +#define CFG_BUSY                BIT(8)
>> +#define CFG_DST_HANDSHAKE(x)    (((x) & 0xf) << 9)
>> +#define CFG_DST_HANDSHAKE_EN    BIT(13)
>> +#define CFG_LLP_CNT(cfg)        (((cfg) >> 16) & 0xf)
>> +
>> +#endif    /* HW_ARM_FTDMAC020_H */
>> --
>> 1.7.9.5
>>
>>



--
Best wishes,
Kuo-Jung Su

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Qemu-devel] [PATCH v9 16/24] The FTSSP010 is a multi-function synchronous serial port interface controller which supports SSP, SPI, I2S, AC97 and SPDIF.
  2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 16/24] The FTSSP010 is a multi-function synchronous serial port interface controller which supports SSP, SPI, I2S, AC97 and SPDIF Kuo-Jung Su
@ 2013-03-31  2:39   ` Peter Crosthwaite
  2013-04-01  1:18     ` Kuo-Jung Su
  0 siblings, 1 reply; 35+ messages in thread
From: Peter Crosthwaite @ 2013-03-31  2:39 UTC (permalink / raw)
  To: Kuo-Jung Su
  Cc: Peter Maydell, i.mitsyanko, qemu-devel, Blue Swirl, Paul Brook,
	Kuo-Jung Su, Andreas, fred.konrad

Hi Kuo-Jung

I think you may have accidentally dropped your subject line and
promoted your long commit message to subject line. Looks better in
previous versions.

On Mon, Mar 25, 2013 at 10:09 PM, Kuo-Jung Su <dantesu@gmail.com> wrote:
>
> From: Kuo-Jung Su <dantesu@faraday-tech.com>
>
> Only I2S and SPI protocol have been implemented in this patch.
>
> Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
> ---
>  hw/arm/Makefile.objs    |    3 +-
>  hw/arm/ftplat_a369.c    |   31 +++
>  hw/arm/ftplat_a369soc.c |   17 ++
>  hw/faraday.h            |    3 +
>  hw/ftssp010.c           |  504 +++++++++++++++++++++++++++++++++++++++++++++++
>  hw/ftssp010.h           |   96 +++++++++
>  6 files changed, 653 insertions(+), 1 deletion(-)
>  create mode 100644 hw/ftssp010.c
>  create mode 100644 hw/ftssp010.h
>
> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
> index 2bb67f7..42c8472 100644
> --- a/hw/arm/Makefile.objs
> +++ b/hw/arm/Makefile.objs
> @@ -25,7 +25,8 @@ obj-y += strongarm.o
>  obj-y += imx_serial.o imx_ccm.o imx_timer.o imx_avic.o
>  obj-$(CONFIG_KVM) += kvm/arm_gic.o
>  obj-y += ftintc020.o ftahbc020.o ftddrii030.o ftpwmtmr010.o ftwdt010.o \
> -                ftrtc011.o ftdmac020.o ftapbbrg020.o ftnandc021.o fti2c010.o
> +                ftrtc011.o ftdmac020.o ftapbbrg020.o ftnandc021.o fti2c010.o \
> +                ftssp010.o
>
>  obj-y := $(addprefix ../,$(obj-y))
>
> diff --git a/hw/arm/ftplat_a369.c b/hw/arm/ftplat_a369.c
> index 827c58a..922fb55 100644
> --- a/hw/arm/ftplat_a369.c
> +++ b/hw/arm/ftplat_a369.c
> @@ -11,6 +11,7 @@
>  #include "hw/arm-misc.h"
>  #include "hw/devices.h"
>  #include "hw/i2c.h"
> +#include "hw/audio.h"
>  #include "hw/boards.h"
>  #include "hw/ssi.h"
>  #include "net/net.h"
> @@ -28,6 +29,7 @@ static void a369_system_reset(void *opaque)
>
>  static void a369_board_init(QEMUMachineInitArgs *args)
>  {
> +    int i, nr_flash;
>      ARMCPU *cpu;
>      DeviceState *ds;
>      FaradaySoCState *s;
> @@ -79,6 +81,35 @@ static void a369_board_init(QEMUMachineInitArgs *args)
>          abort();
>      }
>
> +    /* Attach the spi flash to ftssp010.0 */
> +    nr_flash = 1;
> +    for (i = 0; i < nr_flash; i++) {
> +        SSIBus *ssi = (SSIBus *)qdev_get_child_bus(s->spi[0], "spi");
> +        DeviceState *fl = ssi_create_slave_no_init(ssi, "w25q64");
> +        qemu_irq cs_line;
> +
> +        qdev_init_nofail(fl);
> +        cs_line = qdev_get_gpio_in(fl, 0);
> +        sysbus_connect_irq(SYS_BUS_DEVICE(s->spi[0]), i + 1, cs_line);
> +    }
> +
> +    /* Attach the wm8731 to fti2c010.0 & ftssp010.0 */
> +    for (i = 0; i < 1; ++i) {
> +        i2c_bus *i2c = (i2c_bus *)qdev_get_child_bus(s->i2c[0], "i2c");
> +        ds = i2c_create_slave(i2c, "wm8731", 0x1B);
> +        object_property_set_link(OBJECT(s->i2s[0]),
> +                                 OBJECT(ds),
> +                                 "codec",
> +                                 &local_errp);
> +        if (local_errp) {
> +            fprintf(stderr, "a369: Unable to set codec link for FTSSP010\n");
> +            abort();
> +        }
> +        audio_codec_data_req_set(ds,
> +                                 ftssp010_i2s_data_req,
> +                                 s->i2s[0]);
> +    }
> +
>      /* System start-up */
>
>      if (args->kernel_filename) {
> diff --git a/hw/arm/ftplat_a369soc.c b/hw/arm/ftplat_a369soc.c
> index b6e82ad..9391764 100644
> --- a/hw/arm/ftplat_a369soc.c
> +++ b/hw/arm/ftplat_a369soc.c
> @@ -206,6 +206,23 @@ static void a369soc_chip_init(FaradaySoCState *s)
>      s->i2c[0] = ds;
>      ds = sysbus_create_simple("fti2c010", 0x92A00000, s->pic[52]);
>      s->i2c[1] = ds;
> +
> +    /* ftssp010 */
> +    ds = sysbus_create_simple("ftssp010", 0x92700000, s->pic[49]);
> +    s->spi[0] = ds;
> +    s->i2s[0] = ds;
> +
> +    /* ftssp010 - DMA (Tx) */
> +    ack = qdev_get_gpio_in(ds, 0);
> +    req = qdev_get_gpio_in(s->pdma[0], 7);
> +    qdev_connect_gpio_out(s->pdma[0], 7, ack);
> +    qdev_connect_gpio_out(ds, 0, req);
> +
> +    /* ftssp010 - DMA (Rx) */
> +    ack = qdev_get_gpio_in(ds, 1);
> +    req = qdev_get_gpio_in(s->pdma[0], 8);
> +    qdev_connect_gpio_out(s->pdma[0], 8, ack);
> +    qdev_connect_gpio_out(ds, 1, req);
>  }
>
>  static void a369soc_realize(DeviceState *dev, Error **errp)
> diff --git a/hw/faraday.h b/hw/faraday.h
> index 7373ba0..39a608c 100644
> --- a/hw/faraday.h
> +++ b/hw/faraday.h
> @@ -121,4 +121,7 @@ static inline void faraday_soc_ahb_remap(FaradaySoCState *s, bool active)
>      }
>  }
>
> +/* ftssp010.c */
> +void ftssp010_i2s_data_req(void *opaque, int tx, int rx);
> +
>  #endif
> diff --git a/hw/ftssp010.c b/hw/ftssp010.c
> new file mode 100644
> index 0000000..fe1ddbb
> --- /dev/null
> +++ b/hw/ftssp010.c
> @@ -0,0 +1,504 @@
> +/*
> + * QEMU model of the FTSSP010 Controller
> + *
> + * Copyright (C) 2012 Faraday Technology
> + * Written by Dante Su <dantesu@faraday-tech.com>
> + *
> + * This file is licensed under GNU GPL v2+.
> + */
> +
> +#include "hw/sysbus.h"
> +#include "hw/i2c.h"
> +#include "hw/ssi.h"
> +#include "hw/audio.h"
> +#include "sysemu/sysemu.h"
> +#include "qemu/fifo8.h"
> +
> +#include "hw/faraday.h"
> +#include "hw/ftssp010.h"
> +
> +#define CFG_FIFO_DEPTH  16
> +
> +#define TYPE_FTSSP010   "ftssp010"
> +
> +typedef struct Ftssp010State {
> +    /*< private >*/
> +    SysBusDevice parent;
> +
> +    /*< public >*/
> +    MemoryRegion mmio;
> +
> +    qemu_irq irq;
> +    SSIBus *spi;
> +    AudioCodecState *codec;
> +
> +    uint8_t num_cs;
> +    qemu_irq *cs_lines;
> +
> +    Fifo8 rx_fifo;
> +    Fifo8 tx_fifo;
> +
> +    uint8_t tx_thres;
> +    uint8_t rx_thres;
> +
> +    int busy;

Constant 0 AFAICT. Every set of busy to 1 is followed by a clear
before fn return. I cannot see a code path where anyone reads busy as
1? If there is such a code path then busy should be part of the VMSD
as well.

> +    uint8_t bw;
> +
> +    /* DMA hardware handshake */
> +    qemu_irq req[2];    /* 0 - Tx, 1 - Rx */
> +
> +    /* HW register caches */
> +

Are they really caches?

> +    uint32_t cr0;
> +    uint32_t cr1;
> +    uint32_t cr2;
> +    uint32_t icr;    /* interrupt control register */
> +    uint32_t isr;    /* interrupt status register */
> +
> +} Ftssp010State;
> +
> +#define FTSSP010(obj) \
> +    OBJECT_CHECK(Ftssp010State, obj, TYPE_FTSSP010)
> +
> +/* Update interrupts.  */
> +static void ftssp010_update(Ftssp010State *s)
> +{
> +    if (!(s->cr2 & CR2_SSPEN)) {
> +        return;
> +    }
> +
> +    /* tx fifo status update */
> +    if ((s->tx_fifo.num / (s->bw >> 3)) <= s->tx_thres) {
> +        s->isr |=  ISR_TFTHI;
> +        if (s->icr & ICR_TFDMA) {
> +            qemu_set_irq(s->req[0], 1);
> +        }
> +    } else {
> +        s->isr &= ~ISR_TFTHI;
> +    }
> +
> +    /* rx fifo status update */
> +    switch (s->cr0 & CR0_FFMT_MASK) {
> +    case CR0_FFMT_SPI:
> +        s->isr |=  ISR_RFTHI;
> +        if (s->icr & ICR_RFDMA) {
> +            qemu_set_irq(s->req[1], 1);
> +        }
> +        break;
> +    default:
> +        if ((s->rx_fifo.num / (s->bw >> 3)) >= s->rx_thres) {
> +            s->isr |=  ISR_RFTHI;
> +            if (s->icr & ICR_RFDMA) {
> +                qemu_set_irq(s->req[1], 1);
> +            }
> +        } else {
> +            s->isr &= ~ISR_RFTHI;
> +        }
> +        break;
> +    }
> +
> +    /* update the interrupt signal */
> +    qemu_set_irq(s->irq, (s->icr & s->isr) ? 1 : 0);
> +}
> +
> +static void ftssp010_handle_ack(void *opaque, int line, int level)
> +{
> +    Ftssp010State *s = FTSSP010(opaque);
> +
> +    switch (line) {
> +    case 0:    /* Tx */
> +        if (s->icr & ICR_TFDMA) {
> +            if (level) {
> +                qemu_set_irq(s->req[0], 0);
> +            } else if ((s->tx_fifo.num / (s->bw >> 3)) <= s->tx_thres) {
> +                qemu_set_irq(s->req[0], 1);
> +            }
> +        }
> +        break;
> +    case 1:    /* Rx */
> +        if (s->icr & ICR_RFDMA) {
> +            if (level) {
> +                qemu_set_irq(s->req[1], 0);
> +            } else {
> +                switch (s->cr0 & CR0_FFMT_MASK) {
> +                case CR0_FFMT_SPI:
> +                    qemu_set_irq(s->req[1], 1);
> +                    break;
> +                default:
> +                    if ((s->rx_fifo.num / (s->bw >> 3)) >= s->rx_thres) {
> +                        qemu_set_irq(s->req[1], 1);
> +                    }
> +                    break;
> +                }
> +            }
> +        }
> +        break;
> +    default:
> +        break;
> +    }
> +}
> +
> +void ftssp010_i2s_data_req(void *opaque, int tx, int rx)
> +{
> +    int i, len;
> +    uint32_t sample;
> +    Ftssp010State *s = FTSSP010(opaque);
> +
> +    if (!(s->cr2 & CR2_SSPEN)) {
> +        return;
> +    }
> +
> +    if ((s->cr0 & CR0_FFMT_MASK) != CR0_FFMT_I2S) {
> +        return;
> +    }
> +
> +    s->busy = 1;
> +
> +    if ((s->cr2 & CR2_TXEN) && (s->cr2 & CR2_TXDOE)) {
> +        len = tx * (s->bw / 8);
> +        while (!fifo8_is_empty(&s->tx_fifo) && len > 0) {
> +            sample = 0;
> +            for (i = 0; i < (s->bw >> 3) && len > 0; i++, len--) {
> +                sample = deposit32(sample, i * 8, 8, fifo8_pop(&s->tx_fifo));
> +            }
> +            audio_codec_dac_dat(s->codec, sample);
> +        }
> +
> +        if (fifo8_is_empty(&s->tx_fifo) && len > 0) {
> +            s->isr |= ISR_TFURI;
> +        }
> +    }
> +
> +    if (s->cr2 & CR2_RXEN) {
> +        len = rx * (s->bw / 8);
> +        while (!fifo8_is_full(&s->rx_fifo) && len > 0) {
> +            sample = audio_codec_adc_dat(s->codec);
> +            for (i = 0; i < (s->bw >> 3) && len > 0; i++, len--) {
> +                fifo8_push(&s->rx_fifo, extract32(sample, i * 8, 8));
> +            }
> +        }
> +
> +        if (fifo8_is_full(&s->rx_fifo) && len > 0) {
> +            s->isr |= ISR_RFORI;
> +        }
> +    }
> +
> +    s->busy = 0;
> +
> +    ftssp010_update(s);
> +}
> +
> +static void ftssp010_spi_tx(Ftssp010State *s)
> +{
> +    if (!(s->cr2 & CR2_TXEN)) {
> +        return;
> +    }
> +
> +    s->busy = 1;
> +
> +    if (fifo8_is_empty(&s->tx_fifo)) {
> +        s->isr |= ISR_TFURI;
> +    }
> +
> +    while (!fifo8_is_empty(&s->tx_fifo)) {
> +        ssi_transfer(s->spi, fifo8_pop(&s->tx_fifo));
> +    }
> +
> +    s->busy = 0;
> +}
> +
> +static uint64_t
> +ftssp010_mem_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    Ftssp010State *s = FTSSP010(opaque);
> +    uint32_t i, ret = 0;
> +
> +    if (addr & 0x03) {
> +        fprintf(stderr, "ftssp010: "
> +                 "Although ftssp010 supports byte/half-word access, "
> +                 "the target address still needs to be word aligned\n");
> +        abort();
> +    }
> +
> +    switch (addr) {
> +    case REG_CR0:    /* Control Register 0 */
> +        return s->cr0;
> +    case REG_CR1:    /* Control Register 1 */
> +        return s->cr1;
> +    case REG_CR2:    /* Control Register 2 */
> +        return s->cr2;
> +    case REG_SR:    /* Status Register */
> +        ret |= s->busy ? SR_BUSY : 0;
> +        /* tx fifo status */
> +        ret |= SR_TFVE(s->tx_fifo.num / (s->bw >> 3));
> +        if (!fifo8_is_full(&s->tx_fifo)) {
> +            ret |= SR_TFNF;
> +        }
> +        /* rx fifo status */
> +        switch (s->cr0 & CR0_FFMT_MASK) {
> +        case CR0_FFMT_SPI:
> +            ret |= SR_RFF | SR_RFVE(CFG_FIFO_DEPTH);
> +            break;
> +        case CR0_FFMT_I2S:
> +            ret |= SR_RFVE(s->rx_fifo.num / (s->bw >> 3));
> +            if (fifo8_is_full(&s->rx_fifo)) {
> +                ret |= SR_RFF;
> +            }
> +            break;
> +        default:
> +            break;
> +        }
> +        break;
> +    case REG_ICR:    /* Interrupt Control Register */
> +        return s->icr;
> +    case REG_ISR:    /* Interrupt Status Register */
> +        ret = s->isr;
> +        s->isr &= ~ISR_RCMASK;
> +        ftssp010_update(s);
> +        break;
> +    case REG_DR:    /* Data Register */
> +        if (!(s->cr2 & CR2_SSPEN)) {
> +            break;
> +        }
> +        if (!(s->cr2 & CR2_RXEN)) {
> +            break;
> +        }
> +        switch (s->cr0 & CR0_FFMT_MASK) {
> +        case CR0_FFMT_SPI:
> +            for (i = 0; i < (s->bw >> 3); i++) {
> +                ret = deposit32(ret, i * 8, 8, ssi_transfer(s->spi, 0));
> +            }
> +            break;
> +        case CR0_FFMT_I2S:
> +            for (i = 0; i < (s->bw >> 3); i++) {
> +                if (fifo8_is_empty(&s->rx_fifo)) {
> +                    break;
> +                }
> +                ret = deposit32(ret, i * 8, 8, fifo8_pop(&s->rx_fifo));
> +            }
> +            break;
> +        default:
> +            break;
> +        }
> +        ftssp010_update(s);
> +        break;
> +    case REG_REVR:
> +        return 0x00011901;    /* ver. 1.19.1 */
> +    case REG_FEAR:
> +        return 0x660f0f1f;    /* SPI+I2S, FIFO=16 */
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +            "ftssp010: undefined memory access@%#" HWADDR_PRIx "\n", addr);
> +        break;
> +    }
> +
> +    return ret;
> +}
> +
> +static void
> +ftssp010_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
> +{
> +    int i;
> +    Ftssp010State *s = FTSSP010(opaque);
> +
> +    if (addr & 0x03) {
> +        fprintf(stderr, "ftssp010: "
> +                 "Although ftssp010 supports byte/half-word access, "
> +                 "the target address still needs to be word aligned\n");
> +        abort();

Guest error. Buggy guests should not crash the VM.

> +    }
> +
> +    switch (addr) {
> +    case REG_CR0:    /* Control Register 0 */
> +        s->cr0 = (uint32_t)val;
> +        break;
> +    case REG_CR1:    /* Control Register 1 */
> +        s->cr1 = (uint32_t)val;
> +        s->bw  = extract32(s->cr1, 16, 7) + 1;
> +        break;
> +    case REG_CR2:    /* Control Register 2 */
> +        s->cr2 = (uint32_t)val;
> +        if (s->cr2 & CR2_SSPRST) {
> +            fifo8_reset(&s->tx_fifo);
> +            fifo8_reset(&s->rx_fifo);
> +            s->busy = 0;
> +            s->cr2 &= ~(CR2_SSPRST | CR2_TXFCLR | CR2_RXFCLR);
> +            if (s->cr0 & CR0_FLASH) {
> +                int cs = extract32(s->cr2, 10, 2);
> +                qemu_set_irq(s->cs_lines[cs], 1);
> +                s->cr2 |= CR2_FS;
> +            };
> +        }
> +        if (s->cr2 & CR2_TXFCLR) {
> +            fifo8_reset(&s->tx_fifo);
> +            s->cr2 &= ~CR2_TXFCLR;
> +        }
> +        if (s->cr2 & CR2_RXFCLR) {
> +            fifo8_reset(&s->rx_fifo);
> +            s->cr2 &= ~CR2_RXFCLR;
> +        }
> +        if (s->cr0 & CR0_FLASH) {
> +            int cs = extract32(s->cr2, 10, 2);
> +            qemu_set_irq(s->cs_lines[cs], (s->cr2 & CR2_FS) ? 1 : 0);
> +        }
> +        if (s->cr2 & CR2_SSPEN) {
> +            switch (s->cr0 & CR0_FFMT_MASK) {
> +            case CR0_FFMT_SPI:
> +                ftssp010_spi_tx(s);
> +                break;
> +            default:
> +                break;
> +            }
> +        }
> +        ftssp010_update(s);
> +        break;
> +    case REG_ICR:    /* Interrupt Control Register */
> +        s->icr = (uint32_t)val;
> +        s->tx_thres = extract32(s->icr, 12, 5);
> +        s->rx_thres = extract32(s->icr,  7, 5);
> +        break;
> +    case REG_DR:    /* Data Register */
> +        if (!(s->cr2 & CR2_SSPEN)) {
> +            break;
> +        }
> +        for (i = 0; i < (s->bw >> 3) && !fifo8_is_full(&s->tx_fifo); i++) {
> +            fifo8_push(&s->tx_fifo, extract32((uint32_t)val, i * 8, 8));
> +        }
> +        switch (s->cr0 & CR0_FFMT_MASK) {
> +        case CR0_FFMT_SPI:
> +            ftssp010_spi_tx(s);
> +            break;
> +        default:
> +            break;
> +        }
> +        ftssp010_update(s);
> +        break;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +            "ftssp010: undefined memory access@%#" HWADDR_PRIx "\n", addr);
> +        break;
> +    }
> +}
> +
> +static const MemoryRegionOps mmio_ops = {
> +    .read  = ftssp010_mem_read,
> +    .write = ftssp010_mem_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .valid = {
> +        .min_access_size = 1,
> +        .max_access_size = 4
> +    }
> +};
> +
> +static void ftssp010_reset(DeviceState *ds)
> +{
> +    Ftssp010State *s = FTSSP010(SYS_BUS_DEVICE(ds));
> +    Error *local_errp = NULL;
> +
> +    s->codec = AUDIO_CODEC(object_property_get_link(OBJECT(s),
> +                            "codec", &local_errp));
> +    if (local_errp) {
> +        fprintf(stderr, "ftssp010: Unable to get codec link\n");
> +        abort();
> +    }
> +
> +    s->busy = 0;
> +    s->bw   = 8;    /* 8-bit */
> +
> +    s->cr0 = 0;
> +    s->cr1 = 0;
> +    s->cr2 = 0;
> +    s->tx_thres = 4;
> +    s->rx_thres = 4;
> +    s->icr = ICR_TFTHOD(4) | ICR_RFTHOD(4);
> +    s->isr = ISR_TFTHI;
> +
> +    fifo8_reset(&s->tx_fifo);
> +    fifo8_reset(&s->rx_fifo);
> +
> +    ftssp010_update(s);
> +}
> +
> +static void ftssp010_realize(DeviceState *dev, Error **errp)
> +{
> +    Ftssp010State *s = FTSSP010(dev);
> +    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
> +    int i;
> +
> +    s->spi = ssi_create_bus(&sbd->qdev, "spi");
> +
> +    fifo8_create(&s->tx_fifo, CFG_FIFO_DEPTH * 4);
> +    fifo8_create(&s->rx_fifo, CFG_FIFO_DEPTH * 4);
> +
> +    memory_region_init_io(&s->mmio,
> +                          &mmio_ops,
> +                          s,
> +                          TYPE_FTSSP010,
> +                          0x1000);
> +    sysbus_init_mmio(sbd, &s->mmio);
> +    sysbus_init_irq(sbd, &s->irq);
> +
> +    s->num_cs = 4;
> +    s->cs_lines = g_new(qemu_irq, s->num_cs);

g_new0

Regards,
Peter

> +    ssi_auto_connect_slaves(dev, s->cs_lines, s->spi);
> +    for (i = 0; i < s->num_cs; ++i) {
> +        sysbus_init_irq(sbd, &s->cs_lines[i]);
> +    }
> +
> +    /* DMA hardware handshake */
> +    qdev_init_gpio_in(&sbd->qdev, ftssp010_handle_ack, 2);
> +    qdev_init_gpio_out(&sbd->qdev, s->req, 2);
> +}
> +
> +static const VMStateDescription vmstate_ftssp010 = {
> +    .name = TYPE_FTSSP010,
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .minimum_version_id_old = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_UINT32(cr0, Ftssp010State),
> +        VMSTATE_UINT32(cr1, Ftssp010State),
> +        VMSTATE_UINT32(cr2, Ftssp010State),
> +        VMSTATE_UINT32(icr, Ftssp010State),
> +        VMSTATE_UINT32(isr, Ftssp010State),
> +        VMSTATE_FIFO8(tx_fifo, Ftssp010State),
> +        VMSTATE_FIFO8(rx_fifo, Ftssp010State),
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> +
> +static void ftssp010_instance_init(Object *obj)
> +{
> +    Ftssp010State *s = FTSSP010(obj);
> +
> +    object_property_add_link(obj,
> +                             "codec",
> +                             TYPE_AUDIO_CODEC,
> +                             (Object **) &s->codec,
> +                             NULL);
> +}
> +
> +static void ftssp010_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    dc->vmsd    = &vmstate_ftssp010;
> +    dc->reset   = ftssp010_reset;
> +    dc->realize = ftssp010_realize;
> +    dc->no_user = 1;
> +}
> +
> +static const TypeInfo ftssp010_info = {
> +    .name          = TYPE_FTSSP010,
> +    .parent        = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(Ftssp010State),
> +    .instance_init = ftssp010_instance_init,
> +    .class_init    = ftssp010_class_init,
> +};
> +
> +static void ftssp010_register_types(void)
> +{
> +    type_register_static(&ftssp010_info);
> +}
> +
> +type_init(ftssp010_register_types)
> diff --git a/hw/ftssp010.h b/hw/ftssp010.h
> new file mode 100644
> index 0000000..e3d3e7a
> --- /dev/null
> +++ b/hw/ftssp010.h
> @@ -0,0 +1,96 @@
> +/*
> + * QEMU model of the FTSSP010 Controller
> + *
> + * Copyright (C) 2012 Faraday Technology
> + * Written by Dante Su <dantesu@faraday-tech.com>
> + *
> + * This file is licensed under GNU GPL v2+.
> + */
> +
> +#ifndef HW_ARM_FTSSP010_H
> +#define HW_ARM_FTSSP010_H
> +
> +/* FTSSP010: Registers */
> +#define REG_CR0             0x00    /* control register 0 */
> +#define REG_CR1             0x04    /* control register 1 */
> +#define REG_CR2             0x08    /* control register 2 */
> +#define REG_SR              0x0C    /* status register */
> +#define REG_ICR             0X10    /* interrupt control register */
> +#define REG_ISR             0x14    /* interrupt statis register */
> +#define REG_DR              0x18    /* data register */
> +#define REG_REVR            0x60    /* revision register */
> +#define REG_FEAR            0x64    /* 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_LBM             (1 << 7)  /* Loopback mode */
> +#define CR0_LSB             (1 << 6)  /* LSB first */
> +#define CR0_FSPO            (1 << 5)  /* Frame sync atcive low */
> +#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)  /* SCLK Remain HIGH */
> +#define CR0_SCLKPH          (1 << 0)  /* Half CLK cycle */
> +
> +/* Control Register 1 */
> +
> +/* padding data length */
> +#define CR1_PDL(x)          (((x) & 0xff) << 24)
> +/* serial data length(actual data length-1) */
> +#define CR1_SDL(x)          ((((x) - 1) & 0x1f) << 16)
> +/*  clk divider */
> +#define CR1_CLKDIV(x)       ((x) & 0xffff)
> +
> +/* Control Register 2 */
> +#define CR2_FSOS(x)         (((x) & 0x03) << 10)        /* FS/CS Select */
> +#define CR2_FS              (1 << 9)    /* FS/CS Signal Level */
> +#define CR2_TXEN            (1 << 8)    /* Tx Enable */
> +#define CR2_RXEN            (1 << 7)    /* Rx Enable */
> +#define CR2_SSPRST          (1 << 6)    /* SSP reset */
> +#define CR2_TXFCLR          (1 << 3)    /* TX FIFO Clear */
> +#define CR2_RXFCLR          (1 << 2)    /* RX FIFO Clear */
> +#define CR2_TXDOE           (1 << 1)    /* TX Data Output Enable */
> +#define CR2_SSPEN           (1 << 0)    /* SSP Enable */
> +
> +/*
> + * Status Register
> + */
> +#define SR_TFVE(reg)        (((reg) & 0x1F) << 12)
> +#define SR_RFVE(reg)        (((reg) & 0x1F) << 4)
> +#define SR_BUSY             (1 << 2)
> +#define SR_TFNF             (1 << 1)    /* Tx FIFO Not Full */
> +#define SR_RFF              (1 << 0)    /* Rx FIFO Full */
> +
> +/* Interrupr Control Register */
> +#define ICR_TFTHOD(x)       (((x) & 0x1f) << 12)/* TX FIFO Threshold */
> +#define ICR_RFTHOD(x)       (((x) & 0x1f) << 7) /* RX FIFO Threshold */
> +#define ICR_AC97I           0x40      /* AC97 intr enable */
> +#define ICR_TFDMA           0x20      /* TX DMA enable */
> +#define ICR_RFDMA           0x10      /* RX DMA enable */
> +#define ICR_TFTHI           0x08      /* TX FIFO intr enable */
> +#define ICR_RFTHI           0x04      /* RX FIFO intr enable */
> +#define ICR_TFURI           0x02      /* TX FIFO Underrun intr enable */
> +#define ICR_RFORI           0x01      /* RX FIFO Overrun intr enable */
> +
> +/* Interrupr Status Register */
> +#define ISR_AC97I           0x10      /* AC97 interrupt */
> +#define ISR_TFTHI           0x08      /* TX FIFO interrupt */
> +#define ISR_RFTHI           0x04      /* RX FIFO interrupt */
> +#define ISR_TFURI           0x02      /* TX FIFO Underrun interrupt */
> +#define ISR_RFORI           0x01      /* RX FIFO Overrun interrupt */
> +/* Read-Clear status mask */
> +#define ISR_RCMASK          (ISR_RFORI | ISR_TFURI | ISR_AC97I)
> +
> +#endif
> --
> 1.7.9.5
>
>

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Qemu-devel] [PATCH v9 16/24] The FTSSP010 is a multi-function synchronous serial port interface controller which supports SSP, SPI, I2S, AC97 and SPDIF.
  2013-03-31  2:39   ` Peter Crosthwaite
@ 2013-04-01  1:18     ` Kuo-Jung Su
  0 siblings, 0 replies; 35+ messages in thread
From: Kuo-Jung Su @ 2013-04-01  1:18 UTC (permalink / raw)
  To: Peter Crosthwaite
  Cc: Peter Maydell, Igor Mitsyanko, qemu-devel, Blue Swirl,
	Paul Brook, Kuo-Jung Su, Andreas, fred konrad

2013/3/31 Peter Crosthwaite <peter.crosthwaite@xilinx.com>:
> Hi Kuo-Jung
>
> I think you may have accidentally dropped your subject line and
> promoted your long commit message to subject line. Looks better in
> previous versions.
>

Yes, it's an accident, I'll fix it later.

> On Mon, Mar 25, 2013 at 10:09 PM, Kuo-Jung Su <dantesu@gmail.com> wrote:
>>
>> From: Kuo-Jung Su <dantesu@faraday-tech.com>
>>
>> Only I2S and SPI protocol have been implemented in this patch.
>>
>> Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
>> ---
>>  hw/arm/Makefile.objs    |    3 +-
>>  hw/arm/ftplat_a369.c    |   31 +++
>>  hw/arm/ftplat_a369soc.c |   17 ++
>>  hw/faraday.h            |    3 +
>>  hw/ftssp010.c           |  504 +++++++++++++++++++++++++++++++++++++++++++++++
>>  hw/ftssp010.h           |   96 +++++++++
>>  6 files changed, 653 insertions(+), 1 deletion(-)
>>  create mode 100644 hw/ftssp010.c
>>  create mode 100644 hw/ftssp010.h
>>
>> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
>> index 2bb67f7..42c8472 100644
>> --- a/hw/arm/Makefile.objs
>> +++ b/hw/arm/Makefile.objs
>> @@ -25,7 +25,8 @@ obj-y += strongarm.o
>>  obj-y += imx_serial.o imx_ccm.o imx_timer.o imx_avic.o
>>  obj-$(CONFIG_KVM) += kvm/arm_gic.o
>>  obj-y += ftintc020.o ftahbc020.o ftddrii030.o ftpwmtmr010.o ftwdt010.o \
>> -                ftrtc011.o ftdmac020.o ftapbbrg020.o ftnandc021.o fti2c010.o
>> +                ftrtc011.o ftdmac020.o ftapbbrg020.o ftnandc021.o fti2c010.o \
>> +                ftssp010.o
>>
>>  obj-y := $(addprefix ../,$(obj-y))
>>
>> diff --git a/hw/arm/ftplat_a369.c b/hw/arm/ftplat_a369.c
>> index 827c58a..922fb55 100644
>> --- a/hw/arm/ftplat_a369.c
>> +++ b/hw/arm/ftplat_a369.c
>> @@ -11,6 +11,7 @@
>>  #include "hw/arm-misc.h"
>>  #include "hw/devices.h"
>>  #include "hw/i2c.h"
>> +#include "hw/audio.h"
>>  #include "hw/boards.h"
>>  #include "hw/ssi.h"
>>  #include "net/net.h"
>> @@ -28,6 +29,7 @@ static void a369_system_reset(void *opaque)
>>
>>  static void a369_board_init(QEMUMachineInitArgs *args)
>>  {
>> +    int i, nr_flash;
>>      ARMCPU *cpu;
>>      DeviceState *ds;
>>      FaradaySoCState *s;
>> @@ -79,6 +81,35 @@ static void a369_board_init(QEMUMachineInitArgs *args)
>>          abort();
>>      }
>>
>> +    /* Attach the spi flash to ftssp010.0 */
>> +    nr_flash = 1;
>> +    for (i = 0; i < nr_flash; i++) {
>> +        SSIBus *ssi = (SSIBus *)qdev_get_child_bus(s->spi[0], "spi");
>> +        DeviceState *fl = ssi_create_slave_no_init(ssi, "w25q64");
>> +        qemu_irq cs_line;
>> +
>> +        qdev_init_nofail(fl);
>> +        cs_line = qdev_get_gpio_in(fl, 0);
>> +        sysbus_connect_irq(SYS_BUS_DEVICE(s->spi[0]), i + 1, cs_line);
>> +    }
>> +
>> +    /* Attach the wm8731 to fti2c010.0 & ftssp010.0 */
>> +    for (i = 0; i < 1; ++i) {
>> +        i2c_bus *i2c = (i2c_bus *)qdev_get_child_bus(s->i2c[0], "i2c");
>> +        ds = i2c_create_slave(i2c, "wm8731", 0x1B);
>> +        object_property_set_link(OBJECT(s->i2s[0]),
>> +                                 OBJECT(ds),
>> +                                 "codec",
>> +                                 &local_errp);
>> +        if (local_errp) {
>> +            fprintf(stderr, "a369: Unable to set codec link for FTSSP010\n");
>> +            abort();
>> +        }
>> +        audio_codec_data_req_set(ds,
>> +                                 ftssp010_i2s_data_req,
>> +                                 s->i2s[0]);
>> +    }
>> +
>>      /* System start-up */
>>
>>      if (args->kernel_filename) {
>> diff --git a/hw/arm/ftplat_a369soc.c b/hw/arm/ftplat_a369soc.c
>> index b6e82ad..9391764 100644
>> --- a/hw/arm/ftplat_a369soc.c
>> +++ b/hw/arm/ftplat_a369soc.c
>> @@ -206,6 +206,23 @@ static void a369soc_chip_init(FaradaySoCState *s)
>>      s->i2c[0] = ds;
>>      ds = sysbus_create_simple("fti2c010", 0x92A00000, s->pic[52]);
>>      s->i2c[1] = ds;
>> +
>> +    /* ftssp010 */
>> +    ds = sysbus_create_simple("ftssp010", 0x92700000, s->pic[49]);
>> +    s->spi[0] = ds;
>> +    s->i2s[0] = ds;
>> +
>> +    /* ftssp010 - DMA (Tx) */
>> +    ack = qdev_get_gpio_in(ds, 0);
>> +    req = qdev_get_gpio_in(s->pdma[0], 7);
>> +    qdev_connect_gpio_out(s->pdma[0], 7, ack);
>> +    qdev_connect_gpio_out(ds, 0, req);
>> +
>> +    /* ftssp010 - DMA (Rx) */
>> +    ack = qdev_get_gpio_in(ds, 1);
>> +    req = qdev_get_gpio_in(s->pdma[0], 8);
>> +    qdev_connect_gpio_out(s->pdma[0], 8, ack);
>> +    qdev_connect_gpio_out(ds, 1, req);
>>  }
>>
>>  static void a369soc_realize(DeviceState *dev, Error **errp)
>> diff --git a/hw/faraday.h b/hw/faraday.h
>> index 7373ba0..39a608c 100644
>> --- a/hw/faraday.h
>> +++ b/hw/faraday.h
>> @@ -121,4 +121,7 @@ static inline void faraday_soc_ahb_remap(FaradaySoCState *s, bool active)
>>      }
>>  }
>>
>> +/* ftssp010.c */
>> +void ftssp010_i2s_data_req(void *opaque, int tx, int rx);
>> +
>>  #endif
>> diff --git a/hw/ftssp010.c b/hw/ftssp010.c
>> new file mode 100644
>> index 0000000..fe1ddbb
>> --- /dev/null
>> +++ b/hw/ftssp010.c
>> @@ -0,0 +1,504 @@
>> +/*
>> + * QEMU model of the FTSSP010 Controller
>> + *
>> + * Copyright (C) 2012 Faraday Technology
>> + * Written by Dante Su <dantesu@faraday-tech.com>
>> + *
>> + * This file is licensed under GNU GPL v2+.
>> + */
>> +
>> +#include "hw/sysbus.h"
>> +#include "hw/i2c.h"
>> +#include "hw/ssi.h"
>> +#include "hw/audio.h"
>> +#include "sysemu/sysemu.h"
>> +#include "qemu/fifo8.h"
>> +
>> +#include "hw/faraday.h"
>> +#include "hw/ftssp010.h"
>> +
>> +#define CFG_FIFO_DEPTH  16
>> +
>> +#define TYPE_FTSSP010   "ftssp010"
>> +
>> +typedef struct Ftssp010State {
>> +    /*< private >*/
>> +    SysBusDevice parent;
>> +
>> +    /*< public >*/
>> +    MemoryRegion mmio;
>> +
>> +    qemu_irq irq;
>> +    SSIBus *spi;
>> +    AudioCodecState *codec;
>> +
>> +    uint8_t num_cs;
>> +    qemu_irq *cs_lines;
>> +
>> +    Fifo8 rx_fifo;
>> +    Fifo8 tx_fifo;
>> +
>> +    uint8_t tx_thres;
>> +    uint8_t rx_thres;
>> +
>> +    int busy;
>
> Constant 0 AFAICT. Every set of busy to 1 is followed by a clear
> before fn return. I cannot see a code path where anyone reads busy as
> 1? If there is such a code path then busy should be part of the VMSD
> as well.
>

Yes, it should be a constant 0. It's going to be fixed in next version.

>> +    uint8_t bw;
>> +
>> +    /* DMA hardware handshake */
>> +    qemu_irq req[2];    /* 0 - Tx, 1 - Rx */
>> +
>> +    /* HW register caches */
>> +
>
> Are they really caches?
>
>> +    uint32_t cr0;
>> +    uint32_t cr1;
>> +    uint32_t cr2;
>> +    uint32_t icr;    /* interrupt control register */
>> +    uint32_t isr;    /* interrupt status register */
>> +
>> +} Ftssp010State;
>> +
>> +#define FTSSP010(obj) \
>> +    OBJECT_CHECK(Ftssp010State, obj, TYPE_FTSSP010)
>> +
>> +/* Update interrupts.  */
>> +static void ftssp010_update(Ftssp010State *s)
>> +{
>> +    if (!(s->cr2 & CR2_SSPEN)) {
>> +        return;
>> +    }
>> +
>> +    /* tx fifo status update */
>> +    if ((s->tx_fifo.num / (s->bw >> 3)) <= s->tx_thres) {
>> +        s->isr |=  ISR_TFTHI;
>> +        if (s->icr & ICR_TFDMA) {
>> +            qemu_set_irq(s->req[0], 1);
>> +        }
>> +    } else {
>> +        s->isr &= ~ISR_TFTHI;
>> +    }
>> +
>> +    /* rx fifo status update */
>> +    switch (s->cr0 & CR0_FFMT_MASK) {
>> +    case CR0_FFMT_SPI:
>> +        s->isr |=  ISR_RFTHI;
>> +        if (s->icr & ICR_RFDMA) {
>> +            qemu_set_irq(s->req[1], 1);
>> +        }
>> +        break;
>> +    default:
>> +        if ((s->rx_fifo.num / (s->bw >> 3)) >= s->rx_thres) {
>> +            s->isr |=  ISR_RFTHI;
>> +            if (s->icr & ICR_RFDMA) {
>> +                qemu_set_irq(s->req[1], 1);
>> +            }
>> +        } else {
>> +            s->isr &= ~ISR_RFTHI;
>> +        }
>> +        break;
>> +    }
>> +
>> +    /* update the interrupt signal */
>> +    qemu_set_irq(s->irq, (s->icr & s->isr) ? 1 : 0);
>> +}
>> +
>> +static void ftssp010_handle_ack(void *opaque, int line, int level)
>> +{
>> +    Ftssp010State *s = FTSSP010(opaque);
>> +
>> +    switch (line) {
>> +    case 0:    /* Tx */
>> +        if (s->icr & ICR_TFDMA) {
>> +            if (level) {
>> +                qemu_set_irq(s->req[0], 0);
>> +            } else if ((s->tx_fifo.num / (s->bw >> 3)) <= s->tx_thres) {
>> +                qemu_set_irq(s->req[0], 1);
>> +            }
>> +        }
>> +        break;
>> +    case 1:    /* Rx */
>> +        if (s->icr & ICR_RFDMA) {
>> +            if (level) {
>> +                qemu_set_irq(s->req[1], 0);
>> +            } else {
>> +                switch (s->cr0 & CR0_FFMT_MASK) {
>> +                case CR0_FFMT_SPI:
>> +                    qemu_set_irq(s->req[1], 1);
>> +                    break;
>> +                default:
>> +                    if ((s->rx_fifo.num / (s->bw >> 3)) >= s->rx_thres) {
>> +                        qemu_set_irq(s->req[1], 1);
>> +                    }
>> +                    break;
>> +                }
>> +            }
>> +        }
>> +        break;
>> +    default:
>> +        break;
>> +    }
>> +}
>> +
>> +void ftssp010_i2s_data_req(void *opaque, int tx, int rx)
>> +{
>> +    int i, len;
>> +    uint32_t sample;
>> +    Ftssp010State *s = FTSSP010(opaque);
>> +
>> +    if (!(s->cr2 & CR2_SSPEN)) {
>> +        return;
>> +    }
>> +
>> +    if ((s->cr0 & CR0_FFMT_MASK) != CR0_FFMT_I2S) {
>> +        return;
>> +    }
>> +
>> +    s->busy = 1;
>> +
>> +    if ((s->cr2 & CR2_TXEN) && (s->cr2 & CR2_TXDOE)) {
>> +        len = tx * (s->bw / 8);
>> +        while (!fifo8_is_empty(&s->tx_fifo) && len > 0) {
>> +            sample = 0;
>> +            for (i = 0; i < (s->bw >> 3) && len > 0; i++, len--) {
>> +                sample = deposit32(sample, i * 8, 8, fifo8_pop(&s->tx_fifo));
>> +            }
>> +            audio_codec_dac_dat(s->codec, sample);
>> +        }
>> +
>> +        if (fifo8_is_empty(&s->tx_fifo) && len > 0) {
>> +            s->isr |= ISR_TFURI;
>> +        }
>> +    }
>> +
>> +    if (s->cr2 & CR2_RXEN) {
>> +        len = rx * (s->bw / 8);
>> +        while (!fifo8_is_full(&s->rx_fifo) && len > 0) {
>> +            sample = audio_codec_adc_dat(s->codec);
>> +            for (i = 0; i < (s->bw >> 3) && len > 0; i++, len--) {
>> +                fifo8_push(&s->rx_fifo, extract32(sample, i * 8, 8));
>> +            }
>> +        }
>> +
>> +        if (fifo8_is_full(&s->rx_fifo) && len > 0) {
>> +            s->isr |= ISR_RFORI;
>> +        }
>> +    }
>> +
>> +    s->busy = 0;
>> +
>> +    ftssp010_update(s);
>> +}
>> +
>> +static void ftssp010_spi_tx(Ftssp010State *s)
>> +{
>> +    if (!(s->cr2 & CR2_TXEN)) {
>> +        return;
>> +    }
>> +
>> +    s->busy = 1;
>> +
>> +    if (fifo8_is_empty(&s->tx_fifo)) {
>> +        s->isr |= ISR_TFURI;
>> +    }
>> +
>> +    while (!fifo8_is_empty(&s->tx_fifo)) {
>> +        ssi_transfer(s->spi, fifo8_pop(&s->tx_fifo));
>> +    }
>> +
>> +    s->busy = 0;
>> +}
>> +
>> +static uint64_t
>> +ftssp010_mem_read(void *opaque, hwaddr addr, unsigned size)
>> +{
>> +    Ftssp010State *s = FTSSP010(opaque);
>> +    uint32_t i, ret = 0;
>> +
>> +    if (addr & 0x03) {
>> +        fprintf(stderr, "ftssp010: "
>> +                 "Although ftssp010 supports byte/half-word access, "
>> +                 "the target address still needs to be word aligned\n");
>> +        abort();
>> +    }
>> +
>> +    switch (addr) {
>> +    case REG_CR0:    /* Control Register 0 */
>> +        return s->cr0;
>> +    case REG_CR1:    /* Control Register 1 */
>> +        return s->cr1;
>> +    case REG_CR2:    /* Control Register 2 */
>> +        return s->cr2;
>> +    case REG_SR:    /* Status Register */
>> +        ret |= s->busy ? SR_BUSY : 0;
>> +        /* tx fifo status */
>> +        ret |= SR_TFVE(s->tx_fifo.num / (s->bw >> 3));
>> +        if (!fifo8_is_full(&s->tx_fifo)) {
>> +            ret |= SR_TFNF;
>> +        }
>> +        /* rx fifo status */
>> +        switch (s->cr0 & CR0_FFMT_MASK) {
>> +        case CR0_FFMT_SPI:
>> +            ret |= SR_RFF | SR_RFVE(CFG_FIFO_DEPTH);
>> +            break;
>> +        case CR0_FFMT_I2S:
>> +            ret |= SR_RFVE(s->rx_fifo.num / (s->bw >> 3));
>> +            if (fifo8_is_full(&s->rx_fifo)) {
>> +                ret |= SR_RFF;
>> +            }
>> +            break;
>> +        default:
>> +            break;
>> +        }
>> +        break;
>> +    case REG_ICR:    /* Interrupt Control Register */
>> +        return s->icr;
>> +    case REG_ISR:    /* Interrupt Status Register */
>> +        ret = s->isr;
>> +        s->isr &= ~ISR_RCMASK;
>> +        ftssp010_update(s);
>> +        break;
>> +    case REG_DR:    /* Data Register */
>> +        if (!(s->cr2 & CR2_SSPEN)) {
>> +            break;
>> +        }
>> +        if (!(s->cr2 & CR2_RXEN)) {
>> +            break;
>> +        }
>> +        switch (s->cr0 & CR0_FFMT_MASK) {
>> +        case CR0_FFMT_SPI:
>> +            for (i = 0; i < (s->bw >> 3); i++) {
>> +                ret = deposit32(ret, i * 8, 8, ssi_transfer(s->spi, 0));
>> +            }
>> +            break;
>> +        case CR0_FFMT_I2S:
>> +            for (i = 0; i < (s->bw >> 3); i++) {
>> +                if (fifo8_is_empty(&s->rx_fifo)) {
>> +                    break;
>> +                }
>> +                ret = deposit32(ret, i * 8, 8, fifo8_pop(&s->rx_fifo));
>> +            }
>> +            break;
>> +        default:
>> +            break;
>> +        }
>> +        ftssp010_update(s);
>> +        break;
>> +    case REG_REVR:
>> +        return 0x00011901;    /* ver. 1.19.1 */
>> +    case REG_FEAR:
>> +        return 0x660f0f1f;    /* SPI+I2S, FIFO=16 */
>> +    default:
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +            "ftssp010: undefined memory access@%#" HWADDR_PRIx "\n", addr);
>> +        break;
>> +    }
>> +
>> +    return ret;
>> +}
>> +
>> +static void
>> +ftssp010_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
>> +{
>> +    int i;
>> +    Ftssp010State *s = FTSSP010(opaque);
>> +
>> +    if (addr & 0x03) {
>> +        fprintf(stderr, "ftssp010: "
>> +                 "Although ftssp010 supports byte/half-word access, "
>> +                 "the target address still needs to be word aligned\n");
>> +        abort();
>
> Guest error. Buggy guests should not crash the VM.
>

Got it, thanks

>> +    }
>> +
>> +    switch (addr) {
>> +    case REG_CR0:    /* Control Register 0 */
>> +        s->cr0 = (uint32_t)val;
>> +        break;
>> +    case REG_CR1:    /* Control Register 1 */
>> +        s->cr1 = (uint32_t)val;
>> +        s->bw  = extract32(s->cr1, 16, 7) + 1;
>> +        break;
>> +    case REG_CR2:    /* Control Register 2 */
>> +        s->cr2 = (uint32_t)val;
>> +        if (s->cr2 & CR2_SSPRST) {
>> +            fifo8_reset(&s->tx_fifo);
>> +            fifo8_reset(&s->rx_fifo);
>> +            s->busy = 0;
>> +            s->cr2 &= ~(CR2_SSPRST | CR2_TXFCLR | CR2_RXFCLR);
>> +            if (s->cr0 & CR0_FLASH) {
>> +                int cs = extract32(s->cr2, 10, 2);
>> +                qemu_set_irq(s->cs_lines[cs], 1);
>> +                s->cr2 |= CR2_FS;
>> +            };
>> +        }
>> +        if (s->cr2 & CR2_TXFCLR) {
>> +            fifo8_reset(&s->tx_fifo);
>> +            s->cr2 &= ~CR2_TXFCLR;
>> +        }
>> +        if (s->cr2 & CR2_RXFCLR) {
>> +            fifo8_reset(&s->rx_fifo);
>> +            s->cr2 &= ~CR2_RXFCLR;
>> +        }
>> +        if (s->cr0 & CR0_FLASH) {
>> +            int cs = extract32(s->cr2, 10, 2);
>> +            qemu_set_irq(s->cs_lines[cs], (s->cr2 & CR2_FS) ? 1 : 0);
>> +        }
>> +        if (s->cr2 & CR2_SSPEN) {
>> +            switch (s->cr0 & CR0_FFMT_MASK) {
>> +            case CR0_FFMT_SPI:
>> +                ftssp010_spi_tx(s);
>> +                break;
>> +            default:
>> +                break;
>> +            }
>> +        }
>> +        ftssp010_update(s);
>> +        break;
>> +    case REG_ICR:    /* Interrupt Control Register */
>> +        s->icr = (uint32_t)val;
>> +        s->tx_thres = extract32(s->icr, 12, 5);
>> +        s->rx_thres = extract32(s->icr,  7, 5);
>> +        break;
>> +    case REG_DR:    /* Data Register */
>> +        if (!(s->cr2 & CR2_SSPEN)) {
>> +            break;
>> +        }
>> +        for (i = 0; i < (s->bw >> 3) && !fifo8_is_full(&s->tx_fifo); i++) {
>> +            fifo8_push(&s->tx_fifo, extract32((uint32_t)val, i * 8, 8));
>> +        }
>> +        switch (s->cr0 & CR0_FFMT_MASK) {
>> +        case CR0_FFMT_SPI:
>> +            ftssp010_spi_tx(s);
>> +            break;
>> +        default:
>> +            break;
>> +        }
>> +        ftssp010_update(s);
>> +        break;
>> +    default:
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +            "ftssp010: undefined memory access@%#" HWADDR_PRIx "\n", addr);
>> +        break;
>> +    }
>> +}
>> +
>> +static const MemoryRegionOps mmio_ops = {
>> +    .read  = ftssp010_mem_read,
>> +    .write = ftssp010_mem_write,
>> +    .endianness = DEVICE_LITTLE_ENDIAN,
>> +    .valid = {
>> +        .min_access_size = 1,
>> +        .max_access_size = 4
>> +    }
>> +};
>> +
>> +static void ftssp010_reset(DeviceState *ds)
>> +{
>> +    Ftssp010State *s = FTSSP010(SYS_BUS_DEVICE(ds));
>> +    Error *local_errp = NULL;
>> +
>> +    s->codec = AUDIO_CODEC(object_property_get_link(OBJECT(s),
>> +                            "codec", &local_errp));
>> +    if (local_errp) {
>> +        fprintf(stderr, "ftssp010: Unable to get codec link\n");
>> +        abort();
>> +    }
>> +
>> +    s->busy = 0;
>> +    s->bw   = 8;    /* 8-bit */
>> +
>> +    s->cr0 = 0;
>> +    s->cr1 = 0;
>> +    s->cr2 = 0;
>> +    s->tx_thres = 4;
>> +    s->rx_thres = 4;
>> +    s->icr = ICR_TFTHOD(4) | ICR_RFTHOD(4);
>> +    s->isr = ISR_TFTHI;
>> +
>> +    fifo8_reset(&s->tx_fifo);
>> +    fifo8_reset(&s->rx_fifo);
>> +
>> +    ftssp010_update(s);
>> +}
>> +
>> +static void ftssp010_realize(DeviceState *dev, Error **errp)
>> +{
>> +    Ftssp010State *s = FTSSP010(dev);
>> +    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
>> +    int i;
>> +
>> +    s->spi = ssi_create_bus(&sbd->qdev, "spi");
>> +
>> +    fifo8_create(&s->tx_fifo, CFG_FIFO_DEPTH * 4);
>> +    fifo8_create(&s->rx_fifo, CFG_FIFO_DEPTH * 4);
>> +
>> +    memory_region_init_io(&s->mmio,
>> +                          &mmio_ops,
>> +                          s,
>> +                          TYPE_FTSSP010,
>> +                          0x1000);
>> +    sysbus_init_mmio(sbd, &s->mmio);
>> +    sysbus_init_irq(sbd, &s->irq);
>> +
>> +    s->num_cs = 4;
>> +    s->cs_lines = g_new(qemu_irq, s->num_cs);
>
> g_new0
>

Got it, thanks

Best wishes,
Kuo-Jung Su

> Regards,
> Peter
>
>> +    ssi_auto_connect_slaves(dev, s->cs_lines, s->spi);
>> +    for (i = 0; i < s->num_cs; ++i) {
>> +        sysbus_init_irq(sbd, &s->cs_lines[i]);
>> +    }
>> +
>> +    /* DMA hardware handshake */
>> +    qdev_init_gpio_in(&sbd->qdev, ftssp010_handle_ack, 2);
>> +    qdev_init_gpio_out(&sbd->qdev, s->req, 2);
>> +}
>> +
>> +static const VMStateDescription vmstate_ftssp010 = {
>> +    .name = TYPE_FTSSP010,
>> +    .version_id = 1,
>> +    .minimum_version_id = 1,
>> +    .minimum_version_id_old = 1,
>> +    .fields = (VMStateField[]) {
>> +        VMSTATE_UINT32(cr0, Ftssp010State),
>> +        VMSTATE_UINT32(cr1, Ftssp010State),
>> +        VMSTATE_UINT32(cr2, Ftssp010State),
>> +        VMSTATE_UINT32(icr, Ftssp010State),
>> +        VMSTATE_UINT32(isr, Ftssp010State),
>> +        VMSTATE_FIFO8(tx_fifo, Ftssp010State),
>> +        VMSTATE_FIFO8(rx_fifo, Ftssp010State),
>> +        VMSTATE_END_OF_LIST()
>> +    }
>> +};
>> +
>> +static void ftssp010_instance_init(Object *obj)
>> +{
>> +    Ftssp010State *s = FTSSP010(obj);
>> +
>> +    object_property_add_link(obj,
>> +                             "codec",
>> +                             TYPE_AUDIO_CODEC,
>> +                             (Object **) &s->codec,
>> +                             NULL);
>> +}
>> +
>> +static void ftssp010_class_init(ObjectClass *klass, void *data)
>> +{
>> +    DeviceClass *dc = DEVICE_CLASS(klass);
>> +
>> +    dc->vmsd    = &vmstate_ftssp010;
>> +    dc->reset   = ftssp010_reset;
>> +    dc->realize = ftssp010_realize;
>> +    dc->no_user = 1;
>> +}
>> +
>> +static const TypeInfo ftssp010_info = {
>> +    .name          = TYPE_FTSSP010,
>> +    .parent        = TYPE_SYS_BUS_DEVICE,
>> +    .instance_size = sizeof(Ftssp010State),
>> +    .instance_init = ftssp010_instance_init,
>> +    .class_init    = ftssp010_class_init,
>> +};
>> +
>> +static void ftssp010_register_types(void)
>> +{
>> +    type_register_static(&ftssp010_info);
>> +}
>> +
>> +type_init(ftssp010_register_types)
>> diff --git a/hw/ftssp010.h b/hw/ftssp010.h
>> new file mode 100644
>> index 0000000..e3d3e7a
>> --- /dev/null
>> +++ b/hw/ftssp010.h
>> @@ -0,0 +1,96 @@
>> +/*
>> + * QEMU model of the FTSSP010 Controller
>> + *
>> + * Copyright (C) 2012 Faraday Technology
>> + * Written by Dante Su <dantesu@faraday-tech.com>
>> + *
>> + * This file is licensed under GNU GPL v2+.
>> + */
>> +
>> +#ifndef HW_ARM_FTSSP010_H
>> +#define HW_ARM_FTSSP010_H
>> +
>> +/* FTSSP010: Registers */
>> +#define REG_CR0             0x00    /* control register 0 */
>> +#define REG_CR1             0x04    /* control register 1 */
>> +#define REG_CR2             0x08    /* control register 2 */
>> +#define REG_SR              0x0C    /* status register */
>> +#define REG_ICR             0X10    /* interrupt control register */
>> +#define REG_ISR             0x14    /* interrupt statis register */
>> +#define REG_DR              0x18    /* data register */
>> +#define REG_REVR            0x60    /* revision register */
>> +#define REG_FEAR            0x64    /* 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_LBM             (1 << 7)  /* Loopback mode */
>> +#define CR0_LSB             (1 << 6)  /* LSB first */
>> +#define CR0_FSPO            (1 << 5)  /* Frame sync atcive low */
>> +#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)  /* SCLK Remain HIGH */
>> +#define CR0_SCLKPH          (1 << 0)  /* Half CLK cycle */
>> +
>> +/* Control Register 1 */
>> +
>> +/* padding data length */
>> +#define CR1_PDL(x)          (((x) & 0xff) << 24)
>> +/* serial data length(actual data length-1) */
>> +#define CR1_SDL(x)          ((((x) - 1) & 0x1f) << 16)
>> +/*  clk divider */
>> +#define CR1_CLKDIV(x)       ((x) & 0xffff)
>> +
>> +/* Control Register 2 */
>> +#define CR2_FSOS(x)         (((x) & 0x03) << 10)        /* FS/CS Select */
>> +#define CR2_FS              (1 << 9)    /* FS/CS Signal Level */
>> +#define CR2_TXEN            (1 << 8)    /* Tx Enable */
>> +#define CR2_RXEN            (1 << 7)    /* Rx Enable */
>> +#define CR2_SSPRST          (1 << 6)    /* SSP reset */
>> +#define CR2_TXFCLR          (1 << 3)    /* TX FIFO Clear */
>> +#define CR2_RXFCLR          (1 << 2)    /* RX FIFO Clear */
>> +#define CR2_TXDOE           (1 << 1)    /* TX Data Output Enable */
>> +#define CR2_SSPEN           (1 << 0)    /* SSP Enable */
>> +
>> +/*
>> + * Status Register
>> + */
>> +#define SR_TFVE(reg)        (((reg) & 0x1F) << 12)
>> +#define SR_RFVE(reg)        (((reg) & 0x1F) << 4)
>> +#define SR_BUSY             (1 << 2)
>> +#define SR_TFNF             (1 << 1)    /* Tx FIFO Not Full */
>> +#define SR_RFF              (1 << 0)    /* Rx FIFO Full */
>> +
>> +/* Interrupr Control Register */
>> +#define ICR_TFTHOD(x)       (((x) & 0x1f) << 12)/* TX FIFO Threshold */
>> +#define ICR_RFTHOD(x)       (((x) & 0x1f) << 7) /* RX FIFO Threshold */
>> +#define ICR_AC97I           0x40      /* AC97 intr enable */
>> +#define ICR_TFDMA           0x20      /* TX DMA enable */
>> +#define ICR_RFDMA           0x10      /* RX DMA enable */
>> +#define ICR_TFTHI           0x08      /* TX FIFO intr enable */
>> +#define ICR_RFTHI           0x04      /* RX FIFO intr enable */
>> +#define ICR_TFURI           0x02      /* TX FIFO Underrun intr enable */
>> +#define ICR_RFORI           0x01      /* RX FIFO Overrun intr enable */
>> +
>> +/* Interrupr Status Register */
>> +#define ISR_AC97I           0x10      /* AC97 interrupt */
>> +#define ISR_TFTHI           0x08      /* TX FIFO interrupt */
>> +#define ISR_RFTHI           0x04      /* RX FIFO interrupt */
>> +#define ISR_TFURI           0x02      /* TX FIFO Underrun interrupt */
>> +#define ISR_RFORI           0x01      /* RX FIFO Overrun interrupt */
>> +/* Read-Clear status mask */
>> +#define ISR_RCMASK          (ISR_RFORI | ISR_TFURI | ISR_AC97I)
>> +
>> +#endif
>> --
>> 1.7.9.5
>>
>>

^ permalink raw reply	[flat|nested] 35+ messages in thread

end of thread, other threads:[~2013-04-01  1:19 UTC | newest]

Thread overview: 35+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-03-25 12:09 [Qemu-devel] [PATCH v9 00/24] hw/arm: add Faraday A369 SoC platform support Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 01/24] target-arm: add Faraday ARMv5TE processors support Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 02/24] hw/arm: add Faraday a369 SoC platform support Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 03/24] hw/arm: add FTINTC020 interrupt controller support Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 04/24] hw/arm: add FTAHBC020 AHB " Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 05/24] hw/arm: add FTDDRII030 DDRII " Kuo-Jung Su
2013-03-28  0:09   ` Peter Crosthwaite
2013-03-28  3:24     ` Kuo-Jung Su
2013-03-28  3:58       ` Peter Crosthwaite
2013-03-28  5:28         ` Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 06/24] hw/arm: add FTPWMTMR010 timer support Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 07/24] hw/arm: add FTWDT010 watchdog " Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 08/24] hw/arm: add FTRTC011 RTC " Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 09/24] tests: add QTest for FTRTC011 Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 10/24] hw/arm: add FTDMAC020 AHB DMA support Kuo-Jung Su
2013-03-29  0:22   ` Peter Crosthwaite
2013-03-29  7:23     ` Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 11/24] hw/arm: add FTAPBBRG020 APB " Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 12/24] hw/arm: add FTNANDC021 nand flash controller support Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 13/24] hw/arm: add FTI2C010 I2C " Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 14/24] hw: Add AudioCodecClass for wm87xx audio class abstration Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 15/24] hw: add WM8731 audio codec support Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 16/24] The FTSSP010 is a multi-function synchronous serial port interface controller which supports SSP, SPI, I2S, AC97 and SPDIF Kuo-Jung Su
2013-03-31  2:39   ` Peter Crosthwaite
2013-04-01  1:18     ` Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 17/24] qemu/bitops.h: add the bit ordering reversal functions Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 18/24] hw/arm: add FTGMAC100 1Gbps ethernet support Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 19/24] hw/arm: add FTLCDC200 LCD controller support Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 20/24] hw/arm: add FTTSC010 touchscreen " Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 21/24] hw/arm: add FTSDC010 MMC/SD " Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 22/24] hw/arm: add FTMAC110 10/100Mbps ethernet support Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 23/24] hw/arm: add FTTMR010 timer support Kuo-Jung Su
2013-03-25 12:10 ` [Qemu-devel] [PATCH v9 24/24] hw/arm: add FTSPI020 SPI flash controller support Kuo-Jung Su
2013-03-29  0:02   ` Peter Crosthwaite
2013-03-29  7:15     ` Kuo-Jung Su

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.