Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8fb3b066 authored by Mark Brown's avatar Mark Brown
Browse files

Merge remote-tracking branches 'spi/topic/dw', 'spi/topic/fsl',...

Merge remote-tracking branches 'spi/topic/dw', 'spi/topic/fsl', 'spi/topic/fsl-espi' and 'spi/topic/id-const' into spi-next
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -42,6 +42,10 @@ Required properties:
- interrupts : should contain eSPI interrupt, the device has one interrupt.
- fsl,espi-num-chipselects : the number of the chipselect signals.

Optional properties:
- fsl,csbef: chip select assertion time in bits before frame starts
- fsl,csaft: chip select negation time in bits after frame ends

Example:
	spi@110000 {
		#address-cells = <1>;
@@ -51,4 +55,6 @@ Example:
		interrupts = <53 0x2>;
		interrupt-parent = <&mpic>;
		fsl,espi-num-chipselects = <4>;
		fsl,csbef = <1>;
		fsl,csaft = <1>;
	};
+24 −0
Original line number Diff line number Diff line
Synopsys DesignWare SPI master

Required properties:
- compatible: should be "snps,designware-spi"
- #address-cells: see spi-bus.txt
- #size-cells: see spi-bus.txt
- reg: address and length of the spi master registers
- interrupts: should contain one interrupt
- clocks: spi clock phandle
- num-cs: see spi-bus.txt

Optional properties:
- cs-gpios: see spi-bus.txt

Example:

spi: spi@4020a000 {
	compatible = "snps,designware-spi";
	interrupts = <11 1>;
	reg = <0x4020a000 0x1000>;
	clocks = <&pclk>;
	num-cs = <2>;
	cs-gpios = <&banka 0 0>;
};
+22 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
#include <linux/spi/spi.h>
#include <linux/scatterlist.h>
#include <linux/module.h>
#include <linux/of_gpio.h>

#include "spi-dw.h"

@@ -70,6 +71,27 @@ static int dw_spi_mmio_probe(struct platform_device *pdev)
	dws->num_cs = 4;
	dws->max_freq = clk_get_rate(dwsmmio->clk);

	if (pdev->dev.of_node) {
		int i;

		for (i = 0; i < dws->num_cs; i++) {
			int cs_gpio = of_get_named_gpio(pdev->dev.of_node,
					"cs-gpios", i);

			if (cs_gpio == -EPROBE_DEFER) {
				ret = cs_gpio;
				goto out;
			}

			if (gpio_is_valid(cs_gpio)) {
				ret = devm_gpio_request(&pdev->dev, cs_gpio,
						dev_name(&pdev->dev));
				if (ret)
					goto out;
			}
		}
	}

	ret = dw_spi_add_host(&pdev->dev, dws);
	if (ret)
		goto out;
+22 −175
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/gpio.h>

#include "spi-dw.h"

@@ -36,12 +37,6 @@
#define DONE_STATE	((void *)2)
#define ERROR_STATE	((void *)-1)

#define QUEUE_RUNNING	0
#define QUEUE_STOPPED	1

#define MRST_SPI_DEASSERT	0
#define MRST_SPI_ASSERT		1

/* Slave spi_dev related */
struct chip_data {
	u16 cr0;
@@ -263,28 +258,22 @@ static int map_dma_buffers(struct dw_spi *dws)
static void giveback(struct dw_spi *dws)
{
	struct spi_transfer *last_transfer;
	unsigned long flags;
	struct spi_message *msg;

	spin_lock_irqsave(&dws->lock, flags);
	msg = dws->cur_msg;
	dws->cur_msg = NULL;
	dws->cur_transfer = NULL;
	dws->prev_chip = dws->cur_chip;
	dws->cur_chip = NULL;
	dws->dma_mapped = 0;
	queue_work(dws->workqueue, &dws->pump_messages);
	spin_unlock_irqrestore(&dws->lock, flags);

	last_transfer = list_last_entry(&msg->transfers, struct spi_transfer,
					transfer_list);

	if (!last_transfer->cs_change && dws->cs_control)
		dws->cs_control(MRST_SPI_DEASSERT);
	if (!last_transfer->cs_change)
		spi_chip_sel(dws, dws->cur_msg->spi, 0);

	msg->state = NULL;
	if (msg->complete)
		msg->complete(msg->context);
	spi_finalize_current_message(dws->master);
}

static void int_error_stop(struct dw_spi *dws, const char *msg)
@@ -502,7 +491,7 @@ static void pump_transfers(unsigned long data)
			dw_writew(dws, DW_SPI_CTRL0, cr0);

		spi_set_clk(dws, clk_div ? clk_div : chip->clk_div);
		spi_chip_sel(dws, spi->chip_select);
		spi_chip_sel(dws, spi, 1);

		/* Set the interrupt mask, for poll mode just disable all int */
		spi_mask_intr(dws, 0xff);
@@ -529,30 +518,12 @@ early_exit:
	return;
}

static void pump_messages(struct work_struct *work)
static int dw_spi_transfer_one_message(struct spi_master *master,
		struct spi_message *msg)
{
	struct dw_spi *dws =
		container_of(work, struct dw_spi, pump_messages);
	unsigned long flags;

	/* Lock queue and check for queue work */
	spin_lock_irqsave(&dws->lock, flags);
	if (list_empty(&dws->queue) || dws->run == QUEUE_STOPPED) {
		dws->busy = 0;
		spin_unlock_irqrestore(&dws->lock, flags);
		return;
	}

	/* Make sure we are not already running a message */
	if (dws->cur_msg) {
		spin_unlock_irqrestore(&dws->lock, flags);
		return;
	}

	/* Extract head of queue */
	dws->cur_msg = list_entry(dws->queue.next, struct spi_message, queue);
	list_del_init(&dws->cur_msg->queue);
	struct dw_spi *dws = spi_master_get_devdata(master);

	dws->cur_msg = msg;
	/* Initial message state*/
	dws->cur_msg->state = START_STATE;
	dws->cur_transfer = list_entry(dws->cur_msg->transfers.next,
@@ -560,46 +531,9 @@ static void pump_messages(struct work_struct *work)
						transfer_list);
	dws->cur_chip = spi_get_ctldata(dws->cur_msg->spi);

	/* Mark as busy and launch transfers */
	/* Launch transfers */
	tasklet_schedule(&dws->pump_transfers);

	dws->busy = 1;
	spin_unlock_irqrestore(&dws->lock, flags);
}

/* spi_device use this to queue in their spi_msg */
static int dw_spi_transfer(struct spi_device *spi, struct spi_message *msg)
{
	struct dw_spi *dws = spi_master_get_devdata(spi->master);
	unsigned long flags;

	spin_lock_irqsave(&dws->lock, flags);

	if (dws->run == QUEUE_STOPPED) {
		spin_unlock_irqrestore(&dws->lock, flags);
		return -ESHUTDOWN;
	}

	msg->actual_length = 0;
	msg->status = -EINPROGRESS;
	msg->state = START_STATE;

	list_add_tail(&msg->queue, &dws->queue);

	if (dws->run == QUEUE_RUNNING && !dws->busy) {

		if (dws->cur_transfer || dws->cur_msg)
			queue_work(dws->workqueue,
					&dws->pump_messages);
		else {
			/* If no other data transaction in air, just go */
			spin_unlock_irqrestore(&dws->lock, flags);
			pump_messages(&dws->pump_messages);
			return 0;
		}
	}

	spin_unlock_irqrestore(&dws->lock, flags);
	return 0;
}

@@ -608,6 +542,7 @@ static int dw_spi_setup(struct spi_device *spi)
{
	struct dw_spi_chip *chip_info = NULL;
	struct chip_data *chip;
	int ret;

	/* Only alloc on first setup */
	chip = spi_get_ctldata(spi);
@@ -661,81 +596,13 @@ static int dw_spi_setup(struct spi_device *spi)
			| (spi->mode  << SPI_MODE_OFFSET)
			| (chip->tmode << SPI_TMOD_OFFSET);

	return 0;
}

static int init_queue(struct dw_spi *dws)
{
	INIT_LIST_HEAD(&dws->queue);
	spin_lock_init(&dws->lock);

	dws->run = QUEUE_STOPPED;
	dws->busy = 0;

	tasklet_init(&dws->pump_transfers,
			pump_transfers,	(unsigned long)dws);

	INIT_WORK(&dws->pump_messages, pump_messages);
	dws->workqueue = create_singlethread_workqueue(
					dev_name(dws->master->dev.parent));
	if (dws->workqueue == NULL)
		return -EBUSY;

	return 0;
}

static int start_queue(struct dw_spi *dws)
{
	unsigned long flags;

	spin_lock_irqsave(&dws->lock, flags);

	if (dws->run == QUEUE_RUNNING || dws->busy) {
		spin_unlock_irqrestore(&dws->lock, flags);
		return -EBUSY;
	}

	dws->run = QUEUE_RUNNING;
	dws->cur_msg = NULL;
	dws->cur_transfer = NULL;
	dws->cur_chip = NULL;
	dws->prev_chip = NULL;
	spin_unlock_irqrestore(&dws->lock, flags);

	queue_work(dws->workqueue, &dws->pump_messages);

	return 0;
}

static int stop_queue(struct dw_spi *dws)
{
	unsigned long flags;
	unsigned limit = 50;
	int status = 0;

	spin_lock_irqsave(&dws->lock, flags);
	dws->run = QUEUE_STOPPED;
	while ((!list_empty(&dws->queue) || dws->busy) && limit--) {
		spin_unlock_irqrestore(&dws->lock, flags);
		msleep(10);
		spin_lock_irqsave(&dws->lock, flags);
	}

	if (!list_empty(&dws->queue) || dws->busy)
		status = -EBUSY;
	spin_unlock_irqrestore(&dws->lock, flags);

	return status;
	if (gpio_is_valid(spi->cs_gpio)) {
		ret = gpio_direction_output(spi->cs_gpio,
				!(spi->mode & SPI_CS_HIGH));
		if (ret)
			return ret;
	}

static int destroy_queue(struct dw_spi *dws)
{
	int status;

	status = stop_queue(dws);
	if (status != 0)
		return status;
	destroy_workqueue(dws->workqueue);
	return 0;
}

@@ -794,7 +661,7 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
	master->bus_num = dws->bus_num;
	master->num_chipselect = dws->num_cs;
	master->setup = dw_spi_setup;
	master->transfer = dw_spi_transfer;
	master->transfer_one_message = dw_spi_transfer_one_message;
	master->max_speed_hz = dws->max_freq;

	/* Basic HW init */
@@ -808,33 +675,21 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
		}
	}

	/* Initial and start queue */
	ret = init_queue(dws);
	if (ret) {
		dev_err(&master->dev, "problem initializing queue\n");
		goto err_diable_hw;
	}
	ret = start_queue(dws);
	if (ret) {
		dev_err(&master->dev, "problem starting queue\n");
		goto err_diable_hw;
	}
	tasklet_init(&dws->pump_transfers, pump_transfers, (unsigned long)dws);

	spi_master_set_devdata(master, dws);
	ret = devm_spi_register_master(dev, master);
	if (ret) {
		dev_err(&master->dev, "problem registering spi master\n");
		goto err_queue_alloc;
		goto err_dma_exit;
	}

	mrst_spi_debugfs_init(dws);
	return 0;

err_queue_alloc:
	destroy_queue(dws);
err_dma_exit:
	if (dws->dma_ops && dws->dma_ops->dma_exit)
		dws->dma_ops->dma_exit(dws);
err_diable_hw:
	spi_enable_chip(dws, 0);
err_free_master:
	spi_master_put(master);
@@ -844,18 +699,10 @@ EXPORT_SYMBOL_GPL(dw_spi_add_host);

void dw_spi_remove_host(struct dw_spi *dws)
{
	int status = 0;

	if (!dws)
		return;
	mrst_spi_debugfs_remove(dws);

	/* Remove the queue */
	status = destroy_queue(dws);
	if (status != 0)
		dev_err(&dws->master->dev,
			"dw_spi_remove: workqueue will not complete, message memory not freed\n");

	if (dws->dma_ops && dws->dma_ops->dma_exit)
		dws->dma_ops->dma_exit(dws);
	spi_enable_chip(dws, 0);
@@ -868,7 +715,7 @@ int dw_spi_suspend_host(struct dw_spi *dws)
{
	int ret = 0;

	ret = stop_queue(dws);
	ret = spi_master_suspend(dws->master);
	if (ret)
		return ret;
	spi_enable_chip(dws, 0);
@@ -882,7 +729,7 @@ int dw_spi_resume_host(struct dw_spi *dws)
	int ret;

	spi_hw_init(dws);
	ret = start_queue(dws);
	ret = spi_master_resume(dws->master);
	if (ret)
		dev_err(&dws->master->dev, "fail to start queue (%d)\n", ret);
	return ret;
+11 −13
Original line number Diff line number Diff line
@@ -3,6 +3,7 @@

#include <linux/io.h>
#include <linux/scatterlist.h>
#include <linux/gpio.h>

/* Register offsets */
#define DW_SPI_CTRL0			0x00
@@ -104,14 +105,6 @@ struct dw_spi {
	u16			bus_num;
	u16			num_cs;		/* supported slave numbers */

	/* Driver message queue */
	struct workqueue_struct	*workqueue;
	struct work_struct	pump_messages;
	spinlock_t		lock;
	struct list_head	queue;
	int			busy;
	int			run;

	/* Message Transfer pump */
	struct tasklet_struct	pump_transfers;

@@ -186,14 +179,19 @@ static inline void spi_set_clk(struct dw_spi *dws, u16 div)
	dw_writel(dws, DW_SPI_BAUDR, div);
}

static inline void spi_chip_sel(struct dw_spi *dws, u16 cs)
static inline void spi_chip_sel(struct dw_spi *dws, struct spi_device *spi,
		int active)
{
	if (cs > dws->num_cs)
		return;
	u16 cs = spi->chip_select;
	int gpio_val = active ? (spi->mode & SPI_CS_HIGH) :
		!(spi->mode & SPI_CS_HIGH);

	if (dws->cs_control)
		dws->cs_control(1);
		dws->cs_control(active);
	if (gpio_is_valid(spi->cs_gpio))
		gpio_set_value(spi->cs_gpio, gpio_val);

	if (active)
		dw_writel(dws, DW_SPI_SER, 1 << cs);
}

Loading