mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2024-12-28 16:53:49 +00:00
spi: Updates for v6.13
The only real core work we've got this time around is the completion of the transition to the new host/target naming for the core APIs, Kconfig still needs doing but that's a lot less invasive. Otherwise the big changes are the new drivers that have been added: - Completion of the conversion to spi_alloc_host()/_target() and removal of the old naming. - Cleanups for Rockchip drivers, these brought in a new logging helper in the driver core for warnings during probe. - Support for configuration of the word delay via spidev_test. - Support for AMD HID2 controllers, Apple SPI controller and Realtek SPI-NAND controllers. The Rockchip cleanups -----BEGIN PGP SIGNATURE----- iQEzBAABCgAdFiEEreZoqmdXGLWf4p/qJNaLcl1Uh9AFAmc7QdQACgkQJNaLcl1U h9A7KAf+Od8ORLheHKrokFYWEW1zuiR45EjqWylLk835d3TQn/VfLOouRDhOPKLw wmxy5PjjvI+CHa9JY4TXY6iRTCc8By6fkwRWFZN5KApSC2NQriWiqgTSItFfYiLv yUthZjfRhbfSpf6E/0hq4axpfn+6W/MIWUg7Ag08IEU+GhDd+um8gdpBKsP1BAJF s34Fn3oJNoze0Wwcq5tZ91S1MsP+2vGFGIGC2HA7G2GAXjGFqBZUnIL+zjC1US3j XILAoy4Vx4J0Nn+f+zdGL2m5cm6O49ztaKqUxamVFigwM4va5OSOEpcnFMEPZ8HY 013dIg7tiayUTOTcByCpzfMDWuzHig== =jc6e -----END PGP SIGNATURE----- Merge tag 'spi-v6.13' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi Pull spi updates from Mark Brown: "The only real core work we've got this time around is the completion of the transition to the new host/target naming for the core APIs, Kconfig still needs doing but that's a lot less invasive. Otherwise the big changes are the new drivers that have been added: - Completion of the conversion to spi_alloc_host()/_target() and removal of the old naming. - Cleanups for Rockchip drivers, these brought in a new logging helper in the driver core for warnings during probe. - Support for configuration of the word delay via spidev_test. - Support for AMD HID2 controllers, Apple SPI controller and Realtek SPI-NAND controllers" * tag 'spi-v6.13' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: (58 commits) spi: imx: support word delay spi: imx: pass struct spi_transfer to prepare_transfer() spi: cs42l43: Add GPIO speaker id support to the bridge configuration spi: Delete useless checks spi: apple: Remove unnecessary .owner for apple_spi_driver spi: spidev_test: add support for word delay spi: apple: Add driver for Apple SPI controller spi: dt-bindings: apple,spi: Add binding for Apple SPI controllers spi: Use of_property_present() for non-boolean properties spi: zynqmp-gqspi: Undo runtime PM changes at driver exit time spi: spi-mem: rtl-snand: Correctly handle DMA transfers spi: tegra210-quad: Avoid shift-out-of-bounds spi: axi-spi-engine: Emit trace events for spi transfers dt-bindings: spi: sprd,sc9860-spi: convert to YAML spi: Replace deprecated PCI functions spi: dt-bindings: samsung: Add a compatible for samsung,exynos8895-spi spi: spi-mem: Add Realtek SPI-NAND controller dt-bindings: spi: Add realtek,rtl9301-snand spi: make class structs const spi: dt-bindings: brcm,bcm2835-aux-spi: Convert to dtschema ...
This commit is contained in:
commit
f2ef39727a
20
Documentation/ABI/testing/sysfs-driver-spi-intel
Normal file
20
Documentation/ABI/testing/sysfs-driver-spi-intel
Normal file
@ -0,0 +1,20 @@
|
||||
What: /sys/devices/.../intel_spi_protected
|
||||
Date: Feb 2025
|
||||
KernelVersion: 6.13
|
||||
Contact: Alexander Usyskin <alexander.usyskin@intel.com>
|
||||
Description: This attribute allows the userspace to check if the
|
||||
Intel SPI flash controller is write protected from the host.
|
||||
|
||||
What: /sys/devices/.../intel_spi_locked
|
||||
Date: Feb 2025
|
||||
KernelVersion: 6.13
|
||||
Contact: Alexander Usyskin <alexander.usyskin@intel.com>
|
||||
Description: This attribute allows the user space to check if the
|
||||
Intel SPI flash controller locks supported opcodes.
|
||||
|
||||
What: /sys/devices/.../intel_spi_bios_locked
|
||||
Date: Feb 2025
|
||||
KernelVersion: 6.13
|
||||
Contact: Alexander Usyskin <alexander.usyskin@intel.com>
|
||||
Description: This attribute allows the user space to check if the
|
||||
Intel SPI flash controller BIOS region is locked for writes.
|
62
Documentation/devicetree/bindings/spi/apple,spi.yaml
Normal file
62
Documentation/devicetree/bindings/spi/apple,spi.yaml
Normal file
@ -0,0 +1,62 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/spi/apple,spi.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Apple ARM SoC SPI controller
|
||||
|
||||
allOf:
|
||||
- $ref: spi-controller.yaml#
|
||||
|
||||
maintainers:
|
||||
- Hector Martin <marcan@marcan.st>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- apple,t8103-spi
|
||||
- apple,t8112-spi
|
||||
- apple,t6000-spi
|
||||
- const: apple,spi
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- interrupts
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/apple-aic.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
soc {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
spi@39b104000 {
|
||||
compatible = "apple,t6000-spi", "apple,spi";
|
||||
reg = <0x3 0x9b104000 0x0 0x4000>;
|
||||
interrupt-parent = <&aic>;
|
||||
interrupts = <AIC_IRQ 0 1107 IRQ_TYPE_LEVEL_HIGH>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
clocks = <&clk>;
|
||||
};
|
||||
};
|
@ -1,38 +0,0 @@
|
||||
Broadcom BCM2835 auxiliary SPI1/2 controller
|
||||
|
||||
The BCM2835 contains two forms of SPI master controller, one known simply as
|
||||
SPI0, and the other known as the "Universal SPI Master"; part of the
|
||||
auxiliary block. This binding applies to the SPI1/2 controller.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "brcm,bcm2835-aux-spi".
|
||||
- reg: Should contain register location and length for the spi block
|
||||
- interrupts: Should contain shared interrupt of the aux block
|
||||
- clocks: The clock feeding the SPI controller - needs to
|
||||
point to the auxiliary clock driver of the bcm2835,
|
||||
as this clock will enable the output gate for the specific
|
||||
clock.
|
||||
- cs-gpios: the cs-gpios (native cs is NOT supported)
|
||||
see also spi-bus.txt
|
||||
|
||||
Example:
|
||||
|
||||
spi1@7e215080 {
|
||||
compatible = "brcm,bcm2835-aux-spi";
|
||||
reg = <0x7e215080 0x40>;
|
||||
interrupts = <1 29>;
|
||||
clocks = <&aux_clocks BCM2835_AUX_CLOCK_SPI1>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
cs-gpios = <&gpio 18>, <&gpio 17>, <&gpio 16>;
|
||||
};
|
||||
|
||||
spi2@7e2150c0 {
|
||||
compatible = "brcm,bcm2835-aux-spi";
|
||||
reg = <0x7e2150c0 0x40>;
|
||||
interrupts = <1 29>;
|
||||
clocks = <&aux_clocks BCM2835_AUX_CLOCK_SPI2>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
cs-gpios = <&gpio 43>, <&gpio 44>, <&gpio 45>;
|
||||
};
|
@ -0,0 +1,53 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/spi/brcm,bcm2835-aux-spi.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Broadcom BCM2835 Auxiliary SPI1/2 Controller
|
||||
|
||||
maintainers:
|
||||
- Karan Sanghavi <karansanghvi98@gmail.com>
|
||||
|
||||
description:
|
||||
The BCM2835 contains two forms of SPI master controller. One is known simply
|
||||
as SPI0, and the other as the "Universal SPI Master," which is part of the
|
||||
auxiliary block. This binding applies to the SPI1 and SPI2 auxiliary
|
||||
controllers.
|
||||
|
||||
allOf:
|
||||
- $ref: spi-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- brcm,bcm2835-aux-spi
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/bcm2835-aux.h>
|
||||
spi@7e215080 {
|
||||
compatible = "brcm,bcm2835-aux-spi";
|
||||
reg = <0x7e215080 0x40>;
|
||||
interrupts = <1 29>;
|
||||
clocks = <&aux_clocks BCM2835_AUX_CLOCK_SPI1>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
@ -0,0 +1,62 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/spi/realtek,rtl9301-snand.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: SPI-NAND Flash Controller for Realtek RTL9300 SoCs
|
||||
|
||||
maintainers:
|
||||
- Chris Packham <chris.packham@alliedtelesis.co.nz>
|
||||
|
||||
description:
|
||||
The Realtek RTL9300 SoCs have a built in SPI-NAND controller. It supports
|
||||
typical SPI-NAND page cache operations in single, dual or quad IO mode.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- items:
|
||||
- enum:
|
||||
- realtek,rtl9302b-snand
|
||||
- realtek,rtl9302c-snand
|
||||
- realtek,rtl9303-snand
|
||||
- const: realtek,rtl9301-snand
|
||||
- const: realtek,rtl9301-snand
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/spi/spi-controller.yaml#
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
spi@1a400 {
|
||||
compatible = "realtek,rtl9302c-snand", "realtek,rtl9301-snand";
|
||||
reg = <0x1a400 0x44>;
|
||||
interrupt-parent = <&intc>;
|
||||
interrupts = <19>;
|
||||
clocks = <&lx_clk>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
flash@0 {
|
||||
compatible = "spi-nand";
|
||||
reg = <0>;
|
||||
};
|
||||
};
|
@ -26,6 +26,10 @@ properties:
|
||||
- samsung,exynos850-spi
|
||||
- samsung,exynosautov9-spi
|
||||
- tesla,fsd-spi
|
||||
- items:
|
||||
- enum:
|
||||
- samsung,exynos8895-spi
|
||||
- const: samsung,exynos850-spi
|
||||
- const: samsung,exynos7-spi
|
||||
deprecated: true
|
||||
|
||||
|
@ -1,33 +0,0 @@
|
||||
Spreadtrum SPI Controller
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "sprd,sc9860-spi".
|
||||
- reg: Offset and length of SPI controller register space.
|
||||
- interrupts: Should contain SPI interrupt.
|
||||
- clock-names: Should contain following entries:
|
||||
"spi" for SPI clock,
|
||||
"source" for SPI source (parent) clock,
|
||||
"enable" for SPI module enable clock.
|
||||
- clocks: List of clock input name strings sorted in the same order
|
||||
as the clock-names property.
|
||||
- #address-cells: The number of cells required to define a chip select
|
||||
address on the SPI bus. Should be set to 1.
|
||||
- #size-cells: Should be set to 0.
|
||||
|
||||
Optional properties:
|
||||
dma-names: Should contain names of the SPI used DMA channel.
|
||||
dmas: Should contain DMA channels and DMA slave ids which the SPI used
|
||||
sorted in the same order as the dma-names property.
|
||||
|
||||
Example:
|
||||
spi0: spi@70a00000{
|
||||
compatible = "sprd,sc9860-spi";
|
||||
reg = <0 0x70a00000 0 0x1000>;
|
||||
interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clock-names = "spi", "source","enable";
|
||||
clocks = <&clk_spi0>, <&ext_26m>, <&clk_ap_apb_gates 5>;
|
||||
dma-names = "rx_chn", "tx_chn";
|
||||
dmas = <&apdma 11 11>, <&apdma 12 12>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
@ -9,9 +9,6 @@ title: Xilinx Zynq UltraScale+ MPSoC GQSPI controller
|
||||
maintainers:
|
||||
- Michal Simek <michal.simek@amd.com>
|
||||
|
||||
allOf:
|
||||
- $ref: spi-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
@ -19,6 +16,7 @@ properties:
|
||||
- xlnx,zynqmp-qspi-1.0
|
||||
|
||||
reg:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
interrupts:
|
||||
@ -47,6 +45,24 @@ required:
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
allOf:
|
||||
- $ref: spi-controller.yaml#
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: xlnx,zynqmp-qspi-1.0
|
||||
then:
|
||||
properties:
|
||||
reg:
|
||||
minItems: 2
|
||||
|
||||
else:
|
||||
properties:
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/xlnx-zynqmp-clk.h>
|
||||
|
72
Documentation/devicetree/bindings/spi/sprd,sc9860-spi.yaml
Normal file
72
Documentation/devicetree/bindings/spi/sprd,sc9860-spi.yaml
Normal file
@ -0,0 +1,72 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/spi/sprd,sc9860-spi.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Spreadtrum SC9860 SPI Controller
|
||||
|
||||
maintainers:
|
||||
- Orson Zhai <orsonzhai@gmail.com>
|
||||
- Baolin Wang <baolin.wang7@gmail.com>
|
||||
- Chunyan Zhang <zhang.lyra@gmail.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: sprd,sc9860-spi
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: SPI clock
|
||||
- description: SPI source (parent) clock
|
||||
- description: SPI module enable clock
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: spi
|
||||
- const: source
|
||||
- const: enable
|
||||
|
||||
dmas:
|
||||
maxItems: 2
|
||||
|
||||
dma-names:
|
||||
items:
|
||||
- const: rx_chn
|
||||
- const: tx_chn
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
allOf:
|
||||
- $ref: spi-controller.yaml#
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
spi@70a00000 {
|
||||
compatible = "sprd,sc9860-spi";
|
||||
reg = <0x70a00000 0x1000>;
|
||||
interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clk_spi0>, <&ext_26m>, <&clk_ap_apb_gates 5>;
|
||||
clock-names = "spi", "source", "enable";
|
||||
dmas = <&apdma 11 11>, <&apdma 12 12>;
|
||||
dma-names = "rx_chn", "tx_chn";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
...
|
@ -462,8 +462,8 @@ SLAVE DMA ENGINE
|
||||
devm_acpi_dma_controller_free()
|
||||
|
||||
SPI
|
||||
devm_spi_alloc_master()
|
||||
devm_spi_alloc_slave()
|
||||
devm_spi_alloc_host()
|
||||
devm_spi_alloc_target()
|
||||
devm_spi_optimize_message()
|
||||
devm_spi_register_controller()
|
||||
devm_spi_register_host()
|
||||
|
@ -19504,6 +19504,12 @@ S: Maintained
|
||||
F: Documentation/devicetree/bindings/net/dsa/realtek.yaml
|
||||
F: drivers/net/dsa/realtek/*
|
||||
|
||||
REALTEK SPI-NAND
|
||||
M: Chris Packham <chris.packham@alliedtelesis.co.nz>
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/spi/realtek,rtl9301-snand.yaml
|
||||
F: drivers/spi/spi-realtek-rtl-snand.c
|
||||
|
||||
REALTEK WIRELESS DRIVER (rtlwifi family)
|
||||
M: Ping-Ke Shih <pkshih@realtek.com>
|
||||
L: linux-wireless@vger.kernel.org
|
||||
|
@ -5012,6 +5012,49 @@ define_dev_printk_level(_dev_info, KERN_INFO);
|
||||
|
||||
#endif
|
||||
|
||||
static void __dev_probe_failed(const struct device *dev, int err, bool fatal,
|
||||
const char *fmt, va_list vargsp)
|
||||
{
|
||||
struct va_format vaf;
|
||||
va_list vargs;
|
||||
|
||||
/*
|
||||
* On x86_64 and possibly on other architectures, va_list is actually a
|
||||
* size-1 array containing a structure. As a result, function parameter
|
||||
* vargsp decays from T[1] to T*, and &vargsp has type T** rather than
|
||||
* T(*)[1], which is expected by its assignment to vaf.va below.
|
||||
*
|
||||
* One standard way to solve this mess is by creating a copy in a local
|
||||
* variable of type va_list and then using a pointer to that local copy
|
||||
* instead, which is the approach employed here.
|
||||
*/
|
||||
va_copy(vargs, vargsp);
|
||||
|
||||
vaf.fmt = fmt;
|
||||
vaf.va = &vargs;
|
||||
|
||||
switch (err) {
|
||||
case -EPROBE_DEFER:
|
||||
device_set_deferred_probe_reason(dev, &vaf);
|
||||
dev_dbg(dev, "error %pe: %pV", ERR_PTR(err), &vaf);
|
||||
break;
|
||||
|
||||
case -ENOMEM:
|
||||
/* Don't print anything on -ENOMEM, there's already enough output */
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Log fatal final failures as errors, otherwise produce warnings */
|
||||
if (fatal)
|
||||
dev_err(dev, "error %pe: %pV", ERR_PTR(err), &vaf);
|
||||
else
|
||||
dev_warn(dev, "error %pe: %pV", ERR_PTR(err), &vaf);
|
||||
break;
|
||||
}
|
||||
|
||||
va_end(vargs);
|
||||
}
|
||||
|
||||
/**
|
||||
* dev_err_probe - probe error check and log helper
|
||||
* @dev: the pointer to the struct device
|
||||
@ -5024,7 +5067,7 @@ define_dev_printk_level(_dev_info, KERN_INFO);
|
||||
* -EPROBE_DEFER and propagate error upwards.
|
||||
* In case of -EPROBE_DEFER it sets also defer probe reason, which can be
|
||||
* checked later by reading devices_deferred debugfs attribute.
|
||||
* It replaces code sequence::
|
||||
* It replaces the following code sequence::
|
||||
*
|
||||
* if (err != -EPROBE_DEFER)
|
||||
* dev_err(dev, ...);
|
||||
@ -5036,48 +5079,78 @@ define_dev_printk_level(_dev_info, KERN_INFO);
|
||||
*
|
||||
* return dev_err_probe(dev, err, ...);
|
||||
*
|
||||
* Using this helper in your probe function is totally fine even if @err is
|
||||
* known to never be -EPROBE_DEFER.
|
||||
* Using this helper in your probe function is totally fine even if @err
|
||||
* is known to never be -EPROBE_DEFER.
|
||||
* The benefit compared to a normal dev_err() is the standardized format
|
||||
* of the error code, it being emitted symbolically (i.e. you get "EAGAIN"
|
||||
* instead of "-35") and the fact that the error code is returned which allows
|
||||
* more compact error paths.
|
||||
* of the error code, which is emitted symbolically (i.e. you get "EAGAIN"
|
||||
* instead of "-35"), and having the error code returned allows more
|
||||
* compact error paths.
|
||||
*
|
||||
* Returns @err.
|
||||
*/
|
||||
int dev_err_probe(const struct device *dev, int err, const char *fmt, ...)
|
||||
{
|
||||
struct va_format vaf;
|
||||
va_list args;
|
||||
va_list vargs;
|
||||
|
||||
va_start(args, fmt);
|
||||
vaf.fmt = fmt;
|
||||
vaf.va = &args;
|
||||
va_start(vargs, fmt);
|
||||
|
||||
switch (err) {
|
||||
case -EPROBE_DEFER:
|
||||
device_set_deferred_probe_reason(dev, &vaf);
|
||||
dev_dbg(dev, "error %pe: %pV", ERR_PTR(err), &vaf);
|
||||
break;
|
||||
/* Use dev_err() for logging when err doesn't equal -EPROBE_DEFER */
|
||||
__dev_probe_failed(dev, err, true, fmt, vargs);
|
||||
|
||||
case -ENOMEM:
|
||||
/*
|
||||
* We don't print anything on -ENOMEM, there is already enough
|
||||
* output.
|
||||
*/
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(dev, "error %pe: %pV", ERR_PTR(err), &vaf);
|
||||
break;
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
va_end(vargs);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dev_err_probe);
|
||||
|
||||
/**
|
||||
* dev_warn_probe - probe error check and log helper
|
||||
* @dev: the pointer to the struct device
|
||||
* @err: error value to test
|
||||
* @fmt: printf-style format string
|
||||
* @...: arguments as specified in the format string
|
||||
*
|
||||
* This helper implements common pattern present in probe functions for error
|
||||
* checking: print debug or warning message depending if the error value is
|
||||
* -EPROBE_DEFER and propagate error upwards.
|
||||
* In case of -EPROBE_DEFER it sets also defer probe reason, which can be
|
||||
* checked later by reading devices_deferred debugfs attribute.
|
||||
* It replaces the following code sequence::
|
||||
*
|
||||
* if (err != -EPROBE_DEFER)
|
||||
* dev_warn(dev, ...);
|
||||
* else
|
||||
* dev_dbg(dev, ...);
|
||||
* return err;
|
||||
*
|
||||
* with::
|
||||
*
|
||||
* return dev_warn_probe(dev, err, ...);
|
||||
*
|
||||
* Using this helper in your probe function is totally fine even if @err
|
||||
* is known to never be -EPROBE_DEFER.
|
||||
* The benefit compared to a normal dev_warn() is the standardized format
|
||||
* of the error code, which is emitted symbolically (i.e. you get "EAGAIN"
|
||||
* instead of "-35"), and having the error code returned allows more
|
||||
* compact error paths.
|
||||
*
|
||||
* Returns @err.
|
||||
*/
|
||||
int dev_warn_probe(const struct device *dev, int err, const char *fmt, ...)
|
||||
{
|
||||
va_list vargs;
|
||||
|
||||
va_start(vargs, fmt);
|
||||
|
||||
/* Use dev_warn() for logging when err doesn't equal -EPROBE_DEFER */
|
||||
__dev_probe_failed(dev, err, false, fmt, vargs);
|
||||
|
||||
va_end(vargs);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dev_warn_probe);
|
||||
|
||||
static inline bool fwnode_is_primary(struct fwnode_handle *fwnode)
|
||||
{
|
||||
return fwnode && !IS_ERR(fwnode->secondary);
|
||||
|
@ -175,11 +175,11 @@ int netup_spi_init(struct netup_unidvb_dev *ndev)
|
||||
struct spi_controller *ctlr;
|
||||
struct netup_spi *nspi;
|
||||
|
||||
ctlr = devm_spi_alloc_master(&ndev->pci_dev->dev,
|
||||
sizeof(struct netup_spi));
|
||||
ctlr = devm_spi_alloc_host(&ndev->pci_dev->dev,
|
||||
sizeof(struct netup_spi));
|
||||
if (!ctlr) {
|
||||
dev_err(&ndev->pci_dev->dev,
|
||||
"%s(): unable to alloc SPI master\n", __func__);
|
||||
"%s(): unable to alloc SPI host\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
nspi = spi_controller_get_devdata(ctlr);
|
||||
|
@ -1219,8 +1219,8 @@ static int msi2500_probe(struct usb_interface *intf,
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
/* SPI master adapter */
|
||||
ctlr = spi_alloc_master(dev->dev, 0);
|
||||
/* SPI host adapter */
|
||||
ctlr = spi_alloc_host(dev->dev, 0);
|
||||
if (ctlr == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto err_unregister_v4l2_dev;
|
||||
|
@ -96,6 +96,17 @@ config SPI_AMLOGIC_SPIFC_A1
|
||||
This enables master mode support for the SPIFC (SPI flash
|
||||
controller) available in Amlogic A1 (A113L SoC).
|
||||
|
||||
config SPI_APPLE
|
||||
tristate "Apple SoC SPI Controller platform driver"
|
||||
depends on ARCH_APPLE || COMPILE_TEST
|
||||
help
|
||||
This enables support for the SPI controller present on
|
||||
many Apple SoCs, including the t8103 (M1), t8112 (M2)
|
||||
and t600x (M1 Pro/Max/Ultra). Multiple SPI controller
|
||||
instances are present on the SoC and each connects usually
|
||||
to a single device like spi-nor (nvram), input device controller
|
||||
or fingerprint sensor.
|
||||
|
||||
config SPI_AR934X
|
||||
tristate "Qualcomm Atheros AR934X/QCA95XX SPI controller driver"
|
||||
depends on ATH79 || COMPILE_TEST
|
||||
@ -843,6 +854,17 @@ config SPI_PXA2XX
|
||||
config SPI_PXA2XX_PCI
|
||||
def_tristate SPI_PXA2XX && PCI && COMMON_CLK
|
||||
|
||||
config SPI_REALTEK_SNAND
|
||||
tristate "Realtek SPI-NAND Flash Controller"
|
||||
depends on MACH_REALTEK_RTL || COMPILE_TEST
|
||||
select REGMAP
|
||||
help
|
||||
This enables support for the SPI-NAND Flash controller on
|
||||
Realtek SoCs.
|
||||
|
||||
This driver does not support generic SPI. The implementation
|
||||
only supports the spi-mem interface.
|
||||
|
||||
config SPI_ROCKCHIP
|
||||
tristate "Rockchip SPI controller driver"
|
||||
depends on ARCH_ROCKCHIP || COMPILE_TEST
|
||||
|
@ -19,6 +19,7 @@ obj-$(CONFIG_SPI_ALTERA) += spi-altera-platform.o
|
||||
obj-$(CONFIG_SPI_ALTERA_CORE) += spi-altera-core.o
|
||||
obj-$(CONFIG_SPI_ALTERA_DFL) += spi-altera-dfl.o
|
||||
obj-$(CONFIG_SPI_AMLOGIC_SPIFC_A1) += spi-amlogic-spifc-a1.o
|
||||
obj-$(CONFIG_SPI_APPLE) += spi-apple.o
|
||||
obj-$(CONFIG_SPI_AR934X) += spi-ar934x.o
|
||||
obj-$(CONFIG_SPI_ARMADA_3700) += spi-armada-3700.o
|
||||
obj-$(CONFIG_SPI_ASPEED_SMC) += spi-aspeed-smc.o
|
||||
@ -119,6 +120,7 @@ obj-$(CONFIG_SPI_ROCKCHIP) += spi-rockchip.o
|
||||
obj-$(CONFIG_SPI_ROCKCHIP_SFC) += spi-rockchip-sfc.o
|
||||
obj-$(CONFIG_SPI_RB4XX) += spi-rb4xx.o
|
||||
obj-$(CONFIG_MACH_REALTEK_RTL) += spi-realtek-rtl.o
|
||||
obj-$(CONFIG_SPI_REALTEK_SNAND) += spi-realtek-rtl-snand.o
|
||||
obj-$(CONFIG_SPI_RPCIF) += spi-rpc-if.o
|
||||
obj-$(CONFIG_SPI_RSPI) += spi-rspi.o
|
||||
obj-$(CONFIG_SPI_RZV2M_CSI) += spi-rzv2m-csi.o
|
||||
|
@ -516,21 +516,45 @@ static int atmel_qspi_set_cs_timing(struct spi_device *spi)
|
||||
struct spi_controller *ctrl = spi->controller;
|
||||
struct atmel_qspi *aq = spi_controller_get_devdata(ctrl);
|
||||
unsigned long clk_rate;
|
||||
u32 cs_inactive;
|
||||
u32 cs_setup;
|
||||
u32 cs_hold;
|
||||
int delay;
|
||||
int ret;
|
||||
|
||||
delay = spi_delay_to_ns(&spi->cs_setup, NULL);
|
||||
if (delay <= 0)
|
||||
return delay;
|
||||
|
||||
clk_rate = clk_get_rate(aq->pclk);
|
||||
if (!clk_rate)
|
||||
return -EINVAL;
|
||||
|
||||
/* hold */
|
||||
delay = spi_delay_to_ns(&spi->cs_hold, NULL);
|
||||
if (aq->mr & QSPI_MR_SMM) {
|
||||
if (delay > 0)
|
||||
dev_warn(&aq->pdev->dev,
|
||||
"Ignoring cs_hold, must be 0 in Serial Memory Mode.\n");
|
||||
cs_hold = 0;
|
||||
} else {
|
||||
delay = spi_delay_to_ns(&spi->cs_hold, NULL);
|
||||
if (delay < 0)
|
||||
return delay;
|
||||
|
||||
cs_hold = DIV_ROUND_UP((delay * DIV_ROUND_UP(clk_rate, 1000000)), 32000);
|
||||
}
|
||||
|
||||
/* setup */
|
||||
delay = spi_delay_to_ns(&spi->cs_setup, NULL);
|
||||
if (delay < 0)
|
||||
return delay;
|
||||
|
||||
cs_setup = DIV_ROUND_UP((delay * DIV_ROUND_UP(clk_rate, 1000000)),
|
||||
1000);
|
||||
|
||||
/* inactive */
|
||||
delay = spi_delay_to_ns(&spi->cs_inactive, NULL);
|
||||
if (delay < 0)
|
||||
return delay;
|
||||
cs_inactive = DIV_ROUND_UP((delay * DIV_ROUND_UP(clk_rate, 1000000)), 1000);
|
||||
|
||||
ret = pm_runtime_resume_and_get(ctrl->dev.parent);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
@ -539,6 +563,10 @@ static int atmel_qspi_set_cs_timing(struct spi_device *spi)
|
||||
aq->scr |= QSPI_SCR_DLYBS(cs_setup);
|
||||
atmel_qspi_write(aq->scr, aq, QSPI_SCR);
|
||||
|
||||
aq->mr &= ~(QSPI_MR_DLYBCT_MASK | QSPI_MR_DLYCS_MASK);
|
||||
aq->mr |= QSPI_MR_DLYBCT(cs_hold) | QSPI_MR_DLYCS(cs_inactive);
|
||||
atmel_qspi_write(aq->mr, aq, QSPI_MR);
|
||||
|
||||
pm_runtime_mark_last_busy(ctrl->dev.parent);
|
||||
pm_runtime_put_autosuspend(ctrl->dev.parent);
|
||||
|
||||
@ -840,7 +868,7 @@ static struct platform_driver atmel_qspi_driver = {
|
||||
.pm = pm_ptr(&atmel_qspi_pm_ops),
|
||||
},
|
||||
.probe = atmel_qspi_probe,
|
||||
.remove_new = atmel_qspi_remove,
|
||||
.remove = atmel_qspi_remove,
|
||||
};
|
||||
module_platform_driver(atmel_qspi_driver);
|
||||
|
||||
|
@ -206,13 +206,6 @@ enum airoha_snand_cs {
|
||||
SPI_CHIP_SEL_LOW,
|
||||
};
|
||||
|
||||
struct airoha_snand_dev {
|
||||
size_t buf_len;
|
||||
|
||||
u8 *txrx_buf;
|
||||
dma_addr_t dma_addr;
|
||||
};
|
||||
|
||||
struct airoha_snand_ctrl {
|
||||
struct device *dev;
|
||||
struct regmap *regmap_ctrl;
|
||||
@ -617,9 +610,9 @@ static bool airoha_snand_supports_op(struct spi_mem *mem,
|
||||
|
||||
static int airoha_snand_dirmap_create(struct spi_mem_dirmap_desc *desc)
|
||||
{
|
||||
struct airoha_snand_dev *as_dev = spi_get_ctldata(desc->mem->spi);
|
||||
u8 *txrx_buf = spi_get_ctldata(desc->mem->spi);
|
||||
|
||||
if (!as_dev->txrx_buf)
|
||||
if (!txrx_buf)
|
||||
return -EINVAL;
|
||||
|
||||
if (desc->info.offset + desc->info.length > U32_MAX)
|
||||
@ -634,10 +627,11 @@ static int airoha_snand_dirmap_create(struct spi_mem_dirmap_desc *desc)
|
||||
static ssize_t airoha_snand_dirmap_read(struct spi_mem_dirmap_desc *desc,
|
||||
u64 offs, size_t len, void *buf)
|
||||
{
|
||||
struct spi_device *spi = desc->mem->spi;
|
||||
struct airoha_snand_dev *as_dev = spi_get_ctldata(spi);
|
||||
struct spi_mem_op *op = &desc->info.op_tmpl;
|
||||
struct spi_device *spi = desc->mem->spi;
|
||||
struct airoha_snand_ctrl *as_ctrl;
|
||||
u8 *txrx_buf = spi_get_ctldata(spi);
|
||||
dma_addr_t dma_addr;
|
||||
u32 val, rd_mode;
|
||||
int err;
|
||||
|
||||
@ -662,14 +656,17 @@ static ssize_t airoha_snand_dirmap_read(struct spi_mem_dirmap_desc *desc,
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
dma_sync_single_for_device(as_ctrl->dev, as_dev->dma_addr,
|
||||
as_dev->buf_len, DMA_BIDIRECTIONAL);
|
||||
dma_addr = dma_map_single(as_ctrl->dev, txrx_buf, SPI_NAND_CACHE_SIZE,
|
||||
DMA_FROM_DEVICE);
|
||||
err = dma_mapping_error(as_ctrl->dev, dma_addr);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* set dma addr */
|
||||
err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_STRADDR,
|
||||
as_dev->dma_addr);
|
||||
dma_addr);
|
||||
if (err)
|
||||
return err;
|
||||
goto error_dma_unmap;
|
||||
|
||||
/* set cust sec size */
|
||||
val = as_ctrl->nfi_cfg.sec_size * as_ctrl->nfi_cfg.sec_num;
|
||||
@ -678,58 +675,58 @@ static ssize_t airoha_snand_dirmap_read(struct spi_mem_dirmap_desc *desc,
|
||||
REG_SPI_NFI_SNF_MISC_CTL2,
|
||||
SPI_NFI_READ_DATA_BYTE_NUM, val);
|
||||
if (err)
|
||||
return err;
|
||||
goto error_dma_unmap;
|
||||
|
||||
/* set read command */
|
||||
err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_RD_CTL2,
|
||||
op->cmd.opcode);
|
||||
if (err)
|
||||
return err;
|
||||
goto error_dma_unmap;
|
||||
|
||||
/* set read mode */
|
||||
err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_SNF_MISC_CTL,
|
||||
FIELD_PREP(SPI_NFI_DATA_READ_WR_MODE, rd_mode));
|
||||
if (err)
|
||||
return err;
|
||||
goto error_dma_unmap;
|
||||
|
||||
/* set read addr */
|
||||
err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_RD_CTL3, 0x0);
|
||||
if (err)
|
||||
return err;
|
||||
goto error_dma_unmap;
|
||||
|
||||
/* set nfi read */
|
||||
err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG,
|
||||
SPI_NFI_OPMODE,
|
||||
FIELD_PREP(SPI_NFI_OPMODE, 6));
|
||||
if (err)
|
||||
return err;
|
||||
goto error_dma_unmap;
|
||||
|
||||
err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG,
|
||||
SPI_NFI_READ_MODE | SPI_NFI_DMA_MODE);
|
||||
if (err)
|
||||
return err;
|
||||
goto error_dma_unmap;
|
||||
|
||||
err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_CMD, 0x0);
|
||||
if (err)
|
||||
return err;
|
||||
goto error_dma_unmap;
|
||||
|
||||
/* trigger dma start read */
|
||||
err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
|
||||
SPI_NFI_RD_TRIG);
|
||||
if (err)
|
||||
return err;
|
||||
goto error_dma_unmap;
|
||||
|
||||
err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
|
||||
SPI_NFI_RD_TRIG);
|
||||
if (err)
|
||||
return err;
|
||||
goto error_dma_unmap;
|
||||
|
||||
err = regmap_read_poll_timeout(as_ctrl->regmap_nfi,
|
||||
REG_SPI_NFI_SNF_STA_CTL1, val,
|
||||
(val & SPI_NFI_READ_FROM_CACHE_DONE),
|
||||
0, 1 * USEC_PER_SEC);
|
||||
if (err)
|
||||
return err;
|
||||
goto error_dma_unmap;
|
||||
|
||||
/*
|
||||
* SPI_NFI_READ_FROM_CACHE_DONE bit must be written at the end
|
||||
@ -739,35 +736,41 @@ static ssize_t airoha_snand_dirmap_read(struct spi_mem_dirmap_desc *desc,
|
||||
SPI_NFI_READ_FROM_CACHE_DONE,
|
||||
SPI_NFI_READ_FROM_CACHE_DONE);
|
||||
if (err)
|
||||
return err;
|
||||
goto error_dma_unmap;
|
||||
|
||||
err = regmap_read_poll_timeout(as_ctrl->regmap_nfi, REG_SPI_NFI_INTR,
|
||||
val, (val & SPI_NFI_AHB_DONE), 0,
|
||||
1 * USEC_PER_SEC);
|
||||
if (err)
|
||||
return err;
|
||||
goto error_dma_unmap;
|
||||
|
||||
/* DMA read need delay for data ready from controller to DRAM */
|
||||
udelay(1);
|
||||
|
||||
dma_sync_single_for_cpu(as_ctrl->dev, as_dev->dma_addr,
|
||||
as_dev->buf_len, DMA_BIDIRECTIONAL);
|
||||
dma_unmap_single(as_ctrl->dev, dma_addr, SPI_NAND_CACHE_SIZE,
|
||||
DMA_FROM_DEVICE);
|
||||
err = airoha_snand_set_mode(as_ctrl, SPI_MODE_MANUAL);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
memcpy(buf, as_dev->txrx_buf + offs, len);
|
||||
memcpy(buf, txrx_buf + offs, len);
|
||||
|
||||
return len;
|
||||
|
||||
error_dma_unmap:
|
||||
dma_unmap_single(as_ctrl->dev, dma_addr, SPI_NAND_CACHE_SIZE,
|
||||
DMA_FROM_DEVICE);
|
||||
return err;
|
||||
}
|
||||
|
||||
static ssize_t airoha_snand_dirmap_write(struct spi_mem_dirmap_desc *desc,
|
||||
u64 offs, size_t len, const void *buf)
|
||||
{
|
||||
struct spi_device *spi = desc->mem->spi;
|
||||
struct airoha_snand_dev *as_dev = spi_get_ctldata(spi);
|
||||
struct spi_mem_op *op = &desc->info.op_tmpl;
|
||||
struct spi_device *spi = desc->mem->spi;
|
||||
u8 *txrx_buf = spi_get_ctldata(spi);
|
||||
struct airoha_snand_ctrl *as_ctrl;
|
||||
dma_addr_t dma_addr;
|
||||
u32 wr_mode, val;
|
||||
int err;
|
||||
|
||||
@ -776,19 +779,20 @@ static ssize_t airoha_snand_dirmap_write(struct spi_mem_dirmap_desc *desc,
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
dma_sync_single_for_cpu(as_ctrl->dev, as_dev->dma_addr,
|
||||
as_dev->buf_len, DMA_BIDIRECTIONAL);
|
||||
memcpy(as_dev->txrx_buf + offs, buf, len);
|
||||
dma_sync_single_for_device(as_ctrl->dev, as_dev->dma_addr,
|
||||
as_dev->buf_len, DMA_BIDIRECTIONAL);
|
||||
memcpy(txrx_buf + offs, buf, len);
|
||||
dma_addr = dma_map_single(as_ctrl->dev, txrx_buf, SPI_NAND_CACHE_SIZE,
|
||||
DMA_TO_DEVICE);
|
||||
err = dma_mapping_error(as_ctrl->dev, dma_addr);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = airoha_snand_set_mode(as_ctrl, SPI_MODE_DMA);
|
||||
if (err < 0)
|
||||
return err;
|
||||
goto error_dma_unmap;
|
||||
|
||||
err = airoha_snand_nfi_config(as_ctrl);
|
||||
if (err)
|
||||
return err;
|
||||
goto error_dma_unmap;
|
||||
|
||||
if (op->cmd.opcode == SPI_NAND_OP_PROGRAM_LOAD_QUAD ||
|
||||
op->cmd.opcode == SPI_NAND_OP_PROGRAM_LOAD_RAMDON_QUAD)
|
||||
@ -797,9 +801,9 @@ static ssize_t airoha_snand_dirmap_write(struct spi_mem_dirmap_desc *desc,
|
||||
wr_mode = 0;
|
||||
|
||||
err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_STRADDR,
|
||||
as_dev->dma_addr);
|
||||
dma_addr);
|
||||
if (err)
|
||||
return err;
|
||||
goto error_dma_unmap;
|
||||
|
||||
val = FIELD_PREP(SPI_NFI_PROG_LOAD_BYTE_NUM,
|
||||
as_ctrl->nfi_cfg.sec_size * as_ctrl->nfi_cfg.sec_num);
|
||||
@ -807,65 +811,65 @@ static ssize_t airoha_snand_dirmap_write(struct spi_mem_dirmap_desc *desc,
|
||||
REG_SPI_NFI_SNF_MISC_CTL2,
|
||||
SPI_NFI_PROG_LOAD_BYTE_NUM, val);
|
||||
if (err)
|
||||
return err;
|
||||
goto error_dma_unmap;
|
||||
|
||||
err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_PG_CTL1,
|
||||
FIELD_PREP(SPI_NFI_PG_LOAD_CMD,
|
||||
op->cmd.opcode));
|
||||
if (err)
|
||||
return err;
|
||||
goto error_dma_unmap;
|
||||
|
||||
err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_SNF_MISC_CTL,
|
||||
FIELD_PREP(SPI_NFI_DATA_READ_WR_MODE, wr_mode));
|
||||
if (err)
|
||||
return err;
|
||||
goto error_dma_unmap;
|
||||
|
||||
err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_PG_CTL2, 0x0);
|
||||
if (err)
|
||||
return err;
|
||||
goto error_dma_unmap;
|
||||
|
||||
err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG,
|
||||
SPI_NFI_READ_MODE);
|
||||
if (err)
|
||||
return err;
|
||||
goto error_dma_unmap;
|
||||
|
||||
err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG,
|
||||
SPI_NFI_OPMODE,
|
||||
FIELD_PREP(SPI_NFI_OPMODE, 3));
|
||||
if (err)
|
||||
return err;
|
||||
goto error_dma_unmap;
|
||||
|
||||
err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG,
|
||||
SPI_NFI_DMA_MODE);
|
||||
if (err)
|
||||
return err;
|
||||
goto error_dma_unmap;
|
||||
|
||||
err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_CMD, 0x80);
|
||||
if (err)
|
||||
return err;
|
||||
goto error_dma_unmap;
|
||||
|
||||
err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
|
||||
SPI_NFI_WR_TRIG);
|
||||
if (err)
|
||||
return err;
|
||||
goto error_dma_unmap;
|
||||
|
||||
err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
|
||||
SPI_NFI_WR_TRIG);
|
||||
if (err)
|
||||
return err;
|
||||
goto error_dma_unmap;
|
||||
|
||||
err = regmap_read_poll_timeout(as_ctrl->regmap_nfi, REG_SPI_NFI_INTR,
|
||||
val, (val & SPI_NFI_AHB_DONE), 0,
|
||||
1 * USEC_PER_SEC);
|
||||
if (err)
|
||||
return err;
|
||||
goto error_dma_unmap;
|
||||
|
||||
err = regmap_read_poll_timeout(as_ctrl->regmap_nfi,
|
||||
REG_SPI_NFI_SNF_STA_CTL1, val,
|
||||
(val & SPI_NFI_LOAD_TO_CACHE_DONE),
|
||||
0, 1 * USEC_PER_SEC);
|
||||
if (err)
|
||||
return err;
|
||||
goto error_dma_unmap;
|
||||
|
||||
/*
|
||||
* SPI_NFI_LOAD_TO_CACHE_DONE bit must be written at the end
|
||||
@ -875,13 +879,20 @@ static ssize_t airoha_snand_dirmap_write(struct spi_mem_dirmap_desc *desc,
|
||||
SPI_NFI_LOAD_TO_CACHE_DONE,
|
||||
SPI_NFI_LOAD_TO_CACHE_DONE);
|
||||
if (err)
|
||||
return err;
|
||||
goto error_dma_unmap;
|
||||
|
||||
dma_unmap_single(as_ctrl->dev, dma_addr, SPI_NAND_CACHE_SIZE,
|
||||
DMA_TO_DEVICE);
|
||||
err = airoha_snand_set_mode(as_ctrl, SPI_MODE_MANUAL);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return len;
|
||||
|
||||
error_dma_unmap:
|
||||
dma_unmap_single(as_ctrl->dev, dma_addr, SPI_NAND_CACHE_SIZE,
|
||||
DMA_TO_DEVICE);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int airoha_snand_exec_op(struct spi_mem *mem,
|
||||
@ -956,42 +967,20 @@ static const struct spi_controller_mem_ops airoha_snand_mem_ops = {
|
||||
static int airoha_snand_setup(struct spi_device *spi)
|
||||
{
|
||||
struct airoha_snand_ctrl *as_ctrl;
|
||||
struct airoha_snand_dev *as_dev;
|
||||
|
||||
as_ctrl = spi_controller_get_devdata(spi->controller);
|
||||
|
||||
as_dev = devm_kzalloc(as_ctrl->dev, sizeof(*as_dev), GFP_KERNEL);
|
||||
if (!as_dev)
|
||||
return -ENOMEM;
|
||||
u8 *txrx_buf;
|
||||
|
||||
/* prepare device buffer */
|
||||
as_dev->buf_len = SPI_NAND_CACHE_SIZE;
|
||||
as_dev->txrx_buf = devm_kzalloc(as_ctrl->dev, as_dev->buf_len,
|
||||
GFP_KERNEL);
|
||||
if (!as_dev->txrx_buf)
|
||||
as_ctrl = spi_controller_get_devdata(spi->controller);
|
||||
txrx_buf = devm_kzalloc(as_ctrl->dev, SPI_NAND_CACHE_SIZE,
|
||||
GFP_KERNEL);
|
||||
if (!txrx_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
as_dev->dma_addr = dma_map_single(as_ctrl->dev, as_dev->txrx_buf,
|
||||
as_dev->buf_len, DMA_BIDIRECTIONAL);
|
||||
if (dma_mapping_error(as_ctrl->dev, as_dev->dma_addr))
|
||||
return -ENOMEM;
|
||||
|
||||
spi_set_ctldata(spi, as_dev);
|
||||
spi_set_ctldata(spi, txrx_buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void airoha_snand_cleanup(struct spi_device *spi)
|
||||
{
|
||||
struct airoha_snand_dev *as_dev = spi_get_ctldata(spi);
|
||||
struct airoha_snand_ctrl *as_ctrl;
|
||||
|
||||
as_ctrl = spi_controller_get_devdata(spi->controller);
|
||||
dma_unmap_single(as_ctrl->dev, as_dev->dma_addr,
|
||||
as_dev->buf_len, DMA_BIDIRECTIONAL);
|
||||
spi_set_ctldata(spi, NULL);
|
||||
}
|
||||
|
||||
static int airoha_snand_nfi_setup(struct airoha_snand_ctrl *as_ctrl)
|
||||
{
|
||||
u32 val, sec_size, sec_num;
|
||||
@ -1093,7 +1082,6 @@ static int airoha_snand_probe(struct platform_device *pdev)
|
||||
ctrl->bits_per_word_mask = SPI_BPW_MASK(8);
|
||||
ctrl->mode_bits = SPI_RX_DUAL;
|
||||
ctrl->setup = airoha_snand_setup;
|
||||
ctrl->cleanup = airoha_snand_cleanup;
|
||||
device_set_node(&ctrl->dev, dev_fwnode(dev));
|
||||
|
||||
err = airoha_snand_nfi_setup(as_ctrl);
|
||||
|
@ -7,12 +7,14 @@
|
||||
// Author: Sanjay R Mehta <sanju.mehta@amd.com>
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io-64-nonatomic-lo-hi.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/spi/spi-mem.h>
|
||||
|
||||
#define AMD_SPI_CTRL0_REG 0x00
|
||||
@ -33,10 +35,12 @@
|
||||
#define AMD_SPI_TX_COUNT_REG 0x48
|
||||
#define AMD_SPI_RX_COUNT_REG 0x4B
|
||||
#define AMD_SPI_STATUS_REG 0x4C
|
||||
#define AMD_SPI_ADDR32CTRL_REG 0x50
|
||||
|
||||
#define AMD_SPI_FIFO_SIZE 70
|
||||
#define AMD_SPI_MEM_SIZE 200
|
||||
#define AMD_SPI_MAX_DATA 64
|
||||
#define AMD_SPI_HID2_DMA_SIZE 4096
|
||||
|
||||
#define AMD_SPI_ENA_REG 0x20
|
||||
#define AMD_SPI_ALT_SPD_SHIFT 20
|
||||
@ -47,17 +51,46 @@
|
||||
#define AMD_SPI_SPD7_SHIFT 8
|
||||
#define AMD_SPI_SPD7_MASK GENMASK(13, AMD_SPI_SPD7_SHIFT)
|
||||
|
||||
#define AMD_SPI_HID2_INPUT_RING_BUF0 0X100
|
||||
#define AMD_SPI_HID2_CNTRL 0x150
|
||||
#define AMD_SPI_HID2_INT_STATUS 0x154
|
||||
#define AMD_SPI_HID2_CMD_START 0x156
|
||||
#define AMD_SPI_HID2_INT_MASK 0x158
|
||||
#define AMD_SPI_HID2_READ_CNTRL0 0x170
|
||||
#define AMD_SPI_HID2_READ_CNTRL1 0x174
|
||||
#define AMD_SPI_HID2_READ_CNTRL2 0x180
|
||||
|
||||
#define AMD_SPI_MAX_HZ 100000000
|
||||
#define AMD_SPI_MIN_HZ 800000
|
||||
|
||||
#define AMD_SPI_IO_SLEEP_US 20
|
||||
#define AMD_SPI_IO_TIMEOUT_US 2000000
|
||||
|
||||
/* SPI read command opcodes */
|
||||
#define AMD_SPI_OP_READ 0x03 /* Read data bytes (low frequency) */
|
||||
#define AMD_SPI_OP_READ_FAST 0x0b /* Read data bytes (high frequency) */
|
||||
#define AMD_SPI_OP_READ_1_1_2 0x3b /* Read data bytes (Dual Output SPI) */
|
||||
#define AMD_SPI_OP_READ_1_2_2 0xbb /* Read data bytes (Dual I/O SPI) */
|
||||
#define AMD_SPI_OP_READ_1_1_4 0x6b /* Read data bytes (Quad Output SPI) */
|
||||
#define AMD_SPI_OP_READ_1_4_4 0xeb /* Read data bytes (Quad I/O SPI) */
|
||||
|
||||
/* SPI read command opcodes - 4B address */
|
||||
#define AMD_SPI_OP_READ_FAST_4B 0x0c /* Read data bytes (high frequency) */
|
||||
#define AMD_SPI_OP_READ_1_1_2_4B 0x3c /* Read data bytes (Dual Output SPI) */
|
||||
#define AMD_SPI_OP_READ_1_2_2_4B 0xbc /* Read data bytes (Dual I/O SPI) */
|
||||
#define AMD_SPI_OP_READ_1_1_4_4B 0x6c /* Read data bytes (Quad Output SPI) */
|
||||
#define AMD_SPI_OP_READ_1_4_4_4B 0xec /* Read data bytes (Quad I/O SPI) */
|
||||
|
||||
/**
|
||||
* enum amd_spi_versions - SPI controller versions
|
||||
* @AMD_SPI_V1: AMDI0061 hardware version
|
||||
* @AMD_SPI_V2: AMDI0062 hardware version
|
||||
* @AMD_HID2_SPI: AMDI0063 hardware version
|
||||
*/
|
||||
enum amd_spi_versions {
|
||||
AMD_SPI_V1 = 1,
|
||||
AMD_SPI_V2,
|
||||
AMD_HID2_SPI,
|
||||
};
|
||||
|
||||
enum amd_spi_speed {
|
||||
@ -88,23 +121,27 @@ struct amd_spi_freq {
|
||||
/**
|
||||
* struct amd_spi - SPI driver instance
|
||||
* @io_remap_addr: Start address of the SPI controller registers
|
||||
* @phy_dma_buf: Physical address of DMA buffer
|
||||
* @dma_virt_addr: Virtual address of DMA buffer
|
||||
* @version: SPI controller hardware version
|
||||
* @speed_hz: Device frequency
|
||||
*/
|
||||
struct amd_spi {
|
||||
void __iomem *io_remap_addr;
|
||||
dma_addr_t phy_dma_buf;
|
||||
void *dma_virt_addr;
|
||||
enum amd_spi_versions version;
|
||||
unsigned int speed_hz;
|
||||
};
|
||||
|
||||
static inline u8 amd_spi_readreg8(struct amd_spi *amd_spi, int idx)
|
||||
{
|
||||
return ioread8((u8 __iomem *)amd_spi->io_remap_addr + idx);
|
||||
return readb((u8 __iomem *)amd_spi->io_remap_addr + idx);
|
||||
}
|
||||
|
||||
static inline void amd_spi_writereg8(struct amd_spi *amd_spi, int idx, u8 val)
|
||||
{
|
||||
iowrite8(val, ((u8 __iomem *)amd_spi->io_remap_addr + idx));
|
||||
writeb(val, ((u8 __iomem *)amd_spi->io_remap_addr + idx));
|
||||
}
|
||||
|
||||
static void amd_spi_setclear_reg8(struct amd_spi *amd_spi, int idx, u8 set, u8 clear)
|
||||
@ -115,14 +152,34 @@ static void amd_spi_setclear_reg8(struct amd_spi *amd_spi, int idx, u8 set, u8 c
|
||||
amd_spi_writereg8(amd_spi, idx, tmp);
|
||||
}
|
||||
|
||||
static inline u16 amd_spi_readreg16(struct amd_spi *amd_spi, int idx)
|
||||
{
|
||||
return readw((u8 __iomem *)amd_spi->io_remap_addr + idx);
|
||||
}
|
||||
|
||||
static inline void amd_spi_writereg16(struct amd_spi *amd_spi, int idx, u16 val)
|
||||
{
|
||||
writew(val, ((u8 __iomem *)amd_spi->io_remap_addr + idx));
|
||||
}
|
||||
|
||||
static inline u32 amd_spi_readreg32(struct amd_spi *amd_spi, int idx)
|
||||
{
|
||||
return ioread32((u8 __iomem *)amd_spi->io_remap_addr + idx);
|
||||
return readl((u8 __iomem *)amd_spi->io_remap_addr + idx);
|
||||
}
|
||||
|
||||
static inline void amd_spi_writereg32(struct amd_spi *amd_spi, int idx, u32 val)
|
||||
{
|
||||
iowrite32(val, ((u8 __iomem *)amd_spi->io_remap_addr + idx));
|
||||
writel(val, ((u8 __iomem *)amd_spi->io_remap_addr + idx));
|
||||
}
|
||||
|
||||
static inline u64 amd_spi_readreg64(struct amd_spi *amd_spi, int idx)
|
||||
{
|
||||
return readq((u8 __iomem *)amd_spi->io_remap_addr + idx);
|
||||
}
|
||||
|
||||
static inline void amd_spi_writereg64(struct amd_spi *amd_spi, int idx, u64 val)
|
||||
{
|
||||
writeq(val, ((u8 __iomem *)amd_spi->io_remap_addr + idx));
|
||||
}
|
||||
|
||||
static inline void amd_spi_setclear_reg32(struct amd_spi *amd_spi, int idx, u32 set, u32 clear)
|
||||
@ -156,6 +213,7 @@ static int amd_spi_set_opcode(struct amd_spi *amd_spi, u8 cmd_opcode)
|
||||
AMD_SPI_OPCODE_MASK);
|
||||
return 0;
|
||||
case AMD_SPI_V2:
|
||||
case AMD_HID2_SPI:
|
||||
amd_spi_writereg8(amd_spi, AMD_SPI_OPCODE_REG, cmd_opcode);
|
||||
return 0;
|
||||
default:
|
||||
@ -165,12 +223,12 @@ static int amd_spi_set_opcode(struct amd_spi *amd_spi, u8 cmd_opcode)
|
||||
|
||||
static inline void amd_spi_set_rx_count(struct amd_spi *amd_spi, u8 rx_count)
|
||||
{
|
||||
amd_spi_setclear_reg8(amd_spi, AMD_SPI_RX_COUNT_REG, rx_count, 0xff);
|
||||
amd_spi_writereg8(amd_spi, AMD_SPI_RX_COUNT_REG, rx_count);
|
||||
}
|
||||
|
||||
static inline void amd_spi_set_tx_count(struct amd_spi *amd_spi, u8 tx_count)
|
||||
{
|
||||
amd_spi_setclear_reg8(amd_spi, AMD_SPI_TX_COUNT_REG, tx_count, 0xff);
|
||||
amd_spi_writereg8(amd_spi, AMD_SPI_TX_COUNT_REG, tx_count);
|
||||
}
|
||||
|
||||
static int amd_spi_busy_wait(struct amd_spi *amd_spi)
|
||||
@ -183,6 +241,7 @@ static int amd_spi_busy_wait(struct amd_spi *amd_spi)
|
||||
reg = AMD_SPI_CTRL0_REG;
|
||||
break;
|
||||
case AMD_SPI_V2:
|
||||
case AMD_HID2_SPI:
|
||||
reg = AMD_SPI_STATUS_REG;
|
||||
break;
|
||||
default:
|
||||
@ -208,6 +267,7 @@ static int amd_spi_execute_opcode(struct amd_spi *amd_spi)
|
||||
AMD_SPI_EXEC_CMD);
|
||||
return 0;
|
||||
case AMD_SPI_V2:
|
||||
case AMD_HID2_SPI:
|
||||
/* Trigger the command execution */
|
||||
amd_spi_setclear_reg8(amd_spi, AMD_SPI_CMD_TRIGGER_REG,
|
||||
AMD_SPI_TRIGGER_CMD, AMD_SPI_TRIGGER_CMD);
|
||||
@ -349,6 +409,7 @@ static inline int amd_spi_fifo_xfer(struct amd_spi *amd_spi,
|
||||
case AMD_SPI_V1:
|
||||
break;
|
||||
case AMD_SPI_V2:
|
||||
case AMD_HID2_SPI:
|
||||
amd_spi_clear_chip(amd_spi, spi_get_chipselect(message->spi, 0));
|
||||
break;
|
||||
default:
|
||||
@ -360,20 +421,82 @@ static inline int amd_spi_fifo_xfer(struct amd_spi *amd_spi,
|
||||
return message->status;
|
||||
}
|
||||
|
||||
static inline bool amd_is_spi_read_cmd_4b(const u16 op)
|
||||
{
|
||||
switch (op) {
|
||||
case AMD_SPI_OP_READ_FAST_4B:
|
||||
case AMD_SPI_OP_READ_1_1_2_4B:
|
||||
case AMD_SPI_OP_READ_1_2_2_4B:
|
||||
case AMD_SPI_OP_READ_1_1_4_4B:
|
||||
case AMD_SPI_OP_READ_1_4_4_4B:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool amd_is_spi_read_cmd(const u16 op)
|
||||
{
|
||||
switch (op) {
|
||||
case AMD_SPI_OP_READ:
|
||||
case AMD_SPI_OP_READ_FAST:
|
||||
case AMD_SPI_OP_READ_1_1_2:
|
||||
case AMD_SPI_OP_READ_1_2_2:
|
||||
case AMD_SPI_OP_READ_1_1_4:
|
||||
case AMD_SPI_OP_READ_1_4_4:
|
||||
return true;
|
||||
default:
|
||||
return amd_is_spi_read_cmd_4b(op);
|
||||
}
|
||||
}
|
||||
|
||||
static bool amd_spi_supports_op(struct spi_mem *mem,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
struct amd_spi *amd_spi = spi_controller_get_devdata(mem->spi->controller);
|
||||
|
||||
/* bus width is number of IO lines used to transmit */
|
||||
if (op->cmd.buswidth > 1 || op->addr.buswidth > 1 ||
|
||||
op->data.buswidth > 1 || op->data.nbytes > AMD_SPI_MAX_DATA)
|
||||
if (op->cmd.buswidth > 1 || op->addr.buswidth > 4)
|
||||
return false;
|
||||
|
||||
/* AMD SPI controllers support quad mode only for read operations */
|
||||
if (amd_is_spi_read_cmd(op->cmd.opcode)) {
|
||||
if (op->data.buswidth > 4)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* HID2 SPI controller supports DMA read up to 4K bytes and
|
||||
* doesn't support 4-byte address commands.
|
||||
*/
|
||||
if (amd_spi->version == AMD_HID2_SPI) {
|
||||
if (amd_is_spi_read_cmd_4b(op->cmd.opcode) ||
|
||||
op->data.nbytes > AMD_SPI_HID2_DMA_SIZE)
|
||||
return false;
|
||||
} else if (op->data.nbytes > AMD_SPI_MAX_DATA) {
|
||||
return false;
|
||||
}
|
||||
} else if (op->data.buswidth > 1 || op->data.nbytes > AMD_SPI_MAX_DATA) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return spi_mem_default_supports_op(mem, op);
|
||||
}
|
||||
|
||||
static int amd_spi_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
|
||||
{
|
||||
op->data.nbytes = clamp_val(op->data.nbytes, 0, AMD_SPI_MAX_DATA);
|
||||
struct amd_spi *amd_spi = spi_controller_get_devdata(mem->spi->controller);
|
||||
|
||||
/*
|
||||
* HID2 SPI controller DMA read mode supports reading up to 4k
|
||||
* bytes in single transaction, where as SPI0 and HID2 SPI
|
||||
* controller index mode supports maximum of 64 bytes in a single
|
||||
* transaction.
|
||||
*/
|
||||
if (amd_spi->version == AMD_HID2_SPI && amd_is_spi_read_cmd(op->cmd.opcode))
|
||||
op->data.nbytes = clamp_val(op->data.nbytes, 0, AMD_SPI_HID2_DMA_SIZE);
|
||||
else
|
||||
op->data.nbytes = clamp_val(op->data.nbytes, 0, AMD_SPI_MAX_DATA);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -397,15 +520,23 @@ static void amd_spi_mem_data_out(struct amd_spi *amd_spi,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
int base_addr = AMD_SPI_FIFO_BASE + op->addr.nbytes;
|
||||
u8 *buf = (u8 *)op->data.buf.out;
|
||||
u64 *buf_64 = (u64 *)op->data.buf.out;
|
||||
u32 nbytes = op->data.nbytes;
|
||||
u32 left_data = nbytes;
|
||||
u8 *buf;
|
||||
int i;
|
||||
|
||||
amd_spi_set_opcode(amd_spi, op->cmd.opcode);
|
||||
amd_spi_set_addr(amd_spi, op);
|
||||
|
||||
for (i = 0; i < nbytes; i++)
|
||||
amd_spi_writereg8(amd_spi, (base_addr + i), buf[i]);
|
||||
for (i = 0; left_data >= 8; i++, left_data -= 8)
|
||||
amd_spi_writereg64(amd_spi, base_addr + op->dummy.nbytes + (i * 8), *buf_64++);
|
||||
|
||||
buf = (u8 *)buf_64;
|
||||
for (i = 0; i < left_data; i++) {
|
||||
amd_spi_writereg8(amd_spi, base_addr + op->dummy.nbytes + nbytes + i - left_data,
|
||||
buf[i]);
|
||||
}
|
||||
|
||||
amd_spi_set_tx_count(amd_spi, op->addr.nbytes + op->data.nbytes);
|
||||
amd_spi_set_rx_count(amd_spi, 0);
|
||||
@ -413,26 +544,128 @@ static void amd_spi_mem_data_out(struct amd_spi *amd_spi,
|
||||
amd_spi_execute_opcode(amd_spi);
|
||||
}
|
||||
|
||||
static void amd_spi_hiddma_read(struct amd_spi *amd_spi, const struct spi_mem_op *op)
|
||||
{
|
||||
u16 hid_cmd_start, val;
|
||||
u32 hid_regval;
|
||||
|
||||
/* Set the opcode in hid2_read_control0 register */
|
||||
hid_regval = amd_spi_readreg32(amd_spi, AMD_SPI_HID2_READ_CNTRL0);
|
||||
hid_regval = (hid_regval & ~GENMASK(7, 0)) | op->cmd.opcode;
|
||||
|
||||
/*
|
||||
* Program the address in the hid2_read_control0 register [8:31]. The address should
|
||||
* be written starting from the 8th bit of the register, requiring an 8-bit shift.
|
||||
* Additionally, to convert a 2-byte spinand address to a 3-byte address, another
|
||||
* 8-bit shift is needed. Therefore, a total shift of 16 bits is required.
|
||||
*/
|
||||
hid_regval = (hid_regval & ~GENMASK(31, 8)) | (op->addr.val << 16);
|
||||
amd_spi_writereg32(amd_spi, AMD_SPI_HID2_READ_CNTRL0, hid_regval);
|
||||
|
||||
/* Configure dummy clock cycles for fast read, dual, quad I/O commands */
|
||||
hid_regval = amd_spi_readreg32(amd_spi, AMD_SPI_HID2_READ_CNTRL2);
|
||||
/* Fast read dummy cycle */
|
||||
hid_regval &= ~GENMASK(4, 0);
|
||||
|
||||
/* Fast read Dual I/O dummy cycle */
|
||||
hid_regval &= ~GENMASK(12, 8);
|
||||
|
||||
/* Fast read Quad I/O dummy cycle */
|
||||
hid_regval = (hid_regval & ~GENMASK(20, 16)) | BIT(17);
|
||||
|
||||
/* Set no of preamble bytecount */
|
||||
hid_regval &= ~GENMASK(27, 24);
|
||||
amd_spi_writereg32(amd_spi, AMD_SPI_HID2_READ_CNTRL2, hid_regval);
|
||||
|
||||
/*
|
||||
* Program the HID2 Input Ring Buffer0. 4k aligned buf_memory_addr[31:12],
|
||||
* buf_size[4:0], end_input_ring[5].
|
||||
*/
|
||||
hid_regval = amd_spi->phy_dma_buf | BIT(5) | BIT(0);
|
||||
amd_spi_writereg32(amd_spi, AMD_SPI_HID2_INPUT_RING_BUF0, hid_regval);
|
||||
|
||||
/* Program max read length(no of DWs) in hid2_read_control1 register */
|
||||
hid_regval = amd_spi_readreg32(amd_spi, AMD_SPI_HID2_READ_CNTRL1);
|
||||
hid_regval = (hid_regval & ~GENMASK(15, 0)) | ((op->data.nbytes / 4) - 1);
|
||||
amd_spi_writereg32(amd_spi, AMD_SPI_HID2_READ_CNTRL1, hid_regval);
|
||||
|
||||
/* Set cmd start bit in hid2_cmd_start register to trigger HID basic read operation */
|
||||
hid_cmd_start = amd_spi_readreg16(amd_spi, AMD_SPI_HID2_CMD_START);
|
||||
amd_spi_writereg16(amd_spi, AMD_SPI_HID2_CMD_START, (hid_cmd_start | BIT(3)));
|
||||
|
||||
/* Check interrupt status of HIDDMA basic read operation in hid2_int_status register */
|
||||
readw_poll_timeout(amd_spi->io_remap_addr + AMD_SPI_HID2_INT_STATUS, val,
|
||||
(val & BIT(3)), AMD_SPI_IO_SLEEP_US, AMD_SPI_IO_TIMEOUT_US);
|
||||
|
||||
/* Clear the interrupts by writing to hid2_int_status register */
|
||||
val = amd_spi_readreg16(amd_spi, AMD_SPI_HID2_INT_STATUS);
|
||||
amd_spi_writereg16(amd_spi, AMD_SPI_HID2_INT_STATUS, val);
|
||||
}
|
||||
|
||||
static void amd_spi_mem_data_in(struct amd_spi *amd_spi,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
int offset = (op->addr.nbytes == 0) ? 0 : 1;
|
||||
u8 *buf = (u8 *)op->data.buf.in;
|
||||
int base_addr = AMD_SPI_FIFO_BASE + op->addr.nbytes;
|
||||
u64 *buf_64 = (u64 *)op->data.buf.in;
|
||||
u32 nbytes = op->data.nbytes;
|
||||
int base_addr, i;
|
||||
u32 left_data = nbytes;
|
||||
u32 data;
|
||||
u8 *buf;
|
||||
int i;
|
||||
|
||||
base_addr = AMD_SPI_FIFO_BASE + op->addr.nbytes + offset;
|
||||
/*
|
||||
* Condition for using HID read mode. Only for reading complete page data, use HID read.
|
||||
* Use index mode otherwise.
|
||||
*/
|
||||
if (amd_spi->version == AMD_HID2_SPI && amd_is_spi_read_cmd(op->cmd.opcode)) {
|
||||
amd_spi_hiddma_read(amd_spi, op);
|
||||
|
||||
amd_spi_set_opcode(amd_spi, op->cmd.opcode);
|
||||
amd_spi_set_addr(amd_spi, op);
|
||||
amd_spi_set_tx_count(amd_spi, op->addr.nbytes);
|
||||
amd_spi_set_rx_count(amd_spi, op->data.nbytes + 1);
|
||||
amd_spi_clear_fifo_ptr(amd_spi);
|
||||
amd_spi_execute_opcode(amd_spi);
|
||||
amd_spi_busy_wait(amd_spi);
|
||||
for (i = 0; left_data >= 8; i++, left_data -= 8)
|
||||
*buf_64++ = readq((u8 __iomem *)amd_spi->dma_virt_addr + (i * 8));
|
||||
|
||||
for (i = 0; i < nbytes; i++)
|
||||
buf[i] = amd_spi_readreg8(amd_spi, base_addr + i);
|
||||
buf = (u8 *)buf_64;
|
||||
for (i = 0; i < left_data; i++)
|
||||
buf[i] = readb((u8 __iomem *)amd_spi->dma_virt_addr +
|
||||
(nbytes - left_data + i));
|
||||
|
||||
/* Reset HID RX memory logic */
|
||||
data = amd_spi_readreg32(amd_spi, AMD_SPI_HID2_CNTRL);
|
||||
amd_spi_writereg32(amd_spi, AMD_SPI_HID2_CNTRL, data | BIT(5));
|
||||
} else {
|
||||
/* Index mode */
|
||||
amd_spi_set_opcode(amd_spi, op->cmd.opcode);
|
||||
amd_spi_set_addr(amd_spi, op);
|
||||
amd_spi_set_tx_count(amd_spi, op->addr.nbytes + op->dummy.nbytes);
|
||||
|
||||
for (i = 0; i < op->dummy.nbytes; i++)
|
||||
amd_spi_writereg8(amd_spi, (base_addr + i), 0xff);
|
||||
|
||||
amd_spi_set_rx_count(amd_spi, op->data.nbytes);
|
||||
amd_spi_clear_fifo_ptr(amd_spi);
|
||||
amd_spi_execute_opcode(amd_spi);
|
||||
amd_spi_busy_wait(amd_spi);
|
||||
|
||||
for (i = 0; left_data >= 8; i++, left_data -= 8)
|
||||
*buf_64++ = amd_spi_readreg64(amd_spi, base_addr + op->dummy.nbytes +
|
||||
(i * 8));
|
||||
|
||||
buf = (u8 *)buf_64;
|
||||
for (i = 0; i < left_data; i++)
|
||||
buf[i] = amd_spi_readreg8(amd_spi, base_addr + op->dummy.nbytes +
|
||||
nbytes + i - left_data);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void amd_set_spi_addr_mode(struct amd_spi *amd_spi,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
u32 val = amd_spi_readreg32(amd_spi, AMD_SPI_ADDR32CTRL_REG);
|
||||
|
||||
if (amd_is_spi_read_cmd_4b(op->cmd.opcode))
|
||||
amd_spi_writereg32(amd_spi, AMD_SPI_ADDR32CTRL_REG, val | BIT(0));
|
||||
else
|
||||
amd_spi_writereg32(amd_spi, AMD_SPI_ADDR32CTRL_REG, val & ~BIT(0));
|
||||
}
|
||||
|
||||
static int amd_spi_exec_mem_op(struct spi_mem *mem,
|
||||
@ -447,6 +680,9 @@ static int amd_spi_exec_mem_op(struct spi_mem *mem,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (amd_spi->version == AMD_SPI_V2)
|
||||
amd_set_spi_addr_mode(amd_spi, op);
|
||||
|
||||
switch (op->data.dir) {
|
||||
case SPI_MEM_DATA_IN:
|
||||
amd_spi_mem_data_in(amd_spi, op);
|
||||
@ -489,6 +725,31 @@ static size_t amd_spi_max_transfer_size(struct spi_device *spi)
|
||||
return AMD_SPI_FIFO_SIZE;
|
||||
}
|
||||
|
||||
static int amd_spi_setup_hiddma(struct amd_spi *amd_spi, struct device *dev)
|
||||
{
|
||||
u32 hid_regval;
|
||||
|
||||
/* Allocate DMA buffer to use for HID basic read operation */
|
||||
amd_spi->dma_virt_addr = dma_alloc_coherent(dev, AMD_SPI_HID2_DMA_SIZE,
|
||||
&amd_spi->phy_dma_buf, GFP_KERNEL);
|
||||
if (!amd_spi->dma_virt_addr)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Enable interrupts and set mask bits in hid2_int_mask register to generate interrupt
|
||||
* properly for HIDDMA basic read operations.
|
||||
*/
|
||||
hid_regval = amd_spi_readreg32(amd_spi, AMD_SPI_HID2_INT_MASK);
|
||||
hid_regval = (hid_regval & GENMASK(31, 8)) | BIT(19);
|
||||
amd_spi_writereg32(amd_spi, AMD_SPI_HID2_INT_MASK, hid_regval);
|
||||
|
||||
/* Configure buffer unit(4k) in hid2_control register */
|
||||
hid_regval = amd_spi_readreg32(amd_spi, AMD_SPI_HID2_CNTRL);
|
||||
amd_spi_writereg32(amd_spi, AMD_SPI_HID2_CNTRL, hid_regval & ~BIT(3));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int amd_spi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
@ -512,9 +773,9 @@ static int amd_spi_probe(struct platform_device *pdev)
|
||||
amd_spi->version = (uintptr_t) device_get_match_data(dev);
|
||||
|
||||
/* Initialize the spi_controller fields */
|
||||
host->bus_num = 0;
|
||||
host->bus_num = (amd_spi->version == AMD_HID2_SPI) ? 2 : 0;
|
||||
host->num_chipselect = 4;
|
||||
host->mode_bits = 0;
|
||||
host->mode_bits = SPI_TX_DUAL | SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD;
|
||||
host->flags = SPI_CONTROLLER_HALF_DUPLEX;
|
||||
host->max_speed_hz = AMD_SPI_MAX_HZ;
|
||||
host->min_speed_hz = AMD_SPI_MIN_HZ;
|
||||
@ -529,13 +790,17 @@ static int amd_spi_probe(struct platform_device *pdev)
|
||||
if (err)
|
||||
return dev_err_probe(dev, err, "error registering SPI controller\n");
|
||||
|
||||
return 0;
|
||||
if (amd_spi->version == AMD_HID2_SPI)
|
||||
err = amd_spi_setup_hiddma(amd_spi, dev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id spi_acpi_match[] = {
|
||||
{ "AMDI0061", AMD_SPI_V1 },
|
||||
{ "AMDI0062", AMD_SPI_V2 },
|
||||
{ "AMDI0063", AMD_HID2_SPI },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, spi_acpi_match);
|
||||
|
529
drivers/spi/spi-apple.c
Normal file
529
drivers/spi/spi-apple.c
Normal file
@ -0,0 +1,529 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// Apple SoC SPI device driver
|
||||
//
|
||||
// Copyright The Asahi Linux Contributors
|
||||
//
|
||||
// Based on spi-sifive.c, Copyright 2018 SiFive, Inc.
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.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 APPLE_SPI_CTRL 0x000
|
||||
#define APPLE_SPI_CTRL_RUN BIT(0)
|
||||
#define APPLE_SPI_CTRL_TX_RESET BIT(2)
|
||||
#define APPLE_SPI_CTRL_RX_RESET BIT(3)
|
||||
|
||||
#define APPLE_SPI_CFG 0x004
|
||||
#define APPLE_SPI_CFG_CPHA BIT(1)
|
||||
#define APPLE_SPI_CFG_CPOL BIT(2)
|
||||
#define APPLE_SPI_CFG_MODE GENMASK(6, 5)
|
||||
#define APPLE_SPI_CFG_MODE_POLLED 0
|
||||
#define APPLE_SPI_CFG_MODE_IRQ 1
|
||||
#define APPLE_SPI_CFG_MODE_DMA 2
|
||||
#define APPLE_SPI_CFG_IE_RXCOMPLETE BIT(7)
|
||||
#define APPLE_SPI_CFG_IE_TXRXTHRESH BIT(8)
|
||||
#define APPLE_SPI_CFG_LSB_FIRST BIT(13)
|
||||
#define APPLE_SPI_CFG_WORD_SIZE GENMASK(16, 15)
|
||||
#define APPLE_SPI_CFG_WORD_SIZE_8B 0
|
||||
#define APPLE_SPI_CFG_WORD_SIZE_16B 1
|
||||
#define APPLE_SPI_CFG_WORD_SIZE_32B 2
|
||||
#define APPLE_SPI_CFG_FIFO_THRESH GENMASK(18, 17)
|
||||
#define APPLE_SPI_CFG_FIFO_THRESH_8B 0
|
||||
#define APPLE_SPI_CFG_FIFO_THRESH_4B 1
|
||||
#define APPLE_SPI_CFG_FIFO_THRESH_1B 2
|
||||
#define APPLE_SPI_CFG_IE_TXCOMPLETE BIT(21)
|
||||
|
||||
#define APPLE_SPI_STATUS 0x008
|
||||
#define APPLE_SPI_STATUS_RXCOMPLETE BIT(0)
|
||||
#define APPLE_SPI_STATUS_TXRXTHRESH BIT(1)
|
||||
#define APPLE_SPI_STATUS_TXCOMPLETE BIT(2)
|
||||
|
||||
#define APPLE_SPI_PIN 0x00c
|
||||
#define APPLE_SPI_PIN_KEEP_MOSI BIT(0)
|
||||
#define APPLE_SPI_PIN_CS BIT(1)
|
||||
|
||||
#define APPLE_SPI_TXDATA 0x010
|
||||
#define APPLE_SPI_RXDATA 0x020
|
||||
#define APPLE_SPI_CLKDIV 0x030
|
||||
#define APPLE_SPI_CLKDIV_MAX 0x7ff
|
||||
#define APPLE_SPI_RXCNT 0x034
|
||||
#define APPLE_SPI_WORD_DELAY 0x038
|
||||
#define APPLE_SPI_TXCNT 0x04c
|
||||
|
||||
#define APPLE_SPI_FIFOSTAT 0x10c
|
||||
#define APPLE_SPI_FIFOSTAT_TXFULL BIT(4)
|
||||
#define APPLE_SPI_FIFOSTAT_LEVEL_TX GENMASK(15, 8)
|
||||
#define APPLE_SPI_FIFOSTAT_RXEMPTY BIT(20)
|
||||
#define APPLE_SPI_FIFOSTAT_LEVEL_RX GENMASK(31, 24)
|
||||
|
||||
#define APPLE_SPI_IE_XFER 0x130
|
||||
#define APPLE_SPI_IF_XFER 0x134
|
||||
#define APPLE_SPI_XFER_RXCOMPLETE BIT(0)
|
||||
#define APPLE_SPI_XFER_TXCOMPLETE BIT(1)
|
||||
|
||||
#define APPLE_SPI_IE_FIFO 0x138
|
||||
#define APPLE_SPI_IF_FIFO 0x13c
|
||||
#define APPLE_SPI_FIFO_RXTHRESH BIT(4)
|
||||
#define APPLE_SPI_FIFO_TXTHRESH BIT(5)
|
||||
#define APPLE_SPI_FIFO_RXFULL BIT(8)
|
||||
#define APPLE_SPI_FIFO_TXEMPTY BIT(9)
|
||||
#define APPLE_SPI_FIFO_RXUNDERRUN BIT(16)
|
||||
#define APPLE_SPI_FIFO_TXOVERFLOW BIT(17)
|
||||
|
||||
#define APPLE_SPI_SHIFTCFG 0x150
|
||||
#define APPLE_SPI_SHIFTCFG_CLK_ENABLE BIT(0)
|
||||
#define APPLE_SPI_SHIFTCFG_CS_ENABLE BIT(1)
|
||||
#define APPLE_SPI_SHIFTCFG_AND_CLK_DATA BIT(8)
|
||||
#define APPLE_SPI_SHIFTCFG_CS_AS_DATA BIT(9)
|
||||
#define APPLE_SPI_SHIFTCFG_TX_ENABLE BIT(10)
|
||||
#define APPLE_SPI_SHIFTCFG_RX_ENABLE BIT(11)
|
||||
#define APPLE_SPI_SHIFTCFG_BITS GENMASK(21, 16)
|
||||
#define APPLE_SPI_SHIFTCFG_OVERRIDE_CS BIT(24)
|
||||
|
||||
#define APPLE_SPI_PINCFG 0x154
|
||||
#define APPLE_SPI_PINCFG_KEEP_CLK BIT(0)
|
||||
#define APPLE_SPI_PINCFG_KEEP_CS BIT(1)
|
||||
#define APPLE_SPI_PINCFG_KEEP_MOSI BIT(2)
|
||||
#define APPLE_SPI_PINCFG_CLK_IDLE_VAL BIT(8)
|
||||
#define APPLE_SPI_PINCFG_CS_IDLE_VAL BIT(9)
|
||||
#define APPLE_SPI_PINCFG_MOSI_IDLE_VAL BIT(10)
|
||||
|
||||
#define APPLE_SPI_DELAY_PRE 0x160
|
||||
#define APPLE_SPI_DELAY_POST 0x168
|
||||
#define APPLE_SPI_DELAY_ENABLE BIT(0)
|
||||
#define APPLE_SPI_DELAY_NO_INTERBYTE BIT(1)
|
||||
#define APPLE_SPI_DELAY_SET_SCK BIT(4)
|
||||
#define APPLE_SPI_DELAY_SET_MOSI BIT(6)
|
||||
#define APPLE_SPI_DELAY_SCK_VAL BIT(8)
|
||||
#define APPLE_SPI_DELAY_MOSI_VAL BIT(12)
|
||||
|
||||
#define APPLE_SPI_FIFO_DEPTH 16
|
||||
|
||||
/*
|
||||
* The slowest refclock available is 24MHz, the highest divider is 0x7ff,
|
||||
* the largest word size is 32 bits, the FIFO depth is 16, the maximum
|
||||
* intra-word delay is 0xffff refclocks. So the maximum time a transfer
|
||||
* cycle can take is:
|
||||
*
|
||||
* (0x7ff * 32 + 0xffff) * 16 / 24e6 Hz ~= 87ms
|
||||
*
|
||||
* Double it and round it up to 200ms for good measure.
|
||||
*/
|
||||
#define APPLE_SPI_TIMEOUT_MS 200
|
||||
|
||||
struct apple_spi {
|
||||
void __iomem *regs; /* MMIO register address */
|
||||
struct clk *clk; /* bus clock */
|
||||
struct completion done; /* wake-up from interrupt */
|
||||
};
|
||||
|
||||
static inline void reg_write(struct apple_spi *spi, int offset, u32 value)
|
||||
{
|
||||
writel_relaxed(value, spi->regs + offset);
|
||||
}
|
||||
|
||||
static inline u32 reg_read(struct apple_spi *spi, int offset)
|
||||
{
|
||||
return readl_relaxed(spi->regs + offset);
|
||||
}
|
||||
|
||||
static inline void reg_mask(struct apple_spi *spi, int offset, u32 clear, u32 set)
|
||||
{
|
||||
u32 val = reg_read(spi, offset);
|
||||
|
||||
val &= ~clear;
|
||||
val |= set;
|
||||
reg_write(spi, offset, val);
|
||||
}
|
||||
|
||||
static void apple_spi_init(struct apple_spi *spi)
|
||||
{
|
||||
/* Set CS high (inactive) and disable override and auto-CS */
|
||||
reg_write(spi, APPLE_SPI_PIN, APPLE_SPI_PIN_CS);
|
||||
reg_mask(spi, APPLE_SPI_SHIFTCFG, APPLE_SPI_SHIFTCFG_OVERRIDE_CS, 0);
|
||||
reg_mask(spi, APPLE_SPI_PINCFG, APPLE_SPI_PINCFG_CS_IDLE_VAL, APPLE_SPI_PINCFG_KEEP_CS);
|
||||
|
||||
/* Reset FIFOs */
|
||||
reg_write(spi, APPLE_SPI_CTRL, APPLE_SPI_CTRL_RX_RESET | APPLE_SPI_CTRL_TX_RESET);
|
||||
|
||||
/* Configure defaults */
|
||||
reg_write(spi, APPLE_SPI_CFG,
|
||||
FIELD_PREP(APPLE_SPI_CFG_FIFO_THRESH, APPLE_SPI_CFG_FIFO_THRESH_8B) |
|
||||
FIELD_PREP(APPLE_SPI_CFG_MODE, APPLE_SPI_CFG_MODE_IRQ) |
|
||||
FIELD_PREP(APPLE_SPI_CFG_WORD_SIZE, APPLE_SPI_CFG_WORD_SIZE_8B));
|
||||
|
||||
/* Disable IRQs */
|
||||
reg_write(spi, APPLE_SPI_IE_FIFO, 0);
|
||||
reg_write(spi, APPLE_SPI_IE_XFER, 0);
|
||||
|
||||
/* Disable delays */
|
||||
reg_write(spi, APPLE_SPI_DELAY_PRE, 0);
|
||||
reg_write(spi, APPLE_SPI_DELAY_POST, 0);
|
||||
}
|
||||
|
||||
static int apple_spi_prepare_message(struct spi_controller *ctlr, struct spi_message *msg)
|
||||
{
|
||||
struct apple_spi *spi = spi_controller_get_devdata(ctlr);
|
||||
struct spi_device *device = msg->spi;
|
||||
|
||||
u32 cfg = ((device->mode & SPI_CPHA ? APPLE_SPI_CFG_CPHA : 0) |
|
||||
(device->mode & SPI_CPOL ? APPLE_SPI_CFG_CPOL : 0) |
|
||||
(device->mode & SPI_LSB_FIRST ? APPLE_SPI_CFG_LSB_FIRST : 0));
|
||||
|
||||
/* Update core config */
|
||||
reg_mask(spi, APPLE_SPI_CFG,
|
||||
APPLE_SPI_CFG_CPHA | APPLE_SPI_CFG_CPOL | APPLE_SPI_CFG_LSB_FIRST, cfg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void apple_spi_set_cs(struct spi_device *device, bool is_high)
|
||||
{
|
||||
struct apple_spi *spi = spi_controller_get_devdata(device->controller);
|
||||
|
||||
reg_mask(spi, APPLE_SPI_PIN, APPLE_SPI_PIN_CS, is_high ? APPLE_SPI_PIN_CS : 0);
|
||||
}
|
||||
|
||||
static bool apple_spi_prep_transfer(struct apple_spi *spi, struct spi_transfer *t)
|
||||
{
|
||||
u32 cr, fifo_threshold;
|
||||
|
||||
/* Calculate and program the clock rate */
|
||||
cr = DIV_ROUND_UP(clk_get_rate(spi->clk), t->speed_hz);
|
||||
reg_write(spi, APPLE_SPI_CLKDIV, min_t(u32, cr, APPLE_SPI_CLKDIV_MAX));
|
||||
|
||||
/* Update bits per word */
|
||||
reg_mask(spi, APPLE_SPI_SHIFTCFG, APPLE_SPI_SHIFTCFG_BITS,
|
||||
FIELD_PREP(APPLE_SPI_SHIFTCFG_BITS, t->bits_per_word));
|
||||
|
||||
/* We will want to poll if the time we need to wait is
|
||||
* less than the context switching time.
|
||||
* Let's call that threshold 5us. The operation will take:
|
||||
* bits_per_word * fifo_threshold / hz <= 5 * 10^-6
|
||||
* 200000 * bits_per_word * fifo_threshold <= hz
|
||||
*/
|
||||
fifo_threshold = APPLE_SPI_FIFO_DEPTH / 2;
|
||||
return (200000 * t->bits_per_word * fifo_threshold) <= t->speed_hz;
|
||||
}
|
||||
|
||||
static irqreturn_t apple_spi_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct apple_spi *spi = dev_id;
|
||||
u32 fifo = reg_read(spi, APPLE_SPI_IF_FIFO) & reg_read(spi, APPLE_SPI_IE_FIFO);
|
||||
u32 xfer = reg_read(spi, APPLE_SPI_IF_XFER) & reg_read(spi, APPLE_SPI_IE_XFER);
|
||||
|
||||
if (fifo || xfer) {
|
||||
/* Disable interrupts until next transfer */
|
||||
reg_write(spi, APPLE_SPI_IE_XFER, 0);
|
||||
reg_write(spi, APPLE_SPI_IE_FIFO, 0);
|
||||
complete(&spi->done);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static int apple_spi_wait(struct apple_spi *spi, u32 fifo_bit, u32 xfer_bit, int poll)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (poll) {
|
||||
u32 fifo, xfer;
|
||||
unsigned long timeout = jiffies + APPLE_SPI_TIMEOUT_MS * HZ / 1000;
|
||||
|
||||
do {
|
||||
fifo = reg_read(spi, APPLE_SPI_IF_FIFO);
|
||||
xfer = reg_read(spi, APPLE_SPI_IF_XFER);
|
||||
if (time_after(jiffies, timeout)) {
|
||||
ret = -ETIMEDOUT;
|
||||
break;
|
||||
}
|
||||
} while (!((fifo & fifo_bit) || (xfer & xfer_bit)));
|
||||
} else {
|
||||
reinit_completion(&spi->done);
|
||||
reg_write(spi, APPLE_SPI_IE_XFER, xfer_bit);
|
||||
reg_write(spi, APPLE_SPI_IE_FIFO, fifo_bit);
|
||||
|
||||
if (!wait_for_completion_timeout(&spi->done,
|
||||
msecs_to_jiffies(APPLE_SPI_TIMEOUT_MS)))
|
||||
ret = -ETIMEDOUT;
|
||||
|
||||
reg_write(spi, APPLE_SPI_IE_XFER, 0);
|
||||
reg_write(spi, APPLE_SPI_IE_FIFO, 0);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void apple_spi_tx(struct apple_spi *spi, const void **tx_ptr, u32 *left,
|
||||
unsigned int bytes_per_word)
|
||||
{
|
||||
u32 inuse, words, wrote;
|
||||
|
||||
if (!*tx_ptr)
|
||||
return;
|
||||
|
||||
inuse = FIELD_GET(APPLE_SPI_FIFOSTAT_LEVEL_TX, reg_read(spi, APPLE_SPI_FIFOSTAT));
|
||||
words = wrote = min_t(u32, *left, APPLE_SPI_FIFO_DEPTH - inuse);
|
||||
|
||||
if (!words)
|
||||
return;
|
||||
|
||||
*left -= words;
|
||||
|
||||
switch (bytes_per_word) {
|
||||
case 1: {
|
||||
const u8 *p = *tx_ptr;
|
||||
|
||||
while (words--)
|
||||
reg_write(spi, APPLE_SPI_TXDATA, *p++);
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
const u16 *p = *tx_ptr;
|
||||
|
||||
while (words--)
|
||||
reg_write(spi, APPLE_SPI_TXDATA, *p++);
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
const u32 *p = *tx_ptr;
|
||||
|
||||
while (words--)
|
||||
reg_write(spi, APPLE_SPI_TXDATA, *p++);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
WARN_ON(1);
|
||||
}
|
||||
|
||||
*tx_ptr = ((u8 *)*tx_ptr) + bytes_per_word * wrote;
|
||||
}
|
||||
|
||||
static void apple_spi_rx(struct apple_spi *spi, void **rx_ptr, u32 *left,
|
||||
unsigned int bytes_per_word)
|
||||
{
|
||||
u32 words, read;
|
||||
|
||||
if (!*rx_ptr)
|
||||
return;
|
||||
|
||||
words = read = FIELD_GET(APPLE_SPI_FIFOSTAT_LEVEL_RX, reg_read(spi, APPLE_SPI_FIFOSTAT));
|
||||
WARN_ON(words > *left);
|
||||
|
||||
if (!words)
|
||||
return;
|
||||
|
||||
*left -= min_t(u32, *left, words);
|
||||
|
||||
switch (bytes_per_word) {
|
||||
case 1: {
|
||||
u8 *p = *rx_ptr;
|
||||
|
||||
while (words--)
|
||||
*p++ = reg_read(spi, APPLE_SPI_RXDATA);
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
u16 *p = *rx_ptr;
|
||||
|
||||
while (words--)
|
||||
*p++ = reg_read(spi, APPLE_SPI_RXDATA);
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
u32 *p = *rx_ptr;
|
||||
|
||||
while (words--)
|
||||
*p++ = reg_read(spi, APPLE_SPI_RXDATA);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
WARN_ON(1);
|
||||
}
|
||||
|
||||
*rx_ptr = ((u8 *)*rx_ptr) + bytes_per_word * read;
|
||||
}
|
||||
|
||||
static int apple_spi_transfer_one(struct spi_controller *ctlr, struct spi_device *device,
|
||||
struct spi_transfer *t)
|
||||
{
|
||||
struct apple_spi *spi = spi_controller_get_devdata(ctlr);
|
||||
bool poll = apple_spi_prep_transfer(spi, t);
|
||||
const void *tx_ptr = t->tx_buf;
|
||||
void *rx_ptr = t->rx_buf;
|
||||
unsigned int bytes_per_word;
|
||||
u32 words, remaining_tx, remaining_rx;
|
||||
u32 xfer_flags = 0;
|
||||
u32 fifo_flags;
|
||||
int retries = 100;
|
||||
int ret = 0;
|
||||
|
||||
if (t->bits_per_word > 16)
|
||||
bytes_per_word = 4;
|
||||
else if (t->bits_per_word > 8)
|
||||
bytes_per_word = 2;
|
||||
else
|
||||
bytes_per_word = 1;
|
||||
|
||||
words = t->len / bytes_per_word;
|
||||
remaining_tx = tx_ptr ? words : 0;
|
||||
remaining_rx = rx_ptr ? words : 0;
|
||||
|
||||
/* Reset FIFOs */
|
||||
reg_write(spi, APPLE_SPI_CTRL, APPLE_SPI_CTRL_RX_RESET | APPLE_SPI_CTRL_TX_RESET);
|
||||
|
||||
/* Clear IRQ flags */
|
||||
reg_write(spi, APPLE_SPI_IF_XFER, ~0);
|
||||
reg_write(spi, APPLE_SPI_IF_FIFO, ~0);
|
||||
|
||||
/* Determine transfer completion flags we wait for */
|
||||
if (tx_ptr)
|
||||
xfer_flags |= APPLE_SPI_XFER_TXCOMPLETE;
|
||||
if (rx_ptr)
|
||||
xfer_flags |= APPLE_SPI_XFER_RXCOMPLETE;
|
||||
|
||||
/* Set transfer length */
|
||||
reg_write(spi, APPLE_SPI_TXCNT, remaining_tx);
|
||||
reg_write(spi, APPLE_SPI_RXCNT, remaining_rx);
|
||||
|
||||
/* Prime transmit FIFO */
|
||||
apple_spi_tx(spi, &tx_ptr, &remaining_tx, bytes_per_word);
|
||||
|
||||
/* Start transfer */
|
||||
reg_write(spi, APPLE_SPI_CTRL, APPLE_SPI_CTRL_RUN);
|
||||
|
||||
/* TX again since a few words get popped off immediately */
|
||||
apple_spi_tx(spi, &tx_ptr, &remaining_tx, bytes_per_word);
|
||||
|
||||
while (xfer_flags) {
|
||||
fifo_flags = 0;
|
||||
|
||||
if (remaining_tx)
|
||||
fifo_flags |= APPLE_SPI_FIFO_TXTHRESH;
|
||||
if (remaining_rx)
|
||||
fifo_flags |= APPLE_SPI_FIFO_RXTHRESH;
|
||||
|
||||
/* Wait for anything to happen */
|
||||
ret = apple_spi_wait(spi, fifo_flags, xfer_flags, poll);
|
||||
if (ret) {
|
||||
dev_err(&ctlr->dev, "transfer timed out (remaining %d tx, %d rx)\n",
|
||||
remaining_tx, remaining_rx);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Stop waiting on transfer halves once they complete */
|
||||
xfer_flags &= ~reg_read(spi, APPLE_SPI_IF_XFER);
|
||||
|
||||
/* Transmit and receive everything we can */
|
||||
apple_spi_tx(spi, &tx_ptr, &remaining_tx, bytes_per_word);
|
||||
apple_spi_rx(spi, &rx_ptr, &remaining_rx, bytes_per_word);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sometimes the transfer completes before the last word is in the RX FIFO.
|
||||
* Normally one retry is all it takes to get the last word out.
|
||||
*/
|
||||
while (remaining_rx && retries--)
|
||||
apple_spi_rx(spi, &rx_ptr, &remaining_rx, bytes_per_word);
|
||||
|
||||
if (remaining_tx)
|
||||
dev_err(&ctlr->dev, "transfer completed with %d words left to transmit\n",
|
||||
remaining_tx);
|
||||
if (remaining_rx)
|
||||
dev_err(&ctlr->dev, "transfer completed with %d words left to receive\n",
|
||||
remaining_rx);
|
||||
|
||||
err:
|
||||
fifo_flags = reg_read(spi, APPLE_SPI_IF_FIFO);
|
||||
WARN_ON(fifo_flags & APPLE_SPI_FIFO_TXOVERFLOW);
|
||||
WARN_ON(fifo_flags & APPLE_SPI_FIFO_RXUNDERRUN);
|
||||
|
||||
/* Stop transfer */
|
||||
reg_write(spi, APPLE_SPI_CTRL, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int apple_spi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct apple_spi *spi;
|
||||
int ret, irq;
|
||||
struct spi_controller *ctlr;
|
||||
|
||||
ctlr = devm_spi_alloc_host(&pdev->dev, sizeof(struct apple_spi));
|
||||
if (!ctlr)
|
||||
return -ENOMEM;
|
||||
|
||||
spi = spi_controller_get_devdata(ctlr);
|
||||
init_completion(&spi->done);
|
||||
|
||||
spi->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(spi->regs))
|
||||
return PTR_ERR(spi->regs);
|
||||
|
||||
spi->clk = devm_clk_get_enabled(&pdev->dev, NULL);
|
||||
if (IS_ERR(spi->clk))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(spi->clk),
|
||||
"Unable to find or enable bus clock\n");
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, irq, apple_spi_irq, 0,
|
||||
dev_name(&pdev->dev), spi);
|
||||
if (ret)
|
||||
return dev_err_probe(&pdev->dev, ret, "Unable to bind to interrupt\n");
|
||||
|
||||
ctlr->dev.of_node = pdev->dev.of_node;
|
||||
ctlr->bus_num = pdev->id;
|
||||
ctlr->num_chipselect = 1;
|
||||
ctlr->mode_bits = SPI_CPHA | SPI_CPOL | SPI_LSB_FIRST;
|
||||
ctlr->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32);
|
||||
ctlr->prepare_message = apple_spi_prepare_message;
|
||||
ctlr->set_cs = apple_spi_set_cs;
|
||||
ctlr->transfer_one = apple_spi_transfer_one;
|
||||
ctlr->auto_runtime_pm = true;
|
||||
|
||||
pm_runtime_set_active(&pdev->dev);
|
||||
ret = devm_pm_runtime_enable(&pdev->dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
apple_spi_init(spi);
|
||||
|
||||
ret = devm_spi_register_controller(&pdev->dev, ctlr);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(&pdev->dev, ret, "devm_spi_register_controller failed\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id apple_spi_of_match[] = {
|
||||
{ .compatible = "apple,spi", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, apple_spi_of_match);
|
||||
|
||||
static struct platform_driver apple_spi_driver = {
|
||||
.probe = apple_spi_probe,
|
||||
.driver = {
|
||||
.name = "apple-spi",
|
||||
.of_match_table = apple_spi_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(apple_spi_driver);
|
||||
|
||||
MODULE_AUTHOR("Hector Martin <marcan@marcan.st>");
|
||||
MODULE_DESCRIPTION("Apple SoC SPI driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -223,7 +223,7 @@ static struct platform_driver ar934x_spi_driver = {
|
||||
.of_match_table = ar934x_spi_match,
|
||||
},
|
||||
.probe = ar934x_spi_probe,
|
||||
.remove_new = ar934x_spi_remove,
|
||||
.remove = ar934x_spi_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(ar934x_spi_driver);
|
||||
|
@ -1189,7 +1189,7 @@ MODULE_DEVICE_TABLE(of, aspeed_spi_matches);
|
||||
|
||||
static struct platform_driver aspeed_spi_driver = {
|
||||
.probe = aspeed_spi_probe,
|
||||
.remove_new = aspeed_spi_remove,
|
||||
.remove = aspeed_spi_remove,
|
||||
.driver = {
|
||||
.name = DEVICE_NAME,
|
||||
.of_match_table = aspeed_spi_matches,
|
||||
|
@ -650,7 +650,7 @@ static struct platform_driver at91_usart_spi_driver = {
|
||||
.pm = &at91_usart_spi_pm_ops,
|
||||
},
|
||||
.probe = at91_usart_spi_probe,
|
||||
.remove_new = at91_usart_spi_remove,
|
||||
.remove = at91_usart_spi_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(at91_usart_spi_driver);
|
||||
|
@ -253,7 +253,7 @@ MODULE_DEVICE_TABLE(of, ath79_spi_of_match);
|
||||
|
||||
static struct platform_driver ath79_spi_driver = {
|
||||
.probe = ath79_spi_probe,
|
||||
.remove_new = ath79_spi_remove,
|
||||
.remove = ath79_spi_remove,
|
||||
.shutdown = ath79_spi_shutdown,
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
|
@ -1781,7 +1781,7 @@ static struct platform_driver atmel_spi_driver = {
|
||||
.of_match_table = atmel_spi_dt_ids,
|
||||
},
|
||||
.probe = atmel_spi_probe,
|
||||
.remove_new = atmel_spi_remove,
|
||||
.remove = atmel_spi_remove,
|
||||
};
|
||||
module_platform_driver(atmel_spi_driver);
|
||||
|
||||
|
@ -940,7 +940,7 @@ MODULE_ALIAS("platform:au1550-spi");
|
||||
|
||||
static struct platform_driver au1550_spi_drv = {
|
||||
.probe = au1550_spi_probe,
|
||||
.remove_new = au1550_spi_remove,
|
||||
.remove = au1550_spi_remove,
|
||||
.driver = {
|
||||
.name = "au1550-spi",
|
||||
},
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <linux/overflow.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <trace/events/spi.h>
|
||||
|
||||
#define SPI_ENGINE_REG_RESET 0x40
|
||||
|
||||
@ -590,6 +591,13 @@ static int spi_engine_transfer_one_message(struct spi_controller *host,
|
||||
|
||||
reinit_completion(&spi_engine->msg_complete);
|
||||
|
||||
if (trace_spi_transfer_start_enabled()) {
|
||||
struct spi_transfer *xfer;
|
||||
|
||||
list_for_each_entry(xfer, &msg->transfers, transfer_list)
|
||||
trace_spi_transfer_start(msg, xfer);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&spi_engine->lock, flags);
|
||||
|
||||
if (spi_engine_write_cmd_fifo(spi_engine, msg))
|
||||
@ -617,6 +625,13 @@ static int spi_engine_transfer_one_message(struct spi_controller *host,
|
||||
msg->status = -ETIMEDOUT;
|
||||
}
|
||||
|
||||
if (trace_spi_transfer_stop_enabled()) {
|
||||
struct spi_transfer *xfer;
|
||||
|
||||
list_for_each_entry(xfer, &msg->transfers, transfer_list)
|
||||
trace_spi_transfer_stop(msg, xfer);
|
||||
}
|
||||
|
||||
spi_finalize_current_message(host);
|
||||
|
||||
return msg->status;
|
||||
|
@ -1435,7 +1435,7 @@ static struct platform_driver bcm2835_spi_driver = {
|
||||
.of_match_table = bcm2835_spi_match,
|
||||
},
|
||||
.probe = bcm2835_spi_probe,
|
||||
.remove_new = bcm2835_spi_remove,
|
||||
.remove = bcm2835_spi_remove,
|
||||
.shutdown = bcm2835_spi_remove,
|
||||
};
|
||||
module_platform_driver(bcm2835_spi_driver);
|
||||
|
@ -577,7 +577,7 @@ static struct platform_driver bcm2835aux_spi_driver = {
|
||||
.of_match_table = bcm2835aux_spi_match,
|
||||
},
|
||||
.probe = bcm2835aux_spi_probe,
|
||||
.remove_new = bcm2835aux_spi_remove,
|
||||
.remove = bcm2835aux_spi_remove,
|
||||
};
|
||||
module_platform_driver(bcm2835aux_spi_driver);
|
||||
|
||||
|
@ -944,7 +944,7 @@ static struct platform_driver bcm63xx_hsspi_driver = {
|
||||
.of_match_table = bcm63xx_hsspi_of_match,
|
||||
},
|
||||
.probe = bcm63xx_hsspi_probe,
|
||||
.remove_new = bcm63xx_hsspi_remove,
|
||||
.remove = bcm63xx_hsspi_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(bcm63xx_hsspi_driver);
|
||||
|
@ -656,7 +656,7 @@ static struct platform_driver bcm63xx_spi_driver = {
|
||||
},
|
||||
.id_table = bcm63xx_spi_dev_match,
|
||||
.probe = bcm63xx_spi_probe,
|
||||
.remove_new = bcm63xx_spi_remove,
|
||||
.remove = bcm63xx_spi_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(bcm63xx_spi_driver);
|
||||
|
@ -633,7 +633,7 @@ static struct platform_driver bcmbca_hsspi_driver = {
|
||||
.of_match_table = bcmbca_hsspi_of_match,
|
||||
},
|
||||
.probe = bcmbca_hsspi_probe,
|
||||
.remove_new = bcmbca_hsspi_remove,
|
||||
.remove = bcmbca_hsspi_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(bcmbca_hsspi_driver);
|
||||
|
@ -28,7 +28,7 @@ static void brcmstb_qspi_remove(struct platform_device *pdev)
|
||||
|
||||
static struct platform_driver brcmstb_qspi_driver = {
|
||||
.probe = brcmstb_qspi_probe,
|
||||
.remove_new = brcmstb_qspi_remove,
|
||||
.remove = brcmstb_qspi_remove,
|
||||
.driver = {
|
||||
.name = "brcmstb_qspi",
|
||||
.pm = &bcm_qspi_pm_ops,
|
||||
|
@ -2112,7 +2112,7 @@ MODULE_DEVICE_TABLE(of, cqspi_dt_ids);
|
||||
|
||||
static struct platform_driver cqspi_platform_driver = {
|
||||
.probe = cqspi_probe,
|
||||
.remove_new = cqspi_remove,
|
||||
.remove = cqspi_remove,
|
||||
.driver = {
|
||||
.name = CQSPI_NAME,
|
||||
.pm = pm_ptr(&cqspi_dev_pm_ops),
|
||||
|
@ -806,7 +806,7 @@ MODULE_DEVICE_TABLE(of, cdns_spi_of_match);
|
||||
/* cdns_spi_driver - This structure defines the SPI subsystem platform driver */
|
||||
static struct platform_driver cdns_spi_driver = {
|
||||
.probe = cdns_spi_probe,
|
||||
.remove_new = cdns_spi_remove,
|
||||
.remove = cdns_spi_remove,
|
||||
.driver = {
|
||||
.name = CDNS_SPI_NAME,
|
||||
.of_match_table = cdns_spi_of_match,
|
||||
|
@ -90,7 +90,7 @@ static struct platform_driver octeon_spi_driver = {
|
||||
.of_match_table = octeon_spi_match,
|
||||
},
|
||||
.probe = octeon_spi_probe,
|
||||
.remove_new = octeon_spi_remove,
|
||||
.remove = octeon_spi_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(octeon_spi_driver);
|
||||
|
@ -152,7 +152,7 @@ static int ch341_probe(struct usb_interface *intf,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ctrl = devm_spi_alloc_master(&udev->dev, sizeof(struct ch341_spi_dev));
|
||||
ctrl = devm_spi_alloc_host(&udev->dev, sizeof(struct ch341_spi_dev));
|
||||
if (!ctrl)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -502,7 +502,7 @@ static struct platform_driver mcfqspi_driver = {
|
||||
.driver.name = DRIVER_NAME,
|
||||
.driver.pm = &mcfqspi_pm,
|
||||
.probe = mcfqspi_probe,
|
||||
.remove_new = mcfqspi_remove,
|
||||
.remove = mcfqspi_remove,
|
||||
};
|
||||
module_platform_driver(mcfqspi_driver);
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/gpio/machine.h>
|
||||
#include <linux/gpio/property.h>
|
||||
#include <linux/mfd/cs42l43.h>
|
||||
@ -229,6 +230,33 @@ static size_t cs42l43_spi_max_length(struct spi_device *spi)
|
||||
return CS42L43_SPI_MAX_LENGTH;
|
||||
}
|
||||
|
||||
static int cs42l43_get_speaker_id_gpios(struct cs42l43_spi *priv, int *result)
|
||||
{
|
||||
struct gpio_descs *descs;
|
||||
u32 spkid;
|
||||
int i, ret;
|
||||
|
||||
descs = gpiod_get_array_optional(priv->dev, "spk-id", GPIOD_IN);
|
||||
if (IS_ERR_OR_NULL(descs))
|
||||
return PTR_ERR(descs);
|
||||
|
||||
spkid = 0;
|
||||
for (i = 0; i < descs->ndescs; i++) {
|
||||
ret = gpiod_get_value_cansleep(descs->desc[i]);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
spkid |= (ret << i);
|
||||
}
|
||||
|
||||
dev_dbg(priv->dev, "spk-id-gpios = %d\n", spkid);
|
||||
*result = spkid;
|
||||
err:
|
||||
gpiod_put_array(descs);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct fwnode_handle *cs42l43_find_xu_node(struct fwnode_handle *fwnode)
|
||||
{
|
||||
static const u32 func_smart_amp = 0x1;
|
||||
@ -306,6 +334,7 @@ static int cs42l43_spi_probe(struct platform_device *pdev)
|
||||
struct fwnode_handle *fwnode = dev_fwnode(cs42l43->dev);
|
||||
struct fwnode_handle *xu_fwnode __free(fwnode_handle) = cs42l43_find_xu_node(fwnode);
|
||||
int nsidecars = 0;
|
||||
int spkid = -EINVAL;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
@ -360,6 +389,18 @@ static int cs42l43_spi_probe(struct platform_device *pdev)
|
||||
fwnode_property_read_u32(xu_fwnode, "01fa-sidecar-instances", &nsidecars);
|
||||
|
||||
if (nsidecars) {
|
||||
ret = fwnode_property_read_u32(xu_fwnode, "01fa-spk-id-val", &spkid);
|
||||
if (!ret) {
|
||||
dev_dbg(priv->dev, "01fa-spk-id-val = %d\n", spkid);
|
||||
} else if (ret != -EINVAL) {
|
||||
return dev_err_probe(priv->dev, ret, "Failed to get spk-id-val\n");
|
||||
} else {
|
||||
ret = cs42l43_get_speaker_id_gpios(priv, &spkid);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(priv->dev, ret,
|
||||
"Failed to get spk-id-gpios\n");
|
||||
}
|
||||
|
||||
ret = software_node_register(&cs42l43_gpiochip_swnode);
|
||||
if (ret)
|
||||
return dev_err_probe(priv->dev, ret,
|
||||
@ -385,11 +426,6 @@ static int cs42l43_spi_probe(struct platform_device *pdev)
|
||||
if (nsidecars) {
|
||||
struct spi_board_info *ampl_info;
|
||||
struct spi_board_info *ampr_info;
|
||||
int spkid = -EINVAL;
|
||||
|
||||
fwnode_property_read_u32(xu_fwnode, "01fa-spk-id-val", &spkid);
|
||||
|
||||
dev_dbg(priv->dev, "Found speaker ID %d\n", spkid);
|
||||
|
||||
ampl_info = cs42l43_create_bridge_amp(priv, "cs35l56-left", 0, spkid);
|
||||
if (!ampl_info)
|
||||
|
@ -1039,7 +1039,7 @@ static struct platform_driver davinci_spi_driver = {
|
||||
.of_match_table = of_match_ptr(davinci_spi_of_match),
|
||||
},
|
||||
.probe = davinci_spi_probe,
|
||||
.remove_new = davinci_spi_remove,
|
||||
.remove = davinci_spi_remove,
|
||||
};
|
||||
module_platform_driver(davinci_spi_driver);
|
||||
|
||||
|
@ -871,7 +871,7 @@ static struct platform_driver spi_dln2_driver = {
|
||||
.pm = &dln2_spi_pm,
|
||||
},
|
||||
.probe = dln2_spi_probe,
|
||||
.remove_new = dln2_spi_remove,
|
||||
.remove = dln2_spi_remove,
|
||||
};
|
||||
module_platform_driver(spi_dln2_driver);
|
||||
|
||||
|
@ -317,7 +317,7 @@ MODULE_DEVICE_TABLE(of, dw_spi_bt1_of_match);
|
||||
|
||||
static struct platform_driver dw_spi_bt1_driver = {
|
||||
.probe = dw_spi_bt1_probe,
|
||||
.remove_new = dw_spi_bt1_remove,
|
||||
.remove = dw_spi_bt1_remove,
|
||||
.driver = {
|
||||
.name = "bt1-sys-ssi",
|
||||
.of_match_table = dw_spi_bt1_of_match,
|
||||
|
@ -433,7 +433,7 @@ MODULE_DEVICE_TABLE(acpi, dw_spi_mmio_acpi_match);
|
||||
|
||||
static struct platform_driver dw_spi_mmio_driver = {
|
||||
.probe = dw_spi_mmio_probe,
|
||||
.remove_new = dw_spi_mmio_remove,
|
||||
.remove = dw_spi_mmio_remove,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.of_match_table = dw_spi_mmio_of_match,
|
||||
|
@ -98,15 +98,14 @@ static int dw_spi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *en
|
||||
dws->paddr = pci_resource_start(pdev, pci_bar);
|
||||
pci_set_master(pdev);
|
||||
|
||||
ret = pcim_iomap_regions(pdev, 1 << pci_bar, pci_name(pdev));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
dws->regs = pcim_iomap_table(pdev)[pci_bar];
|
||||
dws->regs = pcim_iomap_region(pdev, pci_bar, pci_name(pdev));
|
||||
if (IS_ERR(dws->regs))
|
||||
return PTR_ERR(dws->regs);
|
||||
|
||||
dws->irq = pci_irq_vector(pdev, 0);
|
||||
|
||||
/*
|
||||
|
@ -729,7 +729,7 @@ static struct platform_driver ep93xx_spi_driver = {
|
||||
.of_match_table = ep93xx_spi_of_ids,
|
||||
},
|
||||
.probe = ep93xx_spi_probe,
|
||||
.remove_new = ep93xx_spi_remove,
|
||||
.remove = ep93xx_spi_remove,
|
||||
};
|
||||
module_platform_driver(ep93xx_spi_driver);
|
||||
|
||||
|
@ -280,25 +280,25 @@ static void dspi_native_dev_to_host(struct fsl_dspi *dspi, u32 rxdata)
|
||||
|
||||
static void dspi_8on32_host_to_dev(struct fsl_dspi *dspi, u32 *txdata)
|
||||
{
|
||||
*txdata = cpu_to_be32(*(u32 *)dspi->tx);
|
||||
*txdata = (__force u32)cpu_to_be32(*(u32 *)dspi->tx);
|
||||
dspi->tx += sizeof(u32);
|
||||
}
|
||||
|
||||
static void dspi_8on32_dev_to_host(struct fsl_dspi *dspi, u32 rxdata)
|
||||
{
|
||||
*(u32 *)dspi->rx = be32_to_cpu(rxdata);
|
||||
*(u32 *)dspi->rx = be32_to_cpu((__force __be32)rxdata);
|
||||
dspi->rx += sizeof(u32);
|
||||
}
|
||||
|
||||
static void dspi_8on16_host_to_dev(struct fsl_dspi *dspi, u32 *txdata)
|
||||
{
|
||||
*txdata = cpu_to_be16(*(u16 *)dspi->tx);
|
||||
*txdata = (__force u32)cpu_to_be16(*(u16 *)dspi->tx);
|
||||
dspi->tx += sizeof(u16);
|
||||
}
|
||||
|
||||
static void dspi_8on16_dev_to_host(struct fsl_dspi *dspi, u32 rxdata)
|
||||
{
|
||||
*(u16 *)dspi->rx = be16_to_cpu(rxdata);
|
||||
*(u16 *)dspi->rx = be16_to_cpu((__force __be16)rxdata);
|
||||
dspi->rx += sizeof(u16);
|
||||
}
|
||||
|
||||
@ -1473,7 +1473,7 @@ static struct platform_driver fsl_dspi_driver = {
|
||||
.driver.of_match_table = fsl_dspi_dt_ids,
|
||||
.driver.pm = &dspi_pm,
|
||||
.probe = dspi_probe,
|
||||
.remove_new = dspi_remove,
|
||||
.remove = dspi_remove,
|
||||
.shutdown = dspi_shutdown,
|
||||
};
|
||||
module_platform_driver(fsl_dspi_driver);
|
||||
|
@ -835,7 +835,7 @@ static struct platform_driver fsl_espi_driver = {
|
||||
.pm = &espi_pm,
|
||||
},
|
||||
.probe = of_fsl_espi_probe,
|
||||
.remove_new = of_fsl_espi_remove,
|
||||
.remove = of_fsl_espi_remove,
|
||||
};
|
||||
module_platform_driver(fsl_espi_driver);
|
||||
|
||||
|
@ -92,6 +92,7 @@ struct lpspi_config {
|
||||
u8 prescale;
|
||||
u16 mode;
|
||||
u32 speed_hz;
|
||||
u32 effective_speed_hz;
|
||||
};
|
||||
|
||||
struct fsl_lpspi_data {
|
||||
@ -315,9 +316,10 @@ static void fsl_lpspi_set_watermark(struct fsl_lpspi_data *fsl_lpspi)
|
||||
static int fsl_lpspi_set_bitrate(struct fsl_lpspi_data *fsl_lpspi)
|
||||
{
|
||||
struct lpspi_config config = fsl_lpspi->config;
|
||||
unsigned int perclk_rate, scldiv, div;
|
||||
unsigned int perclk_rate, div;
|
||||
u8 prescale_max;
|
||||
u8 prescale;
|
||||
int scldiv;
|
||||
|
||||
perclk_rate = clk_get_rate(fsl_lpspi->clk_per);
|
||||
prescale_max = fsl_lpspi->devtype_data->prescale_max;
|
||||
@ -338,19 +340,22 @@ static int fsl_lpspi_set_bitrate(struct fsl_lpspi_data *fsl_lpspi)
|
||||
|
||||
for (prescale = 0; prescale <= prescale_max; prescale++) {
|
||||
scldiv = div / (1 << prescale) - 2;
|
||||
if (scldiv < 256) {
|
||||
if (scldiv >= 0 && scldiv < 256) {
|
||||
fsl_lpspi->config.prescale = prescale;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (scldiv >= 256)
|
||||
if (scldiv < 0 || scldiv >= 256)
|
||||
return -EINVAL;
|
||||
|
||||
writel(scldiv | (scldiv << 8) | ((scldiv >> 1) << 16),
|
||||
fsl_lpspi->base + IMX7ULP_CCR);
|
||||
|
||||
dev_dbg(fsl_lpspi->dev, "perclk=%d, speed=%d, prescale=%d, scldiv=%d\n",
|
||||
fsl_lpspi->config.effective_speed_hz = perclk_rate / (scldiv + 2) *
|
||||
(1 << prescale);
|
||||
|
||||
dev_dbg(fsl_lpspi->dev, "perclk=%u, speed=%u, prescale=%u, scldiv=%d\n",
|
||||
perclk_rate, config.speed_hz, prescale, scldiv);
|
||||
|
||||
return 0;
|
||||
@ -749,6 +754,8 @@ static int fsl_lpspi_transfer_one(struct spi_controller *controller,
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
t->effective_speed_hz = fsl_lpspi->config.effective_speed_hz;
|
||||
|
||||
fsl_lpspi_set_cmd(fsl_lpspi);
|
||||
fsl_lpspi->is_first_byte = false;
|
||||
|
||||
@ -891,7 +898,7 @@ static int fsl_lpspi_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, irq, fsl_lpspi_isr, 0,
|
||||
ret = devm_request_irq(&pdev->dev, irq, fsl_lpspi_isr, IRQF_NO_AUTOEN,
|
||||
dev_name(&pdev->dev), fsl_lpspi);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "can't get irq%d: %d\n", irq, ret);
|
||||
@ -948,14 +955,10 @@ static int fsl_lpspi_probe(struct platform_device *pdev)
|
||||
ret = fsl_lpspi_dma_init(&pdev->dev, fsl_lpspi, controller);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
goto out_pm_get;
|
||||
if (ret < 0)
|
||||
if (ret < 0) {
|
||||
dev_warn(&pdev->dev, "dma setup error %d, use pio\n", ret);
|
||||
else
|
||||
/*
|
||||
* disable LPSPI module IRQ when enable DMA mode successfully,
|
||||
* to prevent the unexpected LPSPI module IRQ events.
|
||||
*/
|
||||
disable_irq(irq);
|
||||
enable_irq(irq);
|
||||
}
|
||||
|
||||
ret = devm_spi_register_controller(&pdev->dev, controller);
|
||||
if (ret < 0) {
|
||||
@ -1024,7 +1027,7 @@ static struct platform_driver fsl_lpspi_driver = {
|
||||
.pm = pm_ptr(&fsl_lpspi_pm_ops),
|
||||
},
|
||||
.probe = fsl_lpspi_probe,
|
||||
.remove_new = fsl_lpspi_remove,
|
||||
.remove = fsl_lpspi_remove,
|
||||
};
|
||||
module_platform_driver(fsl_lpspi_driver);
|
||||
|
||||
|
@ -997,7 +997,7 @@ static struct platform_driver fsl_qspi_driver = {
|
||||
.pm = &fsl_qspi_pm_ops,
|
||||
},
|
||||
.probe = fsl_qspi_probe,
|
||||
.remove_new = fsl_qspi_remove,
|
||||
.remove = fsl_qspi_remove,
|
||||
};
|
||||
module_platform_driver(fsl_qspi_driver);
|
||||
|
||||
|
@ -714,7 +714,7 @@ static struct platform_driver of_fsl_spi_driver = {
|
||||
.of_match_table = of_fsl_spi_match,
|
||||
},
|
||||
.probe = of_fsl_spi_probe,
|
||||
.remove_new = of_fsl_spi_remove,
|
||||
.remove = of_fsl_spi_remove,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_MPC832x_RDB
|
||||
@ -757,7 +757,7 @@ static void plat_mpc8xxx_spi_remove(struct platform_device *pdev)
|
||||
MODULE_ALIAS("platform:mpc8xxx_spi");
|
||||
static struct platform_driver mpc8xxx_spi_driver = {
|
||||
.probe = plat_mpc8xxx_spi_probe,
|
||||
.remove_new = plat_mpc8xxx_spi_remove,
|
||||
.remove = plat_mpc8xxx_spi_remove,
|
||||
.driver = {
|
||||
.name = "mpc8xxx_spi",
|
||||
},
|
||||
|
@ -542,7 +542,7 @@ MODULE_DEVICE_TABLE(acpi, hisi_spi_acpi_match);
|
||||
|
||||
static struct platform_driver hisi_spi_driver = {
|
||||
.probe = hisi_spi_probe,
|
||||
.remove_new = hisi_spi_remove,
|
||||
.remove = hisi_spi_remove,
|
||||
.driver = {
|
||||
.name = "hisi-kunpeng-spi",
|
||||
.acpi_match_table = hisi_spi_acpi_match,
|
||||
|
@ -40,7 +40,7 @@
|
||||
/* Common definition of interrupt bit masks */
|
||||
#define HISI_SFC_V3XX_INT_MASK_ALL (0x1ff) /* all the masks */
|
||||
#define HISI_SFC_V3XX_INT_MASK_CPLT BIT(0) /* command execution complete */
|
||||
#define HISI_SFC_V3XX_INT_MASK_PP_ERR BIT(2) /* page progrom error */
|
||||
#define HISI_SFC_V3XX_INT_MASK_PP_ERR BIT(2) /* page program error */
|
||||
#define HISI_SFC_V3XX_INT_MASK_IACCES BIT(5) /* error visiting inaccessible/
|
||||
* protected address
|
||||
*/
|
||||
|
@ -756,7 +756,7 @@ static struct platform_driver img_spfi_driver = {
|
||||
.of_match_table = of_match_ptr(img_spfi_of_match),
|
||||
},
|
||||
.probe = img_spfi_probe,
|
||||
.remove_new = img_spfi_remove,
|
||||
.remove = img_spfi_remove,
|
||||
};
|
||||
module_platform_driver(img_spfi_driver);
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
// Copyright (C) 2008 Juergen Beisert
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/delay.h>
|
||||
@ -13,7 +14,10 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/math.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/overflow.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
@ -71,7 +75,8 @@ struct spi_imx_data;
|
||||
struct spi_imx_devtype_data {
|
||||
void (*intctrl)(struct spi_imx_data *spi_imx, int enable);
|
||||
int (*prepare_message)(struct spi_imx_data *spi_imx, struct spi_message *msg);
|
||||
int (*prepare_transfer)(struct spi_imx_data *spi_imx, struct spi_device *spi);
|
||||
int (*prepare_transfer)(struct spi_imx_data *spi_imx, struct spi_device *spi,
|
||||
struct spi_transfer *t);
|
||||
void (*trigger)(struct spi_imx_data *spi_imx);
|
||||
int (*rx_available)(struct spi_imx_data *spi_imx);
|
||||
void (*reset)(struct spi_imx_data *spi_imx);
|
||||
@ -301,6 +306,18 @@ static bool spi_imx_can_dma(struct spi_controller *controller, struct spi_device
|
||||
#define MX51_ECSPI_STAT 0x18
|
||||
#define MX51_ECSPI_STAT_RR (1 << 3)
|
||||
|
||||
#define MX51_ECSPI_PERIOD 0x1c
|
||||
#define MX51_ECSPI_PERIOD_MASK 0x7fff
|
||||
/*
|
||||
* As measured on the i.MX6, the SPI host controller inserts a 4 SPI-Clock
|
||||
* (SCLK) delay after each burst if the PERIOD reg is 0x0. This value will be
|
||||
* called MX51_ECSPI_PERIOD_MIN_DELAY_SCK.
|
||||
*
|
||||
* If the PERIOD register is != 0, the controller inserts a delay of
|
||||
* MX51_ECSPI_PERIOD_MIN_DELAY_SCK + register value + 1 SCLK after each burst.
|
||||
*/
|
||||
#define MX51_ECSPI_PERIOD_MIN_DELAY_SCK 4
|
||||
|
||||
#define MX51_ECSPI_TESTREG 0x20
|
||||
#define MX51_ECSPI_TESTREG_LBC BIT(31)
|
||||
|
||||
@ -407,7 +424,7 @@ static void spi_imx_buf_tx_swap(struct spi_imx_data *spi_imx)
|
||||
|
||||
static void mx53_ecspi_rx_target(struct spi_imx_data *spi_imx)
|
||||
{
|
||||
u32 val = be32_to_cpu(readl(spi_imx->base + MXC_CSPIRXDATA));
|
||||
u32 val = ioread32be(spi_imx->base + MXC_CSPIRXDATA);
|
||||
|
||||
if (spi_imx->rx_buf) {
|
||||
int n_bytes = spi_imx->target_burst % sizeof(val);
|
||||
@ -436,13 +453,12 @@ static void mx53_ecspi_tx_target(struct spi_imx_data *spi_imx)
|
||||
if (spi_imx->tx_buf) {
|
||||
memcpy(((u8 *)&val) + sizeof(val) - n_bytes,
|
||||
spi_imx->tx_buf, n_bytes);
|
||||
val = cpu_to_be32(val);
|
||||
spi_imx->tx_buf += n_bytes;
|
||||
}
|
||||
|
||||
spi_imx->count -= n_bytes;
|
||||
|
||||
writel(val, spi_imx->base + MXC_CSPITXDATA);
|
||||
iowrite32be(val, spi_imx->base + MXC_CSPITXDATA);
|
||||
}
|
||||
|
||||
/* MX51 eCSPI */
|
||||
@ -649,9 +665,10 @@ static void mx51_configure_cpha(struct spi_imx_data *spi_imx,
|
||||
}
|
||||
|
||||
static int mx51_ecspi_prepare_transfer(struct spi_imx_data *spi_imx,
|
||||
struct spi_device *spi)
|
||||
struct spi_device *spi, struct spi_transfer *t)
|
||||
{
|
||||
u32 ctrl = readl(spi_imx->base + MX51_ECSPI_CTRL);
|
||||
u64 word_delay_sck;
|
||||
u32 clk;
|
||||
|
||||
/* Clear BL field and set the right value */
|
||||
@ -683,6 +700,49 @@ static int mx51_ecspi_prepare_transfer(struct spi_imx_data *spi_imx,
|
||||
|
||||
writel(ctrl, spi_imx->base + MX51_ECSPI_CTRL);
|
||||
|
||||
/* calculate word delay in SPI Clock (SCLK) cycles */
|
||||
if (t->word_delay.value == 0) {
|
||||
word_delay_sck = 0;
|
||||
} else if (t->word_delay.unit == SPI_DELAY_UNIT_SCK) {
|
||||
word_delay_sck = t->word_delay.value;
|
||||
|
||||
if (word_delay_sck <= MX51_ECSPI_PERIOD_MIN_DELAY_SCK)
|
||||
word_delay_sck = 0;
|
||||
else if (word_delay_sck <= MX51_ECSPI_PERIOD_MIN_DELAY_SCK + 1)
|
||||
word_delay_sck = 1;
|
||||
else
|
||||
word_delay_sck -= MX51_ECSPI_PERIOD_MIN_DELAY_SCK + 1;
|
||||
} else {
|
||||
int word_delay_ns;
|
||||
|
||||
word_delay_ns = spi_delay_to_ns(&t->word_delay, t);
|
||||
if (word_delay_ns < 0)
|
||||
return word_delay_ns;
|
||||
|
||||
if (word_delay_ns <= mul_u64_u32_div(NSEC_PER_SEC,
|
||||
MX51_ECSPI_PERIOD_MIN_DELAY_SCK,
|
||||
spi_imx->spi_bus_clk)) {
|
||||
word_delay_sck = 0;
|
||||
} else if (word_delay_ns <= mul_u64_u32_div(NSEC_PER_SEC,
|
||||
MX51_ECSPI_PERIOD_MIN_DELAY_SCK + 1,
|
||||
spi_imx->spi_bus_clk)) {
|
||||
word_delay_sck = 1;
|
||||
} else {
|
||||
word_delay_ns -= mul_u64_u32_div(NSEC_PER_SEC,
|
||||
MX51_ECSPI_PERIOD_MIN_DELAY_SCK + 1,
|
||||
spi_imx->spi_bus_clk);
|
||||
|
||||
word_delay_sck = DIV_U64_ROUND_UP((u64)word_delay_ns * spi_imx->spi_bus_clk,
|
||||
NSEC_PER_SEC);
|
||||
}
|
||||
}
|
||||
|
||||
if (!FIELD_FIT(MX51_ECSPI_PERIOD_MASK, word_delay_sck))
|
||||
return -EINVAL;
|
||||
|
||||
writel(FIELD_PREP(MX51_ECSPI_PERIOD_MASK, word_delay_sck),
|
||||
spi_imx->base + MX51_ECSPI_PERIOD);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -774,7 +834,7 @@ static int mx31_prepare_message(struct spi_imx_data *spi_imx,
|
||||
}
|
||||
|
||||
static int mx31_prepare_transfer(struct spi_imx_data *spi_imx,
|
||||
struct spi_device *spi)
|
||||
struct spi_device *spi, struct spi_transfer *t)
|
||||
{
|
||||
unsigned int reg = MX31_CSPICTRL_ENABLE | MX31_CSPICTRL_HOST;
|
||||
unsigned int clk;
|
||||
@ -878,7 +938,7 @@ static int mx21_prepare_message(struct spi_imx_data *spi_imx,
|
||||
}
|
||||
|
||||
static int mx21_prepare_transfer(struct spi_imx_data *spi_imx,
|
||||
struct spi_device *spi)
|
||||
struct spi_device *spi, struct spi_transfer *t)
|
||||
{
|
||||
unsigned int reg = MX21_CSPICTRL_ENABLE | MX21_CSPICTRL_HOST;
|
||||
unsigned int max = is_imx27_cspi(spi_imx) ? 16 : 18;
|
||||
@ -953,7 +1013,7 @@ static int mx1_prepare_message(struct spi_imx_data *spi_imx,
|
||||
}
|
||||
|
||||
static int mx1_prepare_transfer(struct spi_imx_data *spi_imx,
|
||||
struct spi_device *spi)
|
||||
struct spi_device *spi, struct spi_transfer *t)
|
||||
{
|
||||
unsigned int reg = MX1_CSPICTRL_ENABLE | MX1_CSPICTRL_HOST;
|
||||
unsigned int clk;
|
||||
@ -1263,11 +1323,13 @@ static int spi_imx_setupxfer(struct spi_device *spi,
|
||||
|
||||
/*
|
||||
* Initialize the functions for transfer. To transfer non byte-aligned
|
||||
* words, we have to use multiple word-size bursts, we can't use
|
||||
* dynamic_burst in that case.
|
||||
* words, we have to use multiple word-size bursts. To insert word
|
||||
* delay, the burst size has to equal the word size. We can't use
|
||||
* dynamic_burst in these cases.
|
||||
*/
|
||||
if (spi_imx->devtype_data->dynamic_burst && !spi_imx->target_mode &&
|
||||
!(spi->mode & SPI_CS_WORD) &&
|
||||
!(t->word_delay.value) &&
|
||||
(spi_imx->bits_per_word == 8 ||
|
||||
spi_imx->bits_per_word == 16 ||
|
||||
spi_imx->bits_per_word == 32)) {
|
||||
@ -1304,7 +1366,7 @@ static int spi_imx_setupxfer(struct spi_device *spi,
|
||||
spi_imx->target_burst = t->len;
|
||||
}
|
||||
|
||||
spi_imx->devtype_data->prepare_transfer(spi_imx, spi);
|
||||
spi_imx->devtype_data->prepare_transfer(spi_imx, spi, t);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1610,12 +1672,30 @@ static int spi_imx_pio_transfer_target(struct spi_device *spi,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned int spi_imx_transfer_estimate_time_us(struct spi_transfer *transfer)
|
||||
{
|
||||
u64 result;
|
||||
|
||||
result = DIV_U64_ROUND_CLOSEST((u64)USEC_PER_SEC * transfer->len * BITS_PER_BYTE,
|
||||
transfer->effective_speed_hz);
|
||||
if (transfer->word_delay.value) {
|
||||
unsigned int word_delay_us;
|
||||
unsigned int words;
|
||||
|
||||
words = DIV_ROUND_UP(transfer->len * BITS_PER_BYTE, transfer->bits_per_word);
|
||||
word_delay_us = DIV_ROUND_CLOSEST(spi_delay_to_ns(&transfer->word_delay, transfer),
|
||||
NSEC_PER_USEC);
|
||||
result += words * word_delay_us;
|
||||
}
|
||||
|
||||
return min(result, U32_MAX);
|
||||
}
|
||||
|
||||
static int spi_imx_transfer_one(struct spi_controller *controller,
|
||||
struct spi_device *spi,
|
||||
struct spi_transfer *transfer)
|
||||
{
|
||||
struct spi_imx_data *spi_imx = spi_controller_get_devdata(spi->controller);
|
||||
unsigned long hz_per_byte, byte_limit;
|
||||
|
||||
spi_imx_setupxfer(spi, transfer);
|
||||
transfer->effective_speed_hz = spi_imx->spi_bus_clk;
|
||||
@ -1634,15 +1714,10 @@ static int spi_imx_transfer_one(struct spi_controller *controller,
|
||||
*/
|
||||
if (spi_imx->usedma)
|
||||
return spi_imx_dma_transfer(spi_imx, transfer);
|
||||
/*
|
||||
* Calculate the estimated time in us the transfer runs. Find
|
||||
* the number of Hz per byte per polling limit.
|
||||
*/
|
||||
hz_per_byte = polling_limit_us ? ((8 + 4) * USEC_PER_SEC) / polling_limit_us : 0;
|
||||
byte_limit = hz_per_byte ? transfer->effective_speed_hz / hz_per_byte : 1;
|
||||
|
||||
/* run in polling mode for short transfers */
|
||||
if (transfer->len < byte_limit)
|
||||
if (transfer->len == 1 || (polling_limit_us &&
|
||||
spi_imx_transfer_estimate_time_us(transfer) < polling_limit_us))
|
||||
return spi_imx_poll_transfer(spi, transfer);
|
||||
|
||||
return spi_imx_pio_transfer(spi, transfer);
|
||||
@ -1956,7 +2031,7 @@ static struct platform_driver spi_imx_driver = {
|
||||
.pm = pm_ptr(&imx_spi_pm),
|
||||
},
|
||||
.probe = spi_imx_probe,
|
||||
.remove_new = spi_imx_remove,
|
||||
.remove = spi_imx_remove,
|
||||
};
|
||||
module_platform_driver(spi_imx_driver);
|
||||
|
||||
|
@ -94,6 +94,7 @@ static struct pci_driver intel_spi_pci_driver = {
|
||||
.name = "intel-spi",
|
||||
.id_table = intel_spi_pci_ids,
|
||||
.probe = intel_spi_pci_probe,
|
||||
.dev_groups = intel_spi_groups,
|
||||
};
|
||||
|
||||
module_pci_driver(intel_spi_pci_driver);
|
||||
|
@ -28,6 +28,7 @@ static struct platform_driver intel_spi_platform_driver = {
|
||||
.probe = intel_spi_platform_probe,
|
||||
.driver = {
|
||||
.name = "intel-spi",
|
||||
.dev_groups = intel_spi_groups,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -148,6 +148,8 @@
|
||||
* @pr_num: Maximum number of protected range registers
|
||||
* @chip0_size: Size of the first flash chip in bytes
|
||||
* @locked: Is SPI setting locked
|
||||
* @protected: Whether the regions are write protected
|
||||
* @bios_locked: Is BIOS region locked
|
||||
* @swseq_reg: Use SW sequencer in register reads/writes
|
||||
* @swseq_erase: Use SW sequencer in erase operation
|
||||
* @atomic_preopcode: Holds preopcode when atomic sequence is requested
|
||||
@ -166,6 +168,8 @@ struct intel_spi {
|
||||
size_t pr_num;
|
||||
size_t chip0_size;
|
||||
bool locked;
|
||||
bool protected;
|
||||
bool bios_locked;
|
||||
bool swseq_reg;
|
||||
bool swseq_erase;
|
||||
u8 atomic_preopcode;
|
||||
@ -1109,10 +1113,13 @@ static int intel_spi_init(struct intel_spi *ispi)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Try to disable write protection if user asked to do so */
|
||||
if (writeable && !intel_spi_set_writeable(ispi)) {
|
||||
dev_warn(ispi->dev, "can't disable chip write protection\n");
|
||||
writeable = false;
|
||||
ispi->bios_locked = true;
|
||||
/* Try to disable BIOS write protection if user asked to do so */
|
||||
if (writeable) {
|
||||
if (intel_spi_set_writeable(ispi))
|
||||
ispi->bios_locked = false;
|
||||
else
|
||||
dev_warn(ispi->dev, "can't disable chip write protection\n");
|
||||
}
|
||||
|
||||
/* Disable #SMI generation from HW sequencer */
|
||||
@ -1247,8 +1254,10 @@ static void intel_spi_fill_partition(struct intel_spi *ispi,
|
||||
* Also if the user did not ask the chip to be writeable
|
||||
* mask the bit too.
|
||||
*/
|
||||
if (!writeable || intel_spi_is_protected(ispi, base, limit))
|
||||
if (!writeable || intel_spi_is_protected(ispi, base, limit)) {
|
||||
part->mask_flags |= MTD_WRITEABLE;
|
||||
ispi->protected = true;
|
||||
}
|
||||
|
||||
end = (limit << 12) + 4096;
|
||||
if (end > part->size)
|
||||
@ -1411,6 +1420,50 @@ static int intel_spi_populate_chip(struct intel_spi *ispi)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t intel_spi_protected_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct intel_spi *ispi = dev_get_drvdata(dev);
|
||||
|
||||
return sysfs_emit(buf, "%d\n", ispi->protected);
|
||||
}
|
||||
static DEVICE_ATTR_ADMIN_RO(intel_spi_protected);
|
||||
|
||||
static ssize_t intel_spi_locked_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct intel_spi *ispi = dev_get_drvdata(dev);
|
||||
|
||||
return sysfs_emit(buf, "%d\n", ispi->locked);
|
||||
}
|
||||
static DEVICE_ATTR_ADMIN_RO(intel_spi_locked);
|
||||
|
||||
static ssize_t intel_spi_bios_locked_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct intel_spi *ispi = dev_get_drvdata(dev);
|
||||
|
||||
return sysfs_emit(buf, "%d\n", ispi->bios_locked);
|
||||
}
|
||||
static DEVICE_ATTR_ADMIN_RO(intel_spi_bios_locked);
|
||||
|
||||
static struct attribute *intel_spi_attrs[] = {
|
||||
&dev_attr_intel_spi_protected.attr,
|
||||
&dev_attr_intel_spi_locked.attr,
|
||||
&dev_attr_intel_spi_bios_locked.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group intel_spi_attr_group = {
|
||||
.attrs = intel_spi_attrs,
|
||||
};
|
||||
|
||||
const struct attribute_group *intel_spi_groups[] = {
|
||||
&intel_spi_attr_group,
|
||||
NULL
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(intel_spi_groups);
|
||||
|
||||
/**
|
||||
* intel_spi_probe() - Probe the Intel SPI flash controller
|
||||
* @dev: Pointer to the parent device
|
||||
@ -1451,6 +1504,7 @@ int intel_spi_probe(struct device *dev, struct resource *mem,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_set_drvdata(dev, ispi);
|
||||
return intel_spi_populate_chip(ispi);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(intel_spi_probe);
|
||||
|
@ -13,6 +13,8 @@
|
||||
|
||||
struct resource;
|
||||
|
||||
extern const struct attribute_group *intel_spi_groups[];
|
||||
|
||||
int intel_spi_probe(struct device *dev, struct resource *mem,
|
||||
const struct intel_spi_boardinfo *info);
|
||||
|
||||
|
@ -138,7 +138,7 @@ MODULE_DEVICE_TABLE(of, bcm_iproc_of_match);
|
||||
|
||||
static struct platform_driver bcm_iproc_driver = {
|
||||
.probe = bcm_iproc_probe,
|
||||
.remove_new = bcm_iproc_remove,
|
||||
.remove = bcm_iproc_remove,
|
||||
.driver = {
|
||||
.name = "bcm_iproc",
|
||||
.pm = &bcm_qspi_pm_ops,
|
||||
|
@ -139,7 +139,7 @@
|
||||
#define LTQ_SPI_FGPO_CLROUTN_S 0
|
||||
|
||||
#define LTQ_SPI_RXREQ_RXCNT_M 0xFFFF /* Receive count value */
|
||||
#define LTQ_SPI_RXCNT_TODO_M 0xFFFF /* Recevie to-do value */
|
||||
#define LTQ_SPI_RXCNT_TODO_M 0xFFFF /* Receive to-do value */
|
||||
|
||||
#define LTQ_SPI_IRNEN_TFI BIT(4) /* TX finished interrupt */
|
||||
#define LTQ_SPI_IRNEN_F BIT(3) /* Frame end interrupt request */
|
||||
@ -1029,7 +1029,7 @@ static void lantiq_ssc_remove(struct platform_device *pdev)
|
||||
|
||||
static struct platform_driver lantiq_ssc_driver = {
|
||||
.probe = lantiq_ssc_probe,
|
||||
.remove_new = lantiq_ssc_remove,
|
||||
.remove = lantiq_ssc_remove,
|
||||
.driver = {
|
||||
.name = "spi-lantiq-ssc",
|
||||
.of_match_table = lantiq_ssc_match,
|
||||
|
@ -19,12 +19,11 @@ static int loongson_spi_pci_register(struct pci_dev *pdev,
|
||||
if (ret < 0)
|
||||
return dev_err_probe(dev, ret, "cannot enable pci device\n");
|
||||
|
||||
ret = pcim_iomap_regions(pdev, BIT(pci_bar), pci_name(pdev));
|
||||
reg_base = pcim_iomap_region(pdev, pci_bar, pci_name(pdev));
|
||||
ret = PTR_ERR_OR_ZERO(reg_base);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed to request and remap memory\n");
|
||||
|
||||
reg_base = pcim_iomap_table(pdev)[pci_bar];
|
||||
|
||||
ret = loongson_spi_init_controller(dev, reg_base);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed to initialize controller\n");
|
||||
|
@ -949,7 +949,7 @@ MODULE_DEVICE_TABLE(of, meson_spicc_of_match);
|
||||
|
||||
static struct platform_driver meson_spicc_driver = {
|
||||
.probe = meson_spicc_probe,
|
||||
.remove_new = meson_spicc_remove,
|
||||
.remove = meson_spicc_remove,
|
||||
.driver = {
|
||||
.name = "meson-spicc",
|
||||
.of_match_table = of_match_ptr(meson_spicc_of_match),
|
||||
|
@ -429,7 +429,7 @@ MODULE_DEVICE_TABLE(of, meson_spifc_dt_match);
|
||||
|
||||
static struct platform_driver meson_spifc_driver = {
|
||||
.probe = meson_spifc_probe,
|
||||
.remove_new = meson_spifc_remove,
|
||||
.remove = meson_spifc_remove,
|
||||
.driver = {
|
||||
.name = "meson-spifc",
|
||||
.of_match_table = of_match_ptr(meson_spifc_dt_match),
|
||||
|
@ -575,7 +575,7 @@ static struct platform_driver mchp_coreqspi_driver = {
|
||||
.name = "microchip,coreqspi",
|
||||
.of_match_table = mchp_coreqspi_of_match,
|
||||
},
|
||||
.remove_new = mchp_coreqspi_remove,
|
||||
.remove = mchp_coreqspi_remove,
|
||||
};
|
||||
module_platform_driver(mchp_coreqspi_driver);
|
||||
|
||||
|
@ -622,7 +622,7 @@ static struct platform_driver mchp_corespi_driver = {
|
||||
.pm = MICROCHIP_SPI_PM_OPS,
|
||||
.of_match_table = of_match_ptr(mchp_corespi_dt_ids),
|
||||
},
|
||||
.remove_new = mchp_corespi_remove,
|
||||
.remove = mchp_corespi_remove,
|
||||
};
|
||||
module_platform_driver(mchp_corespi_driver);
|
||||
MODULE_DESCRIPTION("Microchip coreSPI SPI controller driver");
|
||||
|
@ -107,7 +107,7 @@ static int mpc52xx_psc_spi_transfer_rxtx(struct spi_device *spi,
|
||||
struct mpc52xx_psc_spi *mps = spi_controller_get_devdata(spi->controller);
|
||||
struct mpc52xx_psc __iomem *psc = mps->psc;
|
||||
struct mpc52xx_psc_fifo __iomem *fifo = mps->fifo;
|
||||
unsigned rb = 0; /* number of bytes receieved */
|
||||
unsigned rb = 0; /* number of bytes received */
|
||||
unsigned sb = 0; /* number of bytes sent */
|
||||
unsigned char *rx_buf = (unsigned char *)t->rx_buf;
|
||||
unsigned char *tx_buf = (unsigned char *)t->tx_buf;
|
||||
@ -325,7 +325,7 @@ static int mpc52xx_psc_spi_of_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(mps->psc))
|
||||
return dev_err_probe(dev, PTR_ERR(mps->psc), "could not ioremap I/O port range\n");
|
||||
|
||||
/* On the 5200, fifo regs are immediately ajacent to the psc regs */
|
||||
/* On the 5200, fifo regs are immediately adjacent to the psc regs */
|
||||
mps->fifo = ((void __iomem *)mps->psc) + sizeof(struct mpc52xx_psc);
|
||||
|
||||
mps->irq = platform_get_irq(pdev, 0);
|
||||
|
@ -544,6 +544,6 @@ static struct platform_driver mpc52xx_spi_of_driver = {
|
||||
.of_match_table = mpc52xx_spi_match,
|
||||
},
|
||||
.probe = mpc52xx_spi_probe,
|
||||
.remove_new = mpc52xx_spi_remove,
|
||||
.remove = mpc52xx_spi_remove,
|
||||
};
|
||||
module_platform_driver(mpc52xx_spi_of_driver);
|
||||
|
@ -1432,7 +1432,7 @@ static struct platform_driver mtk_spi_driver = {
|
||||
.of_match_table = mtk_spi_of_match,
|
||||
},
|
||||
.probe = mtk_spi_probe,
|
||||
.remove_new = mtk_spi_remove,
|
||||
.remove = mtk_spi_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(mtk_spi_driver);
|
||||
|
@ -998,7 +998,7 @@ static struct platform_driver mtk_nor_driver = {
|
||||
.pm = &mtk_nor_pm_ops,
|
||||
},
|
||||
.probe = mtk_nor_probe,
|
||||
.remove_new = mtk_nor_remove,
|
||||
.remove = mtk_nor_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(mtk_nor_driver);
|
||||
|
@ -1477,7 +1477,7 @@ static void mtk_snand_remove(struct platform_device *pdev)
|
||||
|
||||
static struct platform_driver mtk_snand_driver = {
|
||||
.probe = mtk_snand_probe,
|
||||
.remove_new = mtk_snand_remove,
|
||||
.remove = mtk_snand_remove,
|
||||
.driver = {
|
||||
.name = "mtk-snand",
|
||||
.of_match_table = mtk_snand_ids,
|
||||
|
@ -836,7 +836,7 @@ MODULE_DEVICE_TABLE(of, mxic_spi_of_ids);
|
||||
|
||||
static struct platform_driver mxic_spi_driver = {
|
||||
.probe = mxic_spi_probe,
|
||||
.remove_new = mxic_spi_remove,
|
||||
.remove = mxic_spi_remove,
|
||||
.driver = {
|
||||
.name = "mxic-spi",
|
||||
.of_match_table = mxic_spi_of_ids,
|
||||
|
@ -657,7 +657,7 @@ static void mxs_spi_remove(struct platform_device *pdev)
|
||||
|
||||
static struct platform_driver mxs_spi_driver = {
|
||||
.probe = mxs_spi_probe,
|
||||
.remove_new = mxs_spi_remove,
|
||||
.remove = mxs_spi_remove,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.of_match_table = mxs_spi_dt_ids,
|
||||
|
@ -766,12 +766,12 @@ MODULE_DEVICE_TABLE(of, npcm_fiu_dt_ids);
|
||||
|
||||
static struct platform_driver npcm_fiu_driver = {
|
||||
.driver = {
|
||||
.name = "NPCM-FIU",
|
||||
.bus = &platform_bus_type,
|
||||
.name = "NPCM-FIU",
|
||||
.bus = &platform_bus_type,
|
||||
.of_match_table = npcm_fiu_dt_ids,
|
||||
},
|
||||
.probe = npcm_fiu_probe,
|
||||
.remove_new = npcm_fiu_remove,
|
||||
.probe = npcm_fiu_probe,
|
||||
.remove = npcm_fiu_remove,
|
||||
};
|
||||
module_platform_driver(npcm_fiu_driver);
|
||||
|
||||
|
@ -452,7 +452,7 @@ static struct platform_driver npcm_pspi_driver = {
|
||||
.of_match_table = npcm_pspi_match,
|
||||
},
|
||||
.probe = npcm_pspi_probe,
|
||||
.remove_new = npcm_pspi_remove,
|
||||
.remove = npcm_pspi_remove,
|
||||
};
|
||||
module_platform_driver(npcm_pspi_driver);
|
||||
|
||||
|
@ -1331,7 +1331,7 @@ static struct platform_driver nxp_fspi_driver = {
|
||||
.pm = &nxp_fspi_pm_ops,
|
||||
},
|
||||
.probe = nxp_fspi_probe,
|
||||
.remove_new = nxp_fspi_remove,
|
||||
.remove = nxp_fspi_remove,
|
||||
};
|
||||
module_platform_driver(nxp_fspi_driver);
|
||||
|
||||
|
@ -288,7 +288,7 @@ MODULE_DEVICE_TABLE(of, tiny_spi_match);
|
||||
|
||||
static struct platform_driver tiny_spi_driver = {
|
||||
.probe = tiny_spi_probe,
|
||||
.remove_new = tiny_spi_remove,
|
||||
.remove = tiny_spi_remove,
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.pm = NULL,
|
||||
|
@ -523,7 +523,7 @@ static struct platform_driver uwire_driver = {
|
||||
.name = "omap_uwire",
|
||||
},
|
||||
.probe = uwire_probe,
|
||||
.remove_new = uwire_remove,
|
||||
.remove = uwire_remove,
|
||||
// suspend ... unuse ck
|
||||
// resume ... use ck
|
||||
};
|
||||
|
@ -1654,7 +1654,7 @@ static struct platform_driver omap2_mcspi_driver = {
|
||||
.of_match_table = omap_mcspi_of_match,
|
||||
},
|
||||
.probe = omap2_mcspi_probe,
|
||||
.remove_new = omap2_mcspi_remove,
|
||||
.remove = omap2_mcspi_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(omap2_mcspi_driver);
|
||||
|
@ -846,7 +846,7 @@ static struct platform_driver orion_spi_driver = {
|
||||
.of_match_table = of_match_ptr(orion_spi_of_match_table),
|
||||
},
|
||||
.probe = orion_spi_probe,
|
||||
.remove_new = orion_spi_remove,
|
||||
.remove = orion_spi_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(orion_spi_driver);
|
||||
|
@ -226,7 +226,7 @@ static irqreturn_t pic32_sqi_isr(int irq, void *dev_id)
|
||||
if (status & PESQI_PKTCOMP) {
|
||||
/* mask all interrupts */
|
||||
enable = 0;
|
||||
/* complete trasaction */
|
||||
/* complete transaction */
|
||||
complete(&sqi->xfer_done);
|
||||
}
|
||||
|
||||
@ -682,7 +682,7 @@ static struct platform_driver pic32_sqi_driver = {
|
||||
.of_match_table = of_match_ptr(pic32_sqi_of_ids),
|
||||
},
|
||||
.probe = pic32_sqi_probe,
|
||||
.remove_new = pic32_sqi_remove,
|
||||
.remove = pic32_sqi_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(pic32_sqi_driver);
|
||||
|
@ -859,7 +859,7 @@ static struct platform_driver pic32_spi_driver = {
|
||||
.of_match_table = of_match_ptr(pic32_spi_of_match),
|
||||
},
|
||||
.probe = pic32_spi_probe,
|
||||
.remove_new = pic32_spi_remove,
|
||||
.remove = pic32_spi_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(pic32_spi_driver);
|
||||
|
@ -899,7 +899,7 @@ static int configure_dma(struct pl022 *pl022)
|
||||
break;
|
||||
}
|
||||
|
||||
/* SPI pecularity: we need to read and write the same width */
|
||||
/* SPI peculiarity: we need to read and write the same width */
|
||||
if (rx_conf.src_addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED)
|
||||
rx_conf.src_addr_width = tx_conf.dst_addr_width;
|
||||
if (tx_conf.dst_addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED)
|
||||
|
@ -484,7 +484,7 @@ MODULE_DEVICE_TABLE(of, spi_ppc4xx_of_match);
|
||||
|
||||
static struct platform_driver spi_ppc4xx_of_driver = {
|
||||
.probe = spi_ppc4xx_of_probe,
|
||||
.remove_new = spi_ppc4xx_of_remove,
|
||||
.remove = spi_ppc4xx_of_remove,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.of_match_table = spi_ppc4xx_of_match,
|
||||
|
@ -273,10 +273,6 @@ static int pxa2xx_spi_pci_probe(struct pci_dev *dev,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = pcim_iomap_regions(dev, 1 << 0, "PXA2xx SPI");
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pdata = devm_kzalloc(&dev->dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return -ENOMEM;
|
||||
@ -284,7 +280,9 @@ static int pxa2xx_spi_pci_probe(struct pci_dev *dev,
|
||||
ssp = &pdata->ssp;
|
||||
ssp->dev = &dev->dev;
|
||||
ssp->phys_base = pci_resource_start(dev, 0);
|
||||
ssp->mmio_base = pcim_iomap_table(dev)[0];
|
||||
ssp->mmio_base = pcim_iomap_region(dev, 0, "PXA2xx SPI");
|
||||
if (IS_ERR(ssp->mmio_base))
|
||||
return PTR_ERR(ssp->mmio_base);
|
||||
|
||||
info = (struct pxa_spi_info *)ent->driver_data;
|
||||
ret = info->setup(dev, pdata);
|
||||
|
@ -207,7 +207,7 @@ static struct platform_driver driver = {
|
||||
.of_match_table = pxa2xx_spi_of_match,
|
||||
},
|
||||
.probe = pxa2xx_spi_platform_probe,
|
||||
.remove_new = pxa2xx_spi_platform_remove,
|
||||
.remove = pxa2xx_spi_platform_remove,
|
||||
};
|
||||
|
||||
static int __init pxa2xx_spi_init(void)
|
||||
|
@ -771,7 +771,7 @@ static int qcom_qspi_probe(struct platform_device *pdev)
|
||||
host->prepare_message = qcom_qspi_prepare_message;
|
||||
host->transfer_one = qcom_qspi_transfer_one;
|
||||
host->handle_err = qcom_qspi_handle_err;
|
||||
if (of_property_read_bool(pdev->dev.of_node, "iommus"))
|
||||
if (of_property_present(pdev->dev.of_node, "iommus"))
|
||||
host->can_dma = qcom_qspi_can_dma;
|
||||
host->auto_runtime_pm = true;
|
||||
host->mem_ops = &qcom_qspi_mem_ops;
|
||||
@ -908,7 +908,7 @@ static struct platform_driver qcom_qspi_driver = {
|
||||
.of_match_table = qcom_qspi_dt_match,
|
||||
},
|
||||
.probe = qcom_qspi_probe,
|
||||
.remove_new = qcom_qspi_remove,
|
||||
.remove = qcom_qspi_remove,
|
||||
};
|
||||
module_platform_driver(qcom_qspi_driver);
|
||||
|
||||
|
@ -1364,7 +1364,7 @@ static struct platform_driver spi_qup_driver = {
|
||||
.of_match_table = spi_qup_dt_match,
|
||||
},
|
||||
.probe = spi_qup_probe,
|
||||
.remove_new = spi_qup_remove,
|
||||
.remove = spi_qup_remove,
|
||||
};
|
||||
module_platform_driver(spi_qup_driver);
|
||||
|
||||
|
@ -196,7 +196,7 @@ MODULE_DEVICE_TABLE(of, rb4xx_spi_dt_match);
|
||||
|
||||
static struct platform_driver rb4xx_spi_drv = {
|
||||
.probe = rb4xx_spi_probe,
|
||||
.remove_new = rb4xx_spi_remove,
|
||||
.remove = rb4xx_spi_remove,
|
||||
.driver = {
|
||||
.name = "rb4xx-spi",
|
||||
.of_match_table = of_match_ptr(rb4xx_spi_dt_match),
|
||||
|
419
drivers/spi/spi-realtek-rtl-snand.c
Normal file
419
drivers/spi/spi-realtek-rtl-snand.c
Normal file
@ -0,0 +1,419 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include <linux/completion.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/spi-mem.h>
|
||||
|
||||
#define SNAFCFR 0x00
|
||||
#define SNAFCFR_DMA_IE BIT(20)
|
||||
#define SNAFCCR 0x04
|
||||
#define SNAFWCMR 0x08
|
||||
#define SNAFRCMR 0x0c
|
||||
#define SNAFRDR 0x10
|
||||
#define SNAFWDR 0x14
|
||||
#define SNAFDTR 0x18
|
||||
#define SNAFDRSAR 0x1c
|
||||
#define SNAFDIR 0x20
|
||||
#define SNAFDIR_DMA_IP BIT(0)
|
||||
#define SNAFDLR 0x24
|
||||
#define SNAFSR 0x40
|
||||
#define SNAFSR_NFCOS BIT(3)
|
||||
#define SNAFSR_NFDRS BIT(2)
|
||||
#define SNAFSR_NFDWS BIT(1)
|
||||
|
||||
#define CMR_LEN(len) ((len) - 1)
|
||||
#define CMR_WID(width) (((width) >> 1) << 28)
|
||||
|
||||
struct rtl_snand {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct completion comp;
|
||||
};
|
||||
|
||||
static irqreturn_t rtl_snand_irq(int irq, void *data)
|
||||
{
|
||||
struct rtl_snand *snand = data;
|
||||
u32 val = 0;
|
||||
|
||||
regmap_read(snand->regmap, SNAFSR, &val);
|
||||
if (val & (SNAFSR_NFCOS | SNAFSR_NFDRS | SNAFSR_NFDWS))
|
||||
return IRQ_NONE;
|
||||
|
||||
regmap_write(snand->regmap, SNAFDIR, SNAFDIR_DMA_IP);
|
||||
complete(&snand->comp);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static bool rtl_snand_supports_op(struct spi_mem *mem,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
if (!spi_mem_default_supports_op(mem, op))
|
||||
return false;
|
||||
if (op->cmd.nbytes != 1 || op->cmd.buswidth != 1)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void rtl_snand_set_cs(struct rtl_snand *snand, int cs, bool active)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
if (active)
|
||||
val = ~(1 << (4 * cs));
|
||||
else
|
||||
val = ~0;
|
||||
|
||||
regmap_write(snand->regmap, SNAFCCR, val);
|
||||
}
|
||||
|
||||
static int rtl_snand_wait_ready(struct rtl_snand *snand)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
return regmap_read_poll_timeout(snand->regmap, SNAFSR, val, !(val & SNAFSR_NFCOS),
|
||||
0, 2 * USEC_PER_MSEC);
|
||||
}
|
||||
|
||||
static int rtl_snand_xfer_head(struct rtl_snand *snand, int cs, const struct spi_mem_op *op)
|
||||
{
|
||||
int ret;
|
||||
u32 val, len = 0;
|
||||
|
||||
rtl_snand_set_cs(snand, cs, true);
|
||||
|
||||
val = op->cmd.opcode << 24;
|
||||
len = 1;
|
||||
if (op->addr.nbytes && op->addr.buswidth == 1) {
|
||||
val |= op->addr.val << ((3 - op->addr.nbytes) * 8);
|
||||
len += op->addr.nbytes;
|
||||
}
|
||||
|
||||
ret = rtl_snand_wait_ready(snand);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_write(snand->regmap, SNAFWCMR, CMR_LEN(len));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_write(snand->regmap, SNAFWDR, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = rtl_snand_wait_ready(snand);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (op->addr.buswidth > 1) {
|
||||
val = op->addr.val << ((3 - op->addr.nbytes) * 8);
|
||||
len = op->addr.nbytes;
|
||||
|
||||
ret = regmap_write(snand->regmap, SNAFWCMR,
|
||||
CMR_WID(op->addr.buswidth) | CMR_LEN(len));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_write(snand->regmap, SNAFWDR, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = rtl_snand_wait_ready(snand);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (op->dummy.nbytes) {
|
||||
val = 0;
|
||||
|
||||
ret = regmap_write(snand->regmap, SNAFWCMR,
|
||||
CMR_WID(op->dummy.buswidth) | CMR_LEN(op->dummy.nbytes));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_write(snand->regmap, SNAFWDR, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = rtl_snand_wait_ready(snand);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rtl_snand_xfer_tail(struct rtl_snand *snand, int cs)
|
||||
{
|
||||
rtl_snand_set_cs(snand, cs, false);
|
||||
}
|
||||
|
||||
static int rtl_snand_xfer(struct rtl_snand *snand, int cs, const struct spi_mem_op *op)
|
||||
{
|
||||
unsigned int pos, nbytes;
|
||||
int ret;
|
||||
u32 val, len = 0;
|
||||
|
||||
ret = rtl_snand_xfer_head(snand, cs, op);
|
||||
if (ret)
|
||||
goto out_deselect;
|
||||
|
||||
if (op->data.dir == SPI_MEM_DATA_IN) {
|
||||
pos = 0;
|
||||
len = op->data.nbytes;
|
||||
|
||||
while (pos < len) {
|
||||
nbytes = len - pos;
|
||||
if (nbytes > 4)
|
||||
nbytes = 4;
|
||||
|
||||
ret = rtl_snand_wait_ready(snand);
|
||||
if (ret)
|
||||
goto out_deselect;
|
||||
|
||||
ret = regmap_write(snand->regmap, SNAFRCMR,
|
||||
CMR_WID(op->data.buswidth) | CMR_LEN(nbytes));
|
||||
if (ret)
|
||||
goto out_deselect;
|
||||
|
||||
ret = rtl_snand_wait_ready(snand);
|
||||
if (ret)
|
||||
goto out_deselect;
|
||||
|
||||
ret = regmap_read(snand->regmap, SNAFRDR, &val);
|
||||
if (ret)
|
||||
goto out_deselect;
|
||||
|
||||
memcpy(op->data.buf.in + pos, &val, nbytes);
|
||||
|
||||
pos += nbytes;
|
||||
}
|
||||
} else if (op->data.dir == SPI_MEM_DATA_OUT) {
|
||||
pos = 0;
|
||||
len = op->data.nbytes;
|
||||
|
||||
while (pos < len) {
|
||||
nbytes = len - pos;
|
||||
if (nbytes > 4)
|
||||
nbytes = 4;
|
||||
|
||||
memcpy(&val, op->data.buf.out + pos, nbytes);
|
||||
|
||||
pos += nbytes;
|
||||
|
||||
ret = regmap_write(snand->regmap, SNAFWCMR, CMR_LEN(nbytes));
|
||||
if (ret)
|
||||
goto out_deselect;
|
||||
|
||||
ret = regmap_write(snand->regmap, SNAFWDR, val);
|
||||
if (ret)
|
||||
goto out_deselect;
|
||||
|
||||
ret = rtl_snand_wait_ready(snand);
|
||||
if (ret)
|
||||
goto out_deselect;
|
||||
}
|
||||
}
|
||||
|
||||
out_deselect:
|
||||
rtl_snand_xfer_tail(snand, cs);
|
||||
|
||||
if (ret)
|
||||
dev_err(snand->dev, "transfer failed %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rtl_snand_dma_xfer(struct rtl_snand *snand, int cs, const struct spi_mem_op *op)
|
||||
{
|
||||
unsigned int pos, nbytes;
|
||||
int ret;
|
||||
dma_addr_t buf_dma;
|
||||
enum dma_data_direction dir;
|
||||
u32 trig, len, maxlen;
|
||||
|
||||
ret = rtl_snand_xfer_head(snand, cs, op);
|
||||
if (ret)
|
||||
goto out_deselect;
|
||||
|
||||
if (op->data.dir == SPI_MEM_DATA_IN) {
|
||||
maxlen = 2080;
|
||||
dir = DMA_FROM_DEVICE;
|
||||
trig = 0;
|
||||
} else if (op->data.dir == SPI_MEM_DATA_OUT) {
|
||||
maxlen = 520;
|
||||
dir = DMA_TO_DEVICE;
|
||||
trig = 1;
|
||||
} else {
|
||||
ret = -EOPNOTSUPP;
|
||||
goto out_deselect;
|
||||
}
|
||||
|
||||
buf_dma = dma_map_single(snand->dev, op->data.buf.in, op->data.nbytes, dir);
|
||||
ret = dma_mapping_error(snand->dev, buf_dma);
|
||||
if (ret)
|
||||
goto out_deselect;
|
||||
|
||||
ret = regmap_write(snand->regmap, SNAFDIR, SNAFDIR_DMA_IP);
|
||||
if (ret)
|
||||
goto out_unmap;
|
||||
|
||||
ret = regmap_update_bits(snand->regmap, SNAFCFR, SNAFCFR_DMA_IE, SNAFCFR_DMA_IE);
|
||||
if (ret)
|
||||
goto out_unmap;
|
||||
|
||||
pos = 0;
|
||||
len = op->data.nbytes;
|
||||
|
||||
while (pos < len) {
|
||||
nbytes = len - pos;
|
||||
if (nbytes > maxlen)
|
||||
nbytes = maxlen;
|
||||
|
||||
reinit_completion(&snand->comp);
|
||||
|
||||
ret = regmap_write(snand->regmap, SNAFDRSAR, buf_dma + pos);
|
||||
if (ret)
|
||||
goto out_disable_int;
|
||||
|
||||
pos += nbytes;
|
||||
|
||||
ret = regmap_write(snand->regmap, SNAFDLR,
|
||||
CMR_WID(op->data.buswidth) | nbytes);
|
||||
if (ret)
|
||||
goto out_disable_int;
|
||||
|
||||
ret = regmap_write(snand->regmap, SNAFDTR, trig);
|
||||
if (ret)
|
||||
goto out_disable_int;
|
||||
|
||||
if (!wait_for_completion_timeout(&snand->comp, usecs_to_jiffies(20000)))
|
||||
ret = -ETIMEDOUT;
|
||||
|
||||
if (ret)
|
||||
goto out_disable_int;
|
||||
}
|
||||
|
||||
out_disable_int:
|
||||
regmap_update_bits(snand->regmap, SNAFCFR, SNAFCFR_DMA_IE, 0);
|
||||
out_unmap:
|
||||
dma_unmap_single(snand->dev, buf_dma, op->data.nbytes, dir);
|
||||
out_deselect:
|
||||
rtl_snand_xfer_tail(snand, cs);
|
||||
|
||||
if (ret)
|
||||
dev_err(snand->dev, "transfer failed %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool rtl_snand_dma_op(const struct spi_mem_op *op)
|
||||
{
|
||||
switch (op->data.dir) {
|
||||
case SPI_MEM_DATA_IN:
|
||||
case SPI_MEM_DATA_OUT:
|
||||
return op->data.nbytes > 32;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static int rtl_snand_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
{
|
||||
struct rtl_snand *snand = spi_controller_get_devdata(mem->spi->controller);
|
||||
int cs = spi_get_chipselect(mem->spi, 0);
|
||||
|
||||
dev_dbg(snand->dev, "cs %d op cmd %02x %d:%d, dummy %d:%d, addr %08llx@%d:%d, data %d:%d\n",
|
||||
cs, op->cmd.opcode,
|
||||
op->cmd.buswidth, op->cmd.nbytes, op->dummy.buswidth,
|
||||
op->dummy.nbytes, op->addr.val, op->addr.buswidth,
|
||||
op->addr.nbytes, op->data.buswidth, op->data.nbytes);
|
||||
|
||||
if (rtl_snand_dma_op(op))
|
||||
return rtl_snand_dma_xfer(snand, cs, op);
|
||||
else
|
||||
return rtl_snand_xfer(snand, cs, op);
|
||||
}
|
||||
|
||||
static const struct spi_controller_mem_ops rtl_snand_mem_ops = {
|
||||
.supports_op = rtl_snand_supports_op,
|
||||
.exec_op = rtl_snand_exec_op,
|
||||
};
|
||||
|
||||
static const struct of_device_id rtl_snand_match[] = {
|
||||
{ .compatible = "realtek,rtl9301-snand" },
|
||||
{ .compatible = "realtek,rtl9302b-snand" },
|
||||
{ .compatible = "realtek,rtl9302c-snand" },
|
||||
{ .compatible = "realtek,rtl9303-snand" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rtl_snand_match);
|
||||
|
||||
static int rtl_snand_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct rtl_snand *snand;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct spi_controller *ctrl;
|
||||
void __iomem *base;
|
||||
const struct regmap_config rc = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.cache_type = REGCACHE_NONE,
|
||||
};
|
||||
int irq, ret;
|
||||
|
||||
ctrl = devm_spi_alloc_host(dev, sizeof(*snand));
|
||||
if (!ctrl)
|
||||
return -ENOMEM;
|
||||
|
||||
snand = spi_controller_get_devdata(ctrl);
|
||||
snand->dev = dev;
|
||||
|
||||
base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
snand->regmap = devm_regmap_init_mmio(dev, base, &rc);
|
||||
if (IS_ERR(snand->regmap))
|
||||
return PTR_ERR(snand->regmap);
|
||||
|
||||
init_completion(&snand->comp);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
ret = dma_set_mask(snand->dev, DMA_BIT_MASK(32));
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed to set DMA mask\n");
|
||||
|
||||
ret = devm_request_irq(dev, irq, rtl_snand_irq, 0, "rtl-snand", snand);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed to request irq\n");
|
||||
|
||||
ctrl->num_chipselect = 2;
|
||||
ctrl->mem_ops = &rtl_snand_mem_ops;
|
||||
ctrl->bits_per_word_mask = SPI_BPW_MASK(8);
|
||||
ctrl->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD | SPI_TX_DUAL | SPI_TX_QUAD;
|
||||
device_set_node(&ctrl->dev, dev_fwnode(dev));
|
||||
|
||||
return devm_spi_register_controller(dev, ctrl);
|
||||
}
|
||||
|
||||
static struct platform_driver rtl_snand_driver = {
|
||||
.driver = {
|
||||
.name = "realtek-rtl-snand",
|
||||
.of_match_table = rtl_snand_match,
|
||||
},
|
||||
.probe = rtl_snand_probe,
|
||||
};
|
||||
module_platform_driver(rtl_snand_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Realtek SPI-NAND Flash Controller Driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -111,7 +111,7 @@
|
||||
#define SFC_VER_4 0x4
|
||||
#define SFC_VER_5 0x5
|
||||
|
||||
/* Delay line controller resiter */
|
||||
/* Delay line controller register */
|
||||
#define SFC_DLL_CTRL0 0x3C
|
||||
#define SFC_DLL_CTRL0_SCLK_SMP_DLL BIT(15)
|
||||
#define SFC_DLL_CTRL0_DLL_MAX_VER4 0xFFU
|
||||
@ -580,19 +580,16 @@ static int rockchip_sfc_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(sfc->regbase);
|
||||
|
||||
sfc->clk = devm_clk_get(&pdev->dev, "clk_sfc");
|
||||
if (IS_ERR(sfc->clk)) {
|
||||
dev_err(&pdev->dev, "Failed to get sfc interface clk\n");
|
||||
return PTR_ERR(sfc->clk);
|
||||
}
|
||||
if (IS_ERR(sfc->clk))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(sfc->clk),
|
||||
"Failed to get sfc interface clk\n");
|
||||
|
||||
sfc->hclk = devm_clk_get(&pdev->dev, "hclk_sfc");
|
||||
if (IS_ERR(sfc->hclk)) {
|
||||
dev_err(&pdev->dev, "Failed to get sfc ahb clk\n");
|
||||
return PTR_ERR(sfc->hclk);
|
||||
}
|
||||
if (IS_ERR(sfc->hclk))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(sfc->hclk),
|
||||
"Failed to get sfc ahb clk\n");
|
||||
|
||||
sfc->use_dma = !of_property_read_bool(sfc->dev->of_node,
|
||||
"rockchip,sfc-no-dma");
|
||||
sfc->use_dma = !of_property_read_bool(sfc->dev->of_node, "rockchip,sfc-no-dma");
|
||||
|
||||
if (sfc->use_dma) {
|
||||
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
|
||||
@ -602,8 +599,7 @@ static int rockchip_sfc_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
sfc->buffer = dmam_alloc_coherent(dev, SFC_MAX_IOSIZE_VER3,
|
||||
&sfc->dma_buffer,
|
||||
GFP_KERNEL);
|
||||
&sfc->dma_buffer, GFP_KERNEL);
|
||||
if (!sfc->buffer)
|
||||
return -ENOMEM;
|
||||
}
|
||||
@ -629,7 +625,6 @@ static int rockchip_sfc_probe(struct platform_device *pdev)
|
||||
0, pdev->name, sfc);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to request irq\n");
|
||||
|
||||
goto err_irq;
|
||||
}
|
||||
|
||||
@ -677,7 +672,7 @@ static struct platform_driver rockchip_sfc_driver = {
|
||||
.of_match_table = rockchip_sfc_dt_ids,
|
||||
},
|
||||
.probe = rockchip_sfc_probe,
|
||||
.remove_new = rockchip_sfc_remove,
|
||||
.remove = rockchip_sfc_remove,
|
||||
};
|
||||
module_platform_driver(rockchip_sfc_driver);
|
||||
|
||||
|
@ -192,7 +192,7 @@ struct rockchip_spi {
|
||||
u8 rsd;
|
||||
|
||||
bool target_abort;
|
||||
bool cs_inactive; /* spi target tansmition stop when cs inactive */
|
||||
bool cs_inactive; /* spi target transmission stop when cs inactive */
|
||||
bool cs_high_supported; /* native CS supports active-high polarity */
|
||||
|
||||
struct spi_transfer *xfer; /* Store xfer temporarily */
|
||||
@ -742,22 +742,20 @@ static int rockchip_spi_setup(struct spi_device *spi)
|
||||
|
||||
static int rockchip_spi_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct rockchip_spi *rs;
|
||||
struct spi_controller *ctlr;
|
||||
struct resource *mem;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct spi_controller *ctlr;
|
||||
struct rockchip_spi *rs;
|
||||
struct resource *mem;
|
||||
u32 rsd_nsecs, num_cs;
|
||||
bool target_mode;
|
||||
int ret;
|
||||
|
||||
target_mode = of_property_read_bool(np, "spi-slave");
|
||||
|
||||
if (target_mode)
|
||||
ctlr = spi_alloc_target(&pdev->dev,
|
||||
sizeof(struct rockchip_spi));
|
||||
ctlr = spi_alloc_target(&pdev->dev, sizeof(struct rockchip_spi));
|
||||
else
|
||||
ctlr = spi_alloc_host(&pdev->dev,
|
||||
sizeof(struct rockchip_spi));
|
||||
ctlr = spi_alloc_host(&pdev->dev, sizeof(struct rockchip_spi));
|
||||
|
||||
if (!ctlr)
|
||||
return -ENOMEM;
|
||||
@ -769,21 +767,21 @@ static int rockchip_spi_probe(struct platform_device *pdev)
|
||||
/* Get basic io resource and map it */
|
||||
rs->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem);
|
||||
if (IS_ERR(rs->regs)) {
|
||||
ret = PTR_ERR(rs->regs);
|
||||
ret = PTR_ERR(rs->regs);
|
||||
goto err_put_ctlr;
|
||||
}
|
||||
|
||||
rs->apb_pclk = devm_clk_get_enabled(&pdev->dev, "apb_pclk");
|
||||
if (IS_ERR(rs->apb_pclk)) {
|
||||
dev_err(&pdev->dev, "Failed to get apb_pclk\n");
|
||||
ret = PTR_ERR(rs->apb_pclk);
|
||||
ret = dev_err_probe(&pdev->dev, PTR_ERR(rs->apb_pclk),
|
||||
"Failed to get apb_pclk\n");
|
||||
goto err_put_ctlr;
|
||||
}
|
||||
|
||||
rs->spiclk = devm_clk_get_enabled(&pdev->dev, "spiclk");
|
||||
if (IS_ERR(rs->spiclk)) {
|
||||
dev_err(&pdev->dev, "Failed to get spi_pclk\n");
|
||||
ret = PTR_ERR(rs->spiclk);
|
||||
ret = dev_err_probe(&pdev->dev, PTR_ERR(rs->spiclk),
|
||||
"Failed to get spi_pclk\n");
|
||||
goto err_put_ctlr;
|
||||
}
|
||||
|
||||
@ -794,7 +792,7 @@ static int rockchip_spi_probe(struct platform_device *pdev)
|
||||
goto err_put_ctlr;
|
||||
|
||||
ret = devm_request_threaded_irq(&pdev->dev, ret, rockchip_spi_isr, NULL,
|
||||
IRQF_ONESHOT, dev_name(&pdev->dev), ctlr);
|
||||
IRQF_ONESHOT, dev_name(&pdev->dev), ctlr);
|
||||
if (ret)
|
||||
goto err_put_ctlr;
|
||||
|
||||
@ -804,24 +802,22 @@ static int rockchip_spi_probe(struct platform_device *pdev)
|
||||
if (!of_property_read_u32(pdev->dev.of_node, "rx-sample-delay-ns",
|
||||
&rsd_nsecs)) {
|
||||
/* rx sample delay is expressed in parent clock cycles (max 3) */
|
||||
u32 rsd = DIV_ROUND_CLOSEST(rsd_nsecs * (rs->freq >> 8),
|
||||
1000000000 >> 8);
|
||||
u32 rsd = DIV_ROUND_CLOSEST(rsd_nsecs * (rs->freq >> 8), 1000000000 >> 8);
|
||||
if (!rsd) {
|
||||
dev_warn(rs->dev, "%u Hz are too slow to express %u ns delay\n",
|
||||
rs->freq, rsd_nsecs);
|
||||
rs->freq, rsd_nsecs);
|
||||
} else if (rsd > CR0_RSD_MAX) {
|
||||
rsd = CR0_RSD_MAX;
|
||||
dev_warn(rs->dev, "%u Hz are too fast to express %u ns delay, clamping at %u ns\n",
|
||||
rs->freq, rsd_nsecs,
|
||||
CR0_RSD_MAX * 1000000000U / rs->freq);
|
||||
dev_warn(rs->dev,
|
||||
"%u Hz are too fast to express %u ns delay, clamping at %u ns\n",
|
||||
rs->freq, rsd_nsecs, CR0_RSD_MAX * 1000000000U / rs->freq);
|
||||
}
|
||||
rs->rsd = rsd;
|
||||
}
|
||||
|
||||
rs->fifo_len = get_fifo_len(rs);
|
||||
if (!rs->fifo_len) {
|
||||
dev_err(&pdev->dev, "Failed to get fifo length\n");
|
||||
ret = -EINVAL;
|
||||
ret = dev_err_probe(&pdev->dev, -EINVAL, "Failed to get fifo length\n");
|
||||
goto err_put_ctlr;
|
||||
}
|
||||
|
||||
@ -861,22 +857,21 @@ static int rockchip_spi_probe(struct platform_device *pdev)
|
||||
|
||||
ctlr->dma_tx = dma_request_chan(rs->dev, "tx");
|
||||
if (IS_ERR(ctlr->dma_tx)) {
|
||||
/* Check tx to see if we need defer probing driver */
|
||||
if (PTR_ERR(ctlr->dma_tx) == -EPROBE_DEFER) {
|
||||
ret = -EPROBE_DEFER;
|
||||
/* Check tx to see if we need to defer driver probing */
|
||||
ret = dev_warn_probe(rs->dev, PTR_ERR(ctlr->dma_tx),
|
||||
"Failed to request optional TX DMA channel\n");
|
||||
if (ret == -EPROBE_DEFER)
|
||||
goto err_disable_pm_runtime;
|
||||
}
|
||||
dev_warn(rs->dev, "Failed to request TX DMA channel\n");
|
||||
ctlr->dma_tx = NULL;
|
||||
}
|
||||
|
||||
ctlr->dma_rx = dma_request_chan(rs->dev, "rx");
|
||||
if (IS_ERR(ctlr->dma_rx)) {
|
||||
if (PTR_ERR(ctlr->dma_rx) == -EPROBE_DEFER) {
|
||||
ret = -EPROBE_DEFER;
|
||||
/* Check rx to see if we need to defer driver probing */
|
||||
ret = dev_warn_probe(rs->dev, PTR_ERR(ctlr->dma_rx),
|
||||
"Failed to request optional RX DMA channel\n");
|
||||
if (ret == -EPROBE_DEFER)
|
||||
goto err_free_dma_tx;
|
||||
}
|
||||
dev_warn(rs->dev, "Failed to request RX DMA channel\n");
|
||||
ctlr->dma_rx = NULL;
|
||||
}
|
||||
|
||||
@ -1036,7 +1031,7 @@ static struct platform_driver rockchip_spi_driver = {
|
||||
.of_match_table = of_match_ptr(rockchip_spi_dt_match),
|
||||
},
|
||||
.probe = rockchip_spi_probe,
|
||||
.remove_new = rockchip_spi_remove,
|
||||
.remove = rockchip_spi_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(rockchip_spi_driver);
|
||||
|
@ -206,7 +206,7 @@ MODULE_DEVICE_TABLE(platform, rpc_if_spi_id_table);
|
||||
|
||||
static struct platform_driver rpcif_spi_driver = {
|
||||
.probe = rpcif_spi_probe,
|
||||
.remove_new = rpcif_spi_remove,
|
||||
.remove = rpcif_spi_remove,
|
||||
.id_table = rpc_if_spi_id_table,
|
||||
.driver = {
|
||||
.name = "rpc-if-spi",
|
||||
|
@ -1427,7 +1427,7 @@ static SIMPLE_DEV_PM_OPS(rspi_pm_ops, rspi_suspend, rspi_resume);
|
||||
|
||||
static struct platform_driver rspi_driver = {
|
||||
.probe = rspi_probe,
|
||||
.remove_new = rspi_remove,
|
||||
.remove = rspi_remove,
|
||||
.id_table = spi_driver_ids,
|
||||
.driver = {
|
||||
.name = "renesas_spi",
|
||||
|
@ -683,7 +683,7 @@ MODULE_DEVICE_TABLE(of, rzv2m_csi_match);
|
||||
|
||||
static struct platform_driver rzv2m_csi_drv = {
|
||||
.probe = rzv2m_csi_probe,
|
||||
.remove_new = rzv2m_csi_remove,
|
||||
.remove = rzv2m_csi_remove,
|
||||
.driver = {
|
||||
.name = "rzv2m_csi",
|
||||
.of_match_table = rzv2m_csi_match,
|
||||
|
@ -1353,7 +1353,7 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
|
||||
/* Setup Deufult Mode */
|
||||
/* Setup Default Mode */
|
||||
s3c64xx_spi_hwinit(sdd);
|
||||
|
||||
spin_lock_init(&sdd->lock);
|
||||
@ -1681,7 +1681,7 @@ static struct platform_driver s3c64xx_spi_driver = {
|
||||
.of_match_table = of_match_ptr(s3c64xx_spi_dt_match),
|
||||
},
|
||||
.probe = s3c64xx_spi_probe,
|
||||
.remove_new = s3c64xx_spi_remove,
|
||||
.remove = s3c64xx_spi_remove,
|
||||
.id_table = s3c64xx_spi_driver_ids,
|
||||
};
|
||||
MODULE_ALIAS("platform:s3c64xx-spi");
|
||||
|
@ -293,7 +293,7 @@ MODULE_DEVICE_TABLE(of, hspi_of_match);
|
||||
|
||||
static struct platform_driver hspi_driver = {
|
||||
.probe = hspi_probe,
|
||||
.remove_new = hspi_remove,
|
||||
.remove = hspi_remove,
|
||||
.driver = {
|
||||
.name = "sh-hspi",
|
||||
.of_match_table = hspi_of_match,
|
||||
|
@ -1429,7 +1429,7 @@ static SIMPLE_DEV_PM_OPS(sh_msiof_spi_pm_ops, sh_msiof_spi_suspend,
|
||||
|
||||
static struct platform_driver sh_msiof_spi_drv = {
|
||||
.probe = sh_msiof_spi_probe,
|
||||
.remove_new = sh_msiof_spi_remove,
|
||||
.remove = sh_msiof_spi_remove,
|
||||
.id_table = spi_driver_ids,
|
||||
.driver = {
|
||||
.name = "spi_sh_msiof",
|
||||
|
@ -183,7 +183,7 @@ static void sh_sci_spi_remove(struct platform_device *dev)
|
||||
|
||||
static struct platform_driver sh_sci_spi_drv = {
|
||||
.probe = sh_sci_spi_probe,
|
||||
.remove_new = sh_sci_spi_remove,
|
||||
.remove = sh_sci_spi_remove,
|
||||
.driver = {
|
||||
.name = "spi_sh_sci",
|
||||
},
|
||||
|
@ -459,7 +459,7 @@ static int spi_sh_probe(struct platform_device *pdev)
|
||||
|
||||
static struct platform_driver spi_sh_driver = {
|
||||
.probe = spi_sh_probe,
|
||||
.remove_new = spi_sh_remove,
|
||||
.remove = spi_sh_remove,
|
||||
.driver = {
|
||||
.name = "sh_spi",
|
||||
},
|
||||
|
@ -471,7 +471,7 @@ MODULE_DEVICE_TABLE(of, sifive_spi_of_match);
|
||||
|
||||
static struct platform_driver sifive_spi_driver = {
|
||||
.probe = sifive_spi_probe,
|
||||
.remove_new = sifive_spi_remove,
|
||||
.remove = sifive_spi_remove,
|
||||
.driver = {
|
||||
.name = SIFIVE_SPI_DRIVER_NAME,
|
||||
.pm = &sifive_spi_pm_ops,
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user