[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH v9 24/24] hw/arm: add FTSPI020 SPI flash control
From: |
Kuo-Jung Su |
Subject: |
Re: [Qemu-devel] [PATCH v9 24/24] hw/arm: add FTSPI020 SPI flash controller support |
Date: |
Fri, 29 Mar 2013 15:15:20 +0800 |
2013/3/29 Peter Crosthwaite <address@hidden>:
> Hi Kuo-Jung,
>
> On Mon, Mar 25, 2013 at 10:10 PM, Kuo-Jung Su <address@hidden> wrote:
>> From: Kuo-Jung Su <address@hidden>
>>
>> The FTSPI020 is an integrated SPI Flash controller
>> which supports up to 4 flash chips.
>>
>> Signed-off-by: Kuo-Jung Su <address@hidden>
>> ---
>> 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 <address@hidden>
>> + *
>> + * 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 address@hidden" 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 address@hidden" 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 <address@hidden>
>> + *
>> + * 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
- [Qemu-devel] [PATCH v9 07/24] hw/arm: add FTWDT010 watchdog timer support, (continued)
- [Qemu-devel] [PATCH v9 07/24] hw/arm: add FTWDT010 watchdog timer support, Kuo-Jung Su, 2013/03/25
- [Qemu-devel] [PATCH v9 17/24] qemu/bitops.h: add the bit ordering reversal functions, Kuo-Jung Su, 2013/03/25
- [Qemu-devel] [PATCH v9 18/24] hw/arm: add FTGMAC100 1Gbps ethernet support, Kuo-Jung Su, 2013/03/25
- [Qemu-devel] [PATCH v9 20/24] hw/arm: add FTTSC010 touchscreen controller support, Kuo-Jung Su, 2013/03/25
- [Qemu-devel] [PATCH v9 21/24] hw/arm: add FTSDC010 MMC/SD controller support, Kuo-Jung Su, 2013/03/25
- [Qemu-devel] [PATCH v9 19/24] hw/arm: add FTLCDC200 LCD controller support, Kuo-Jung Su, 2013/03/25
- [Qemu-devel] [PATCH v9 23/24] hw/arm: add FTTMR010 timer support, Kuo-Jung Su, 2013/03/25
- [Qemu-devel] [PATCH v9 22/24] hw/arm: add FTMAC110 10/100Mbps ethernet support, Kuo-Jung Su, 2013/03/25
- [Qemu-devel] [PATCH v9 24/24] hw/arm: add FTSPI020 SPI flash controller support, Kuo-Jung Su, 2013/03/25
- [Qemu-devel] [PATCH v9 08/24] hw/arm: add FTRTC011 RTC timer support, Kuo-Jung Su, 2013/03/25
- [Qemu-devel] [PATCH v9 14/24] hw: Add AudioCodecClass for wm87xx audio class abstration., Kuo-Jung Su, 2013/03/25
- [Qemu-devel] [PATCH v9 10/24] hw/arm: add FTDMAC020 AHB DMA support, Kuo-Jung Su, 2013/03/25
- [Qemu-devel] [PATCH v9 11/24] hw/arm: add FTAPBBRG020 APB DMA support, Kuo-Jung Su, 2013/03/25
- [Qemu-devel] [PATCH v9 12/24] hw/arm: add FTNANDC021 nand flash controller support, Kuo-Jung Su, 2013/03/25
- [Qemu-devel] [PATCH v9 15/24] hw: add WM8731 audio codec support, Kuo-Jung Su, 2013/03/25
- [Qemu-devel] [PATCH v9 09/24] tests: add QTest for FTRTC011, Kuo-Jung Su, 2013/03/25
- [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