cregit-Linux how code gets into the kernel

Release 4.14 drivers/spi/spi-bfin-sport.c

Directory: drivers/spi
/*
 * SPI bus via the Blackfin SPORT peripheral
 *
 * Enter bugs at http://blackfin.uclinux.org/
 *
 * Copyright 2009-2011 Analog Devices Inc.
 *
 * Licensed under the GPL-2 or later.
 */

#include <linux/module.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/irq.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/workqueue.h>

#include <asm/portmux.h>
#include <asm/bfin5xx_spi.h>
#include <asm/blackfin.h>
#include <asm/bfin_sport.h>
#include <asm/cacheflush.h>


#define DRV_NAME	"bfin-sport-spi"

#define DRV_DESC	"SPI bus via the Blackfin SPORT"

MODULE_AUTHOR("Cliff Cai");

MODULE_DESCRIPTION(DRV_DESC);
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:bfin-sport-spi");


enum bfin_sport_spi_state {
	
START_STATE,
	
RUNNING_STATE,
	
DONE_STATE,
	
ERROR_STATE,
};

struct bfin_sport_spi_master_data;


struct bfin_sport_transfer_ops {
	
void (*write) (struct bfin_sport_spi_master_data *);
	
void (*read) (struct bfin_sport_spi_master_data *);
	
void (*duplex) (struct bfin_sport_spi_master_data *);
};


struct bfin_sport_spi_master_data {
	/* Driver model hookup */
	
struct device *dev;

	/* SPI framework hookup */
	
struct spi_master *master;

	/* Regs base of SPI controller */
	
struct sport_register __iomem *regs;
	
int err_irq;

	/* Pin request list */
	
u16 *pin_req;

	
struct work_struct pump_messages;
	
spinlock_t lock;
	
struct list_head queue;
	
int busy;
	
bool run;

	/* Message Transfer pump */
	
struct tasklet_struct pump_transfers;

	/* Current message transfer state info */
	
enum bfin_sport_spi_state state;
	
struct spi_message *cur_msg;
	
struct spi_transfer *cur_transfer;
	
struct bfin_sport_spi_slave_data *cur_chip;
	union {
		
void *tx;
		
u8 *tx8;
		
u16 *tx16;
	};
	
void *tx_end;
	union {
		
void *rx;
		
u8 *rx8;
		
u16 *rx16;
	};
	
void *rx_end;

	
int cs_change;
	
struct bfin_sport_transfer_ops *ops;
};


struct bfin_sport_spi_slave_data {
	
u16 ctl_reg;
	
u16 baud;
	
u16 cs_chg_udelay;	/* Some devices require > 255usec delay */
	
u32 cs_gpio;
	
u16 idle_tx_val;
	
struct bfin_sport_transfer_ops *ops;
};


static void bfin_sport_spi_enable(struct bfin_sport_spi_master_data *drv_data) { bfin_write_or(&drv_data->regs->tcr1, TSPEN); bfin_write_or(&drv_data->regs->rcr1, TSPEN); SSYNC(); }

Contributors

PersonTokensPropCommitsCommitProp
Cliff Cai38100.00%1100.00%
Total38100.00%1100.00%


static void bfin_sport_spi_disable(struct bfin_sport_spi_master_data *drv_data) { bfin_write_and(&drv_data->regs->tcr1, ~TSPEN); bfin_write_and(&drv_data->regs->rcr1, ~TSPEN); SSYNC(); }

Contributors

PersonTokensPropCommitsCommitProp
Cliff Cai40100.00%1100.00%
Total40100.00%1100.00%

/* Caculate the SPI_BAUD register value based on input HZ */
static u16 bfin_sport_hz_to_spi_baud(u32 speed_hz) { u_long clk, sclk = get_sclk(); int div = (sclk / (2 * speed_hz)) - 1; if (div < 0) div = 0; clk = sclk / (2 * (div + 1)); if (clk > speed_hz) div++; return div; }

Contributors

PersonTokensPropCommitsCommitProp
Cliff Cai68100.00%1100.00%
Total68100.00%1100.00%

/* Chip select operation functions for cs_change flag */
static void bfin_sport_spi_cs_active(struct bfin_sport_spi_slave_data *chip) { gpio_direction_output(chip->cs_gpio, 0); }

Contributors

PersonTokensPropCommitsCommitProp
Cliff Cai20100.00%1100.00%
Total20100.00%1100.00%


static void bfin_sport_spi_cs_deactive(struct bfin_sport_spi_slave_data *chip) { gpio_direction_output(chip->cs_gpio, 1); /* Move delay here for consistency */ if (chip->cs_chg_udelay) udelay(chip->cs_chg_udelay); }

Contributors

PersonTokensPropCommitsCommitProp
Cliff Cai34100.00%1100.00%
Total34100.00%1100.00%


static void bfin_sport_spi_stat_poll_complete(struct bfin_sport_spi_master_data *drv_data) { unsigned long timeout = jiffies + HZ; while (!(bfin_read(&drv_data->regs->stat) & RXNE)) { if (!time_before(jiffies, timeout)) break; } }

Contributors

PersonTokensPropCommitsCommitProp
Cliff Cai49100.00%1100.00%
Total49100.00%1100.00%


static void bfin_sport_spi_u8_writer(struct bfin_sport_spi_master_data *drv_data) { u16 dummy; while (drv_data->tx < drv_data->tx_end) { bfin_write(&drv_data->regs->tx16, *drv_data->tx8++); bfin_sport_spi_stat_poll_complete(drv_data); dummy = bfin_read(&drv_data->regs->rx16); } }

Contributors

PersonTokensPropCommitsCommitProp
Cliff Cai59100.00%1100.00%
Total59100.00%1100.00%


static void bfin_sport_spi_u8_reader(struct bfin_sport_spi_master_data *drv_data) { u16 tx_val = drv_data->cur_chip->idle_tx_val; while (drv_data->rx < drv_data->rx_end) { bfin_write(&drv_data->regs->tx16, tx_val); bfin_sport_spi_stat_poll_complete(drv_data); *drv_data->rx8++ = bfin_read(&drv_data->regs->rx16); } }

Contributors

PersonTokensPropCommitsCommitProp
Cliff Cai65100.00%1100.00%
Total65100.00%1100.00%


static void bfin_sport_spi_u8_duplex(struct bfin_sport_spi_master_data *drv_data) { while (drv_data->rx < drv_data->rx_end) { bfin_write(&drv_data->regs->tx16, *drv_data->tx8++); bfin_sport_spi_stat_poll_complete(drv_data); *drv_data->rx8++ = bfin_read(&drv_data->regs->rx16); } }

Contributors

PersonTokensPropCommitsCommitProp
Cliff Cai60100.00%1100.00%
Total60100.00%1100.00%

static struct bfin_sport_transfer_ops bfin_sport_transfer_ops_u8 = { .write = bfin_sport_spi_u8_writer, .read = bfin_sport_spi_u8_reader, .duplex = bfin_sport_spi_u8_duplex, };
static void bfin_sport_spi_u16_writer(struct bfin_sport_spi_master_data *drv_data) { u16 dummy; while (drv_data->tx < drv_data->tx_end) { bfin_write(&drv_data->regs->tx16, *drv_data->tx16++); bfin_sport_spi_stat_poll_complete(drv_data); dummy = bfin_read(&drv_data->regs->rx16); } }

Contributors

PersonTokensPropCommitsCommitProp
Cliff Cai59100.00%1100.00%
Total59100.00%1100.00%


static void bfin_sport_spi_u16_reader(struct bfin_sport_spi_master_data *drv_data) { u16 tx_val = drv_data->cur_chip->idle_tx_val; while (drv_data->rx < drv_data->rx_end) { bfin_write(&drv_data->regs->tx16, tx_val); bfin_sport_spi_stat_poll_complete(drv_data); *drv_data->rx16++ = bfin_read(&drv_data->regs->rx16); } }

Contributors

PersonTokensPropCommitsCommitProp
Cliff Cai65100.00%1100.00%
Total65100.00%1100.00%


static void bfin_sport_spi_u16_duplex(struct bfin_sport_spi_master_data *drv_data) { while (drv_data->rx < drv_data->rx_end) { bfin_write(&drv_data->regs->tx16, *drv_data->tx16++); bfin_sport_spi_stat_poll_complete(drv_data); *drv_data->rx16++ = bfin_read(&drv_data->regs->rx16); } }

Contributors

PersonTokensPropCommitsCommitProp
Cliff Cai60100.00%1100.00%
Total60100.00%1100.00%

static struct bfin_sport_transfer_ops bfin_sport_transfer_ops_u16 = { .write = bfin_sport_spi_u16_writer, .read = bfin_sport_spi_u16_reader, .duplex = bfin_sport_spi_u16_duplex, }; /* stop controller and re-config current chip */
static void bfin_sport_spi_restore_state(struct bfin_sport_spi_master_data *drv_data) { struct bfin_sport_spi_slave_data *chip = drv_data->cur_chip; bfin_sport_spi_disable(drv_data); dev_dbg(drv_data->dev, "restoring spi ctl state\n"); bfin_write(&drv_data->regs->tcr1, chip->ctl_reg); bfin_write(&drv_data->regs->tclkdiv, chip->baud); SSYNC(); bfin_write(&drv_data->regs->rcr1, chip->ctl_reg & ~(ITCLK | ITFS)); SSYNC(); bfin_sport_spi_cs_active(chip); }

Contributors

PersonTokensPropCommitsCommitProp
Cliff Cai94100.00%1100.00%
Total94100.00%1100.00%

/* test if there is more transfer to be done */
static enum bfin_sport_spi_state bfin_sport_spi_next_transfer(struct bfin_sport_spi_master_data *drv_data) { struct spi_message *msg = drv_data->cur_msg; struct spi_transfer *trans = drv_data->cur_transfer; /* Move to next transfer */ if (trans->transfer_list.next != &msg->transfers) { drv_data->cur_transfer = list_entry(trans->transfer_list.next, struct spi_transfer, transfer_list); return RUNNING_STATE; } return DONE_STATE; }

Contributors

PersonTokensPropCommitsCommitProp
Cliff Cai70100.00%1100.00%
Total70100.00%1100.00%

/* * caller already set message->status; * dma and pio irqs are blocked give finished message back */
static void bfin_sport_spi_giveback(struct bfin_sport_spi_master_data *drv_data) { struct bfin_sport_spi_slave_data *chip = drv_data->cur_chip; unsigned long flags; struct spi_message *msg; spin_lock_irqsave(&drv_data->lock, flags); msg = drv_data->cur_msg; drv_data->state = START_STATE; drv_data->cur_msg = NULL; drv_data->cur_transfer = NULL; drv_data->cur_chip = NULL; schedule_work(&drv_data->pump_messages); spin_unlock_irqrestore(&drv_data->lock, flags); if (!drv_data->cs_change) bfin_sport_spi_cs_deactive(chip); if (msg->complete) msg->complete(msg->context); }

Contributors

PersonTokensPropCommitsCommitProp
Cliff Cai11399.12%150.00%
Bhaktipriya Shridhar10.88%150.00%
Total114100.00%2100.00%


static irqreturn_t sport_err_handler(int irq, void *dev_id) { struct bfin_sport_spi_master_data *drv_data = dev_id; u16 status; dev_dbg(drv_data->dev, "%s enter\n", __func__); status = bfin_read(&drv_data->regs->stat) & (TOVF | TUVF | ROVF | RUVF); if (status) { bfin_write(&drv_data->regs->stat, status); SSYNC(); bfin_sport_spi_disable(drv_data); dev_err(drv_data->dev, "status error:%s%s%s%s\n", status & TOVF ? " TOVF" : "", status & TUVF ? " TUVF" : "", status & ROVF ? " ROVF" : "", status & RUVF ? " RUVF" : ""); } return IRQ_HANDLED; }

Contributors

PersonTokensPropCommitsCommitProp
Cliff Cai126100.00%1100.00%
Total126100.00%1100.00%


static void bfin_sport_spi_pump_transfers(unsigned long data) { struct bfin_sport_spi_master_data *drv_data = (void *)data; struct spi_message *message = NULL; struct spi_transfer *transfer = NULL; struct spi_transfer *previous = NULL; struct bfin_sport_spi_slave_data *chip = NULL; unsigned int bits_per_word; u32 tranf_success = 1; u32 transfer_speed; u8 full_duplex = 0; /* Get current state information */ message = drv_data->cur_msg; transfer = drv_data->cur_transfer; chip = drv_data->cur_chip; transfer_speed = bfin_sport_hz_to_spi_baud(transfer->speed_hz); bfin_write(&drv_data->regs->tclkdiv, transfer_speed); SSYNC(); /* * if msg is error or done, report it back using complete() callback */ /* Handle for abort */ if (drv_data->state == ERROR_STATE) { dev_dbg(drv_data->dev, "transfer: we've hit an error\n"); message->status = -EIO; bfin_sport_spi_giveback(drv_data); return; } /* Handle end of message */ if (drv_data->state == DONE_STATE) { dev_dbg(drv_data->dev, "transfer: all done!\n"); message->status = 0; bfin_sport_spi_giveback(drv_data); return; } /* Delay if requested at end of transfer */ if (drv_data->state == RUNNING_STATE) { dev_dbg(drv_data->dev, "transfer: still running ...\n"); previous = list_entry(transfer->transfer_list.prev, struct spi_transfer, transfer_list); if (previous->delay_usecs) udelay(previous->delay_usecs); } if (transfer->len == 0) { /* Move to next transfer of this msg */ drv_data->state = bfin_sport_spi_next_transfer(drv_data); /* Schedule next transfer tasklet */ tasklet_schedule(&drv_data->pump_transfers); } if (transfer->tx_buf != NULL) { drv_data->tx = (void *)transfer->tx_buf; drv_data->tx_end = drv_data->tx + transfer->len; dev_dbg(drv_data->dev, "tx_buf is %p, tx_end is %p\n", transfer->tx_buf, drv_data->tx_end); } else drv_data->tx = NULL; if (transfer->rx_buf != NULL) { full_duplex = transfer->tx_buf != NULL; drv_data->rx = transfer->rx_buf; drv_data->rx_end = drv_data->rx + transfer->len; dev_dbg(drv_data->dev, "rx_buf is %p, rx_end is %p\n", transfer->rx_buf, drv_data->rx_end); } else drv_data->rx = NULL; drv_data->cs_change = transfer->cs_change; /* Bits per word setup */ bits_per_word = transfer->bits_per_word; if (bits_per_word == 16) drv_data->ops = &bfin_sport_transfer_ops_u16; else drv_data->ops = &bfin_sport_transfer_ops_u8; bfin_write(&drv_data->regs->tcr2, bits_per_word - 1); bfin_write(&drv_data->regs->tfsdiv, bits_per_word - 1); bfin_write(&drv_data->regs->rcr2, bits_per_word - 1); drv_data->state = RUNNING_STATE; if (drv_data->cs_change) bfin_sport_spi_cs_active(chip); dev_dbg(drv_data->dev, "now pumping a transfer: width is %d, len is %d\n", bits_per_word, transfer->len); /* PIO mode write then read */ dev_dbg(drv_data->dev, "doing IO transfer\n"); bfin_sport_spi_enable(drv_data); if (full_duplex) { /* full duplex mode */ BUG_ON((drv_data->tx_end - drv_data->tx) != (drv_data->rx_end - drv_data->rx)); drv_data->ops->duplex(drv_data); if (drv_data->tx != drv_data->tx_end) tranf_success = 0; } else if (drv_data->tx != NULL) { /* write only half duplex */ drv_data->ops->write(drv_data); if (drv_data->tx != drv_data->tx_end) tranf_success = 0; } else if (drv_data->rx != NULL) { /* read only half duplex */ drv_data->ops->read(drv_data); if (drv_data->rx != drv_data->rx_end) tranf_success = 0; } bfin_sport_spi_disable(drv_data); if (!tranf_success) { dev_dbg(drv_data->dev, "IO write error!\n"); drv_data->state = ERROR_STATE; } else { /* Update total byte transferred */ message->actual_length += transfer->len; /* Move to next transfer of this msg */ drv_data->state = bfin_sport_spi_next_transfer(drv_data); if (drv_data->cs_change) bfin_sport_spi_cs_deactive(chip); } /* Schedule next transfer tasklet */ tasklet_schedule(&drv_data->pump_transfers); }

Contributors

PersonTokensPropCommitsCommitProp
Cliff Cai64493.33%120.00%
Scott Jiang446.38%240.00%
Masanari Iida10.14%120.00%
Stephen Warren10.14%120.00%
Total690100.00%5100.00%

/* pop a msg from queue and kick off real transfer */
static void bfin_sport_spi_pump_messages(struct work_struct *work) { struct bfin_sport_spi_master_data *drv_data; unsigned long flags; struct spi_message *next_msg; drv_data = container_of(work, struct bfin_sport_spi_master_data, pump_messages); /* Lock queue and check for queue work */ spin_lock_irqsave(&drv_data->lock, flags); if (list_empty(&drv_data->queue) || !drv_data->run) { /* pumper kicked off but no work to do */ drv_data->busy = 0; spin_unlock_irqrestore(&drv_data->lock, flags); return; } /* Make sure we are not already running a message */ if (drv_data->cur_msg) { spin_unlock_irqrestore(&drv_data->lock, flags); return; } /* Extract head of queue */ next_msg = list_entry(drv_data->queue.next, struct spi_message, queue); drv_data->cur_msg = next_msg; /* Setup the SSP using the per chip configuration */ drv_data->cur_chip = spi_get_ctldata(drv_data->cur_msg->spi); list_del_init(&drv_data->cur_msg->queue); /* Initialize message state */ drv_data->cur_msg->state = START_STATE; drv_data->cur_transfer = list_entry(drv_data->cur_msg->transfers.next, struct spi_transfer, transfer_list); bfin_sport_spi_restore_state(drv_data); dev_dbg(drv_data->dev, "got a message to pump, " "state is set to: baud %d, cs_gpio %i, ctl 0x%x\n", drv_data->cur_chip->baud, drv_data->cur_chip->cs_gpio, drv_data->cur_chip->ctl_reg); dev_dbg(drv_data->dev, "the first transfer len is %d\n", drv_data->cur_transfer->len); /* Mark as busy and launch transfers */ tasklet_schedule(&drv_data->pump_transfers); drv_data->busy = 1; spin_unlock_irqrestore(&drv_data->lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
Cliff Cai252100.00%1100.00%
Total252100.00%1100.00%

/* * got a msg to transfer, queue it in drv_data->queue. * And kick off message pumper */
static int bfin_sport_spi_transfer(struct spi_device *spi, struct spi_message *msg) { struct bfin_sport_spi_master_data *drv_data = spi_master_get_devdata(spi->master); unsigned long flags; spin_lock_irqsave(&drv_data->lock, flags); if (!drv_data->run) { spin_unlock_irqrestore(&drv_data->lock, flags); return -ESHUTDOWN; } msg->actual_length = 0; msg->status = -EINPROGRESS; msg->state = START_STATE; dev_dbg(&spi->dev, "adding an msg in transfer()\n"); list_add_tail(&msg->queue, &drv_data->queue); if (drv_data->run && !drv_data->busy) schedule_work(&drv_data->pump_messages); spin_unlock_irqrestore(&drv_data->lock, flags); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Cliff Cai13899.28%150.00%
Bhaktipriya Shridhar10.72%150.00%
Total139100.00%2100.00%

/* Called every time common spi devices change state */
static int bfin_sport_spi_setup(struct spi_device *spi) { struct bfin_sport_spi_slave_data *chip, *first = NULL; int ret; /* Only alloc (or use chip_info) on first setup */ chip = spi_get_ctldata(spi); if (chip == NULL) { struct bfin5xx_spi_chip *chip_info; chip = first = kzalloc(sizeof(*chip), GFP_KERNEL); if (!chip) return -ENOMEM; /* platform chip_info isn't required */ chip_info = spi->controller_data; if (chip_info) { /* * DITFS and TDTYPE are only thing we don't set, but * they probably shouldn't be changed by people. */ if (chip_info->ctl_reg || chip_info->enable_dma) { ret = -EINVAL; dev_err(&spi->dev, "don't set ctl_reg/enable_dma fields\n"); goto error; } chip->cs_chg_udelay = chip_info->cs_chg_udelay; chip->idle_tx_val = chip_info->idle_tx_val; } } /* translate common spi framework into our register * following configure contents are same for tx and rx. */ if (spi->mode & SPI_CPHA) chip->ctl_reg &= ~TCKFE; else chip->ctl_reg |= TCKFE; if (spi->mode & SPI_LSB_FIRST) chip->ctl_reg |= TLSBIT; else chip->ctl_reg &= ~TLSBIT; /* Sport in master mode */ chip->ctl_reg |= ITCLK | ITFS | TFSR | LATFS | LTFS; chip->baud = bfin_sport_hz_to_spi_baud(spi->max_speed_hz); chip->cs_gpio = spi->chip_select; ret = gpio_request(chip->cs_gpio, spi->modalias); if (ret) goto error; dev_dbg(&spi->dev, "setup spi chip %s, width is %d\n", spi->modalias, spi->bits_per_word); dev_dbg(&spi->dev, "ctl_reg is 0x%x, GPIO is %i\n", chip->ctl_reg, spi->chip_select); spi_set_ctldata(spi, chip); bfin_sport_spi_cs_deactive(chip); return ret; error: kfree(first); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Cliff Cai28899.65%150.00%
Jarkko Nikula10.35%150.00%
Total289100.00%2100.00%

/* * callback for spi framework. * clean driver specific data */
static void bfin_sport_spi_cleanup(struct spi_device *spi) { struct bfin_sport_spi_slave_data *chip = spi_get_ctldata(spi); if (!chip) return; gpio_free(chip->cs_gpio); kfree(chip); }

Contributors

PersonTokensPropCommitsCommitProp
Cliff Cai39100.00%1100.00%
Total39100.00%1100.00%


static int bfin_sport_spi_init_queue(struct bfin_sport_spi_master_data *drv_data) { INIT_LIST_HEAD(&drv_data->queue); spin_lock_init(&drv_data->lock); drv_data->run = false; drv_data->busy = 0; /* init transfer tasklet */ tasklet_init(&drv_data->pump_transfers, bfin_sport_spi_pump_transfers, (unsigned long)drv_data); INIT_WORK(&drv_data->pump_messages, bfin_sport_spi_pump_messages); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Cliff Cai69100.00%1100.00%
Total69100.00%1100.00%


static int bfin_sport_spi_start_queue(struct bfin_sport_spi_master_data *drv_data) { unsigned long flags; spin_lock_irqsave(&drv_data->lock, flags); if (drv_data->run || drv_data->busy) { spin_unlock_irqrestore(&drv_data->lock, flags); return -EBUSY; } drv_data->run = true; drv_data->cur_msg = NULL; drv_data->cur_transfer = NULL; drv_data->cur_chip = NULL; spin_unlock_irqrestore(&drv_data->lock, flags); schedule_work(&drv_data->pump_messages); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Cliff Cai9598.96%150.00%
Bhaktipriya Shridhar11.04%150.00%
Total96100.00%2100.00%


static inline int bfin_sport_spi_stop_queue(struct bfin_sport_spi_master_data *drv_data) { unsigned long flags; unsigned limit = 500; int status = 0; spin_lock_irqsave(&drv_data->lock, flags); /* * This is a bit lame, but is optimized for the common execution path. * A wait_queue on the drv_data->busy could be used, but then the common * execution path (pump_messages) would be required to call wake_up or * friends on every SPI message. Do this instead */ drv_data->run = false; while (!list_empty(&drv_data->queue) && drv_data->busy && limit--) { spin_unlock_irqrestore(&drv_data->lock, flags); msleep(10); spin_lock_irqsave(&drv_data->lock, flags); } if (!list_empty(&drv_data->queue) || drv_data->busy) status = -EBUSY; spin_unlock_irqrestore(&drv_data->lock, flags); return status; }

Contributors

PersonTokensPropCommitsCommitProp
Cliff Cai121100.00%1100.00%
Total121100.00%1100.00%


static inline int bfin_sport_spi_destroy_queue(struct bfin_sport_spi_master_data *drv_data) { int status; status = bfin_sport_spi_stop_queue(drv_data); if (status) return status; flush_work(&drv_data->pump_messages); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Cliff Cai3792.50%150.00%
Bhaktipriya Shridhar37.50%150.00%
Total40100.00%2100.00%


static int bfin_sport_spi_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct bfin5xx_spi_master *platform_info; struct spi_master *master; struct resource *res, *ires; struct bfin_sport_spi_master_data *drv_data; int status; platform_info = dev_get_platdata(dev); /* Allocate master with space for drv_data */ master = spi_alloc_master(dev, sizeof(*master) + 16); if (!master) { dev_err(dev, "cannot alloc spi_master\n"); return -ENOMEM; } drv_data = spi_master_get_devdata(master); drv_data->master = master; drv_data->dev = dev; drv_data->pin_req = platform_info->pin_req; master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST; master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16); master->bus_num = pdev->id; master->num_chipselect = platform_info->num_chipselect; master->cleanup = bfin_sport_spi_cleanup; master->setup = bfin_sport_spi_setup; master->transfer = bfin_sport_spi_transfer; /* Find and map our resources */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res == NULL) { dev_err(dev, "cannot get IORESOURCE_MEM\n"); status = -ENOENT; goto out_error_get_res; } drv_data->regs = ioremap(res->start, resource_size(res)); if (drv_data->regs == NULL) { dev_err(dev, "cannot map registers\n"); status = -ENXIO; goto out_error_ioremap; } ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!ires) { dev_err(dev, "cannot get IORESOURCE_IRQ\n"); status = -ENODEV; goto out_error_get_ires; } drv_data->err_irq = ires->start; /* Initial and start queue */ status = bfin_sport_spi_init_queue(drv_data); if (status) { dev_err(dev, "problem initializing queue\n"); goto out_error_queue_alloc; } status = bfin_sport_spi_start_queue(drv_data); if (status) { dev_err(dev, "problem starting queue\n"); goto out_error_queue_alloc; } status = request_irq(drv_data->err_irq, sport_err_handler, 0, "sport_spi_err", drv_data); if (status) { dev_err(dev, "unable to request sport err irq\n"); goto out_error_irq; } status = peripheral_request_list(drv_data->pin_req, DRV_NAME); if (status) { dev_err(dev, "requesting peripherals failed\n"); goto out_error_peripheral; } /* Register with the SPI framework */ platform_set_drvdata(pdev, drv_data); status = spi_register_master(master); if (status) { dev_err(dev, "problem registering spi master\n"); goto out_error_master; } dev_info(dev, "%s, regs_base@%p\n", DRV_DESC, drv_data->regs); return 0; out_error_master: peripheral_free_list(drv_data->pin_req); out_error_peripheral: free_irq(drv_data->err_irq, drv_data); out_error_irq: out_error_queue_alloc: bfin_sport_spi_destroy_queue(drv_data); out_error_get_ires: iounmap(drv_data->regs); out_error_ioremap: out_error_get_res: spi_master_put(master); return status; }

Contributors

PersonTokensPropCommitsCommitProp
Cliff Cai47796.56%133.33%
Stephen Warren142.83%133.33%
Jingoo Han30.61%133.33%
Total494100.00%3100.00%

/* stop hardware and remove the driver */
static int bfin_sport_spi_remove(struct platform_device *pdev) { struct bfin_sport_spi_master_data *drv_data = platform_get_drvdata(pdev); int status = 0; if (!drv_data) return 0; /* Remove the queue */ status = bfin_sport_spi_destroy_queue(drv_data); if (status) return status; /* Disable the SSP at the peripheral and SOC level */ bfin_sport_spi_disable(drv_data); /* Disconnect from the SPI framework */ spi_unregister_master(drv_data->master); peripheral_free_list(drv_data->pin_req); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Cliff Cai73100.00%1100.00%
Total73100.00%1100.00%

#ifdef CONFIG_PM_SLEEP
static int bfin_sport_spi_suspend(struct device *dev) { struct bfin_sport_spi_master_data *drv_data = dev_get_drvdata(dev); int status; status = bfin_sport_spi_stop_queue(drv_data); if (status) return status; /* stop hardware */ bfin_sport_spi_disable(drv_data); return status; }

Contributors

PersonTokensPropCommitsCommitProp
Cliff Cai4391.49%150.00%
Jingoo Han48.51%150.00%
Total47100.00%2100.00%


static int bfin_sport_spi_resume(struct device *dev) { struct bfin_sport_spi_master_data *drv_data = dev_get_drvdata(dev); int status; /* Enable the SPI interface */ bfin_sport_spi_enable(drv_data); /* Start the queue running */ status = bfin_sport_spi_start_queue(drv_data); if (status) dev_err(drv_data->dev, "problem resuming queue\n"); return status; }

Contributors

PersonTokensPropCommitsCommitProp
Cliff Cai5092.59%150.00%
Jingoo Han47.41%150.00%
Total54100.00%2100.00%

static SIMPLE_DEV_PM_OPS(bfin_sport_spi_pm_ops, bfin_sport_spi_suspend, bfin_sport_spi_resume); #define BFIN_SPORT_SPI_PM_OPS (&bfin_sport_spi_pm_ops) #else #define BFIN_SPORT_SPI_PM_OPS NULL #endif static struct platform_driver bfin_sport_spi_driver = { .driver = { .name = DRV_NAME, .pm = BFIN_SPORT_SPI_PM_OPS, }, .probe = bfin_sport_spi_probe, .remove = bfin_sport_spi_remove, }; module_platform_driver(bfin_sport_spi_driver);

Overall Contributors

PersonTokensPropCommitsCommitProp
Cliff Cai372397.44%110.00%
Scott Jiang441.15%220.00%
Jingoo Han300.79%220.00%
Stephen Warren150.39%110.00%
Bhaktipriya Shridhar60.16%110.00%
Jarkko Nikula10.03%110.00%
Masanari Iida10.03%110.00%
Grant C. Likely10.03%110.00%
Total3821100.00%10100.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.