Commit 55a734f5 authored by Xu Chenjiao's avatar Xu Chenjiao Committed by guzitao
Browse files

sw64: spi: update spi controller driver

Sunway inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/IBDJNZ



--------------------------------

Update spi controller driver. It should be noted as follows:

- The m25p80 driver has been removed in commit b35b9a10 ("mtd:
spi-nor: Move m25p80 code in spi-nor.c").

- The data structures used by spi memory driver have changed.

Signed-off-by: default avatarXu Chenjiao <xuchenjiao@wxiat.com>
Reviewed-by: default avatarHe Sheng <hesheng@wxiat.com>
Signed-off-by: default avatarGu Zitao <guzitao@wxiat.com>
parent 70b8bd62
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -160,7 +160,7 @@

					partition@0 {
						label = "test";
						reg = <0 0x400000>;
						reg = <0 0x800000>;
					};
				};
			};
@@ -178,7 +178,7 @@

					partition@0 {
						label = "test";
						reg = <0 0x400000>;
						reg = <0 0x800000>;
					};
				};
			};
+15 −7
Original line number Diff line number Diff line
@@ -281,6 +281,21 @@ config SPI_DW_BT1_DIRMAP

endif

config SPI_SUNWAY
	tristate "Sunway SPI controller support"
	depends on SW64
	imply SPI_MEM
	help
	  general driver for Sunway SPI controller

if SPI_SUNWAY

config SPI_SUNWAY_MMIO
	tristate "Memory-mapped io interface driver for Sunway SPI controller"
	depends on HAS_IOMEM

endif

config SPI_DLN2
       tristate "Diolan DLN-2 USB SPI adapter"
       depends on MFD_DLN2
@@ -981,13 +996,6 @@ config SPI_AMD
#
# Add new SPI master controllers in alphabetical order above this line
#
config SPI_CHIP3
	tristate "Memory-mapped io interface driver for SUNWAY CHIP3 SPI core"
        depends on UNCORE_XUELANG
	help
          general driver for SPI controller core from DesignWare


comment "SPI Multiplexer support"

config SPI_MUX
+2 −1
Original line number Diff line number Diff line
@@ -41,8 +41,9 @@ spi-dw-y := spi-dw-core.o
spi-dw-$(CONFIG_SPI_DW_DMA)		+= spi-dw-dma.o
obj-$(CONFIG_SPI_DW_BT1)		+= spi-dw-bt1.o
obj-$(CONFIG_SPI_DW_MMIO)		+= spi-dw-mmio.o
obj-$(CONFIG_SPI_CHIP3)			+= spi-chip3.o spi-chip3-mmio.o
obj-$(CONFIG_SPI_DW_PCI)		+= spi-dw-pci.o
obj-$(CONFIG_SPI_SUNWAY)		+= spi-sunway.o
obj-$(CONFIG_SPI_SUNWAY_MMIO)		+= spi-sunway-mmio.o
obj-$(CONFIG_SPI_EFM32)			+= spi-efm32.o
obj-$(CONFIG_SPI_EP93XX)		+= spi-ep93xx.o
obj-$(CONFIG_SPI_FALCON)		+= spi-falcon.o
+144 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/*
 * Memory-mapped interface driver for SUNWAY CHIP SPI Core
 */

#include <linux/clk.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/scatterlist.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_platform.h>
#include <linux/property.h>
#include <linux/regmap.h>

#include "spi-sunway.h"


#define DRIVER_NAME "sunway_chip_spi"

struct chip_spi_mmio {
	struct spi_chip  spi_chip;
	struct clk     *clk;
	void           *priv;
};

static int chip_spi_mmio_probe(struct platform_device *pdev)
{
	int (*init_func)(struct platform_device *pdev,
			 struct chip_spi_mmio *spimmio);
	struct chip_spi_mmio *spimmio;
	struct spi_chip *spi_chip;
	struct resource *mem;
	int ret;
	int num_cs;

	spimmio = devm_kzalloc(&pdev->dev, sizeof(struct chip_spi_mmio),
			GFP_KERNEL);
	if (!spimmio)
		return -ENOMEM;

	spi_chip = &spimmio->spi_chip;

	/* Get basic io resource and map it */
	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	spi_chip->regs = devm_ioremap_resource(&pdev->dev, mem);
	if (IS_ERR(spi_chip->regs)) {
		dev_err(&pdev->dev, "SPI region map failed\n");
		return PTR_ERR(spi_chip->regs);
	}

	spimmio->clk = devm_clk_get(&pdev->dev, NULL);
	if (IS_ERR(spimmio->clk))
		return PTR_ERR(spimmio->clk);
	ret = clk_prepare_enable(spimmio->clk);
	if (ret)
		return ret;

	spi_chip->bus_num = pdev->id;
	spi_chip->max_freq = clk_get_rate(spimmio->clk);

	device_property_read_u32(&pdev->dev, "reg-io-width",
				&spi_chip->reg_io_width);

	num_cs = 4;
	device_property_read_u32(&pdev->dev, "num-cs", &num_cs);
	spi_chip->num_cs = num_cs;

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

		for (i = 0; i < spi_chip->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;
			}
		}
	}

	init_func = device_get_match_data(&pdev->dev);
	if (init_func) {
		ret = init_func(pdev, spimmio);
		if (ret)
			goto out;
	}

	spi_chip->flags = SPI_PLAT;

	ret = spi_chip_add_host(&pdev->dev, spi_chip);
	if (ret)
		goto out;

	platform_set_drvdata(pdev, spimmio);

	return 0;
out:
	clk_disable_unprepare(spimmio->clk);
	return ret;
}

static int chip_spi_mmio_remove(struct platform_device *pdev)
{
	struct chip_spi_mmio *spimmio = platform_get_drvdata(pdev);

	spi_chip_remove_host(&spimmio->spi_chip);
	clk_disable_unprepare(spimmio->clk);

	return 0;
}

static const struct of_device_id chip_spi_mmio_of_match[] = {
	{ .compatible = "sunway,chip-spi",},
	{ /* end of table */}
};
MODULE_DEVICE_TABLE(of, chip_spi_mmio_of_match);

static struct platform_driver chip_spi_mmio_driver = {
	.probe		= chip_spi_mmio_probe,
	.remove		= chip_spi_mmio_remove,
	.driver		= {
		.name	= DRIVER_NAME,
		.of_match_table = chip_spi_mmio_of_match,
	},
};
module_platform_driver(chip_spi_mmio_driver);

MODULE_AUTHOR("Platform@wiat.com");
MODULE_DESCRIPTION("Memory-mapped I/O interface driver for Sunway CHIP");
MODULE_LICENSE("GPL v2");
+409 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/*
 * SUNWAY CHIP3 SPI core controller driver
 * SPI core controller driver
 */

#include <linux/dma-mapping.h>
@@ -16,7 +16,7 @@
#include <linux/mtd/spi-nor.h>
#include <linux/kernel.h>

#include "spi-chip3.h"
#include "spi-sunway.h"

/* Slave spi_dev related */
struct chip_data {
@@ -30,25 +30,25 @@ struct chip_data {
	void (*cs_control)(u32 command);
};

static void chip3_spi_handle_err(struct spi_controller *master,
static void spi_chip_handle_err(struct spi_controller *master,
		struct spi_message *msg)
{
	struct chip3_spi *dws = spi_controller_get_devdata(master);
	struct spi_chip *spi_chip = spi_controller_get_devdata(master);

	spi_reset_chip(dws);
	spi_reset_chip(spi_chip);
}

static size_t chip3_spi_max_length(struct spi_device *spi)
static size_t spi_chip_max_length(struct spi_device *spi)
{
	struct chip3_spi *dws = spi_controller_get_devdata(spi->master);
	struct spi_chip *spi_chip = spi_controller_get_devdata(spi->master);

	return dws->fifo_len;
	return spi_chip->fifo_len;
}

static int chip3_spi_transfer_one_message(struct spi_controller *master,
static int spi_chip_transfer_one_message(struct spi_controller *master,
		struct spi_message *m)
{
	struct chip3_spi *dws = spi_controller_get_devdata(master);
	struct spi_chip *spi_chip = spi_controller_get_devdata(master);
	struct spi_transfer *t = NULL;
	u16 clk_div;
	u32 freq;
@@ -58,31 +58,31 @@ static int chip3_spi_transfer_one_message(struct spi_controller *master,
	int ret = 0;
	int i = 0;

	spi_enable_chip(dws, 0);
	spi_enable_chip(spi_chip, 0);

	/* Handle per transfer options for bpw and speed. */
	freq = clamp(m->spi->max_speed_hz, 0U, dws->max_freq);
	clk_div = (DIV_ROUND_UP(dws->max_freq, freq) + 1) & 0xfffe;
	speed_hz = dws->max_freq / clk_div;
	freq = clamp(m->spi->max_speed_hz, 0U, spi_chip->max_freq);
	clk_div = (DIV_ROUND_UP(spi_chip->max_freq, freq) + 1) & 0xfffe;
	speed_hz = spi_chip->max_freq / clk_div;

	if (dws->current_freq != speed_hz) {
		spi_set_clk(dws, clk_div);
		dws->current_freq = speed_hz;
	if (spi_chip->current_freq != speed_hz) {
		spi_set_clk(spi_chip, clk_div);
		spi_chip->current_freq = speed_hz;
	}

	dws->n_bytes = 1;
	spi_chip->n_bytes = 1;

	/* For poll mode just disable all interrupts */
	spi_mask_intr(dws, 0xff);
	spi_mask_intr(spi_chip, 0xff);

	chip3_writel(dws, CHIP3_SPI_CTRL0, SPI_TRANSMIT_RECEIVE);
	spi_writel(spi_chip, SPI_CHIP_CTRL0, SPI_TRANSMIT_RECEIVE);

	spi_enable_chip(dws, 1);
	spi_enable_chip(spi_chip, 1);

	list_for_each_entry(t, &m->transfers, transfer_list) {
		len += t->len;
		/* Judge if data is overflow */
		if (len > dws->fifo_len) {
		if (len > spi_chip->fifo_len) {
			pr_err("SPI transfer overflow.\n");
			m->actual_length = 0;
			m->status = -EIO;
@@ -91,27 +91,28 @@ static int chip3_spi_transfer_one_message(struct spi_controller *master,
		}

		if (t->tx_buf)
			memcpy(&dws->buf[len], t->tx_buf, t->len);
			memcpy(&spi_chip->buf[len], t->tx_buf, t->len);
		else
			memset(&dws->buf[len], 0, t->len);
			memset(&spi_chip->buf[len], 0, t->len);
	}

	chip3_writel(dws, CHIP3_SPI_SER, 0x0);
	spi_writel(spi_chip, SPI_CHIP_SER, 0x0);
	for (i = 0; i < len; i++)
		chip3_writel(dws, CHIP3_SPI_DR, dws->buf[i]);
	chip3_writel(dws, CHIP3_SPI_SER, BIT(m->spi->chip_select));
		spi_writel(spi_chip, SPI_CHIP_DR, spi_chip->buf[i]);
	spi_writel(spi_chip, SPI_CHIP_SER, BIT(m->spi->chip_select));

	do {
		status = chip3_readl(dws, CHIP3_SPI_SR);
		status = spi_readl(spi_chip, SPI_CHIP_SR);
	} while (status & SR_BUSY);

	list_for_each_entry(t, &m->transfers, transfer_list) {
		if (t->rx_buf) {
			for (i = 0; i < t->len; i++, t->rx_buf += 1)
				*(u8 *)t->rx_buf = chip3_readl(dws, CHIP3_SPI_DR);
				*(u8 *)t->rx_buf = spi_readl(spi_chip,
							     SPI_CHIP_DR);
		} else {
			for (i = 0; i < t->len; i++)
				chip3_readl(dws, CHIP3_SPI_DR);
				spi_readl(spi_chip, SPI_CHIP_DR);
		}
	}

@@ -123,32 +124,34 @@ static int chip3_spi_transfer_one_message(struct spi_controller *master,
	return ret;
}

static int chip3_spi_adjust_mem_op_size(struct spi_mem *mem,
static int spi_chip_adjust_mem_op_size(struct spi_mem *mem,
				       struct spi_mem_op *op)
{
	struct chip3_spi *dws = spi_controller_get_devdata(mem->spi->controller);
	struct spi_chip *spi_chip = spi_controller_get_devdata(
					   mem->spi->controller);
	size_t len;

	len = sizeof(op->cmd.opcode) + op->addr.nbytes + op->dummy.nbytes;

	op->data.nbytes = min((size_t)op->data.nbytes, (dws->fifo_len - len));
	op->data.nbytes = min((size_t)op->data.nbytes,
			     (spi_chip->fifo_len - len));
	if (!op->data.nbytes)
		return -EINVAL;

	return 0;
}

static int chip3_spi_init_mem_buf(struct chip3_spi *dws,
static int spi_chip_init_mem_buf(struct spi_chip *spi_chip,
		const struct spi_mem_op *op)
{
	int ret = 0;
	int i, j, len;

	/* Calculate the total length of the transfer. */
	len = sizeof(op->cmd.opcode) + op->addr.nbytes + op->dummy.nbytes;
	len = op->cmd.nbytes + op->addr.nbytes + op->dummy.nbytes;

	/* Judge if data is overflow */
	if (len + op->data.nbytes > dws->fifo_len) {
	if (len + op->data.nbytes > spi_chip->fifo_len) {
		ret = -EIO;
		goto way_out;
	}
@@ -158,36 +161,37 @@ static int chip3_spi_init_mem_buf(struct chip3_spi *dws,
	 * buffer. If it's a transfer with data to be sent, also copy it into
	 * the single buffer.
	 */
	for (i = 0; i < sizeof(op->cmd.opcode); i++)
		dws->buf[i] = op->cmd.opcode;
	for (i = 0; i < op->cmd.nbytes; i++)
		spi_chip->buf[i] = op->cmd.opcode;
	for (j = 0; j < op->addr.nbytes; i++, j++)
		dws->buf[i] = op->addr.val >> (8 * (op->addr.nbytes - i));
		spi_chip->buf[i] = op->addr.val >> (8 * (op->addr.nbytes - i));
	for (j = 0; j < op->dummy.nbytes; i++, j++)
		dws->buf[i] = 0xff;
		spi_chip->buf[i] = 0xff;

	if (op->data.dir == SPI_MEM_DATA_OUT) {
		memcpy(&dws->buf[i], op->data.buf.out, op->data.nbytes);
		memcpy(&spi_chip->buf[i], op->data.buf.out, op->data.nbytes);
		len += op->data.nbytes;
	}

	dws->tx_len = len;
	spi_chip->tx_len = len;

	if (op->data.dir == SPI_MEM_DATA_IN) {
		dws->rx = op->data.buf.in;
		dws->rx_len = op->data.nbytes;
		spi_chip->rx = op->data.buf.in;
		spi_chip->rx_len = op->data.nbytes;
	} else {
		dws->rx = NULL;
		dws->rx_len = 0;
		spi_chip->rx = NULL;
		spi_chip->rx_len = 0;
	}

way_out:
	return ret;
}

static int chip3_spi_exec_mem_op(struct spi_mem *mem,
static int spi_chip_exec_mem_op(struct spi_mem *mem,
				const struct spi_mem_op *op)
{
	struct chip3_spi *dws = spi_controller_get_devdata(mem->spi->controller);
	struct spi_chip *spi_chip = spi_controller_get_devdata(
					   mem->spi->controller);
	u16 clk_div;
	int ret = 0;
	int i;
@@ -195,55 +199,55 @@ static int chip3_spi_exec_mem_op(struct spi_mem *mem,
	u32 freq;
	u32 speed_hz;

	ret = chip3_spi_init_mem_buf(dws, op);
	ret = spi_chip_init_mem_buf(spi_chip, op);
	if (ret)
		return ret;

	spi_enable_chip(dws, 0);
	spi_enable_chip(spi_chip, 0);

	/* Handle per transfer options for bpw and speed. */
	freq = clamp(mem->spi->max_speed_hz, 0U, dws->max_freq);
	clk_div = (DIV_ROUND_UP(dws->max_freq, freq) + 1) & 0xfffe;
	speed_hz = dws->max_freq / clk_div;
	freq = clamp(mem->spi->max_speed_hz, 0U, spi_chip->max_freq);
	clk_div = (DIV_ROUND_UP(spi_chip->max_freq, freq) + 1) & 0xfffe;
	speed_hz = spi_chip->max_freq / clk_div;

	if (dws->current_freq != speed_hz) {
		spi_set_clk(dws, clk_div);
		dws->current_freq = speed_hz;
	if (spi_chip->current_freq != speed_hz) {
		spi_set_clk(spi_chip, clk_div);
		spi_chip->current_freq = speed_hz;
	}

	dws->n_bytes = 1;
	spi_chip->n_bytes = 1;

	/* For poll mode just disable all interrupts */
	spi_mask_intr(dws, 0xff);
	spi_mask_intr(spi_chip, 0xff);

	if ((dws->tx_len != 0) && (dws->rx_len != 0)) {
		chip3_writel(dws, CHIP3_SPI_CTRL0, SPI_EEPROM_READ);
		chip3_writel(dws, CHIP3_SPI_CTRL1, (dws->rx_len - 1));
	if ((spi_chip->tx_len != 0) && (spi_chip->rx_len != 0)) {
		spi_writel(spi_chip, SPI_CHIP_CTRL0, SPI_EEPROM_READ);
		spi_writel(spi_chip, SPI_CHIP_CTRL1, (spi_chip->rx_len - 1));
	} else {
		chip3_writel(dws, CHIP3_SPI_CTRL0, SPI_TRANSMIT_ONLY);
		spi_writel(spi_chip, SPI_CHIP_CTRL0, SPI_TRANSMIT_ONLY);
	}

	spi_enable_chip(dws, 1);
	spi_enable_chip(spi_chip, 1);

	chip3_writel(dws, CHIP3_SPI_SER, 0x0);
	for (i = 0; i < dws->tx_len; i++)
		chip3_writel(dws, CHIP3_SPI_DR, dws->buf[i]);
	chip3_writel(dws, CHIP3_SPI_SER, BIT(mem->spi->chip_select));
	spi_writel(spi_chip, SPI_CHIP_SER, 0x0);
	for (i = 0; i < spi_chip->tx_len; i++)
		spi_writel(spi_chip, SPI_CHIP_DR, spi_chip->buf[i]);
	spi_writel(spi_chip, SPI_CHIP_SER, BIT(mem->spi->chip_select));

	value = chip3_readl(dws, CHIP3_SPI_SR);
	value = spi_readl(spi_chip, SPI_CHIP_SR);
	while (value & SR_BUSY)
		value = chip3_readl(dws, CHIP3_SPI_SR);
		value = spi_readl(spi_chip, SPI_CHIP_SR);

	for (i = 0; i < dws->rx_len; dws->rx += dws->n_bytes, i++)
		*(u8 *)dws->rx = chip3_readl(dws, CHIP3_SPI_DR);
	for (i = 0; i < spi_chip->rx_len; spi_chip->rx += spi_chip->n_bytes, i++)
		*(u8 *)spi_chip->rx = spi_readl(spi_chip, SPI_CHIP_DR);

	return ret;
}

/* This may be called twice for each spi dev */
static int chip3_spi_setup(struct spi_device *spi)
static int spi_chip_setup(struct spi_device *spi)
{
	struct chip3_spi_chip *chip_info = NULL;
	struct spi_chip_info *chip_info = NULL;
	struct chip_data *chip;
	u32 poll_mode = 0;
	struct device_node *np = spi->dev.of_node;
@@ -282,7 +286,7 @@ static int chip3_spi_setup(struct spi_device *spi)
	return 0;
}

static void chip3_spi_cleanup(struct spi_device *spi)
static void spi_chip_cleanup(struct spi_device *spi)
{
	struct chip_data *chip = spi_get_ctldata(spi);

@@ -291,114 +295,115 @@ static void chip3_spi_cleanup(struct spi_device *spi)
}

/* Restart the controller, disable all interrupts, clean rx fifo */
static void spi_hw_init(struct device *dev, struct chip3_spi *dws)
static void spi_hw_init(struct device *dev, struct spi_chip *spi_chip)
{
	spi_reset_chip(dws);
	spi_reset_chip(spi_chip);

	/*
	 * Try to detect the FIFO depth if not set by interface driver,
	 * the depth could be from 2 to 256 from HW spec
	 */
	if (!dws->fifo_len) {
	if (!spi_chip->fifo_len) {
		u32 fifo;

		for (fifo = 1; fifo < 256; fifo++) {
			chip3_writel(dws, CHIP3_SPI_TXFLTR, fifo);
			if (fifo != chip3_readl(dws, CHIP3_SPI_TXFLTR))
			spi_writel(spi_chip, SPI_CHIP_TXFLTR, fifo);
			if (fifo != spi_readl(spi_chip, SPI_CHIP_TXFLTR))
				break;
		}
		chip3_writel(dws, CHIP3_SPI_TXFLTR, 0);
		spi_writel(spi_chip, SPI_CHIP_TXFLTR, 0);

		dws->fifo_len = (fifo == 1) ? 0 : fifo;
		dev_info(dev, "Detected FIFO size: %u bytes\n", dws->fifo_len);
		spi_chip->fifo_len = (fifo == 1) ? 0 : fifo;
		dev_info(dev, "Detected FIFO size: %u bytes\n",
			 spi_chip->fifo_len);
	}
}

static const struct spi_controller_mem_ops chip3_mem_ops = {
	.adjust_op_size = chip3_spi_adjust_mem_op_size,
	.exec_op = chip3_spi_exec_mem_op,
static const struct spi_controller_mem_ops spi_mem_ops = {
	.adjust_op_size = spi_chip_adjust_mem_op_size,
	.exec_op = spi_chip_exec_mem_op,
};


int chip3_spi_add_host(struct device *dev, struct chip3_spi *dws)
int spi_chip_add_host(struct device *dev, struct spi_chip *spi_chip)
{
	struct spi_controller *master;
	int ret;

	BUG_ON(dws == NULL);
	WARN_ON(spi_chip == NULL);

	master = spi_alloc_master(dev, 0);
	if (!master)
		return -ENOMEM;

	dws->master = master;
	dws->type = SSI_MOTO_SPI;
	spi_chip->master = master;
	spi_chip->type = SSI_MOTO_SPI;

	spi_controller_set_devdata(master, dws);
	spi_controller_set_devdata(master, spi_chip);

	master->mode_bits = SPI_CPOL | SPI_CPHA;
	master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16);
	master->bus_num = dws->bus_num;
	master->num_chipselect = dws->num_cs;
	master->setup = chip3_spi_setup;
	master->cleanup = chip3_spi_cleanup;
	master->transfer_one_message = chip3_spi_transfer_one_message;
	master->handle_err = chip3_spi_handle_err;
	master->max_speed_hz = dws->max_freq;
	master->bus_num = spi_chip->bus_num;
	master->num_chipselect = spi_chip->num_cs;
	master->setup = spi_chip_setup;
	master->cleanup = spi_chip_cleanup;
	master->transfer_one_message = spi_chip_transfer_one_message;
	master->handle_err = spi_chip_handle_err;
	master->max_speed_hz = spi_chip->max_freq;
	master->dev.of_node = dev->of_node;
	master->flags = SPI_MASTER_GPIO_SS;
	master->max_transfer_size = chip3_spi_max_length;
	master->max_message_size = chip3_spi_max_length;
	master->max_transfer_size = spi_chip_max_length;
	master->max_message_size = spi_chip_max_length;

	master->mem_ops = &chip3_mem_ops;
	master->mem_ops = &spi_mem_ops;

	/* Basic HW init */
	spi_hw_init(dev, dws);
	spi_hw_init(dev, spi_chip);

	ret = devm_spi_register_controller(dev, master);
	if (ret) {
		dev_err(&master->dev, "problem registering spi master\n");
		spi_enable_chip(dws, 0);
		free_irq(dws->irq, master);
		spi_enable_chip(spi_chip, 0);
		free_irq(spi_chip->irq, master);
	}

	return 0;
}
EXPORT_SYMBOL_GPL(chip3_spi_add_host);
EXPORT_SYMBOL_GPL(spi_chip_add_host);

void chip3_spi_remove_host(struct chip3_spi *dws)
void spi_chip_remove_host(struct spi_chip *spi_chip)
{
	spi_shutdown_chip(dws);
	spi_shutdown_chip(spi_chip);

	free_irq(dws->irq, dws->master);
	free_irq(spi_chip->irq, spi_chip->master);
}
EXPORT_SYMBOL_GPL(chip3_spi_remove_host);
EXPORT_SYMBOL_GPL(spi_chip_remove_host);

int chip3_spi_suspend_host(struct chip3_spi *dws)
int spi_chip_suspend_host(struct spi_chip *spi_chip)
{
	int ret;

	ret = spi_controller_suspend(dws->master);
	ret = spi_controller_suspend(spi_chip->master);
	if (ret)
		return ret;

	spi_shutdown_chip(dws);
	spi_shutdown_chip(spi_chip);
	return 0;
}
EXPORT_SYMBOL_GPL(chip3_spi_suspend_host);
EXPORT_SYMBOL_GPL(spi_chip_suspend_host);

int chip3_spi_resume_host(struct chip3_spi *dws)
int spi_chip_resume_host(struct spi_chip *spi_chip)
{
	int ret;

	spi_hw_init(&dws->master->dev, dws);
	ret = spi_controller_resume(dws->master);
	spi_hw_init(&spi_chip->master->dev, spi_chip);
	ret = spi_controller_resume(spi_chip->master);
	if (ret)
		dev_err(&dws->master->dev, "fail to start queue (%d)\n", ret);
		dev_err(&spi_chip->master->dev, "fail to start queue (%d)\n",
			ret);
	return ret;
}
EXPORT_SYMBOL_GPL(chip3_spi_resume_host);
EXPORT_SYMBOL_GPL(spi_chip_resume_host);

MODULE_AUTHOR("Platform@wiat.com");
MODULE_DESCRIPTION("Driver for Sunway CHIP3 SPI controller core");
MODULE_DESCRIPTION("Driver for SPI controller core");
MODULE_LICENSE("GPL v2");
Loading