cregit-Linux how code gets into the kernel

Release 4.14 drivers/spi/spi-sh.c

Directory: drivers/spi
/*
 * SH SPI bus driver
 *
 * Copyright (C) 2011  Renesas Solutions Corp.
 *
 * Based on pxa2xx_spi.c:
 * Copyright (C) 2005 Stephen Street / StreetFire Sound Labs
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/timer.h>
#include <linux/delay.h>
#include <linux/list.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/spi/spi.h>


#define SPI_SH_TBR		0x00

#define SPI_SH_RBR		0x00

#define SPI_SH_CR1		0x08

#define SPI_SH_CR2		0x10

#define SPI_SH_CR3		0x18

#define SPI_SH_CR4		0x20

#define SPI_SH_CR5		0x28

/* CR1 */

#define SPI_SH_TBE		0x80

#define SPI_SH_TBF		0x40

#define SPI_SH_RBE		0x20

#define SPI_SH_RBF		0x10

#define SPI_SH_PFONRD		0x08

#define SPI_SH_SSDB		0x04

#define SPI_SH_SSD		0x02

#define SPI_SH_SSA		0x01

/* CR2 */

#define SPI_SH_RSTF		0x80

#define SPI_SH_LOOPBK		0x40

#define SPI_SH_CPOL		0x20

#define SPI_SH_CPHA		0x10

#define SPI_SH_L1M0		0x08

/* CR3 */

#define SPI_SH_MAX_BYTE		0xFF

/* CR4 */

#define SPI_SH_TBEI		0x80

#define SPI_SH_TBFI		0x40

#define SPI_SH_RBEI		0x20

#define SPI_SH_RBFI		0x10

#define SPI_SH_WPABRT		0x04

#define SPI_SH_SSS		0x01

/* CR8 */

#define SPI_SH_P1L0		0x80

#define SPI_SH_PP1L0		0x40

#define SPI_SH_MUXI		0x20

#define SPI_SH_MUXIRQ		0x10


#define SPI_SH_FIFO_SIZE	32

#define SPI_SH_SEND_TIMEOUT	(3 * HZ)

#define SPI_SH_RECEIVE_TIMEOUT	(HZ >> 3)


#undef DEBUG


struct spi_sh_data {
	
void __iomem *addr;
	
int irq;
	
struct spi_master *master;
	
struct list_head queue;
	
struct work_struct ws;
	
unsigned long cr1;
	
wait_queue_head_t wait;
	
spinlock_t lock;
	
int width;
};


static void spi_sh_write(struct spi_sh_data *ss, unsigned long data, unsigned long offset) { if (ss->width == 8) iowrite8(data, ss->addr + (offset >> 2)); else if (ss->width == 32) iowrite32(data, ss->addr + offset); }

Contributors

PersonTokensPropCommitsCommitProp
Yoshihiro Shimoda62100.00%2100.00%
Total62100.00%2100.00%


static unsigned long spi_sh_read(struct spi_sh_data *ss, unsigned long offset) { if (ss->width == 8) return ioread8(ss->addr + (offset >> 2)); else if (ss->width == 32) return ioread32(ss->addr + offset); else return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Yoshihiro Shimoda61100.00%2100.00%
Total61100.00%2100.00%


static void spi_sh_set_bit(struct spi_sh_data *ss, unsigned long val, unsigned long offset) { unsigned long tmp; tmp = spi_sh_read(ss, offset); tmp |= val; spi_sh_write(ss, tmp, offset); }

Contributors

PersonTokensPropCommitsCommitProp
Yoshihiro Shimoda45100.00%1100.00%
Total45100.00%1100.00%


static void spi_sh_clear_bit(struct spi_sh_data *ss, unsigned long val, unsigned long offset) { unsigned long tmp; tmp = spi_sh_read(ss, offset); tmp &= ~val; spi_sh_write(ss, tmp, offset); }

Contributors

PersonTokensPropCommitsCommitProp
Yoshihiro Shimoda46100.00%1100.00%
Total46100.00%1100.00%


static void clear_fifo(struct spi_sh_data *ss) { spi_sh_set_bit(ss, SPI_SH_RSTF, SPI_SH_CR2); spi_sh_clear_bit(ss, SPI_SH_RSTF, SPI_SH_CR2); }

Contributors

PersonTokensPropCommitsCommitProp
Yoshihiro Shimoda29100.00%1100.00%
Total29100.00%1100.00%


static int spi_sh_wait_receive_buffer(struct spi_sh_data *ss) { int timeout = 100000; while (spi_sh_read(ss, SPI_SH_CR1) & SPI_SH_RBE) { udelay(10); if (timeout-- < 0) return -ETIMEDOUT; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Yoshihiro Shimoda48100.00%1100.00%
Total48100.00%1100.00%


static int spi_sh_wait_write_buffer_empty(struct spi_sh_data *ss) { int timeout = 100000; while (!(spi_sh_read(ss, SPI_SH_CR1) & SPI_SH_TBE)) { udelay(10); if (timeout-- < 0) return -ETIMEDOUT; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Yoshihiro Shimoda51100.00%1100.00%
Total51100.00%1100.00%


static int spi_sh_send(struct spi_sh_data *ss, struct spi_message *mesg, struct spi_transfer *t) { int i, retval = 0; int remain = t->len; int cur_len; unsigned char *data; long ret; if (t->len) spi_sh_set_bit(ss, SPI_SH_SSA, SPI_SH_CR1); data = (unsigned char *)t->tx_buf; while (remain > 0) { cur_len = min(SPI_SH_FIFO_SIZE, remain); for (i = 0; i < cur_len && !(spi_sh_read(ss, SPI_SH_CR4) & SPI_SH_WPABRT) && !(spi_sh_read(ss, SPI_SH_CR1) & SPI_SH_TBF); i++) spi_sh_write(ss, (unsigned long)data[i], SPI_SH_TBR); if (spi_sh_read(ss, SPI_SH_CR4) & SPI_SH_WPABRT) { /* Abort SPI operation */ spi_sh_set_bit(ss, SPI_SH_WPABRT, SPI_SH_CR4); retval = -EIO; break; } cur_len = i; remain -= cur_len; data += cur_len; if (remain > 0) { ss->cr1 &= ~SPI_SH_TBE; spi_sh_set_bit(ss, SPI_SH_TBE, SPI_SH_CR4); ret = wait_event_interruptible_timeout(ss->wait, ss->cr1 & SPI_SH_TBE, SPI_SH_SEND_TIMEOUT); if (ret == 0 && !(ss->cr1 & SPI_SH_TBE)) { printk(KERN_ERR "%s: timeout\n", __func__); return -ETIMEDOUT; } } } if (list_is_last(&t->transfer_list, &mesg->transfers)) { spi_sh_clear_bit(ss, SPI_SH_SSD | SPI_SH_SSDB, SPI_SH_CR1); spi_sh_set_bit(ss, SPI_SH_SSA, SPI_SH_CR1); ss->cr1 &= ~SPI_SH_TBE; spi_sh_set_bit(ss, SPI_SH_TBE, SPI_SH_CR4); ret = wait_event_interruptible_timeout(ss->wait, ss->cr1 & SPI_SH_TBE, SPI_SH_SEND_TIMEOUT); if (ret == 0 && (ss->cr1 & SPI_SH_TBE)) { printk(KERN_ERR "%s: timeout\n", __func__); return -ETIMEDOUT; } } return retval; }

Contributors

PersonTokensPropCommitsCommitProp
Yoshihiro Shimoda35399.72%150.00%
Axel Lin10.28%150.00%
Total354100.00%2100.00%


static int spi_sh_receive(struct spi_sh_data *ss, struct spi_message *mesg, struct spi_transfer *t) { int i; int remain = t->len; int cur_len; unsigned char *data; long ret; if (t->len > SPI_SH_MAX_BYTE) spi_sh_write(ss, SPI_SH_MAX_BYTE, SPI_SH_CR3); else spi_sh_write(ss, t->len, SPI_SH_CR3); spi_sh_clear_bit(ss, SPI_SH_SSD | SPI_SH_SSDB, SPI_SH_CR1); spi_sh_set_bit(ss, SPI_SH_SSA, SPI_SH_CR1); spi_sh_wait_write_buffer_empty(ss); data = (unsigned char *)t->rx_buf; while (remain > 0) { if (remain >= SPI_SH_FIFO_SIZE) { ss->cr1 &= ~SPI_SH_RBF; spi_sh_set_bit(ss, SPI_SH_RBF, SPI_SH_CR4); ret = wait_event_interruptible_timeout(ss->wait, ss->cr1 & SPI_SH_RBF, SPI_SH_RECEIVE_TIMEOUT); if (ret == 0 && spi_sh_read(ss, SPI_SH_CR1) & SPI_SH_RBE) { printk(KERN_ERR "%s: timeout\n", __func__); return -ETIMEDOUT; } } cur_len = min(SPI_SH_FIFO_SIZE, remain); for (i = 0; i < cur_len; i++) { if (spi_sh_wait_receive_buffer(ss)) break; data[i] = (unsigned char)spi_sh_read(ss, SPI_SH_RBR); } remain -= cur_len; data += cur_len; } /* deassert CS when SPI is receiving. */ if (t->len > SPI_SH_MAX_BYTE) { clear_fifo(ss); spi_sh_write(ss, 1, SPI_SH_CR3); } else { spi_sh_write(ss, 0, SPI_SH_CR3); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Yoshihiro Shimoda28099.64%150.00%
Axel Lin10.36%150.00%
Total281100.00%2100.00%


static void spi_sh_work(struct work_struct *work) { struct spi_sh_data *ss = container_of(work, struct spi_sh_data, ws); struct spi_message *mesg; struct spi_transfer *t; unsigned long flags; int ret; pr_debug("%s: enter\n", __func__); spin_lock_irqsave(&ss->lock, flags); while (!list_empty(&ss->queue)) { mesg = list_entry(ss->queue.next, struct spi_message, queue); list_del_init(&mesg->queue); spin_unlock_irqrestore(&ss->lock, flags); list_for_each_entry(t, &mesg->transfers, transfer_list) { pr_debug("tx_buf = %p, rx_buf = %p\n", t->tx_buf, t->rx_buf); pr_debug("len = %d, delay_usecs = %d\n", t->len, t->delay_usecs); if (t->tx_buf) { ret = spi_sh_send(ss, mesg, t); if (ret < 0) goto error; } if (t->rx_buf) { ret = spi_sh_receive(ss, mesg, t); if (ret < 0) goto error; } mesg->actual_length += t->len; } spin_lock_irqsave(&ss->lock, flags); mesg->status = 0; if (mesg->complete) mesg->complete(mesg->context); } clear_fifo(ss); spi_sh_set_bit(ss, SPI_SH_SSD, SPI_SH_CR1); udelay(100); spi_sh_clear_bit(ss, SPI_SH_SSA | SPI_SH_SSDB | SPI_SH_SSD, SPI_SH_CR1); clear_fifo(ss); spin_unlock_irqrestore(&ss->lock, flags); return; error: mesg->status = ret; if (mesg->complete) mesg->complete(mesg->context); spi_sh_clear_bit(ss, SPI_SH_SSA | SPI_SH_SSDB | SPI_SH_SSD, SPI_SH_CR1); clear_fifo(ss); }

Contributors

PersonTokensPropCommitsCommitProp
Yoshihiro Shimoda31596.33%150.00%
Axel Lin123.67%150.00%
Total327100.00%2100.00%


static int spi_sh_setup(struct spi_device *spi) { struct spi_sh_data *ss = spi_master_get_devdata(spi->master); pr_debug("%s: enter\n", __func__); spi_sh_write(ss, 0xfe, SPI_SH_CR1); /* SPI sycle stop */ spi_sh_write(ss, 0x00, SPI_SH_CR1); /* CR1 init */ spi_sh_write(ss, 0x00, SPI_SH_CR3); /* CR3 init */ clear_fifo(ss); /* 1/8 clock */ spi_sh_write(ss, spi_sh_read(ss, SPI_SH_CR2) | 0x07, SPI_SH_CR2); udelay(10); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Yoshihiro Shimoda90100.00%1100.00%
Total90100.00%1100.00%


static int spi_sh_transfer(struct spi_device *spi, struct spi_message *mesg) { struct spi_sh_data *ss = spi_master_get_devdata(spi->master); unsigned long flags; pr_debug("%s: enter\n", __func__); pr_debug("\tmode = %02x\n", spi->mode); spin_lock_irqsave(&ss->lock, flags); mesg->actual_length = 0; mesg->status = -EINPROGRESS; spi_sh_clear_bit(ss, SPI_SH_SSA, SPI_SH_CR1); list_add_tail(&mesg->queue, &ss->queue); schedule_work(&ss->ws); spin_unlock_irqrestore(&ss->lock, flags); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Yoshihiro Shimoda11399.12%150.00%
Bhaktipriya Shridhar10.88%150.00%
Total114100.00%2100.00%


static void spi_sh_cleanup(struct spi_device *spi) { struct spi_sh_data *ss = spi_master_get_devdata(spi->master); pr_debug("%s: enter\n", __func__); spi_sh_clear_bit(ss, SPI_SH_SSA | SPI_SH_SSDB | SPI_SH_SSD, SPI_SH_CR1); }

Contributors

PersonTokensPropCommitsCommitProp
Yoshihiro Shimoda43100.00%1100.00%
Total43100.00%1100.00%


static irqreturn_t spi_sh_irq(int irq, void *_ss) { struct spi_sh_data *ss = (struct spi_sh_data *)_ss; unsigned long cr1; cr1 = spi_sh_read(ss, SPI_SH_CR1); if (cr1 & SPI_SH_TBE) ss->cr1 |= SPI_SH_TBE; if (cr1 & SPI_SH_TBF) ss->cr1 |= SPI_SH_TBF; if (cr1 & SPI_SH_RBE) ss->cr1 |= SPI_SH_RBE; if (cr1 & SPI_SH_RBF) ss->cr1 |= SPI_SH_RBF; if (ss->cr1) { spi_sh_clear_bit(ss, ss->cr1, SPI_SH_CR4); wake_up(&ss->wait); } return IRQ_HANDLED; }

Contributors

PersonTokensPropCommitsCommitProp
Yoshihiro Shimoda116100.00%1100.00%
Total116100.00%1100.00%


static int spi_sh_remove(struct platform_device *pdev) { struct spi_sh_data *ss = platform_get_drvdata(pdev); spi_unregister_master(ss->master); flush_work(&ss->ws); free_irq(ss->irq, ss); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Yoshihiro Shimoda3777.08%125.00%
Axel Lin714.58%125.00%
Bhaktipriya Shridhar36.25%125.00%
Jingoo Han12.08%125.00%
Total48100.00%4100.00%


static int spi_sh_probe(struct platform_device *pdev) { struct resource *res; struct spi_master *master; struct spi_sh_data *ss; int ret, irq; /* get base addr */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (unlikely(res == NULL)) { dev_err(&pdev->dev, "invalid resource\n"); return -EINVAL; } irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(&pdev->dev, "platform_get_irq error: %d\n", irq); return irq; } master = spi_alloc_master(&pdev->dev, sizeof(struct spi_sh_data)); if (master == NULL) { dev_err(&pdev->dev, "spi_alloc_master error.\n"); return -ENOMEM; } ss = spi_master_get_devdata(master); platform_set_drvdata(pdev, ss); switch (res->flags & IORESOURCE_MEM_TYPE_MASK) { case IORESOURCE_MEM_8BIT: ss->width = 8; break; case IORESOURCE_MEM_32BIT: ss->width = 32; break; default: dev_err(&pdev->dev, "No support width\n"); ret = -ENODEV; goto error1; } ss->irq = irq; ss->master = master; ss->addr = devm_ioremap(&pdev->dev, res->start, resource_size(res)); if (ss->addr == NULL) { dev_err(&pdev->dev, "ioremap error.\n"); ret = -ENOMEM; goto error1; } INIT_LIST_HEAD(&ss->queue); spin_lock_init(&ss->lock); INIT_WORK(&ss->ws, spi_sh_work); init_waitqueue_head(&ss->wait); ret = request_irq(irq, spi_sh_irq, 0, "spi_sh", ss); if (ret < 0) { dev_err(&pdev->dev, "request_irq error\n"); goto error1; } master->num_chipselect = 2; master->bus_num = pdev->id; master->setup = spi_sh_setup; master->transfer = spi_sh_transfer; master->cleanup = spi_sh_cleanup; ret = spi_register_master(master); if (ret < 0) { printk(KERN_ERR "spi_register_master error.\n"); goto error3; } return 0; error3: free_irq(irq, ss); error1: spi_master_put(master); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Yoshihiro Shimoda39596.34%228.57%
Himangi Saraogi81.95%114.29%
Gustavo A. R. Silva40.98%114.29%
Jingoo Han10.24%114.29%
Bhaktipriya Shridhar10.24%114.29%
Yong Zhang10.24%114.29%
Total410100.00%7100.00%

static struct platform_driver spi_sh_driver = { .probe = spi_sh_probe, .remove = spi_sh_remove, .driver = { .name = "sh_spi", }, }; module_platform_driver(spi_sh_driver); MODULE_DESCRIPTION("SH SPI bus driver"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Yoshihiro Shimoda"); MODULE_ALIAS("platform:sh_spi");

Overall Contributors

PersonTokensPropCommitsCommitProp
Yoshihiro Shimoda235498.21%216.67%
Axel Lin210.88%325.00%
Himangi Saraogi80.33%18.33%
Bhaktipriya Shridhar50.21%18.33%
Gustavo A. R. Silva40.17%18.33%
Jingoo Han20.08%18.33%
Jarkko Nikula10.04%18.33%
Grant C. Likely10.04%18.33%
Yong Zhang10.04%18.33%
Total2397100.00%12100.00%
Directory: drivers/spi
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.