diff options
Diffstat (limited to 'drivers/spi/sprd_spi_r4p0.c')
| -rwxr-xr-x | drivers/spi/sprd_spi_r4p0.c | 785 |
1 files changed, 785 insertions, 0 deletions
diff --git a/drivers/spi/sprd_spi_r4p0.c b/drivers/spi/sprd_spi_r4p0.c new file mode 100755 index 00000000..b9d927d0 --- /dev/null +++ b/drivers/spi/sprd_spi_r4p0.c @@ -0,0 +1,785 @@ +/* + * Copyright (C) 2013 Spreadtrum Communications Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/spi/spi.h> +#include <linux/sched.h> +#include <linux/kthread.h> +#include <linux/of.h> +#include <linux/of_device.h> + +#include <soc/sprd/sci_glb_regs.h> +#include <soc/sprd/sci.h> +#include "sprd_spi_r4p0.h" + +unsigned long REGS_SPI0_BASE = 0; +unsigned long REGS_SPI1_BASE = 0; +unsigned long REGS_SPI2_BASE = 0; + +#define SPI_BASE(id) \ + ((id == 0) ? REGS_SPI0_BASE : ((id == 1) ? REGS_SPI1_BASE : REGS_SPI2_BASE )) + +#define ALIGN_UP(a, b)\ + (((a) + ((b) - 1)) & ~((b) - 1)) + +#define MHz(inte, dec) ((inte) * 1000 * 1000 + (dec) * 1000 * 100) + +struct clk_src { + u32 freq; + const char *name; +}; + +static struct clk_src spi_src_tab[] = { + { + .freq = MHz(26, 0), + .name = "ext_26m", + }, + { + .freq = MHz(96, 0), + .name = "clk_96m", + }, + { + .freq = MHz(153, 6), + .name = "clk_153m6", + }, + { + .freq = MHz(192, 0), + .name = "clk_192m", + }, +}; + +struct sprd_spi_devdata { + void __iomem *reg_base; + int irq_num; + struct clk *clk; + spinlock_t lock; + struct list_head msg_queue; + struct work_struct work; + struct workqueue_struct *work_queue; + /* backup list + *0 clk_div + *1 ctl0 + *2 ctl1 + *3 ctl2 + *4 ctl3 + *5 ctl4 + *6 ctl5 + *7 int_en + *8 dsp_wait + *9 ctl6 + *10 ctl7 + */ + u32 reg_backup[11]; + bool is_active; +}; + +enum spi_transfer_mode { + tx_mode, + rx_mode, + rt_mode, +}; + +//extern void clk_force_disable(struct clk *); + +static void sprd_spi_dump_regs(unsigned long reg_base) +{ + printk("SPI_CLKD:0x%x \n", __raw_readl((void __iomem *)(reg_base + SPI_CLKD))); + printk("SPI_CTL0:0x%x \n", __raw_readl((void __iomem *)(reg_base + SPI_CTL0))); + printk("SPI_CTL1:0x%x \n", __raw_readl((void __iomem *)(reg_base + SPI_CTL1))); + printk("SPI_CTL2:0x%x \n", __raw_readl((void __iomem *)(reg_base + SPI_CTL2))); + printk("SPI_CTL3:0x%x \n", __raw_readl((void __iomem *)(reg_base + SPI_CTL3))); + printk("SPI_CTL4:0x%x \n", __raw_readl((void __iomem *)(reg_base + SPI_CTL4))); + printk("SPI_CTL5:0x%x \n", __raw_readl((void __iomem *)(reg_base + SPI_CTL5))); + printk("SPI_INT_EN:0x%x \n", __raw_readl((void __iomem *)(reg_base + SPI_INT_EN))); + printk("SPI_DSP_WAIT:0x%x \n", __raw_readl((void __iomem *)(reg_base + SPI_DSP_WAIT))); + printk("SPI_CTL6:0x%x \n", __raw_readl((void __iomem *)(reg_base + SPI_CTL6))); + printk("SPI_CTL7:0x%x \n", __raw_readl((void __iomem *)(reg_base + SPI_CTL7))); +} + +static void sprd_spi_wait_for_send_complete(unsigned long reg_base) +{ + u32 timeout = 0; + while (!(__raw_readl((void __iomem *)(reg_base + SPI_STS2)) & SPI_TX_FIFO_REALLY_EMPTY)) + { + if (++timeout > SPI_TIME_OUT) { + /*fixme, set timeout*/ + printk("spi send timeout!\n"); + sprd_spi_dump_regs(reg_base); + BUG_ON(1); + } + } + + while (__raw_readl((void __iomem *)(reg_base + SPI_STS2)) & SPI_TX_BUSY) + { + if (++timeout > SPI_TIME_OUT) { + /*fixme, set timeout*/ + printk("spi send timeout!\n"); + sprd_spi_dump_regs(reg_base); + BUG_ON(1); + } + } +} + +static int sprd_spi_transfer_full_duplex(struct spi_device *spi_dev, + struct spi_transfer *trans_node) +{ + int i; + u32 reg_val; + u32 bits_per_word, block_num; + u32 send_data_msk; + u8 *tx_u8_p, *rx_u8_p; + u16 *tx_u16_p, *rx_u16_p; + u32 *tx_u32_p, *rx_u32_p; + const void *src_buf; + void *dst_buf; + u32 transfer_mode; + unsigned long reg_base; + struct sprd_spi_devdata *spi_chip; + + spi_chip = spi_master_get_devdata(spi_dev->master); + reg_base = (unsigned long)spi_chip->reg_base; +#if 0 + /* We config the bits_per_word in function spi_new_device() + * but you can reconfig thi option in spi_transfer + */ + if (unlikely(trans_node->bits_per_word != spi_dev->bits_per_word)) { + printk("%s %d\n", __func__, __LINE__); + reg_val = __raw_readl(spi_chip->reg_base + SPI_CTL0); + reg_val &= ~(0x1f << 2); + if (trans_node->bits_per_word != MAX_BITS_PER_WORD) { + reg_val |= trans_node->bits_per_word << 2; + printk("%s %d\n", __func__, __LINE__); + } + __raw_writel(reg_val, spi_chip->reg_base + SPI_CTL0); + } + /*fixme!the bits_per_word alaign up with 8*/ + bytes_per_word = ALIGN_UP(trans_node->bits_per_word, 8) >> 3; +#else + bits_per_word = ALIGN_UP(spi_dev->bits_per_word, 8); + + reg_val = __raw_readl((void __iomem *)(reg_base + SPI_CTL0)); + reg_val &= ~(0x1f << 2); + if (32 != bits_per_word) { + reg_val |= bits_per_word << 2; + } + writel(reg_val, (void __iomem *)(reg_base + SPI_CTL0)); + +#endif + /*send data buf*/ + src_buf = trans_node->tx_buf; + + /*recv data buf*/ + dst_buf = trans_node->rx_buf; + + send_data_msk = 0xffffffff; + + if (src_buf && dst_buf) { + transfer_mode = rt_mode; + } else { + if (dst_buf) { + transfer_mode = rx_mode; + /*in rx mode, we always send 0 to slave*/ + send_data_msk = 0x0; + src_buf = trans_node->rx_buf; + } else { + transfer_mode = tx_mode; + } + } + + reg_val = __raw_readl((void __iomem *)(reg_base + SPI_CTL1)); + + reg_val &= ~(0x3 << 12); + + if (transfer_mode == tx_mode) { + reg_val |= SPI_TX_MODE; + } else { + reg_val |= SPI_RX_MODE |SPI_TX_MODE; + } + + writel(reg_val, (void __iomem *)(reg_base + SPI_CTL1)); + + /*reset the fifo*/ + writel(0x1, (void __iomem *)(reg_base + SPI_FIFO_RST)); + writel(0x0, (void __iomem *)(reg_base + SPI_FIFO_RST)); + + /*alaway set cs pin to low level when transfer */ + if (spi_dev->chip_select < SPRD_SPI_CHIP_CS_NUM) { + reg_val = __raw_readl((void __iomem *)(reg_base + SPI_CTL0)); + reg_val &= ~(0x1 << (spi_dev->chip_select + 8)); + writel(reg_val, (void __iomem *)(reg_base + SPI_CTL0)); + } else { + /*fixme, need to support gpio cs*/ + } + + switch (bits_per_word) { + case 8: + tx_u8_p = (u8 *)src_buf; + rx_u8_p = (u8 *)dst_buf; + block_num = trans_node->len; + for (; block_num >= SPRD_SPI_FIFO_SIZE; block_num -= SPRD_SPI_FIFO_SIZE) + { + for (i = 0; i < SPRD_SPI_FIFO_SIZE; i++, tx_u8_p++) + { + __raw_writeb(*tx_u8_p & send_data_msk, + (void __iomem *)(reg_base + SPI_TXD)); + } + + sprd_spi_wait_for_send_complete(reg_base); + + if (transfer_mode == tx_mode) + continue; + + for (i = 0; i < SPRD_SPI_FIFO_SIZE; i++, rx_u8_p++) + { + *rx_u8_p = __raw_readb((void __iomem *)(reg_base + SPI_TXD)); + } + } + + for (i = 0; i < block_num; i++, tx_u8_p++) + { + __raw_writeb(*tx_u8_p & send_data_msk, (void __iomem *)(reg_base + SPI_TXD)); + } + + sprd_spi_wait_for_send_complete(reg_base); + + if (transfer_mode == tx_mode) + break; + + for (i = 0; i < block_num; i++, rx_u8_p++) + { + *rx_u8_p = __raw_readb((void __iomem *)(reg_base + SPI_TXD)); + } + break; + + case 16: + tx_u16_p = (u16 *)src_buf; + rx_u16_p = (u16 *)dst_buf; + block_num = trans_node->len >> 1; + + for (;block_num >= SPRD_SPI_FIFO_SIZE; block_num -= SPRD_SPI_FIFO_SIZE) + { + for (i =0; i < SPRD_SPI_FIFO_SIZE; i++, tx_u16_p++) + { + __raw_writew(*tx_u16_p & send_data_msk, + (void __iomem *)(reg_base + SPI_TXD)); + } + + sprd_spi_wait_for_send_complete(reg_base); + + if (transfer_mode == tx_mode) + continue; + + for (i = 0; i < SPRD_SPI_FIFO_SIZE; i++, rx_u16_p++) + { + *rx_u16_p = __raw_readw((void __iomem *)(reg_base + SPI_TXD)); + } + } + + for (i = 0; i < block_num; i++, tx_u16_p++) + { + __raw_writew(*tx_u16_p & send_data_msk, (void __iomem *)(reg_base + SPI_TXD)); + } + + sprd_spi_wait_for_send_complete(reg_base); + + if (transfer_mode == tx_mode) + break; + + for (i = 0; i < block_num; i++, rx_u16_p++) + { + *rx_u16_p = __raw_readw((void __iomem *)(reg_base + SPI_TXD)); + } + break; + + case 32: + tx_u32_p = (u32 *)src_buf; + rx_u32_p = (u32 *)dst_buf; + block_num = (trans_node->len + 3) >> 2; + + for (;block_num >= SPRD_SPI_FIFO_SIZE; block_num -= SPRD_SPI_FIFO_SIZE) + { + for (i = 0; i < SPRD_SPI_FIFO_SIZE; i++, tx_u32_p++) + { + __raw_writel(*tx_u32_p & send_data_msk, + (void __iomem *)(reg_base + SPI_TXD)); + } + + sprd_spi_wait_for_send_complete(reg_base); + + if (transfer_mode == tx_mode) + continue; + + for (i = 0; i < SPRD_SPI_FIFO_SIZE; i++, rx_u32_p++) + { + *rx_u32_p = __raw_readl((void __iomem *)(reg_base + SPI_TXD)); + } + } + + for (i = 0; i < block_num; i++, tx_u32_p++) + { + __raw_writel(*tx_u32_p & send_data_msk, (void __iomem *)(reg_base + SPI_TXD)); + } + + sprd_spi_wait_for_send_complete(reg_base); + + if (transfer_mode == tx_mode) + break; + + for (i = 0; i < block_num; i++, rx_u32_p++) + { + *rx_u32_p = __raw_readl((void __iomem *)(reg_base + SPI_TXD)); + } + + break; + default: + /*fixme*/ + break; + } + + if (spi_dev->chip_select < SPRD_SPI_CHIP_CS_NUM) { + reg_val = __raw_readl((void __iomem *)(reg_base + SPI_CTL0)); + reg_val |= 0xf << 8; + __raw_writel(reg_val, (void __iomem *)(reg_base + SPI_CTL0)); + } else { + /*fixme, need to support gpio cs*/ + } + + reg_val = __raw_readl((void __iomem *)(reg_base + SPI_CTL1)); + reg_val &= ~(0x3 << 12); + writel(reg_val, (void __iomem *)(reg_base + SPI_CTL1)); + + return trans_node->len; +} + +static void sprd_spi_transfer_work(struct work_struct *work) +{ + int ret; + struct sprd_spi_devdata *spi_chip; + struct spi_message *spi_msg; + struct spi_transfer *transfer_node; + unsigned long flags; + + spi_chip = container_of(work, struct sprd_spi_devdata, work); + + clk_prepare_enable(spi_chip->clk); + + /*fixme*/ + spin_lock_irqsave(&spi_chip->lock, flags); + + while (!list_empty(&spi_chip->msg_queue)) + { + spi_msg = container_of(spi_chip->msg_queue.next, + struct spi_message, queue); + list_del_init(&spi_msg->queue); + spin_unlock_irqrestore(&spi_chip->lock, flags); + + list_for_each_entry(transfer_node, + &spi_msg->transfers, transfer_list) + { + if (transfer_node->tx_buf || transfer_node->rx_buf) { + ret = sprd_spi_transfer_full_duplex(spi_msg->spi, + transfer_node); + } + } + + if (spi_msg->complete) { + spi_msg->status = 0x0; + spi_msg->complete(spi_msg->context); + } + spin_lock_irqsave(&spi_chip->lock, flags); + } + + //clk_disable_unprepare(spi_chip->clk); + + spin_unlock_irqrestore(&spi_chip->lock, flags); + + clk_disable_unprepare(spi_chip->clk); +} + +static int sprd_spi_transfer(struct spi_device *spi_dev, struct spi_message *msg) +{ + struct sprd_spi_devdata *spi_chip; + unsigned long flags; + + spi_chip = spi_master_get_devdata(spi_dev->master); + + spin_lock_irqsave(&spi_chip->lock, flags); + + list_add_tail(&msg->queue, &spi_chip->msg_queue); + + queue_work(spi_chip->work_queue, &spi_chip->work); + + spin_unlock_irqrestore(&spi_chip->lock, flags); + + return 0; +} + +/*this function must be exectued in spi's clk is enable*/ +static void sprd_spi_backup_config(struct sprd_spi_devdata *spi_chip) +{ + unsigned long reg_base; + + reg_base = (unsigned long)spi_chip->reg_base; + + spi_chip->reg_backup[0] = __raw_readl((void __iomem *)(reg_base + SPI_CLKD)); + spi_chip->reg_backup[1] = __raw_readl((void __iomem *)(reg_base + SPI_CTL0)); + spi_chip->reg_backup[2] = __raw_readl((void __iomem *)(reg_base + SPI_CTL1)); + spi_chip->reg_backup[3] = __raw_readl((void __iomem *)(reg_base + SPI_CTL2)); + spi_chip->reg_backup[4] = __raw_readl((void __iomem *)(reg_base + SPI_CTL3)); + spi_chip->reg_backup[5] = __raw_readl((void __iomem *)(reg_base + SPI_CTL4)); + spi_chip->reg_backup[6] = __raw_readl((void __iomem *)(reg_base + SPI_CTL5)); + spi_chip->reg_backup[7] = __raw_readl((void __iomem *)(reg_base + SPI_INT_EN)); + spi_chip->reg_backup[8] = __raw_readl((void __iomem *)(reg_base + SPI_DSP_WAIT)); + spi_chip->reg_backup[9] = __raw_readl((void __iomem *)(reg_base + SPI_CTL6)); + spi_chip->reg_backup[10] = __raw_readl((void __iomem *)(reg_base + SPI_CTL7)); +} + +/*this function must be exectued in spi's clk is enable*/ +static void sprd_spi_restore_config(const struct sprd_spi_devdata *spi_chip) +{ + unsigned long reg_base; + + reg_base = (unsigned long)spi_chip->reg_base; + + __raw_writel(spi_chip->reg_backup[0], (void __iomem *)(reg_base + SPI_CLKD)); + __raw_writel(spi_chip->reg_backup[1], (void __iomem *)(reg_base + SPI_CTL0)); + __raw_writel(spi_chip->reg_backup[2], (void __iomem *)(reg_base + SPI_CTL1)); + __raw_writel(spi_chip->reg_backup[3], (void __iomem *)(reg_base + SPI_CTL2)); + __raw_writel(spi_chip->reg_backup[4], (void __iomem *)(reg_base + SPI_CTL3)); + __raw_writel(spi_chip->reg_backup[5], (void __iomem *)(reg_base + SPI_CTL4)); + __raw_writel(spi_chip->reg_backup[6], (void __iomem *)(reg_base + SPI_CTL5)); + __raw_writel(spi_chip->reg_backup[7], (void __iomem *)(reg_base + SPI_INT_EN)); + __raw_writel(spi_chip->reg_backup[8], (void __iomem *)(reg_base + SPI_DSP_WAIT)); + __raw_writel(spi_chip->reg_backup[9], (void __iomem *)(reg_base + SPI_CTL6)); + __raw_writel(spi_chip->reg_backup[10], (void __iomem *)(reg_base + SPI_CTL7)); +} + +static int sprd_spi_setup(struct spi_device *spi_dev) +{ + int i; + u32 reg_val; + u32 spi_work_clk, spi_src_clk, spi_clk_div; + const char *src_clk_name; + struct clk *clk_parent; + struct sprd_spi_devdata *spi_chip; + + spi_chip = spi_master_get_devdata(spi_dev->master); + + /*fixme, need to check the parmeter*/ + + /*if the spi_src_clk is 48MHz,the spi max working clk is 24MHz*/ + spi_work_clk = spi_dev->max_speed_hz << 1; + if (spi_work_clk > MHz(192, 0)) + return -EINVAL; + for(i = 0; i < ARRAY_SIZE(spi_src_tab); i++) { + if(!(spi_src_tab[i].freq % spi_work_clk)) + break; + } + if(i == ARRAY_SIZE(spi_src_tab)) + i--; + src_clk_name = spi_src_tab[i].name; + spi_src_clk = spi_src_tab[i].freq; + + clk_parent = clk_get(&(spi_dev->master->dev), src_clk_name); + if (IS_ERR(clk_parent)) { + printk("Can't get the clock source: %s\n", src_clk_name); + return -EINVAL; + } + + clk_set_parent(spi_chip->clk, clk_parent); + + /*global enable*/ + clk_prepare_enable(spi_chip->clk); + + /*spi config*/ + if (spi_dev->bits_per_word > MAX_BITS_PER_WORD) { + /*fixme*/ + } + + /*the default bits_per_word is 8*/ + if (spi_dev->bits_per_word == MAX_BITS_PER_WORD) { + reg_val = 0x0 << 2; + } else { + reg_val = spi_dev->bits_per_word << 2; + } + + /*set all cs to high level*/ + reg_val |= 0xf << 8; + + /*spi mode config*/ + switch (spi_dev->mode & 0x3) { + case SPI_MODE_0: + reg_val |= BIT(1); + break; + case SPI_MODE_1: + reg_val |= BIT(0); + break; + case SPI_MODE_2: + reg_val |= BIT(13) | BIT(1); + break; + case SPI_MODE_3: + reg_val |= BIT(13) | BIT(0); + break; + default: + /*fixme*/ + break; + } + __raw_writel(reg_val, (void __iomem *)((unsigned long)spi_chip->reg_base + SPI_CTL0)); + __raw_writel(0x0, (void __iomem *)((unsigned long)spi_chip->reg_base + SPI_CTL1)); + /*spi master mode*/ + __raw_writel(0x0, (void __iomem *)((unsigned long)spi_chip->reg_base + SPI_CTL2)); + __raw_writel(0x0, (void __iomem *)((unsigned long)spi_chip->reg_base + SPI_CTL4)); + __raw_writel(0x9, (void __iomem *)((unsigned long)spi_chip->reg_base + SPI_CTL5)); + __raw_writel(0x0, (void __iomem *)((unsigned long)spi_chip->reg_base + SPI_INT_EN)); + /*reset fifo*/ + __raw_writel(0x1, (void __iomem *)((unsigned long)spi_chip->reg_base + SPI_FIFO_RST)); + for (i = 0; i < 0x20; i++); + __raw_writel(0x0, (void __iomem *)((unsigned long)spi_chip->reg_base + SPI_FIFO_RST)); + + clk_set_rate(spi_chip->clk, spi_src_clk); + + spi_clk_div = spi_src_clk / (spi_dev->max_speed_hz << 1) - 1; + + __raw_writel(spi_clk_div, (void __iomem *)((unsigned long)spi_chip->reg_base + SPI_CLKD)); + + /*wait the clk config become effective*/ + msleep(5); + + sprd_spi_backup_config(spi_chip); + spi_chip->is_active = true; + + /*disable the clk after config complete*/ + clk_disable_unprepare(spi_chip->clk); + + return 0; +} + +static void sprd_spi_cleanup(struct spi_device *spi) +{ + struct sprd_spi_devdata *spi_chip; + + spi_chip = spi_master_get_devdata(spi->master); + + clk_disable_unprepare(spi_chip->clk); +} + +static int sprd_spi_probe(struct platform_device *pdev) +{ + int ret; + int irq_num; + struct resource *regs; + struct spi_master *master; + struct sprd_spi_devdata *spi_chip; + + pdev->id = of_alias_get_id(pdev->dev.of_node, "spi"); + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) { + dev_err(&pdev->dev, "spi_%d get io resource failed!\n", pdev->id); + return -ENODEV; + } + + if (0 == pdev->id) { + REGS_SPI0_BASE = (unsigned long)ioremap_nocache(regs->start, + resource_size(regs)); + if (!REGS_SPI0_BASE) + BUG(); + } + else if (1 == pdev->id) { + REGS_SPI1_BASE = (unsigned long)ioremap_nocache(regs->start, + resource_size(regs)); + if (!REGS_SPI1_BASE) + BUG(); + } + else if (2 == pdev->id) { + REGS_SPI2_BASE = (unsigned long)ioremap_nocache(regs->start, + resource_size(regs)); + if (!REGS_SPI2_BASE) + BUG(); + } + + dev_info(&pdev->dev, "probe spi %d get regbase 0x%lx\n", pdev->id, + SPI_BASE(pdev->id)); + + irq_num = platform_get_irq(pdev, 0); + if (irq_num < 0) { + dev_err(&pdev->dev, "spi_%d get irq resource failed!\n", pdev->id); + return irq_num; + } + dev_info(&pdev->dev, "probe spi %d get irq num %d\n", pdev->id, irq_num); + + master = spi_alloc_master(&pdev->dev, sizeof (*spi_chip)); + if (!master) { + dev_err(&pdev->dev, "spi_%d spi_alloc_master failed!\n", pdev->id); + return -ENOMEM; + } + + spi_chip = spi_master_get_devdata(master); + + /*get clk source*/ + switch (pdev->id) { + case sprd_spi_0: + spi_chip->clk = clk_get(NULL, "clk_spi0"); + break; + case sprd_spi_1: + spi_chip->clk = clk_get(NULL, "clk_spi1"); + break; + case sprd_spi_2: + spi_chip->clk = clk_get(NULL, "clk_spi2"); + break; + default: + ret = -EINVAL; + goto err_exit; + } + if (IS_ERR(spi_chip->clk)) { + ret = -ENXIO; + goto err_exit; + } + spi_chip->reg_base = (void __iomem *)SPI_BASE(pdev->id); + spi_chip->irq_num = irq_num; + + master->mode_bits = SPI_CPOL | SPI_CPHA; + master->bus_num = pdev->id; + master->num_chipselect = SPRD_SPI_CHIP_CS_NUM; + + master->setup = sprd_spi_setup; +#ifdef SPI_NEW_INTERFACE + master->prepare_transfer_hardware = xxxxx; + master->transfer_one_message = xxxx; + master->unprepare_transfer_hardware = xxxx; +#else + master->transfer = sprd_spi_transfer; + master->cleanup = sprd_spi_cleanup; +#endif + + INIT_LIST_HEAD(&spi_chip->msg_queue); + spin_lock_init(&spi_chip->lock); + /*on multi core system, use the create_workqueue() is better??*/ +// spi_chip->work_queue = create_singlethread_workqueue(pdev->name); + spi_chip->work_queue = create_workqueue(pdev->name); + INIT_WORK(&spi_chip->work, sprd_spi_transfer_work); + + platform_set_drvdata(pdev, master); + + ret = spi_register_master(master); + if (ret) { + printk("register spi master %d failed!\n", master->bus_num); + goto err_exit; + } + + return 0; + +err_exit: + spi_master_put(master); + kfree(master); + + return ret; +} + +static int __exit sprd_spi_remove(struct platform_device *pdev) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct sprd_spi_devdata *spi_chip = spi_master_get_devdata(master); + + destroy_workqueue(spi_chip->work_queue); + + clk_disable_unprepare(spi_chip->clk); + + spi_unregister_master(master); + + spi_master_put(master); + + return 0; +} + +#ifdef CONFIG_PM +static int sprd_spi_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + struct spi_master *master = platform_get_drvdata(pdev); + + struct sprd_spi_devdata *spi_chip = spi_master_get_devdata(master); + if (IS_ERR(spi_chip->clk)) { + pr_err("can't get spi_clk when suspend()\n"); + return -1; + } + + if (spi_chip->is_active) + clk_disable_unprepare(spi_chip->clk); + + return 0; +} + +static int sprd_spi_resume(struct platform_device *pdev) +{ + struct spi_master *master = platform_get_drvdata(pdev); + + struct sprd_spi_devdata *spi_chip = spi_master_get_devdata(master); + + if (spi_chip->is_active) { + + clk_prepare_enable(spi_chip->clk); + + sprd_spi_restore_config(spi_chip); + + clk_disable_unprepare(spi_chip->clk); + } + + return 0; +} + +#else +#define sprd_spi_suspend NULL +#define sprd_spi_resume NULL +#endif + +static const struct of_device_id sprd_spi_of_match[] = { + { .compatible = "sprd,sprd-spi", }, + { /* sentinel */ } +}; + +static struct platform_driver sprd_spi_driver = { + .driver = { + .name = "sprd spi", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(sprd_spi_of_match), + }, + .suspend = sprd_spi_suspend, + .resume = sprd_spi_resume, + .probe = sprd_spi_probe, + .remove = __exit_p(sprd_spi_remove), +}; + +static int __init sprd_spi_init(void) +{ + return platform_driver_register(&sprd_spi_driver); +} + +subsys_initcall(sprd_spi_init); + +static void __exit sprd_spi_exit(void) +{ + platform_driver_unregister(&sprd_spi_driver); +} + +module_exit(sprd_spi_exit); + +MODULE_DESCRIPTION("SpreadTrum SPI(r4p0 version) Controller driver"); +MODULE_AUTHOR("Jack.Jiang <Jack.Jiang@spreadtrum.com>"); +MODULE_LICENSE("GPL"); |
