From: Sunny Luo Date: Fri, 31 Mar 2017 13:14:34 +0000 (+0800) Subject: spicc: initial add spicc driver X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=1570047aa4f19c2c2f44bb5197481a87ca752491;p=GitHub%2FLineageOS%2FG12%2Fandroid_kernel_amlogic_linux-4.9.git spicc: initial add spicc driver PD#138714: spicc: initial add spicc driver Change-Id: I60aa176f7bd9d64bd6e9db56adc7f592bc856f50 Signed-off-by: Sunny Luo --- diff --git a/MAINTAINERS b/MAINTAINERS index c6ad00754616..0e995849cdb9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13751,3 +13751,10 @@ F: drivers/amlogic/media/video_processor/video_dev/Makefile F: drivers/amlogic/media/video_processor/video_dev/amlvideo.h F: drivers/amlogic/media/video_processor/video_dev/amlvideo.c F: drivers/amlogic/media/video_processor/video_dev/common/vfutil.c + +AMLOGIC SPICC DRIVER +M: Sunny Luo +F: drivers/amlogic/spicc/spicc.c +F: drivers/amlogic/spicc/spicc.h +F: drivers/amlogic/spicc/Kconfig +F: drivers/amlogic/spicc/Makefile diff --git a/arch/arm64/boot/dts/amlogic/gxl_p212_1g.dts b/arch/arm64/boot/dts/amlogic/gxl_p212_1g.dts index 8ec31b441dcc..bc3ef3f58a70 100644 --- a/arch/arm64/boot/dts/amlogic/gxl_p212_1g.dts +++ b/arch/arm64/boot/dts/amlogic/gxl_p212_1g.dts @@ -1152,3 +1152,19 @@ &efuse { status = "ok"; }; + +&spicc{ + status = "disabled"; + pinctrl-names = "spicc_pulldown","spicc_pullup"; + pinctrl-0 = <&spicc_pulldown_x8x9x11>; + pinctrl-1 = <&spicc_pullup_x8x9x11>; + num_chipselect = <1>; + cs-gpios = <&gpio GPIOX_10 GPIO_ACTIVE_HIGH>; + dma_en = <0>; + dma_tx_threshold = <3>; + dma_rx_threshold = <3>; + dma_num_per_read_burst = <3>; + dma_num_per_write_burst = <3>; + delay_control = <0x15>; + ssctl = <0>; +}; diff --git a/arch/arm64/boot/dts/amlogic/mesongxl.dtsi b/arch/arm64/boot/dts/amlogic/mesongxl.dtsi index 7a319dfe933c..93ae8f420976 100644 --- a/arch/arm64/boot/dts/amlogic/mesongxl.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesongxl.dtsi @@ -248,6 +248,17 @@ /* 0: 100.0M 1: 166.7M 2: 200.0M 3: 250.0M */ /* 4: 333.3M 5: 400.0M 6: 500.0M 7: 666.7M */ }; + + spicc:@c1108d80{ + compatible = "amlogic, spicc"; + status = "disabled"; + reg = <0x0 0xc1108d80 0x0 0x28>; + clocks = <&clkc CLKID_SPICC>; + clock-names = "spicc_clk"; + interrupts = <0 81 1>; + device_id = <0>; + }; + i2c_ao: i2c@c8100500{ /*I2C-AO*/ compatible = "amlogic, meson-i2c"; dev_name = "i2c-AO"; @@ -775,15 +786,6 @@ }; }; - spicc_pins_z11z12z13: spicc_pins_z11z12z13 { - mux { - groups = "spi_sclk_0", - "spi_miso_0", - "spi_mosi_0"; - function = "spi"; - }; - }; - spicc_pulldown_z11z12z13: spicc_pulldown_z11z12z13 { mux { groups = "spi_sclk_0", @@ -802,15 +804,6 @@ }; }; - spicc_pins_x8x9x11: spicc_pins_x8x9x11 { - mux { - groups = "spi_sclk_1", - "spi_miso_1", - "spi_mosi_1"; - function = "spi"; - }; - }; - spicc_pulldown_x8x9x11: spicc_pulldown_x8x9x11 { mux { groups = "spi_sclk_1", @@ -1127,4 +1120,3 @@ tbl = <&clk125_cfg &clk285_cfg &clk400_cfg &clk500_cfg &clk666_cfg &clk750_cfg &clk750_cfg>; }; - diff --git a/arch/arm64/boot/dts/amlogic/mesongxm.dtsi b/arch/arm64/boot/dts/amlogic/mesongxm.dtsi index fc5fe6feab12..64f5035b1a81 100644 --- a/arch/arm64/boot/dts/amlogic/mesongxm.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesongxm.dtsi @@ -338,6 +338,17 @@ /* 0: 100.0M 1: 166.7M 2: 200.0M 3: 250.0M */ /* 4: 333.3M 5: 400.0M 6: 500.0M 7: 666.7M */ }; + + spicc:@c1108d80{ + compatible = "amlogic, spicc"; + status = "disabled"; + reg = <0x0 0xc1108d80 0x0 0x28>; + clocks = <&clkc CLKID_SPICC>; + clock-names = "spicc_clk"; + interrupts = <0 81 1>; + device_id = <0>; + }; + i2c_ao: i2c@c8100500{ /*I2C-AO*/ compatible = "amlogic, meson-i2c"; dev_name = "i2c-AO"; @@ -865,15 +876,6 @@ }; }; - spicc_pins_z11z12z13: spicc_pins_z11z12z13 { - mux { - groups = "spi_sclk_0", - "spi_miso_0", - "spi_mosi_0"; - function = "spi"; - }; - }; - spicc_pulldown_z11z12z13: spicc_pulldown_z11z12z13 { mux { groups = "spi_sclk_0", @@ -892,15 +894,6 @@ }; }; - spicc_pins_x8x9x11: spicc_pins_x8x9x11 { - mux { - groups = "spi_sclk_1", - "spi_miso_1", - "spi_mosi_1"; - function = "spi"; - }; - }; - spicc_pulldown_x8x9x11: spicc_pulldown_x8x9x11 { mux { groups = "spi_sclk_1", diff --git a/arch/arm64/configs/meson64_defconfig b/arch/arm64/configs/meson64_defconfig index 8c6e3af66ae1..aba75d856e40 100644 --- a/arch/arm64/configs/meson64_defconfig +++ b/arch/arm64/configs/meson64_defconfig @@ -170,6 +170,7 @@ CONFIG_AMLOGIC_USB2PHY=y CONFIG_AMLOGIC_USB3PHY=y CONFIG_AMLOGIC_I2C=y CONFIG_AMLOGIC_I2C_MASTER=y +CONFIG_AMLOGIC_SPICC_MASTER=y CONFIG_AMLOGIC_SEC=y CONFIG_AMLOGIC_CPU_VERSION=y CONFIG_AMLOGIC_MESON64_VERSION=y @@ -289,6 +290,10 @@ CONFIG_INPUT_UINPUT=y # CONFIG_DEVMEM is not set # CONFIG_DEVKMEM is not set CONFIG_HW_RANDOM=y +CONFIG_SPI=y +CONFIG_SPI_DEBUG=y +CONFIG_SPI_GPIO=y +CONFIG_SPI_SPIDEV=y CONFIG_THERMAL=y CONFIG_THERMAL_WRITABLE_TRIPS=y CONFIG_THERMAL_DEFAULT_GOV_POWER_ALLOCATOR=y diff --git a/drivers/amlogic/Kconfig b/drivers/amlogic/Kconfig index 966cfb163f08..25aca809f6ed 100644 --- a/drivers/amlogic/Kconfig +++ b/drivers/amlogic/Kconfig @@ -21,6 +21,8 @@ source "drivers/amlogic/usb/Kconfig" source "drivers/amlogic/i2c/Kconfig" +source "drivers/amlogic/spicc/Kconfig" + source "drivers/amlogic/secmon/Kconfig" source "drivers/amlogic/cpu_version/Kconfig" diff --git a/drivers/amlogic/Makefile b/drivers/amlogic/Makefile index 92ed400377b6..cc07d573e654 100644 --- a/drivers/amlogic/Makefile +++ b/drivers/amlogic/Makefile @@ -21,6 +21,8 @@ obj-$(CONFIG_AMLOGIC_CPU_VERSION) += cpu_version/ obj-$(CONFIG_AMLOGIC_I2C) += i2c/ +obj-$(CONFIG_AMLOGIC_SPICC_MASTER) += spicc/ + obj-$(CONFIG_AMLOGIC_CPU_INFO) += cpu_info/ obj-$(CONFIG_AMLOGIC_MHU_MBOX) += mailbox/ diff --git a/drivers/amlogic/spicc/Kconfig b/drivers/amlogic/spicc/Kconfig new file mode 100644 index 000000000000..1099cbc1caf3 --- /dev/null +++ b/drivers/amlogic/spicc/Kconfig @@ -0,0 +1,15 @@ +# +# Sensor device configuration +# + +menu "AMLOGIC SPI Hardware bus support" + + +config AMLOGIC_SPICC_MASTER + tristate "AMLOGIC SPICC MASTER SUPPORT" + default n + help + This is amlogic SPICC hardware bus master. + This driver can also be built as a module. + +endmenu diff --git a/drivers/amlogic/spicc/Makefile b/drivers/amlogic/spicc/Makefile new file mode 100644 index 000000000000..f6225070311f --- /dev/null +++ b/drivers/amlogic/spicc/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for the amlogic SPI bus drivers. +# + +obj-$(CONFIG_AMLOGIC_SPICC_MASTER) += spicc.o diff --git a/drivers/amlogic/spicc/spicc.c b/drivers/amlogic/spicc/spicc.c new file mode 100644 index 000000000000..281d28cea2e4 --- /dev/null +++ b/drivers/amlogic/spicc/spicc.c @@ -0,0 +1,1122 @@ +/* + * drivers/amlogic/spicc/spicc.c + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "spicc.h" + +/* #define CONFIG_SPICC_LOG */ +#ifdef CONFIG_SPICC_LOG +struct my_log { + struct timeval tv; + unsigned int irq_data; + unsigned int reg_val[13]; + unsigned int param[8]; + unsigned int param_count; + const char *comment; +}; +#endif + +/** + * struct spicc + * @lock: spinlock for SPICC controller. + * @msg_queue: link with the spi message list. + * @wq: work queque + * @work: work + * @master: spi master alloc for this driver. + * @spi: spi device on working. + * @regs: the start register address of this SPICC controller. + */ +struct spicc { + spinlock_t lock; + struct list_head msg_queue; + struct workqueue_struct *wq; + struct work_struct work; + struct spi_master *master; + struct spi_device *spi; + struct class cls; + + int device_id; + struct reset_control *rst; + struct clk *clk; + void __iomem *regs; + struct pinctrl *pinctrl; + struct pinctrl_state *pullup; + struct pinctrl_state *pulldown; + int bits_per_word; + int mode; + int speed; + unsigned int dma_tx_threshold; + unsigned int dma_rx_threshold; + unsigned int dma_num_per_read_burst; + unsigned int dma_num_per_write_burst; + int irq; + struct completion completion; +#define FLAG_DMA_EN 0 +#define FLAG_TEST_DATA_AUTO_INC 1 +#define FLAG_SSCTL 2 +#define FLAG_ENHANCE 3 + unsigned int flags; + u8 test_data; + unsigned int delay_control; + unsigned int cs_delay; + int remain; + u8 *txp; + u8 *rxp; + int burst_len; +#ifdef CONFIG_SPICC_LOG + struct my_log *log; + int log_size; + int log_count; +#endif +}; + +#ifdef CONFIG_SPICC_LOG +enum { + PROBE_BEGIN, + PROBE_END, + REQUEST_IRQ, + HAND_MSG_BEGIN, + HAND_MSG_END, + XFER_COMP_ISR, + XFER_COMP_POLLING, + DMA_BEGIN, + DMA_END, + PIO_BEGIN, + PIO_END, +}; + +static const char * const log_comment[] = { + "probe begin", + "probe end", + "request irq", + "handle msg begin", + "handle msg end", + "xfer complete isr", + "xfer complete polling", + "dma begin", + "dma end", + "pio begin", + "pio end" +}; + +static void spicc_log_init(struct spicc *spicc) +{ + spicc->log = 0; + spicc->log_count = 0; + if (spicc->log_size) + spicc->log = kzalloc(spicc->log_size * + sizeof(struct my_log), GFP_KERNEL); + pr_info("spicc: log_size=%d, log=%p", + spicc->log_size, spicc->log); +} + +static void spicc_log_exit(struct spicc *spicc) +{ + spicc->log = 0; + spicc->log_count = 0; + spicc->log_size = 0; + kfree(spicc->log); +} + +static void spicc_log( + struct spicc *spicc, + unsigned int *param, + int param_count, + int comment_id) +{ + struct my_log *log; + struct irq_desc *desc; + int i; + + if (IS_ERR_OR_NULL(spicc->log)) + return; + log = &spicc->log[spicc->log_count]; + log->tv = ktime_to_timeval(ktime_get()); + if (spicc->irq) { + desc = irq_to_desc(spicc->irq); + log->irq_data = desc->irq_data.state_use_accessors; + } + for (i = 0; i < ARRAY_SIZE(log->reg_val); i++) + log->reg_val[i] = readl(spicc->regs + ((i+2)<<2)); + + if (param) { + if (param_count > ARRAY_SIZE(log->param)) + param_count = ARRAY_SIZE(log->param); + for (i = 0; i < param_count; i++) + log->param[i] = param[i]; + log->param_count = param_count; + } + log->comment = log_comment[comment_id]; + + if (++spicc->log_count > spicc->log_size) + spicc->log_count = 0; +} + +static void spicc_log_print(struct spicc *spicc) +{ + struct my_log *log; + int i, j; + unsigned int *p; + + if (IS_ERR_OR_NULL(spicc->log)) + return; + pr_info("log total:%d\n", spicc->log_count); + for (i = 0; i < spicc->log_count; i++) { + log = &spicc->log[i]; + pr_info("[%6u.%6u]spicc-%d: %s\n", + (unsigned int)log->tv.tv_sec, + (unsigned int)log->tv.tv_usec, + i, log->comment); + pr_info("irq_data=0x%x\n", log->irq_data); + p = log->reg_val; + for (j = 0; j < ARRAY_SIZE(log->reg_val); j++) + if (*p) + pr_info("reg[%2d]=0x%x\n", j+2, *p++); + p = log->param; + for (j = 0; j < log->param_count; j++) + pr_info("param[%d]=0x%x\n", j, *p++); + } +} +#else +#define spicc_log_init(spicc) +#define spicc_log_exit(spicc) +#define spicc_log(spicc, param, param_count, comment_id) +#define spicc_log_print(spicc) +#endif + +/* Note this is chip_select enable or disable */ +void spicc_chip_select(struct spi_device *spi, bool select) +{ + struct spicc *spicc; + unsigned int level; + + spicc = spi_master_get_devdata(spi->master); + level = (spi->mode & SPI_CS_HIGH) ? select : !select; + + if (spi->mode & SPI_NO_CS) + return; + if (spi->cs_gpio >= 0) { + gpio_direction_output(spi->cs_gpio, level); + } else if (select) { + setb(spicc->regs, CON_CHIP_SELECT, spi->chip_select); + setb(spicc->regs, CON_SS_POL, level); + } +} +EXPORT_SYMBOL(spicc_chip_select); + +static inline bool spicc_get_flag(struct spicc *spicc, unsigned int flag) +{ + bool ret; + + ret = (spicc->flags >> flag) & 1; + return ret; +} + +static inline void spicc_set_flag( + struct spicc *spicc, + unsigned int flag, + bool value) +{ + if (value) + spicc->flags |= 1<flags &= ~(1<regs, CON_BITS_PER_WORD, bw-1); + spicc->bits_per_word = bw; +} + +static void spicc_set_mode(struct spicc *spicc, u8 mode) +{ + bool cpol = (mode & SPI_CPOL) ? 1:0; + bool cpha = (mode & SPI_CPHA) ? 1:0; + + if (mode == spicc->mode) + return; + + spicc->mode = mode; + if (!spicc_get_flag(spicc, FLAG_ENHANCE)) { + if (cpol && !IS_ERR_OR_NULL(spicc->pullup)) + pinctrl_select_state(spicc->pinctrl, spicc->pullup); + else if (!cpol && !IS_ERR_OR_NULL(spicc->pulldown)) + pinctrl_select_state(spicc->pinctrl, spicc->pulldown); + } + setb(spicc->regs, CON_CLK_PHA, cpha); + setb(spicc->regs, CON_CLK_POL, cpol); +} + +static void spicc_set_clk(struct spicc *spicc, int speed) +{ + unsigned int sys_clk_rate; + unsigned int div, mid_speed; + + if (!speed) + clk_disable_unprepare(spicc->clk); + else + clk_prepare_enable(spicc->clk); + if (!speed || (speed == spicc->speed)) + return; + + spicc->speed = speed; + sys_clk_rate = clk_get_rate(spicc->clk); + + if (spicc_get_flag(spicc, FLAG_ENHANCE)) { + div = (sys_clk_rate/speed)-1; + setb(spicc->regs, ENHANCE_CLK_DIV, div); + setb(spicc->regs, ENHANCE_CLK_DIV_SELECT, 1); + } else { + /* speed = sys_clk_rate / 2^(conreg.data_rate_div+2) */ + mid_speed = (sys_clk_rate * 3) >> 4; + for (div = 0; div < 7; div++) { + if (speed >= mid_speed) + break; + mid_speed >>= 1; + } + setb(spicc->regs, CON_DATA_RATE_DIV, div); + } +} + +static inline void spicc_set_txfifo(struct spicc *spicc, u32 dat) +{ + writel(dat, spicc->regs + SPICC_REG_TXDATA); +} + +static inline u32 spicc_get_rxfifo(struct spicc *spicc) +{ + return readl(spicc->regs + SPICC_REG_RXDATA); +} + +static inline void spicc_enable(struct spicc *spicc, bool en) +{ + setb(spicc->regs, CON_ENABLE, en); +} + +static void dma_one_burst(struct spicc *spicc) +{ + void __iomem *mem_base = spicc->regs; + + setb(mem_base, STA_XFER_COM, 1); + if (spicc->remain > 0) { + spicc->burst_len = min_t(size_t, spicc->remain, BURST_LEN_MAX); + setb(mem_base, CON_BURST_LEN, spicc->burst_len - 1); + setb(mem_base, CON_XCH, 1); + spicc->remain -= spicc->burst_len; + } +} + +static void pio_one_burst_recv(struct spicc *spicc) +{ + int bytes_per_word; + unsigned int dat; + int i, j; + + bytes_per_word = ((spicc->bits_per_word - 1)>>3) + 1; + for (i = 0; i < spicc->burst_len; i++) { + dat = spicc_get_rxfifo(spicc); + if (spicc->rxp) { + for (j = 0; j < bytes_per_word; j++) { + *spicc->rxp++ = dat & 0xff; + dat >>= 8; + } + } + } +} + +static void pio_one_burst_send(struct spicc *spicc) +{ + void __iomem *mem_base = spicc->regs; + int bytes_per_word; + unsigned int dat; + int i, j; + + setb(mem_base, STA_XFER_COM, 1); + bytes_per_word = ((spicc->bits_per_word - 1)>>3) + 1; + if (spicc->remain > 0) { + spicc->burst_len = min_t(size_t, spicc->remain, + SPICC_FIFO_SIZE); + for (i = 0; i < spicc->burst_len; i++) { + dat = 0; + if (spicc->txp) { + for (j = 0; j < bytes_per_word; j++) { + dat <<= 8; + dat += *spicc->txp++; + } + } + spicc_set_txfifo(spicc, dat); + } + setb(mem_base, CON_BURST_LEN, spicc->burst_len - 1); + setb(mem_base, CON_XCH, 1); + spicc->remain -= spicc->burst_len; + } +} + +/** + * Return: same with wait_for_completion_interruptible_timeout + * =0: timed out, + * >0: completed, number of usec left till timeout. + */ +static int spicc_wait_complete(struct spicc *spicc, int us) +{ + void __iomem *mem_base = spicc->regs; + int i; + + for (i = 0; i < us; i++) { + udelay(1); + if (getb(mem_base, STA_XFER_COM)) { + setb(spicc->regs, STA_XFER_COM, 1); /* set 1 to clear */ + break; + } + } + return us - i; +} + +static irqreturn_t spicc_xfer_complete_isr(int irq, void *dev_id) +{ + struct spicc *spicc = (struct spicc *)dev_id; + unsigned long flags; + + spin_lock_irqsave(&spicc->lock, flags); + spicc_wait_complete(spicc, 100); + spicc_log(spicc, &spicc->remain, 1, XFER_COMP_ISR); + if (!spicc_get_flag(spicc, FLAG_DMA_EN)) + pio_one_burst_recv(spicc); + if (!spicc->remain) + complete(&spicc->completion); + else if (spicc_get_flag(spicc, FLAG_DMA_EN)) + dma_one_burst(spicc); + else + pio_one_burst_send(spicc); + spin_unlock_irqrestore(&spicc->lock, flags); + return IRQ_HANDLED; +} + +#define INVALID_DMA_ADDRESS 0xffffffff +/* + * For DMA, tx_buf/tx_dma have the same relationship as rx_buf/rx_dma: + * - The buffer is either valid for CPU access, else NULL + * - If the buffer is valid, so is its DMA address + * + * This driver manages the dma address unless message->is_dma_mapped. + */ +static int spicc_dma_map(struct spicc *spicc, struct spi_transfer *t) +{ + struct device *dev = spicc->master->dev.parent; + u8 buf[8], *p = (u8 *)t->tx_buf; + int i, len = 0; + + t->tx_dma = t->rx_dma = INVALID_DMA_ADDRESS; + if (t->tx_buf) { + while (len < t->len) { + memcpy(buf, p, 8); + for (i = 0; i < 8; i++) + *p++ = buf[7-i]; + len += 8; + } + t->tx_dma = dma_map_single(dev, + (void *)t->tx_buf, t->len, DMA_TO_DEVICE); + if (dma_mapping_error(dev, t->tx_dma)) { + dev_err(dev, "tx_dma map failed\n"); + return -ENOMEM; + } + } + if (t->rx_buf) { + t->rx_dma = dma_map_single(dev, + t->rx_buf, t->len, DMA_FROM_DEVICE); + if (dma_mapping_error(dev, t->rx_dma)) { + if (t->tx_buf) { + dev_err(dev, "rx_dma map failed\n"); + dma_unmap_single(dev, t->tx_dma, t->len, + DMA_TO_DEVICE); + } + return -ENOMEM; + } + } + return 0; +} + +static void spicc_dma_unmap(struct spicc *spicc, struct spi_transfer *t) +{ + struct device *dev = spicc->master->dev.parent; + u8 buf[8], *p = (u8 *)t->rx_buf; + int i, len = 0; + + if (t->tx_dma != INVALID_DMA_ADDRESS) + dma_unmap_single(dev, t->tx_dma, t->len, DMA_TO_DEVICE); + if (t->rx_dma != INVALID_DMA_ADDRESS) { + dma_unmap_single(dev, t->rx_dma, t->len, DMA_FROM_DEVICE); + while (len < t->len) { + memcpy(buf, p, 8); + for (i = 0; i < 8; i++) + *p++ = buf[7-i]; + len += 8; + } + } +} + +/** + * Return: + * =0: xfer success, + * -ETIMEDOUT: timeout. + */ +static int spicc_dma_xfer(struct spicc *spicc, struct spi_transfer *t) +{ + void __iomem *mem_base = spicc->regs; + int ret; + + setb(mem_base, RX_FIFO_RESET, 1); + setb(mem_base, TX_FIFO_RESET, 1); + setb(mem_base, CON_XCH, 0); + spicc_set_bit_width(spicc, 64); + if (t->tx_dma != INVALID_DMA_ADDRESS) + writel(t->tx_dma, mem_base + SPICC_REG_DRADDR); + if (t->rx_dma != INVALID_DMA_ADDRESS) + writel(t->rx_dma, mem_base + SPICC_REG_DWADDR); + setb(mem_base, DMA_EN, 1); + spicc->remain = ((t->len - 1) >> 3) + 1; + spicc->burst_len = 0; + spicc_log(spicc, &spicc->remain, 1, DMA_BEGIN); + if (spicc->irq) { + setb(mem_base, INT_XFER_COM_EN, 1); + enable_irq(spicc->irq); + dma_one_burst(spicc); + ret = wait_for_completion_interruptible_timeout( + &spicc->completion, msecs_to_jiffies(2000)); + disable_irq_nosync(spicc->irq); + setb(mem_base, INT_XFER_COM_EN, 0); + } else { + while (spicc->remain) { + dma_one_burst(spicc); + ret = spicc_wait_complete(spicc, + spicc->burst_len << 13); + spicc_log(spicc, &spicc->remain, 1, XFER_COMP_POLLING); + if (!ret) + break; + } + } + setb(mem_base, DMA_EN, 0); + ret = ret ? 0 : -ETIMEDOUT; + spicc_log(spicc, &ret, 1, DMA_END); + return ret; +} + +/** + * Return: + * =0: xfer success, + * -ETIMEDOUT: timeout. + */ +static int spicc_hw_xfer(struct spicc *spicc, u8 *txp, u8 *rxp, int len) +{ + void __iomem *mem_base = spicc->regs; + int bytes_per_word; + int ret; + + setb(mem_base, RX_FIFO_RESET, 1); + setb(mem_base, TX_FIFO_RESET, 1); + setb(mem_base, CON_XCH, 0); + bytes_per_word = ((spicc->bits_per_word - 1)>>3) + 1; + spicc->remain = len / bytes_per_word; + spicc->txp = txp; + spicc->rxp = rxp; + spicc_log(spicc, &spicc->remain, 1, PIO_BEGIN); + if (spicc->irq) { + setb(mem_base, INT_XFER_COM_EN, 1); + enable_irq(spicc->irq); + pio_one_burst_send(spicc); + ret = wait_for_completion_interruptible_timeout( + &spicc->completion, msecs_to_jiffies(2000)); + disable_irq_nosync(spicc->irq); + setb(mem_base, INT_XFER_COM_EN, 0); + } else { + while (spicc->remain) { + pio_one_burst_send(spicc); + ret = spicc_wait_complete(spicc, + spicc->burst_len << 10); + spicc_log(spicc, &spicc->remain, 1, XFER_COMP_POLLING); + if (!ret) + break; + pio_one_burst_recv(spicc); + } + } + ret = ret ? 0 : -ETIMEDOUT; + spicc_log(spicc, &ret, 1, PIO_END); + return ret; +} + +static void spicc_hw_init(struct spicc *spicc) +{ + void __iomem *mem_base = spicc->regs; + + spicc->bits_per_word = -1; + spicc->mode = -1; + spicc->speed = -1; + spicc_enable(spicc, 0); + setb(mem_base, CLK_FREE_EN, 1); + setb(mem_base, CON_MODE, 1); /* 0-slave, 1-master */ + setb(mem_base, CON_XCH, 0); + setb(mem_base, CON_SMC, 0); + setb(mem_base, CON_SS_CTL, spicc_get_flag(spicc, FLAG_SSCTL)); + setb(mem_base, CON_DRCTL, 0); + spicc_enable(spicc, 1); + setb(mem_base, DELAY_CONTROL, spicc->delay_control); + writel((0x00<<26 | + 0x00<<20 | + 0x0<<19 | + spicc->dma_num_per_write_burst<<15 | + spicc->dma_num_per_read_burst<<11 | + spicc->dma_rx_threshold<<6 | + spicc->dma_tx_threshold<<1 | + 0x0<<0), + mem_base + SPICC_REG_DMA); + spicc_set_bit_width(spicc, SPICC_DEFAULT_BIT_WIDTH); + spicc_set_mode(spicc, SPI_MODE_0); + spicc_set_clk(spicc, SPICC_DEFAULT_SPEED_HZ); + if (spicc_get_flag(spicc, FLAG_ENHANCE)) { + setb(spicc->regs, MOSI_OEN, 1); + setb(spicc->regs, CLK_OEN, 1); + setb(mem_base, CS_OEN, 1); + setb(mem_base, CS_DELAY, spicc->cs_delay); + setb(mem_base, CS_DELAY_EN, 1); + } + /* spicc_enable(spicc, 0); */ +} + + +int dirspi_xfer(struct spi_device *spi, u8 *tx_buf, u8 *rx_buf, int len) +{ + struct spicc *spicc; + + spicc = spi_master_get_devdata(spi->master); + return spicc_hw_xfer(spicc, tx_buf, rx_buf, len); +} +EXPORT_SYMBOL(dirspi_xfer); + +int dirspi_write(struct spi_device *spi, u8 *buf, int len) +{ + struct spicc *spicc; + + spicc = spi_master_get_devdata(spi->master); + return spicc_hw_xfer(spicc, buf, 0, len); +} +EXPORT_SYMBOL(dirspi_write); + +int dirspi_read(struct spi_device *spi, u8 *buf, int len) +{ + struct spicc *spicc; + + spicc = spi_master_get_devdata(spi->master); + return spicc_hw_xfer(spicc, 0, buf, len); +} +EXPORT_SYMBOL(dirspi_read); + +void dirspi_start(struct spi_device *spi) +{ + spicc_chip_select(spi, 1); +} +EXPORT_SYMBOL(dirspi_start); + +void dirspi_stop(struct spi_device *spi) +{ + spicc_chip_select(spi, 0); +} +EXPORT_SYMBOL(dirspi_stop); + + +/* setting clock and pinmux here */ +static int spicc_setup(struct spi_device *spi) +{ + struct spicc *spicc; + + spicc = spi_master_get_devdata(spi->master); + if (spi->cs_gpio >= 0) + gpio_request(spi->cs_gpio, "spicc"); + dev_info(&spi->dev, "chip_select=%d cs_gpio=%d mode=%d speed=%d\n", + spi->chip_select, spi->cs_gpio, + spi->mode, spi->max_speed_hz); + spicc_chip_select(spi, 0); + return 0; +} + +static void spicc_cleanup(struct spi_device *spi) +{ + spicc_chip_select(spi, 0); + if (spi->cs_gpio >= 0) + gpio_free(spi->cs_gpio); +} + +static void spicc_handle_one_msg(struct spicc *spicc, struct spi_message *m) +{ + struct spi_device *spi = m->spi; + struct spi_transfer *t; + int ret = 0; + bool cs_changed = 0; + + spicc_log(spicc, 0, 0, HAND_MSG_BEGIN); + /* spicc_enable(spicc, 1); */ + if (spi) { + spicc_set_bit_width(spicc, + spi->bits_per_word ? : SPICC_DEFAULT_BIT_WIDTH); + spicc_set_clk(spicc, + spi->max_speed_hz ? : SPICC_DEFAULT_SPEED_HZ); + spicc_set_mode(spicc, spi->mode); + spicc_chip_select(spi, 1); + } + list_for_each_entry(t, &m->transfers, transfer_list) { + if (t->bits_per_word) + spicc_set_bit_width(spicc, t->bits_per_word); + if (t->speed_hz) + spicc_set_clk(spicc, t->speed_hz); + if (spi && cs_changed) { + spicc_chip_select(spi, 1); + cs_changed = 0; + } + if (t->delay_usecs >> 10) + udelay(t->delay_usecs >> 10); + + if (spicc_get_flag(spicc, FLAG_DMA_EN)) { + if (!m->is_dma_mapped) + spicc_dma_map(spicc, t); + ret = spicc_dma_xfer(spicc, t); + if (!m->is_dma_mapped) + spicc_dma_unmap(spicc, t); + } else { + ret = spicc_hw_xfer(spicc, (u8 *)t->tx_buf, + (u8 *)t->rx_buf, t->len); + } + if (ret) + goto spicc_handle_end; + m->actual_length += t->len; + + /* + * to delay after this transfer before + * (optionally) changing the chipselect status. + */ + if (t->delay_usecs & 0x3ff) + udelay(t->delay_usecs & 0x3ff); + if (spi && t->cs_change) { + spicc_chip_select(spi, 0); + cs_changed = 1; + } + } + +spicc_handle_end: + if (spi) + spicc_chip_select(spi, 0); + /* spicc_enable(spicc, 0); */ + m->status = ret; + if (m->context) + m->complete(m->context); + spicc_log(spicc, 0, 0, HAND_MSG_END); +} + +static int spicc_transfer(struct spi_device *spi, struct spi_message *m) +{ + struct spicc *spicc = spi_master_get_devdata(spi->master); + unsigned long flags; + + m->actual_length = 0; + m->status = -EINPROGRESS; + + spin_lock_irqsave(&spicc->lock, flags); + list_add_tail(&m->queue, &spicc->msg_queue); + queue_work(spicc->wq, &spicc->work); + spin_unlock_irqrestore(&spicc->lock, flags); + + return 0; +} + +static void spicc_work(struct work_struct *work) +{ + struct spicc *spicc = container_of(work, struct spicc, work); + struct spi_message *m; + unsigned long flags; + + spin_lock_irqsave(&spicc->lock, flags); + while (!list_empty(&spicc->msg_queue)) { + m = container_of(spicc->msg_queue.next, + struct spi_message, queue); + list_del_init(&m->queue); + spin_unlock_irqrestore(&spicc->lock, flags); + spicc_handle_one_msg(spicc, m); + spin_lock_irqsave(&spicc->lock, flags); + } + spin_unlock_irqrestore(&spicc->lock, flags); +} + +static ssize_t show_setting(struct class *class, + struct class_attribute *attr, char *buf) +{ + int ret = 0; + struct spicc *spicc = container_of(class, struct spicc, cls); + + if (!strcmp(attr->attr.name, "speed")) + ret = sprintf(buf, "speed=%d\n", spicc->speed); + else if (!strcmp(attr->attr.name, "mode")) + ret = sprintf(buf, "mode=%d\n", spicc->mode); + else if (!strcmp(attr->attr.name, "bit_width")) + ret = sprintf(buf, "bit_width=%d\n", spicc->bits_per_word); + else if (!strcmp(attr->attr.name, "flags")) + ret = sprintf(buf, "flags=0x%x\n", spicc->flags); + else if (!strcmp(attr->attr.name, "test_data")) + ret = sprintf(buf, "test_data=0x%x\n", spicc->test_data); + else if (!strcmp(attr->attr.name, "help")) { + pr_info("SPI device test help\n"); + pr_info("echo cs_gpio speed mode bits_per_word num"); + pr_info("[wbyte1 wbyte2...] >test\n"); + } +#ifdef CONFIG_SPICC_LOG + else if (!strcmp(attr->attr.name, "log")) { + spicc_log_print(spicc); + spicc->log_count = 0; + } +#endif + return ret; +} + + +static ssize_t store_setting( + struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + long value; + struct spicc *spicc = container_of(class, struct spicc, cls); + + if (kstrtol(buf, 0, &value)) + return -EINVAL; + if (!strcmp(attr->attr.name, "speed")) + spicc_set_clk(spicc, value); + else if (!strcmp(attr->attr.name, "mode")) + spicc_set_mode(spicc, value); + else if (!strcmp(attr->attr.name, "bit_width")) + spicc_set_bit_width(spicc, value); + else if (!strcmp(attr->attr.name, "flags")) + spicc->flags = value; + else if (!strcmp(attr->attr.name, "test_data")) + spicc->test_data = (u8)(value & 0xff); + return count; +} + +static ssize_t store_test( + struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + struct spicc *spicc = container_of(class, struct spicc, cls); + struct device *dev = spicc->master->dev.parent; + unsigned int cs_gpio, speed, mode, bits_per_word, num; + u8 *tx_buf, *rx_buf; + unsigned long value; + char *kstr, *str_temp, *token; + int i; + struct spi_transfer t; + struct spi_message m; + + if (sscanf(buf, "%d%d%d%d%d", &cs_gpio, &speed, + &mode, &bits_per_word, &num) != 5) { + dev_err(dev, "error test data\n"); + return count; + } + kstr = kstrdup(buf, GFP_KERNEL); + tx_buf = kzalloc(num, GFP_KERNEL | GFP_DMA); + rx_buf = kzalloc(num, GFP_KERNEL | GFP_DMA); + if (IS_ERR_OR_NULL(kstr) || + IS_ERR_OR_NULL(tx_buf) || + IS_ERR_OR_NULL(rx_buf)) { + dev_err(dev, "failed to alloc tx rx buffer\n"); + goto test_end; + } + + str_temp = kstr; + /* skip pass over "cs_gpio speed mode bits_per_word num" */ + for (i = 0; i < 5; i++) + strsep(&str_temp, ", "); + for (i = 0; i < num; i++) { + token = strsep(&str_temp, ", "); + if (!token || kstrtoul(token, 16, &value)) + break; + tx_buf[i] = (u8)(value & 0xff); + } + for (; i < num; i++) { + tx_buf[i] = spicc->test_data; + if (spicc_get_flag(spicc, FLAG_TEST_DATA_AUTO_INC)) + spicc->test_data++; + } + + spi_message_init(&m); + m.spi = spi_alloc_device(spicc->master); + if (cs_gpio < 1000) + m.spi->cs_gpio = cs_gpio; + else { + m.spi->cs_gpio = -ENOENT; + m.spi->chip_select = cs_gpio - 1000; + } + m.spi->max_speed_hz = speed; + m.spi->mode = mode; + m.spi->bits_per_word = bits_per_word; + spicc_setup(m.spi); + memset(&t, 0, sizeof(t)); + t.tx_buf = (void *)tx_buf; + t.rx_buf = (void *)rx_buf; + t.len = num; + spi_message_add_tail(&t, &m); + spicc_handle_one_msg(spicc, &m); + spi_dev_put(m.spi); + + dev_info(dev, "read back data ok\n"); + for (i = 0; i < min_t(size_t, 32, num); i++) + dev_info(dev, "[%d]: 0x%2x, 0x%2x\n", i, tx_buf[i], rx_buf[i]); +test_end: + kfree(kstr); + kfree(tx_buf); + kfree(rx_buf); + return count; +} + +static struct class_attribute spicc_class_attrs[] = { + __ATTR(test, 0200, NULL, store_test), + __ATTR(test_data, 0644, show_setting, store_setting), + __ATTR(speed, 0644, show_setting, store_setting), + __ATTR(mode, 0644, show_setting, store_setting), + __ATTR(bit_width, 0644, show_setting, store_setting), + __ATTR(flags, 0644, show_setting, store_setting), + __ATTR(help, 0444, show_setting, NULL), +#ifdef CONFIG_SPICC_LOG + __ATTR(log, 0444, show_setting, NULL), +#endif + __ATTR_NULL +}; + + +static int of_spicc_get_data( + struct spicc *spicc, + struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct resource *res; + int err; + unsigned int value; + + err = of_property_read_u32(np, "device_id", &spicc->device_id); + if (err) { + dev_err(&pdev->dev, "get device_id fail\n"); + return err; + } + + err = of_property_read_u32(np, "dma_en", &value); + spicc_set_flag(spicc, FLAG_DMA_EN, err ? 0 : (!!value)); + dev_info(&pdev->dev, "dma_en=%d\n", spicc_get_flag(spicc, FLAG_DMA_EN)); + err = of_property_read_u32(np, "dma_tx_threshold", &value); + spicc->dma_tx_threshold = err ? 3 : value; + err = of_property_read_u32(np, "dma_rx_threshold", &value); + spicc->dma_rx_threshold = err ? 3 : value; + err = of_property_read_u32(np, "dma_num_per_read_burst", &value); + spicc->dma_num_per_read_burst = err ? 3 : value; + err = of_property_read_u32(np, "dma_num_per_write_burst", &value); + spicc->dma_num_per_write_burst = err ? 3 : value; + spicc->irq = irq_of_parse_and_map(np, 0); + dev_info(&pdev->dev, "irq = 0x%x\n", spicc->irq); + err = of_property_read_u32(np, "delay_control", &value); + spicc->delay_control = err ? 0x15 : value; + dev_info(&pdev->dev, "delay_control=%d\n", spicc->delay_control); + err = of_property_read_u32(np, "enhance", &value); + spicc_set_flag(spicc, FLAG_ENHANCE, err ? 0 : (!!value)); + dev_info(&pdev->dev, "enhance=%d\n", + spicc_get_flag(spicc, FLAG_ENHANCE)); + err = of_property_read_u32(np, "cs_delay", &value); + spicc->cs_delay = err ? 0 : value; + err = of_property_read_u32(np, "ssctl", &value); + spicc_set_flag(spicc, FLAG_SSCTL, err ? 0 : (!!value)); +#ifdef CONFIG_SPICC_LOG + err = of_property_read_u32(np, "log_size", &value); + spicc->log_size = err ? 0 : value; +#endif + + spicc->pinctrl = devm_pinctrl_get(&pdev->dev); + if (IS_ERR_OR_NULL(spicc->pinctrl)) { + dev_err(&pdev->dev, "get pinctrl fail\n"); + return PTR_ERR(spicc->pinctrl); + } + spicc->pullup = pinctrl_lookup_state(spicc->pinctrl, "default"); + if (!IS_ERR_OR_NULL(spicc->pullup)) + pinctrl_select_state(spicc->pinctrl, spicc->pullup); + spicc->pullup = pinctrl_lookup_state( + spicc->pinctrl, "spicc_pullup"); + spicc->pulldown = pinctrl_lookup_state( + spicc->pinctrl, "spicc_pulldown"); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + spicc->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR_OR_NULL(spicc->regs)) { + dev_err(&pdev->dev, "get resource fail\n"); + return PTR_ERR(spicc->regs); + } + + spicc->clk = devm_clk_get(&pdev->dev, "spicc_clk"); + if (IS_ERR_OR_NULL(spicc->clk)) { + dev_err(&pdev->dev, "get clk fail\n"); + return PTR_ERR(spicc->clk); + } + return 0; +} + +static int spicc_probe(struct platform_device *pdev) +{ + struct spi_master *master; + struct spicc *spicc; + int i, ret; + + WARN_ON(!pdev->dev.of_node); + master = spi_alloc_master(&pdev->dev, sizeof(*spicc)); + if (IS_ERR_OR_NULL(master)) { + dev_err(&pdev->dev, "allocate spi master failed!\n"); + return -ENOMEM; + } + spicc = spi_master_get_devdata(master); + spicc->master = master; + dev_set_drvdata(&pdev->dev, spicc); + ret = of_spicc_get_data(spicc, pdev); + if (ret) { + dev_err(&pdev->dev, "of error=%d\n", ret); + goto err; + } + spicc_hw_init(spicc); + spicc_log_init(spicc); + spicc_log(spicc, 0, 0, PROBE_BEGIN); + + master->dev.of_node = pdev->dev.of_node; + master->bus_num = spicc->device_id; + master->setup = spicc_setup; + master->transfer = spicc_transfer; + master->cleanup = spicc_cleanup; + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; + ret = spi_register_master(master); + if (ret < 0) { + dev_err(&pdev->dev, "register spi master failed! (%d)\n", ret); + goto err; + } + + INIT_WORK(&spicc->work, spicc_work); + spicc->wq = create_singlethread_workqueue(dev_name(master->dev.parent)); + if (spicc->wq == NULL) { + ret = -EBUSY; + goto err; + } + spin_lock_init(&spicc->lock); + INIT_LIST_HEAD(&spicc->msg_queue); + + init_completion(&spicc->completion); + if (spicc->irq) { + if (request_irq(spicc->irq, spicc_xfer_complete_isr, + IRQF_SHARED, "spicc", spicc)) { + dev_err(&pdev->dev, "master %d request irq(%d) failed!\n", + master->bus_num, spicc->irq); + spicc->irq = 0; + } else + disable_irq_nosync(spicc->irq); + } + spicc_log(spicc, &spicc->irq, 1, REQUEST_IRQ); + + /*setup class*/ + spicc->cls.name = kzalloc(10, GFP_KERNEL); + sprintf((char *)spicc->cls.name, "spicc%d", master->bus_num); + spicc->cls.class_attrs = spicc_class_attrs; + ret = class_register(&spicc->cls); + if (ret < 0) + dev_err(&pdev->dev, "register class failed! (%d)\n", ret); + + dev_info(&pdev->dev, "cs_num=%d\n", master->num_chipselect); + if (master->cs_gpios) { + for (i = 0; i < master->num_chipselect; i++) + dev_info(&pdev->dev, "cs[%d]=%d\n", i, + master->cs_gpios[i]); + } + spicc_log(spicc, 0, 0, PROBE_END); + return ret; +err: + spi_master_put(master); + return ret; +} + +static int spicc_remove(struct platform_device *pdev) +{ + struct spicc *spicc; + + spicc = (struct spicc *)dev_get_drvdata(&pdev->dev); + spicc_log_exit(spicc); + spi_unregister_master(spicc->master); + destroy_workqueue(spicc->wq); + if (spicc->pinctrl) + devm_pinctrl_put(spicc->pinctrl); + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id spicc_of_match[] = { + { .compatible = "amlogic, spicc", }, + {}, +}; +#else +#define spicc_of_match NULL +#endif + +static struct platform_driver spicc_driver = { + .probe = spicc_probe, + .remove = spicc_remove, + .driver = { + .name = "spicc", + .of_match_table = spicc_of_match, + .owner = THIS_MODULE, + }, +}; + +static int __init spicc_init(void) +{ + return platform_driver_register(&spicc_driver); +} + +static void __exit spicc_exit(void) +{ + platform_driver_unregister(&spicc_driver); +} + +subsys_initcall(spicc_init); +module_exit(spicc_exit); + +MODULE_DESCRIPTION("Amlogic SPICC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/amlogic/spicc/spicc.h b/drivers/amlogic/spicc/spicc.h new file mode 100644 index 000000000000..51e43d1eb173 --- /dev/null +++ b/drivers/amlogic/spicc/spicc.h @@ -0,0 +1,112 @@ +/* + * drivers/amlogic/spicc/spicc.h + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#ifndef __SPICC_H__ +#define __SPICC_H__ + +#include + +#define SPICC_FIFO_SIZE 16 +#define SPICC_DEFAULT_BIT_WIDTH 8 +#define SPICC_DEFAULT_SPEED_HZ 3000000 + +#define SPICC_REG_RXDATA (0<<2) +#define SPICC_REG_TXDATA (1<<2) +#define SPICC_REG_CON (2<<2) +#define SPICC_REG_INT (3<<2) +#define SPICC_REG_DMA (4<<2) +#define SPICC_REG_STA (5<<2) +#define SPICC_REG_PERIOD (6<<2) +#define SPICC_REG_TEST (7<<2) +#define SPICC_REG_DRADDR (8<<2) +#define SPICC_REG_DWADDR (9<<2) +#define SPICC_REG_LD_CNTL0 (10<<2) +#define SPICC_REG_LD_CNTL1 (11<<2) +#define SPICC_REG_LD_RADDR (12<<2) +#define SPICC_REG_LD_WADDR (13<<2) +#define SPICC_REG_ENHANCE_CNTL (14<<2) + +#define CON_ENABLE bits_desc(SPICC_REG_CON, 0, 1) +#define CON_MODE bits_desc(SPICC_REG_CON, 1, 1) +#define CON_XCH bits_desc(SPICC_REG_CON, 2, 1) +#define CON_SMC bits_desc(SPICC_REG_CON, 3, 1) +#define CON_CLK_POL bits_desc(SPICC_REG_CON, 4, 1) +#define CON_CLK_PHA bits_desc(SPICC_REG_CON, 5, 1) +#define CON_SS_CTL bits_desc(SPICC_REG_CON, 6, 1) +#define CON_SS_POL bits_desc(SPICC_REG_CON, 7, 1) +#define CON_DRCTL bits_desc(SPICC_REG_CON, 8, 2) +#define CON_CHIP_SELECT bits_desc(SPICC_REG_CON, 12, 2) +#define CON_DATA_RATE_DIV bits_desc(SPICC_REG_CON, 16, 3) +#define CON_BITS_PER_WORD bits_desc(SPICC_REG_CON, 19, 6) +#define CON_BURST_LEN bits_desc(SPICC_REG_CON, 25, 7) +#define BURST_LEN_MAX 128 + +#define INT_TX_EMPTY_EN bits_desc(SPICC_REG_INT, 0, 1) +#define INT_TX_HALF_EN bits_desc(SPICC_REG_INT, 1, 1) +#define INT_TX_FULL_EN bits_desc(SPICC_REG_INT, 2, 1) +#define INT_RX_READY_EN bits_desc(SPICC_REG_INT, 3, 1) +#define INT_RX_HALF_EN bits_desc(SPICC_REG_INT, 4, 1) +#define INT_RX_FULL_EN bits_desc(SPICC_REG_INT, 5, 1) +#define INT_RX_OF_EN bits_desc(SPICC_REG_INT, 6, 1) +#define INT_XFER_COM_EN bits_desc(SPICC_REG_INT, 7, 1) + +#define DMA_EN bits_desc(SPICC_REG_DMA, 0, 1) +#define DMA_TX_FIFO_TH bits_desc(SPICC_REG_DMA, 1, 5) +#define DMA_RX_FIFO_TH bits_desc(SPICC_REG_DMA, 6, 5) +#define DMA_NUM_RD_BURST bits_desc(SPICC_REG_DMA, 11, 4) +#define DMA_NUM_WR_BURST bits_desc(SPICC_REG_DMA, 15, 4) +#define DMA_URGENT bits_desc(SPICC_REG_DMA, 19, 1) +#define DMA_THREAD_ID bits_desc(SPICC_REG_DMA, 20, 6) +#define DMA_BURST_NUM bits_desc(SPICC_REG_DMA, 26, 6) + +#define STA_TX_EMPTY bits_desc(SPICC_REG_STA, 0, 1) +#define STA_TX_HALF bits_desc(SPICC_REG_STA, 1, 1) +#define STA_TX_FULL bits_desc(SPICC_REG_STA, 2, 1) +#define STA_RX_READY bits_desc(SPICC_REG_STA, 3, 1) +#define STA_RX_HALF bits_desc(SPICC_REG_STA, 4, 1) +#define STA_RX_FULL bits_desc(SPICC_REG_STA, 5, 1) +#define STA_RX_OF bits_desc(SPICC_REG_STA, 6, 1) +#define STA_XFER_COM bits_desc(SPICC_REG_STA, 7, 1) + +#define TX_COUNT bits_desc(SPICC_REG_TEST, 0, 5) +#define RX_COUNT bits_desc(SPICC_REG_TEST, 5, 5) +#define DELAY_CONTROL bits_desc(SPICC_REG_TEST, 16, 6) +#define RX_FIFO_RESET bits_desc(SPICC_REG_TEST, 22, 1) +#define TX_FIFO_RESET bits_desc(SPICC_REG_TEST, 23, 1) +#define CLK_FREE_EN bits_desc(SPICC_REG_TEST, 24, 1) + +#define CS_DELAY bits_desc(SPICC_REG_ENHANCE_CNTL, 0, 16) +#define ENHANCE_CLK_DIV bits_desc(SPICC_REG_ENHANCE_CNTL, 16, 8) +#define ENHANCE_CLK_DIV_SELECT bits_desc(SPICC_REG_ENHANCE_CNTL, 24, 1) +#define MOSI_OEN bits_desc(SPICC_REG_ENHANCE_CNTL, 25, 1) +#define CLK_OEN bits_desc(SPICC_REG_ENHANCE_CNTL, 26, 1) +#define CS_OEN bits_desc(SPICC_REG_ENHANCE_CNTL, 27, 1) +#define CS_DELAY_EN bits_desc(SPICC_REG_ENHANCE_CNTL, 28, 1) +#define MAIN_CLK_AO bits_desc(SPICC_REG_ENHANCE_CNTL, 29, 1) + + +struct spicc_platform_data { + int device_id; + struct spicc_regs __iomem *regs; + struct pinctrl *pinctrl; + struct clk *clk; + int num_chipselect; + int *cs_gpios; +}; + +#endif +