Merge remote-tracking branches 'spi/topic/sunxi', 'spi/topic/tegra114', 'spi/topic/ti-qspi', 'spi/topic/ti-ssp', 'spi/topic/topcliff-pch', 'spi/topic/txx9', 'spi/topic/xcomm' and 'spi/topic/xfer' into spi-next

This commit is contained in:
Mark Brown 2014-03-30 00:51:41 +00:00
13 изменённых файлов: 1053 добавлений и 459 удалений

Просмотреть файл

@ -0,0 +1,24 @@
Allwinner A10 SPI controller
Required properties:
- compatible: Should be "allwinner,sun4-a10-spi".
- reg: Should contain register location and length.
- interrupts: Should contain interrupt.
- clocks: phandle to the clocks feeding the SPI controller. Two are
needed:
- "ahb": the gated AHB parent clock
- "mod": the parent module clock
- clock-names: Must contain the clock names described just above
Example:
spi1: spi@01c06000 {
compatible = "allwinner,sun4i-a10-spi";
reg = <0x01c06000 0x1000>;
interrupts = <11>;
clocks = <&ahb_gates 21>, <&spi1_clk>;
clock-names = "ahb", "mod";
status = "disabled";
#address-cells = <1>;
#size-cells = <0>;
};

Просмотреть файл

@ -0,0 +1,24 @@
Allwinner A31 SPI controller
Required properties:
- compatible: Should be "allwinner,sun6i-a31-spi".
- reg: Should contain register location and length.
- interrupts: Should contain interrupt.
- clocks: phandle to the clocks feeding the SPI controller. Two are
needed:
- "ahb": the gated AHB parent clock
- "mod": the parent module clock
- clock-names: Must contain the clock names described just above
- resets: phandle to the reset controller asserting this device in
reset
Example:
spi1: spi@01c69000 {
compatible = "allwinner,sun6i-a31-spi";
reg = <0x01c69000 0x1000>;
interrupts = <0 66 4>;
clocks = <&ahb1_gates 21>, <&spi1_clk>;
clock-names = "ahb", "mod";
resets = <&ahb1_rst 21>;
};

Просмотреть файл

@ -458,6 +458,19 @@ config SPI_SIRF
help
SPI driver for CSR SiRFprimaII SoCs
config SPI_SUN4I
tristate "Allwinner A10 SoCs SPI controller"
depends on ARCH_SUNXI || COMPILE_TEST
help
SPI driver for Allwinner sun4i, sun5i and sun7i SoCs
config SPI_SUN6I
tristate "Allwinner A31 SPI controller"
depends on ARCH_SUNXI || COMPILE_TEST
depends on RESET_CONTROLLER
help
This enables using the SPI controller on the Allwinner A31 SoCs.
config SPI_MXS
tristate "Freescale MXS SPI controller"
depends on ARCH_MXS
@ -490,13 +503,6 @@ config SPI_TEGRA20_SLINK
help
SPI driver for Nvidia Tegra20/Tegra30 SLINK Controller interface.
config SPI_TI_SSP
tristate "TI Sequencer Serial Port - SPI Support"
depends on MFD_TI_SSP
help
This selects an SPI master implementation using a TI sequencer
serial port.
config SPI_TOPCLIFF_PCH
tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) SPI"
depends on PCI

Просмотреть файл

@ -71,10 +71,11 @@ obj-$(CONFIG_SPI_SH_HSPI) += spi-sh-hspi.o
obj-$(CONFIG_SPI_SH_MSIOF) += spi-sh-msiof.o
obj-$(CONFIG_SPI_SH_SCI) += spi-sh-sci.o
obj-$(CONFIG_SPI_SIRF) += spi-sirf.o
obj-$(CONFIG_SPI_SUN4I) += spi-sun4i.o
obj-$(CONFIG_SPI_SUN6I) += spi-sun6i.o
obj-$(CONFIG_SPI_TEGRA114) += spi-tegra114.o
obj-$(CONFIG_SPI_TEGRA20_SFLASH) += spi-tegra20-sflash.o
obj-$(CONFIG_SPI_TEGRA20_SLINK) += spi-tegra20-slink.o
obj-$(CONFIG_SPI_TI_SSP) += spi-ti-ssp.o
obj-$(CONFIG_SPI_TLE62X0) += spi-tle62x0.o
obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi-topcliff-pch.o
obj-$(CONFIG_SPI_TXX9) += spi-txx9.o

478
drivers/spi/spi-sun4i.c Normal file
Просмотреть файл

@ -0,0 +1,478 @@
/*
* Copyright (C) 2012 - 2014 Allwinner Tech
* Pan Nan <pannan@allwinnertech.com>
*
* Copyright (C) 2014 Maxime Ripard
* Maxime Ripard <maxime.ripard@free-electrons.com>
*
* 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; either version 2 of
* the License, or (at your option) any later version.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/workqueue.h>
#include <linux/spi/spi.h>
#define SUN4I_FIFO_DEPTH 64
#define SUN4I_RXDATA_REG 0x00
#define SUN4I_TXDATA_REG 0x04
#define SUN4I_CTL_REG 0x08
#define SUN4I_CTL_ENABLE BIT(0)
#define SUN4I_CTL_MASTER BIT(1)
#define SUN4I_CTL_CPHA BIT(2)
#define SUN4I_CTL_CPOL BIT(3)
#define SUN4I_CTL_CS_ACTIVE_LOW BIT(4)
#define SUN4I_CTL_LMTF BIT(6)
#define SUN4I_CTL_TF_RST BIT(8)
#define SUN4I_CTL_RF_RST BIT(9)
#define SUN4I_CTL_XCH BIT(10)
#define SUN4I_CTL_CS_MASK 0x3000
#define SUN4I_CTL_CS(cs) (((cs) << 12) & SUN4I_CTL_CS_MASK)
#define SUN4I_CTL_DHB BIT(15)
#define SUN4I_CTL_CS_MANUAL BIT(16)
#define SUN4I_CTL_CS_LEVEL BIT(17)
#define SUN4I_CTL_TP BIT(18)
#define SUN4I_INT_CTL_REG 0x0c
#define SUN4I_INT_CTL_TC BIT(16)
#define SUN4I_INT_STA_REG 0x10
#define SUN4I_DMA_CTL_REG 0x14
#define SUN4I_WAIT_REG 0x18
#define SUN4I_CLK_CTL_REG 0x1c
#define SUN4I_CLK_CTL_CDR2_MASK 0xff
#define SUN4I_CLK_CTL_CDR2(div) ((div) & SUN4I_CLK_CTL_CDR2_MASK)
#define SUN4I_CLK_CTL_CDR1_MASK 0xf
#define SUN4I_CLK_CTL_CDR1(div) (((div) & SUN4I_CLK_CTL_CDR1_MASK) << 8)
#define SUN4I_CLK_CTL_DRS BIT(12)
#define SUN4I_BURST_CNT_REG 0x20
#define SUN4I_BURST_CNT(cnt) ((cnt) & 0xffffff)
#define SUN4I_XMIT_CNT_REG 0x24
#define SUN4I_XMIT_CNT(cnt) ((cnt) & 0xffffff)
#define SUN4I_FIFO_STA_REG 0x28
#define SUN4I_FIFO_STA_RF_CNT_MASK 0x7f
#define SUN4I_FIFO_STA_RF_CNT_BITS 0
#define SUN4I_FIFO_STA_TF_CNT_MASK 0x7f
#define SUN4I_FIFO_STA_TF_CNT_BITS 16
struct sun4i_spi {
struct spi_master *master;
void __iomem *base_addr;
struct clk *hclk;
struct clk *mclk;
struct completion done;
const u8 *tx_buf;
u8 *rx_buf;
int len;
};
static inline u32 sun4i_spi_read(struct sun4i_spi *sspi, u32 reg)
{
return readl(sspi->base_addr + reg);
}
static inline void sun4i_spi_write(struct sun4i_spi *sspi, u32 reg, u32 value)
{
writel(value, sspi->base_addr + reg);
}
static inline void sun4i_spi_drain_fifo(struct sun4i_spi *sspi, int len)
{
u32 reg, cnt;
u8 byte;
/* See how much data is available */
reg = sun4i_spi_read(sspi, SUN4I_FIFO_STA_REG);
reg &= SUN4I_FIFO_STA_RF_CNT_MASK;
cnt = reg >> SUN4I_FIFO_STA_RF_CNT_BITS;
if (len > cnt)
len = cnt;
while (len--) {
byte = readb(sspi->base_addr + SUN4I_RXDATA_REG);
if (sspi->rx_buf)
*sspi->rx_buf++ = byte;
}
}
static inline void sun4i_spi_fill_fifo(struct sun4i_spi *sspi, int len)
{
u8 byte;
if (len > sspi->len)
len = sspi->len;
while (len--) {
byte = sspi->tx_buf ? *sspi->tx_buf++ : 0;
writeb(byte, sspi->base_addr + SUN4I_TXDATA_REG);
sspi->len--;
}
}
static void sun4i_spi_set_cs(struct spi_device *spi, bool enable)
{
struct sun4i_spi *sspi = spi_master_get_devdata(spi->master);
u32 reg;
reg = sun4i_spi_read(sspi, SUN4I_CTL_REG);
reg &= ~SUN4I_CTL_CS_MASK;
reg |= SUN4I_CTL_CS(spi->chip_select);
if (enable)
reg |= SUN4I_CTL_CS_LEVEL;
else
reg &= ~SUN4I_CTL_CS_LEVEL;
/*
* Even though this looks irrelevant since we are supposed to
* be controlling the chip select manually, this bit also
* controls the levels of the chip select for inactive
* devices.
*
* If we don't set it, the chip select level will go low by
* default when the device is idle, which is not really
* expected in the common case where the chip select is active
* low.
*/
if (spi->mode & SPI_CS_HIGH)
reg &= ~SUN4I_CTL_CS_ACTIVE_LOW;
else
reg |= SUN4I_CTL_CS_ACTIVE_LOW;
sun4i_spi_write(sspi, SUN4I_CTL_REG, reg);
}
static int sun4i_spi_transfer_one(struct spi_master *master,
struct spi_device *spi,
struct spi_transfer *tfr)
{
struct sun4i_spi *sspi = spi_master_get_devdata(master);
unsigned int mclk_rate, div, timeout;
unsigned int tx_len = 0;
int ret = 0;
u32 reg;
/* We don't support transfer larger than the FIFO */
if (tfr->len > SUN4I_FIFO_DEPTH)
return -EINVAL;
reinit_completion(&sspi->done);
sspi->tx_buf = tfr->tx_buf;
sspi->rx_buf = tfr->rx_buf;
sspi->len = tfr->len;
/* Clear pending interrupts */
sun4i_spi_write(sspi, SUN4I_INT_STA_REG, ~0);
reg = sun4i_spi_read(sspi, SUN4I_CTL_REG);
/* Reset FIFOs */
sun4i_spi_write(sspi, SUN4I_CTL_REG,
reg | SUN4I_CTL_RF_RST | SUN4I_CTL_TF_RST);
/*
* Setup the transfer control register: Chip Select,
* polarities, etc.
*/
if (spi->mode & SPI_CPOL)
reg |= SUN4I_CTL_CPOL;
else
reg &= ~SUN4I_CTL_CPOL;
if (spi->mode & SPI_CPHA)
reg |= SUN4I_CTL_CPHA;
else
reg &= ~SUN4I_CTL_CPHA;
if (spi->mode & SPI_LSB_FIRST)
reg |= SUN4I_CTL_LMTF;
else
reg &= ~SUN4I_CTL_LMTF;
/*
* If it's a TX only transfer, we don't want to fill the RX
* FIFO with bogus data
*/
if (sspi->rx_buf)
reg &= ~SUN4I_CTL_DHB;
else
reg |= SUN4I_CTL_DHB;
/* We want to control the chip select manually */
reg |= SUN4I_CTL_CS_MANUAL;
sun4i_spi_write(sspi, SUN4I_CTL_REG, reg);
/* Ensure that we have a parent clock fast enough */
mclk_rate = clk_get_rate(sspi->mclk);
if (mclk_rate < (2 * spi->max_speed_hz)) {
clk_set_rate(sspi->mclk, 2 * spi->max_speed_hz);
mclk_rate = clk_get_rate(sspi->mclk);
}
/*
* Setup clock divider.
*
* We have two choices there. Either we can use the clock
* divide rate 1, which is calculated thanks to this formula:
* SPI_CLK = MOD_CLK / (2 ^ (cdr + 1))
* Or we can use CDR2, which is calculated with the formula:
* SPI_CLK = MOD_CLK / (2 * (cdr + 1))
* Wether we use the former or the latter is set through the
* DRS bit.
*
* First try CDR2, and if we can't reach the expected
* frequency, fall back to CDR1.
*/
div = mclk_rate / (2 * spi->max_speed_hz);
if (div <= (SUN4I_CLK_CTL_CDR2_MASK + 1)) {
if (div > 0)
div--;
reg = SUN4I_CLK_CTL_CDR2(div) | SUN4I_CLK_CTL_DRS;
} else {
div = ilog2(mclk_rate) - ilog2(spi->max_speed_hz);
reg = SUN4I_CLK_CTL_CDR1(div);
}
sun4i_spi_write(sspi, SUN4I_CLK_CTL_REG, reg);
/* Setup the transfer now... */
if (sspi->tx_buf)
tx_len = tfr->len;
/* Setup the counters */
sun4i_spi_write(sspi, SUN4I_BURST_CNT_REG, SUN4I_BURST_CNT(tfr->len));
sun4i_spi_write(sspi, SUN4I_XMIT_CNT_REG, SUN4I_XMIT_CNT(tx_len));
/* Fill the TX FIFO */
sun4i_spi_fill_fifo(sspi, SUN4I_FIFO_DEPTH);
/* Enable the interrupts */
sun4i_spi_write(sspi, SUN4I_INT_CTL_REG, SUN4I_INT_CTL_TC);
/* Start the transfer */
reg = sun4i_spi_read(sspi, SUN4I_CTL_REG);
sun4i_spi_write(sspi, SUN4I_CTL_REG, reg | SUN4I_CTL_XCH);
timeout = wait_for_completion_timeout(&sspi->done,
msecs_to_jiffies(1000));
if (!timeout) {
ret = -ETIMEDOUT;
goto out;
}
sun4i_spi_drain_fifo(sspi, SUN4I_FIFO_DEPTH);
out:
sun4i_spi_write(sspi, SUN4I_INT_CTL_REG, 0);
return ret;
}
static irqreturn_t sun4i_spi_handler(int irq, void *dev_id)
{
struct sun4i_spi *sspi = dev_id;
u32 status = sun4i_spi_read(sspi, SUN4I_INT_STA_REG);
/* Transfer complete */
if (status & SUN4I_INT_CTL_TC) {
sun4i_spi_write(sspi, SUN4I_INT_STA_REG, SUN4I_INT_CTL_TC);
complete(&sspi->done);
return IRQ_HANDLED;
}
return IRQ_NONE;
}
static int sun4i_spi_runtime_resume(struct device *dev)
{
struct spi_master *master = dev_get_drvdata(dev);
struct sun4i_spi *sspi = spi_master_get_devdata(master);
int ret;
ret = clk_prepare_enable(sspi->hclk);
if (ret) {
dev_err(dev, "Couldn't enable AHB clock\n");
goto out;
}
ret = clk_prepare_enable(sspi->mclk);
if (ret) {
dev_err(dev, "Couldn't enable module clock\n");
goto err;
}
sun4i_spi_write(sspi, SUN4I_CTL_REG,
SUN4I_CTL_ENABLE | SUN4I_CTL_MASTER | SUN4I_CTL_TP);
return 0;
err:
clk_disable_unprepare(sspi->hclk);
out:
return ret;
}
static int sun4i_spi_runtime_suspend(struct device *dev)
{
struct spi_master *master = dev_get_drvdata(dev);
struct sun4i_spi *sspi = spi_master_get_devdata(master);
clk_disable_unprepare(sspi->mclk);
clk_disable_unprepare(sspi->hclk);
return 0;
}
static int sun4i_spi_probe(struct platform_device *pdev)
{
struct spi_master *master;
struct sun4i_spi *sspi;
struct resource *res;
int ret = 0, irq;
master = spi_alloc_master(&pdev->dev, sizeof(struct sun4i_spi));
if (!master) {
dev_err(&pdev->dev, "Unable to allocate SPI Master\n");
return -ENOMEM;
}
platform_set_drvdata(pdev, master);
sspi = spi_master_get_devdata(master);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
sspi->base_addr = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(sspi->base_addr)) {
ret = PTR_ERR(sspi->base_addr);
goto err_free_master;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "No spi IRQ specified\n");
ret = -ENXIO;
goto err_free_master;
}
ret = devm_request_irq(&pdev->dev, irq, sun4i_spi_handler,
0, "sun4i-spi", sspi);
if (ret) {
dev_err(&pdev->dev, "Cannot request IRQ\n");
goto err_free_master;
}
sspi->master = master;
master->set_cs = sun4i_spi_set_cs;
master->transfer_one = sun4i_spi_transfer_one;
master->num_chipselect = 4;
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST;
master->bits_per_word_mask = SPI_BPW_MASK(8);
master->dev.of_node = pdev->dev.of_node;
master->auto_runtime_pm = true;
sspi->hclk = devm_clk_get(&pdev->dev, "ahb");
if (IS_ERR(sspi->hclk)) {
dev_err(&pdev->dev, "Unable to acquire AHB clock\n");
ret = PTR_ERR(sspi->hclk);
goto err_free_master;
}
sspi->mclk = devm_clk_get(&pdev->dev, "mod");
if (IS_ERR(sspi->mclk)) {
dev_err(&pdev->dev, "Unable to acquire module clock\n");
ret = PTR_ERR(sspi->mclk);
goto err_free_master;
}
init_completion(&sspi->done);
/*
* This wake-up/shutdown pattern is to be able to have the
* device woken up, even if runtime_pm is disabled
*/
ret = sun4i_spi_runtime_resume(&pdev->dev);
if (ret) {
dev_err(&pdev->dev, "Couldn't resume the device\n");
goto err_free_master;
}
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
pm_runtime_idle(&pdev->dev);
ret = devm_spi_register_master(&pdev->dev, master);
if (ret) {
dev_err(&pdev->dev, "cannot register SPI master\n");
goto err_pm_disable;
}
return 0;
err_pm_disable:
pm_runtime_disable(&pdev->dev);
sun4i_spi_runtime_suspend(&pdev->dev);
err_free_master:
spi_master_put(master);
return ret;
}
static int sun4i_spi_remove(struct platform_device *pdev)
{
pm_runtime_disable(&pdev->dev);
return 0;
}
static const struct of_device_id sun4i_spi_match[] = {
{ .compatible = "allwinner,sun4i-a10-spi", },
{}
};
MODULE_DEVICE_TABLE(of, sun4i_spi_match);
static const struct dev_pm_ops sun4i_spi_pm_ops = {
.runtime_resume = sun4i_spi_runtime_resume,
.runtime_suspend = sun4i_spi_runtime_suspend,
};
static struct platform_driver sun4i_spi_driver = {
.probe = sun4i_spi_probe,
.remove = sun4i_spi_remove,
.driver = {
.name = "sun4i-spi",
.owner = THIS_MODULE,
.of_match_table = sun4i_spi_match,
.pm = &sun4i_spi_pm_ops,
},
};
module_platform_driver(sun4i_spi_driver);
MODULE_AUTHOR("Pan Nan <pannan@allwinnertech.com>");
MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
MODULE_DESCRIPTION("Allwinner A1X/A20 SPI controller driver");
MODULE_LICENSE("GPL");

484
drivers/spi/spi-sun6i.c Normal file
Просмотреть файл

@ -0,0 +1,484 @@
/*
* Copyright (C) 2012 - 2014 Allwinner Tech
* Pan Nan <pannan@allwinnertech.com>
*
* Copyright (C) 2014 Maxime Ripard
* Maxime Ripard <maxime.ripard@free-electrons.com>
*
* 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; either version 2 of
* the License, or (at your option) any later version.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <linux/workqueue.h>
#include <linux/spi/spi.h>
#define SUN6I_FIFO_DEPTH 128
#define SUN6I_GBL_CTL_REG 0x04
#define SUN6I_GBL_CTL_BUS_ENABLE BIT(0)
#define SUN6I_GBL_CTL_MASTER BIT(1)
#define SUN6I_GBL_CTL_TP BIT(7)
#define SUN6I_GBL_CTL_RST BIT(31)
#define SUN6I_TFR_CTL_REG 0x08
#define SUN6I_TFR_CTL_CPHA BIT(0)
#define SUN6I_TFR_CTL_CPOL BIT(1)
#define SUN6I_TFR_CTL_SPOL BIT(2)
#define SUN6I_TFR_CTL_CS_MASK 0x30
#define SUN6I_TFR_CTL_CS(cs) (((cs) << 4) & SUN6I_TFR_CTL_CS_MASK)
#define SUN6I_TFR_CTL_CS_MANUAL BIT(6)
#define SUN6I_TFR_CTL_CS_LEVEL BIT(7)
#define SUN6I_TFR_CTL_DHB BIT(8)
#define SUN6I_TFR_CTL_FBS BIT(12)
#define SUN6I_TFR_CTL_XCH BIT(31)
#define SUN6I_INT_CTL_REG 0x10
#define SUN6I_INT_CTL_RF_OVF BIT(8)
#define SUN6I_INT_CTL_TC BIT(12)
#define SUN6I_INT_STA_REG 0x14
#define SUN6I_FIFO_CTL_REG 0x18
#define SUN6I_FIFO_CTL_RF_RST BIT(15)
#define SUN6I_FIFO_CTL_TF_RST BIT(31)
#define SUN6I_FIFO_STA_REG 0x1c
#define SUN6I_FIFO_STA_RF_CNT_MASK 0x7f
#define SUN6I_FIFO_STA_RF_CNT_BITS 0
#define SUN6I_FIFO_STA_TF_CNT_MASK 0x7f
#define SUN6I_FIFO_STA_TF_CNT_BITS 16
#define SUN6I_CLK_CTL_REG 0x24
#define SUN6I_CLK_CTL_CDR2_MASK 0xff
#define SUN6I_CLK_CTL_CDR2(div) (((div) & SUN6I_CLK_CTL_CDR2_MASK) << 0)
#define SUN6I_CLK_CTL_CDR1_MASK 0xf
#define SUN6I_CLK_CTL_CDR1(div) (((div) & SUN6I_CLK_CTL_CDR1_MASK) << 8)
#define SUN6I_CLK_CTL_DRS BIT(12)
#define SUN6I_BURST_CNT_REG 0x30
#define SUN6I_BURST_CNT(cnt) ((cnt) & 0xffffff)
#define SUN6I_XMIT_CNT_REG 0x34
#define SUN6I_XMIT_CNT(cnt) ((cnt) & 0xffffff)
#define SUN6I_BURST_CTL_CNT_REG 0x38
#define SUN6I_BURST_CTL_CNT_STC(cnt) ((cnt) & 0xffffff)
#define SUN6I_TXDATA_REG 0x200
#define SUN6I_RXDATA_REG 0x300
struct sun6i_spi {
struct spi_master *master;
void __iomem *base_addr;
struct clk *hclk;
struct clk *mclk;
struct reset_control *rstc;
struct completion done;
const u8 *tx_buf;
u8 *rx_buf;
int len;
};
static inline u32 sun6i_spi_read(struct sun6i_spi *sspi, u32 reg)
{
return readl(sspi->base_addr + reg);
}
static inline void sun6i_spi_write(struct sun6i_spi *sspi, u32 reg, u32 value)
{
writel(value, sspi->base_addr + reg);
}
static inline void sun6i_spi_drain_fifo(struct sun6i_spi *sspi, int len)
{
u32 reg, cnt;
u8 byte;
/* See how much data is available */
reg = sun6i_spi_read(sspi, SUN6I_FIFO_STA_REG);
reg &= SUN6I_FIFO_STA_RF_CNT_MASK;
cnt = reg >> SUN6I_FIFO_STA_RF_CNT_BITS;
if (len > cnt)
len = cnt;
while (len--) {
byte = readb(sspi->base_addr + SUN6I_RXDATA_REG);
if (sspi->rx_buf)
*sspi->rx_buf++ = byte;
}
}
static inline void sun6i_spi_fill_fifo(struct sun6i_spi *sspi, int len)
{
u8 byte;
if (len > sspi->len)
len = sspi->len;
while (len--) {
byte = sspi->tx_buf ? *sspi->tx_buf++ : 0;
writeb(byte, sspi->base_addr + SUN6I_TXDATA_REG);
sspi->len--;
}
}
static void sun6i_spi_set_cs(struct spi_device *spi, bool enable)
{
struct sun6i_spi *sspi = spi_master_get_devdata(spi->master);
u32 reg;
reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG);
reg &= ~SUN6I_TFR_CTL_CS_MASK;
reg |= SUN6I_TFR_CTL_CS(spi->chip_select);
if (enable)
reg |= SUN6I_TFR_CTL_CS_LEVEL;
else
reg &= ~SUN6I_TFR_CTL_CS_LEVEL;
sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg);
}
static int sun6i_spi_transfer_one(struct spi_master *master,
struct spi_device *spi,
struct spi_transfer *tfr)
{
struct sun6i_spi *sspi = spi_master_get_devdata(master);
unsigned int mclk_rate, div, timeout;
unsigned int tx_len = 0;
int ret = 0;
u32 reg;
/* We don't support transfer larger than the FIFO */
if (tfr->len > SUN6I_FIFO_DEPTH)
return -EINVAL;
reinit_completion(&sspi->done);
sspi->tx_buf = tfr->tx_buf;
sspi->rx_buf = tfr->rx_buf;
sspi->len = tfr->len;
/* Clear pending interrupts */
sun6i_spi_write(sspi, SUN6I_INT_STA_REG, ~0);
/* Reset FIFO */
sun6i_spi_write(sspi, SUN6I_FIFO_CTL_REG,
SUN6I_FIFO_CTL_RF_RST | SUN6I_FIFO_CTL_TF_RST);
/*
* Setup the transfer control register: Chip Select,
* polarities, etc.
*/
reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG);
if (spi->mode & SPI_CPOL)
reg |= SUN6I_TFR_CTL_CPOL;
else
reg &= ~SUN6I_TFR_CTL_CPOL;
if (spi->mode & SPI_CPHA)
reg |= SUN6I_TFR_CTL_CPHA;
else
reg &= ~SUN6I_TFR_CTL_CPHA;
if (spi->mode & SPI_LSB_FIRST)
reg |= SUN6I_TFR_CTL_FBS;
else
reg &= ~SUN6I_TFR_CTL_FBS;
/*
* If it's a TX only transfer, we don't want to fill the RX
* FIFO with bogus data
*/
if (sspi->rx_buf)
reg &= ~SUN6I_TFR_CTL_DHB;
else
reg |= SUN6I_TFR_CTL_DHB;
/* We want to control the chip select manually */
reg |= SUN6I_TFR_CTL_CS_MANUAL;
sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg);
/* Ensure that we have a parent clock fast enough */
mclk_rate = clk_get_rate(sspi->mclk);
if (mclk_rate < (2 * spi->max_speed_hz)) {
clk_set_rate(sspi->mclk, 2 * spi->max_speed_hz);
mclk_rate = clk_get_rate(sspi->mclk);
}
/*
* Setup clock divider.
*
* We have two choices there. Either we can use the clock
* divide rate 1, which is calculated thanks to this formula:
* SPI_CLK = MOD_CLK / (2 ^ cdr)
* Or we can use CDR2, which is calculated with the formula:
* SPI_CLK = MOD_CLK / (2 * (cdr + 1))
* Wether we use the former or the latter is set through the
* DRS bit.
*
* First try CDR2, and if we can't reach the expected
* frequency, fall back to CDR1.
*/
div = mclk_rate / (2 * spi->max_speed_hz);
if (div <= (SUN6I_CLK_CTL_CDR2_MASK + 1)) {
if (div > 0)
div--;
reg = SUN6I_CLK_CTL_CDR2(div) | SUN6I_CLK_CTL_DRS;
} else {
div = ilog2(mclk_rate) - ilog2(spi->max_speed_hz);
reg = SUN6I_CLK_CTL_CDR1(div);
}
sun6i_spi_write(sspi, SUN6I_CLK_CTL_REG, reg);
/* Setup the transfer now... */
if (sspi->tx_buf)
tx_len = tfr->len;
/* Setup the counters */
sun6i_spi_write(sspi, SUN6I_BURST_CNT_REG, SUN6I_BURST_CNT(tfr->len));
sun6i_spi_write(sspi, SUN6I_XMIT_CNT_REG, SUN6I_XMIT_CNT(tx_len));
sun6i_spi_write(sspi, SUN6I_BURST_CTL_CNT_REG,
SUN6I_BURST_CTL_CNT_STC(tx_len));
/* Fill the TX FIFO */
sun6i_spi_fill_fifo(sspi, SUN6I_FIFO_DEPTH);
/* Enable the interrupts */
sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, SUN6I_INT_CTL_TC);
/* Start the transfer */
reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG);
sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg | SUN6I_TFR_CTL_XCH);
timeout = wait_for_completion_timeout(&sspi->done,
msecs_to_jiffies(1000));
if (!timeout) {
ret = -ETIMEDOUT;
goto out;
}
sun6i_spi_drain_fifo(sspi, SUN6I_FIFO_DEPTH);
out:
sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, 0);
return ret;
}
static irqreturn_t sun6i_spi_handler(int irq, void *dev_id)
{
struct sun6i_spi *sspi = dev_id;
u32 status = sun6i_spi_read(sspi, SUN6I_INT_STA_REG);
/* Transfer complete */
if (status & SUN6I_INT_CTL_TC) {
sun6i_spi_write(sspi, SUN6I_INT_STA_REG, SUN6I_INT_CTL_TC);
complete(&sspi->done);
return IRQ_HANDLED;
}
return IRQ_NONE;
}
static int sun6i_spi_runtime_resume(struct device *dev)
{
struct spi_master *master = dev_get_drvdata(dev);
struct sun6i_spi *sspi = spi_master_get_devdata(master);
int ret;
ret = clk_prepare_enable(sspi->hclk);
if (ret) {
dev_err(dev, "Couldn't enable AHB clock\n");
goto out;
}
ret = clk_prepare_enable(sspi->mclk);
if (ret) {
dev_err(dev, "Couldn't enable module clock\n");
goto err;
}
ret = reset_control_deassert(sspi->rstc);
if (ret) {
dev_err(dev, "Couldn't deassert the device from reset\n");
goto err2;
}
sun6i_spi_write(sspi, SUN6I_GBL_CTL_REG,
SUN6I_GBL_CTL_BUS_ENABLE | SUN6I_GBL_CTL_MASTER | SUN6I_GBL_CTL_TP);
return 0;
err2:
clk_disable_unprepare(sspi->mclk);
err:
clk_disable_unprepare(sspi->hclk);
out:
return ret;
}
static int sun6i_spi_runtime_suspend(struct device *dev)
{
struct spi_master *master = dev_get_drvdata(dev);
struct sun6i_spi *sspi = spi_master_get_devdata(master);
reset_control_assert(sspi->rstc);
clk_disable_unprepare(sspi->mclk);
clk_disable_unprepare(sspi->hclk);
return 0;
}
static int sun6i_spi_probe(struct platform_device *pdev)
{
struct spi_master *master;
struct sun6i_spi *sspi;
struct resource *res;
int ret = 0, irq;
master = spi_alloc_master(&pdev->dev, sizeof(struct sun6i_spi));
if (!master) {
dev_err(&pdev->dev, "Unable to allocate SPI Master\n");
return -ENOMEM;
}
platform_set_drvdata(pdev, master);
sspi = spi_master_get_devdata(master);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
sspi->base_addr = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(sspi->base_addr)) {
ret = PTR_ERR(sspi->base_addr);
goto err_free_master;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "No spi IRQ specified\n");
ret = -ENXIO;
goto err_free_master;
}
ret = devm_request_irq(&pdev->dev, irq, sun6i_spi_handler,
0, "sun6i-spi", sspi);
if (ret) {
dev_err(&pdev->dev, "Cannot request IRQ\n");
goto err_free_master;
}
sspi->master = master;
master->set_cs = sun6i_spi_set_cs;
master->transfer_one = sun6i_spi_transfer_one;
master->num_chipselect = 4;
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST;
master->bits_per_word_mask = SPI_BPW_MASK(8);
master->dev.of_node = pdev->dev.of_node;
master->auto_runtime_pm = true;
sspi->hclk = devm_clk_get(&pdev->dev, "ahb");
if (IS_ERR(sspi->hclk)) {
dev_err(&pdev->dev, "Unable to acquire AHB clock\n");
ret = PTR_ERR(sspi->hclk);
goto err_free_master;
}
sspi->mclk = devm_clk_get(&pdev->dev, "mod");
if (IS_ERR(sspi->mclk)) {
dev_err(&pdev->dev, "Unable to acquire module clock\n");
ret = PTR_ERR(sspi->mclk);
goto err_free_master;
}
init_completion(&sspi->done);
sspi->rstc = devm_reset_control_get(&pdev->dev, NULL);
if (IS_ERR(sspi->rstc)) {
dev_err(&pdev->dev, "Couldn't get reset controller\n");
ret = PTR_ERR(sspi->rstc);
goto err_free_master;
}
/*
* This wake-up/shutdown pattern is to be able to have the
* device woken up, even if runtime_pm is disabled
*/
ret = sun6i_spi_runtime_resume(&pdev->dev);
if (ret) {
dev_err(&pdev->dev, "Couldn't resume the device\n");
goto err_free_master;
}
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
pm_runtime_idle(&pdev->dev);
ret = devm_spi_register_master(&pdev->dev, master);
if (ret) {
dev_err(&pdev->dev, "cannot register SPI master\n");
goto err_pm_disable;
}
return 0;
err_pm_disable:
pm_runtime_disable(&pdev->dev);
sun6i_spi_runtime_suspend(&pdev->dev);
err_free_master:
spi_master_put(master);
return ret;
}
static int sun6i_spi_remove(struct platform_device *pdev)
{
pm_runtime_disable(&pdev->dev);
return 0;
}
static const struct of_device_id sun6i_spi_match[] = {
{ .compatible = "allwinner,sun6i-a31-spi", },
{}
};
MODULE_DEVICE_TABLE(of, sun6i_spi_match);
static const struct dev_pm_ops sun6i_spi_pm_ops = {
.runtime_resume = sun6i_spi_runtime_resume,
.runtime_suspend = sun6i_spi_runtime_suspend,
};
static struct platform_driver sun6i_spi_driver = {
.probe = sun6i_spi_probe,
.remove = sun6i_spi_remove,
.driver = {
.name = "sun6i-spi",
.owner = THIS_MODULE,
.of_match_table = sun6i_spi_match,
.pm = &sun6i_spi_pm_ops,
},
};
module_platform_driver(sun6i_spi_driver);
MODULE_AUTHOR("Pan Nan <pannan@allwinnertech.com>");
MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
MODULE_DESCRIPTION("Allwinner A31 SPI controller driver");
MODULE_LICENSE("GPL");

Просмотреть файл

@ -846,8 +846,8 @@ complete_xfer:
SPI_COMMAND1);
tegra_spi_transfer_delay(xfer->delay_usecs);
goto exit;
} else if (msg->transfers.prev == &xfer->transfer_list) {
/* This is the last transfer in message */
} else if (list_is_last(&xfer->transfer_list,
&msg->transfers)) {
if (xfer->cs_change)
tspi->cs_control = spi;
else {

Просмотреть файл

@ -461,7 +461,6 @@ static int ti_qspi_probe(struct platform_device *pdev)
if (res_mmap == NULL) {
dev_err(&pdev->dev,
"memory mapped resource not required\n");
return -ENODEV;
}
}

Просмотреть файл

@ -1,378 +0,0 @@
/*
* Sequencer Serial Port (SSP) based SPI master driver
*
* Copyright (C) 2010 Texas Instruments Inc
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/mfd/ti_ssp.h>
#define MODE_BITS (SPI_CPHA | SPI_CPOL | SPI_CS_HIGH)
struct ti_ssp_spi {
struct spi_master *master;
struct device *dev;
spinlock_t lock;
struct list_head msg_queue;
struct completion complete;
bool shutdown;
struct workqueue_struct *workqueue;
struct work_struct work;
u8 mode, bpw;
int cs_active;
u32 pc_en, pc_dis, pc_wr, pc_rd;
void (*select)(int cs);
};
static u32 ti_ssp_spi_rx(struct ti_ssp_spi *hw)
{
u32 ret;
ti_ssp_run(hw->dev, hw->pc_rd, 0, &ret);
return ret;
}
static void ti_ssp_spi_tx(struct ti_ssp_spi *hw, u32 data)
{
ti_ssp_run(hw->dev, hw->pc_wr, data << (32 - hw->bpw), NULL);
}
static int ti_ssp_spi_txrx(struct ti_ssp_spi *hw, struct spi_message *msg,
struct spi_transfer *t)
{
int count;
if (hw->bpw <= 8) {
u8 *rx = t->rx_buf;
const u8 *tx = t->tx_buf;
for (count = 0; count < t->len; count += 1) {
if (t->tx_buf)
ti_ssp_spi_tx(hw, *tx++);
if (t->rx_buf)
*rx++ = ti_ssp_spi_rx(hw);
}
} else if (hw->bpw <= 16) {
u16 *rx = t->rx_buf;
const u16 *tx = t->tx_buf;
for (count = 0; count < t->len; count += 2) {
if (t->tx_buf)
ti_ssp_spi_tx(hw, *tx++);
if (t->rx_buf)
*rx++ = ti_ssp_spi_rx(hw);
}
} else {
u32 *rx = t->rx_buf;
const u32 *tx = t->tx_buf;
for (count = 0; count < t->len; count += 4) {
if (t->tx_buf)
ti_ssp_spi_tx(hw, *tx++);
if (t->rx_buf)
*rx++ = ti_ssp_spi_rx(hw);
}
}
msg->actual_length += count; /* bytes transferred */
dev_dbg(&msg->spi->dev, "xfer %s%s, %d bytes, %d bpw, count %d%s\n",
t->tx_buf ? "tx" : "", t->rx_buf ? "rx" : "", t->len,
hw->bpw, count, (count < t->len) ? " (under)" : "");
return (count < t->len) ? -EIO : 0; /* left over data */
}
static void ti_ssp_spi_chip_select(struct ti_ssp_spi *hw, int cs_active)
{
cs_active = !!cs_active;
if (cs_active == hw->cs_active)
return;
ti_ssp_run(hw->dev, cs_active ? hw->pc_en : hw->pc_dis, 0, NULL);
hw->cs_active = cs_active;
}
#define __SHIFT_OUT(bits) (SSP_OPCODE_SHIFT | SSP_OUT_MODE | \
cs_en | clk | SSP_COUNT((bits) * 2 - 1))
#define __SHIFT_IN(bits) (SSP_OPCODE_SHIFT | SSP_IN_MODE | \
cs_en | clk | SSP_COUNT((bits) * 2 - 1))
static int ti_ssp_spi_setup_transfer(struct ti_ssp_spi *hw, u8 bpw, u8 mode)
{
int error, idx = 0;
u32 seqram[16];
u32 cs_en, cs_dis, clk;
u32 topbits, botbits;
mode &= MODE_BITS;
if (mode == hw->mode && bpw == hw->bpw)
return 0;
cs_en = (mode & SPI_CS_HIGH) ? SSP_CS_HIGH : SSP_CS_LOW;
cs_dis = (mode & SPI_CS_HIGH) ? SSP_CS_LOW : SSP_CS_HIGH;
clk = (mode & SPI_CPOL) ? SSP_CLK_HIGH : SSP_CLK_LOW;
/* Construct instructions */
/* Disable Chip Select */
hw->pc_dis = idx;
seqram[idx++] = SSP_OPCODE_DIRECT | SSP_OUT_MODE | cs_dis | clk;
seqram[idx++] = SSP_OPCODE_STOP | SSP_OUT_MODE | cs_dis | clk;
/* Enable Chip Select */
hw->pc_en = idx;
seqram[idx++] = SSP_OPCODE_DIRECT | SSP_OUT_MODE | cs_en | clk;
seqram[idx++] = SSP_OPCODE_STOP | SSP_OUT_MODE | cs_en | clk;
/* Reads and writes need to be split for bpw > 16 */
topbits = (bpw > 16) ? 16 : bpw;
botbits = bpw - topbits;
/* Write */
hw->pc_wr = idx;
seqram[idx++] = __SHIFT_OUT(topbits) | SSP_ADDR_REG;
if (botbits)
seqram[idx++] = __SHIFT_OUT(botbits) | SSP_DATA_REG;
seqram[idx++] = SSP_OPCODE_STOP | SSP_OUT_MODE | cs_en | clk;
/* Read */
hw->pc_rd = idx;
if (botbits)
seqram[idx++] = __SHIFT_IN(botbits) | SSP_ADDR_REG;
seqram[idx++] = __SHIFT_IN(topbits) | SSP_DATA_REG;
seqram[idx++] = SSP_OPCODE_STOP | SSP_OUT_MODE | cs_en | clk;
error = ti_ssp_load(hw->dev, 0, seqram, idx);
if (error < 0)
return error;
error = ti_ssp_set_mode(hw->dev, ((mode & SPI_CPHA) ?
0 : SSP_EARLY_DIN));
if (error < 0)
return error;
hw->bpw = bpw;
hw->mode = mode;
return error;
}
static void ti_ssp_spi_work(struct work_struct *work)
{
struct ti_ssp_spi *hw = container_of(work, struct ti_ssp_spi, work);
spin_lock(&hw->lock);
while (!list_empty(&hw->msg_queue)) {
struct spi_message *m;
struct spi_device *spi;
struct spi_transfer *t = NULL;
int status = 0;
m = container_of(hw->msg_queue.next, struct spi_message,
queue);
list_del_init(&m->queue);
spin_unlock(&hw->lock);
spi = m->spi;
if (hw->select)
hw->select(spi->chip_select);
list_for_each_entry(t, &m->transfers, transfer_list) {
int bpw = spi->bits_per_word;
int xfer_status;
if (t->bits_per_word)
bpw = t->bits_per_word;
if (ti_ssp_spi_setup_transfer(hw, bpw, spi->mode) < 0)
break;
ti_ssp_spi_chip_select(hw, 1);
xfer_status = ti_ssp_spi_txrx(hw, m, t);
if (xfer_status < 0)
status = xfer_status;
if (t->delay_usecs)
udelay(t->delay_usecs);
if (t->cs_change)
ti_ssp_spi_chip_select(hw, 0);
}
ti_ssp_spi_chip_select(hw, 0);
m->status = status;
m->complete(m->context);
spin_lock(&hw->lock);
}
if (hw->shutdown)
complete(&hw->complete);
spin_unlock(&hw->lock);
}
static int ti_ssp_spi_transfer(struct spi_device *spi, struct spi_message *m)
{
struct ti_ssp_spi *hw;
struct spi_transfer *t;
int error = 0;
m->actual_length = 0;
m->status = -EINPROGRESS;
hw = spi_master_get_devdata(spi->master);
if (list_empty(&m->transfers) || !m->complete)
return -EINVAL;
list_for_each_entry(t, &m->transfers, transfer_list) {
if (t->len && !(t->rx_buf || t->tx_buf)) {
dev_err(&spi->dev, "invalid xfer, no buffer\n");
return -EINVAL;
}
if (t->len && t->rx_buf && t->tx_buf) {
dev_err(&spi->dev, "invalid xfer, full duplex\n");
return -EINVAL;
}
}
spin_lock(&hw->lock);
if (hw->shutdown) {
error = -ESHUTDOWN;
goto error_unlock;
}
list_add_tail(&m->queue, &hw->msg_queue);
queue_work(hw->workqueue, &hw->work);
error_unlock:
spin_unlock(&hw->lock);
return error;
}
static int ti_ssp_spi_probe(struct platform_device *pdev)
{
const struct ti_ssp_spi_data *pdata;
struct ti_ssp_spi *hw;
struct spi_master *master;
struct device *dev = &pdev->dev;
int error = 0;
pdata = dev_get_platdata(dev);
if (!pdata) {
dev_err(dev, "platform data not found\n");
return -EINVAL;
}
master = spi_alloc_master(dev, sizeof(struct ti_ssp_spi));
if (!master) {
dev_err(dev, "cannot allocate SPI master\n");
return -ENOMEM;
}
hw = spi_master_get_devdata(master);
platform_set_drvdata(pdev, hw);
hw->master = master;
hw->dev = dev;
hw->select = pdata->select;
spin_lock_init(&hw->lock);
init_completion(&hw->complete);
INIT_LIST_HEAD(&hw->msg_queue);
INIT_WORK(&hw->work, ti_ssp_spi_work);
hw->workqueue = create_singlethread_workqueue(dev_name(dev));
if (!hw->workqueue) {
error = -ENOMEM;
dev_err(dev, "work queue creation failed\n");
goto error_wq;
}
error = ti_ssp_set_iosel(hw->dev, pdata->iosel);
if (error < 0) {
dev_err(dev, "io setup failed\n");
goto error_iosel;
}
master->bus_num = pdev->id;
master->num_chipselect = pdata->num_cs;
master->mode_bits = MODE_BITS;
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32);
master->flags = SPI_MASTER_HALF_DUPLEX;
master->transfer = ti_ssp_spi_transfer;
error = spi_register_master(master);
if (error) {
dev_err(dev, "master registration failed\n");
goto error_reg;
}
return 0;
error_reg:
error_iosel:
destroy_workqueue(hw->workqueue);
error_wq:
spi_master_put(master);
return error;
}
static int ti_ssp_spi_remove(struct platform_device *pdev)
{
struct ti_ssp_spi *hw = platform_get_drvdata(pdev);
int error;
hw->shutdown = 1;
while (!list_empty(&hw->msg_queue)) {
error = wait_for_completion_interruptible(&hw->complete);
if (error < 0) {
hw->shutdown = 0;
return error;
}
}
destroy_workqueue(hw->workqueue);
spi_unregister_master(hw->master);
return 0;
}
static struct platform_driver ti_ssp_spi_driver = {
.probe = ti_ssp_spi_probe,
.remove = ti_ssp_spi_remove,
.driver = {
.name = "ti-ssp-spi",
.owner = THIS_MODULE,
},
};
module_platform_driver(ti_ssp_spi_driver);
MODULE_DESCRIPTION("SSP SPI Master");
MODULE_AUTHOR("Cyril Chemparathy");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:ti-ssp-spi");

Просмотреть файл

@ -332,7 +332,7 @@ static void pch_spi_handler_sub(struct pch_spi_data *data, u32 reg_spsr_val,
data->transfer_active = false;
wake_up(&data->wait);
} else {
dev_err(&data->master->dev,
dev_vdbg(&data->master->dev,
"%s : Transfer is not completed",
__func__);
}
@ -464,20 +464,6 @@ static void pch_spi_reset(struct spi_master *master)
pch_spi_writereg(master, PCH_SRST, 0x0);
}
static int pch_spi_setup(struct spi_device *pspi)
{
/* Check baud rate setting */
/* if baud rate of chip is greater than
max we can support,return error */
if ((pspi->max_speed_hz) > PCH_MAX_BAUDRATE)
pspi->max_speed_hz = PCH_MAX_BAUDRATE;
dev_dbg(&pspi->dev, "%s MODE = %x\n", __func__,
(pspi->mode) & (SPI_CPOL | SPI_CPHA));
return 0;
}
static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg)
{
@ -486,23 +472,6 @@ static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg)
int retval;
unsigned long flags;
/* validate spi message and baud rate */
if (unlikely(list_empty(&pmsg->transfers) == 1)) {
dev_err(&pspi->dev, "%s list empty\n", __func__);
retval = -EINVAL;
goto err_out;
}
if (unlikely(pspi->max_speed_hz == 0)) {
dev_err(&pspi->dev, "%s pch_spi_transfer maxspeed=%d\n",
__func__, pspi->max_speed_hz);
retval = -EINVAL;
goto err_out;
}
dev_dbg(&pspi->dev,
"%s Transfer List not empty. Transfer Speed is set.\n", __func__);
spin_lock_irqsave(&data->lock, flags);
/* validate Tx/Rx buffers and Transfer length */
list_for_each_entry(transfer, &pmsg->transfers, transfer_list) {
@ -523,10 +492,6 @@ static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg)
dev_dbg(&pspi->dev,
"%s Tx/Rx buffer valid. Transfer length valid\n",
__func__);
/* if baud rate has been specified validate the same */
if (transfer->speed_hz > PCH_MAX_BAUDRATE)
transfer->speed_hz = PCH_MAX_BAUDRATE;
}
spin_unlock_irqrestore(&data->lock, flags);
@ -1151,8 +1116,7 @@ static void pch_spi_handle_dma(struct pch_spi_data *data, int *bpw)
dma->nent = num;
dma->desc_tx = desc_tx;
dev_dbg(&data->master->dev, "\n%s:Pulling down SSN low - writing "
"0x2 to SSNXCR\n", __func__);
dev_dbg(&data->master->dev, "%s:Pulling down SSN low - writing 0x2 to SSNXCR\n", __func__);
spin_lock_irqsave(&data->lock, flags);
pch_spi_writereg(data->master, PCH_SSNXCR, SSN_LOW);
@ -1418,10 +1382,10 @@ static int pch_spi_pd_probe(struct platform_device *plat_dev)
/* initialize members of SPI master */
master->num_chipselect = PCH_MAX_CS;
master->setup = pch_spi_setup;
master->transfer = pch_spi_transfer;
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST;
master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16);
master->max_speed_hz = PCH_MAX_BAUDRATE;
data->board_dat = board_dat;
data->plat_dev = plat_dev;
@ -1605,8 +1569,7 @@ static struct platform_driver pch_spi_pd_driver = {
.resume = pch_spi_pd_resume
};
static int pch_spi_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
static int pch_spi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct pch_spi_board_data *board_dat;
struct platform_device *pd_dev = NULL;
@ -1676,6 +1639,8 @@ static int pch_spi_probe(struct pci_dev *pdev,
return 0;
err_platform_device:
while (--i >= 0)
platform_device_unregister(pd_dev_save->pd_save[i]);
pci_disable_device(pdev);
pci_enable_device:
pci_release_regions(pdev);

Просмотреть файл

@ -80,7 +80,6 @@ struct txx9spi {
void __iomem *membase;
int baseclk;
struct clk *clk;
u32 max_speed_hz, min_speed_hz;
int last_chipselect;
int last_chipselect_val;
};
@ -117,9 +116,7 @@ static int txx9spi_setup(struct spi_device *spi)
{
struct txx9spi *c = spi_master_get_devdata(spi->master);
if (!spi->max_speed_hz
|| spi->max_speed_hz > c->max_speed_hz
|| spi->max_speed_hz < c->min_speed_hz)
if (!spi->max_speed_hz)
return -EINVAL;
if (gpio_direction_output(spi->chip_select,
@ -309,15 +306,8 @@ static int txx9spi_transfer(struct spi_device *spi, struct spi_message *m)
/* check each transfer's parameters */
list_for_each_entry(t, &m->transfers, transfer_list) {
u32 speed_hz = t->speed_hz ? : spi->max_speed_hz;
u8 bits_per_word = t->bits_per_word;
if (!t->tx_buf && !t->rx_buf && t->len)
return -EINVAL;
if (t->len & ((bits_per_word >> 3) - 1))
return -EINVAL;
if (speed_hz < c->min_speed_hz || speed_hz > c->max_speed_hz)
return -EINVAL;
}
spin_lock_irqsave(&c->lock, flags);
@ -360,17 +350,12 @@ static int txx9spi_probe(struct platform_device *dev)
goto exit;
}
c->baseclk = clk_get_rate(c->clk);
c->min_speed_hz = DIV_ROUND_UP(c->baseclk, SPI_MAX_DIVIDER + 1);
c->max_speed_hz = c->baseclk / (SPI_MIN_DIVIDER + 1);
master->min_speed_hz = DIV_ROUND_UP(c->baseclk, SPI_MAX_DIVIDER + 1);
master->max_speed_hz = c->baseclk / (SPI_MIN_DIVIDER + 1);
res = platform_get_resource(dev, IORESOURCE_MEM, 0);
if (!res)
goto exit_busy;
if (!devm_request_mem_region(&dev->dev, res->start, resource_size(res),
"spi_txx9"))
goto exit_busy;
c->membase = devm_ioremap(&dev->dev, res->start, resource_size(res));
if (!c->membase)
c->membase = devm_ioremap_resource(&dev->dev, res);
if (IS_ERR(c->membase))
goto exit_busy;
/* enter config mode */

Просмотреть файл

@ -73,15 +73,13 @@ static void spi_xcomm_chipselect(struct spi_xcomm *spi_xcomm,
static int spi_xcomm_setup_transfer(struct spi_xcomm *spi_xcomm,
struct spi_device *spi, struct spi_transfer *t, unsigned int *settings)
{
unsigned int speed;
if (t->len > 62)
return -EINVAL;
speed = t->speed_hz ? t->speed_hz : spi->max_speed_hz;
if (t->speed_hz != spi_xcomm->current_speed) {
unsigned int divider;
if (speed != spi_xcomm->current_speed) {
unsigned int divider = DIV_ROUND_UP(SPI_XCOMM_CLOCK, speed);
divider = DIV_ROUND_UP(SPI_XCOMM_CLOCK, t->speed_hz);
if (divider >= 64)
*settings |= SPI_XCOMM_SETTINGS_CLOCK_DIV_64;
else if (divider >= 16)
@ -89,7 +87,7 @@ static int spi_xcomm_setup_transfer(struct spi_xcomm *spi_xcomm,
else
*settings |= SPI_XCOMM_SETTINGS_CLOCK_DIV_4;
spi_xcomm->current_speed = speed;
spi_xcomm->current_speed = t->speed_hz;
}
if (spi->mode & SPI_CPOL)
@ -147,8 +145,6 @@ static int spi_xcomm_transfer_one(struct spi_master *master,
int status = 0;
bool is_last;
is_first = true;
spi_xcomm_chipselect(spi_xcomm, spi, true);
list_for_each_entry(t, &msg->transfers, transfer_list) {

Просмотреть файл

@ -756,6 +756,7 @@ static int spi_transfer_one_message(struct spi_master *master,
struct spi_transfer *xfer;
bool keep_cs = false;
int ret = 0;
int ms = 1;
spi_set_cs(msg->spi, true);
@ -773,7 +774,16 @@ static int spi_transfer_one_message(struct spi_master *master,
if (ret > 0) {
ret = 0;
wait_for_completion(&master->xfer_completion);
ms = xfer->len * 8 * 1000 / xfer->speed_hz;
ms += 10; /* some tolerance */
ms = wait_for_completion_timeout(&master->xfer_completion,
msecs_to_jiffies(ms));
}
if (ms == 0) {
dev_err(&msg->spi->dev, "SPI transfer timed out\n");
msg->status = -ETIMEDOUT;
}
trace_spi_transfer_stop(msg, xfer);