Merge remote-tracking branches 'spi/topic/fsl-dspi', 'spi/topic/imx', 'spi/topic/mxs', 'spi/topic/omap-100k' and 'spi/topic/orion' into spi-next
This commit is contained in:
Коммит
899d81b974
|
@ -0,0 +1,47 @@
|
||||||
|
Device-Tree binding for regmap
|
||||||
|
|
||||||
|
The endianness mode of CPU & Device scenarios:
|
||||||
|
Index Device Endianness properties
|
||||||
|
---------------------------------------------------
|
||||||
|
1 BE 'big-endian'
|
||||||
|
2 LE 'little-endian'
|
||||||
|
|
||||||
|
For one device driver, which will run in different scenarios above
|
||||||
|
on different SoCs using the devicetree, we need one way to simplify
|
||||||
|
this.
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- {big,little}-endian: these are boolean properties, if absent
|
||||||
|
meaning that the CPU and the Device are in the same endianness mode,
|
||||||
|
these properties are for register values and all the buffers only.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
Scenario 1 : CPU in LE mode & device in LE mode.
|
||||||
|
dev: dev@40031000 {
|
||||||
|
compatible = "name";
|
||||||
|
reg = <0x40031000 0x1000>;
|
||||||
|
...
|
||||||
|
};
|
||||||
|
|
||||||
|
Scenario 2 : CPU in LE mode & device in BE mode.
|
||||||
|
dev: dev@40031000 {
|
||||||
|
compatible = "name";
|
||||||
|
reg = <0x40031000 0x1000>;
|
||||||
|
...
|
||||||
|
big-endian;
|
||||||
|
};
|
||||||
|
|
||||||
|
Scenario 3 : CPU in BE mode & device in BE mode.
|
||||||
|
dev: dev@40031000 {
|
||||||
|
compatible = "name";
|
||||||
|
reg = <0x40031000 0x1000>;
|
||||||
|
...
|
||||||
|
};
|
||||||
|
|
||||||
|
Scenario 4 : CPU in BE mode & device in LE mode.
|
||||||
|
dev: dev@40031000 {
|
||||||
|
compatible = "name";
|
||||||
|
reg = <0x40031000 0x1000>;
|
||||||
|
...
|
||||||
|
little-endian;
|
||||||
|
};
|
|
@ -7,6 +7,9 @@ Required properties:
|
||||||
- interrupts : Should contain CSPI/eCSPI interrupt
|
- interrupts : Should contain CSPI/eCSPI interrupt
|
||||||
- fsl,spi-num-chipselects : Contains the number of the chipselect
|
- fsl,spi-num-chipselects : Contains the number of the chipselect
|
||||||
- cs-gpios : Specifies the gpio pins to be used for chipselects.
|
- cs-gpios : Specifies the gpio pins to be used for chipselects.
|
||||||
|
- dmas: DMA specifiers for tx and rx dma. See the DMA client binding,
|
||||||
|
Documentation/devicetree/bindings/dma/dma.txt
|
||||||
|
- dma-names: DMA request names should include "tx" and "rx" if present.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
|
@ -19,4 +22,6 @@ ecspi@70010000 {
|
||||||
fsl,spi-num-chipselects = <2>;
|
fsl,spi-num-chipselects = <2>;
|
||||||
cs-gpios = <&gpio3 24 0>, /* GPIO3_24 */
|
cs-gpios = <&gpio3 24 0>, /* GPIO3_24 */
|
||||||
<&gpio3 25 0>; /* GPIO3_25 */
|
<&gpio3 25 0>; /* GPIO3_25 */
|
||||||
|
dmas = <&sdma 3 7 1>, <&sdma 4 7 2>;
|
||||||
|
dma-names = "rx", "tx";
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,7 +10,12 @@ Required properties:
|
||||||
- pinctrl-names: must contain a "default" entry.
|
- pinctrl-names: must contain a "default" entry.
|
||||||
- spi-num-chipselects : the number of the chipselect signals.
|
- spi-num-chipselects : the number of the chipselect signals.
|
||||||
- bus-num : the slave chip chipselect signal number.
|
- bus-num : the slave chip chipselect signal number.
|
||||||
- big-endian : if DSPI modudle is big endian, the bool will be set in node.
|
|
||||||
|
Optional property:
|
||||||
|
- big-endian: If present the dspi device's registers are implemented
|
||||||
|
in big endian mode, otherwise in native mode(same with CPU), for more
|
||||||
|
detail please see: Documentation/devicetree/bindings/regmap/regmap.txt.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
dspi0@4002c000 {
|
dspi0@4002c000 {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
Marvell Orion SPI device
|
Marvell Orion SPI device
|
||||||
|
|
||||||
Required properties:
|
Required properties:
|
||||||
- compatible : should be "marvell,orion-spi".
|
- compatible : should be "marvell,orion-spi" or "marvell,armada-370-spi".
|
||||||
- reg : offset and length of the register set for the device
|
- reg : offset and length of the register set for the device
|
||||||
- cell-index : Which of multiple SPI controllers is this.
|
- cell-index : Which of multiple SPI controllers is this.
|
||||||
Optional properties:
|
Optional properties:
|
||||||
|
|
|
@ -168,6 +168,8 @@ static struct regmap_bus regmap_i2c = {
|
||||||
.write = regmap_i2c_write,
|
.write = regmap_i2c_write,
|
||||||
.gather_write = regmap_i2c_gather_write,
|
.gather_write = regmap_i2c_gather_write,
|
||||||
.read = regmap_i2c_read,
|
.read = regmap_i2c_read,
|
||||||
|
.reg_format_endian_default = REGMAP_ENDIAN_BIG,
|
||||||
|
.val_format_endian_default = REGMAP_ENDIAN_BIG,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c,
|
static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c,
|
||||||
|
|
|
@ -109,6 +109,8 @@ static struct regmap_bus regmap_spi = {
|
||||||
.async_alloc = regmap_spi_async_alloc,
|
.async_alloc = regmap_spi_async_alloc,
|
||||||
.read = regmap_spi_read,
|
.read = regmap_spi_read,
|
||||||
.read_flag_mask = 0x80,
|
.read_flag_mask = 0x80,
|
||||||
|
.reg_format_endian_default = REGMAP_ENDIAN_BIG,
|
||||||
|
.val_format_endian_default = REGMAP_ENDIAN_BIG,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include <linux/export.h>
|
#include <linux/export.h>
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
|
#include <linux/of.h>
|
||||||
#include <linux/rbtree.h>
|
#include <linux/rbtree.h>
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
|
|
||||||
|
@ -448,6 +449,66 @@ int regmap_attach_dev(struct device *dev, struct regmap *map,
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(regmap_attach_dev);
|
EXPORT_SYMBOL_GPL(regmap_attach_dev);
|
||||||
|
|
||||||
|
static enum regmap_endian regmap_get_reg_endian(const struct regmap_bus *bus,
|
||||||
|
const struct regmap_config *config)
|
||||||
|
{
|
||||||
|
enum regmap_endian endian;
|
||||||
|
|
||||||
|
/* Retrieve the endianness specification from the regmap config */
|
||||||
|
endian = config->reg_format_endian;
|
||||||
|
|
||||||
|
/* If the regmap config specified a non-default value, use that */
|
||||||
|
if (endian != REGMAP_ENDIAN_DEFAULT)
|
||||||
|
return endian;
|
||||||
|
|
||||||
|
/* Retrieve the endianness specification from the bus config */
|
||||||
|
if (bus && bus->reg_format_endian_default)
|
||||||
|
endian = bus->reg_format_endian_default;
|
||||||
|
|
||||||
|
/* If the bus specified a non-default value, use that */
|
||||||
|
if (endian != REGMAP_ENDIAN_DEFAULT)
|
||||||
|
return endian;
|
||||||
|
|
||||||
|
/* Use this if no other value was found */
|
||||||
|
return REGMAP_ENDIAN_BIG;
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum regmap_endian regmap_get_val_endian(struct device *dev,
|
||||||
|
const struct regmap_bus *bus,
|
||||||
|
const struct regmap_config *config)
|
||||||
|
{
|
||||||
|
struct device_node *np = dev->of_node;
|
||||||
|
enum regmap_endian endian;
|
||||||
|
|
||||||
|
/* Retrieve the endianness specification from the regmap config */
|
||||||
|
endian = config->val_format_endian;
|
||||||
|
|
||||||
|
/* If the regmap config specified a non-default value, use that */
|
||||||
|
if (endian != REGMAP_ENDIAN_DEFAULT)
|
||||||
|
return endian;
|
||||||
|
|
||||||
|
/* Parse the device's DT node for an endianness specification */
|
||||||
|
if (of_property_read_bool(np, "big-endian"))
|
||||||
|
endian = REGMAP_ENDIAN_BIG;
|
||||||
|
else if (of_property_read_bool(np, "little-endian"))
|
||||||
|
endian = REGMAP_ENDIAN_LITTLE;
|
||||||
|
|
||||||
|
/* If the endianness was specified in DT, use that */
|
||||||
|
if (endian != REGMAP_ENDIAN_DEFAULT)
|
||||||
|
return endian;
|
||||||
|
|
||||||
|
/* Retrieve the endianness specification from the bus config */
|
||||||
|
if (bus && bus->val_format_endian_default)
|
||||||
|
endian = bus->val_format_endian_default;
|
||||||
|
|
||||||
|
/* If the bus specified a non-default value, use that */
|
||||||
|
if (endian != REGMAP_ENDIAN_DEFAULT)
|
||||||
|
return endian;
|
||||||
|
|
||||||
|
/* Use this if no other value was found */
|
||||||
|
return REGMAP_ENDIAN_BIG;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* regmap_init(): Initialise register map
|
* regmap_init(): Initialise register map
|
||||||
*
|
*
|
||||||
|
@ -551,17 +612,8 @@ struct regmap *regmap_init(struct device *dev,
|
||||||
map->reg_read = _regmap_bus_read;
|
map->reg_read = _regmap_bus_read;
|
||||||
}
|
}
|
||||||
|
|
||||||
reg_endian = config->reg_format_endian;
|
reg_endian = regmap_get_reg_endian(bus, config);
|
||||||
if (reg_endian == REGMAP_ENDIAN_DEFAULT)
|
val_endian = regmap_get_val_endian(dev, bus, config);
|
||||||
reg_endian = bus->reg_format_endian_default;
|
|
||||||
if (reg_endian == REGMAP_ENDIAN_DEFAULT)
|
|
||||||
reg_endian = REGMAP_ENDIAN_BIG;
|
|
||||||
|
|
||||||
val_endian = config->val_format_endian;
|
|
||||||
if (val_endian == REGMAP_ENDIAN_DEFAULT)
|
|
||||||
val_endian = bus->val_format_endian_default;
|
|
||||||
if (val_endian == REGMAP_ENDIAN_DEFAULT)
|
|
||||||
val_endian = REGMAP_ENDIAN_BIG;
|
|
||||||
|
|
||||||
switch (config->reg_bits + map->reg_shift) {
|
switch (config->reg_bits + map->reg_shift) {
|
||||||
case 2:
|
case 2:
|
||||||
|
|
|
@ -493,9 +493,6 @@ static int dspi_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
dspi_regmap_config.lock_arg = dspi;
|
dspi_regmap_config.lock_arg = dspi;
|
||||||
dspi_regmap_config.val_format_endian =
|
|
||||||
of_property_read_bool(np, "big-endian")
|
|
||||||
? REGMAP_ENDIAN_BIG : REGMAP_ENDIAN_DEFAULT;
|
|
||||||
dspi->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "dspi", base,
|
dspi->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "dspi", base,
|
||||||
&dspi_regmap_config);
|
&dspi_regmap_config);
|
||||||
if (IS_ERR(dspi->regmap)) {
|
if (IS_ERR(dspi->regmap)) {
|
||||||
|
@ -535,7 +532,6 @@ static int dspi_probe(struct platform_device *pdev)
|
||||||
goto out_clk_put;
|
goto out_clk_put;
|
||||||
}
|
}
|
||||||
|
|
||||||
pr_info(KERN_INFO "Freescale DSPI master initialized\n");
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
out_clk_put:
|
out_clk_put:
|
||||||
|
|
|
@ -21,6 +21,8 @@
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/completion.h>
|
#include <linux/completion.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
|
#include <linux/dmaengine.h>
|
||||||
|
#include <linux/dma-mapping.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
|
@ -37,6 +39,7 @@
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
#include <linux/of_gpio.h>
|
#include <linux/of_gpio.h>
|
||||||
|
|
||||||
|
#include <linux/platform_data/dma-imx.h>
|
||||||
#include <linux/platform_data/spi-imx.h>
|
#include <linux/platform_data/spi-imx.h>
|
||||||
|
|
||||||
#define DRIVER_NAME "spi_imx"
|
#define DRIVER_NAME "spi_imx"
|
||||||
|
@ -51,6 +54,9 @@
|
||||||
#define MXC_INT_RR (1 << 0) /* Receive data ready interrupt */
|
#define MXC_INT_RR (1 << 0) /* Receive data ready interrupt */
|
||||||
#define MXC_INT_TE (1 << 1) /* Transmit FIFO empty interrupt */
|
#define MXC_INT_TE (1 << 1) /* Transmit FIFO empty interrupt */
|
||||||
|
|
||||||
|
/* The maximum bytes that a sdma BD can transfer.*/
|
||||||
|
#define MAX_SDMA_BD_BYTES (1 << 15)
|
||||||
|
#define IMX_DMA_TIMEOUT (msecs_to_jiffies(3000))
|
||||||
struct spi_imx_config {
|
struct spi_imx_config {
|
||||||
unsigned int speed_hz;
|
unsigned int speed_hz;
|
||||||
unsigned int bpw;
|
unsigned int bpw;
|
||||||
|
@ -95,6 +101,16 @@ struct spi_imx_data {
|
||||||
const void *tx_buf;
|
const void *tx_buf;
|
||||||
unsigned int txfifo; /* number of words pushed in tx FIFO */
|
unsigned int txfifo; /* number of words pushed in tx FIFO */
|
||||||
|
|
||||||
|
/* DMA */
|
||||||
|
unsigned int dma_is_inited;
|
||||||
|
unsigned int dma_finished;
|
||||||
|
bool usedma;
|
||||||
|
u32 rx_wml;
|
||||||
|
u32 tx_wml;
|
||||||
|
u32 rxt_wml;
|
||||||
|
struct completion dma_rx_completion;
|
||||||
|
struct completion dma_tx_completion;
|
||||||
|
|
||||||
const struct spi_imx_devtype_data *devtype_data;
|
const struct spi_imx_devtype_data *devtype_data;
|
||||||
int chipselect[0];
|
int chipselect[0];
|
||||||
};
|
};
|
||||||
|
@ -181,9 +197,21 @@ static unsigned int spi_imx_clkdiv_2(unsigned int fin,
|
||||||
return 7;
|
return 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool spi_imx_can_dma(struct spi_master *master, struct spi_device *spi,
|
||||||
|
struct spi_transfer *transfer)
|
||||||
|
{
|
||||||
|
struct spi_imx_data *spi_imx = spi_master_get_devdata(master);
|
||||||
|
|
||||||
|
if (spi_imx->dma_is_inited && (transfer->len > spi_imx->rx_wml)
|
||||||
|
&& (transfer->len > spi_imx->tx_wml))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
#define MX51_ECSPI_CTRL 0x08
|
#define MX51_ECSPI_CTRL 0x08
|
||||||
#define MX51_ECSPI_CTRL_ENABLE (1 << 0)
|
#define MX51_ECSPI_CTRL_ENABLE (1 << 0)
|
||||||
#define MX51_ECSPI_CTRL_XCH (1 << 2)
|
#define MX51_ECSPI_CTRL_XCH (1 << 2)
|
||||||
|
#define MX51_ECSPI_CTRL_SMC (1 << 3)
|
||||||
#define MX51_ECSPI_CTRL_MODE_MASK (0xf << 4)
|
#define MX51_ECSPI_CTRL_MODE_MASK (0xf << 4)
|
||||||
#define MX51_ECSPI_CTRL_POSTDIV_OFFSET 8
|
#define MX51_ECSPI_CTRL_POSTDIV_OFFSET 8
|
||||||
#define MX51_ECSPI_CTRL_PREDIV_OFFSET 12
|
#define MX51_ECSPI_CTRL_PREDIV_OFFSET 12
|
||||||
|
@ -201,6 +229,18 @@ static unsigned int spi_imx_clkdiv_2(unsigned int fin,
|
||||||
#define MX51_ECSPI_INT_TEEN (1 << 0)
|
#define MX51_ECSPI_INT_TEEN (1 << 0)
|
||||||
#define MX51_ECSPI_INT_RREN (1 << 3)
|
#define MX51_ECSPI_INT_RREN (1 << 3)
|
||||||
|
|
||||||
|
#define MX51_ECSPI_DMA 0x14
|
||||||
|
#define MX51_ECSPI_DMA_TX_WML_OFFSET 0
|
||||||
|
#define MX51_ECSPI_DMA_TX_WML_MASK 0x3F
|
||||||
|
#define MX51_ECSPI_DMA_RX_WML_OFFSET 16
|
||||||
|
#define MX51_ECSPI_DMA_RX_WML_MASK (0x3F << 16)
|
||||||
|
#define MX51_ECSPI_DMA_RXT_WML_OFFSET 24
|
||||||
|
#define MX51_ECSPI_DMA_RXT_WML_MASK (0x3F << 24)
|
||||||
|
|
||||||
|
#define MX51_ECSPI_DMA_TEDEN_OFFSET 7
|
||||||
|
#define MX51_ECSPI_DMA_RXDEN_OFFSET 23
|
||||||
|
#define MX51_ECSPI_DMA_RXTDEN_OFFSET 31
|
||||||
|
|
||||||
#define MX51_ECSPI_STAT 0x18
|
#define MX51_ECSPI_STAT 0x18
|
||||||
#define MX51_ECSPI_STAT_RR (1 << 3)
|
#define MX51_ECSPI_STAT_RR (1 << 3)
|
||||||
|
|
||||||
|
@ -257,17 +297,22 @@ static void __maybe_unused mx51_ecspi_intctrl(struct spi_imx_data *spi_imx, int
|
||||||
|
|
||||||
static void __maybe_unused mx51_ecspi_trigger(struct spi_imx_data *spi_imx)
|
static void __maybe_unused mx51_ecspi_trigger(struct spi_imx_data *spi_imx)
|
||||||
{
|
{
|
||||||
u32 reg;
|
u32 reg = readl(spi_imx->base + MX51_ECSPI_CTRL);
|
||||||
|
|
||||||
reg = readl(spi_imx->base + MX51_ECSPI_CTRL);
|
if (!spi_imx->usedma)
|
||||||
reg |= MX51_ECSPI_CTRL_XCH;
|
reg |= MX51_ECSPI_CTRL_XCH;
|
||||||
|
else if (!spi_imx->dma_finished)
|
||||||
|
reg |= MX51_ECSPI_CTRL_SMC;
|
||||||
|
else
|
||||||
|
reg &= ~MX51_ECSPI_CTRL_SMC;
|
||||||
writel(reg, spi_imx->base + MX51_ECSPI_CTRL);
|
writel(reg, spi_imx->base + MX51_ECSPI_CTRL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx,
|
static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx,
|
||||||
struct spi_imx_config *config)
|
struct spi_imx_config *config)
|
||||||
{
|
{
|
||||||
u32 ctrl = MX51_ECSPI_CTRL_ENABLE, cfg = 0;
|
u32 ctrl = MX51_ECSPI_CTRL_ENABLE, cfg = 0, dma = 0;
|
||||||
|
u32 tx_wml_cfg, rx_wml_cfg, rxt_wml_cfg;
|
||||||
u32 clk = config->speed_hz, delay;
|
u32 clk = config->speed_hz, delay;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -319,6 +364,30 @@ static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx,
|
||||||
else /* SCLK is _very_ slow */
|
else /* SCLK is _very_ slow */
|
||||||
usleep_range(delay, delay + 10);
|
usleep_range(delay, delay + 10);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Configure the DMA register: setup the watermark
|
||||||
|
* and enable DMA request.
|
||||||
|
*/
|
||||||
|
if (spi_imx->dma_is_inited) {
|
||||||
|
dma = readl(spi_imx->base + MX51_ECSPI_DMA);
|
||||||
|
|
||||||
|
spi_imx->tx_wml = spi_imx_get_fifosize(spi_imx) / 2;
|
||||||
|
spi_imx->rx_wml = spi_imx_get_fifosize(spi_imx) / 2;
|
||||||
|
spi_imx->rxt_wml = spi_imx_get_fifosize(spi_imx) / 2;
|
||||||
|
rx_wml_cfg = spi_imx->rx_wml << MX51_ECSPI_DMA_RX_WML_OFFSET;
|
||||||
|
tx_wml_cfg = spi_imx->tx_wml << MX51_ECSPI_DMA_TX_WML_OFFSET;
|
||||||
|
rxt_wml_cfg = spi_imx->rxt_wml << MX51_ECSPI_DMA_RXT_WML_OFFSET;
|
||||||
|
dma = (dma & ~MX51_ECSPI_DMA_TX_WML_MASK
|
||||||
|
& ~MX51_ECSPI_DMA_RX_WML_MASK
|
||||||
|
& ~MX51_ECSPI_DMA_RXT_WML_MASK)
|
||||||
|
| rx_wml_cfg | tx_wml_cfg | rxt_wml_cfg
|
||||||
|
|(1 << MX51_ECSPI_DMA_TEDEN_OFFSET)
|
||||||
|
|(1 << MX51_ECSPI_DMA_RXDEN_OFFSET)
|
||||||
|
|(1 << MX51_ECSPI_DMA_RXTDEN_OFFSET);
|
||||||
|
|
||||||
|
writel(dma, spi_imx->base + MX51_ECSPI_DMA);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -730,7 +799,186 @@ static int spi_imx_setupxfer(struct spi_device *spi,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int spi_imx_transfer(struct spi_device *spi,
|
static void spi_imx_sdma_exit(struct spi_imx_data *spi_imx)
|
||||||
|
{
|
||||||
|
struct spi_master *master = spi_imx->bitbang.master;
|
||||||
|
|
||||||
|
if (master->dma_rx) {
|
||||||
|
dma_release_channel(master->dma_rx);
|
||||||
|
master->dma_rx = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (master->dma_tx) {
|
||||||
|
dma_release_channel(master->dma_tx);
|
||||||
|
master->dma_tx = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
spi_imx->dma_is_inited = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int spi_imx_sdma_init(struct device *dev, struct spi_imx_data *spi_imx,
|
||||||
|
struct spi_master *master,
|
||||||
|
const struct resource *res)
|
||||||
|
{
|
||||||
|
struct dma_slave_config slave_config = {};
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Prepare for TX DMA: */
|
||||||
|
master->dma_tx = dma_request_slave_channel(dev, "tx");
|
||||||
|
if (!master->dma_tx) {
|
||||||
|
dev_err(dev, "cannot get the TX DMA channel!\n");
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
slave_config.direction = DMA_MEM_TO_DEV;
|
||||||
|
slave_config.dst_addr = res->start + MXC_CSPITXDATA;
|
||||||
|
slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
|
||||||
|
slave_config.dst_maxburst = spi_imx_get_fifosize(spi_imx) / 2;
|
||||||
|
ret = dmaengine_slave_config(master->dma_tx, &slave_config);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "error in TX dma configuration.\n");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Prepare for RX : */
|
||||||
|
master->dma_rx = dma_request_slave_channel(dev, "rx");
|
||||||
|
if (!master->dma_rx) {
|
||||||
|
dev_dbg(dev, "cannot get the DMA channel.\n");
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
slave_config.direction = DMA_DEV_TO_MEM;
|
||||||
|
slave_config.src_addr = res->start + MXC_CSPIRXDATA;
|
||||||
|
slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
|
||||||
|
slave_config.src_maxburst = spi_imx_get_fifosize(spi_imx) / 2;
|
||||||
|
ret = dmaengine_slave_config(master->dma_rx, &slave_config);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "error in RX dma configuration.\n");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
init_completion(&spi_imx->dma_rx_completion);
|
||||||
|
init_completion(&spi_imx->dma_tx_completion);
|
||||||
|
master->can_dma = spi_imx_can_dma;
|
||||||
|
master->max_dma_len = MAX_SDMA_BD_BYTES;
|
||||||
|
spi_imx->bitbang.master->flags = SPI_MASTER_MUST_RX |
|
||||||
|
SPI_MASTER_MUST_TX;
|
||||||
|
spi_imx->dma_is_inited = 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
err:
|
||||||
|
spi_imx_sdma_exit(spi_imx);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void spi_imx_dma_rx_callback(void *cookie)
|
||||||
|
{
|
||||||
|
struct spi_imx_data *spi_imx = (struct spi_imx_data *)cookie;
|
||||||
|
|
||||||
|
complete(&spi_imx->dma_rx_completion);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void spi_imx_dma_tx_callback(void *cookie)
|
||||||
|
{
|
||||||
|
struct spi_imx_data *spi_imx = (struct spi_imx_data *)cookie;
|
||||||
|
|
||||||
|
complete(&spi_imx->dma_tx_completion);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int spi_imx_dma_transfer(struct spi_imx_data *spi_imx,
|
||||||
|
struct spi_transfer *transfer)
|
||||||
|
{
|
||||||
|
struct dma_async_tx_descriptor *desc_tx = NULL, *desc_rx = NULL;
|
||||||
|
int ret;
|
||||||
|
u32 dma;
|
||||||
|
int left;
|
||||||
|
struct spi_master *master = spi_imx->bitbang.master;
|
||||||
|
struct sg_table *tx = &transfer->tx_sg, *rx = &transfer->rx_sg;
|
||||||
|
|
||||||
|
if (tx) {
|
||||||
|
desc_tx = dmaengine_prep_slave_sg(master->dma_tx,
|
||||||
|
tx->sgl, tx->nents, DMA_TO_DEVICE,
|
||||||
|
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||||
|
if (!desc_tx)
|
||||||
|
goto no_dma;
|
||||||
|
|
||||||
|
desc_tx->callback = spi_imx_dma_tx_callback;
|
||||||
|
desc_tx->callback_param = (void *)spi_imx;
|
||||||
|
dmaengine_submit(desc_tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rx) {
|
||||||
|
desc_rx = dmaengine_prep_slave_sg(master->dma_rx,
|
||||||
|
rx->sgl, rx->nents, DMA_FROM_DEVICE,
|
||||||
|
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||||
|
if (!desc_rx)
|
||||||
|
goto no_dma;
|
||||||
|
|
||||||
|
desc_rx->callback = spi_imx_dma_rx_callback;
|
||||||
|
desc_rx->callback_param = (void *)spi_imx;
|
||||||
|
dmaengine_submit(desc_rx);
|
||||||
|
}
|
||||||
|
|
||||||
|
reinit_completion(&spi_imx->dma_rx_completion);
|
||||||
|
reinit_completion(&spi_imx->dma_tx_completion);
|
||||||
|
|
||||||
|
/* Trigger the cspi module. */
|
||||||
|
spi_imx->dma_finished = 0;
|
||||||
|
|
||||||
|
dma = readl(spi_imx->base + MX51_ECSPI_DMA);
|
||||||
|
dma = dma & (~MX51_ECSPI_DMA_RXT_WML_MASK);
|
||||||
|
/* Change RX_DMA_LENGTH trigger dma fetch tail data */
|
||||||
|
left = transfer->len % spi_imx->rxt_wml;
|
||||||
|
if (left)
|
||||||
|
writel(dma | (left << MX51_ECSPI_DMA_RXT_WML_OFFSET),
|
||||||
|
spi_imx->base + MX51_ECSPI_DMA);
|
||||||
|
spi_imx->devtype_data->trigger(spi_imx);
|
||||||
|
|
||||||
|
dma_async_issue_pending(master->dma_tx);
|
||||||
|
dma_async_issue_pending(master->dma_rx);
|
||||||
|
/* Wait SDMA to finish the data transfer.*/
|
||||||
|
ret = wait_for_completion_timeout(&spi_imx->dma_tx_completion,
|
||||||
|
IMX_DMA_TIMEOUT);
|
||||||
|
if (!ret) {
|
||||||
|
pr_warn("%s %s: I/O Error in DMA TX\n",
|
||||||
|
dev_driver_string(&master->dev),
|
||||||
|
dev_name(&master->dev));
|
||||||
|
dmaengine_terminate_all(master->dma_tx);
|
||||||
|
} else {
|
||||||
|
ret = wait_for_completion_timeout(&spi_imx->dma_rx_completion,
|
||||||
|
IMX_DMA_TIMEOUT);
|
||||||
|
if (!ret) {
|
||||||
|
pr_warn("%s %s: I/O Error in DMA RX\n",
|
||||||
|
dev_driver_string(&master->dev),
|
||||||
|
dev_name(&master->dev));
|
||||||
|
spi_imx->devtype_data->reset(spi_imx);
|
||||||
|
dmaengine_terminate_all(master->dma_rx);
|
||||||
|
}
|
||||||
|
writel(dma |
|
||||||
|
spi_imx->rxt_wml << MX51_ECSPI_DMA_RXT_WML_OFFSET,
|
||||||
|
spi_imx->base + MX51_ECSPI_DMA);
|
||||||
|
}
|
||||||
|
|
||||||
|
spi_imx->dma_finished = 1;
|
||||||
|
spi_imx->devtype_data->trigger(spi_imx);
|
||||||
|
|
||||||
|
if (!ret)
|
||||||
|
ret = -ETIMEDOUT;
|
||||||
|
else if (ret > 0)
|
||||||
|
ret = transfer->len;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
no_dma:
|
||||||
|
pr_warn_once("%s %s: DMA not available, falling back to PIO\n",
|
||||||
|
dev_driver_string(&master->dev),
|
||||||
|
dev_name(&master->dev));
|
||||||
|
return -EAGAIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int spi_imx_pio_transfer(struct spi_device *spi,
|
||||||
struct spi_transfer *transfer)
|
struct spi_transfer *transfer)
|
||||||
{
|
{
|
||||||
struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master);
|
struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master);
|
||||||
|
@ -751,6 +999,24 @@ static int spi_imx_transfer(struct spi_device *spi,
|
||||||
return transfer->len;
|
return transfer->len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int spi_imx_transfer(struct spi_device *spi,
|
||||||
|
struct spi_transfer *transfer)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master);
|
||||||
|
|
||||||
|
if (spi_imx->bitbang.master->can_dma &&
|
||||||
|
spi_imx_can_dma(spi_imx->bitbang.master, spi, transfer)) {
|
||||||
|
spi_imx->usedma = true;
|
||||||
|
ret = spi_imx_dma_transfer(spi_imx, transfer);
|
||||||
|
if (ret != -EAGAIN)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
spi_imx->usedma = false;
|
||||||
|
|
||||||
|
return spi_imx_pio_transfer(spi, transfer);
|
||||||
|
}
|
||||||
|
|
||||||
static int spi_imx_setup(struct spi_device *spi)
|
static int spi_imx_setup(struct spi_device *spi)
|
||||||
{
|
{
|
||||||
struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master);
|
struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master);
|
||||||
|
@ -911,6 +1177,13 @@ static int spi_imx_probe(struct platform_device *pdev)
|
||||||
goto out_put_per;
|
goto out_put_per;
|
||||||
|
|
||||||
spi_imx->spi_clk = clk_get_rate(spi_imx->clk_per);
|
spi_imx->spi_clk = clk_get_rate(spi_imx->clk_per);
|
||||||
|
/*
|
||||||
|
* Only validated on i.mx6 now, can remove the constrain if validated on
|
||||||
|
* other chips.
|
||||||
|
*/
|
||||||
|
if (spi_imx->devtype_data == &imx51_ecspi_devtype_data
|
||||||
|
&& spi_imx_sdma_init(&pdev->dev, spi_imx, master, res))
|
||||||
|
dev_err(&pdev->dev, "dma setup error,use pio instead\n");
|
||||||
|
|
||||||
spi_imx->devtype_data->reset(spi_imx);
|
spi_imx->devtype_data->reset(spi_imx);
|
||||||
|
|
||||||
|
@ -949,6 +1222,7 @@ static int spi_imx_remove(struct platform_device *pdev)
|
||||||
writel(0, spi_imx->base + MXC_CSPICTRL);
|
writel(0, spi_imx->base + MXC_CSPICTRL);
|
||||||
clk_unprepare(spi_imx->clk_ipg);
|
clk_unprepare(spi_imx->clk_ipg);
|
||||||
clk_unprepare(spi_imx->clk_per);
|
clk_unprepare(spi_imx->clk_per);
|
||||||
|
spi_imx_sdma_exit(spi_imx);
|
||||||
spi_master_put(master);
|
spi_master_put(master);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -85,7 +85,7 @@ static int mxs_spi_setup_transfer(struct spi_device *dev,
|
||||||
mxs_ssp_set_clk_rate(ssp, hz);
|
mxs_ssp_set_clk_rate(ssp, hz);
|
||||||
/*
|
/*
|
||||||
* Save requested rate, hz, rather than the actual rate,
|
* Save requested rate, hz, rather than the actual rate,
|
||||||
* ssp->clk_rate. Otherwise we would set the rate every trasfer
|
* ssp->clk_rate. Otherwise we would set the rate every transfer
|
||||||
* when the actual rate is not quite the same as requested rate.
|
* when the actual rate is not quite the same as requested rate.
|
||||||
*/
|
*/
|
||||||
spi->sck = hz;
|
spi->sck = hz;
|
||||||
|
|
|
@ -70,10 +70,6 @@
|
||||||
#define SPI_STATUS_WE (1UL << 1)
|
#define SPI_STATUS_WE (1UL << 1)
|
||||||
#define SPI_STATUS_RD (1UL << 0)
|
#define SPI_STATUS_RD (1UL << 0)
|
||||||
|
|
||||||
#define WRITE 0
|
|
||||||
#define READ 1
|
|
||||||
|
|
||||||
|
|
||||||
/* use PIO for small transfers, avoiding DMA setup/teardown overhead and
|
/* use PIO for small transfers, avoiding DMA setup/teardown overhead and
|
||||||
* cache operations; better heuristics consider wordsize and bitrate.
|
* cache operations; better heuristics consider wordsize and bitrate.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/sizes.h>
|
#include <linux/sizes.h>
|
||||||
#include <asm/unaligned.h>
|
#include <asm/unaligned.h>
|
||||||
|
@ -40,13 +41,27 @@
|
||||||
#define ORION_SPI_MODE_CPHA (1 << 12)
|
#define ORION_SPI_MODE_CPHA (1 << 12)
|
||||||
#define ORION_SPI_IF_8_16_BIT_MODE (1 << 5)
|
#define ORION_SPI_IF_8_16_BIT_MODE (1 << 5)
|
||||||
#define ORION_SPI_CLK_PRESCALE_MASK 0x1F
|
#define ORION_SPI_CLK_PRESCALE_MASK 0x1F
|
||||||
|
#define ARMADA_SPI_CLK_PRESCALE_MASK 0xDF
|
||||||
#define ORION_SPI_MODE_MASK (ORION_SPI_MODE_CPOL | \
|
#define ORION_SPI_MODE_MASK (ORION_SPI_MODE_CPOL | \
|
||||||
ORION_SPI_MODE_CPHA)
|
ORION_SPI_MODE_CPHA)
|
||||||
|
|
||||||
|
enum orion_spi_type {
|
||||||
|
ORION_SPI,
|
||||||
|
ARMADA_SPI,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct orion_spi_dev {
|
||||||
|
enum orion_spi_type typ;
|
||||||
|
unsigned int min_divisor;
|
||||||
|
unsigned int max_divisor;
|
||||||
|
u32 prescale_mask;
|
||||||
|
};
|
||||||
|
|
||||||
struct orion_spi {
|
struct orion_spi {
|
||||||
struct spi_master *master;
|
struct spi_master *master;
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
|
const struct orion_spi_dev *devdata;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline void __iomem *spi_reg(struct orion_spi *orion_spi, u32 reg)
|
static inline void __iomem *spi_reg(struct orion_spi *orion_spi, u32 reg)
|
||||||
|
@ -83,30 +98,66 @@ static int orion_spi_baudrate_set(struct spi_device *spi, unsigned int speed)
|
||||||
u32 prescale;
|
u32 prescale;
|
||||||
u32 reg;
|
u32 reg;
|
||||||
struct orion_spi *orion_spi;
|
struct orion_spi *orion_spi;
|
||||||
|
const struct orion_spi_dev *devdata;
|
||||||
|
|
||||||
orion_spi = spi_master_get_devdata(spi->master);
|
orion_spi = spi_master_get_devdata(spi->master);
|
||||||
|
devdata = orion_spi->devdata;
|
||||||
|
|
||||||
tclk_hz = clk_get_rate(orion_spi->clk);
|
tclk_hz = clk_get_rate(orion_spi->clk);
|
||||||
|
|
||||||
/*
|
if (devdata->typ == ARMADA_SPI) {
|
||||||
* the supported rates are: 4,6,8...30
|
unsigned int clk, spr, sppr, sppr2, err;
|
||||||
* round up as we look for equal or less speed
|
unsigned int best_spr, best_sppr, best_err;
|
||||||
*/
|
|
||||||
rate = DIV_ROUND_UP(tclk_hz, speed);
|
|
||||||
rate = roundup(rate, 2);
|
|
||||||
|
|
||||||
/* check if requested speed is too small */
|
best_err = speed;
|
||||||
if (rate > 30)
|
best_spr = 0;
|
||||||
return -EINVAL;
|
best_sppr = 0;
|
||||||
|
|
||||||
if (rate < 4)
|
/* Iterate over the valid range looking for best fit */
|
||||||
rate = 4;
|
for (sppr = 0; sppr < 8; sppr++) {
|
||||||
|
sppr2 = 0x1 << sppr;
|
||||||
|
|
||||||
/* Convert the rate to SPI clock divisor value. */
|
spr = tclk_hz / sppr2;
|
||||||
prescale = 0x10 + rate/2;
|
spr = DIV_ROUND_UP(spr, speed);
|
||||||
|
if ((spr == 0) || (spr > 15))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
clk = tclk_hz / (spr * sppr2);
|
||||||
|
err = speed - clk;
|
||||||
|
|
||||||
|
if (err < best_err) {
|
||||||
|
best_spr = spr;
|
||||||
|
best_sppr = sppr;
|
||||||
|
best_err = err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((best_sppr == 0) && (best_spr == 0))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
prescale = ((best_sppr & 0x6) << 5) |
|
||||||
|
((best_sppr & 0x1) << 4) | best_spr;
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* the supported rates are: 4,6,8...30
|
||||||
|
* round up as we look for equal or less speed
|
||||||
|
*/
|
||||||
|
rate = DIV_ROUND_UP(tclk_hz, speed);
|
||||||
|
rate = roundup(rate, 2);
|
||||||
|
|
||||||
|
/* check if requested speed is too small */
|
||||||
|
if (rate > 30)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (rate < 4)
|
||||||
|
rate = 4;
|
||||||
|
|
||||||
|
/* Convert the rate to SPI clock divisor value. */
|
||||||
|
prescale = 0x10 + rate/2;
|
||||||
|
}
|
||||||
|
|
||||||
reg = readl(spi_reg(orion_spi, ORION_SPI_IF_CONFIG_REG));
|
reg = readl(spi_reg(orion_spi, ORION_SPI_IF_CONFIG_REG));
|
||||||
reg = ((reg & ~ORION_SPI_CLK_PRESCALE_MASK) | prescale);
|
reg = ((reg & ~devdata->prescale_mask) | prescale);
|
||||||
writel(reg, spi_reg(orion_spi, ORION_SPI_IF_CONFIG_REG));
|
writel(reg, spi_reg(orion_spi, ORION_SPI_IF_CONFIG_REG));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -342,8 +393,31 @@ static int orion_spi_reset(struct orion_spi *orion_spi)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct orion_spi_dev orion_spi_dev_data = {
|
||||||
|
.typ = ORION_SPI,
|
||||||
|
.min_divisor = 4,
|
||||||
|
.max_divisor = 30,
|
||||||
|
.prescale_mask = ORION_SPI_CLK_PRESCALE_MASK,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct orion_spi_dev armada_spi_dev_data = {
|
||||||
|
.typ = ARMADA_SPI,
|
||||||
|
.min_divisor = 1,
|
||||||
|
.max_divisor = 1920,
|
||||||
|
.prescale_mask = ARMADA_SPI_CLK_PRESCALE_MASK,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct of_device_id orion_spi_of_match_table[] = {
|
||||||
|
{ .compatible = "marvell,orion-spi", .data = &orion_spi_dev_data, },
|
||||||
|
{ .compatible = "marvell,armada-370-spi", .data = &armada_spi_dev_data, },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, orion_spi_of_match_table);
|
||||||
|
|
||||||
static int orion_spi_probe(struct platform_device *pdev)
|
static int orion_spi_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
|
const struct of_device_id *of_id;
|
||||||
|
const struct orion_spi_dev *devdata;
|
||||||
struct spi_master *master;
|
struct spi_master *master;
|
||||||
struct orion_spi *spi;
|
struct orion_spi *spi;
|
||||||
struct resource *r;
|
struct resource *r;
|
||||||
|
@ -379,6 +453,10 @@ static int orion_spi_probe(struct platform_device *pdev)
|
||||||
spi = spi_master_get_devdata(master);
|
spi = spi_master_get_devdata(master);
|
||||||
spi->master = master;
|
spi->master = master;
|
||||||
|
|
||||||
|
of_id = of_match_device(orion_spi_of_match_table, &pdev->dev);
|
||||||
|
devdata = of_id->data;
|
||||||
|
spi->devdata = devdata;
|
||||||
|
|
||||||
spi->clk = devm_clk_get(&pdev->dev, NULL);
|
spi->clk = devm_clk_get(&pdev->dev, NULL);
|
||||||
if (IS_ERR(spi->clk)) {
|
if (IS_ERR(spi->clk)) {
|
||||||
status = PTR_ERR(spi->clk);
|
status = PTR_ERR(spi->clk);
|
||||||
|
@ -390,8 +468,8 @@ static int orion_spi_probe(struct platform_device *pdev)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
tclk_hz = clk_get_rate(spi->clk);
|
tclk_hz = clk_get_rate(spi->clk);
|
||||||
master->max_speed_hz = DIV_ROUND_UP(tclk_hz, 4);
|
master->max_speed_hz = DIV_ROUND_UP(tclk_hz, devdata->min_divisor);
|
||||||
master->min_speed_hz = DIV_ROUND_UP(tclk_hz, 30);
|
master->min_speed_hz = DIV_ROUND_UP(tclk_hz, devdata->max_divisor);
|
||||||
|
|
||||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
spi->base = devm_ioremap_resource(&pdev->dev, r);
|
spi->base = devm_ioremap_resource(&pdev->dev, r);
|
||||||
|
@ -470,12 +548,6 @@ static const struct dev_pm_ops orion_spi_pm_ops = {
|
||||||
NULL)
|
NULL)
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct of_device_id orion_spi_of_match_table[] = {
|
|
||||||
{ .compatible = "marvell,orion-spi", },
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
MODULE_DEVICE_TABLE(of, orion_spi_of_match_table);
|
|
||||||
|
|
||||||
static struct platform_driver orion_spi_driver = {
|
static struct platform_driver orion_spi_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = DRIVER_NAME,
|
.name = DRIVER_NAME,
|
||||||
|
|
Загрузка…
Ссылка в новой задаче