mirror of
https://github.com/rd-stuffs/msm-4.14.git
synced 2025-02-20 11:45:48 +08:00
Merge remote-tracking branches 'spi/topic/omap-uwire', 'spi/topic/omap100k', 'spi/topic/omap2', 'spi/topic/orion', 'spi/topic/pl022', 'spi/topic/qup', 'spi/topic/rspi' and 'spi/topic/s3c24xx' into spi-next
This commit is contained in:
parent
6e07b9179a
cbab80464f
d1c18caace
aca0924b48
cd2ac0c0cd
84a5dc41f6
00cce74d08
490c97747d
3806037120
commit
1752368064
85
Documentation/devicetree/bindings/spi/qcom,spi-qup.txt
Normal file
85
Documentation/devicetree/bindings/spi/qcom,spi-qup.txt
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
Qualcomm Universal Peripheral (QUP) Serial Peripheral Interface (SPI)
|
||||||
|
|
||||||
|
The QUP core is an AHB slave that provides a common data path (an output FIFO
|
||||||
|
and an input FIFO) for serial peripheral interface (SPI) mini-core.
|
||||||
|
|
||||||
|
SPI in master mode supports up to 50MHz, up to four chip selects, programmable
|
||||||
|
data path from 4 bits to 32 bits and numerous protocol variants.
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: Should contain "qcom,spi-qup-v2.1.1" or "qcom,spi-qup-v2.2.1"
|
||||||
|
- reg: Should contain base register location and length
|
||||||
|
- interrupts: Interrupt number used by this controller
|
||||||
|
|
||||||
|
- clocks: Should contain the core clock and the AHB clock.
|
||||||
|
- clock-names: Should be "core" for the core clock and "iface" for the
|
||||||
|
AHB clock.
|
||||||
|
|
||||||
|
- #address-cells: Number of cells required to define a chip select
|
||||||
|
address on the SPI bus. Should be set to 1.
|
||||||
|
- #size-cells: Should be zero.
|
||||||
|
|
||||||
|
Optional properties:
|
||||||
|
- spi-max-frequency: Specifies maximum SPI clock frequency,
|
||||||
|
Units - Hz. Definition as per
|
||||||
|
Documentation/devicetree/bindings/spi/spi-bus.txt
|
||||||
|
|
||||||
|
SPI slave nodes must be children of the SPI master node and can contain
|
||||||
|
properties described in Documentation/devicetree/bindings/spi/spi-bus.txt
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
spi_8: spi@f9964000 { /* BLSP2 QUP2 */
|
||||||
|
|
||||||
|
compatible = "qcom,spi-qup-v2";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
reg = <0xf9964000 0x1000>;
|
||||||
|
interrupts = <0 102 0>;
|
||||||
|
spi-max-frequency = <19200000>;
|
||||||
|
|
||||||
|
clocks = <&gcc GCC_BLSP2_QUP2_SPI_APPS_CLK>, <&gcc GCC_BLSP2_AHB_CLK>;
|
||||||
|
clock-names = "core", "iface";
|
||||||
|
|
||||||
|
pinctrl-names = "default";
|
||||||
|
pinctrl-0 = <&spi8_default>;
|
||||||
|
|
||||||
|
device@0 {
|
||||||
|
compatible = "arm,pl022-dummy";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <1>;
|
||||||
|
reg = <0>; /* Chip select 0 */
|
||||||
|
spi-max-frequency = <19200000>;
|
||||||
|
spi-cpol;
|
||||||
|
};
|
||||||
|
|
||||||
|
device@1 {
|
||||||
|
compatible = "arm,pl022-dummy";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <1>;
|
||||||
|
reg = <1>; /* Chip select 1 */
|
||||||
|
spi-max-frequency = <9600000>;
|
||||||
|
spi-cpha;
|
||||||
|
};
|
||||||
|
|
||||||
|
device@2 {
|
||||||
|
compatible = "arm,pl022-dummy";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <1>;
|
||||||
|
reg = <2>; /* Chip select 2 */
|
||||||
|
spi-max-frequency = <19200000>;
|
||||||
|
spi-cpol;
|
||||||
|
spi-cpha;
|
||||||
|
};
|
||||||
|
|
||||||
|
device@3 {
|
||||||
|
compatible = "arm,pl022-dummy";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <1>;
|
||||||
|
reg = <3>; /* Chip select 3 */
|
||||||
|
spi-max-frequency = <19200000>;
|
||||||
|
spi-cpol;
|
||||||
|
spi-cpha;
|
||||||
|
spi-cs-high;
|
||||||
|
};
|
||||||
|
};
|
61
Documentation/devicetree/bindings/spi/spi-rspi.txt
Normal file
61
Documentation/devicetree/bindings/spi/spi-rspi.txt
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
Device tree configuration for Renesas RSPI/QSPI driver
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible : For Renesas Serial Peripheral Interface on legacy SH:
|
||||||
|
"renesas,rspi-<soctype>", "renesas,rspi" as fallback.
|
||||||
|
For Renesas Serial Peripheral Interface on RZ/A1H:
|
||||||
|
"renesas,rspi-<soctype>", "renesas,rspi-rz" as fallback.
|
||||||
|
For Quad Serial Peripheral Interface on R-Car Gen2:
|
||||||
|
"renesas,qspi-<soctype>", "renesas,qspi" as fallback.
|
||||||
|
Examples with soctypes are:
|
||||||
|
- "renesas,rspi-sh7757" (SH)
|
||||||
|
- "renesas,rspi-r7s72100" (RZ/A1H)
|
||||||
|
- "renesas,qspi-r8a7790" (R-Car H2)
|
||||||
|
- "renesas,qspi-r8a7791" (R-Car M2)
|
||||||
|
- reg : Address start and address range size of the device
|
||||||
|
- interrupts : A list of interrupt-specifiers, one for each entry in
|
||||||
|
interrupt-names.
|
||||||
|
If interrupt-names is not present, an interrupt specifier
|
||||||
|
for a single muxed interrupt.
|
||||||
|
- interrupt-names : A list of interrupt names. Should contain (if present):
|
||||||
|
- "error" for SPEI,
|
||||||
|
- "rx" for SPRI,
|
||||||
|
- "tx" to SPTI,
|
||||||
|
- "mux" for a single muxed interrupt.
|
||||||
|
- interrupt-parent : The phandle for the interrupt controller that
|
||||||
|
services interrupts for this device.
|
||||||
|
- num-cs : Number of chip selects. Some RSPI cores have more than 1.
|
||||||
|
- #address-cells : Must be <1>
|
||||||
|
- #size-cells : Must be <0>
|
||||||
|
|
||||||
|
Optional properties:
|
||||||
|
- clocks : Must contain a reference to the functional clock.
|
||||||
|
|
||||||
|
Pinctrl properties might be needed, too. See
|
||||||
|
Documentation/devicetree/bindings/pinctrl/renesas,*.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
spi0: spi@e800c800 {
|
||||||
|
compatible = "renesas,rspi-r7s72100", "renesas,rspi-rz";
|
||||||
|
reg = <0xe800c800 0x24>;
|
||||||
|
interrupts = <0 238 IRQ_TYPE_LEVEL_HIGH>,
|
||||||
|
<0 239 IRQ_TYPE_LEVEL_HIGH>,
|
||||||
|
<0 240 IRQ_TYPE_LEVEL_HIGH>;
|
||||||
|
interrupt-names = "error", "rx", "tx";
|
||||||
|
interrupt-parent = <&gic>;
|
||||||
|
num-cs = <1>;
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
};
|
||||||
|
|
||||||
|
spi: spi@e6b10000 {
|
||||||
|
compatible = "renesas,qspi-r8a7791", "renesas,qspi";
|
||||||
|
reg = <0 0xe6b10000 0 0x2c>;
|
||||||
|
interrupt-parent = <&gic>;
|
||||||
|
interrupts = <0 184 IRQ_TYPE_LEVEL_HIGH>;
|
||||||
|
clocks = <&mstp9_clks R8A7791_CLK_QSPI_MOD>;
|
||||||
|
num-cs = <1>;
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
};
|
@ -1,6 +1,5 @@
|
|||||||
obj-$(CONFIG_PM) += sysfs.o generic_ops.o common.o qos.o
|
obj-$(CONFIG_PM) += sysfs.o generic_ops.o common.o qos.o runtime.o
|
||||||
obj-$(CONFIG_PM_SLEEP) += main.o wakeup.o
|
obj-$(CONFIG_PM_SLEEP) += main.o wakeup.o
|
||||||
obj-$(CONFIG_PM_RUNTIME) += runtime.o
|
|
||||||
obj-$(CONFIG_PM_TRACE_RTC) += trace.o
|
obj-$(CONFIG_PM_TRACE_RTC) += trace.o
|
||||||
obj-$(CONFIG_PM_OPP) += opp.o
|
obj-$(CONFIG_PM_OPP) += opp.o
|
||||||
obj-$(CONFIG_PM_GENERIC_DOMAINS) += domain.o domain_governor.o
|
obj-$(CONFIG_PM_GENERIC_DOMAINS) += domain.o domain_governor.o
|
||||||
|
@ -13,6 +13,43 @@
|
|||||||
#include <trace/events/rpm.h>
|
#include <trace/events/rpm.h>
|
||||||
#include "power.h"
|
#include "power.h"
|
||||||
|
|
||||||
|
#define RPM_GET_CALLBACK(dev, cb) \
|
||||||
|
({ \
|
||||||
|
int (*__rpm_cb)(struct device *__d); \
|
||||||
|
\
|
||||||
|
if (dev->pm_domain) \
|
||||||
|
__rpm_cb = dev->pm_domain->ops.cb; \
|
||||||
|
else if (dev->type && dev->type->pm) \
|
||||||
|
__rpm_cb = dev->type->pm->cb; \
|
||||||
|
else if (dev->class && dev->class->pm) \
|
||||||
|
__rpm_cb = dev->class->pm->cb; \
|
||||||
|
else if (dev->bus && dev->bus->pm) \
|
||||||
|
__rpm_cb = dev->bus->pm->cb; \
|
||||||
|
else \
|
||||||
|
__rpm_cb = NULL; \
|
||||||
|
\
|
||||||
|
if (!__rpm_cb && dev->driver && dev->driver->pm) \
|
||||||
|
__rpm_cb = dev->driver->pm->cb; \
|
||||||
|
\
|
||||||
|
__rpm_cb; \
|
||||||
|
})
|
||||||
|
|
||||||
|
static int (*rpm_get_suspend_cb(struct device *dev))(struct device *)
|
||||||
|
{
|
||||||
|
return RPM_GET_CALLBACK(dev, runtime_suspend);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int (*rpm_get_resume_cb(struct device *dev))(struct device *)
|
||||||
|
{
|
||||||
|
return RPM_GET_CALLBACK(dev, runtime_resume);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM_RUNTIME
|
||||||
|
static int (*rpm_get_idle_cb(struct device *dev))(struct device *)
|
||||||
|
{
|
||||||
|
return RPM_GET_CALLBACK(dev, runtime_idle);
|
||||||
|
}
|
||||||
|
|
||||||
static int rpm_resume(struct device *dev, int rpmflags);
|
static int rpm_resume(struct device *dev, int rpmflags);
|
||||||
static int rpm_suspend(struct device *dev, int rpmflags);
|
static int rpm_suspend(struct device *dev, int rpmflags);
|
||||||
|
|
||||||
@ -310,19 +347,7 @@ static int rpm_idle(struct device *dev, int rpmflags)
|
|||||||
|
|
||||||
dev->power.idle_notification = true;
|
dev->power.idle_notification = true;
|
||||||
|
|
||||||
if (dev->pm_domain)
|
callback = rpm_get_idle_cb(dev);
|
||||||
callback = dev->pm_domain->ops.runtime_idle;
|
|
||||||
else if (dev->type && dev->type->pm)
|
|
||||||
callback = dev->type->pm->runtime_idle;
|
|
||||||
else if (dev->class && dev->class->pm)
|
|
||||||
callback = dev->class->pm->runtime_idle;
|
|
||||||
else if (dev->bus && dev->bus->pm)
|
|
||||||
callback = dev->bus->pm->runtime_idle;
|
|
||||||
else
|
|
||||||
callback = NULL;
|
|
||||||
|
|
||||||
if (!callback && dev->driver && dev->driver->pm)
|
|
||||||
callback = dev->driver->pm->runtime_idle;
|
|
||||||
|
|
||||||
if (callback)
|
if (callback)
|
||||||
retval = __rpm_callback(callback, dev);
|
retval = __rpm_callback(callback, dev);
|
||||||
@ -492,19 +517,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)
|
|||||||
|
|
||||||
__update_runtime_status(dev, RPM_SUSPENDING);
|
__update_runtime_status(dev, RPM_SUSPENDING);
|
||||||
|
|
||||||
if (dev->pm_domain)
|
callback = rpm_get_suspend_cb(dev);
|
||||||
callback = dev->pm_domain->ops.runtime_suspend;
|
|
||||||
else if (dev->type && dev->type->pm)
|
|
||||||
callback = dev->type->pm->runtime_suspend;
|
|
||||||
else if (dev->class && dev->class->pm)
|
|
||||||
callback = dev->class->pm->runtime_suspend;
|
|
||||||
else if (dev->bus && dev->bus->pm)
|
|
||||||
callback = dev->bus->pm->runtime_suspend;
|
|
||||||
else
|
|
||||||
callback = NULL;
|
|
||||||
|
|
||||||
if (!callback && dev->driver && dev->driver->pm)
|
|
||||||
callback = dev->driver->pm->runtime_suspend;
|
|
||||||
|
|
||||||
retval = rpm_callback(callback, dev);
|
retval = rpm_callback(callback, dev);
|
||||||
if (retval)
|
if (retval)
|
||||||
@ -724,19 +737,7 @@ static int rpm_resume(struct device *dev, int rpmflags)
|
|||||||
|
|
||||||
__update_runtime_status(dev, RPM_RESUMING);
|
__update_runtime_status(dev, RPM_RESUMING);
|
||||||
|
|
||||||
if (dev->pm_domain)
|
callback = rpm_get_resume_cb(dev);
|
||||||
callback = dev->pm_domain->ops.runtime_resume;
|
|
||||||
else if (dev->type && dev->type->pm)
|
|
||||||
callback = dev->type->pm->runtime_resume;
|
|
||||||
else if (dev->class && dev->class->pm)
|
|
||||||
callback = dev->class->pm->runtime_resume;
|
|
||||||
else if (dev->bus && dev->bus->pm)
|
|
||||||
callback = dev->bus->pm->runtime_resume;
|
|
||||||
else
|
|
||||||
callback = NULL;
|
|
||||||
|
|
||||||
if (!callback && dev->driver && dev->driver->pm)
|
|
||||||
callback = dev->driver->pm->runtime_resume;
|
|
||||||
|
|
||||||
retval = rpm_callback(callback, dev);
|
retval = rpm_callback(callback, dev);
|
||||||
if (retval) {
|
if (retval) {
|
||||||
@ -1401,3 +1402,86 @@ void pm_runtime_remove(struct device *dev)
|
|||||||
if (dev->power.irq_safe && dev->parent)
|
if (dev->power.irq_safe && dev->parent)
|
||||||
pm_runtime_put(dev->parent);
|
pm_runtime_put(dev->parent);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pm_runtime_force_suspend - Force a device into suspend state if needed.
|
||||||
|
* @dev: Device to suspend.
|
||||||
|
*
|
||||||
|
* Disable runtime PM so we safely can check the device's runtime PM status and
|
||||||
|
* if it is active, invoke it's .runtime_suspend callback to bring it into
|
||||||
|
* suspend state. Keep runtime PM disabled to preserve the state unless we
|
||||||
|
* encounter errors.
|
||||||
|
*
|
||||||
|
* Typically this function may be invoked from a system suspend callback to make
|
||||||
|
* sure the device is put into low power state.
|
||||||
|
*/
|
||||||
|
int pm_runtime_force_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
int (*callback)(struct device *);
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
pm_runtime_disable(dev);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note that pm_runtime_status_suspended() returns false while
|
||||||
|
* !CONFIG_PM_RUNTIME, which means the device will be put into low
|
||||||
|
* power state.
|
||||||
|
*/
|
||||||
|
if (pm_runtime_status_suspended(dev))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
callback = rpm_get_suspend_cb(dev);
|
||||||
|
|
||||||
|
if (!callback) {
|
||||||
|
ret = -ENOSYS;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = callback(dev);
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
pm_runtime_set_suspended(dev);
|
||||||
|
return 0;
|
||||||
|
err:
|
||||||
|
pm_runtime_enable(dev);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(pm_runtime_force_suspend);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pm_runtime_force_resume - Force a device into resume state.
|
||||||
|
* @dev: Device to resume.
|
||||||
|
*
|
||||||
|
* Prior invoking this function we expect the user to have brought the device
|
||||||
|
* into low power state by a call to pm_runtime_force_suspend(). Here we reverse
|
||||||
|
* those actions and brings the device into full power. We update the runtime PM
|
||||||
|
* status and re-enables runtime PM.
|
||||||
|
*
|
||||||
|
* Typically this function may be invoked from a system resume callback to make
|
||||||
|
* sure the device is put into full power state.
|
||||||
|
*/
|
||||||
|
int pm_runtime_force_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
int (*callback)(struct device *);
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
callback = rpm_get_resume_cb(dev);
|
||||||
|
|
||||||
|
if (!callback) {
|
||||||
|
ret = -ENOSYS;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = callback(dev);
|
||||||
|
if (ret)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
pm_runtime_set_active(dev);
|
||||||
|
pm_runtime_mark_last_busy(dev);
|
||||||
|
out:
|
||||||
|
pm_runtime_enable(dev);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(pm_runtime_force_resume);
|
||||||
|
@ -307,7 +307,7 @@ config SPI_OMAP_UWIRE
|
|||||||
|
|
||||||
config SPI_OMAP24XX
|
config SPI_OMAP24XX
|
||||||
tristate "McSPI driver for OMAP"
|
tristate "McSPI driver for OMAP"
|
||||||
depends on ARM || ARM64 || AVR32 || HEXAGON || MIPS || SH
|
depends on ARM || ARM64 || AVR32 || HEXAGON || MIPS || SUPERH
|
||||||
depends on ARCH_OMAP2PLUS || COMPILE_TEST
|
depends on ARCH_OMAP2PLUS || COMPILE_TEST
|
||||||
help
|
help
|
||||||
SPI master controller for OMAP24XX and later Multichannel SPI
|
SPI master controller for OMAP24XX and later Multichannel SPI
|
||||||
@ -381,6 +381,19 @@ config SPI_RSPI
|
|||||||
help
|
help
|
||||||
SPI driver for Renesas RSPI and QSPI blocks.
|
SPI driver for Renesas RSPI and QSPI blocks.
|
||||||
|
|
||||||
|
config SPI_QUP
|
||||||
|
tristate "Qualcomm SPI controller with QUP interface"
|
||||||
|
depends on ARCH_MSM_DT || (ARM && COMPILE_TEST)
|
||||||
|
help
|
||||||
|
Qualcomm Universal Peripheral (QUP) core is an AHB slave that
|
||||||
|
provides a common data path (an output FIFO and an input FIFO)
|
||||||
|
for serial peripheral interface (SPI) mini-core. SPI in master
|
||||||
|
mode supports up to 50MHz, up to four chip selects, programmable
|
||||||
|
data path from 4 bits to 32 bits and numerous protocol variants.
|
||||||
|
|
||||||
|
This driver can also be built as a module. If so, the module
|
||||||
|
will be called spi_qup.
|
||||||
|
|
||||||
config SPI_S3C24XX
|
config SPI_S3C24XX
|
||||||
tristate "Samsung S3C24XX series SPI"
|
tristate "Samsung S3C24XX series SPI"
|
||||||
depends on ARCH_S3C24XX
|
depends on ARCH_S3C24XX
|
||||||
|
@ -59,6 +59,7 @@ spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_PXADMA) += spi-pxa2xx-pxadma.o
|
|||||||
spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_DMA) += spi-pxa2xx-dma.o
|
spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_DMA) += spi-pxa2xx-dma.o
|
||||||
obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx-platform.o
|
obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx-platform.o
|
||||||
obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o
|
obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o
|
||||||
|
obj-$(CONFIG_SPI_QUP) += spi-qup.o
|
||||||
obj-$(CONFIG_SPI_RSPI) += spi-rspi.o
|
obj-$(CONFIG_SPI_RSPI) += spi-rspi.o
|
||||||
obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o
|
obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o
|
||||||
spi-s3c24xx-hw-y := spi-s3c24xx.o
|
spi-s3c24xx-hw-y := spi-s3c24xx.o
|
||||||
|
@ -83,15 +83,11 @@
|
|||||||
#define SPI_SHUTDOWN 1
|
#define SPI_SHUTDOWN 1
|
||||||
|
|
||||||
struct omap1_spi100k {
|
struct omap1_spi100k {
|
||||||
struct spi_master *master;
|
|
||||||
struct clk *ick;
|
struct clk *ick;
|
||||||
struct clk *fck;
|
struct clk *fck;
|
||||||
|
|
||||||
/* Virtual base address of the controller */
|
/* Virtual base address of the controller */
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
|
|
||||||
/* State of the SPI */
|
|
||||||
unsigned int state;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct omap1_spi100k_cs {
|
struct omap1_spi100k_cs {
|
||||||
@ -99,13 +95,6 @@ struct omap1_spi100k_cs {
|
|||||||
int word_len;
|
int word_len;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define MOD_REG_BIT(val, mask, set) do { \
|
|
||||||
if (set) \
|
|
||||||
val |= mask; \
|
|
||||||
else \
|
|
||||||
val &= ~mask; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
static void spi100k_enable_clock(struct spi_master *master)
|
static void spi100k_enable_clock(struct spi_master *master)
|
||||||
{
|
{
|
||||||
unsigned int val;
|
unsigned int val;
|
||||||
@ -139,7 +128,7 @@ static void spi100k_write_data(struct spi_master *master, int len, int data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
spi100k_enable_clock(master);
|
spi100k_enable_clock(master);
|
||||||
writew( data , spi100k->base + SPI_TX_MSB);
|
writew(data , spi100k->base + SPI_TX_MSB);
|
||||||
|
|
||||||
writew(SPI_CTRL_SEN(0) |
|
writew(SPI_CTRL_SEN(0) |
|
||||||
SPI_CTRL_WORD_SIZE(len) |
|
SPI_CTRL_WORD_SIZE(len) |
|
||||||
@ -147,7 +136,8 @@ static void spi100k_write_data(struct spi_master *master, int len, int data)
|
|||||||
spi100k->base + SPI_CTRL);
|
spi100k->base + SPI_CTRL);
|
||||||
|
|
||||||
/* Wait for bit ack send change */
|
/* Wait for bit ack send change */
|
||||||
while((readw(spi100k->base + SPI_STATUS) & SPI_STATUS_WE) != SPI_STATUS_WE);
|
while ((readw(spi100k->base + SPI_STATUS) & SPI_STATUS_WE) != SPI_STATUS_WE)
|
||||||
|
;
|
||||||
udelay(1000);
|
udelay(1000);
|
||||||
|
|
||||||
spi100k_disable_clock(master);
|
spi100k_disable_clock(master);
|
||||||
@ -155,7 +145,7 @@ static void spi100k_write_data(struct spi_master *master, int len, int data)
|
|||||||
|
|
||||||
static int spi100k_read_data(struct spi_master *master, int len)
|
static int spi100k_read_data(struct spi_master *master, int len)
|
||||||
{
|
{
|
||||||
int dataH,dataL;
|
int dataH, dataL;
|
||||||
struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
|
struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
|
||||||
|
|
||||||
/* Always do at least 16 bits */
|
/* Always do at least 16 bits */
|
||||||
@ -168,7 +158,8 @@ static int spi100k_read_data(struct spi_master *master, int len)
|
|||||||
SPI_CTRL_RD,
|
SPI_CTRL_RD,
|
||||||
spi100k->base + SPI_CTRL);
|
spi100k->base + SPI_CTRL);
|
||||||
|
|
||||||
while((readw(spi100k->base + SPI_STATUS) & SPI_STATUS_RD) != SPI_STATUS_RD);
|
while ((readw(spi100k->base + SPI_STATUS) & SPI_STATUS_RD) != SPI_STATUS_RD)
|
||||||
|
;
|
||||||
udelay(1000);
|
udelay(1000);
|
||||||
|
|
||||||
dataL = readw(spi100k->base + SPI_RX_LSB);
|
dataL = readw(spi100k->base + SPI_RX_LSB);
|
||||||
@ -204,12 +195,10 @@ static void omap1_spi100k_force_cs(struct omap1_spi100k *spi100k, int enable)
|
|||||||
static unsigned
|
static unsigned
|
||||||
omap1_spi100k_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
|
omap1_spi100k_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
|
||||||
{
|
{
|
||||||
struct omap1_spi100k *spi100k;
|
|
||||||
struct omap1_spi100k_cs *cs = spi->controller_state;
|
struct omap1_spi100k_cs *cs = spi->controller_state;
|
||||||
unsigned int count, c;
|
unsigned int count, c;
|
||||||
int word_len;
|
int word_len;
|
||||||
|
|
||||||
spi100k = spi_master_get_devdata(spi->master);
|
|
||||||
count = xfer->len;
|
count = xfer->len;
|
||||||
c = count;
|
c = count;
|
||||||
word_len = cs->word_len;
|
word_len = cs->word_len;
|
||||||
@ -221,12 +210,12 @@ omap1_spi100k_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
|
|||||||
rx = xfer->rx_buf;
|
rx = xfer->rx_buf;
|
||||||
tx = xfer->tx_buf;
|
tx = xfer->tx_buf;
|
||||||
do {
|
do {
|
||||||
c-=1;
|
c -= 1;
|
||||||
if (xfer->tx_buf != NULL)
|
if (xfer->tx_buf != NULL)
|
||||||
spi100k_write_data(spi->master, word_len, *tx++);
|
spi100k_write_data(spi->master, word_len, *tx++);
|
||||||
if (xfer->rx_buf != NULL)
|
if (xfer->rx_buf != NULL)
|
||||||
*rx++ = spi100k_read_data(spi->master, word_len);
|
*rx++ = spi100k_read_data(spi->master, word_len);
|
||||||
} while(c);
|
} while (c);
|
||||||
} else if (word_len <= 16) {
|
} else if (word_len <= 16) {
|
||||||
u16 *rx;
|
u16 *rx;
|
||||||
const u16 *tx;
|
const u16 *tx;
|
||||||
@ -234,12 +223,12 @@ omap1_spi100k_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
|
|||||||
rx = xfer->rx_buf;
|
rx = xfer->rx_buf;
|
||||||
tx = xfer->tx_buf;
|
tx = xfer->tx_buf;
|
||||||
do {
|
do {
|
||||||
c-=2;
|
c -= 2;
|
||||||
if (xfer->tx_buf != NULL)
|
if (xfer->tx_buf != NULL)
|
||||||
spi100k_write_data(spi->master,word_len, *tx++);
|
spi100k_write_data(spi->master, word_len, *tx++);
|
||||||
if (xfer->rx_buf != NULL)
|
if (xfer->rx_buf != NULL)
|
||||||
*rx++ = spi100k_read_data(spi->master,word_len);
|
*rx++ = spi100k_read_data(spi->master, word_len);
|
||||||
} while(c);
|
} while (c);
|
||||||
} else if (word_len <= 32) {
|
} else if (word_len <= 32) {
|
||||||
u32 *rx;
|
u32 *rx;
|
||||||
const u32 *tx;
|
const u32 *tx;
|
||||||
@ -247,12 +236,12 @@ omap1_spi100k_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
|
|||||||
rx = xfer->rx_buf;
|
rx = xfer->rx_buf;
|
||||||
tx = xfer->tx_buf;
|
tx = xfer->tx_buf;
|
||||||
do {
|
do {
|
||||||
c-=4;
|
c -= 4;
|
||||||
if (xfer->tx_buf != NULL)
|
if (xfer->tx_buf != NULL)
|
||||||
spi100k_write_data(spi->master,word_len, *tx);
|
spi100k_write_data(spi->master, word_len, *tx);
|
||||||
if (xfer->rx_buf != NULL)
|
if (xfer->rx_buf != NULL)
|
||||||
*rx = spi100k_read_data(spi->master,word_len);
|
*rx = spi100k_read_data(spi->master, word_len);
|
||||||
} while(c);
|
} while (c);
|
||||||
}
|
}
|
||||||
return count - c;
|
return count - c;
|
||||||
}
|
}
|
||||||
@ -294,7 +283,7 @@ static int omap1_spi100k_setup(struct spi_device *spi)
|
|||||||
spi100k = spi_master_get_devdata(spi->master);
|
spi100k = spi_master_get_devdata(spi->master);
|
||||||
|
|
||||||
if (!cs) {
|
if (!cs) {
|
||||||
cs = kzalloc(sizeof *cs, GFP_KERNEL);
|
cs = devm_kzalloc(&spi->dev, sizeof(*cs), GFP_KERNEL);
|
||||||
if (!cs)
|
if (!cs)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
cs->base = spi100k->base + spi->chip_select * 0x14;
|
cs->base = spi100k->base + spi->chip_select * 0x14;
|
||||||
@ -411,14 +400,14 @@ static int omap1_spi100k_probe(struct platform_device *pdev)
|
|||||||
if (!pdev->id)
|
if (!pdev->id)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
master = spi_alloc_master(&pdev->dev, sizeof *spi100k);
|
master = spi_alloc_master(&pdev->dev, sizeof(*spi100k));
|
||||||
if (master == NULL) {
|
if (master == NULL) {
|
||||||
dev_dbg(&pdev->dev, "master allocation failed\n");
|
dev_dbg(&pdev->dev, "master allocation failed\n");
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pdev->id != -1)
|
if (pdev->id != -1)
|
||||||
master->bus_num = pdev->id;
|
master->bus_num = pdev->id;
|
||||||
|
|
||||||
master->setup = omap1_spi100k_setup;
|
master->setup = omap1_spi100k_setup;
|
||||||
master->transfer_one_message = omap1_spi100k_transfer_one_message;
|
master->transfer_one_message = omap1_spi100k_transfer_one_message;
|
||||||
@ -434,7 +423,6 @@ static int omap1_spi100k_probe(struct platform_device *pdev)
|
|||||||
platform_set_drvdata(pdev, master);
|
platform_set_drvdata(pdev, master);
|
||||||
|
|
||||||
spi100k = spi_master_get_devdata(master);
|
spi100k = spi_master_get_devdata(master);
|
||||||
spi100k->master = master;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The memory region base address is taken as the platform_data.
|
* The memory region base address is taken as the platform_data.
|
||||||
@ -461,8 +449,6 @@ static int omap1_spi100k_probe(struct platform_device *pdev)
|
|||||||
if (status < 0)
|
if (status < 0)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
spi100k->state = SPI_RUNNING;
|
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
|
@ -99,7 +99,6 @@ struct uwire_spi {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct uwire_state {
|
struct uwire_state {
|
||||||
unsigned bits_per_word;
|
|
||||||
unsigned div1_idx;
|
unsigned div1_idx;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -210,9 +209,8 @@ static void uwire_chipselect(struct spi_device *spi, int value)
|
|||||||
|
|
||||||
static int uwire_txrx(struct spi_device *spi, struct spi_transfer *t)
|
static int uwire_txrx(struct spi_device *spi, struct spi_transfer *t)
|
||||||
{
|
{
|
||||||
struct uwire_state *ust = spi->controller_state;
|
|
||||||
unsigned len = t->len;
|
unsigned len = t->len;
|
||||||
unsigned bits = ust->bits_per_word;
|
unsigned bits = t->bits_per_word ? : spi->bits_per_word;
|
||||||
unsigned bytes;
|
unsigned bytes;
|
||||||
u16 val, w;
|
u16 val, w;
|
||||||
int status = 0;
|
int status = 0;
|
||||||
@ -220,10 +218,6 @@ static int uwire_txrx(struct spi_device *spi, struct spi_transfer *t)
|
|||||||
if (!t->tx_buf && !t->rx_buf)
|
if (!t->tx_buf && !t->rx_buf)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* Microwire doesn't read and write concurrently */
|
|
||||||
if (t->tx_buf && t->rx_buf)
|
|
||||||
return -EPERM;
|
|
||||||
|
|
||||||
w = spi->chip_select << 10;
|
w = spi->chip_select << 10;
|
||||||
w |= CS_CMD;
|
w |= CS_CMD;
|
||||||
|
|
||||||
@ -322,7 +316,6 @@ static int uwire_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
|
|||||||
struct uwire_state *ust = spi->controller_state;
|
struct uwire_state *ust = spi->controller_state;
|
||||||
struct uwire_spi *uwire;
|
struct uwire_spi *uwire;
|
||||||
unsigned flags = 0;
|
unsigned flags = 0;
|
||||||
unsigned bits;
|
|
||||||
unsigned hz;
|
unsigned hz;
|
||||||
unsigned long rate;
|
unsigned long rate;
|
||||||
int div1_idx;
|
int div1_idx;
|
||||||
@ -332,17 +325,6 @@ static int uwire_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
|
|||||||
|
|
||||||
uwire = spi_master_get_devdata(spi->master);
|
uwire = spi_master_get_devdata(spi->master);
|
||||||
|
|
||||||
bits = spi->bits_per_word;
|
|
||||||
if (t != NULL && t->bits_per_word)
|
|
||||||
bits = t->bits_per_word;
|
|
||||||
|
|
||||||
if (bits > 16) {
|
|
||||||
pr_debug("%s: wordsize %d?\n", dev_name(&spi->dev), bits);
|
|
||||||
status = -ENODEV;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
ust->bits_per_word = bits;
|
|
||||||
|
|
||||||
/* mode 0..3, clock inverted separately;
|
/* mode 0..3, clock inverted separately;
|
||||||
* standard nCS signaling;
|
* standard nCS signaling;
|
||||||
* don't treat DI=high as "not ready"
|
* don't treat DI=high as "not ready"
|
||||||
@ -496,6 +478,7 @@ static int uwire_probe(struct platform_device *pdev)
|
|||||||
status = PTR_ERR(uwire->ck);
|
status = PTR_ERR(uwire->ck);
|
||||||
dev_dbg(&pdev->dev, "no functional clock?\n");
|
dev_dbg(&pdev->dev, "no functional clock?\n");
|
||||||
spi_master_put(master);
|
spi_master_put(master);
|
||||||
|
iounmap(uwire_base);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
clk_enable(uwire->ck);
|
clk_enable(uwire->ck);
|
||||||
@ -509,7 +492,7 @@ static int uwire_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
/* the spi->mode bits understood by this driver: */
|
/* the spi->mode bits understood by this driver: */
|
||||||
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
|
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
|
||||||
|
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 16);
|
||||||
master->flags = SPI_MASTER_HALF_DUPLEX;
|
master->flags = SPI_MASTER_HALF_DUPLEX;
|
||||||
|
|
||||||
master->bus_num = 2; /* "official" */
|
master->bus_num = 2; /* "official" */
|
||||||
|
@ -44,6 +44,7 @@
|
|||||||
#include <linux/platform_data/spi-omap2-mcspi.h>
|
#include <linux/platform_data/spi-omap2-mcspi.h>
|
||||||
|
|
||||||
#define OMAP2_MCSPI_MAX_FREQ 48000000
|
#define OMAP2_MCSPI_MAX_FREQ 48000000
|
||||||
|
#define OMAP2_MCSPI_MAX_DIVIDER 4096
|
||||||
#define OMAP2_MCSPI_MAX_FIFODEPTH 64
|
#define OMAP2_MCSPI_MAX_FIFODEPTH 64
|
||||||
#define OMAP2_MCSPI_MAX_FIFOWCNT 0xFFFF
|
#define OMAP2_MCSPI_MAX_FIFOWCNT 0xFFFF
|
||||||
#define SPI_AUTOSUSPEND_TIMEOUT 2000
|
#define SPI_AUTOSUSPEND_TIMEOUT 2000
|
||||||
@ -88,6 +89,7 @@
|
|||||||
#define OMAP2_MCSPI_CHCONF_FORCE BIT(20)
|
#define OMAP2_MCSPI_CHCONF_FORCE BIT(20)
|
||||||
#define OMAP2_MCSPI_CHCONF_FFET BIT(27)
|
#define OMAP2_MCSPI_CHCONF_FFET BIT(27)
|
||||||
#define OMAP2_MCSPI_CHCONF_FFER BIT(28)
|
#define OMAP2_MCSPI_CHCONF_FFER BIT(28)
|
||||||
|
#define OMAP2_MCSPI_CHCONF_CLKG BIT(29)
|
||||||
|
|
||||||
#define OMAP2_MCSPI_CHSTAT_RXS BIT(0)
|
#define OMAP2_MCSPI_CHSTAT_RXS BIT(0)
|
||||||
#define OMAP2_MCSPI_CHSTAT_TXS BIT(1)
|
#define OMAP2_MCSPI_CHSTAT_TXS BIT(1)
|
||||||
@ -95,6 +97,7 @@
|
|||||||
#define OMAP2_MCSPI_CHSTAT_TXFFE BIT(3)
|
#define OMAP2_MCSPI_CHSTAT_TXFFE BIT(3)
|
||||||
|
|
||||||
#define OMAP2_MCSPI_CHCTRL_EN BIT(0)
|
#define OMAP2_MCSPI_CHCTRL_EN BIT(0)
|
||||||
|
#define OMAP2_MCSPI_CHCTRL_EXTCLK_MASK (0xff << 8)
|
||||||
|
|
||||||
#define OMAP2_MCSPI_WAKEUPENABLE_WKEN BIT(0)
|
#define OMAP2_MCSPI_WAKEUPENABLE_WKEN BIT(0)
|
||||||
|
|
||||||
@ -148,7 +151,7 @@ struct omap2_mcspi_cs {
|
|||||||
int word_len;
|
int word_len;
|
||||||
struct list_head node;
|
struct list_head node;
|
||||||
/* Context save and restore shadow register */
|
/* Context save and restore shadow register */
|
||||||
u32 chconf0;
|
u32 chconf0, chctrl0;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline void mcspi_write_reg(struct spi_master *master,
|
static inline void mcspi_write_reg(struct spi_master *master,
|
||||||
@ -229,10 +232,16 @@ static void omap2_mcspi_set_dma_req(const struct spi_device *spi,
|
|||||||
|
|
||||||
static void omap2_mcspi_set_enable(const struct spi_device *spi, int enable)
|
static void omap2_mcspi_set_enable(const struct spi_device *spi, int enable)
|
||||||
{
|
{
|
||||||
|
struct omap2_mcspi_cs *cs = spi->controller_state;
|
||||||
u32 l;
|
u32 l;
|
||||||
|
|
||||||
l = enable ? OMAP2_MCSPI_CHCTRL_EN : 0;
|
l = cs->chctrl0;
|
||||||
mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCTRL0, l);
|
if (enable)
|
||||||
|
l |= OMAP2_MCSPI_CHCTRL_EN;
|
||||||
|
else
|
||||||
|
l &= ~OMAP2_MCSPI_CHCTRL_EN;
|
||||||
|
cs->chctrl0 = l;
|
||||||
|
mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCTRL0, cs->chctrl0);
|
||||||
/* Flash post-writes */
|
/* Flash post-writes */
|
||||||
mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCTRL0);
|
mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCTRL0);
|
||||||
}
|
}
|
||||||
@ -839,7 +848,7 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi,
|
|||||||
struct omap2_mcspi_cs *cs = spi->controller_state;
|
struct omap2_mcspi_cs *cs = spi->controller_state;
|
||||||
struct omap2_mcspi *mcspi;
|
struct omap2_mcspi *mcspi;
|
||||||
struct spi_master *spi_cntrl;
|
struct spi_master *spi_cntrl;
|
||||||
u32 l = 0, div = 0;
|
u32 l = 0, clkd = 0, div, extclk = 0, clkg = 0;
|
||||||
u8 word_len = spi->bits_per_word;
|
u8 word_len = spi->bits_per_word;
|
||||||
u32 speed_hz = spi->max_speed_hz;
|
u32 speed_hz = spi->max_speed_hz;
|
||||||
|
|
||||||
@ -855,7 +864,17 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi,
|
|||||||
speed_hz = t->speed_hz;
|
speed_hz = t->speed_hz;
|
||||||
|
|
||||||
speed_hz = min_t(u32, speed_hz, OMAP2_MCSPI_MAX_FREQ);
|
speed_hz = min_t(u32, speed_hz, OMAP2_MCSPI_MAX_FREQ);
|
||||||
div = omap2_mcspi_calc_divisor(speed_hz);
|
if (speed_hz < (OMAP2_MCSPI_MAX_FREQ / OMAP2_MCSPI_MAX_DIVIDER)) {
|
||||||
|
clkd = omap2_mcspi_calc_divisor(speed_hz);
|
||||||
|
speed_hz = OMAP2_MCSPI_MAX_FREQ >> clkd;
|
||||||
|
clkg = 0;
|
||||||
|
} else {
|
||||||
|
div = (OMAP2_MCSPI_MAX_FREQ + speed_hz - 1) / speed_hz;
|
||||||
|
speed_hz = OMAP2_MCSPI_MAX_FREQ / div;
|
||||||
|
clkd = (div - 1) & 0xf;
|
||||||
|
extclk = (div - 1) >> 4;
|
||||||
|
clkg = OMAP2_MCSPI_CHCONF_CLKG;
|
||||||
|
}
|
||||||
|
|
||||||
l = mcspi_cached_chconf0(spi);
|
l = mcspi_cached_chconf0(spi);
|
||||||
|
|
||||||
@ -884,7 +903,16 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi,
|
|||||||
|
|
||||||
/* set clock divisor */
|
/* set clock divisor */
|
||||||
l &= ~OMAP2_MCSPI_CHCONF_CLKD_MASK;
|
l &= ~OMAP2_MCSPI_CHCONF_CLKD_MASK;
|
||||||
l |= div << 2;
|
l |= clkd << 2;
|
||||||
|
|
||||||
|
/* set clock granularity */
|
||||||
|
l &= ~OMAP2_MCSPI_CHCONF_CLKG;
|
||||||
|
l |= clkg;
|
||||||
|
if (clkg) {
|
||||||
|
cs->chctrl0 &= ~OMAP2_MCSPI_CHCTRL_EXTCLK_MASK;
|
||||||
|
cs->chctrl0 |= extclk << 8;
|
||||||
|
mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCTRL0, cs->chctrl0);
|
||||||
|
}
|
||||||
|
|
||||||
/* set SPI mode 0..3 */
|
/* set SPI mode 0..3 */
|
||||||
if (spi->mode & SPI_CPOL)
|
if (spi->mode & SPI_CPOL)
|
||||||
@ -899,7 +927,7 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi,
|
|||||||
mcspi_write_chconf0(spi, l);
|
mcspi_write_chconf0(spi, l);
|
||||||
|
|
||||||
dev_dbg(&spi->dev, "setup: speed %d, sample %s edge, clk %s\n",
|
dev_dbg(&spi->dev, "setup: speed %d, sample %s edge, clk %s\n",
|
||||||
OMAP2_MCSPI_MAX_FREQ >> div,
|
speed_hz,
|
||||||
(spi->mode & SPI_CPHA) ? "trailing" : "leading",
|
(spi->mode & SPI_CPHA) ? "trailing" : "leading",
|
||||||
(spi->mode & SPI_CPOL) ? "inverted" : "normal");
|
(spi->mode & SPI_CPOL) ? "inverted" : "normal");
|
||||||
|
|
||||||
@ -971,6 +999,7 @@ static int omap2_mcspi_setup(struct spi_device *spi)
|
|||||||
cs->base = mcspi->base + spi->chip_select * 0x14;
|
cs->base = mcspi->base + spi->chip_select * 0x14;
|
||||||
cs->phys = mcspi->phys + spi->chip_select * 0x14;
|
cs->phys = mcspi->phys + spi->chip_select * 0x14;
|
||||||
cs->chconf0 = 0;
|
cs->chconf0 = 0;
|
||||||
|
cs->chctrl0 = 0;
|
||||||
spi->controller_state = cs;
|
spi->controller_state = cs;
|
||||||
/* Link this to context save list */
|
/* Link this to context save list */
|
||||||
list_add_tail(&cs->node, &ctx->cs);
|
list_add_tail(&cs->node, &ctx->cs);
|
||||||
@ -1056,12 +1085,15 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m)
|
|||||||
status = -EINVAL;
|
status = -EINVAL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (par_override || t->speed_hz || t->bits_per_word) {
|
if (par_override ||
|
||||||
|
(t->speed_hz != spi->max_speed_hz) ||
|
||||||
|
(t->bits_per_word != spi->bits_per_word)) {
|
||||||
par_override = 1;
|
par_override = 1;
|
||||||
status = omap2_mcspi_setup_transfer(spi, t);
|
status = omap2_mcspi_setup_transfer(spi, t);
|
||||||
if (status < 0)
|
if (status < 0)
|
||||||
break;
|
break;
|
||||||
if (!t->speed_hz && !t->bits_per_word)
|
if (t->speed_hz == spi->max_speed_hz &&
|
||||||
|
t->bits_per_word == spi->bits_per_word)
|
||||||
par_override = 0;
|
par_override = 0;
|
||||||
}
|
}
|
||||||
if (cd && cd->cs_per_word) {
|
if (cd && cd->cs_per_word) {
|
||||||
@ -1175,16 +1207,12 @@ static int omap2_mcspi_transfer_one_message(struct spi_master *master,
|
|||||||
m->actual_length = 0;
|
m->actual_length = 0;
|
||||||
m->status = 0;
|
m->status = 0;
|
||||||
|
|
||||||
/* reject invalid messages and transfers */
|
|
||||||
if (list_empty(&m->transfers))
|
|
||||||
return -EINVAL;
|
|
||||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||||
const void *tx_buf = t->tx_buf;
|
const void *tx_buf = t->tx_buf;
|
||||||
void *rx_buf = t->rx_buf;
|
void *rx_buf = t->rx_buf;
|
||||||
unsigned len = t->len;
|
unsigned len = t->len;
|
||||||
|
|
||||||
if (t->speed_hz > OMAP2_MCSPI_MAX_FREQ
|
if ((len && !(rx_buf || tx_buf))) {
|
||||||
|| (len && !(rx_buf || tx_buf))) {
|
|
||||||
dev_dbg(mcspi->dev, "transfer: %d Hz, %d %s%s, %d bpw\n",
|
dev_dbg(mcspi->dev, "transfer: %d Hz, %d %s%s, %d bpw\n",
|
||||||
t->speed_hz,
|
t->speed_hz,
|
||||||
len,
|
len,
|
||||||
@ -1193,12 +1221,6 @@ static int omap2_mcspi_transfer_one_message(struct spi_master *master,
|
|||||||
t->bits_per_word);
|
t->bits_per_word);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
if (t->speed_hz && t->speed_hz < (OMAP2_MCSPI_MAX_FREQ >> 15)) {
|
|
||||||
dev_dbg(mcspi->dev, "speed_hz %d below minimum %d Hz\n",
|
|
||||||
t->speed_hz,
|
|
||||||
OMAP2_MCSPI_MAX_FREQ >> 15);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m->is_dma_mapped || len < DMA_MIN_BYTES)
|
if (m->is_dma_mapped || len < DMA_MIN_BYTES)
|
||||||
continue;
|
continue;
|
||||||
@ -1310,6 +1332,8 @@ static int omap2_mcspi_probe(struct platform_device *pdev)
|
|||||||
master->transfer_one_message = omap2_mcspi_transfer_one_message;
|
master->transfer_one_message = omap2_mcspi_transfer_one_message;
|
||||||
master->cleanup = omap2_mcspi_cleanup;
|
master->cleanup = omap2_mcspi_cleanup;
|
||||||
master->dev.of_node = node;
|
master->dev.of_node = node;
|
||||||
|
master->max_speed_hz = OMAP2_MCSPI_MAX_FREQ;
|
||||||
|
master->min_speed_hz = OMAP2_MCSPI_MAX_FREQ >> 15;
|
||||||
|
|
||||||
platform_set_drvdata(pdev, master);
|
platform_set_drvdata(pdev, master);
|
||||||
|
|
||||||
|
@ -42,8 +42,6 @@
|
|||||||
struct orion_spi {
|
struct orion_spi {
|
||||||
struct spi_master *master;
|
struct spi_master *master;
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
unsigned int max_speed;
|
|
||||||
unsigned int min_speed;
|
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -74,23 +72,6 @@ orion_spi_clrbits(struct orion_spi *orion_spi, u32 reg, u32 mask)
|
|||||||
writel(val, reg_addr);
|
writel(val, reg_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int orion_spi_set_transfer_size(struct orion_spi *orion_spi, int size)
|
|
||||||
{
|
|
||||||
if (size == 16) {
|
|
||||||
orion_spi_setbits(orion_spi, ORION_SPI_IF_CONFIG_REG,
|
|
||||||
ORION_SPI_IF_8_16_BIT_MODE);
|
|
||||||
} else if (size == 8) {
|
|
||||||
orion_spi_clrbits(orion_spi, ORION_SPI_IF_CONFIG_REG,
|
|
||||||
ORION_SPI_IF_8_16_BIT_MODE);
|
|
||||||
} else {
|
|
||||||
pr_debug("Bad bits per word value %d (only 8 or 16 are allowed).\n",
|
|
||||||
size);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int orion_spi_baudrate_set(struct spi_device *spi, unsigned int speed)
|
static int orion_spi_baudrate_set(struct spi_device *spi, unsigned int speed)
|
||||||
{
|
{
|
||||||
u32 tclk_hz;
|
u32 tclk_hz;
|
||||||
@ -169,7 +150,14 @@ orion_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
|
|||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
return orion_spi_set_transfer_size(orion_spi, bits_per_word);
|
if (bits_per_word == 16)
|
||||||
|
orion_spi_setbits(orion_spi, ORION_SPI_IF_CONFIG_REG,
|
||||||
|
ORION_SPI_IF_8_16_BIT_MODE);
|
||||||
|
else
|
||||||
|
orion_spi_clrbits(orion_spi, ORION_SPI_IF_CONFIG_REG,
|
||||||
|
ORION_SPI_IF_8_16_BIT_MODE);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void orion_spi_set_cs(struct orion_spi *orion_spi, int enable)
|
static void orion_spi_set_cs(struct orion_spi *orion_spi, int enable)
|
||||||
@ -259,11 +247,9 @@ orion_spi_write_read_16bit(struct spi_device *spi,
|
|||||||
static unsigned int
|
static unsigned int
|
||||||
orion_spi_write_read(struct spi_device *spi, struct spi_transfer *xfer)
|
orion_spi_write_read(struct spi_device *spi, struct spi_transfer *xfer)
|
||||||
{
|
{
|
||||||
struct orion_spi *orion_spi;
|
|
||||||
unsigned int count;
|
unsigned int count;
|
||||||
int word_len;
|
int word_len;
|
||||||
|
|
||||||
orion_spi = spi_master_get_devdata(spi->master);
|
|
||||||
word_len = spi->bits_per_word;
|
word_len = spi->bits_per_word;
|
||||||
count = xfer->len;
|
count = xfer->len;
|
||||||
|
|
||||||
@ -309,27 +295,6 @@ static int orion_spi_transfer_one_message(struct spi_master *master,
|
|||||||
goto msg_done;
|
goto msg_done;
|
||||||
|
|
||||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||||
/* make sure buffer length is even when working in 16
|
|
||||||
* bit mode*/
|
|
||||||
if ((t->bits_per_word == 16) && (t->len & 1)) {
|
|
||||||
dev_err(&spi->dev,
|
|
||||||
"message rejected : "
|
|
||||||
"odd data length %d while in 16 bit mode\n",
|
|
||||||
t->len);
|
|
||||||
status = -EIO;
|
|
||||||
goto msg_done;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (t->speed_hz && t->speed_hz < orion_spi->min_speed) {
|
|
||||||
dev_err(&spi->dev,
|
|
||||||
"message rejected : "
|
|
||||||
"device min speed (%d Hz) exceeds "
|
|
||||||
"required transfer speed (%d Hz)\n",
|
|
||||||
orion_spi->min_speed, t->speed_hz);
|
|
||||||
status = -EIO;
|
|
||||||
goto msg_done;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (par_override || t->speed_hz || t->bits_per_word) {
|
if (par_override || t->speed_hz || t->bits_per_word) {
|
||||||
par_override = 1;
|
par_override = 1;
|
||||||
status = orion_spi_setup_transfer(spi, t);
|
status = orion_spi_setup_transfer(spi, t);
|
||||||
@ -374,28 +339,6 @@ static int orion_spi_reset(struct orion_spi *orion_spi)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int orion_spi_setup(struct spi_device *spi)
|
|
||||||
{
|
|
||||||
struct orion_spi *orion_spi;
|
|
||||||
|
|
||||||
orion_spi = spi_master_get_devdata(spi->master);
|
|
||||||
|
|
||||||
if ((spi->max_speed_hz == 0)
|
|
||||||
|| (spi->max_speed_hz > orion_spi->max_speed))
|
|
||||||
spi->max_speed_hz = orion_spi->max_speed;
|
|
||||||
|
|
||||||
if (spi->max_speed_hz < orion_spi->min_speed) {
|
|
||||||
dev_err(&spi->dev, "setup: requested speed too low %d Hz\n",
|
|
||||||
spi->max_speed_hz);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* baudrate & width will be set orion_spi_setup_transfer
|
|
||||||
*/
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int orion_spi_probe(struct platform_device *pdev)
|
static int orion_spi_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct spi_master *master;
|
struct spi_master *master;
|
||||||
@ -424,9 +367,9 @@ static int orion_spi_probe(struct platform_device *pdev)
|
|||||||
/* we support only mode 0, and no options */
|
/* we support only mode 0, and no options */
|
||||||
master->mode_bits = SPI_CPHA | SPI_CPOL;
|
master->mode_bits = SPI_CPHA | SPI_CPOL;
|
||||||
|
|
||||||
master->setup = orion_spi_setup;
|
|
||||||
master->transfer_one_message = orion_spi_transfer_one_message;
|
master->transfer_one_message = orion_spi_transfer_one_message;
|
||||||
master->num_chipselect = ORION_NUM_CHIPSELECTS;
|
master->num_chipselect = ORION_NUM_CHIPSELECTS;
|
||||||
|
master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16);
|
||||||
|
|
||||||
platform_set_drvdata(pdev, master);
|
platform_set_drvdata(pdev, master);
|
||||||
|
|
||||||
@ -442,8 +385,8 @@ static int orion_spi_probe(struct platform_device *pdev)
|
|||||||
clk_prepare(spi->clk);
|
clk_prepare(spi->clk);
|
||||||
clk_enable(spi->clk);
|
clk_enable(spi->clk);
|
||||||
tclk_hz = clk_get_rate(spi->clk);
|
tclk_hz = clk_get_rate(spi->clk);
|
||||||
spi->max_speed = DIV_ROUND_UP(tclk_hz, 4);
|
master->max_speed_hz = DIV_ROUND_UP(tclk_hz, 4);
|
||||||
spi->min_speed = DIV_ROUND_UP(tclk_hz, 30);
|
master->min_speed_hz = DIV_ROUND_UP(tclk_hz, 30);
|
||||||
|
|
||||||
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);
|
||||||
|
@ -2108,8 +2108,6 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id)
|
|||||||
pl022->chipselects = devm_kzalloc(dev, num_cs * sizeof(int),
|
pl022->chipselects = devm_kzalloc(dev, num_cs * sizeof(int),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
|
|
||||||
pinctrl_pm_select_default_state(dev);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Bus Number Which has been Assigned to this SSP controller
|
* Bus Number Which has been Assigned to this SSP controller
|
||||||
* on this board
|
* on this board
|
||||||
@ -2182,13 +2180,7 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id)
|
|||||||
goto err_no_clk;
|
goto err_no_clk;
|
||||||
}
|
}
|
||||||
|
|
||||||
status = clk_prepare(pl022->clk);
|
status = clk_prepare_enable(pl022->clk);
|
||||||
if (status) {
|
|
||||||
dev_err(&adev->dev, "could not prepare SSP/SPI bus clock\n");
|
|
||||||
goto err_clk_prep;
|
|
||||||
}
|
|
||||||
|
|
||||||
status = clk_enable(pl022->clk);
|
|
||||||
if (status) {
|
if (status) {
|
||||||
dev_err(&adev->dev, "could not enable SSP/SPI bus clock\n");
|
dev_err(&adev->dev, "could not enable SSP/SPI bus clock\n");
|
||||||
goto err_no_clk_en;
|
goto err_no_clk_en;
|
||||||
@ -2249,10 +2241,8 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id)
|
|||||||
if (platform_info->enable_dma)
|
if (platform_info->enable_dma)
|
||||||
pl022_dma_remove(pl022);
|
pl022_dma_remove(pl022);
|
||||||
err_no_irq:
|
err_no_irq:
|
||||||
clk_disable(pl022->clk);
|
clk_disable_unprepare(pl022->clk);
|
||||||
err_no_clk_en:
|
err_no_clk_en:
|
||||||
clk_unprepare(pl022->clk);
|
|
||||||
err_clk_prep:
|
|
||||||
err_no_clk:
|
err_no_clk:
|
||||||
err_no_ioremap:
|
err_no_ioremap:
|
||||||
amba_release_regions(adev);
|
amba_release_regions(adev);
|
||||||
@ -2280,42 +2270,13 @@ pl022_remove(struct amba_device *adev)
|
|||||||
if (pl022->master_info->enable_dma)
|
if (pl022->master_info->enable_dma)
|
||||||
pl022_dma_remove(pl022);
|
pl022_dma_remove(pl022);
|
||||||
|
|
||||||
clk_disable(pl022->clk);
|
clk_disable_unprepare(pl022->clk);
|
||||||
clk_unprepare(pl022->clk);
|
|
||||||
amba_release_regions(adev);
|
amba_release_regions(adev);
|
||||||
tasklet_disable(&pl022->pump_transfers);
|
tasklet_disable(&pl022->pump_transfers);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(CONFIG_SUSPEND) || defined(CONFIG_PM_RUNTIME)
|
#ifdef CONFIG_PM_SLEEP
|
||||||
/*
|
|
||||||
* These two functions are used from both suspend/resume and
|
|
||||||
* the runtime counterparts to handle external resources like
|
|
||||||
* clocks, pins and regulators when going to sleep.
|
|
||||||
*/
|
|
||||||
static void pl022_suspend_resources(struct pl022 *pl022, bool runtime)
|
|
||||||
{
|
|
||||||
clk_disable(pl022->clk);
|
|
||||||
|
|
||||||
if (runtime)
|
|
||||||
pinctrl_pm_select_idle_state(&pl022->adev->dev);
|
|
||||||
else
|
|
||||||
pinctrl_pm_select_sleep_state(&pl022->adev->dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pl022_resume_resources(struct pl022 *pl022, bool runtime)
|
|
||||||
{
|
|
||||||
/* First go to the default state */
|
|
||||||
pinctrl_pm_select_default_state(&pl022->adev->dev);
|
|
||||||
if (!runtime)
|
|
||||||
/* Then let's idle the pins until the next transfer happens */
|
|
||||||
pinctrl_pm_select_idle_state(&pl022->adev->dev);
|
|
||||||
|
|
||||||
clk_enable(pl022->clk);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef CONFIG_SUSPEND
|
|
||||||
static int pl022_suspend(struct device *dev)
|
static int pl022_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct pl022 *pl022 = dev_get_drvdata(dev);
|
struct pl022 *pl022 = dev_get_drvdata(dev);
|
||||||
@ -2327,8 +2288,13 @@ static int pl022_suspend(struct device *dev)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
pm_runtime_get_sync(dev);
|
ret = pm_runtime_force_suspend(dev);
|
||||||
pl022_suspend_resources(pl022, false);
|
if (ret) {
|
||||||
|
spi_master_resume(pl022->master);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
pinctrl_pm_select_sleep_state(dev);
|
||||||
|
|
||||||
dev_dbg(dev, "suspended\n");
|
dev_dbg(dev, "suspended\n");
|
||||||
return 0;
|
return 0;
|
||||||
@ -2339,8 +2305,9 @@ static int pl022_resume(struct device *dev)
|
|||||||
struct pl022 *pl022 = dev_get_drvdata(dev);
|
struct pl022 *pl022 = dev_get_drvdata(dev);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
pl022_resume_resources(pl022, false);
|
ret = pm_runtime_force_resume(dev);
|
||||||
pm_runtime_put(dev);
|
if (ret)
|
||||||
|
dev_err(dev, "problem resuming\n");
|
||||||
|
|
||||||
/* Start the queue running */
|
/* Start the queue running */
|
||||||
ret = spi_master_resume(pl022->master);
|
ret = spi_master_resume(pl022->master);
|
||||||
@ -2351,14 +2318,16 @@ static int pl022_resume(struct device *dev)
|
|||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_PM */
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_PM_RUNTIME
|
#ifdef CONFIG_PM
|
||||||
static int pl022_runtime_suspend(struct device *dev)
|
static int pl022_runtime_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct pl022 *pl022 = dev_get_drvdata(dev);
|
struct pl022 *pl022 = dev_get_drvdata(dev);
|
||||||
|
|
||||||
pl022_suspend_resources(pl022, true);
|
clk_disable_unprepare(pl022->clk);
|
||||||
|
pinctrl_pm_select_idle_state(dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2366,14 +2335,16 @@ static int pl022_runtime_resume(struct device *dev)
|
|||||||
{
|
{
|
||||||
struct pl022 *pl022 = dev_get_drvdata(dev);
|
struct pl022 *pl022 = dev_get_drvdata(dev);
|
||||||
|
|
||||||
pl022_resume_resources(pl022, true);
|
pinctrl_pm_select_default_state(dev);
|
||||||
|
clk_prepare_enable(pl022->clk);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static const struct dev_pm_ops pl022_dev_pm_ops = {
|
static const struct dev_pm_ops pl022_dev_pm_ops = {
|
||||||
SET_SYSTEM_SLEEP_PM_OPS(pl022_suspend, pl022_resume)
|
SET_SYSTEM_SLEEP_PM_OPS(pl022_suspend, pl022_resume)
|
||||||
SET_RUNTIME_PM_OPS(pl022_runtime_suspend, pl022_runtime_resume, NULL)
|
SET_PM_RUNTIME_PM_OPS(pl022_runtime_suspend, pl022_runtime_resume, NULL)
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct vendor_data vendor_arm = {
|
static struct vendor_data vendor_arm = {
|
||||||
|
779
drivers/spi/spi-qup.c
Normal file
779
drivers/spi/spi-qup.c
Normal file
@ -0,0 +1,779 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2008-2014, The Linux foundation. All rights reserved.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License rev 2 and
|
||||||
|
* only rev 2 as published by the free Software foundation.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/list.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
|
#include <linux/spi/spi.h>
|
||||||
|
|
||||||
|
#define QUP_CONFIG 0x0000
|
||||||
|
#define QUP_STATE 0x0004
|
||||||
|
#define QUP_IO_M_MODES 0x0008
|
||||||
|
#define QUP_SW_RESET 0x000c
|
||||||
|
#define QUP_OPERATIONAL 0x0018
|
||||||
|
#define QUP_ERROR_FLAGS 0x001c
|
||||||
|
#define QUP_ERROR_FLAGS_EN 0x0020
|
||||||
|
#define QUP_OPERATIONAL_MASK 0x0028
|
||||||
|
#define QUP_HW_VERSION 0x0030
|
||||||
|
#define QUP_MX_OUTPUT_CNT 0x0100
|
||||||
|
#define QUP_OUTPUT_FIFO 0x0110
|
||||||
|
#define QUP_MX_WRITE_CNT 0x0150
|
||||||
|
#define QUP_MX_INPUT_CNT 0x0200
|
||||||
|
#define QUP_MX_READ_CNT 0x0208
|
||||||
|
#define QUP_INPUT_FIFO 0x0218
|
||||||
|
|
||||||
|
#define SPI_CONFIG 0x0300
|
||||||
|
#define SPI_IO_CONTROL 0x0304
|
||||||
|
#define SPI_ERROR_FLAGS 0x0308
|
||||||
|
#define SPI_ERROR_FLAGS_EN 0x030c
|
||||||
|
|
||||||
|
/* QUP_CONFIG fields */
|
||||||
|
#define QUP_CONFIG_SPI_MODE (1 << 8)
|
||||||
|
#define QUP_CONFIG_CLOCK_AUTO_GATE BIT(13)
|
||||||
|
#define QUP_CONFIG_NO_INPUT BIT(7)
|
||||||
|
#define QUP_CONFIG_NO_OUTPUT BIT(6)
|
||||||
|
#define QUP_CONFIG_N 0x001f
|
||||||
|
|
||||||
|
/* QUP_STATE fields */
|
||||||
|
#define QUP_STATE_VALID BIT(2)
|
||||||
|
#define QUP_STATE_RESET 0
|
||||||
|
#define QUP_STATE_RUN 1
|
||||||
|
#define QUP_STATE_PAUSE 3
|
||||||
|
#define QUP_STATE_MASK 3
|
||||||
|
#define QUP_STATE_CLEAR 2
|
||||||
|
|
||||||
|
#define QUP_HW_VERSION_2_1_1 0x20010001
|
||||||
|
|
||||||
|
/* QUP_IO_M_MODES fields */
|
||||||
|
#define QUP_IO_M_PACK_EN BIT(15)
|
||||||
|
#define QUP_IO_M_UNPACK_EN BIT(14)
|
||||||
|
#define QUP_IO_M_INPUT_MODE_MASK_SHIFT 12
|
||||||
|
#define QUP_IO_M_OUTPUT_MODE_MASK_SHIFT 10
|
||||||
|
#define QUP_IO_M_INPUT_MODE_MASK (3 << QUP_IO_M_INPUT_MODE_MASK_SHIFT)
|
||||||
|
#define QUP_IO_M_OUTPUT_MODE_MASK (3 << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT)
|
||||||
|
|
||||||
|
#define QUP_IO_M_OUTPUT_BLOCK_SIZE(x) (((x) & (0x03 << 0)) >> 0)
|
||||||
|
#define QUP_IO_M_OUTPUT_FIFO_SIZE(x) (((x) & (0x07 << 2)) >> 2)
|
||||||
|
#define QUP_IO_M_INPUT_BLOCK_SIZE(x) (((x) & (0x03 << 5)) >> 5)
|
||||||
|
#define QUP_IO_M_INPUT_FIFO_SIZE(x) (((x) & (0x07 << 7)) >> 7)
|
||||||
|
|
||||||
|
#define QUP_IO_M_MODE_FIFO 0
|
||||||
|
#define QUP_IO_M_MODE_BLOCK 1
|
||||||
|
#define QUP_IO_M_MODE_DMOV 2
|
||||||
|
#define QUP_IO_M_MODE_BAM 3
|
||||||
|
|
||||||
|
/* QUP_OPERATIONAL fields */
|
||||||
|
#define QUP_OP_MAX_INPUT_DONE_FLAG BIT(11)
|
||||||
|
#define QUP_OP_MAX_OUTPUT_DONE_FLAG BIT(10)
|
||||||
|
#define QUP_OP_IN_SERVICE_FLAG BIT(9)
|
||||||
|
#define QUP_OP_OUT_SERVICE_FLAG BIT(8)
|
||||||
|
#define QUP_OP_IN_FIFO_FULL BIT(7)
|
||||||
|
#define QUP_OP_OUT_FIFO_FULL BIT(6)
|
||||||
|
#define QUP_OP_IN_FIFO_NOT_EMPTY BIT(5)
|
||||||
|
#define QUP_OP_OUT_FIFO_NOT_EMPTY BIT(4)
|
||||||
|
|
||||||
|
/* QUP_ERROR_FLAGS and QUP_ERROR_FLAGS_EN fields */
|
||||||
|
#define QUP_ERROR_OUTPUT_OVER_RUN BIT(5)
|
||||||
|
#define QUP_ERROR_INPUT_UNDER_RUN BIT(4)
|
||||||
|
#define QUP_ERROR_OUTPUT_UNDER_RUN BIT(3)
|
||||||
|
#define QUP_ERROR_INPUT_OVER_RUN BIT(2)
|
||||||
|
|
||||||
|
/* SPI_CONFIG fields */
|
||||||
|
#define SPI_CONFIG_HS_MODE BIT(10)
|
||||||
|
#define SPI_CONFIG_INPUT_FIRST BIT(9)
|
||||||
|
#define SPI_CONFIG_LOOPBACK BIT(8)
|
||||||
|
|
||||||
|
/* SPI_IO_CONTROL fields */
|
||||||
|
#define SPI_IO_C_FORCE_CS BIT(11)
|
||||||
|
#define SPI_IO_C_CLK_IDLE_HIGH BIT(10)
|
||||||
|
#define SPI_IO_C_MX_CS_MODE BIT(8)
|
||||||
|
#define SPI_IO_C_CS_N_POLARITY_0 BIT(4)
|
||||||
|
#define SPI_IO_C_CS_SELECT(x) (((x) & 3) << 2)
|
||||||
|
#define SPI_IO_C_CS_SELECT_MASK 0x000c
|
||||||
|
#define SPI_IO_C_TRISTATE_CS BIT(1)
|
||||||
|
#define SPI_IO_C_NO_TRI_STATE BIT(0)
|
||||||
|
|
||||||
|
/* SPI_ERROR_FLAGS and SPI_ERROR_FLAGS_EN fields */
|
||||||
|
#define SPI_ERROR_CLK_OVER_RUN BIT(1)
|
||||||
|
#define SPI_ERROR_CLK_UNDER_RUN BIT(0)
|
||||||
|
|
||||||
|
#define SPI_NUM_CHIPSELECTS 4
|
||||||
|
|
||||||
|
/* high speed mode is when bus rate is greater then 26MHz */
|
||||||
|
#define SPI_HS_MIN_RATE 26000000
|
||||||
|
#define SPI_MAX_RATE 50000000
|
||||||
|
|
||||||
|
#define SPI_DELAY_THRESHOLD 1
|
||||||
|
#define SPI_DELAY_RETRY 10
|
||||||
|
|
||||||
|
struct spi_qup {
|
||||||
|
void __iomem *base;
|
||||||
|
struct device *dev;
|
||||||
|
struct clk *cclk; /* core clock */
|
||||||
|
struct clk *iclk; /* interface clock */
|
||||||
|
int irq;
|
||||||
|
spinlock_t lock;
|
||||||
|
|
||||||
|
int in_fifo_sz;
|
||||||
|
int out_fifo_sz;
|
||||||
|
int in_blk_sz;
|
||||||
|
int out_blk_sz;
|
||||||
|
|
||||||
|
struct spi_transfer *xfer;
|
||||||
|
struct completion done;
|
||||||
|
int error;
|
||||||
|
int w_size; /* bytes per SPI word */
|
||||||
|
int tx_bytes;
|
||||||
|
int rx_bytes;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static inline bool spi_qup_is_valid_state(struct spi_qup *controller)
|
||||||
|
{
|
||||||
|
u32 opstate = readl_relaxed(controller->base + QUP_STATE);
|
||||||
|
|
||||||
|
return opstate & QUP_STATE_VALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int spi_qup_set_state(struct spi_qup *controller, u32 state)
|
||||||
|
{
|
||||||
|
unsigned long loop;
|
||||||
|
u32 cur_state;
|
||||||
|
|
||||||
|
loop = 0;
|
||||||
|
while (!spi_qup_is_valid_state(controller)) {
|
||||||
|
|
||||||
|
usleep_range(SPI_DELAY_THRESHOLD, SPI_DELAY_THRESHOLD * 2);
|
||||||
|
|
||||||
|
if (++loop > SPI_DELAY_RETRY)
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loop)
|
||||||
|
dev_dbg(controller->dev, "invalid state for %ld,us %d\n",
|
||||||
|
loop, state);
|
||||||
|
|
||||||
|
cur_state = readl_relaxed(controller->base + QUP_STATE);
|
||||||
|
/*
|
||||||
|
* Per spec: for PAUSE_STATE to RESET_STATE, two writes
|
||||||
|
* of (b10) are required
|
||||||
|
*/
|
||||||
|
if (((cur_state & QUP_STATE_MASK) == QUP_STATE_PAUSE) &&
|
||||||
|
(state == QUP_STATE_RESET)) {
|
||||||
|
writel_relaxed(QUP_STATE_CLEAR, controller->base + QUP_STATE);
|
||||||
|
writel_relaxed(QUP_STATE_CLEAR, controller->base + QUP_STATE);
|
||||||
|
} else {
|
||||||
|
cur_state &= ~QUP_STATE_MASK;
|
||||||
|
cur_state |= state;
|
||||||
|
writel_relaxed(cur_state, controller->base + QUP_STATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
loop = 0;
|
||||||
|
while (!spi_qup_is_valid_state(controller)) {
|
||||||
|
|
||||||
|
usleep_range(SPI_DELAY_THRESHOLD, SPI_DELAY_THRESHOLD * 2);
|
||||||
|
|
||||||
|
if (++loop > SPI_DELAY_RETRY)
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void spi_qup_fifo_read(struct spi_qup *controller,
|
||||||
|
struct spi_transfer *xfer)
|
||||||
|
{
|
||||||
|
u8 *rx_buf = xfer->rx_buf;
|
||||||
|
u32 word, state;
|
||||||
|
int idx, shift, w_size;
|
||||||
|
|
||||||
|
w_size = controller->w_size;
|
||||||
|
|
||||||
|
while (controller->rx_bytes < xfer->len) {
|
||||||
|
|
||||||
|
state = readl_relaxed(controller->base + QUP_OPERATIONAL);
|
||||||
|
if (0 == (state & QUP_OP_IN_FIFO_NOT_EMPTY))
|
||||||
|
break;
|
||||||
|
|
||||||
|
word = readl_relaxed(controller->base + QUP_INPUT_FIFO);
|
||||||
|
|
||||||
|
if (!rx_buf) {
|
||||||
|
controller->rx_bytes += w_size;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (idx = 0; idx < w_size; idx++, controller->rx_bytes++) {
|
||||||
|
/*
|
||||||
|
* The data format depends on bytes per SPI word:
|
||||||
|
* 4 bytes: 0x12345678
|
||||||
|
* 2 bytes: 0x00001234
|
||||||
|
* 1 byte : 0x00000012
|
||||||
|
*/
|
||||||
|
shift = BITS_PER_BYTE;
|
||||||
|
shift *= (w_size - idx - 1);
|
||||||
|
rx_buf[controller->rx_bytes] = word >> shift;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void spi_qup_fifo_write(struct spi_qup *controller,
|
||||||
|
struct spi_transfer *xfer)
|
||||||
|
{
|
||||||
|
const u8 *tx_buf = xfer->tx_buf;
|
||||||
|
u32 word, state, data;
|
||||||
|
int idx, w_size;
|
||||||
|
|
||||||
|
w_size = controller->w_size;
|
||||||
|
|
||||||
|
while (controller->tx_bytes < xfer->len) {
|
||||||
|
|
||||||
|
state = readl_relaxed(controller->base + QUP_OPERATIONAL);
|
||||||
|
if (state & QUP_OP_OUT_FIFO_FULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
word = 0;
|
||||||
|
for (idx = 0; idx < w_size; idx++, controller->tx_bytes++) {
|
||||||
|
|
||||||
|
if (!tx_buf) {
|
||||||
|
controller->tx_bytes += w_size;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
data = tx_buf[controller->tx_bytes];
|
||||||
|
word |= data << (BITS_PER_BYTE * (3 - idx));
|
||||||
|
}
|
||||||
|
|
||||||
|
writel_relaxed(word, controller->base + QUP_OUTPUT_FIFO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
|
||||||
|
{
|
||||||
|
struct spi_qup *controller = dev_id;
|
||||||
|
struct spi_transfer *xfer;
|
||||||
|
u32 opflags, qup_err, spi_err;
|
||||||
|
unsigned long flags;
|
||||||
|
int error = 0;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&controller->lock, flags);
|
||||||
|
xfer = controller->xfer;
|
||||||
|
controller->xfer = NULL;
|
||||||
|
spin_unlock_irqrestore(&controller->lock, flags);
|
||||||
|
|
||||||
|
qup_err = readl_relaxed(controller->base + QUP_ERROR_FLAGS);
|
||||||
|
spi_err = readl_relaxed(controller->base + SPI_ERROR_FLAGS);
|
||||||
|
opflags = readl_relaxed(controller->base + QUP_OPERATIONAL);
|
||||||
|
|
||||||
|
writel_relaxed(qup_err, controller->base + QUP_ERROR_FLAGS);
|
||||||
|
writel_relaxed(spi_err, controller->base + SPI_ERROR_FLAGS);
|
||||||
|
writel_relaxed(opflags, controller->base + QUP_OPERATIONAL);
|
||||||
|
|
||||||
|
if (!xfer) {
|
||||||
|
dev_err_ratelimited(controller->dev, "unexpected irq %x08 %x08 %x08\n",
|
||||||
|
qup_err, spi_err, opflags);
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qup_err) {
|
||||||
|
if (qup_err & QUP_ERROR_OUTPUT_OVER_RUN)
|
||||||
|
dev_warn(controller->dev, "OUTPUT_OVER_RUN\n");
|
||||||
|
if (qup_err & QUP_ERROR_INPUT_UNDER_RUN)
|
||||||
|
dev_warn(controller->dev, "INPUT_UNDER_RUN\n");
|
||||||
|
if (qup_err & QUP_ERROR_OUTPUT_UNDER_RUN)
|
||||||
|
dev_warn(controller->dev, "OUTPUT_UNDER_RUN\n");
|
||||||
|
if (qup_err & QUP_ERROR_INPUT_OVER_RUN)
|
||||||
|
dev_warn(controller->dev, "INPUT_OVER_RUN\n");
|
||||||
|
|
||||||
|
error = -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spi_err) {
|
||||||
|
if (spi_err & SPI_ERROR_CLK_OVER_RUN)
|
||||||
|
dev_warn(controller->dev, "CLK_OVER_RUN\n");
|
||||||
|
if (spi_err & SPI_ERROR_CLK_UNDER_RUN)
|
||||||
|
dev_warn(controller->dev, "CLK_UNDER_RUN\n");
|
||||||
|
|
||||||
|
error = -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opflags & QUP_OP_IN_SERVICE_FLAG)
|
||||||
|
spi_qup_fifo_read(controller, xfer);
|
||||||
|
|
||||||
|
if (opflags & QUP_OP_OUT_SERVICE_FLAG)
|
||||||
|
spi_qup_fifo_write(controller, xfer);
|
||||||
|
|
||||||
|
spin_lock_irqsave(&controller->lock, flags);
|
||||||
|
controller->error = error;
|
||||||
|
controller->xfer = xfer;
|
||||||
|
spin_unlock_irqrestore(&controller->lock, flags);
|
||||||
|
|
||||||
|
if (controller->rx_bytes == xfer->len || error)
|
||||||
|
complete(&controller->done);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* set clock freq ... bits per word */
|
||||||
|
static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
|
||||||
|
{
|
||||||
|
struct spi_qup *controller = spi_master_get_devdata(spi->master);
|
||||||
|
u32 config, iomode, mode;
|
||||||
|
int ret, n_words, w_size;
|
||||||
|
|
||||||
|
if (spi->mode & SPI_LOOP && xfer->len > controller->in_fifo_sz) {
|
||||||
|
dev_err(controller->dev, "too big size for loopback %d > %d\n",
|
||||||
|
xfer->len, controller->in_fifo_sz);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = clk_set_rate(controller->cclk, xfer->speed_hz);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(controller->dev, "fail to set frequency %d",
|
||||||
|
xfer->speed_hz);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spi_qup_set_state(controller, QUP_STATE_RESET)) {
|
||||||
|
dev_err(controller->dev, "cannot set RESET state\n");
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
w_size = 4;
|
||||||
|
if (xfer->bits_per_word <= 8)
|
||||||
|
w_size = 1;
|
||||||
|
else if (xfer->bits_per_word <= 16)
|
||||||
|
w_size = 2;
|
||||||
|
|
||||||
|
n_words = xfer->len / w_size;
|
||||||
|
controller->w_size = w_size;
|
||||||
|
|
||||||
|
if (n_words <= controller->in_fifo_sz) {
|
||||||
|
mode = QUP_IO_M_MODE_FIFO;
|
||||||
|
writel_relaxed(n_words, controller->base + QUP_MX_READ_CNT);
|
||||||
|
writel_relaxed(n_words, controller->base + QUP_MX_WRITE_CNT);
|
||||||
|
/* must be zero for FIFO */
|
||||||
|
writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT);
|
||||||
|
writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
|
||||||
|
} else {
|
||||||
|
mode = QUP_IO_M_MODE_BLOCK;
|
||||||
|
writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT);
|
||||||
|
writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT);
|
||||||
|
/* must be zero for BLOCK and BAM */
|
||||||
|
writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
|
||||||
|
writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
|
||||||
|
}
|
||||||
|
|
||||||
|
iomode = readl_relaxed(controller->base + QUP_IO_M_MODES);
|
||||||
|
/* Set input and output transfer mode */
|
||||||
|
iomode &= ~(QUP_IO_M_INPUT_MODE_MASK | QUP_IO_M_OUTPUT_MODE_MASK);
|
||||||
|
iomode &= ~(QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN);
|
||||||
|
iomode |= (mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT);
|
||||||
|
iomode |= (mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT);
|
||||||
|
|
||||||
|
writel_relaxed(iomode, controller->base + QUP_IO_M_MODES);
|
||||||
|
|
||||||
|
config = readl_relaxed(controller->base + SPI_CONFIG);
|
||||||
|
|
||||||
|
if (spi->mode & SPI_LOOP)
|
||||||
|
config |= SPI_CONFIG_LOOPBACK;
|
||||||
|
else
|
||||||
|
config &= ~SPI_CONFIG_LOOPBACK;
|
||||||
|
|
||||||
|
if (spi->mode & SPI_CPHA)
|
||||||
|
config &= ~SPI_CONFIG_INPUT_FIRST;
|
||||||
|
else
|
||||||
|
config |= SPI_CONFIG_INPUT_FIRST;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* HS_MODE improves signal stability for spi-clk high rates,
|
||||||
|
* but is invalid in loop back mode.
|
||||||
|
*/
|
||||||
|
if ((xfer->speed_hz >= SPI_HS_MIN_RATE) && !(spi->mode & SPI_LOOP))
|
||||||
|
config |= SPI_CONFIG_HS_MODE;
|
||||||
|
else
|
||||||
|
config &= ~SPI_CONFIG_HS_MODE;
|
||||||
|
|
||||||
|
writel_relaxed(config, controller->base + SPI_CONFIG);
|
||||||
|
|
||||||
|
config = readl_relaxed(controller->base + QUP_CONFIG);
|
||||||
|
config &= ~(QUP_CONFIG_NO_INPUT | QUP_CONFIG_NO_OUTPUT | QUP_CONFIG_N);
|
||||||
|
config |= xfer->bits_per_word - 1;
|
||||||
|
config |= QUP_CONFIG_SPI_MODE;
|
||||||
|
writel_relaxed(config, controller->base + QUP_CONFIG);
|
||||||
|
|
||||||
|
writel_relaxed(0, controller->base + QUP_OPERATIONAL_MASK);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void spi_qup_set_cs(struct spi_device *spi, bool enable)
|
||||||
|
{
|
||||||
|
struct spi_qup *controller = spi_master_get_devdata(spi->master);
|
||||||
|
|
||||||
|
u32 iocontol, mask;
|
||||||
|
|
||||||
|
iocontol = readl_relaxed(controller->base + SPI_IO_CONTROL);
|
||||||
|
|
||||||
|
/* Disable auto CS toggle and use manual */
|
||||||
|
iocontol &= ~SPI_IO_C_MX_CS_MODE;
|
||||||
|
iocontol |= SPI_IO_C_FORCE_CS;
|
||||||
|
|
||||||
|
iocontol &= ~SPI_IO_C_CS_SELECT_MASK;
|
||||||
|
iocontol |= SPI_IO_C_CS_SELECT(spi->chip_select);
|
||||||
|
|
||||||
|
mask = SPI_IO_C_CS_N_POLARITY_0 << spi->chip_select;
|
||||||
|
|
||||||
|
if (enable)
|
||||||
|
iocontol |= mask;
|
||||||
|
else
|
||||||
|
iocontol &= ~mask;
|
||||||
|
|
||||||
|
writel_relaxed(iocontol, controller->base + SPI_IO_CONTROL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int spi_qup_transfer_one(struct spi_master *master,
|
||||||
|
struct spi_device *spi,
|
||||||
|
struct spi_transfer *xfer)
|
||||||
|
{
|
||||||
|
struct spi_qup *controller = spi_master_get_devdata(master);
|
||||||
|
unsigned long timeout, flags;
|
||||||
|
int ret = -EIO;
|
||||||
|
|
||||||
|
ret = spi_qup_io_config(spi, xfer);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
timeout = DIV_ROUND_UP(xfer->speed_hz, MSEC_PER_SEC);
|
||||||
|
timeout = DIV_ROUND_UP(xfer->len * 8, timeout);
|
||||||
|
timeout = 100 * msecs_to_jiffies(timeout);
|
||||||
|
|
||||||
|
reinit_completion(&controller->done);
|
||||||
|
|
||||||
|
spin_lock_irqsave(&controller->lock, flags);
|
||||||
|
controller->xfer = xfer;
|
||||||
|
controller->error = 0;
|
||||||
|
controller->rx_bytes = 0;
|
||||||
|
controller->tx_bytes = 0;
|
||||||
|
spin_unlock_irqrestore(&controller->lock, flags);
|
||||||
|
|
||||||
|
if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
|
||||||
|
dev_warn(controller->dev, "cannot set RUN state\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spi_qup_set_state(controller, QUP_STATE_PAUSE)) {
|
||||||
|
dev_warn(controller->dev, "cannot set PAUSE state\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
spi_qup_fifo_write(controller, xfer);
|
||||||
|
|
||||||
|
if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
|
||||||
|
dev_warn(controller->dev, "cannot set EXECUTE state\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!wait_for_completion_timeout(&controller->done, timeout))
|
||||||
|
ret = -ETIMEDOUT;
|
||||||
|
exit:
|
||||||
|
spi_qup_set_state(controller, QUP_STATE_RESET);
|
||||||
|
spin_lock_irqsave(&controller->lock, flags);
|
||||||
|
controller->xfer = NULL;
|
||||||
|
if (!ret)
|
||||||
|
ret = controller->error;
|
||||||
|
spin_unlock_irqrestore(&controller->lock, flags);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int spi_qup_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct spi_master *master;
|
||||||
|
struct clk *iclk, *cclk;
|
||||||
|
struct spi_qup *controller;
|
||||||
|
struct resource *res;
|
||||||
|
struct device *dev;
|
||||||
|
void __iomem *base;
|
||||||
|
u32 data, max_freq, iomode;
|
||||||
|
int ret, irq, size;
|
||||||
|
|
||||||
|
dev = &pdev->dev;
|
||||||
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
base = devm_ioremap_resource(dev, res);
|
||||||
|
if (IS_ERR(base))
|
||||||
|
return PTR_ERR(base);
|
||||||
|
|
||||||
|
irq = platform_get_irq(pdev, 0);
|
||||||
|
if (irq < 0)
|
||||||
|
return irq;
|
||||||
|
|
||||||
|
cclk = devm_clk_get(dev, "core");
|
||||||
|
if (IS_ERR(cclk))
|
||||||
|
return PTR_ERR(cclk);
|
||||||
|
|
||||||
|
iclk = devm_clk_get(dev, "iface");
|
||||||
|
if (IS_ERR(iclk))
|
||||||
|
return PTR_ERR(iclk);
|
||||||
|
|
||||||
|
/* This is optional parameter */
|
||||||
|
if (of_property_read_u32(dev->of_node, "spi-max-frequency", &max_freq))
|
||||||
|
max_freq = SPI_MAX_RATE;
|
||||||
|
|
||||||
|
if (!max_freq || max_freq > SPI_MAX_RATE) {
|
||||||
|
dev_err(dev, "invalid clock frequency %d\n", max_freq);
|
||||||
|
return -ENXIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = clk_prepare_enable(cclk);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "cannot enable core clock\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = clk_prepare_enable(iclk);
|
||||||
|
if (ret) {
|
||||||
|
clk_disable_unprepare(cclk);
|
||||||
|
dev_err(dev, "cannot enable iface clock\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
data = readl_relaxed(base + QUP_HW_VERSION);
|
||||||
|
|
||||||
|
if (data < QUP_HW_VERSION_2_1_1) {
|
||||||
|
clk_disable_unprepare(cclk);
|
||||||
|
clk_disable_unprepare(iclk);
|
||||||
|
dev_err(dev, "v.%08x is not supported\n", data);
|
||||||
|
return -ENXIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
master = spi_alloc_master(dev, sizeof(struct spi_qup));
|
||||||
|
if (!master) {
|
||||||
|
clk_disable_unprepare(cclk);
|
||||||
|
clk_disable_unprepare(iclk);
|
||||||
|
dev_err(dev, "cannot allocate master\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
master->bus_num = pdev->id;
|
||||||
|
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP;
|
||||||
|
master->num_chipselect = SPI_NUM_CHIPSELECTS;
|
||||||
|
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
|
||||||
|
master->max_speed_hz = max_freq;
|
||||||
|
master->set_cs = spi_qup_set_cs;
|
||||||
|
master->transfer_one = spi_qup_transfer_one;
|
||||||
|
master->dev.of_node = pdev->dev.of_node;
|
||||||
|
master->auto_runtime_pm = true;
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, master);
|
||||||
|
|
||||||
|
controller = spi_master_get_devdata(master);
|
||||||
|
|
||||||
|
controller->dev = dev;
|
||||||
|
controller->base = base;
|
||||||
|
controller->iclk = iclk;
|
||||||
|
controller->cclk = cclk;
|
||||||
|
controller->irq = irq;
|
||||||
|
|
||||||
|
spin_lock_init(&controller->lock);
|
||||||
|
init_completion(&controller->done);
|
||||||
|
|
||||||
|
iomode = readl_relaxed(base + QUP_IO_M_MODES);
|
||||||
|
|
||||||
|
size = QUP_IO_M_OUTPUT_BLOCK_SIZE(iomode);
|
||||||
|
if (size)
|
||||||
|
controller->out_blk_sz = size * 16;
|
||||||
|
else
|
||||||
|
controller->out_blk_sz = 4;
|
||||||
|
|
||||||
|
size = QUP_IO_M_INPUT_BLOCK_SIZE(iomode);
|
||||||
|
if (size)
|
||||||
|
controller->in_blk_sz = size * 16;
|
||||||
|
else
|
||||||
|
controller->in_blk_sz = 4;
|
||||||
|
|
||||||
|
size = QUP_IO_M_OUTPUT_FIFO_SIZE(iomode);
|
||||||
|
controller->out_fifo_sz = controller->out_blk_sz * (2 << size);
|
||||||
|
|
||||||
|
size = QUP_IO_M_INPUT_FIFO_SIZE(iomode);
|
||||||
|
controller->in_fifo_sz = controller->in_blk_sz * (2 << size);
|
||||||
|
|
||||||
|
dev_info(dev, "v.%08x IN:block:%d, fifo:%d, OUT:block:%d, fifo:%d\n",
|
||||||
|
data, controller->in_blk_sz, controller->in_fifo_sz,
|
||||||
|
controller->out_blk_sz, controller->out_fifo_sz);
|
||||||
|
|
||||||
|
writel_relaxed(1, base + QUP_SW_RESET);
|
||||||
|
|
||||||
|
ret = spi_qup_set_state(controller, QUP_STATE_RESET);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "cannot set RESET state\n");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
writel_relaxed(0, base + QUP_OPERATIONAL);
|
||||||
|
writel_relaxed(0, base + QUP_IO_M_MODES);
|
||||||
|
writel_relaxed(0, base + QUP_OPERATIONAL_MASK);
|
||||||
|
writel_relaxed(SPI_ERROR_CLK_UNDER_RUN | SPI_ERROR_CLK_OVER_RUN,
|
||||||
|
base + SPI_ERROR_FLAGS_EN);
|
||||||
|
|
||||||
|
writel_relaxed(0, base + SPI_CONFIG);
|
||||||
|
writel_relaxed(SPI_IO_C_NO_TRI_STATE, base + SPI_IO_CONTROL);
|
||||||
|
|
||||||
|
ret = devm_request_irq(dev, irq, spi_qup_qup_irq,
|
||||||
|
IRQF_TRIGGER_HIGH, pdev->name, controller);
|
||||||
|
if (ret)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
ret = devm_spi_register_master(dev, master);
|
||||||
|
if (ret)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
pm_runtime_set_autosuspend_delay(dev, MSEC_PER_SEC);
|
||||||
|
pm_runtime_use_autosuspend(dev);
|
||||||
|
pm_runtime_set_active(dev);
|
||||||
|
pm_runtime_enable(dev);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
error:
|
||||||
|
clk_disable_unprepare(cclk);
|
||||||
|
clk_disable_unprepare(iclk);
|
||||||
|
spi_master_put(master);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM_RUNTIME
|
||||||
|
static int spi_qup_pm_suspend_runtime(struct device *device)
|
||||||
|
{
|
||||||
|
struct spi_master *master = dev_get_drvdata(device);
|
||||||
|
struct spi_qup *controller = spi_master_get_devdata(master);
|
||||||
|
u32 config;
|
||||||
|
|
||||||
|
/* Enable clocks auto gaiting */
|
||||||
|
config = readl(controller->base + QUP_CONFIG);
|
||||||
|
config |= QUP_CONFIG_CLOCK_AUTO_GATE;
|
||||||
|
writel_relaxed(config, controller->base + QUP_CONFIG);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int spi_qup_pm_resume_runtime(struct device *device)
|
||||||
|
{
|
||||||
|
struct spi_master *master = dev_get_drvdata(device);
|
||||||
|
struct spi_qup *controller = spi_master_get_devdata(master);
|
||||||
|
u32 config;
|
||||||
|
|
||||||
|
/* Disable clocks auto gaiting */
|
||||||
|
config = readl_relaxed(controller->base + QUP_CONFIG);
|
||||||
|
config &= ~QUP_CONFIG_CLOCK_AUTO_GATE;
|
||||||
|
writel_relaxed(config, controller->base + QUP_CONFIG);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_PM_RUNTIME */
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM_SLEEP
|
||||||
|
static int spi_qup_suspend(struct device *device)
|
||||||
|
{
|
||||||
|
struct spi_master *master = dev_get_drvdata(device);
|
||||||
|
struct spi_qup *controller = spi_master_get_devdata(master);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = spi_master_suspend(master);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = spi_qup_set_state(controller, QUP_STATE_RESET);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
clk_disable_unprepare(controller->cclk);
|
||||||
|
clk_disable_unprepare(controller->iclk);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int spi_qup_resume(struct device *device)
|
||||||
|
{
|
||||||
|
struct spi_master *master = dev_get_drvdata(device);
|
||||||
|
struct spi_qup *controller = spi_master_get_devdata(master);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = clk_prepare_enable(controller->iclk);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = clk_prepare_enable(controller->cclk);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = spi_qup_set_state(controller, QUP_STATE_RESET);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return spi_master_resume(master);
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_PM_SLEEP */
|
||||||
|
|
||||||
|
static int spi_qup_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct spi_master *master = dev_get_drvdata(&pdev->dev);
|
||||||
|
struct spi_qup *controller = spi_master_get_devdata(master);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = pm_runtime_get_sync(&pdev->dev);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = spi_qup_set_state(controller, QUP_STATE_RESET);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
clk_disable_unprepare(controller->cclk);
|
||||||
|
clk_disable_unprepare(controller->iclk);
|
||||||
|
|
||||||
|
pm_runtime_put_noidle(&pdev->dev);
|
||||||
|
pm_runtime_disable(&pdev->dev);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct of_device_id spi_qup_dt_match[] = {
|
||||||
|
{ .compatible = "qcom,spi-qup-v2.1.1", },
|
||||||
|
{ .compatible = "qcom,spi-qup-v2.2.1", },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, spi_qup_dt_match);
|
||||||
|
|
||||||
|
static const struct dev_pm_ops spi_qup_dev_pm_ops = {
|
||||||
|
SET_SYSTEM_SLEEP_PM_OPS(spi_qup_suspend, spi_qup_resume)
|
||||||
|
SET_RUNTIME_PM_OPS(spi_qup_pm_suspend_runtime,
|
||||||
|
spi_qup_pm_resume_runtime,
|
||||||
|
NULL)
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct platform_driver spi_qup_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "spi_qup",
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.pm = &spi_qup_dev_pm_ops,
|
||||||
|
.of_match_table = spi_qup_dt_match,
|
||||||
|
},
|
||||||
|
.probe = spi_qup_probe,
|
||||||
|
.remove = spi_qup_remove,
|
||||||
|
};
|
||||||
|
module_platform_driver(spi_qup_driver);
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL v2");
|
||||||
|
MODULE_ALIAS("platform:spi_qup");
|
File diff suppressed because it is too large
Load Diff
@ -122,25 +122,15 @@ static int s3c24xx_spi_update_state(struct spi_device *spi,
|
|||||||
{
|
{
|
||||||
struct s3c24xx_spi *hw = to_hw(spi);
|
struct s3c24xx_spi *hw = to_hw(spi);
|
||||||
struct s3c24xx_spi_devstate *cs = spi->controller_state;
|
struct s3c24xx_spi_devstate *cs = spi->controller_state;
|
||||||
unsigned int bpw;
|
|
||||||
unsigned int hz;
|
unsigned int hz;
|
||||||
unsigned int div;
|
unsigned int div;
|
||||||
unsigned long clk;
|
unsigned long clk;
|
||||||
|
|
||||||
bpw = t ? t->bits_per_word : spi->bits_per_word;
|
|
||||||
hz = t ? t->speed_hz : spi->max_speed_hz;
|
hz = t ? t->speed_hz : spi->max_speed_hz;
|
||||||
|
|
||||||
if (!bpw)
|
|
||||||
bpw = 8;
|
|
||||||
|
|
||||||
if (!hz)
|
if (!hz)
|
||||||
hz = spi->max_speed_hz;
|
hz = spi->max_speed_hz;
|
||||||
|
|
||||||
if (bpw != 8) {
|
|
||||||
dev_err(&spi->dev, "invalid bits-per-word (%d)\n", bpw);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spi->mode != cs->mode) {
|
if (spi->mode != cs->mode) {
|
||||||
u8 spcon = SPCON_DEFAULT | S3C2410_SPCON_ENSCK;
|
u8 spcon = SPCON_DEFAULT | S3C2410_SPCON_ENSCK;
|
||||||
|
|
||||||
@ -543,6 +533,7 @@ static int s3c24xx_spi_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
master->num_chipselect = hw->pdata->num_cs;
|
master->num_chipselect = hw->pdata->num_cs;
|
||||||
master->bus_num = pdata->bus_num;
|
master->bus_num = pdata->bus_num;
|
||||||
|
master->bits_per_word_mask = SPI_BPW_MASK(8);
|
||||||
|
|
||||||
/* setup the state for the bitbang driver */
|
/* setup the state for the bitbang driver */
|
||||||
|
|
||||||
@ -642,6 +633,11 @@ static int s3c24xx_spi_remove(struct platform_device *dev)
|
|||||||
static int s3c24xx_spi_suspend(struct device *dev)
|
static int s3c24xx_spi_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct s3c24xx_spi *hw = dev_get_drvdata(dev);
|
struct s3c24xx_spi *hw = dev_get_drvdata(dev);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = spi_master_suspend(hw->master);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
if (hw->pdata && hw->pdata->gpio_setup)
|
if (hw->pdata && hw->pdata->gpio_setup)
|
||||||
hw->pdata->gpio_setup(hw->pdata, 0);
|
hw->pdata->gpio_setup(hw->pdata, 0);
|
||||||
@ -655,7 +651,7 @@ static int s3c24xx_spi_resume(struct device *dev)
|
|||||||
struct s3c24xx_spi *hw = dev_get_drvdata(dev);
|
struct s3c24xx_spi *hw = dev_get_drvdata(dev);
|
||||||
|
|
||||||
s3c24xx_spi_initialsetup(hw);
|
s3c24xx_spi_initialsetup(hw);
|
||||||
return 0;
|
return spi_master_resume(hw->master);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct dev_pm_ops s3c24xx_spi_pmops = {
|
static const struct dev_pm_ops s3c24xx_spi_pmops = {
|
||||||
|
@ -26,9 +26,13 @@
|
|||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
extern int pm_generic_runtime_suspend(struct device *dev);
|
extern int pm_generic_runtime_suspend(struct device *dev);
|
||||||
extern int pm_generic_runtime_resume(struct device *dev);
|
extern int pm_generic_runtime_resume(struct device *dev);
|
||||||
|
extern int pm_runtime_force_suspend(struct device *dev);
|
||||||
|
extern int pm_runtime_force_resume(struct device *dev);
|
||||||
#else
|
#else
|
||||||
static inline int pm_generic_runtime_suspend(struct device *dev) { return 0; }
|
static inline int pm_generic_runtime_suspend(struct device *dev) { return 0; }
|
||||||
static inline int pm_generic_runtime_resume(struct device *dev) { return 0; }
|
static inline int pm_generic_runtime_resume(struct device *dev) { return 0; }
|
||||||
|
static inline int pm_runtime_force_suspend(struct device *dev) { return 0; }
|
||||||
|
static inline int pm_runtime_force_resume(struct device *dev) { return 0; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_PM_RUNTIME
|
#ifdef CONFIG_PM_RUNTIME
|
||||||
|
Loading…
x
Reference in New Issue
Block a user