Merge branch 'spi-5.7' into spi-next
This commit is contained in:
commit
1ba0b52ea7
|
@ -0,0 +1,36 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-or-later)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/fsi/ibm,fsi2spi.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: IBM FSI-attached SPI controllers
|
||||
|
||||
maintainers:
|
||||
- Eddie James <eajames@linux.ibm.com>
|
||||
|
||||
description: |
|
||||
This binding describes an FSI CFAM engine called the FSI2SPI. Therefore this
|
||||
node will always be a child of an FSI CFAM node; see fsi.txt for details on
|
||||
FSI slave and CFAM nodes. This FSI2SPI engine provides access to a number of
|
||||
SPI controllers.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- ibm,fsi2spi
|
||||
|
||||
reg:
|
||||
items:
|
||||
- description: FSI slave address
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
examples:
|
||||
- |
|
||||
fsi2spi@1c00 {
|
||||
compatible = "ibm,fsi2spi";
|
||||
reg = <0x1c00 0x400>;
|
||||
};
|
|
@ -61,6 +61,7 @@ Regulator nodes are identified by their compatible:
|
|||
"qcom,rpm-pm8901-regulators"
|
||||
"qcom,rpm-pm8921-regulators"
|
||||
"qcom,rpm-pm8018-regulators"
|
||||
"qcom,rpm-smb208-regulators"
|
||||
|
||||
- vdd_l0_l1_lvs-supply:
|
||||
- vdd_l2_l11_l12-supply:
|
||||
|
@ -171,6 +172,9 @@ pm8018:
|
|||
s1, s2, s3, s4, s5, , l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11,
|
||||
l12, l14, lvs1
|
||||
|
||||
smb208:
|
||||
s1a, s1b, s2a, s2b
|
||||
|
||||
The content of each sub-node is defined by the standard binding for regulators -
|
||||
see regulator.txt - with additional custom properties described below:
|
||||
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/regulator/mps,mp5416.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Monolithic Power System MP5416 PMIC
|
||||
|
||||
maintainers:
|
||||
- Saravanan Sekar <sravanhome@gmail.com>
|
||||
|
||||
properties:
|
||||
$nodename:
|
||||
pattern: "^pmic@[0-9a-f]{1,2}$"
|
||||
compatible:
|
||||
enum:
|
||||
- mps,mp5416
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
regulators:
|
||||
type: object
|
||||
description: |
|
||||
list of regulators provided by this controller, must be named
|
||||
after their hardware counterparts BUCK[1-4] and LDO[1-4]
|
||||
|
||||
patternProperties:
|
||||
"^buck[1-4]$":
|
||||
allOf:
|
||||
- $ref: "regulator.yaml#"
|
||||
type: object
|
||||
|
||||
"^ldo[1-4]$":
|
||||
allOf:
|
||||
- $ref: "regulator.yaml#"
|
||||
type: object
|
||||
|
||||
additionalProperties: false
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- regulators
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
pmic@69 {
|
||||
compatible = "mps,mp5416";
|
||||
reg = <0x69>;
|
||||
|
||||
regulators {
|
||||
|
||||
buck1 {
|
||||
regulator-name = "buck1";
|
||||
regulator-min-microvolt = <600000>;
|
||||
regulator-max-microvolt = <2187500>;
|
||||
regulator-min-microamp = <3800000>;
|
||||
regulator-max-microamp = <6800000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
|
||||
ldo2 {
|
||||
regulator-name = "ldo2";
|
||||
regulator-min-microvolt = <800000>;
|
||||
regulator-max-microvolt = <3975000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
|
@ -22,6 +22,7 @@ properties:
|
|||
enum:
|
||||
- amlogic,meson-gx-spicc # SPICC controller on Amlogic GX and compatible SoCs
|
||||
- amlogic,meson-axg-spicc # SPICC controller on Amlogic AXG and compatible SoCs
|
||||
- amlogic,meson-g12a-spicc # SPICC controller on Amlogic G12A and compatible SoCs
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
@ -40,6 +41,27 @@ properties:
|
|||
items:
|
||||
- const: core
|
||||
|
||||
if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- amlogic,meson-g12a-spicc
|
||||
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
contains:
|
||||
items:
|
||||
- description: controller register bus clock
|
||||
- description: baud rate generator and delay control clock
|
||||
|
||||
clock-names:
|
||||
minItems: 2
|
||||
items:
|
||||
- const: core
|
||||
- const: pclk
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
|
|
@ -10,7 +10,10 @@ Required properties:
|
|||
- "fsl,imx35-cspi" for SPI compatible with the one integrated on i.MX35
|
||||
- "fsl,imx51-ecspi" for SPI compatible with the one integrated on i.MX51
|
||||
- "fsl,imx53-ecspi" for SPI compatible with the one integrated on i.MX53 and later Soc
|
||||
- "fsl,imx8mq-ecspi" for SPI compatible with the one integrated on i.MX8M
|
||||
- "fsl,imx8mq-ecspi" for SPI compatible with the one integrated on i.MX8MQ
|
||||
- "fsl,imx8mm-ecspi" for SPI compatible with the one integrated on i.MX8MM
|
||||
- "fsl,imx8mn-ecspi" for SPI compatible with the one integrated on i.MX8MN
|
||||
- "fsl,imx8mp-ecspi" for SPI compatible with the one integrated on i.MX8MP
|
||||
- reg : Offset and length of the register set for the device
|
||||
- interrupts : Should contain CSPI/eCSPI interrupt
|
||||
- clocks : Clock specifiers for both ipg and per clocks.
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/spi/qca,ar934x-spi.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm Atheros AR934x/QCA95xx SoC SPI controller
|
||||
|
||||
maintainers:
|
||||
- Chuanhong Guo <gch981213@gmail.com>
|
||||
|
||||
allOf:
|
||||
- $ref: spi-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: qca,ar934x-spi
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- '#address-cells'
|
||||
- '#size-cells'
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/ath79-clk.h>
|
||||
spi: spi@1f000000 {
|
||||
compatible = "qca,ar934x-spi";
|
||||
reg = <0x1f000000 0x1c>;
|
||||
clocks = <&pll ATH79_CLK_AHB>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
|
@ -52,6 +52,12 @@ properties:
|
|||
description:
|
||||
The SPI controller acts as a slave, instead of a master.
|
||||
|
||||
oneOf:
|
||||
- required:
|
||||
- "#address-cells"
|
||||
- required:
|
||||
- spi-slave
|
||||
|
||||
patternProperties:
|
||||
"^slave$":
|
||||
type: object
|
||||
|
@ -114,7 +120,7 @@ patternProperties:
|
|||
- enum: [ 1, 2, 4, 8 ]
|
||||
- default: 1
|
||||
description:
|
||||
Bus width to the SPI bus used for MISO.
|
||||
Bus width to the SPI bus used for read transfers.
|
||||
|
||||
spi-rx-delay-us:
|
||||
description:
|
||||
|
@ -126,7 +132,7 @@ patternProperties:
|
|||
- enum: [ 1, 2, 4, 8 ]
|
||||
- default: 1
|
||||
description:
|
||||
Bus width to the SPI bus used for MOSI.
|
||||
Bus width to the SPI bus used for write transfers.
|
||||
|
||||
spi-tx-delay-us:
|
||||
description:
|
||||
|
|
|
@ -1,12 +1,17 @@
|
|||
ARM Freescale DSPI controller
|
||||
|
||||
Required properties:
|
||||
- compatible : "fsl,vf610-dspi", "fsl,ls1021a-v1.0-dspi",
|
||||
"fsl,ls2085a-dspi"
|
||||
or
|
||||
"fsl,ls2080a-dspi" followed by "fsl,ls2085a-dspi"
|
||||
"fsl,ls1012a-dspi" followed by "fsl,ls1021a-v1.0-dspi"
|
||||
"fsl,ls1088a-dspi" followed by "fsl,ls1021a-v1.0-dspi"
|
||||
- compatible : must be one of:
|
||||
"fsl,vf610-dspi",
|
||||
"fsl,ls1021a-v1.0-dspi",
|
||||
"fsl,ls1012a-dspi" (optionally followed by "fsl,ls1021a-v1.0-dspi"),
|
||||
"fsl,ls1028a-dspi",
|
||||
"fsl,ls1043a-dspi" (optionally followed by "fsl,ls1021a-v1.0-dspi"),
|
||||
"fsl,ls1046a-dspi" (optionally followed by "fsl,ls1021a-v1.0-dspi"),
|
||||
"fsl,ls1088a-dspi" (optionally followed by "fsl,ls1021a-v1.0-dspi"),
|
||||
"fsl,ls2080a-dspi" (optionally followed by "fsl,ls2085a-dspi"),
|
||||
"fsl,ls2085a-dspi",
|
||||
"fsl,lx2160a-dspi",
|
||||
- reg : Offset and length of the register set for the device
|
||||
- interrupts : Should contain SPI controller interrupt
|
||||
- clocks: from common clock binding: handle to dspi clock.
|
||||
|
@ -14,11 +19,11 @@ Required properties:
|
|||
- pinctrl-0: pin control group to be used for this controller.
|
||||
- pinctrl-names: must contain a "default" entry.
|
||||
- spi-num-chipselects : the number of the chipselect signals.
|
||||
- bus-num : the slave chip chipselect signal number.
|
||||
|
||||
Optional property:
|
||||
- big-endian: If present the dspi device's registers are implemented
|
||||
in big endian mode.
|
||||
- bus-num : the slave chip chipselect signal number.
|
||||
|
||||
Optional SPI slave node properties:
|
||||
- fsl,spi-cs-sck-delay: a delay in nanoseconds between activating chip
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
* Serial NOR flash controller for MediaTek SoCs
|
||||
* Serial NOR flash controller for MediaTek ARM SoCs
|
||||
|
||||
Required properties:
|
||||
- compatible: For mt8173, compatible should be "mediatek,mt8173-nor",
|
||||
|
@ -13,6 +13,7 @@ Required properties:
|
|||
"mediatek,mt7629-nor", "mediatek,mt8173-nor"
|
||||
"mediatek,mt8173-nor"
|
||||
- reg: physical base address and length of the controller's register
|
||||
- interrupts: Interrupt number used by the controller.
|
||||
- clocks: the phandle of the clocks needed by the nor controller
|
||||
- clock-names: the names of the clocks
|
||||
the clocks should be named "spi" and "sf". "spi" is used for spi bus,
|
||||
|
@ -22,20 +23,16 @@ Required properties:
|
|||
- #address-cells: should be <1>
|
||||
- #size-cells: should be <0>
|
||||
|
||||
The SPI flash must be a child of the nor_flash node and must have a
|
||||
compatible property. Also see jedec,spi-nor.txt.
|
||||
|
||||
Required properties:
|
||||
- compatible: May include a device-specific string consisting of the manufacturer
|
||||
and name of the chip. Must also include "jedec,spi-nor" for any
|
||||
SPI NOR flash that can be identified by the JEDEC READ ID opcode (0x9F).
|
||||
- reg : Chip-Select number
|
||||
There should be only one spi slave device following generic spi bindings.
|
||||
It's not recommended to use this controller for devices other than SPI NOR
|
||||
flash due to limited transfer capability of this controller.
|
||||
|
||||
Example:
|
||||
|
||||
nor_flash: spi@1100d000 {
|
||||
compatible = "mediatek,mt8173-nor";
|
||||
reg = <0 0x1100d000 0 0xe0>;
|
||||
interrupts = <&spi_flash_irq>;
|
||||
clocks = <&pericfg CLK_PERI_SPI>,
|
||||
<&topckgen CLK_TOP_SPINFI_IFR_SEL>;
|
||||
clock-names = "spi", "sf";
|
|
@ -0,0 +1,89 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/spi/spi-mux.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Generic SPI Multiplexer
|
||||
|
||||
description: |
|
||||
This binding describes a SPI bus multiplexer to route the SPI chip select
|
||||
signals. This can be used when you need more devices than the SPI controller
|
||||
has chip selects available. An example setup is shown in ASCII art; the actual
|
||||
setting of the multiplexer to a channel needs to be done by a specific SPI mux
|
||||
driver.
|
||||
|
||||
MOSI /--------------------------------+--------+--------+--------\
|
||||
MISO |/------------------------------+|-------+|-------+|-------\|
|
||||
SCL ||/----------------------------+||------+||------+||------\||
|
||||
||| ||| ||| ||| |||
|
||||
+------------+ ||| ||| ||| |||
|
||||
| SoC ||| | +-+++-+ +-+++-+ +-+++-+ +-+++-+
|
||||
| ||| | | dev | | dev | | dev | | dev |
|
||||
| +--+++-+ | CS-X +------+\ +--+--+ +--+--+ +--+--+ +--+--+
|
||||
| | SPI +-|-------+ Mux |\\ CS-0 | | | |
|
||||
| +------+ | +--+---+\\\-------/ CS-1 | | |
|
||||
| | | \\\----------------/ CS-2 | |
|
||||
| +------+ | | \\-------------------------/ CS-3 |
|
||||
| | ? +-|----------/ \----------------------------------/
|
||||
| +------+ |
|
||||
+------------+
|
||||
|
||||
allOf:
|
||||
- $ref: "/schemas/spi/spi-controller.yaml#"
|
||||
|
||||
maintainers:
|
||||
- Chris Packham <chris.packham@alliedtelesis.co.nz>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: spi-mux
|
||||
|
||||
mux-controls:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- spi-max-frequency
|
||||
- mux-controls
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
mux: mux-controller {
|
||||
compatible = "gpio-mux";
|
||||
#mux-control-cells = <0>;
|
||||
|
||||
mux-gpios = <&gpio0 3 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
spi@0 {
|
||||
compatible = "spi-mux";
|
||||
reg = <0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
spi-max-frequency = <100000000>;
|
||||
|
||||
mux-controls = <&mux>;
|
||||
|
||||
spi-flash@0 {
|
||||
compatible = "jedec,spi-nor";
|
||||
reg = <0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
spi-max-frequency = <40000000>;
|
||||
};
|
||||
|
||||
spi-device@1 {
|
||||
compatible = "lineartechnology,ltc2488";
|
||||
reg = <1>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
spi-max-frequency = <10000000>;
|
||||
};
|
||||
};
|
||||
};
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
Required properties:
|
||||
- compatible : Should be "nxp,lx2160a-fspi"
|
||||
"nxp,imx8qxp-fspi"
|
||||
"nxp,imx8mm-fspi"
|
||||
|
||||
- reg : First contains the register location and length,
|
||||
Second contains the memory mapping address and length
|
||||
- reg-names : Should contain the resource reg names:
|
||||
|
|
|
@ -1,58 +0,0 @@
|
|||
* Rockchip SPI Controller
|
||||
|
||||
The Rockchip SPI controller is used to interface with various devices such as flash
|
||||
and display controllers using the SPI communication interface.
|
||||
|
||||
Required Properties:
|
||||
|
||||
- compatible: should be one of the following.
|
||||
"rockchip,rv1108-spi" for rv1108 SoCs.
|
||||
"rockchip,px30-spi", "rockchip,rk3066-spi" for px30 SoCs.
|
||||
"rockchip,rk3036-spi" for rk3036 SoCS.
|
||||
"rockchip,rk3066-spi" for rk3066 SoCs.
|
||||
"rockchip,rk3188-spi" for rk3188 SoCs.
|
||||
"rockchip,rk3228-spi" for rk3228 SoCS.
|
||||
"rockchip,rk3288-spi" for rk3288 SoCs.
|
||||
"rockchip,rk3368-spi" for rk3368 SoCs.
|
||||
"rockchip,rk3399-spi" for rk3399 SoCs.
|
||||
- reg: physical base address of the controller and length of memory mapped
|
||||
region.
|
||||
- interrupts: The interrupt number to the cpu. The interrupt specifier format
|
||||
depends on the interrupt controller.
|
||||
- clocks: Must contain an entry for each entry in clock-names.
|
||||
- clock-names: Shall be "spiclk" for the transfer-clock, and "apb_pclk" for
|
||||
the peripheral clock.
|
||||
- #address-cells: should be 1.
|
||||
- #size-cells: should be 0.
|
||||
|
||||
Optional Properties:
|
||||
|
||||
- dmas: DMA specifiers for tx and rx dma. See the DMA client binding,
|
||||
Documentation/devicetree/bindings/dma/dma.txt
|
||||
- dma-names: DMA request names should include "tx" and "rx" if present.
|
||||
- rx-sample-delay-ns: nanoseconds to delay after the SCLK edge before sampling
|
||||
Rx data (may need to be fine tuned for high capacitance lines).
|
||||
No delay (0) by default.
|
||||
- pinctrl-names: Names for the pin configuration(s); may be "default" or
|
||||
"sleep", where the "sleep" configuration may describe the state
|
||||
the pins should be in during system suspend. See also
|
||||
pinctrl/pinctrl-bindings.txt.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
spi0: spi@ff110000 {
|
||||
compatible = "rockchip,rk3066-spi";
|
||||
reg = <0xff110000 0x1000>;
|
||||
dmas = <&pdma1 11>, <&pdma1 12>;
|
||||
dma-names = "tx", "rx";
|
||||
rx-sample-delay-ns = <10>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
interrupts = <GIC_SPI 44 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&cru SCLK_SPI0>, <&cru PCLK_SPI0>;
|
||||
clock-names = "spiclk", "apb_pclk";
|
||||
pinctrl-0 = <&spi1_pins>;
|
||||
pinctrl-1 = <&spi1_sleep>;
|
||||
pinctrl-names = "default", "sleep";
|
||||
};
|
|
@ -0,0 +1,107 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/spi/spi-rockchip.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Rockchip SPI Controller
|
||||
|
||||
description:
|
||||
The Rockchip SPI controller is used to interface with various devices such
|
||||
as flash and display controllers using the SPI communication interface.
|
||||
|
||||
allOf:
|
||||
- $ref: "spi-controller.yaml#"
|
||||
|
||||
maintainers:
|
||||
- Heiko Stuebner <heiko@sntech.de>
|
||||
|
||||
# Everything else is described in the common file
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- const: rockchip,rk3036-spi
|
||||
- const: rockchip,rk3066-spi
|
||||
- const: rockchip,rk3228-spi
|
||||
- const: rockchip,rv1108-spi
|
||||
- items:
|
||||
- enum:
|
||||
- rockchip,px30-spi
|
||||
- rockchip,rk3188-spi
|
||||
- rockchip,rk3288-spi
|
||||
- rockchip,rk3308-spi
|
||||
- rockchip,rk3328-spi
|
||||
- rockchip,rk3368-spi
|
||||
- rockchip,rk3399-spi
|
||||
- const: rockchip,rk3066-spi
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: transfer-clock
|
||||
- description: peripheral clock
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: spiclk
|
||||
- const: apb_pclk
|
||||
|
||||
dmas:
|
||||
items:
|
||||
- description: TX DMA Channel
|
||||
- description: RX DMA Channel
|
||||
|
||||
dma-names:
|
||||
items:
|
||||
- const: tx
|
||||
- const: rx
|
||||
|
||||
rx-sample-delay-ns:
|
||||
default: 0
|
||||
description:
|
||||
Nano seconds to delay after the SCLK edge before sampling Rx data
|
||||
(may need to be fine tuned for high capacitance lines).
|
||||
If not specified 0 will be used.
|
||||
|
||||
pinctrl-names:
|
||||
minItems: 1
|
||||
items:
|
||||
- const: default
|
||||
- const: sleep
|
||||
description:
|
||||
Names for the pin configuration(s); may be "default" or "sleep",
|
||||
where the "sleep" configuration may describe the state
|
||||
the pins should be in during system suspend.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/rk3188-cru-common.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
spi0: spi@ff110000 {
|
||||
compatible = "rockchip,rk3066-spi";
|
||||
reg = <0xff110000 0x1000>;
|
||||
interrupts = <GIC_SPI 44 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&cru SCLK_SPI0>, <&cru PCLK_SPI0>;
|
||||
clock-names = "spiclk", "apb_pclk";
|
||||
dmas = <&pdma1 11>, <&pdma1 12>;
|
||||
dma-names = "tx", "rx";
|
||||
pinctrl-0 = <&spi1_pins>;
|
||||
pinctrl-1 = <&spi1_sleep>;
|
||||
pinctrl-names = "default", "sleep";
|
||||
rx-sample-delay-ns = <10>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
11
MAINTAINERS
11
MAINTAINERS
|
@ -2276,6 +2276,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/mmind/linux-rockchip.git
|
|||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/i2c/i2c-rk3x.txt
|
||||
F: Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.yaml
|
||||
F: Documentation/devicetree/bindings/spi/spi-rockchip.yaml
|
||||
F: arch/arm/boot/dts/rk3*
|
||||
F: arch/arm/boot/dts/rv1108*
|
||||
F: arch/arm/mach-rockchip/
|
||||
|
@ -6858,6 +6859,13 @@ S: Maintained
|
|||
F: drivers/i2c/busses/i2c-fsi.c
|
||||
F: Documentation/devicetree/bindings/i2c/i2c-fsi.txt
|
||||
|
||||
FSI-ATTACHED SPI DRIVER
|
||||
M: Eddie James <eajames@linux.ibm.com>
|
||||
L: linux-spi@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/spi/spi-fsi.c
|
||||
F: Documentation/devicetree/bindings/fsi/ibm,fsi2spi.yaml
|
||||
|
||||
FSNOTIFY: FILESYSTEM NOTIFICATION INFRASTRUCTURE
|
||||
M: Jan Kara <jack@suse.cz>
|
||||
R: Amir Goldstein <amir73il@gmail.com>
|
||||
|
@ -11256,7 +11264,8 @@ F: drivers/tty/mxser.*
|
|||
MONOLITHIC POWER SYSTEM PMIC DRIVER
|
||||
M: Saravanan Sekar <sravanhome@gmail.com>
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/regulator/mpq7920.yaml
|
||||
F: Documentation/devicetree/bindings/regulator/mps,mp*.yaml
|
||||
F: drivers/regulator/mp5416.c
|
||||
F: drivers/regulator/mpq7920.c
|
||||
F: drivers/regulator/mpq7920.h
|
||||
|
||||
|
|
|
@ -52,14 +52,6 @@ config SPI_HISI_SFC
|
|||
help
|
||||
This enables support for HiSilicon FMC SPI-NOR flash controller.
|
||||
|
||||
config SPI_MTK_QUADSPI
|
||||
tristate "MediaTek Quad SPI controller"
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
This enables support for the Quad SPI controller in master mode.
|
||||
This controller does not support generic SPI. It only supports
|
||||
SPI NOR.
|
||||
|
||||
config SPI_NXP_SPIFI
|
||||
tristate "NXP SPI Flash Interface (SPIFI)"
|
||||
depends on OF && (ARCH_LPC18XX || COMPILE_TEST)
|
||||
|
|
|
@ -3,7 +3,6 @@ obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o
|
|||
obj-$(CONFIG_SPI_ASPEED_SMC) += aspeed-smc.o
|
||||
obj-$(CONFIG_SPI_CADENCE_QUADSPI) += cadence-quadspi.o
|
||||
obj-$(CONFIG_SPI_HISI_SFC) += hisi-sfc.o
|
||||
obj-$(CONFIG_SPI_MTK_QUADSPI) += mtk-quadspi.o
|
||||
obj-$(CONFIG_SPI_NXP_SPIFI) += nxp-spifi.o
|
||||
obj-$(CONFIG_SPI_INTEL_SPI) += intel-spi.o
|
||||
obj-$(CONFIG_SPI_INTEL_SPI_PCI) += intel-spi-pci.o
|
||||
|
|
|
@ -1,565 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2015 MediaTek Inc.
|
||||
* Author: Bayi Cheng <bayi.cheng@mediatek.com>
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/mtd/spi-nor.h>
|
||||
|
||||
#define MTK_NOR_CMD_REG 0x00
|
||||
#define MTK_NOR_CNT_REG 0x04
|
||||
#define MTK_NOR_RDSR_REG 0x08
|
||||
#define MTK_NOR_RDATA_REG 0x0c
|
||||
#define MTK_NOR_RADR0_REG 0x10
|
||||
#define MTK_NOR_RADR1_REG 0x14
|
||||
#define MTK_NOR_RADR2_REG 0x18
|
||||
#define MTK_NOR_WDATA_REG 0x1c
|
||||
#define MTK_NOR_PRGDATA0_REG 0x20
|
||||
#define MTK_NOR_PRGDATA1_REG 0x24
|
||||
#define MTK_NOR_PRGDATA2_REG 0x28
|
||||
#define MTK_NOR_PRGDATA3_REG 0x2c
|
||||
#define MTK_NOR_PRGDATA4_REG 0x30
|
||||
#define MTK_NOR_PRGDATA5_REG 0x34
|
||||
#define MTK_NOR_SHREG0_REG 0x38
|
||||
#define MTK_NOR_SHREG1_REG 0x3c
|
||||
#define MTK_NOR_SHREG2_REG 0x40
|
||||
#define MTK_NOR_SHREG3_REG 0x44
|
||||
#define MTK_NOR_SHREG4_REG 0x48
|
||||
#define MTK_NOR_SHREG5_REG 0x4c
|
||||
#define MTK_NOR_SHREG6_REG 0x50
|
||||
#define MTK_NOR_SHREG7_REG 0x54
|
||||
#define MTK_NOR_SHREG8_REG 0x58
|
||||
#define MTK_NOR_SHREG9_REG 0x5c
|
||||
#define MTK_NOR_CFG1_REG 0x60
|
||||
#define MTK_NOR_CFG2_REG 0x64
|
||||
#define MTK_NOR_CFG3_REG 0x68
|
||||
#define MTK_NOR_STATUS0_REG 0x70
|
||||
#define MTK_NOR_STATUS1_REG 0x74
|
||||
#define MTK_NOR_STATUS2_REG 0x78
|
||||
#define MTK_NOR_STATUS3_REG 0x7c
|
||||
#define MTK_NOR_FLHCFG_REG 0x84
|
||||
#define MTK_NOR_TIME_REG 0x94
|
||||
#define MTK_NOR_PP_DATA_REG 0x98
|
||||
#define MTK_NOR_PREBUF_STUS_REG 0x9c
|
||||
#define MTK_NOR_DELSEL0_REG 0xa0
|
||||
#define MTK_NOR_DELSEL1_REG 0xa4
|
||||
#define MTK_NOR_INTRSTUS_REG 0xa8
|
||||
#define MTK_NOR_INTREN_REG 0xac
|
||||
#define MTK_NOR_CHKSUM_CTL_REG 0xb8
|
||||
#define MTK_NOR_CHKSUM_REG 0xbc
|
||||
#define MTK_NOR_CMD2_REG 0xc0
|
||||
#define MTK_NOR_WRPROT_REG 0xc4
|
||||
#define MTK_NOR_RADR3_REG 0xc8
|
||||
#define MTK_NOR_DUAL_REG 0xcc
|
||||
#define MTK_NOR_DELSEL2_REG 0xd0
|
||||
#define MTK_NOR_DELSEL3_REG 0xd4
|
||||
#define MTK_NOR_DELSEL4_REG 0xd8
|
||||
|
||||
/* commands for mtk nor controller */
|
||||
#define MTK_NOR_READ_CMD 0x0
|
||||
#define MTK_NOR_RDSR_CMD 0x2
|
||||
#define MTK_NOR_PRG_CMD 0x4
|
||||
#define MTK_NOR_WR_CMD 0x10
|
||||
#define MTK_NOR_PIO_WR_CMD 0x90
|
||||
#define MTK_NOR_WRSR_CMD 0x20
|
||||
#define MTK_NOR_PIO_READ_CMD 0x81
|
||||
#define MTK_NOR_WR_BUF_ENABLE 0x1
|
||||
#define MTK_NOR_WR_BUF_DISABLE 0x0
|
||||
#define MTK_NOR_ENABLE_SF_CMD 0x30
|
||||
#define MTK_NOR_DUAD_ADDR_EN 0x8
|
||||
#define MTK_NOR_QUAD_READ_EN 0x4
|
||||
#define MTK_NOR_DUAL_ADDR_EN 0x2
|
||||
#define MTK_NOR_DUAL_READ_EN 0x1
|
||||
#define MTK_NOR_DUAL_DISABLE 0x0
|
||||
#define MTK_NOR_FAST_READ 0x1
|
||||
|
||||
#define SFLASH_WRBUF_SIZE 128
|
||||
|
||||
/* Can shift up to 48 bits (6 bytes) of TX/RX */
|
||||
#define MTK_NOR_MAX_RX_TX_SHIFT 6
|
||||
/* can shift up to 56 bits (7 bytes) transfer by MTK_NOR_PRG_CMD */
|
||||
#define MTK_NOR_MAX_SHIFT 7
|
||||
/* nor controller 4-byte address mode enable bit */
|
||||
#define MTK_NOR_4B_ADDR_EN BIT(4)
|
||||
|
||||
/* Helpers for accessing the program data / shift data registers */
|
||||
#define MTK_NOR_PRG_REG(n) (MTK_NOR_PRGDATA0_REG + 4 * (n))
|
||||
#define MTK_NOR_SHREG(n) (MTK_NOR_SHREG0_REG + 4 * (n))
|
||||
|
||||
struct mtk_nor {
|
||||
struct spi_nor nor;
|
||||
struct device *dev;
|
||||
void __iomem *base; /* nor flash base address */
|
||||
struct clk *spi_clk;
|
||||
struct clk *nor_clk;
|
||||
};
|
||||
|
||||
static void mtk_nor_set_read_mode(struct mtk_nor *mtk_nor)
|
||||
{
|
||||
struct spi_nor *nor = &mtk_nor->nor;
|
||||
|
||||
switch (nor->read_proto) {
|
||||
case SNOR_PROTO_1_1_1:
|
||||
writeb(nor->read_opcode, mtk_nor->base +
|
||||
MTK_NOR_PRGDATA3_REG);
|
||||
writeb(MTK_NOR_FAST_READ, mtk_nor->base +
|
||||
MTK_NOR_CFG1_REG);
|
||||
break;
|
||||
case SNOR_PROTO_1_1_2:
|
||||
writeb(nor->read_opcode, mtk_nor->base +
|
||||
MTK_NOR_PRGDATA3_REG);
|
||||
writeb(MTK_NOR_DUAL_READ_EN, mtk_nor->base +
|
||||
MTK_NOR_DUAL_REG);
|
||||
break;
|
||||
case SNOR_PROTO_1_1_4:
|
||||
writeb(nor->read_opcode, mtk_nor->base +
|
||||
MTK_NOR_PRGDATA4_REG);
|
||||
writeb(MTK_NOR_QUAD_READ_EN, mtk_nor->base +
|
||||
MTK_NOR_DUAL_REG);
|
||||
break;
|
||||
default:
|
||||
writeb(MTK_NOR_DUAL_DISABLE, mtk_nor->base +
|
||||
MTK_NOR_DUAL_REG);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int mtk_nor_execute_cmd(struct mtk_nor *mtk_nor, u8 cmdval)
|
||||
{
|
||||
int reg;
|
||||
u8 val = cmdval & 0x1f;
|
||||
|
||||
writeb(cmdval, mtk_nor->base + MTK_NOR_CMD_REG);
|
||||
return readl_poll_timeout(mtk_nor->base + MTK_NOR_CMD_REG, reg,
|
||||
!(reg & val), 100, 10000);
|
||||
}
|
||||
|
||||
static int mtk_nor_do_tx_rx(struct mtk_nor *mtk_nor, u8 op,
|
||||
const u8 *tx, size_t txlen, u8 *rx, size_t rxlen)
|
||||
{
|
||||
size_t len = 1 + txlen + rxlen;
|
||||
int i, ret, idx;
|
||||
|
||||
if (len > MTK_NOR_MAX_SHIFT)
|
||||
return -EINVAL;
|
||||
|
||||
writeb(len * 8, mtk_nor->base + MTK_NOR_CNT_REG);
|
||||
|
||||
/* start at PRGDATA5, go down to PRGDATA0 */
|
||||
idx = MTK_NOR_MAX_RX_TX_SHIFT - 1;
|
||||
|
||||
/* opcode */
|
||||
writeb(op, mtk_nor->base + MTK_NOR_PRG_REG(idx));
|
||||
idx--;
|
||||
|
||||
/* program TX data */
|
||||
for (i = 0; i < txlen; i++, idx--)
|
||||
writeb(tx[i], mtk_nor->base + MTK_NOR_PRG_REG(idx));
|
||||
|
||||
/* clear out rest of TX registers */
|
||||
while (idx >= 0) {
|
||||
writeb(0, mtk_nor->base + MTK_NOR_PRG_REG(idx));
|
||||
idx--;
|
||||
}
|
||||
|
||||
ret = mtk_nor_execute_cmd(mtk_nor, MTK_NOR_PRG_CMD);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* restart at first RX byte */
|
||||
idx = rxlen - 1;
|
||||
|
||||
/* read out RX data */
|
||||
for (i = 0; i < rxlen; i++, idx--)
|
||||
rx[i] = readb(mtk_nor->base + MTK_NOR_SHREG(idx));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Do a WRSR (Write Status Register) command */
|
||||
static int mtk_nor_wr_sr(struct mtk_nor *mtk_nor, const u8 sr)
|
||||
{
|
||||
writeb(sr, mtk_nor->base + MTK_NOR_PRGDATA5_REG);
|
||||
writeb(8, mtk_nor->base + MTK_NOR_CNT_REG);
|
||||
return mtk_nor_execute_cmd(mtk_nor, MTK_NOR_WRSR_CMD);
|
||||
}
|
||||
|
||||
static int mtk_nor_write_buffer_enable(struct mtk_nor *mtk_nor)
|
||||
{
|
||||
u8 reg;
|
||||
|
||||
/* the bit0 of MTK_NOR_CFG2_REG is pre-fetch buffer
|
||||
* 0: pre-fetch buffer use for read
|
||||
* 1: pre-fetch buffer use for page program
|
||||
*/
|
||||
writel(MTK_NOR_WR_BUF_ENABLE, mtk_nor->base + MTK_NOR_CFG2_REG);
|
||||
return readb_poll_timeout(mtk_nor->base + MTK_NOR_CFG2_REG, reg,
|
||||
0x01 == (reg & 0x01), 100, 10000);
|
||||
}
|
||||
|
||||
static int mtk_nor_write_buffer_disable(struct mtk_nor *mtk_nor)
|
||||
{
|
||||
u8 reg;
|
||||
|
||||
writel(MTK_NOR_WR_BUF_DISABLE, mtk_nor->base + MTK_NOR_CFG2_REG);
|
||||
return readb_poll_timeout(mtk_nor->base + MTK_NOR_CFG2_REG, reg,
|
||||
MTK_NOR_WR_BUF_DISABLE == (reg & 0x1), 100,
|
||||
10000);
|
||||
}
|
||||
|
||||
static void mtk_nor_set_addr_width(struct mtk_nor *mtk_nor)
|
||||
{
|
||||
u8 val;
|
||||
struct spi_nor *nor = &mtk_nor->nor;
|
||||
|
||||
val = readb(mtk_nor->base + MTK_NOR_DUAL_REG);
|
||||
|
||||
switch (nor->addr_width) {
|
||||
case 3:
|
||||
val &= ~MTK_NOR_4B_ADDR_EN;
|
||||
break;
|
||||
case 4:
|
||||
val |= MTK_NOR_4B_ADDR_EN;
|
||||
break;
|
||||
default:
|
||||
dev_warn(mtk_nor->dev, "Unexpected address width %u.\n",
|
||||
nor->addr_width);
|
||||
break;
|
||||
}
|
||||
|
||||
writeb(val, mtk_nor->base + MTK_NOR_DUAL_REG);
|
||||
}
|
||||
|
||||
static void mtk_nor_set_addr(struct mtk_nor *mtk_nor, u32 addr)
|
||||
{
|
||||
int i;
|
||||
|
||||
mtk_nor_set_addr_width(mtk_nor);
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
writeb(addr & 0xff, mtk_nor->base + MTK_NOR_RADR0_REG + i * 4);
|
||||
addr >>= 8;
|
||||
}
|
||||
/* Last register is non-contiguous */
|
||||
writeb(addr & 0xff, mtk_nor->base + MTK_NOR_RADR3_REG);
|
||||
}
|
||||
|
||||
static ssize_t mtk_nor_read(struct spi_nor *nor, loff_t from, size_t length,
|
||||
u_char *buffer)
|
||||
{
|
||||
int i, ret;
|
||||
int addr = (int)from;
|
||||
u8 *buf = (u8 *)buffer;
|
||||
struct mtk_nor *mtk_nor = nor->priv;
|
||||
|
||||
/* set mode for fast read mode ,dual mode or quad mode */
|
||||
mtk_nor_set_read_mode(mtk_nor);
|
||||
mtk_nor_set_addr(mtk_nor, addr);
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
ret = mtk_nor_execute_cmd(mtk_nor, MTK_NOR_PIO_READ_CMD);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
buf[i] = readb(mtk_nor->base + MTK_NOR_RDATA_REG);
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
static int mtk_nor_write_single_byte(struct mtk_nor *mtk_nor,
|
||||
int addr, int length, u8 *data)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
mtk_nor_set_addr(mtk_nor, addr);
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
writeb(*data++, mtk_nor->base + MTK_NOR_WDATA_REG);
|
||||
ret = mtk_nor_execute_cmd(mtk_nor, MTK_NOR_PIO_WR_CMD);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_nor_write_buffer(struct mtk_nor *mtk_nor, int addr,
|
||||
const u8 *buf)
|
||||
{
|
||||
int i, bufidx, data;
|
||||
|
||||
mtk_nor_set_addr(mtk_nor, addr);
|
||||
|
||||
bufidx = 0;
|
||||
for (i = 0; i < SFLASH_WRBUF_SIZE; i += 4) {
|
||||
data = buf[bufidx + 3]<<24 | buf[bufidx + 2]<<16 |
|
||||
buf[bufidx + 1]<<8 | buf[bufidx];
|
||||
bufidx += 4;
|
||||
writel(data, mtk_nor->base + MTK_NOR_PP_DATA_REG);
|
||||
}
|
||||
return mtk_nor_execute_cmd(mtk_nor, MTK_NOR_WR_CMD);
|
||||
}
|
||||
|
||||
static ssize_t mtk_nor_write(struct spi_nor *nor, loff_t to, size_t len,
|
||||
const u_char *buf)
|
||||
{
|
||||
int ret;
|
||||
struct mtk_nor *mtk_nor = nor->priv;
|
||||
size_t i;
|
||||
|
||||
ret = mtk_nor_write_buffer_enable(mtk_nor);
|
||||
if (ret < 0) {
|
||||
dev_warn(mtk_nor->dev, "write buffer enable failed!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i + SFLASH_WRBUF_SIZE <= len; i += SFLASH_WRBUF_SIZE) {
|
||||
ret = mtk_nor_write_buffer(mtk_nor, to, buf);
|
||||
if (ret < 0) {
|
||||
dev_err(mtk_nor->dev, "write buffer failed!\n");
|
||||
return ret;
|
||||
}
|
||||
to += SFLASH_WRBUF_SIZE;
|
||||
buf += SFLASH_WRBUF_SIZE;
|
||||
}
|
||||
ret = mtk_nor_write_buffer_disable(mtk_nor);
|
||||
if (ret < 0) {
|
||||
dev_warn(mtk_nor->dev, "write buffer disable failed!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (i < len) {
|
||||
ret = mtk_nor_write_single_byte(mtk_nor, to,
|
||||
(int)(len - i), (u8 *)buf);
|
||||
if (ret < 0) {
|
||||
dev_err(mtk_nor->dev, "write single byte failed!\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int mtk_nor_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, size_t len)
|
||||
{
|
||||
int ret;
|
||||
struct mtk_nor *mtk_nor = nor->priv;
|
||||
|
||||
switch (opcode) {
|
||||
case SPINOR_OP_RDSR:
|
||||
ret = mtk_nor_execute_cmd(mtk_nor, MTK_NOR_RDSR_CMD);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (len == 1)
|
||||
*buf = readb(mtk_nor->base + MTK_NOR_RDSR_REG);
|
||||
else
|
||||
dev_err(mtk_nor->dev, "len should be 1 for read status!\n");
|
||||
break;
|
||||
default:
|
||||
ret = mtk_nor_do_tx_rx(mtk_nor, opcode, NULL, 0, buf, len);
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mtk_nor_write_reg(struct spi_nor *nor, u8 opcode, const u8 *buf,
|
||||
size_t len)
|
||||
{
|
||||
int ret;
|
||||
struct mtk_nor *mtk_nor = nor->priv;
|
||||
|
||||
switch (opcode) {
|
||||
case SPINOR_OP_WRSR:
|
||||
/* We only handle 1 byte */
|
||||
ret = mtk_nor_wr_sr(mtk_nor, *buf);
|
||||
break;
|
||||
default:
|
||||
ret = mtk_nor_do_tx_rx(mtk_nor, opcode, buf, len, NULL, 0);
|
||||
if (ret)
|
||||
dev_warn(mtk_nor->dev, "write reg failure!\n");
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void mtk_nor_disable_clk(struct mtk_nor *mtk_nor)
|
||||
{
|
||||
clk_disable_unprepare(mtk_nor->spi_clk);
|
||||
clk_disable_unprepare(mtk_nor->nor_clk);
|
||||
}
|
||||
|
||||
static int mtk_nor_enable_clk(struct mtk_nor *mtk_nor)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(mtk_nor->spi_clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(mtk_nor->nor_clk);
|
||||
if (ret) {
|
||||
clk_disable_unprepare(mtk_nor->spi_clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_nor_controller_ops mtk_controller_ops = {
|
||||
.read_reg = mtk_nor_read_reg,
|
||||
.write_reg = mtk_nor_write_reg,
|
||||
.read = mtk_nor_read,
|
||||
.write = mtk_nor_write,
|
||||
};
|
||||
|
||||
static int mtk_nor_init(struct mtk_nor *mtk_nor,
|
||||
struct device_node *flash_node)
|
||||
{
|
||||
const struct spi_nor_hwcaps hwcaps = {
|
||||
.mask = SNOR_HWCAPS_READ |
|
||||
SNOR_HWCAPS_READ_FAST |
|
||||
SNOR_HWCAPS_READ_1_1_2 |
|
||||
SNOR_HWCAPS_PP,
|
||||
};
|
||||
int ret;
|
||||
struct spi_nor *nor;
|
||||
|
||||
/* initialize controller to accept commands */
|
||||
writel(MTK_NOR_ENABLE_SF_CMD, mtk_nor->base + MTK_NOR_WRPROT_REG);
|
||||
|
||||
nor = &mtk_nor->nor;
|
||||
nor->dev = mtk_nor->dev;
|
||||
nor->priv = mtk_nor;
|
||||
spi_nor_set_flash_node(nor, flash_node);
|
||||
nor->controller_ops = &mtk_controller_ops;
|
||||
|
||||
nor->mtd.name = "mtk_nor";
|
||||
/* initialized with NULL */
|
||||
ret = spi_nor_scan(nor, NULL, &hwcaps);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return mtd_device_register(&nor->mtd, NULL, 0);
|
||||
}
|
||||
|
||||
static int mtk_nor_drv_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *flash_np;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
struct mtk_nor *mtk_nor;
|
||||
|
||||
if (!pdev->dev.of_node) {
|
||||
dev_err(&pdev->dev, "No DT found\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mtk_nor = devm_kzalloc(&pdev->dev, sizeof(*mtk_nor), GFP_KERNEL);
|
||||
if (!mtk_nor)
|
||||
return -ENOMEM;
|
||||
platform_set_drvdata(pdev, mtk_nor);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
mtk_nor->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(mtk_nor->base))
|
||||
return PTR_ERR(mtk_nor->base);
|
||||
|
||||
mtk_nor->spi_clk = devm_clk_get(&pdev->dev, "spi");
|
||||
if (IS_ERR(mtk_nor->spi_clk))
|
||||
return PTR_ERR(mtk_nor->spi_clk);
|
||||
|
||||
mtk_nor->nor_clk = devm_clk_get(&pdev->dev, "sf");
|
||||
if (IS_ERR(mtk_nor->nor_clk))
|
||||
return PTR_ERR(mtk_nor->nor_clk);
|
||||
|
||||
mtk_nor->dev = &pdev->dev;
|
||||
|
||||
ret = mtk_nor_enable_clk(mtk_nor);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* only support one attached flash */
|
||||
flash_np = of_get_next_available_child(pdev->dev.of_node, NULL);
|
||||
if (!flash_np) {
|
||||
dev_err(&pdev->dev, "no SPI flash device to configure\n");
|
||||
ret = -ENODEV;
|
||||
goto nor_free;
|
||||
}
|
||||
ret = mtk_nor_init(mtk_nor, flash_np);
|
||||
|
||||
nor_free:
|
||||
if (ret)
|
||||
mtk_nor_disable_clk(mtk_nor);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mtk_nor_drv_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mtk_nor *mtk_nor = platform_get_drvdata(pdev);
|
||||
|
||||
mtk_nor_disable_clk(mtk_nor);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int mtk_nor_suspend(struct device *dev)
|
||||
{
|
||||
struct mtk_nor *mtk_nor = dev_get_drvdata(dev);
|
||||
|
||||
mtk_nor_disable_clk(mtk_nor);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_nor_resume(struct device *dev)
|
||||
{
|
||||
struct mtk_nor *mtk_nor = dev_get_drvdata(dev);
|
||||
|
||||
return mtk_nor_enable_clk(mtk_nor);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops mtk_nor_dev_pm_ops = {
|
||||
.suspend = mtk_nor_suspend,
|
||||
.resume = mtk_nor_resume,
|
||||
};
|
||||
|
||||
#define MTK_NOR_DEV_PM_OPS (&mtk_nor_dev_pm_ops)
|
||||
#else
|
||||
#define MTK_NOR_DEV_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static const struct of_device_id mtk_nor_of_ids[] = {
|
||||
{ .compatible = "mediatek,mt8173-nor"},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mtk_nor_of_ids);
|
||||
|
||||
static struct platform_driver mtk_nor_driver = {
|
||||
.probe = mtk_nor_drv_probe,
|
||||
.remove = mtk_nor_drv_remove,
|
||||
.driver = {
|
||||
.name = "mtk-nor",
|
||||
.pm = MTK_NOR_DEV_PM_OPS,
|
||||
.of_match_table = mtk_nor_of_ids,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(mtk_nor_driver);
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("MediaTek SPI NOR Flash Driver");
|
|
@ -107,6 +107,7 @@ config REGULATOR_AD5398
|
|||
|
||||
config REGULATOR_ANATOP
|
||||
tristate "Freescale i.MX on-chip ANATOP LDO regulators"
|
||||
depends on ARCH_MXC || COMPILE_TEST
|
||||
depends on MFD_SYSCON
|
||||
help
|
||||
Say y here to support Freescale i.MX on-chip ANATOP LDOs
|
||||
|
@ -613,6 +614,16 @@ config REGULATOR_MCP16502
|
|||
through the regulator interface. In addition it enables
|
||||
suspend-to-ram/standby transition.
|
||||
|
||||
config REGULATOR_MP5416
|
||||
tristate "Monolithic MP5416 PMIC"
|
||||
depends on I2C && OF
|
||||
select REGMAP_I2C
|
||||
help
|
||||
Say y here to support the MP5416 PMIC. This will enable supports
|
||||
the software controllable 4 buck and 4 LDO regulators.
|
||||
Say M here if you want to include support for the regulator as a
|
||||
module.
|
||||
|
||||
config REGULATOR_MP8859
|
||||
tristate "MPS MP8859 regulator driver"
|
||||
depends on I2C
|
||||
|
|
|
@ -78,6 +78,7 @@ obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o
|
|||
obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o
|
||||
obj-$(CONFIG_REGULATOR_MCP16502) += mcp16502.o
|
||||
obj-$(CONFIG_REGULATOR_MP5416) += mp5416.o
|
||||
obj-$(CONFIG_REGULATOR_MP8859) += mp8859.o
|
||||
obj-$(CONFIG_REGULATOR_MPQ7920) += mpq7920.o
|
||||
obj-$(CONFIG_REGULATOR_MT6311) += mt6311-regulator.o
|
||||
|
|
|
@ -305,9 +305,13 @@ static int anatop_regulator_probe(struct platform_device *pdev)
|
|||
/* register regulator */
|
||||
rdev = devm_regulator_register(dev, rdesc, &config);
|
||||
if (IS_ERR(rdev)) {
|
||||
dev_err(dev, "failed to register %s\n",
|
||||
rdesc->name);
|
||||
return PTR_ERR(rdev);
|
||||
ret = PTR_ERR(rdev);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
dev_dbg(dev, "failed to register %s, deferring...\n",
|
||||
rdesc->name);
|
||||
else
|
||||
dev_err(dev, "failed to register %s\n", rdesc->name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, rdev);
|
||||
|
|
|
@ -381,8 +381,7 @@ static int axp20x_set_ramp_delay(struct regulator_dev *rdev, int ramp)
|
|||
mask = AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_RATE_MASK |
|
||||
AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_EN_MASK;
|
||||
enable = (ramp > 0) ?
|
||||
AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_EN :
|
||||
!AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_EN;
|
||||
AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_EN : 0;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -393,8 +392,7 @@ static int axp20x_set_ramp_delay(struct regulator_dev *rdev, int ramp)
|
|||
mask = AXP20X_DCDC2_LDO3_V_RAMP_LDO3_RATE_MASK |
|
||||
AXP20X_DCDC2_LDO3_V_RAMP_LDO3_EN_MASK;
|
||||
enable = (ramp > 0) ?
|
||||
AXP20X_DCDC2_LDO3_V_RAMP_LDO3_EN :
|
||||
!AXP20X_DCDC2_LDO3_V_RAMP_LDO3_EN;
|
||||
AXP20X_DCDC2_LDO3_V_RAMP_LDO3_EN : 0;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -73,7 +73,7 @@ struct da9062_regulators {
|
|||
int irq_ldo_lim;
|
||||
unsigned n_regulators;
|
||||
/* Array size to be defined during init. Keep at end. */
|
||||
struct da9062_regulator regulator[0];
|
||||
struct da9062_regulator regulator[];
|
||||
};
|
||||
|
||||
/* Regulator operations */
|
||||
|
|
|
@ -66,7 +66,7 @@ struct da9063_regulator_data {
|
|||
};
|
||||
|
||||
struct da9063_regulators_pdata {
|
||||
unsigned n_regulators;
|
||||
unsigned int n_regulators;
|
||||
struct da9063_regulator_data *regulator_data;
|
||||
};
|
||||
|
||||
|
@ -131,7 +131,7 @@ struct da9063_regulator_info {
|
|||
/* Defines asignment of regulators info table to chip model */
|
||||
struct da9063_dev_model {
|
||||
const struct da9063_regulator_info *regulator_info;
|
||||
unsigned n_regulators;
|
||||
unsigned int n_regulators;
|
||||
enum da9063_type type;
|
||||
};
|
||||
|
||||
|
@ -150,9 +150,9 @@ struct da9063_regulator {
|
|||
|
||||
/* Encapsulates all information for the regulators driver */
|
||||
struct da9063_regulators {
|
||||
unsigned n_regulators;
|
||||
unsigned int n_regulators;
|
||||
/* Array size to be defined during init. Keep at end. */
|
||||
struct da9063_regulator regulator[0];
|
||||
struct da9063_regulator regulator[];
|
||||
};
|
||||
|
||||
/* BUCK modes for DA9063 */
|
||||
|
@ -165,38 +165,46 @@ enum {
|
|||
|
||||
/* Regulator operations */
|
||||
|
||||
/* Current limits array (in uA) for BCORE1, BCORE2, BPRO.
|
||||
Entry indexes corresponds to register values. */
|
||||
/*
|
||||
* Current limits array (in uA) for BCORE1, BCORE2, BPRO.
|
||||
* Entry indexes corresponds to register values.
|
||||
*/
|
||||
static const unsigned int da9063_buck_a_limits[] = {
|
||||
500000, 600000, 700000, 800000, 900000, 1000000, 1100000, 1200000,
|
||||
1300000, 1400000, 1500000, 1600000, 1700000, 1800000, 1900000, 2000000
|
||||
};
|
||||
|
||||
/* Current limits array (in uA) for BMEM, BIO, BPERI.
|
||||
Entry indexes corresponds to register values. */
|
||||
/*
|
||||
* Current limits array (in uA) for BMEM, BIO, BPERI.
|
||||
* Entry indexes corresponds to register values.
|
||||
*/
|
||||
static const unsigned int da9063_buck_b_limits[] = {
|
||||
1500000, 1600000, 1700000, 1800000, 1900000, 2000000, 2100000, 2200000,
|
||||
2300000, 2400000, 2500000, 2600000, 2700000, 2800000, 2900000, 3000000
|
||||
};
|
||||
|
||||
/* Current limits array (in uA) for merged BCORE1 and BCORE2.
|
||||
Entry indexes corresponds to register values. */
|
||||
/*
|
||||
* Current limits array (in uA) for merged BCORE1 and BCORE2.
|
||||
* Entry indexes corresponds to register values.
|
||||
*/
|
||||
static const unsigned int da9063_bcores_merged_limits[] = {
|
||||
1000000, 1200000, 1400000, 1600000, 1800000, 2000000, 2200000, 2400000,
|
||||
2600000, 2800000, 3000000, 3200000, 3400000, 3600000, 3800000, 4000000
|
||||
};
|
||||
|
||||
/* Current limits array (in uA) for merged BMEM and BIO.
|
||||
Entry indexes corresponds to register values. */
|
||||
/*
|
||||
* Current limits array (in uA) for merged BMEM and BIO.
|
||||
* Entry indexes corresponds to register values.
|
||||
*/
|
||||
static const unsigned int da9063_bmem_bio_merged_limits[] = {
|
||||
3000000, 3200000, 3400000, 3600000, 3800000, 4000000, 4200000, 4400000,
|
||||
4600000, 4800000, 5000000, 5200000, 5400000, 5600000, 5800000, 6000000
|
||||
};
|
||||
|
||||
static int da9063_buck_set_mode(struct regulator_dev *rdev, unsigned mode)
|
||||
static int da9063_buck_set_mode(struct regulator_dev *rdev, unsigned int mode)
|
||||
{
|
||||
struct da9063_regulator *regl = rdev_get_drvdata(rdev);
|
||||
unsigned val;
|
||||
unsigned int val;
|
||||
|
||||
switch (mode) {
|
||||
case REGULATOR_MODE_FAST:
|
||||
|
@ -221,7 +229,7 @@ static int da9063_buck_set_mode(struct regulator_dev *rdev, unsigned mode)
|
|||
* There are 3 modes to map to: FAST, NORMAL, and STANDBY.
|
||||
*/
|
||||
|
||||
static unsigned da9063_buck_get_mode(struct regulator_dev *rdev)
|
||||
static unsigned int da9063_buck_get_mode(struct regulator_dev *rdev)
|
||||
{
|
||||
struct da9063_regulator *regl = rdev_get_drvdata(rdev);
|
||||
struct regmap_field *field;
|
||||
|
@ -271,10 +279,10 @@ static unsigned da9063_buck_get_mode(struct regulator_dev *rdev)
|
|||
* There are 2 modes to map to: NORMAL and STANDBY (sleep) for each state.
|
||||
*/
|
||||
|
||||
static int da9063_ldo_set_mode(struct regulator_dev *rdev, unsigned mode)
|
||||
static int da9063_ldo_set_mode(struct regulator_dev *rdev, unsigned int mode)
|
||||
{
|
||||
struct da9063_regulator *regl = rdev_get_drvdata(rdev);
|
||||
unsigned val;
|
||||
unsigned int val;
|
||||
|
||||
switch (mode) {
|
||||
case REGULATOR_MODE_NORMAL:
|
||||
|
@ -290,7 +298,7 @@ static int da9063_ldo_set_mode(struct regulator_dev *rdev, unsigned mode)
|
|||
return regmap_field_write(regl->sleep, val);
|
||||
}
|
||||
|
||||
static unsigned da9063_ldo_get_mode(struct regulator_dev *rdev)
|
||||
static unsigned int da9063_ldo_get_mode(struct regulator_dev *rdev)
|
||||
{
|
||||
struct da9063_regulator *regl = rdev_get_drvdata(rdev);
|
||||
struct regmap_field *field;
|
||||
|
@ -383,7 +391,8 @@ static int da9063_suspend_disable(struct regulator_dev *rdev)
|
|||
return regmap_field_write(regl->suspend, 0);
|
||||
}
|
||||
|
||||
static int da9063_buck_set_suspend_mode(struct regulator_dev *rdev, unsigned mode)
|
||||
static int da9063_buck_set_suspend_mode(struct regulator_dev *rdev,
|
||||
unsigned int mode)
|
||||
{
|
||||
struct da9063_regulator *regl = rdev_get_drvdata(rdev);
|
||||
int val;
|
||||
|
@ -405,10 +414,11 @@ static int da9063_buck_set_suspend_mode(struct regulator_dev *rdev, unsigned mod
|
|||
return regmap_field_write(regl->mode, val);
|
||||
}
|
||||
|
||||
static int da9063_ldo_set_suspend_mode(struct regulator_dev *rdev, unsigned mode)
|
||||
static int da9063_ldo_set_suspend_mode(struct regulator_dev *rdev,
|
||||
unsigned int mode)
|
||||
{
|
||||
struct da9063_regulator *regl = rdev_get_drvdata(rdev);
|
||||
unsigned val;
|
||||
unsigned int val;
|
||||
|
||||
switch (mode) {
|
||||
case REGULATOR_MODE_NORMAL:
|
||||
|
@ -593,7 +603,7 @@ static irqreturn_t da9063_ldo_lim_event(int irq, void *data)
|
|||
struct da9063_regulators *regulators = data;
|
||||
struct da9063 *hw = regulators->regulator[0].hw;
|
||||
struct da9063_regulator *regl;
|
||||
int bits, i , ret;
|
||||
int bits, i, ret;
|
||||
|
||||
ret = regmap_read(hw->regmap, DA9063_REG_STATUS_D, &bits);
|
||||
if (ret < 0)
|
||||
|
@ -605,10 +615,10 @@ static irqreturn_t da9063_ldo_lim_event(int irq, void *data)
|
|||
continue;
|
||||
|
||||
if (BIT(regl->info->oc_event.lsb) & bits) {
|
||||
regulator_lock(regl->rdev);
|
||||
regulator_lock(regl->rdev);
|
||||
regulator_notifier_call_chain(regl->rdev,
|
||||
REGULATOR_EVENT_OVER_CURRENT, NULL);
|
||||
regulator_unlock(regl->rdev);
|
||||
regulator_unlock(regl->rdev);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -833,7 +843,7 @@ static int da9063_regulator_probe(struct platform_device *pdev)
|
|||
|
||||
if (regl->info->suspend_sleep.reg) {
|
||||
regl->suspend_sleep = devm_regmap_field_alloc(&pdev->dev,
|
||||
da9063->regmap, regl->info->suspend_sleep);
|
||||
da9063->regmap, regl->info->suspend_sleep);
|
||||
if (IS_ERR(regl->suspend_sleep))
|
||||
return PTR_ERR(regl->suspend_sleep);
|
||||
}
|
||||
|
@ -867,12 +877,10 @@ static int da9063_regulator_probe(struct platform_device *pdev)
|
|||
NULL, da9063_ldo_lim_event,
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||
"LDO_LIM", regulators);
|
||||
if (ret) {
|
||||
if (ret)
|
||||
dev_err(&pdev->dev, "Failed to request LDO_LIM IRQ.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct platform_driver da9063_regulator_driver = {
|
||||
|
|
|
@ -0,0 +1,245 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
//
|
||||
// mp5416.c - regulator driver for mps mp5416
|
||||
//
|
||||
// Copyright 2020 Monolithic Power Systems, Inc
|
||||
//
|
||||
// Author: Saravanan Sekar <sravanhome@gmail.com>
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
#include <linux/i2c.h>
|
||||
|
||||
#define MP5416_REG_CTL0 0x00
|
||||
#define MP5416_REG_CTL1 0x01
|
||||
#define MP5416_REG_CTL2 0x02
|
||||
#define MP5416_REG_ILIM 0x03
|
||||
#define MP5416_REG_BUCK1 0x04
|
||||
#define MP5416_REG_BUCK2 0x05
|
||||
#define MP5416_REG_BUCK3 0x06
|
||||
#define MP5416_REG_BUCK4 0x07
|
||||
#define MP5416_REG_LDO1 0x08
|
||||
#define MP5416_REG_LDO2 0x09
|
||||
#define MP5416_REG_LDO3 0x0a
|
||||
#define MP5416_REG_LDO4 0x0b
|
||||
|
||||
#define MP5416_REGULATOR_EN BIT(7)
|
||||
#define MP5416_MASK_VSET 0x7f
|
||||
#define MP5416_MASK_BUCK1_ILIM 0xc0
|
||||
#define MP5416_MASK_BUCK2_ILIM 0x0c
|
||||
#define MP5416_MASK_BUCK3_ILIM 0x30
|
||||
#define MP5416_MASK_BUCK4_ILIM 0x03
|
||||
#define MP5416_MASK_DVS_SLEWRATE 0xc0
|
||||
|
||||
/* values in uV */
|
||||
#define MP5416_VOLT1_MIN 600000
|
||||
#define MP5416_VOLT1_MAX 2187500
|
||||
#define MP5416_VOLT1_STEP 12500
|
||||
#define MP5416_VOLT2_MIN 800000
|
||||
#define MP5416_VOLT2_MAX 3975000
|
||||
#define MP5416_VOLT2_STEP 25000
|
||||
|
||||
#define MP5416_VOLT1_RANGE \
|
||||
((MP5416_VOLT1_MAX - MP5416_VOLT1_MIN)/MP5416_VOLT1_STEP + 1)
|
||||
#define MP5416_VOLT2_RANGE \
|
||||
((MP5416_VOLT2_MAX - MP5416_VOLT2_MIN)/MP5416_VOLT2_STEP + 1)
|
||||
|
||||
#define MP5416BUCK(_name, _id, _ilim, _dreg, _dval, _vsel) \
|
||||
[MP5416_BUCK ## _id] = { \
|
||||
.id = MP5416_BUCK ## _id, \
|
||||
.name = _name, \
|
||||
.of_match = _name, \
|
||||
.regulators_node = "regulators", \
|
||||
.ops = &mp5416_buck_ops, \
|
||||
.min_uV = MP5416_VOLT ##_vsel## _MIN, \
|
||||
.uV_step = MP5416_VOLT ##_vsel## _STEP, \
|
||||
.n_voltages = MP5416_VOLT ##_vsel## _RANGE, \
|
||||
.curr_table = _ilim, \
|
||||
.n_current_limits = ARRAY_SIZE(_ilim), \
|
||||
.csel_reg = MP5416_REG_ILIM, \
|
||||
.csel_mask = MP5416_MASK_BUCK ## _id ##_ILIM, \
|
||||
.vsel_reg = MP5416_REG_BUCK ## _id, \
|
||||
.vsel_mask = MP5416_MASK_VSET, \
|
||||
.enable_reg = MP5416_REG_BUCK ## _id, \
|
||||
.enable_mask = MP5416_REGULATOR_EN, \
|
||||
.active_discharge_on = _dval, \
|
||||
.active_discharge_reg = _dreg, \
|
||||
.active_discharge_mask = _dval, \
|
||||
.owner = THIS_MODULE, \
|
||||
}
|
||||
|
||||
#define MP5416LDO(_name, _id, _dval) \
|
||||
[MP5416_LDO ## _id] = { \
|
||||
.id = MP5416_LDO ## _id, \
|
||||
.name = _name, \
|
||||
.of_match = _name, \
|
||||
.regulators_node = "regulators", \
|
||||
.ops = &mp5416_ldo_ops, \
|
||||
.min_uV = MP5416_VOLT2_MIN, \
|
||||
.uV_step = MP5416_VOLT2_STEP, \
|
||||
.n_voltages = MP5416_VOLT2_RANGE, \
|
||||
.vsel_reg = MP5416_REG_LDO ##_id, \
|
||||
.vsel_mask = MP5416_MASK_VSET, \
|
||||
.enable_reg = MP5416_REG_LDO ##_id, \
|
||||
.enable_mask = MP5416_REGULATOR_EN, \
|
||||
.active_discharge_on = _dval, \
|
||||
.active_discharge_reg = MP5416_REG_CTL2, \
|
||||
.active_discharge_mask = _dval, \
|
||||
.owner = THIS_MODULE, \
|
||||
}
|
||||
|
||||
enum mp5416_regulators {
|
||||
MP5416_BUCK1,
|
||||
MP5416_BUCK2,
|
||||
MP5416_BUCK3,
|
||||
MP5416_BUCK4,
|
||||
MP5416_LDO1,
|
||||
MP5416_LDO2,
|
||||
MP5416_LDO3,
|
||||
MP5416_LDO4,
|
||||
MP5416_MAX_REGULATORS,
|
||||
};
|
||||
|
||||
static const struct regmap_config mp5416_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = 0x0d,
|
||||
};
|
||||
|
||||
/* Current limits array (in uA)
|
||||
* ILIM1 & ILIM3
|
||||
*/
|
||||
static const unsigned int mp5416_I_limits1[] = {
|
||||
3800000, 4600000, 5600000, 6800000
|
||||
};
|
||||
|
||||
/* ILIM2 & ILIM4 */
|
||||
static const unsigned int mp5416_I_limits2[] = {
|
||||
2200000, 3200000, 4200000, 5200000
|
||||
};
|
||||
|
||||
static int mp5416_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay);
|
||||
|
||||
static const struct regulator_ops mp5416_ldo_ops = {
|
||||
.enable = regulator_enable_regmap,
|
||||
.disable = regulator_disable_regmap,
|
||||
.is_enabled = regulator_is_enabled_regmap,
|
||||
.list_voltage = regulator_list_voltage_linear,
|
||||
.map_voltage = regulator_map_voltage_linear,
|
||||
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
||||
.set_voltage_sel = regulator_set_voltage_sel_regmap,
|
||||
.set_active_discharge = regulator_set_active_discharge_regmap,
|
||||
};
|
||||
|
||||
static const struct regulator_ops mp5416_buck_ops = {
|
||||
.enable = regulator_enable_regmap,
|
||||
.disable = regulator_disable_regmap,
|
||||
.is_enabled = regulator_is_enabled_regmap,
|
||||
.list_voltage = regulator_list_voltage_linear,
|
||||
.map_voltage = regulator_map_voltage_linear,
|
||||
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
||||
.set_voltage_sel = regulator_set_voltage_sel_regmap,
|
||||
.set_active_discharge = regulator_set_active_discharge_regmap,
|
||||
.get_current_limit = regulator_get_current_limit_regmap,
|
||||
.set_current_limit = regulator_set_current_limit_regmap,
|
||||
.set_ramp_delay = mp5416_set_ramp_delay,
|
||||
};
|
||||
|
||||
static struct regulator_desc mp5416_regulators_desc[MP5416_MAX_REGULATORS] = {
|
||||
MP5416BUCK("buck1", 1, mp5416_I_limits1, MP5416_REG_CTL1, BIT(0), 1),
|
||||
MP5416BUCK("buck2", 2, mp5416_I_limits2, MP5416_REG_CTL1, BIT(1), 2),
|
||||
MP5416BUCK("buck3", 3, mp5416_I_limits1, MP5416_REG_CTL1, BIT(2), 1),
|
||||
MP5416BUCK("buck4", 4, mp5416_I_limits2, MP5416_REG_CTL2, BIT(5), 2),
|
||||
MP5416LDO("ldo1", 1, BIT(4)),
|
||||
MP5416LDO("ldo2", 2, BIT(3)),
|
||||
MP5416LDO("ldo3", 3, BIT(2)),
|
||||
MP5416LDO("ldo4", 4, BIT(1)),
|
||||
};
|
||||
|
||||
/*
|
||||
* DVS ramp rate BUCK1 to BUCK4
|
||||
* 00: 32mV/us
|
||||
* 01: 16mV/us
|
||||
* 10: 8mV/us
|
||||
* 11: 4mV/us
|
||||
*/
|
||||
static int mp5416_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay)
|
||||
{
|
||||
unsigned int ramp_val;
|
||||
|
||||
if (ramp_delay > 32000 || ramp_delay < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (ramp_delay <= 4000)
|
||||
ramp_val = 3;
|
||||
else if (ramp_delay <= 8000)
|
||||
ramp_val = 2;
|
||||
else if (ramp_delay <= 16000)
|
||||
ramp_val = 1;
|
||||
else
|
||||
ramp_val = 0;
|
||||
|
||||
return regmap_update_bits(rdev->regmap, MP5416_REG_CTL2,
|
||||
MP5416_MASK_DVS_SLEWRATE, ramp_val << 6);
|
||||
}
|
||||
|
||||
static int mp5416_i2c_probe(struct i2c_client *client)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct regulator_config config = { NULL, };
|
||||
struct regulator_dev *rdev;
|
||||
struct regmap *regmap;
|
||||
int i;
|
||||
|
||||
regmap = devm_regmap_init_i2c(client, &mp5416_regmap_config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(dev, "Failed to allocate regmap!\n");
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
config.dev = dev;
|
||||
config.regmap = regmap;
|
||||
|
||||
for (i = 0; i < MP5416_MAX_REGULATORS; i++) {
|
||||
rdev = devm_regulator_register(dev,
|
||||
&mp5416_regulators_desc[i],
|
||||
&config);
|
||||
if (IS_ERR(rdev)) {
|
||||
dev_err(dev, "Failed to register regulator!\n");
|
||||
return PTR_ERR(rdev);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id mp5416_of_match[] = {
|
||||
{ .compatible = "mps,mp5416" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mp5416_of_match);
|
||||
|
||||
static const struct i2c_device_id mp5416_id[] = {
|
||||
{ "mp5416", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mp5416_id);
|
||||
|
||||
static struct i2c_driver mp5416_regulator_driver = {
|
||||
.driver = {
|
||||
.name = "mp5416",
|
||||
.of_match_table = of_match_ptr(mp5416_of_match),
|
||||
},
|
||||
.probe_new = mp5416_i2c_probe,
|
||||
.id_table = mp5416_id,
|
||||
};
|
||||
module_i2c_driver(mp5416_regulator_driver);
|
||||
|
||||
MODULE_AUTHOR("Saravanan Sekar <sravanhome@gmail.com>");
|
||||
MODULE_DESCRIPTION("MP5416 PMIC regulator driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -95,6 +95,7 @@ static const struct regulator_desc mp8859_regulators[] = {
|
|||
.id = 0,
|
||||
.type = REGULATOR_VOLTAGE,
|
||||
.name = "mp8859_dcdc",
|
||||
.supply_name = "vin",
|
||||
.of_match = of_match_ptr("mp8859_dcdc"),
|
||||
.n_voltages = VOL_MAX_IDX + 1,
|
||||
.linear_ranges = mp8859_dcdc_ranges,
|
||||
|
|
|
@ -354,7 +354,11 @@ static int pwm_regulator_probe(struct platform_device *pdev)
|
|||
drvdata->pwm = devm_pwm_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(drvdata->pwm)) {
|
||||
ret = PTR_ERR(drvdata->pwm);
|
||||
dev_err(&pdev->dev, "Failed to get PWM: %d\n", ret);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
dev_dbg(&pdev->dev,
|
||||
"Failed to get PWM, deferring probe\n");
|
||||
else
|
||||
dev_err(&pdev->dev, "Failed to get PWM: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -925,12 +925,21 @@ static const struct rpm_regulator_data rpm_pm8921_regulators[] = {
|
|||
{ }
|
||||
};
|
||||
|
||||
static const struct rpm_regulator_data rpm_smb208_regulators[] = {
|
||||
{ "s1a", QCOM_RPM_SMB208_S1a, &smb208_smps, "vin_s1a" },
|
||||
{ "s1b", QCOM_RPM_SMB208_S1b, &smb208_smps, "vin_s1b" },
|
||||
{ "s2a", QCOM_RPM_SMB208_S2a, &smb208_smps, "vin_s2a" },
|
||||
{ "s2b", QCOM_RPM_SMB208_S2b, &smb208_smps, "vin_s2b" },
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct of_device_id rpm_of_match[] = {
|
||||
{ .compatible = "qcom,rpm-pm8018-regulators",
|
||||
.data = &rpm_pm8018_regulators },
|
||||
{ .compatible = "qcom,rpm-pm8058-regulators", .data = &rpm_pm8058_regulators },
|
||||
{ .compatible = "qcom,rpm-pm8901-regulators", .data = &rpm_pm8901_regulators },
|
||||
{ .compatible = "qcom,rpm-pm8921-regulators", .data = &rpm_pm8921_regulators },
|
||||
{ .compatible = "qcom,rpm-smb208-regulators", .data = &rpm_smb208_regulators },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rpm_of_match);
|
||||
|
|
|
@ -62,6 +62,13 @@ config SPI_ALTERA
|
|||
help
|
||||
This is the driver for the Altera SPI Controller.
|
||||
|
||||
config SPI_AR934X
|
||||
tristate "Qualcomm Atheros AR934X/QCA95XX SPI controller driver"
|
||||
depends on ATH79 || COMPILE_TEST
|
||||
help
|
||||
This enables support for the SPI controller present on the
|
||||
Qualcomm Atheros AR934X/QCA95XX SoCs.
|
||||
|
||||
config SPI_ATH79
|
||||
tristate "Atheros AR71XX/AR724X/AR913X SPI controller driver"
|
||||
depends on ATH79 || COMPILE_TEST
|
||||
|
@ -264,6 +271,13 @@ config SPI_FALCON
|
|||
has only been tested with m25p80 type chips. The hardware has no
|
||||
support for other types of SPI peripherals.
|
||||
|
||||
config SPI_FSI
|
||||
tristate "FSI SPI driver"
|
||||
depends on FSI
|
||||
help
|
||||
This enables support for the driver for FSI bus attached SPI
|
||||
controllers.
|
||||
|
||||
config SPI_FSL_LPSPI
|
||||
tristate "Freescale i.MX LPSPI controller"
|
||||
depends on ARCH_MXC || COMPILE_TEST
|
||||
|
@ -285,7 +299,6 @@ config SPI_HISI_SFC_V3XX
|
|||
tristate "HiSilicon SPI-NOR Flash Controller for Hi16XX chipsets"
|
||||
depends on (ARM64 && ACPI) || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
select CONFIG_MTD_SPI_NOR
|
||||
help
|
||||
This enables support for HiSilicon v3xx SPI-NOR flash controller
|
||||
found in hi16xx chipsets.
|
||||
|
@ -415,6 +428,7 @@ config SPI_FSL_ESPI
|
|||
|
||||
config SPI_MESON_SPICC
|
||||
tristate "Amlogic Meson SPICC controller"
|
||||
depends on COMMON_CLK
|
||||
depends on ARCH_MESON || COMPILE_TEST
|
||||
help
|
||||
This enables master mode support for the SPICC (SPI communication
|
||||
|
@ -443,6 +457,16 @@ config SPI_MT7621
|
|||
help
|
||||
This selects a driver for the MediaTek MT7621 SPI Controller.
|
||||
|
||||
config SPI_MTK_NOR
|
||||
tristate "MediaTek SPI NOR controller"
|
||||
depends on ARCH_MEDIATEK || COMPILE_TEST
|
||||
help
|
||||
This enables support for SPI NOR controller found on MediaTek
|
||||
ARM SoCs. This is a controller specifically for SPI-NOR flash.
|
||||
It can perform generic SPI transfers up to 6 bytes via generic
|
||||
SPI interface as well as several SPI-NOR specific instructions
|
||||
via SPI MEM interface.
|
||||
|
||||
config SPI_NPCM_FIU
|
||||
tristate "Nuvoton NPCM FLASH Interface Unit"
|
||||
depends on ARCH_NPCM || COMPILE_TEST
|
||||
|
@ -890,6 +914,17 @@ config SPI_ZYNQMP_GQSPI
|
|||
# Add new SPI master controllers in alphabetical order above this line
|
||||
#
|
||||
|
||||
comment "SPI Multiplexer support"
|
||||
|
||||
config SPI_MUX
|
||||
tristate "SPI multiplexer support"
|
||||
select MULTIPLEXER
|
||||
help
|
||||
This adds support for SPI multiplexers. Each SPI mux will be
|
||||
accessible as a SPI controller, the devices behind the mux will appear
|
||||
to be chip selects on this controller. It is still necessary to
|
||||
select one or more specific mux-controller drivers.
|
||||
|
||||
#
|
||||
# There are lots of SPI device types, with sensors and memory
|
||||
# being probably the most widely used ones.
|
||||
|
|
|
@ -9,11 +9,13 @@ ccflags-$(CONFIG_SPI_DEBUG) := -DDEBUG
|
|||
# config declarations into driver model code
|
||||
obj-$(CONFIG_SPI_MASTER) += spi.o
|
||||
obj-$(CONFIG_SPI_MEM) += spi-mem.o
|
||||
obj-$(CONFIG_SPI_MUX) += spi-mux.o
|
||||
obj-$(CONFIG_SPI_SPIDEV) += spidev.o
|
||||
obj-$(CONFIG_SPI_LOOPBACK_TEST) += spi-loopback-test.o
|
||||
|
||||
# SPI master controller drivers (bus)
|
||||
obj-$(CONFIG_SPI_ALTERA) += spi-altera.o
|
||||
obj-$(CONFIG_SPI_AR934X) += spi-ar934x.o
|
||||
obj-$(CONFIG_SPI_ARMADA_3700) += spi-armada-3700.o
|
||||
obj-$(CONFIG_SPI_ATMEL) += spi-atmel.o
|
||||
obj-$(CONFIG_SPI_ATMEL_QUADSPI) += atmel-quadspi.o
|
||||
|
@ -40,6 +42,7 @@ spi-dw-midpci-objs := spi-dw-pci.o spi-dw-mid.o
|
|||
obj-$(CONFIG_SPI_EFM32) += spi-efm32.o
|
||||
obj-$(CONFIG_SPI_EP93XX) += spi-ep93xx.o
|
||||
obj-$(CONFIG_SPI_FALCON) += spi-falcon.o
|
||||
obj-$(CONFIG_SPI_FSI) += spi-fsi.o
|
||||
obj-$(CONFIG_SPI_FSL_CPM) += spi-fsl-cpm.o
|
||||
obj-$(CONFIG_SPI_FSL_DSPI) += spi-fsl-dspi.o
|
||||
obj-$(CONFIG_SPI_FSL_LIB) += spi-fsl-lib.o
|
||||
|
@ -62,6 +65,7 @@ obj-$(CONFIG_SPI_MPC52xx_PSC) += spi-mpc52xx-psc.o
|
|||
obj-$(CONFIG_SPI_MPC52xx) += spi-mpc52xx.o
|
||||
obj-$(CONFIG_SPI_MT65XX) += spi-mt65xx.o
|
||||
obj-$(CONFIG_SPI_MT7621) += spi-mt7621.o
|
||||
obj-$(CONFIG_SPI_MTK_NOR) += spi-mtk-nor.o
|
||||
obj-$(CONFIG_SPI_MXIC) += spi-mxic.o
|
||||
obj-$(CONFIG_SPI_MXS) += spi-mxs.o
|
||||
obj-$(CONFIG_SPI_NPCM_FIU) += spi-npcm-fiu.o
|
||||
|
|
|
@ -173,6 +173,81 @@ static const struct atmel_qspi_mode atmel_qspi_modes[] = {
|
|||
{ 4, 4, 4, QSPI_IFR_WIDTH_QUAD_CMD },
|
||||
};
|
||||
|
||||
#ifdef VERBOSE_DEBUG
|
||||
static const char *atmel_qspi_reg_name(u32 offset, char *tmp, size_t sz)
|
||||
{
|
||||
switch (offset) {
|
||||
case QSPI_CR:
|
||||
return "CR";
|
||||
case QSPI_MR:
|
||||
return "MR";
|
||||
case QSPI_RD:
|
||||
return "MR";
|
||||
case QSPI_TD:
|
||||
return "TD";
|
||||
case QSPI_SR:
|
||||
return "SR";
|
||||
case QSPI_IER:
|
||||
return "IER";
|
||||
case QSPI_IDR:
|
||||
return "IDR";
|
||||
case QSPI_IMR:
|
||||
return "IMR";
|
||||
case QSPI_SCR:
|
||||
return "SCR";
|
||||
case QSPI_IAR:
|
||||
return "IAR";
|
||||
case QSPI_ICR:
|
||||
return "ICR/WICR";
|
||||
case QSPI_IFR:
|
||||
return "IFR";
|
||||
case QSPI_RICR:
|
||||
return "RICR";
|
||||
case QSPI_SMR:
|
||||
return "SMR";
|
||||
case QSPI_SKR:
|
||||
return "SKR";
|
||||
case QSPI_WPMR:
|
||||
return "WPMR";
|
||||
case QSPI_WPSR:
|
||||
return "WPSR";
|
||||
case QSPI_VERSION:
|
||||
return "VERSION";
|
||||
default:
|
||||
snprintf(tmp, sz, "0x%02x", offset);
|
||||
break;
|
||||
}
|
||||
|
||||
return tmp;
|
||||
}
|
||||
#endif /* VERBOSE_DEBUG */
|
||||
|
||||
static u32 atmel_qspi_read(struct atmel_qspi *aq, u32 offset)
|
||||
{
|
||||
u32 value = readl_relaxed(aq->regs + offset);
|
||||
|
||||
#ifdef VERBOSE_DEBUG
|
||||
char tmp[8];
|
||||
|
||||
dev_vdbg(&aq->pdev->dev, "read 0x%08x from %s\n", value,
|
||||
atmel_qspi_reg_name(offset, tmp, sizeof(tmp)));
|
||||
#endif /* VERBOSE_DEBUG */
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static void atmel_qspi_write(u32 value, struct atmel_qspi *aq, u32 offset)
|
||||
{
|
||||
#ifdef VERBOSE_DEBUG
|
||||
char tmp[8];
|
||||
|
||||
dev_vdbg(&aq->pdev->dev, "write 0x%08x into %s\n", value,
|
||||
atmel_qspi_reg_name(offset, tmp, sizeof(tmp)));
|
||||
#endif /* VERBOSE_DEBUG */
|
||||
|
||||
writel_relaxed(value, aq->regs + offset);
|
||||
}
|
||||
|
||||
static inline bool atmel_qspi_is_compatible(const struct spi_mem_op *op,
|
||||
const struct atmel_qspi_mode *mode)
|
||||
{
|
||||
|
@ -293,32 +368,32 @@ static int atmel_qspi_set_cfg(struct atmel_qspi *aq,
|
|||
* Serial Memory Mode (SMM).
|
||||
*/
|
||||
if (aq->mr != QSPI_MR_SMM) {
|
||||
writel_relaxed(QSPI_MR_SMM, aq->regs + QSPI_MR);
|
||||
atmel_qspi_write(QSPI_MR_SMM, aq, QSPI_MR);
|
||||
aq->mr = QSPI_MR_SMM;
|
||||
}
|
||||
|
||||
/* Clear pending interrupts */
|
||||
(void)readl_relaxed(aq->regs + QSPI_SR);
|
||||
(void)atmel_qspi_read(aq, QSPI_SR);
|
||||
|
||||
if (aq->caps->has_ricr) {
|
||||
if (!op->addr.nbytes && op->data.dir == SPI_MEM_DATA_IN)
|
||||
ifr |= QSPI_IFR_APBTFRTYP_READ;
|
||||
|
||||
/* Set QSPI Instruction Frame registers */
|
||||
writel_relaxed(iar, aq->regs + QSPI_IAR);
|
||||
atmel_qspi_write(iar, aq, QSPI_IAR);
|
||||
if (op->data.dir == SPI_MEM_DATA_IN)
|
||||
writel_relaxed(icr, aq->regs + QSPI_RICR);
|
||||
atmel_qspi_write(icr, aq, QSPI_RICR);
|
||||
else
|
||||
writel_relaxed(icr, aq->regs + QSPI_WICR);
|
||||
writel_relaxed(ifr, aq->regs + QSPI_IFR);
|
||||
atmel_qspi_write(icr, aq, QSPI_WICR);
|
||||
atmel_qspi_write(ifr, aq, QSPI_IFR);
|
||||
} else {
|
||||
if (op->data.dir == SPI_MEM_DATA_OUT)
|
||||
ifr |= QSPI_IFR_SAMA5D2_WRITE_TRSFR;
|
||||
|
||||
/* Set QSPI Instruction Frame registers */
|
||||
writel_relaxed(iar, aq->regs + QSPI_IAR);
|
||||
writel_relaxed(icr, aq->regs + QSPI_ICR);
|
||||
writel_relaxed(ifr, aq->regs + QSPI_IFR);
|
||||
atmel_qspi_write(iar, aq, QSPI_IAR);
|
||||
atmel_qspi_write(icr, aq, QSPI_ICR);
|
||||
atmel_qspi_write(ifr, aq, QSPI_IFR);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -345,7 +420,7 @@ static int atmel_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
|||
/* Skip to the final steps if there is no data */
|
||||
if (op->data.nbytes) {
|
||||
/* Dummy read of QSPI_IFR to synchronize APB and AHB accesses */
|
||||
(void)readl_relaxed(aq->regs + QSPI_IFR);
|
||||
(void)atmel_qspi_read(aq, QSPI_IFR);
|
||||
|
||||
/* Send/Receive data */
|
||||
if (op->data.dir == SPI_MEM_DATA_IN)
|
||||
|
@ -356,22 +431,22 @@ static int atmel_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
|||
op->data.nbytes);
|
||||
|
||||
/* Release the chip-select */
|
||||
writel_relaxed(QSPI_CR_LASTXFER, aq->regs + QSPI_CR);
|
||||
atmel_qspi_write(QSPI_CR_LASTXFER, aq, QSPI_CR);
|
||||
}
|
||||
|
||||
/* Poll INSTRuction End status */
|
||||
sr = readl_relaxed(aq->regs + QSPI_SR);
|
||||
sr = atmel_qspi_read(aq, QSPI_SR);
|
||||
if ((sr & QSPI_SR_CMD_COMPLETED) == QSPI_SR_CMD_COMPLETED)
|
||||
return err;
|
||||
|
||||
/* Wait for INSTRuction End interrupt */
|
||||
reinit_completion(&aq->cmd_completion);
|
||||
aq->pending = sr & QSPI_SR_CMD_COMPLETED;
|
||||
writel_relaxed(QSPI_SR_CMD_COMPLETED, aq->regs + QSPI_IER);
|
||||
atmel_qspi_write(QSPI_SR_CMD_COMPLETED, aq, QSPI_IER);
|
||||
if (!wait_for_completion_timeout(&aq->cmd_completion,
|
||||
msecs_to_jiffies(1000)))
|
||||
err = -ETIMEDOUT;
|
||||
writel_relaxed(QSPI_SR_CMD_COMPLETED, aq->regs + QSPI_IDR);
|
||||
atmel_qspi_write(QSPI_SR_CMD_COMPLETED, aq, QSPI_IDR);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
@ -410,7 +485,7 @@ static int atmel_qspi_setup(struct spi_device *spi)
|
|||
scbr--;
|
||||
|
||||
aq->scr = QSPI_SCR_SCBR(scbr);
|
||||
writel_relaxed(aq->scr, aq->regs + QSPI_SCR);
|
||||
atmel_qspi_write(aq->scr, aq, QSPI_SCR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -418,14 +493,14 @@ static int atmel_qspi_setup(struct spi_device *spi)
|
|||
static void atmel_qspi_init(struct atmel_qspi *aq)
|
||||
{
|
||||
/* Reset the QSPI controller */
|
||||
writel_relaxed(QSPI_CR_SWRST, aq->regs + QSPI_CR);
|
||||
atmel_qspi_write(QSPI_CR_SWRST, aq, QSPI_CR);
|
||||
|
||||
/* Set the QSPI controller by default in Serial Memory Mode */
|
||||
writel_relaxed(QSPI_MR_SMM, aq->regs + QSPI_MR);
|
||||
atmel_qspi_write(QSPI_MR_SMM, aq, QSPI_MR);
|
||||
aq->mr = QSPI_MR_SMM;
|
||||
|
||||
/* Enable the QSPI controller */
|
||||
writel_relaxed(QSPI_CR_QSPIEN, aq->regs + QSPI_CR);
|
||||
atmel_qspi_write(QSPI_CR_QSPIEN, aq, QSPI_CR);
|
||||
}
|
||||
|
||||
static irqreturn_t atmel_qspi_interrupt(int irq, void *dev_id)
|
||||
|
@ -433,8 +508,8 @@ static irqreturn_t atmel_qspi_interrupt(int irq, void *dev_id)
|
|||
struct atmel_qspi *aq = dev_id;
|
||||
u32 status, mask, pending;
|
||||
|
||||
status = readl_relaxed(aq->regs + QSPI_SR);
|
||||
mask = readl_relaxed(aq->regs + QSPI_IMR);
|
||||
status = atmel_qspi_read(aq, QSPI_SR);
|
||||
mask = atmel_qspi_read(aq, QSPI_IMR);
|
||||
pending = status & mask;
|
||||
|
||||
if (!pending)
|
||||
|
@ -569,7 +644,7 @@ static int atmel_qspi_remove(struct platform_device *pdev)
|
|||
struct atmel_qspi *aq = spi_controller_get_devdata(ctrl);
|
||||
|
||||
spi_unregister_controller(ctrl);
|
||||
writel_relaxed(QSPI_CR_QSPIDIS, aq->regs + QSPI_CR);
|
||||
atmel_qspi_write(QSPI_CR_QSPIDIS, aq, QSPI_CR);
|
||||
clk_disable_unprepare(aq->qspick);
|
||||
clk_disable_unprepare(aq->pclk);
|
||||
return 0;
|
||||
|
@ -596,7 +671,7 @@ static int __maybe_unused atmel_qspi_resume(struct device *dev)
|
|||
|
||||
atmel_qspi_init(aq);
|
||||
|
||||
writel_relaxed(aq->scr, aq->regs + QSPI_SCR);
|
||||
atmel_qspi_write(aq->scr, aq, QSPI_SCR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,235 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// SPI controller driver for Qualcomm Atheros AR934x/QCA95xx SoCs
|
||||
//
|
||||
// Copyright (C) 2020 Chuanhong Guo <gch981213@gmail.com>
|
||||
//
|
||||
// Based on spi-mt7621.c:
|
||||
// Copyright (C) 2011 Sergiy <piratfm@gmail.com>
|
||||
// Copyright (C) 2011-2013 Gabor Juhos <juhosg@openwrt.org>
|
||||
// Copyright (C) 2014-2015 Felix Fietkau <nbd@nbd.name>
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#define DRIVER_NAME "spi-ar934x"
|
||||
|
||||
#define AR934X_SPI_REG_FS 0x00
|
||||
#define AR934X_SPI_ENABLE BIT(0)
|
||||
|
||||
#define AR934X_SPI_REG_IOC 0x08
|
||||
#define AR934X_SPI_IOC_INITVAL 0x70000
|
||||
|
||||
#define AR934X_SPI_REG_CTRL 0x04
|
||||
#define AR934X_SPI_CLK_MASK GENMASK(5, 0)
|
||||
|
||||
#define AR934X_SPI_DATAOUT 0x10
|
||||
|
||||
#define AR934X_SPI_REG_SHIFT_CTRL 0x14
|
||||
#define AR934X_SPI_SHIFT_EN BIT(31)
|
||||
#define AR934X_SPI_SHIFT_CS(n) BIT(28 + (n))
|
||||
#define AR934X_SPI_SHIFT_TERM 26
|
||||
#define AR934X_SPI_SHIFT_VAL(cs, term, count) \
|
||||
(AR934X_SPI_SHIFT_EN | AR934X_SPI_SHIFT_CS(cs) | \
|
||||
(term) << AR934X_SPI_SHIFT_TERM | (count))
|
||||
|
||||
#define AR934X_SPI_DATAIN 0x18
|
||||
|
||||
struct ar934x_spi {
|
||||
struct spi_controller *ctlr;
|
||||
void __iomem *base;
|
||||
struct clk *clk;
|
||||
unsigned int clk_freq;
|
||||
};
|
||||
|
||||
static inline int ar934x_spi_clk_div(struct ar934x_spi *sp, unsigned int freq)
|
||||
{
|
||||
int div = DIV_ROUND_UP(sp->clk_freq, freq * 2) - 1;
|
||||
|
||||
if (div < 0)
|
||||
return 0;
|
||||
else if (div > AR934X_SPI_CLK_MASK)
|
||||
return -EINVAL;
|
||||
else
|
||||
return div;
|
||||
}
|
||||
|
||||
static int ar934x_spi_setup(struct spi_device *spi)
|
||||
{
|
||||
struct ar934x_spi *sp = spi_controller_get_devdata(spi->master);
|
||||
|
||||
if ((spi->max_speed_hz == 0) ||
|
||||
(spi->max_speed_hz > (sp->clk_freq / 2))) {
|
||||
spi->max_speed_hz = sp->clk_freq / 2;
|
||||
} else if (spi->max_speed_hz < (sp->clk_freq / 128)) {
|
||||
dev_err(&spi->dev, "spi clock is too low\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ar934x_spi_transfer_one_message(struct spi_controller *master,
|
||||
struct spi_message *m)
|
||||
{
|
||||
struct ar934x_spi *sp = spi_controller_get_devdata(master);
|
||||
struct spi_transfer *t = NULL;
|
||||
struct spi_device *spi = m->spi;
|
||||
unsigned long trx_done, trx_cur;
|
||||
int stat = 0;
|
||||
u8 term = 0;
|
||||
int div, i;
|
||||
u32 reg;
|
||||
const u8 *tx_buf;
|
||||
u8 *buf;
|
||||
|
||||
m->actual_length = 0;
|
||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||
if (t->speed_hz)
|
||||
div = ar934x_spi_clk_div(sp, t->speed_hz);
|
||||
else
|
||||
div = ar934x_spi_clk_div(sp, spi->max_speed_hz);
|
||||
if (div < 0) {
|
||||
stat = -EIO;
|
||||
goto msg_done;
|
||||
}
|
||||
|
||||
reg = ioread32(sp->base + AR934X_SPI_REG_CTRL);
|
||||
reg &= ~AR934X_SPI_CLK_MASK;
|
||||
reg |= div;
|
||||
iowrite32(reg, sp->base + AR934X_SPI_REG_CTRL);
|
||||
iowrite32(0, sp->base + AR934X_SPI_DATAOUT);
|
||||
|
||||
for (trx_done = 0; trx_done < t->len; trx_done += 4) {
|
||||
trx_cur = t->len - trx_done;
|
||||
if (trx_cur > 4)
|
||||
trx_cur = 4;
|
||||
else if (list_is_last(&t->transfer_list, &m->transfers))
|
||||
term = 1;
|
||||
|
||||
if (t->tx_buf) {
|
||||
tx_buf = t->tx_buf + trx_done;
|
||||
reg = tx_buf[0];
|
||||
for (i = 1; i < trx_cur; i++)
|
||||
reg = reg << 8 | tx_buf[i];
|
||||
iowrite32(reg, sp->base + AR934X_SPI_DATAOUT);
|
||||
}
|
||||
|
||||
reg = AR934X_SPI_SHIFT_VAL(spi->chip_select, term,
|
||||
trx_cur * 8);
|
||||
iowrite32(reg, sp->base + AR934X_SPI_REG_SHIFT_CTRL);
|
||||
stat = readl_poll_timeout(
|
||||
sp->base + AR934X_SPI_REG_SHIFT_CTRL, reg,
|
||||
!(reg & AR934X_SPI_SHIFT_EN), 0, 5);
|
||||
if (stat < 0)
|
||||
goto msg_done;
|
||||
|
||||
if (t->rx_buf) {
|
||||
reg = ioread32(sp->base + AR934X_SPI_DATAIN);
|
||||
buf = t->rx_buf + trx_done;
|
||||
for (i = 0; i < trx_cur; i++) {
|
||||
buf[trx_cur - i - 1] = reg & 0xff;
|
||||
reg >>= 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
m->actual_length += t->len;
|
||||
}
|
||||
|
||||
msg_done:
|
||||
m->status = stat;
|
||||
spi_finalize_current_message(master);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ar934x_spi_match[] = {
|
||||
{ .compatible = "qca,ar934x-spi" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ar934x_spi_match);
|
||||
|
||||
static int ar934x_spi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_controller *ctlr;
|
||||
struct ar934x_spi *sp;
|
||||
void __iomem *base;
|
||||
struct clk *clk;
|
||||
int ret;
|
||||
|
||||
base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(&pdev->dev, "failed to get clock\n");
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ctlr = spi_alloc_master(&pdev->dev, sizeof(*sp));
|
||||
if (!ctlr) {
|
||||
dev_info(&pdev->dev, "failed to allocate spi controller\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* disable flash mapping and expose spi controller registers */
|
||||
iowrite32(AR934X_SPI_ENABLE, base + AR934X_SPI_REG_FS);
|
||||
/* restore pins to default state: CSn=1 DO=CLK=0 */
|
||||
iowrite32(AR934X_SPI_IOC_INITVAL, base + AR934X_SPI_REG_IOC);
|
||||
|
||||
ctlr->mode_bits = SPI_LSB_FIRST;
|
||||
ctlr->setup = ar934x_spi_setup;
|
||||
ctlr->transfer_one_message = ar934x_spi_transfer_one_message;
|
||||
ctlr->bits_per_word_mask = SPI_BPW_MASK(8);
|
||||
ctlr->dev.of_node = pdev->dev.of_node;
|
||||
ctlr->num_chipselect = 3;
|
||||
|
||||
dev_set_drvdata(&pdev->dev, ctlr);
|
||||
|
||||
sp = spi_controller_get_devdata(ctlr);
|
||||
sp->base = base;
|
||||
sp->clk = clk;
|
||||
sp->clk_freq = clk_get_rate(clk);
|
||||
sp->ctlr = ctlr;
|
||||
|
||||
return devm_spi_register_controller(&pdev->dev, ctlr);
|
||||
}
|
||||
|
||||
static int ar934x_spi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_controller *ctlr;
|
||||
struct ar934x_spi *sp;
|
||||
|
||||
ctlr = dev_get_drvdata(&pdev->dev);
|
||||
sp = spi_controller_get_devdata(ctlr);
|
||||
|
||||
clk_disable_unprepare(sp->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver ar934x_spi_driver = {
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.of_match_table = ar934x_spi_match,
|
||||
},
|
||||
.probe = ar934x_spi_probe,
|
||||
.remove = ar934x_spi_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(ar934x_spi_driver);
|
||||
|
||||
MODULE_DESCRIPTION("SPI controller driver for Qualcomm Atheros AR934x/QCA95xx");
|
||||
MODULE_AUTHOR("Chuanhong Guo <gch981213@gmail.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:" DRIVER_NAME);
|
|
@ -6,14 +6,13 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/spi_bitbang.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/platform_data/efm32-spi.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#define DRIVER_NAME "efm32-spi"
|
||||
|
||||
|
@ -82,9 +81,6 @@ struct efm32_spi_ddata {
|
|||
const u8 *tx_buf;
|
||||
u8 *rx_buf;
|
||||
unsigned tx_len, rx_len;
|
||||
|
||||
/* chip selects */
|
||||
unsigned csgpio[];
|
||||
};
|
||||
|
||||
#define ddata_to_dev(ddata) (&(ddata->bitbang.master->dev))
|
||||
|
@ -102,14 +98,6 @@ static u32 efm32_spi_read32(struct efm32_spi_ddata *ddata, unsigned offset)
|
|||
return readl_relaxed(ddata->base + offset);
|
||||
}
|
||||
|
||||
static void efm32_spi_chipselect(struct spi_device *spi, int is_on)
|
||||
{
|
||||
struct efm32_spi_ddata *ddata = spi_master_get_devdata(spi->master);
|
||||
int value = !(spi->mode & SPI_CS_HIGH) == !(is_on == BITBANG_CS_ACTIVE);
|
||||
|
||||
gpio_set_value(ddata->csgpio[spi->chip_select], value);
|
||||
}
|
||||
|
||||
static int efm32_spi_setup_transfer(struct spi_device *spi,
|
||||
struct spi_transfer *t)
|
||||
{
|
||||
|
@ -320,17 +308,11 @@ static int efm32_spi_probe(struct platform_device *pdev)
|
|||
int ret;
|
||||
struct spi_master *master;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
int num_cs, i;
|
||||
|
||||
if (!np)
|
||||
return -EINVAL;
|
||||
|
||||
num_cs = of_gpio_named_count(np, "cs-gpios");
|
||||
if (num_cs < 0)
|
||||
return num_cs;
|
||||
|
||||
master = spi_alloc_master(&pdev->dev,
|
||||
sizeof(*ddata) + num_cs * sizeof(unsigned));
|
||||
master = spi_alloc_master(&pdev->dev, sizeof(*ddata));
|
||||
if (!master) {
|
||||
dev_dbg(&pdev->dev,
|
||||
"failed to allocate spi master controller\n");
|
||||
|
@ -340,14 +322,13 @@ static int efm32_spi_probe(struct platform_device *pdev)
|
|||
|
||||
master->dev.of_node = pdev->dev.of_node;
|
||||
|
||||
master->num_chipselect = num_cs;
|
||||
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
|
||||
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16);
|
||||
master->use_gpio_descriptors = true;
|
||||
|
||||
ddata = spi_master_get_devdata(master);
|
||||
|
||||
ddata->bitbang.master = master;
|
||||
ddata->bitbang.chipselect = efm32_spi_chipselect;
|
||||
ddata->bitbang.setup_transfer = efm32_spi_setup_transfer;
|
||||
ddata->bitbang.txrx_bufs = efm32_spi_txrx_bufs;
|
||||
|
||||
|
@ -361,25 +342,6 @@ static int efm32_spi_probe(struct platform_device *pdev)
|
|||
goto err;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_cs; ++i) {
|
||||
ret = of_get_named_gpio(np, "cs-gpios", i);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to get csgpio#%u (%d)\n",
|
||||
i, ret);
|
||||
goto err;
|
||||
}
|
||||
ddata->csgpio[i] = ret;
|
||||
dev_dbg(&pdev->dev, "csgpio#%u = %u\n", i, ddata->csgpio[i]);
|
||||
ret = devm_gpio_request_one(&pdev->dev, ddata->csgpio[i],
|
||||
GPIOF_OUT_INIT_LOW, DRIVER_NAME);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev,
|
||||
"failed to configure csgpio#%u (%d)\n",
|
||||
i, ret);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
ret = -ENODEV;
|
||||
|
|
|
@ -0,0 +1,558 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// Copyright (C) IBM Corporation 2020
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/fsi.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#define FSI_ENGID_SPI 0x23
|
||||
#define FSI_MBOX_ROOT_CTRL_8 0x2860
|
||||
|
||||
#define FSI2SPI_DATA0 0x00
|
||||
#define FSI2SPI_DATA1 0x04
|
||||
#define FSI2SPI_CMD 0x08
|
||||
#define FSI2SPI_CMD_WRITE BIT(31)
|
||||
#define FSI2SPI_RESET 0x18
|
||||
#define FSI2SPI_STATUS 0x1c
|
||||
#define FSI2SPI_STATUS_ANY_ERROR BIT(31)
|
||||
#define FSI2SPI_IRQ 0x20
|
||||
|
||||
#define SPI_FSI_BASE 0x70000
|
||||
#define SPI_FSI_INIT_TIMEOUT_MS 1000
|
||||
#define SPI_FSI_MAX_TRANSFER_SIZE 2048
|
||||
|
||||
#define SPI_FSI_ERROR 0x0
|
||||
#define SPI_FSI_COUNTER_CFG 0x1
|
||||
#define SPI_FSI_COUNTER_CFG_LOOPS(x) (((u64)(x) & 0xffULL) << 32)
|
||||
#define SPI_FSI_CFG1 0x2
|
||||
#define SPI_FSI_CLOCK_CFG 0x3
|
||||
#define SPI_FSI_CLOCK_CFG_MM_ENABLE BIT_ULL(32)
|
||||
#define SPI_FSI_CLOCK_CFG_ECC_DISABLE (BIT_ULL(35) | BIT_ULL(33))
|
||||
#define SPI_FSI_CLOCK_CFG_RESET1 (BIT_ULL(36) | BIT_ULL(38))
|
||||
#define SPI_FSI_CLOCK_CFG_RESET2 (BIT_ULL(37) | BIT_ULL(39))
|
||||
#define SPI_FSI_CLOCK_CFG_MODE (BIT_ULL(41) | BIT_ULL(42))
|
||||
#define SPI_FSI_CLOCK_CFG_SCK_RECV_DEL GENMASK_ULL(51, 44)
|
||||
#define SPI_FSI_CLOCK_CFG_SCK_NO_DEL BIT_ULL(51)
|
||||
#define SPI_FSI_CLOCK_CFG_SCK_DIV GENMASK_ULL(63, 52)
|
||||
#define SPI_FSI_MMAP 0x4
|
||||
#define SPI_FSI_DATA_TX 0x5
|
||||
#define SPI_FSI_DATA_RX 0x6
|
||||
#define SPI_FSI_SEQUENCE 0x7
|
||||
#define SPI_FSI_SEQUENCE_STOP 0x00
|
||||
#define SPI_FSI_SEQUENCE_SEL_SLAVE(x) (0x10 | ((x) & 0xf))
|
||||
#define SPI_FSI_SEQUENCE_SHIFT_OUT(x) (0x30 | ((x) & 0xf))
|
||||
#define SPI_FSI_SEQUENCE_SHIFT_IN(x) (0x40 | ((x) & 0xf))
|
||||
#define SPI_FSI_SEQUENCE_COPY_DATA_TX 0xc0
|
||||
#define SPI_FSI_SEQUENCE_BRANCH(x) (0xe0 | ((x) & 0xf))
|
||||
#define SPI_FSI_STATUS 0x8
|
||||
#define SPI_FSI_STATUS_ERROR \
|
||||
(GENMASK_ULL(31, 21) | GENMASK_ULL(15, 12))
|
||||
#define SPI_FSI_STATUS_SEQ_STATE GENMASK_ULL(55, 48)
|
||||
#define SPI_FSI_STATUS_SEQ_STATE_IDLE BIT_ULL(48)
|
||||
#define SPI_FSI_STATUS_TDR_UNDERRUN BIT_ULL(57)
|
||||
#define SPI_FSI_STATUS_TDR_OVERRUN BIT_ULL(58)
|
||||
#define SPI_FSI_STATUS_TDR_FULL BIT_ULL(59)
|
||||
#define SPI_FSI_STATUS_RDR_UNDERRUN BIT_ULL(61)
|
||||
#define SPI_FSI_STATUS_RDR_OVERRUN BIT_ULL(62)
|
||||
#define SPI_FSI_STATUS_RDR_FULL BIT_ULL(63)
|
||||
#define SPI_FSI_STATUS_ANY_ERROR \
|
||||
(SPI_FSI_STATUS_ERROR | SPI_FSI_STATUS_TDR_UNDERRUN | \
|
||||
SPI_FSI_STATUS_TDR_OVERRUN | SPI_FSI_STATUS_RDR_UNDERRUN | \
|
||||
SPI_FSI_STATUS_RDR_OVERRUN)
|
||||
#define SPI_FSI_PORT_CTRL 0x9
|
||||
|
||||
struct fsi_spi {
|
||||
struct device *dev; /* SPI controller device */
|
||||
struct fsi_device *fsi; /* FSI2SPI CFAM engine device */
|
||||
u32 base;
|
||||
};
|
||||
|
||||
struct fsi_spi_sequence {
|
||||
int bit;
|
||||
u64 data;
|
||||
};
|
||||
|
||||
static int fsi_spi_check_status(struct fsi_spi *ctx)
|
||||
{
|
||||
int rc;
|
||||
u32 sts;
|
||||
__be32 sts_be;
|
||||
|
||||
rc = fsi_device_read(ctx->fsi, FSI2SPI_STATUS, &sts_be,
|
||||
sizeof(sts_be));
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
sts = be32_to_cpu(sts_be);
|
||||
if (sts & FSI2SPI_STATUS_ANY_ERROR) {
|
||||
dev_err(ctx->dev, "Error with FSI2SPI interface: %08x.\n", sts);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsi_spi_read_reg(struct fsi_spi *ctx, u32 offset, u64 *value)
|
||||
{
|
||||
int rc;
|
||||
__be32 cmd_be;
|
||||
__be32 data_be;
|
||||
u32 cmd = offset + ctx->base;
|
||||
|
||||
*value = 0ULL;
|
||||
|
||||
if (cmd & FSI2SPI_CMD_WRITE)
|
||||
return -EINVAL;
|
||||
|
||||
cmd_be = cpu_to_be32(cmd);
|
||||
rc = fsi_device_write(ctx->fsi, FSI2SPI_CMD, &cmd_be, sizeof(cmd_be));
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = fsi_spi_check_status(ctx);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = fsi_device_read(ctx->fsi, FSI2SPI_DATA0, &data_be,
|
||||
sizeof(data_be));
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
*value |= (u64)be32_to_cpu(data_be) << 32;
|
||||
|
||||
rc = fsi_device_read(ctx->fsi, FSI2SPI_DATA1, &data_be,
|
||||
sizeof(data_be));
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
*value |= (u64)be32_to_cpu(data_be);
|
||||
dev_dbg(ctx->dev, "Read %02x[%016llx].\n", offset, *value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsi_spi_write_reg(struct fsi_spi *ctx, u32 offset, u64 value)
|
||||
{
|
||||
int rc;
|
||||
__be32 cmd_be;
|
||||
__be32 data_be;
|
||||
u32 cmd = offset + ctx->base;
|
||||
|
||||
if (cmd & FSI2SPI_CMD_WRITE)
|
||||
return -EINVAL;
|
||||
|
||||
dev_dbg(ctx->dev, "Write %02x[%016llx].\n", offset, value);
|
||||
|
||||
data_be = cpu_to_be32(upper_32_bits(value));
|
||||
rc = fsi_device_write(ctx->fsi, FSI2SPI_DATA0, &data_be,
|
||||
sizeof(data_be));
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
data_be = cpu_to_be32(lower_32_bits(value));
|
||||
rc = fsi_device_write(ctx->fsi, FSI2SPI_DATA1, &data_be,
|
||||
sizeof(data_be));
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
cmd_be = cpu_to_be32(cmd | FSI2SPI_CMD_WRITE);
|
||||
rc = fsi_device_write(ctx->fsi, FSI2SPI_CMD, &cmd_be, sizeof(cmd_be));
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return fsi_spi_check_status(ctx);
|
||||
}
|
||||
|
||||
static int fsi_spi_data_in(u64 in, u8 *rx, int len)
|
||||
{
|
||||
int i;
|
||||
int num_bytes = min(len, 8);
|
||||
|
||||
for (i = 0; i < num_bytes; ++i)
|
||||
rx[i] = (u8)(in >> (8 * ((num_bytes - 1) - i)));
|
||||
|
||||
return num_bytes;
|
||||
}
|
||||
|
||||
static int fsi_spi_data_out(u64 *out, const u8 *tx, int len)
|
||||
{
|
||||
int i;
|
||||
int num_bytes = min(len, 8);
|
||||
u8 *out_bytes = (u8 *)out;
|
||||
|
||||
/* Unused bytes of the tx data should be 0. */
|
||||
*out = 0ULL;
|
||||
|
||||
for (i = 0; i < num_bytes; ++i)
|
||||
out_bytes[8 - (i + 1)] = tx[i];
|
||||
|
||||
return num_bytes;
|
||||
}
|
||||
|
||||
static int fsi_spi_reset(struct fsi_spi *ctx)
|
||||
{
|
||||
int rc;
|
||||
|
||||
dev_dbg(ctx->dev, "Resetting SPI controller.\n");
|
||||
|
||||
rc = fsi_spi_write_reg(ctx, SPI_FSI_CLOCK_CFG,
|
||||
SPI_FSI_CLOCK_CFG_RESET1);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return fsi_spi_write_reg(ctx, SPI_FSI_CLOCK_CFG,
|
||||
SPI_FSI_CLOCK_CFG_RESET2);
|
||||
}
|
||||
|
||||
static int fsi_spi_sequence_add(struct fsi_spi_sequence *seq, u8 val)
|
||||
{
|
||||
/*
|
||||
* Add the next byte of instruction to the 8-byte sequence register.
|
||||
* Then decrement the counter so that the next instruction will go in
|
||||
* the right place. Return the number of "slots" left in the sequence
|
||||
* register.
|
||||
*/
|
||||
seq->data |= (u64)val << seq->bit;
|
||||
seq->bit -= 8;
|
||||
|
||||
return ((64 - seq->bit) / 8) - 2;
|
||||
}
|
||||
|
||||
static void fsi_spi_sequence_init(struct fsi_spi_sequence *seq)
|
||||
{
|
||||
seq->bit = 56;
|
||||
seq->data = 0ULL;
|
||||
}
|
||||
|
||||
static int fsi_spi_sequence_transfer(struct fsi_spi *ctx,
|
||||
struct fsi_spi_sequence *seq,
|
||||
struct spi_transfer *transfer)
|
||||
{
|
||||
int loops;
|
||||
int idx;
|
||||
int rc;
|
||||
u8 len = min(transfer->len, 8U);
|
||||
u8 rem = transfer->len % len;
|
||||
|
||||
loops = transfer->len / len;
|
||||
|
||||
if (transfer->tx_buf) {
|
||||
idx = fsi_spi_sequence_add(seq,
|
||||
SPI_FSI_SEQUENCE_SHIFT_OUT(len));
|
||||
if (rem)
|
||||
rem = SPI_FSI_SEQUENCE_SHIFT_OUT(rem);
|
||||
} else if (transfer->rx_buf) {
|
||||
idx = fsi_spi_sequence_add(seq,
|
||||
SPI_FSI_SEQUENCE_SHIFT_IN(len));
|
||||
if (rem)
|
||||
rem = SPI_FSI_SEQUENCE_SHIFT_IN(rem);
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (loops > 1) {
|
||||
fsi_spi_sequence_add(seq, SPI_FSI_SEQUENCE_BRANCH(idx));
|
||||
|
||||
if (rem)
|
||||
fsi_spi_sequence_add(seq, rem);
|
||||
|
||||
rc = fsi_spi_write_reg(ctx, SPI_FSI_COUNTER_CFG,
|
||||
SPI_FSI_COUNTER_CFG_LOOPS(loops - 1));
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsi_spi_transfer_data(struct fsi_spi *ctx,
|
||||
struct spi_transfer *transfer)
|
||||
{
|
||||
int rc = 0;
|
||||
u64 status = 0ULL;
|
||||
|
||||
if (transfer->tx_buf) {
|
||||
int nb;
|
||||
int sent = 0;
|
||||
u64 out = 0ULL;
|
||||
const u8 *tx = transfer->tx_buf;
|
||||
|
||||
while (transfer->len > sent) {
|
||||
nb = fsi_spi_data_out(&out, &tx[sent],
|
||||
(int)transfer->len - sent);
|
||||
|
||||
rc = fsi_spi_write_reg(ctx, SPI_FSI_DATA_TX, out);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
do {
|
||||
rc = fsi_spi_read_reg(ctx, SPI_FSI_STATUS,
|
||||
&status);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (status & SPI_FSI_STATUS_ANY_ERROR) {
|
||||
rc = fsi_spi_reset(ctx);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return -EREMOTEIO;
|
||||
}
|
||||
} while (status & SPI_FSI_STATUS_TDR_FULL);
|
||||
|
||||
sent += nb;
|
||||
}
|
||||
} else if (transfer->rx_buf) {
|
||||
int recv = 0;
|
||||
u64 in = 0ULL;
|
||||
u8 *rx = transfer->rx_buf;
|
||||
|
||||
while (transfer->len > recv) {
|
||||
do {
|
||||
rc = fsi_spi_read_reg(ctx, SPI_FSI_STATUS,
|
||||
&status);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (status & SPI_FSI_STATUS_ANY_ERROR) {
|
||||
rc = fsi_spi_reset(ctx);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return -EREMOTEIO;
|
||||
}
|
||||
} while (!(status & SPI_FSI_STATUS_RDR_FULL));
|
||||
|
||||
rc = fsi_spi_read_reg(ctx, SPI_FSI_DATA_RX, &in);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
recv += fsi_spi_data_in(in, &rx[recv],
|
||||
(int)transfer->len - recv);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsi_spi_transfer_init(struct fsi_spi *ctx)
|
||||
{
|
||||
int rc;
|
||||
bool reset = false;
|
||||
unsigned long end;
|
||||
u64 seq_state;
|
||||
u64 clock_cfg = 0ULL;
|
||||
u64 status = 0ULL;
|
||||
u64 wanted_clock_cfg = SPI_FSI_CLOCK_CFG_ECC_DISABLE |
|
||||
SPI_FSI_CLOCK_CFG_SCK_NO_DEL |
|
||||
FIELD_PREP(SPI_FSI_CLOCK_CFG_SCK_DIV, 4);
|
||||
|
||||
end = jiffies + msecs_to_jiffies(SPI_FSI_INIT_TIMEOUT_MS);
|
||||
do {
|
||||
if (time_after(jiffies, end))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
rc = fsi_spi_read_reg(ctx, SPI_FSI_STATUS, &status);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
seq_state = status & SPI_FSI_STATUS_SEQ_STATE;
|
||||
|
||||
if (status & (SPI_FSI_STATUS_ANY_ERROR |
|
||||
SPI_FSI_STATUS_TDR_FULL |
|
||||
SPI_FSI_STATUS_RDR_FULL)) {
|
||||
if (reset)
|
||||
return -EIO;
|
||||
|
||||
rc = fsi_spi_reset(ctx);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
reset = true;
|
||||
continue;
|
||||
}
|
||||
} while (seq_state && (seq_state != SPI_FSI_STATUS_SEQ_STATE_IDLE));
|
||||
|
||||
rc = fsi_spi_read_reg(ctx, SPI_FSI_CLOCK_CFG, &clock_cfg);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if ((clock_cfg & (SPI_FSI_CLOCK_CFG_MM_ENABLE |
|
||||
SPI_FSI_CLOCK_CFG_ECC_DISABLE |
|
||||
SPI_FSI_CLOCK_CFG_MODE |
|
||||
SPI_FSI_CLOCK_CFG_SCK_RECV_DEL |
|
||||
SPI_FSI_CLOCK_CFG_SCK_DIV)) != wanted_clock_cfg)
|
||||
rc = fsi_spi_write_reg(ctx, SPI_FSI_CLOCK_CFG,
|
||||
wanted_clock_cfg);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int fsi_spi_transfer_one_message(struct spi_controller *ctlr,
|
||||
struct spi_message *mesg)
|
||||
{
|
||||
int rc = 0;
|
||||
u8 seq_slave = SPI_FSI_SEQUENCE_SEL_SLAVE(mesg->spi->chip_select + 1);
|
||||
struct spi_transfer *transfer;
|
||||
struct fsi_spi *ctx = spi_controller_get_devdata(ctlr);
|
||||
|
||||
list_for_each_entry(transfer, &mesg->transfers, transfer_list) {
|
||||
struct fsi_spi_sequence seq;
|
||||
struct spi_transfer *next = NULL;
|
||||
|
||||
/* Sequencer must do shift out (tx) first. */
|
||||
if (!transfer->tx_buf ||
|
||||
transfer->len > SPI_FSI_MAX_TRANSFER_SIZE) {
|
||||
rc = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
dev_dbg(ctx->dev, "Start tx of %d bytes.\n", transfer->len);
|
||||
|
||||
rc = fsi_spi_transfer_init(ctx);
|
||||
if (rc < 0)
|
||||
goto error;
|
||||
|
||||
fsi_spi_sequence_init(&seq);
|
||||
fsi_spi_sequence_add(&seq, seq_slave);
|
||||
|
||||
rc = fsi_spi_sequence_transfer(ctx, &seq, transfer);
|
||||
if (rc)
|
||||
goto error;
|
||||
|
||||
if (!list_is_last(&transfer->transfer_list,
|
||||
&mesg->transfers)) {
|
||||
next = list_next_entry(transfer, transfer_list);
|
||||
|
||||
/* Sequencer can only do shift in (rx) after tx. */
|
||||
if (next->rx_buf) {
|
||||
if (next->len > SPI_FSI_MAX_TRANSFER_SIZE) {
|
||||
rc = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
dev_dbg(ctx->dev, "Sequence rx of %d bytes.\n",
|
||||
next->len);
|
||||
|
||||
rc = fsi_spi_sequence_transfer(ctx, &seq,
|
||||
next);
|
||||
if (rc)
|
||||
goto error;
|
||||
} else {
|
||||
next = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
fsi_spi_sequence_add(&seq, SPI_FSI_SEQUENCE_SEL_SLAVE(0));
|
||||
|
||||
rc = fsi_spi_write_reg(ctx, SPI_FSI_SEQUENCE, seq.data);
|
||||
if (rc)
|
||||
goto error;
|
||||
|
||||
rc = fsi_spi_transfer_data(ctx, transfer);
|
||||
if (rc)
|
||||
goto error;
|
||||
|
||||
if (next) {
|
||||
rc = fsi_spi_transfer_data(ctx, next);
|
||||
if (rc)
|
||||
goto error;
|
||||
|
||||
transfer = next;
|
||||
}
|
||||
}
|
||||
|
||||
error:
|
||||
mesg->status = rc;
|
||||
spi_finalize_current_message(ctlr);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static size_t fsi_spi_max_transfer_size(struct spi_device *spi)
|
||||
{
|
||||
return SPI_FSI_MAX_TRANSFER_SIZE;
|
||||
}
|
||||
|
||||
static int fsi_spi_probe(struct device *dev)
|
||||
{
|
||||
int rc;
|
||||
u32 root_ctrl_8;
|
||||
struct device_node *np;
|
||||
int num_controllers_registered = 0;
|
||||
struct fsi_device *fsi = to_fsi_dev(dev);
|
||||
|
||||
/*
|
||||
* Check the SPI mux before attempting to probe. If the mux isn't set
|
||||
* then the SPI controllers can't access their slave devices.
|
||||
*/
|
||||
rc = fsi_slave_read(fsi->slave, FSI_MBOX_ROOT_CTRL_8, &root_ctrl_8,
|
||||
sizeof(root_ctrl_8));
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (!root_ctrl_8) {
|
||||
dev_dbg(dev, "SPI mux not set, aborting probe.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
for_each_available_child_of_node(dev->of_node, np) {
|
||||
u32 base;
|
||||
struct fsi_spi *ctx;
|
||||
struct spi_controller *ctlr;
|
||||
|
||||
if (of_property_read_u32(np, "reg", &base))
|
||||
continue;
|
||||
|
||||
ctlr = spi_alloc_master(dev, sizeof(*ctx));
|
||||
if (!ctlr)
|
||||
break;
|
||||
|
||||
ctlr->dev.of_node = np;
|
||||
ctlr->num_chipselect = of_get_available_child_count(np) ?: 1;
|
||||
ctlr->flags = SPI_CONTROLLER_HALF_DUPLEX;
|
||||
ctlr->max_transfer_size = fsi_spi_max_transfer_size;
|
||||
ctlr->transfer_one_message = fsi_spi_transfer_one_message;
|
||||
|
||||
ctx = spi_controller_get_devdata(ctlr);
|
||||
ctx->dev = &ctlr->dev;
|
||||
ctx->fsi = fsi;
|
||||
ctx->base = base + SPI_FSI_BASE;
|
||||
|
||||
rc = devm_spi_register_controller(dev, ctlr);
|
||||
if (rc)
|
||||
spi_controller_put(ctlr);
|
||||
else
|
||||
num_controllers_registered++;
|
||||
}
|
||||
|
||||
if (!num_controllers_registered)
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct fsi_device_id fsi_spi_ids[] = {
|
||||
{ FSI_ENGID_SPI, FSI_VERSION_ANY },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(fsi, fsi_spi_ids);
|
||||
|
||||
static struct fsi_driver fsi_spi_driver = {
|
||||
.id_table = fsi_spi_ids,
|
||||
.drv = {
|
||||
.name = "spi-fsi",
|
||||
.bus = &fsi_bus_type,
|
||||
.probe = fsi_spi_probe,
|
||||
},
|
||||
};
|
||||
module_fsi_driver(fsi_spi_driver);
|
||||
|
||||
MODULE_AUTHOR("Eddie James <eajames@linux.ibm.com>");
|
||||
MODULE_DESCRIPTION("FSI attached SPI controller");
|
||||
MODULE_LICENSE("GPL");
|
File diff suppressed because it is too large
Load Diff
|
@ -86,8 +86,6 @@
|
|||
#define TCR_RXMSK BIT(19)
|
||||
#define TCR_TXMSK BIT(18)
|
||||
|
||||
static int clkdivs[] = {1, 2, 4, 8, 16, 32, 64, 128};
|
||||
|
||||
struct lpspi_config {
|
||||
u8 bpw;
|
||||
u8 chip_select;
|
||||
|
@ -125,7 +123,7 @@ struct fsl_lpspi_data {
|
|||
struct completion dma_rx_completion;
|
||||
struct completion dma_tx_completion;
|
||||
|
||||
int chipselect[0];
|
||||
int chipselect[];
|
||||
};
|
||||
|
||||
static const struct of_device_id fsl_lpspi_dt_ids[] = {
|
||||
|
@ -331,15 +329,14 @@ static int fsl_lpspi_set_bitrate(struct fsl_lpspi_data *fsl_lpspi)
|
|||
}
|
||||
|
||||
for (prescale = 0; prescale < 8; prescale++) {
|
||||
scldiv = perclk_rate /
|
||||
(clkdivs[prescale] * config.speed_hz) - 2;
|
||||
scldiv = perclk_rate / config.speed_hz / (1 << prescale) - 2;
|
||||
if (scldiv < 256) {
|
||||
fsl_lpspi->config.prescale = prescale;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (prescale == 8 && scldiv >= 256)
|
||||
if (scldiv >= 256)
|
||||
return -EINVAL;
|
||||
|
||||
writel(scldiv | (scldiv << 8) | ((scldiv >> 1) << 16),
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/qcom-geni-se.h>
|
||||
|
@ -536,6 +535,7 @@ static int spi_geni_probe(struct platform_device *pdev)
|
|||
struct spi_geni_master *mas;
|
||||
void __iomem *base;
|
||||
struct clk *clk;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
|
@ -545,28 +545,25 @@ static int spi_geni_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
clk = devm_clk_get(&pdev->dev, "se");
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(&pdev->dev, "Err getting SE Core clk %ld\n",
|
||||
PTR_ERR(clk));
|
||||
clk = devm_clk_get(dev, "se");
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
|
||||
spi = spi_alloc_master(&pdev->dev, sizeof(*mas));
|
||||
spi = spi_alloc_master(dev, sizeof(*mas));
|
||||
if (!spi)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, spi);
|
||||
mas = spi_master_get_devdata(spi);
|
||||
mas->irq = irq;
|
||||
mas->dev = &pdev->dev;
|
||||
mas->se.dev = &pdev->dev;
|
||||
mas->se.wrapper = dev_get_drvdata(pdev->dev.parent);
|
||||
mas->dev = dev;
|
||||
mas->se.dev = dev;
|
||||
mas->se.wrapper = dev_get_drvdata(dev->parent);
|
||||
mas->se.base = base;
|
||||
mas->se.clk = clk;
|
||||
|
||||
spi->bus_num = -1;
|
||||
spi->dev.of_node = pdev->dev.of_node;
|
||||
spi->dev.of_node = dev->of_node;
|
||||
spi->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP | SPI_CS_HIGH;
|
||||
spi->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
|
||||
spi->num_chipselect = 4;
|
||||
|
@ -579,14 +576,13 @@ static int spi_geni_probe(struct platform_device *pdev)
|
|||
|
||||
init_completion(&mas->xfer_done);
|
||||
spin_lock_init(&mas->lock);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
ret = spi_geni_init(mas);
|
||||
if (ret)
|
||||
goto spi_geni_probe_runtime_disable;
|
||||
|
||||
ret = request_irq(mas->irq, geni_spi_isr,
|
||||
IRQF_TRIGGER_HIGH, "spi_geni", spi);
|
||||
ret = request_irq(mas->irq, geni_spi_isr, 0, dev_name(dev), spi);
|
||||
if (ret)
|
||||
goto spi_geni_probe_runtime_disable;
|
||||
|
||||
|
@ -598,7 +594,7 @@ static int spi_geni_probe(struct platform_device *pdev)
|
|||
spi_geni_probe_free_irq:
|
||||
free_irq(mas->irq, spi);
|
||||
spi_geni_probe_runtime_disable:
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_disable(dev);
|
||||
spi_master_put(spi);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
@ -17,6 +18,12 @@
|
|||
#define HISI_SFC_V3XX_VERSION (0x1f8)
|
||||
|
||||
#define HISI_SFC_V3XX_CMD_CFG (0x300)
|
||||
#define HISI_SFC_V3XX_CMD_CFG_DUAL_IN_DUAL_OUT (1 << 17)
|
||||
#define HISI_SFC_V3XX_CMD_CFG_DUAL_IO (2 << 17)
|
||||
#define HISI_SFC_V3XX_CMD_CFG_FULL_DIO (3 << 17)
|
||||
#define HISI_SFC_V3XX_CMD_CFG_QUAD_IN_QUAD_OUT (5 << 17)
|
||||
#define HISI_SFC_V3XX_CMD_CFG_QUAD_IO (6 << 17)
|
||||
#define HISI_SFC_V3XX_CMD_CFG_FULL_QIO (7 << 17)
|
||||
#define HISI_SFC_V3XX_CMD_CFG_DATA_CNT_OFF 9
|
||||
#define HISI_SFC_V3XX_CMD_CFG_RW_MSK BIT(8)
|
||||
#define HISI_SFC_V3XX_CMD_CFG_DATA_EN_MSK BIT(7)
|
||||
|
@ -161,6 +168,43 @@ static int hisi_sfc_v3xx_generic_exec_op(struct hisi_sfc_v3xx_host *host,
|
|||
if (op->addr.nbytes)
|
||||
config |= HISI_SFC_V3XX_CMD_CFG_ADDR_EN_MSK;
|
||||
|
||||
switch (op->data.buswidth) {
|
||||
case 0 ... 1:
|
||||
break;
|
||||
case 2:
|
||||
if (op->addr.buswidth <= 1) {
|
||||
config |= HISI_SFC_V3XX_CMD_CFG_DUAL_IN_DUAL_OUT;
|
||||
} else if (op->addr.buswidth == 2) {
|
||||
if (op->cmd.buswidth <= 1) {
|
||||
config |= HISI_SFC_V3XX_CMD_CFG_DUAL_IO;
|
||||
} else if (op->cmd.buswidth == 2) {
|
||||
config |= HISI_SFC_V3XX_CMD_CFG_FULL_DIO;
|
||||
} else {
|
||||
return -EIO;
|
||||
}
|
||||
} else {
|
||||
return -EIO;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if (op->addr.buswidth <= 1) {
|
||||
config |= HISI_SFC_V3XX_CMD_CFG_QUAD_IN_QUAD_OUT;
|
||||
} else if (op->addr.buswidth == 4) {
|
||||
if (op->cmd.buswidth <= 1) {
|
||||
config |= HISI_SFC_V3XX_CMD_CFG_QUAD_IO;
|
||||
} else if (op->cmd.buswidth == 4) {
|
||||
config |= HISI_SFC_V3XX_CMD_CFG_FULL_QIO;
|
||||
} else {
|
||||
return -EIO;
|
||||
}
|
||||
} else {
|
||||
return -EIO;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (op->data.dir != SPI_MEM_NO_DATA) {
|
||||
config |= (len - 1) << HISI_SFC_V3XX_CMD_CFG_DATA_CNT_OFF;
|
||||
config |= HISI_SFC_V3XX_CMD_CFG_DATA_EN_MSK;
|
||||
|
@ -207,6 +251,44 @@ static const struct spi_controller_mem_ops hisi_sfc_v3xx_mem_ops = {
|
|||
.exec_op = hisi_sfc_v3xx_exec_op,
|
||||
};
|
||||
|
||||
static int hisi_sfc_v3xx_buswidth_override_bits;
|
||||
|
||||
/*
|
||||
* ACPI FW does not allow us to currently set the device buswidth, so quirk it
|
||||
* depending on the board.
|
||||
*/
|
||||
static int __init hisi_sfc_v3xx_dmi_quirk(const struct dmi_system_id *d)
|
||||
{
|
||||
hisi_sfc_v3xx_buswidth_override_bits = SPI_RX_QUAD | SPI_TX_QUAD;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dmi_system_id hisi_sfc_v3xx_dmi_quirk_table[] = {
|
||||
{
|
||||
.callback = hisi_sfc_v3xx_dmi_quirk,
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Huawei"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "D06"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.callback = hisi_sfc_v3xx_dmi_quirk,
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Huawei"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "TaiShan 2280 V2"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.callback = hisi_sfc_v3xx_dmi_quirk,
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Huawei"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "TaiShan 200 (Model 2280)"),
|
||||
},
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
static int hisi_sfc_v3xx_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
|
@ -222,6 +304,8 @@ static int hisi_sfc_v3xx_probe(struct platform_device *pdev)
|
|||
ctlr->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD |
|
||||
SPI_TX_DUAL | SPI_TX_QUAD;
|
||||
|
||||
ctlr->buswidth_override_bits = hisi_sfc_v3xx_buswidth_override_bits;
|
||||
|
||||
host = spi_controller_get_devdata(ctlr);
|
||||
host->dev = dev;
|
||||
|
||||
|
@ -277,7 +361,20 @@ static struct platform_driver hisi_sfc_v3xx_spi_driver = {
|
|||
.probe = hisi_sfc_v3xx_probe,
|
||||
};
|
||||
|
||||
module_platform_driver(hisi_sfc_v3xx_spi_driver);
|
||||
static int __init hisi_sfc_v3xx_spi_init(void)
|
||||
{
|
||||
dmi_check_system(hisi_sfc_v3xx_dmi_quirk_table);
|
||||
|
||||
return platform_driver_register(&hisi_sfc_v3xx_spi_driver);
|
||||
}
|
||||
|
||||
static void __exit hisi_sfc_v3xx_spi_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&hisi_sfc_v3xx_spi_driver);
|
||||
}
|
||||
|
||||
module_init(hisi_sfc_v3xx_spi_init);
|
||||
module_exit(hisi_sfc_v3xx_spi_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("John Garry <john.garry@huawei.com>");
|
||||
|
|
|
@ -418,12 +418,13 @@ int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
|
|||
struct spi_controller *ctlr = mem->spi->controller;
|
||||
size_t len;
|
||||
|
||||
len = sizeof(op->cmd.opcode) + op->addr.nbytes + op->dummy.nbytes;
|
||||
|
||||
if (ctlr->mem_ops && ctlr->mem_ops->adjust_op_size)
|
||||
return ctlr->mem_ops->adjust_op_size(mem, op);
|
||||
|
||||
if (!ctlr->mem_ops || !ctlr->mem_ops->exec_op) {
|
||||
len = sizeof(op->cmd.opcode) + op->addr.nbytes +
|
||||
op->dummy.nbytes;
|
||||
|
||||
if (len > spi_max_transfer_size(mem->spi))
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -487,7 +488,7 @@ static ssize_t spi_mem_no_dirmap_write(struct spi_mem_dirmap_desc *desc,
|
|||
* This function is creating a direct mapping descriptor which can then be used
|
||||
* to access the memory using spi_mem_dirmap_read() or spi_mem_dirmap_write().
|
||||
* If the SPI controller driver does not support direct mapping, this function
|
||||
* fallback to an implementation using spi_mem_exec_op(), so that the caller
|
||||
* falls back to an implementation using spi_mem_exec_op(), so that the caller
|
||||
* doesn't have to bother implementing a fallback on his own.
|
||||
*
|
||||
* Return: a valid pointer in case of success, and ERR_PTR() otherwise.
|
||||
|
|
|
@ -9,11 +9,13 @@
|
|||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/types.h>
|
||||
|
@ -33,7 +35,6 @@
|
|||
* to have a CS go down over the full transfer
|
||||
*/
|
||||
|
||||
#define SPICC_MAX_FREQ 30000000
|
||||
#define SPICC_MAX_BURST 128
|
||||
|
||||
/* Register Map */
|
||||
|
@ -105,7 +106,21 @@
|
|||
#define SPICC_SWAP_RO BIT(14) /* RX FIFO Data Swap Read-Only */
|
||||
#define SPICC_SWAP_W1 BIT(15) /* RX FIFO Data Swap Write-Only */
|
||||
#define SPICC_DLYCTL_RO_MASK GENMASK(20, 15) /* Delay Control Read-Only */
|
||||
#define SPICC_DLYCTL_W1_MASK GENMASK(21, 16) /* Delay Control Write-Only */
|
||||
#define SPICC_MO_DELAY_MASK GENMASK(17, 16) /* Master Output Delay */
|
||||
#define SPICC_MO_NO_DELAY 0
|
||||
#define SPICC_MO_DELAY_1_CYCLE 1
|
||||
#define SPICC_MO_DELAY_2_CYCLE 2
|
||||
#define SPICC_MO_DELAY_3_CYCLE 3
|
||||
#define SPICC_MI_DELAY_MASK GENMASK(19, 18) /* Master Input Delay */
|
||||
#define SPICC_MI_NO_DELAY 0
|
||||
#define SPICC_MI_DELAY_1_CYCLE 1
|
||||
#define SPICC_MI_DELAY_2_CYCLE 2
|
||||
#define SPICC_MI_DELAY_3_CYCLE 3
|
||||
#define SPICC_MI_CAP_DELAY_MASK GENMASK(21, 20) /* Master Capture Delay */
|
||||
#define SPICC_CAP_AHEAD_2_CYCLE 0
|
||||
#define SPICC_CAP_AHEAD_1_CYCLE 1
|
||||
#define SPICC_CAP_NO_DELAY 2
|
||||
#define SPICC_CAP_DELAY_1_CYCLE 3
|
||||
#define SPICC_FIFORST_RO_MASK GENMASK(22, 21) /* FIFO Softreset Read-Only */
|
||||
#define SPICC_FIFORST_W1_MASK GENMASK(23, 22) /* FIFO Softreset Write-Only */
|
||||
|
||||
|
@ -113,31 +128,59 @@
|
|||
|
||||
#define SPICC_DWADDR 0x24 /* Write Address of DMA */
|
||||
|
||||
#define SPICC_ENH_CTL0 0x38 /* Enhanced Feature */
|
||||
#define SPICC_ENH_CLK_CS_DELAY_MASK GENMASK(15, 0)
|
||||
#define SPICC_ENH_DATARATE_MASK GENMASK(23, 16)
|
||||
#define SPICC_ENH_DATARATE_EN BIT(24)
|
||||
#define SPICC_ENH_MOSI_OEN BIT(25)
|
||||
#define SPICC_ENH_CLK_OEN BIT(26)
|
||||
#define SPICC_ENH_CS_OEN BIT(27)
|
||||
#define SPICC_ENH_CLK_CS_DELAY_EN BIT(28)
|
||||
#define SPICC_ENH_MAIN_CLK_AO BIT(29)
|
||||
|
||||
#define writel_bits_relaxed(mask, val, addr) \
|
||||
writel_relaxed((readl_relaxed(addr) & ~(mask)) | (val), addr)
|
||||
|
||||
#define SPICC_BURST_MAX 16
|
||||
#define SPICC_FIFO_HALF 10
|
||||
struct meson_spicc_data {
|
||||
unsigned int max_speed_hz;
|
||||
unsigned int min_speed_hz;
|
||||
unsigned int fifo_size;
|
||||
bool has_oen;
|
||||
bool has_enhance_clk_div;
|
||||
bool has_pclk;
|
||||
};
|
||||
|
||||
struct meson_spicc_device {
|
||||
struct spi_master *master;
|
||||
struct platform_device *pdev;
|
||||
void __iomem *base;
|
||||
struct clk *core;
|
||||
struct clk *pclk;
|
||||
struct clk *clk;
|
||||
struct spi_message *message;
|
||||
struct spi_transfer *xfer;
|
||||
const struct meson_spicc_data *data;
|
||||
u8 *tx_buf;
|
||||
u8 *rx_buf;
|
||||
unsigned int bytes_per_word;
|
||||
unsigned long tx_remain;
|
||||
unsigned long txb_remain;
|
||||
unsigned long rx_remain;
|
||||
unsigned long rxb_remain;
|
||||
unsigned long xfer_remain;
|
||||
bool is_burst_end;
|
||||
bool is_last_burst;
|
||||
};
|
||||
|
||||
static void meson_spicc_oen_enable(struct meson_spicc_device *spicc)
|
||||
{
|
||||
u32 conf;
|
||||
|
||||
if (!spicc->data->has_oen)
|
||||
return;
|
||||
|
||||
conf = readl_relaxed(spicc->base + SPICC_ENH_CTL0) |
|
||||
SPICC_ENH_MOSI_OEN | SPICC_ENH_CLK_OEN | SPICC_ENH_CS_OEN;
|
||||
|
||||
writel_relaxed(conf, spicc->base + SPICC_ENH_CTL0);
|
||||
}
|
||||
|
||||
static inline bool meson_spicc_txfull(struct meson_spicc_device *spicc)
|
||||
{
|
||||
return !!FIELD_GET(SPICC_TF,
|
||||
|
@ -146,7 +189,7 @@ static inline bool meson_spicc_txfull(struct meson_spicc_device *spicc)
|
|||
|
||||
static inline bool meson_spicc_rxready(struct meson_spicc_device *spicc)
|
||||
{
|
||||
return FIELD_GET(SPICC_RH | SPICC_RR | SPICC_RF_EN,
|
||||
return FIELD_GET(SPICC_RH | SPICC_RR | SPICC_RF,
|
||||
readl_relaxed(spicc->base + SPICC_STATREG));
|
||||
}
|
||||
|
||||
|
@ -201,34 +244,22 @@ static inline void meson_spicc_tx(struct meson_spicc_device *spicc)
|
|||
spicc->base + SPICC_TXDATA);
|
||||
}
|
||||
|
||||
static inline u32 meson_spicc_setup_rx_irq(struct meson_spicc_device *spicc,
|
||||
u32 irq_ctrl)
|
||||
static inline void meson_spicc_setup_burst(struct meson_spicc_device *spicc)
|
||||
{
|
||||
if (spicc->rx_remain > SPICC_FIFO_HALF)
|
||||
irq_ctrl |= SPICC_RH_EN;
|
||||
else
|
||||
irq_ctrl |= SPICC_RR_EN;
|
||||
|
||||
return irq_ctrl;
|
||||
}
|
||||
|
||||
static inline void meson_spicc_setup_burst(struct meson_spicc_device *spicc,
|
||||
unsigned int burst_len)
|
||||
{
|
||||
unsigned int burst_len = min_t(unsigned int,
|
||||
spicc->xfer_remain /
|
||||
spicc->bytes_per_word,
|
||||
spicc->data->fifo_size);
|
||||
/* Setup Xfer variables */
|
||||
spicc->tx_remain = burst_len;
|
||||
spicc->rx_remain = burst_len;
|
||||
spicc->xfer_remain -= burst_len * spicc->bytes_per_word;
|
||||
spicc->is_burst_end = false;
|
||||
if (burst_len < SPICC_BURST_MAX || !spicc->xfer_remain)
|
||||
spicc->is_last_burst = true;
|
||||
else
|
||||
spicc->is_last_burst = false;
|
||||
|
||||
/* Setup burst length */
|
||||
writel_bits_relaxed(SPICC_BURSTLENGTH_MASK,
|
||||
FIELD_PREP(SPICC_BURSTLENGTH_MASK,
|
||||
burst_len),
|
||||
burst_len - 1),
|
||||
spicc->base + SPICC_CONREG);
|
||||
|
||||
/* Fill TX FIFO */
|
||||
|
@ -238,97 +269,71 @@ static inline void meson_spicc_setup_burst(struct meson_spicc_device *spicc,
|
|||
static irqreturn_t meson_spicc_irq(int irq, void *data)
|
||||
{
|
||||
struct meson_spicc_device *spicc = (void *) data;
|
||||
u32 ctrl = readl_relaxed(spicc->base + SPICC_INTREG);
|
||||
u32 stat = readl_relaxed(spicc->base + SPICC_STATREG) & ctrl;
|
||||
|
||||
ctrl &= ~(SPICC_RH_EN | SPICC_RR_EN);
|
||||
writel_bits_relaxed(SPICC_TC, SPICC_TC, spicc->base + SPICC_STATREG);
|
||||
|
||||
/* Empty RX FIFO */
|
||||
meson_spicc_rx(spicc);
|
||||
|
||||
/* Enable TC interrupt since we transferred everything */
|
||||
if (!spicc->tx_remain && !spicc->rx_remain) {
|
||||
spicc->is_burst_end = true;
|
||||
if (!spicc->xfer_remain) {
|
||||
/* Disable all IRQs */
|
||||
writel(0, spicc->base + SPICC_INTREG);
|
||||
|
||||
/* Enable TC interrupt */
|
||||
ctrl |= SPICC_TC_EN;
|
||||
spi_finalize_current_transfer(spicc->master);
|
||||
|
||||
/* Reload IRQ status */
|
||||
stat = readl_relaxed(spicc->base + SPICC_STATREG) & ctrl;
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* Check transfer complete */
|
||||
if ((stat & SPICC_TC) && spicc->is_burst_end) {
|
||||
unsigned int burst_len;
|
||||
/* Setup burst */
|
||||
meson_spicc_setup_burst(spicc);
|
||||
|
||||
/* Clear TC bit */
|
||||
writel_relaxed(SPICC_TC, spicc->base + SPICC_STATREG);
|
||||
|
||||
/* Disable TC interrupt */
|
||||
ctrl &= ~SPICC_TC_EN;
|
||||
|
||||
if (spicc->is_last_burst) {
|
||||
/* Disable all IRQs */
|
||||
writel(0, spicc->base + SPICC_INTREG);
|
||||
|
||||
spi_finalize_current_transfer(spicc->master);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
burst_len = min_t(unsigned int,
|
||||
spicc->xfer_remain / spicc->bytes_per_word,
|
||||
SPICC_BURST_MAX);
|
||||
|
||||
/* Setup burst */
|
||||
meson_spicc_setup_burst(spicc, burst_len);
|
||||
|
||||
/* Restart burst */
|
||||
writel_bits_relaxed(SPICC_XCH, SPICC_XCH,
|
||||
spicc->base + SPICC_CONREG);
|
||||
}
|
||||
|
||||
/* Setup RX interrupt trigger */
|
||||
ctrl = meson_spicc_setup_rx_irq(spicc, ctrl);
|
||||
|
||||
/* Reconfigure interrupts */
|
||||
writel(ctrl, spicc->base + SPICC_INTREG);
|
||||
/* Start burst */
|
||||
writel_bits_relaxed(SPICC_XCH, SPICC_XCH, spicc->base + SPICC_CONREG);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static u32 meson_spicc_setup_speed(struct meson_spicc_device *spicc, u32 conf,
|
||||
u32 speed)
|
||||
static void meson_spicc_auto_io_delay(struct meson_spicc_device *spicc)
|
||||
{
|
||||
unsigned long parent, value;
|
||||
unsigned int i, div;
|
||||
u32 div, hz;
|
||||
u32 mi_delay, cap_delay;
|
||||
u32 conf;
|
||||
|
||||
parent = clk_get_rate(spicc->core);
|
||||
|
||||
/* Find closest inferior/equal possible speed */
|
||||
for (i = 0 ; i < 7 ; ++i) {
|
||||
/* 2^(data_rate+2) */
|
||||
value = parent >> (i + 2);
|
||||
|
||||
if (value <= speed)
|
||||
break;
|
||||
if (spicc->data->has_enhance_clk_div) {
|
||||
div = FIELD_GET(SPICC_ENH_DATARATE_MASK,
|
||||
readl_relaxed(spicc->base + SPICC_ENH_CTL0));
|
||||
div++;
|
||||
div <<= 1;
|
||||
} else {
|
||||
div = FIELD_GET(SPICC_DATARATE_MASK,
|
||||
readl_relaxed(spicc->base + SPICC_CONREG));
|
||||
div += 2;
|
||||
div = 1 << div;
|
||||
}
|
||||
|
||||
/* If provided speed it lower than max divider, use max divider */
|
||||
if (i > 7) {
|
||||
div = 7;
|
||||
dev_warn_once(&spicc->pdev->dev, "unable to get close to speed %u\n",
|
||||
speed);
|
||||
} else
|
||||
div = i;
|
||||
mi_delay = SPICC_MI_NO_DELAY;
|
||||
cap_delay = SPICC_CAP_AHEAD_2_CYCLE;
|
||||
hz = clk_get_rate(spicc->clk);
|
||||
|
||||
dev_dbg(&spicc->pdev->dev, "parent %lu, speed %u -> %lu (%u)\n",
|
||||
parent, speed, value, div);
|
||||
if (hz >= 100000000)
|
||||
cap_delay = SPICC_CAP_DELAY_1_CYCLE;
|
||||
else if (hz >= 80000000)
|
||||
cap_delay = SPICC_CAP_NO_DELAY;
|
||||
else if (hz >= 40000000)
|
||||
cap_delay = SPICC_CAP_AHEAD_1_CYCLE;
|
||||
else if (div >= 16)
|
||||
mi_delay = SPICC_MI_DELAY_3_CYCLE;
|
||||
else if (div >= 8)
|
||||
mi_delay = SPICC_MI_DELAY_2_CYCLE;
|
||||
else if (div >= 6)
|
||||
mi_delay = SPICC_MI_DELAY_1_CYCLE;
|
||||
|
||||
conf &= ~SPICC_DATARATE_MASK;
|
||||
conf |= FIELD_PREP(SPICC_DATARATE_MASK, div);
|
||||
|
||||
return conf;
|
||||
conf = readl_relaxed(spicc->base + SPICC_TESTREG);
|
||||
conf &= ~(SPICC_MO_DELAY_MASK | SPICC_MI_DELAY_MASK
|
||||
| SPICC_MI_CAP_DELAY_MASK);
|
||||
conf |= FIELD_PREP(SPICC_MI_DELAY_MASK, mi_delay);
|
||||
conf |= FIELD_PREP(SPICC_MI_CAP_DELAY_MASK, cap_delay);
|
||||
writel_relaxed(conf, spicc->base + SPICC_TESTREG);
|
||||
}
|
||||
|
||||
static void meson_spicc_setup_xfer(struct meson_spicc_device *spicc,
|
||||
|
@ -339,9 +344,6 @@ static void meson_spicc_setup_xfer(struct meson_spicc_device *spicc,
|
|||
/* Read original configuration */
|
||||
conf = conf_orig = readl_relaxed(spicc->base + SPICC_CONREG);
|
||||
|
||||
/* Select closest divider */
|
||||
conf = meson_spicc_setup_speed(spicc, conf, xfer->speed_hz);
|
||||
|
||||
/* Setup word width */
|
||||
conf &= ~SPICC_BITLENGTH_MASK;
|
||||
conf |= FIELD_PREP(SPICC_BITLENGTH_MASK,
|
||||
|
@ -350,6 +352,32 @@ static void meson_spicc_setup_xfer(struct meson_spicc_device *spicc,
|
|||
/* Ignore if unchanged */
|
||||
if (conf != conf_orig)
|
||||
writel_relaxed(conf, spicc->base + SPICC_CONREG);
|
||||
|
||||
clk_set_rate(spicc->clk, xfer->speed_hz);
|
||||
|
||||
meson_spicc_auto_io_delay(spicc);
|
||||
|
||||
writel_relaxed(0, spicc->base + SPICC_DMAREG);
|
||||
}
|
||||
|
||||
static void meson_spicc_reset_fifo(struct meson_spicc_device *spicc)
|
||||
{
|
||||
u32 data;
|
||||
|
||||
if (spicc->data->has_oen)
|
||||
writel_bits_relaxed(SPICC_ENH_MAIN_CLK_AO,
|
||||
SPICC_ENH_MAIN_CLK_AO,
|
||||
spicc->base + SPICC_ENH_CTL0);
|
||||
|
||||
writel_bits_relaxed(SPICC_FIFORST_W1_MASK, SPICC_FIFORST_W1_MASK,
|
||||
spicc->base + SPICC_TESTREG);
|
||||
|
||||
while (meson_spicc_rxready(spicc))
|
||||
data = readl_relaxed(spicc->base + SPICC_RXDATA);
|
||||
|
||||
if (spicc->data->has_oen)
|
||||
writel_bits_relaxed(SPICC_ENH_MAIN_CLK_AO, 0,
|
||||
spicc->base + SPICC_ENH_CTL0);
|
||||
}
|
||||
|
||||
static int meson_spicc_transfer_one(struct spi_master *master,
|
||||
|
@ -357,8 +385,6 @@ static int meson_spicc_transfer_one(struct spi_master *master,
|
|||
struct spi_transfer *xfer)
|
||||
{
|
||||
struct meson_spicc_device *spicc = spi_master_get_devdata(master);
|
||||
unsigned int burst_len;
|
||||
u32 irq = 0;
|
||||
|
||||
/* Store current transfer */
|
||||
spicc->xfer = xfer;
|
||||
|
@ -372,22 +398,22 @@ static int meson_spicc_transfer_one(struct spi_master *master,
|
|||
spicc->bytes_per_word =
|
||||
DIV_ROUND_UP(spicc->xfer->bits_per_word, 8);
|
||||
|
||||
if (xfer->len % spicc->bytes_per_word)
|
||||
return -EINVAL;
|
||||
|
||||
/* Setup transfer parameters */
|
||||
meson_spicc_setup_xfer(spicc, xfer);
|
||||
|
||||
burst_len = min_t(unsigned int,
|
||||
spicc->xfer_remain / spicc->bytes_per_word,
|
||||
SPICC_BURST_MAX);
|
||||
meson_spicc_reset_fifo(spicc);
|
||||
|
||||
meson_spicc_setup_burst(spicc, burst_len);
|
||||
|
||||
irq = meson_spicc_setup_rx_irq(spicc, irq);
|
||||
/* Setup burst */
|
||||
meson_spicc_setup_burst(spicc);
|
||||
|
||||
/* Start burst */
|
||||
writel_bits_relaxed(SPICC_XCH, SPICC_XCH, spicc->base + SPICC_CONREG);
|
||||
|
||||
/* Enable interrupts */
|
||||
writel_relaxed(irq, spicc->base + SPICC_INTREG);
|
||||
writel_relaxed(SPICC_TC_EN, spicc->base + SPICC_INTREG);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -444,7 +470,7 @@ static int meson_spicc_prepare_message(struct spi_master *master,
|
|||
/* Setup no wait cycles by default */
|
||||
writel_relaxed(0, spicc->base + SPICC_PERIODREG);
|
||||
|
||||
writel_bits_relaxed(BIT(24), BIT(24), spicc->base + SPICC_TESTREG);
|
||||
writel_bits_relaxed(SPICC_LBC_W1, 0, spicc->base + SPICC_TESTREG);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -456,9 +482,6 @@ static int meson_spicc_unprepare_transfer(struct spi_master *master)
|
|||
/* Disable all IRQs */
|
||||
writel(0, spicc->base + SPICC_INTREG);
|
||||
|
||||
/* Disable controller */
|
||||
writel_bits_relaxed(SPICC_ENABLE, 0, spicc->base + SPICC_CONREG);
|
||||
|
||||
device_reset_optional(&spicc->pdev->dev);
|
||||
|
||||
return 0;
|
||||
|
@ -477,11 +500,167 @@ static void meson_spicc_cleanup(struct spi_device *spi)
|
|||
spi->controller_state = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* The Clock Mux
|
||||
* x-----------------x x------------x x------\
|
||||
* |---| pow2 fixed div |---| pow2 div |----| |
|
||||
* | x-----------------x x------------x | |
|
||||
* src ---| | mux |-- out
|
||||
* | x-----------------x x------------x | |
|
||||
* |---| enh fixed div |---| enh div |0---| |
|
||||
* x-----------------x x------------x x------/
|
||||
*
|
||||
* Clk path for GX series:
|
||||
* src -> pow2 fixed div -> pow2 div -> out
|
||||
*
|
||||
* Clk path for AXG series:
|
||||
* src -> pow2 fixed div -> pow2 div -> mux -> out
|
||||
* src -> enh fixed div -> enh div -> mux -> out
|
||||
*
|
||||
* Clk path for G12A series:
|
||||
* pclk -> pow2 fixed div -> pow2 div -> mux -> out
|
||||
* pclk -> enh fixed div -> enh div -> mux -> out
|
||||
*/
|
||||
|
||||
static int meson_spicc_clk_init(struct meson_spicc_device *spicc)
|
||||
{
|
||||
struct device *dev = &spicc->pdev->dev;
|
||||
struct clk_fixed_factor *pow2_fixed_div, *enh_fixed_div;
|
||||
struct clk_divider *pow2_div, *enh_div;
|
||||
struct clk_mux *mux;
|
||||
struct clk_init_data init;
|
||||
struct clk *clk;
|
||||
struct clk_parent_data parent_data[2];
|
||||
char name[64];
|
||||
|
||||
memset(&init, 0, sizeof(init));
|
||||
memset(&parent_data, 0, sizeof(parent_data));
|
||||
|
||||
init.parent_data = parent_data;
|
||||
|
||||
/* algorithm for pow2 div: rate = freq / 4 / (2 ^ N) */
|
||||
|
||||
pow2_fixed_div = devm_kzalloc(dev, sizeof(*pow2_fixed_div), GFP_KERNEL);
|
||||
if (!pow2_fixed_div)
|
||||
return -ENOMEM;
|
||||
|
||||
snprintf(name, sizeof(name), "%s#pow2_fixed_div", dev_name(dev));
|
||||
init.name = name;
|
||||
init.ops = &clk_fixed_factor_ops;
|
||||
init.flags = 0;
|
||||
if (spicc->data->has_pclk)
|
||||
parent_data[0].hw = __clk_get_hw(spicc->pclk);
|
||||
else
|
||||
parent_data[0].hw = __clk_get_hw(spicc->core);
|
||||
init.num_parents = 1;
|
||||
|
||||
pow2_fixed_div->mult = 1,
|
||||
pow2_fixed_div->div = 4,
|
||||
pow2_fixed_div->hw.init = &init;
|
||||
|
||||
clk = devm_clk_register(dev, &pow2_fixed_div->hw);
|
||||
if (WARN_ON(IS_ERR(clk)))
|
||||
return PTR_ERR(clk);
|
||||
|
||||
pow2_div = devm_kzalloc(dev, sizeof(*pow2_div), GFP_KERNEL);
|
||||
if (!pow2_div)
|
||||
return -ENOMEM;
|
||||
|
||||
snprintf(name, sizeof(name), "%s#pow2_div", dev_name(dev));
|
||||
init.name = name;
|
||||
init.ops = &clk_divider_ops;
|
||||
init.flags = CLK_SET_RATE_PARENT;
|
||||
parent_data[0].hw = &pow2_fixed_div->hw;
|
||||
init.num_parents = 1;
|
||||
|
||||
pow2_div->shift = 16,
|
||||
pow2_div->width = 3,
|
||||
pow2_div->flags = CLK_DIVIDER_POWER_OF_TWO,
|
||||
pow2_div->reg = spicc->base + SPICC_CONREG;
|
||||
pow2_div->hw.init = &init;
|
||||
|
||||
clk = devm_clk_register(dev, &pow2_div->hw);
|
||||
if (WARN_ON(IS_ERR(clk)))
|
||||
return PTR_ERR(clk);
|
||||
|
||||
if (!spicc->data->has_enhance_clk_div) {
|
||||
spicc->clk = clk;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* algorithm for enh div: rate = freq / 2 / (N + 1) */
|
||||
|
||||
enh_fixed_div = devm_kzalloc(dev, sizeof(*enh_fixed_div), GFP_KERNEL);
|
||||
if (!enh_fixed_div)
|
||||
return -ENOMEM;
|
||||
|
||||
snprintf(name, sizeof(name), "%s#enh_fixed_div", dev_name(dev));
|
||||
init.name = name;
|
||||
init.ops = &clk_fixed_factor_ops;
|
||||
init.flags = 0;
|
||||
if (spicc->data->has_pclk)
|
||||
parent_data[0].hw = __clk_get_hw(spicc->pclk);
|
||||
else
|
||||
parent_data[0].hw = __clk_get_hw(spicc->core);
|
||||
init.num_parents = 1;
|
||||
|
||||
enh_fixed_div->mult = 1,
|
||||
enh_fixed_div->div = 2,
|
||||
enh_fixed_div->hw.init = &init;
|
||||
|
||||
clk = devm_clk_register(dev, &enh_fixed_div->hw);
|
||||
if (WARN_ON(IS_ERR(clk)))
|
||||
return PTR_ERR(clk);
|
||||
|
||||
enh_div = devm_kzalloc(dev, sizeof(*enh_div), GFP_KERNEL);
|
||||
if (!enh_div)
|
||||
return -ENOMEM;
|
||||
|
||||
snprintf(name, sizeof(name), "%s#enh_div", dev_name(dev));
|
||||
init.name = name;
|
||||
init.ops = &clk_divider_ops;
|
||||
init.flags = CLK_SET_RATE_PARENT;
|
||||
parent_data[0].hw = &enh_fixed_div->hw;
|
||||
init.num_parents = 1;
|
||||
|
||||
enh_div->shift = 16,
|
||||
enh_div->width = 8,
|
||||
enh_div->reg = spicc->base + SPICC_ENH_CTL0;
|
||||
enh_div->hw.init = &init;
|
||||
|
||||
clk = devm_clk_register(dev, &enh_div->hw);
|
||||
if (WARN_ON(IS_ERR(clk)))
|
||||
return PTR_ERR(clk);
|
||||
|
||||
mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
|
||||
if (!mux)
|
||||
return -ENOMEM;
|
||||
|
||||
snprintf(name, sizeof(name), "%s#sel", dev_name(dev));
|
||||
init.name = name;
|
||||
init.ops = &clk_mux_ops;
|
||||
parent_data[0].hw = &pow2_div->hw;
|
||||
parent_data[1].hw = &enh_div->hw;
|
||||
init.num_parents = 2;
|
||||
init.flags = CLK_SET_RATE_PARENT;
|
||||
|
||||
mux->mask = 0x1,
|
||||
mux->shift = 24,
|
||||
mux->reg = spicc->base + SPICC_ENH_CTL0;
|
||||
mux->hw.init = &init;
|
||||
|
||||
spicc->clk = devm_clk_register(dev, &mux->hw);
|
||||
if (WARN_ON(IS_ERR(spicc->clk)))
|
||||
return PTR_ERR(spicc->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int meson_spicc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_master *master;
|
||||
struct meson_spicc_device *spicc;
|
||||
int ret, irq, rate;
|
||||
int ret, irq;
|
||||
|
||||
master = spi_alloc_master(&pdev->dev, sizeof(*spicc));
|
||||
if (!master) {
|
||||
|
@ -491,6 +670,13 @@ static int meson_spicc_probe(struct platform_device *pdev)
|
|||
spicc = spi_master_get_devdata(master);
|
||||
spicc->master = master;
|
||||
|
||||
spicc->data = of_device_get_match_data(&pdev->dev);
|
||||
if (!spicc->data) {
|
||||
dev_err(&pdev->dev, "failed to get match data\n");
|
||||
ret = -EINVAL;
|
||||
goto out_master;
|
||||
}
|
||||
|
||||
spicc->pdev = pdev;
|
||||
platform_set_drvdata(pdev, spicc);
|
||||
|
||||
|
@ -501,6 +687,10 @@ static int meson_spicc_probe(struct platform_device *pdev)
|
|||
goto out_master;
|
||||
}
|
||||
|
||||
/* Set master mode and enable controller */
|
||||
writel_relaxed(SPICC_ENABLE | SPICC_MODE_MASTER,
|
||||
spicc->base + SPICC_CONREG);
|
||||
|
||||
/* Disable all IRQs */
|
||||
writel_relaxed(0, spicc->base + SPICC_INTREG);
|
||||
|
||||
|
@ -519,12 +709,26 @@ static int meson_spicc_probe(struct platform_device *pdev)
|
|||
goto out_master;
|
||||
}
|
||||
|
||||
if (spicc->data->has_pclk) {
|
||||
spicc->pclk = devm_clk_get(&pdev->dev, "pclk");
|
||||
if (IS_ERR(spicc->pclk)) {
|
||||
dev_err(&pdev->dev, "pclk clock request failed\n");
|
||||
ret = PTR_ERR(spicc->pclk);
|
||||
goto out_master;
|
||||
}
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(spicc->core);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "core clock enable failed\n");
|
||||
goto out_master;
|
||||
}
|
||||
rate = clk_get_rate(spicc->core);
|
||||
|
||||
ret = clk_prepare_enable(spicc->pclk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "pclk clock enable failed\n");
|
||||
goto out_master;
|
||||
}
|
||||
|
||||
device_reset_optional(&pdev->dev);
|
||||
|
||||
|
@ -536,7 +740,8 @@ static int meson_spicc_probe(struct platform_device *pdev)
|
|||
SPI_BPW_MASK(16) |
|
||||
SPI_BPW_MASK(8);
|
||||
master->flags = (SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX);
|
||||
master->min_speed_hz = rate >> 9;
|
||||
master->min_speed_hz = spicc->data->min_speed_hz;
|
||||
master->max_speed_hz = spicc->data->max_speed_hz;
|
||||
master->setup = meson_spicc_setup;
|
||||
master->cleanup = meson_spicc_cleanup;
|
||||
master->prepare_message = meson_spicc_prepare_message;
|
||||
|
@ -544,11 +749,13 @@ static int meson_spicc_probe(struct platform_device *pdev)
|
|||
master->transfer_one = meson_spicc_transfer_one;
|
||||
master->use_gpio_descriptors = true;
|
||||
|
||||
/* Setup max rate according to the Meson GX datasheet */
|
||||
if ((rate >> 2) > SPICC_MAX_FREQ)
|
||||
master->max_speed_hz = SPICC_MAX_FREQ;
|
||||
else
|
||||
master->max_speed_hz = rate >> 2;
|
||||
meson_spicc_oen_enable(spicc);
|
||||
|
||||
ret = meson_spicc_clk_init(spicc);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "clock registration failed\n");
|
||||
goto out_master;
|
||||
}
|
||||
|
||||
ret = devm_spi_register_master(&pdev->dev, master);
|
||||
if (ret) {
|
||||
|
@ -560,6 +767,7 @@ static int meson_spicc_probe(struct platform_device *pdev)
|
|||
|
||||
out_clk:
|
||||
clk_disable_unprepare(spicc->core);
|
||||
clk_disable_unprepare(spicc->pclk);
|
||||
|
||||
out_master:
|
||||
spi_master_put(master);
|
||||
|
@ -575,13 +783,47 @@ static int meson_spicc_remove(struct platform_device *pdev)
|
|||
writel(0, spicc->base + SPICC_CONREG);
|
||||
|
||||
clk_disable_unprepare(spicc->core);
|
||||
clk_disable_unprepare(spicc->pclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct meson_spicc_data meson_spicc_gx_data = {
|
||||
.max_speed_hz = 30000000,
|
||||
.min_speed_hz = 325000,
|
||||
.fifo_size = 16,
|
||||
};
|
||||
|
||||
static const struct meson_spicc_data meson_spicc_axg_data = {
|
||||
.max_speed_hz = 80000000,
|
||||
.min_speed_hz = 325000,
|
||||
.fifo_size = 16,
|
||||
.has_oen = true,
|
||||
.has_enhance_clk_div = true,
|
||||
};
|
||||
|
||||
static const struct meson_spicc_data meson_spicc_g12a_data = {
|
||||
.max_speed_hz = 166666666,
|
||||
.min_speed_hz = 50000,
|
||||
.fifo_size = 15,
|
||||
.has_oen = true,
|
||||
.has_enhance_clk_div = true,
|
||||
.has_pclk = true,
|
||||
};
|
||||
|
||||
static const struct of_device_id meson_spicc_of_match[] = {
|
||||
{ .compatible = "amlogic,meson-gx-spicc", },
|
||||
{ .compatible = "amlogic,meson-axg-spicc", },
|
||||
{
|
||||
.compatible = "amlogic,meson-gx-spicc",
|
||||
.data = &meson_spicc_gx_data,
|
||||
},
|
||||
{
|
||||
.compatible = "amlogic,meson-axg-spicc",
|
||||
.data = &meson_spicc_axg_data,
|
||||
},
|
||||
{
|
||||
.compatible = "amlogic,meson-g12a-spicc",
|
||||
.data = &meson_spicc_g12a_data,
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, meson_spicc_of_match);
|
||||
|
|
|
@ -0,0 +1,689 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// Mediatek SPI NOR controller driver
|
||||
//
|
||||
// Copyright (C) 2020 Chuanhong Guo <gch981213@gmail.com>
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/spi-mem.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#define DRIVER_NAME "mtk-spi-nor"
|
||||
|
||||
#define MTK_NOR_REG_CMD 0x00
|
||||
#define MTK_NOR_CMD_WRITE BIT(4)
|
||||
#define MTK_NOR_CMD_PROGRAM BIT(2)
|
||||
#define MTK_NOR_CMD_READ BIT(0)
|
||||
#define MTK_NOR_CMD_MASK GENMASK(5, 0)
|
||||
|
||||
#define MTK_NOR_REG_PRG_CNT 0x04
|
||||
#define MTK_NOR_REG_RDATA 0x0c
|
||||
|
||||
#define MTK_NOR_REG_RADR0 0x10
|
||||
#define MTK_NOR_REG_RADR(n) (MTK_NOR_REG_RADR0 + 4 * (n))
|
||||
#define MTK_NOR_REG_RADR3 0xc8
|
||||
|
||||
#define MTK_NOR_REG_WDATA 0x1c
|
||||
|
||||
#define MTK_NOR_REG_PRGDATA0 0x20
|
||||
#define MTK_NOR_REG_PRGDATA(n) (MTK_NOR_REG_PRGDATA0 + 4 * (n))
|
||||
#define MTK_NOR_REG_PRGDATA_MAX 5
|
||||
|
||||
#define MTK_NOR_REG_SHIFT0 0x38
|
||||
#define MTK_NOR_REG_SHIFT(n) (MTK_NOR_REG_SHIFT0 + 4 * (n))
|
||||
#define MTK_NOR_REG_SHIFT_MAX 9
|
||||
|
||||
#define MTK_NOR_REG_CFG1 0x60
|
||||
#define MTK_NOR_FAST_READ BIT(0)
|
||||
|
||||
#define MTK_NOR_REG_CFG2 0x64
|
||||
#define MTK_NOR_WR_CUSTOM_OP_EN BIT(4)
|
||||
#define MTK_NOR_WR_BUF_EN BIT(0)
|
||||
|
||||
#define MTK_NOR_REG_PP_DATA 0x98
|
||||
|
||||
#define MTK_NOR_REG_IRQ_STAT 0xa8
|
||||
#define MTK_NOR_REG_IRQ_EN 0xac
|
||||
#define MTK_NOR_IRQ_DMA BIT(7)
|
||||
#define MTK_NOR_IRQ_MASK GENMASK(7, 0)
|
||||
|
||||
#define MTK_NOR_REG_CFG3 0xb4
|
||||
#define MTK_NOR_DISABLE_WREN BIT(7)
|
||||
#define MTK_NOR_DISABLE_SR_POLL BIT(5)
|
||||
|
||||
#define MTK_NOR_REG_WP 0xc4
|
||||
#define MTK_NOR_ENABLE_SF_CMD 0x30
|
||||
|
||||
#define MTK_NOR_REG_BUSCFG 0xcc
|
||||
#define MTK_NOR_4B_ADDR BIT(4)
|
||||
#define MTK_NOR_QUAD_ADDR BIT(3)
|
||||
#define MTK_NOR_QUAD_READ BIT(2)
|
||||
#define MTK_NOR_DUAL_ADDR BIT(1)
|
||||
#define MTK_NOR_DUAL_READ BIT(0)
|
||||
#define MTK_NOR_BUS_MODE_MASK GENMASK(4, 0)
|
||||
|
||||
#define MTK_NOR_REG_DMA_CTL 0x718
|
||||
#define MTK_NOR_DMA_START BIT(0)
|
||||
|
||||
#define MTK_NOR_REG_DMA_FADR 0x71c
|
||||
#define MTK_NOR_REG_DMA_DADR 0x720
|
||||
#define MTK_NOR_REG_DMA_END_DADR 0x724
|
||||
|
||||
#define MTK_NOR_PRG_MAX_SIZE 6
|
||||
// Reading DMA src/dst addresses have to be 16-byte aligned
|
||||
#define MTK_NOR_DMA_ALIGN 16
|
||||
#define MTK_NOR_DMA_ALIGN_MASK (MTK_NOR_DMA_ALIGN - 1)
|
||||
// and we allocate a bounce buffer if destination address isn't aligned.
|
||||
#define MTK_NOR_BOUNCE_BUF_SIZE PAGE_SIZE
|
||||
|
||||
// Buffered page program can do one 128-byte transfer
|
||||
#define MTK_NOR_PP_SIZE 128
|
||||
|
||||
#define CLK_TO_US(sp, clkcnt) ((clkcnt) * 1000000 / sp->spi_freq)
|
||||
|
||||
struct mtk_nor {
|
||||
struct spi_controller *ctlr;
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
u8 *buffer;
|
||||
struct clk *spi_clk;
|
||||
struct clk *ctlr_clk;
|
||||
unsigned int spi_freq;
|
||||
bool wbuf_en;
|
||||
bool has_irq;
|
||||
struct completion op_done;
|
||||
};
|
||||
|
||||
static inline void mtk_nor_rmw(struct mtk_nor *sp, u32 reg, u32 set, u32 clr)
|
||||
{
|
||||
u32 val = readl(sp->base + reg);
|
||||
|
||||
val &= ~clr;
|
||||
val |= set;
|
||||
writel(val, sp->base + reg);
|
||||
}
|
||||
|
||||
static inline int mtk_nor_cmd_exec(struct mtk_nor *sp, u32 cmd, ulong clk)
|
||||
{
|
||||
ulong delay = CLK_TO_US(sp, clk);
|
||||
u32 reg;
|
||||
int ret;
|
||||
|
||||
writel(cmd, sp->base + MTK_NOR_REG_CMD);
|
||||
ret = readl_poll_timeout(sp->base + MTK_NOR_REG_CMD, reg, !(reg & cmd),
|
||||
delay / 3, (delay + 1) * 200);
|
||||
if (ret < 0)
|
||||
dev_err(sp->dev, "command %u timeout.\n", cmd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void mtk_nor_set_addr(struct mtk_nor *sp, const struct spi_mem_op *op)
|
||||
{
|
||||
u32 addr = op->addr.val;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
writeb(addr & 0xff, sp->base + MTK_NOR_REG_RADR(i));
|
||||
addr >>= 8;
|
||||
}
|
||||
if (op->addr.nbytes == 4) {
|
||||
writeb(addr & 0xff, sp->base + MTK_NOR_REG_RADR3);
|
||||
mtk_nor_rmw(sp, MTK_NOR_REG_BUSCFG, MTK_NOR_4B_ADDR, 0);
|
||||
} else {
|
||||
mtk_nor_rmw(sp, MTK_NOR_REG_BUSCFG, 0, MTK_NOR_4B_ADDR);
|
||||
}
|
||||
}
|
||||
|
||||
static bool mtk_nor_match_read(const struct spi_mem_op *op)
|
||||
{
|
||||
int dummy = 0;
|
||||
|
||||
if (op->dummy.buswidth)
|
||||
dummy = op->dummy.nbytes * BITS_PER_BYTE / op->dummy.buswidth;
|
||||
|
||||
if ((op->data.buswidth == 2) || (op->data.buswidth == 4)) {
|
||||
if (op->addr.buswidth == 1)
|
||||
return dummy == 8;
|
||||
else if (op->addr.buswidth == 2)
|
||||
return dummy == 4;
|
||||
else if (op->addr.buswidth == 4)
|
||||
return dummy == 6;
|
||||
} else if ((op->addr.buswidth == 1) && (op->data.buswidth == 1)) {
|
||||
if (op->cmd.opcode == 0x03)
|
||||
return dummy == 0;
|
||||
else if (op->cmd.opcode == 0x0b)
|
||||
return dummy == 8;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int mtk_nor_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
if (!op->data.nbytes)
|
||||
return 0;
|
||||
|
||||
if ((op->addr.nbytes == 3) || (op->addr.nbytes == 4)) {
|
||||
if ((op->data.dir == SPI_MEM_DATA_IN) &&
|
||||
mtk_nor_match_read(op)) {
|
||||
if ((op->addr.val & MTK_NOR_DMA_ALIGN_MASK) ||
|
||||
(op->data.nbytes < MTK_NOR_DMA_ALIGN))
|
||||
op->data.nbytes = 1;
|
||||
else if (!((ulong)(op->data.buf.in) &
|
||||
MTK_NOR_DMA_ALIGN_MASK))
|
||||
op->data.nbytes &= ~MTK_NOR_DMA_ALIGN_MASK;
|
||||
else if (op->data.nbytes > MTK_NOR_BOUNCE_BUF_SIZE)
|
||||
op->data.nbytes = MTK_NOR_BOUNCE_BUF_SIZE;
|
||||
return 0;
|
||||
} else if (op->data.dir == SPI_MEM_DATA_OUT) {
|
||||
if (op->data.nbytes >= MTK_NOR_PP_SIZE)
|
||||
op->data.nbytes = MTK_NOR_PP_SIZE;
|
||||
else
|
||||
op->data.nbytes = 1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
len = MTK_NOR_PRG_MAX_SIZE - sizeof(op->cmd.opcode) - op->addr.nbytes -
|
||||
op->dummy.nbytes;
|
||||
if (op->data.nbytes > len)
|
||||
op->data.nbytes = len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool mtk_nor_supports_op(struct spi_mem *mem,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
if (op->cmd.buswidth != 1)
|
||||
return false;
|
||||
|
||||
if ((op->addr.nbytes == 3) || (op->addr.nbytes == 4)) {
|
||||
if ((op->data.dir == SPI_MEM_DATA_IN) && mtk_nor_match_read(op))
|
||||
return true;
|
||||
else if (op->data.dir == SPI_MEM_DATA_OUT)
|
||||
return (op->addr.buswidth == 1) &&
|
||||
(op->dummy.buswidth == 0) &&
|
||||
(op->data.buswidth == 1);
|
||||
}
|
||||
len = sizeof(op->cmd.opcode) + op->addr.nbytes + op->dummy.nbytes;
|
||||
if ((len > MTK_NOR_PRG_MAX_SIZE) ||
|
||||
((op->data.nbytes) && (len == MTK_NOR_PRG_MAX_SIZE)))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void mtk_nor_setup_bus(struct mtk_nor *sp, const struct spi_mem_op *op)
|
||||
{
|
||||
u32 reg = 0;
|
||||
|
||||
if (op->addr.nbytes == 4)
|
||||
reg |= MTK_NOR_4B_ADDR;
|
||||
|
||||
if (op->data.buswidth == 4) {
|
||||
reg |= MTK_NOR_QUAD_READ;
|
||||
writeb(op->cmd.opcode, sp->base + MTK_NOR_REG_PRGDATA(4));
|
||||
if (op->addr.buswidth == 4)
|
||||
reg |= MTK_NOR_QUAD_ADDR;
|
||||
} else if (op->data.buswidth == 2) {
|
||||
reg |= MTK_NOR_DUAL_READ;
|
||||
writeb(op->cmd.opcode, sp->base + MTK_NOR_REG_PRGDATA(3));
|
||||
if (op->addr.buswidth == 2)
|
||||
reg |= MTK_NOR_DUAL_ADDR;
|
||||
} else {
|
||||
if (op->cmd.opcode == 0x0b)
|
||||
mtk_nor_rmw(sp, MTK_NOR_REG_CFG1, MTK_NOR_FAST_READ, 0);
|
||||
else
|
||||
mtk_nor_rmw(sp, MTK_NOR_REG_CFG1, 0, MTK_NOR_FAST_READ);
|
||||
}
|
||||
mtk_nor_rmw(sp, MTK_NOR_REG_BUSCFG, reg, MTK_NOR_BUS_MODE_MASK);
|
||||
}
|
||||
|
||||
static int mtk_nor_read_dma(struct mtk_nor *sp, u32 from, unsigned int length,
|
||||
u8 *buffer)
|
||||
{
|
||||
int ret = 0;
|
||||
ulong delay;
|
||||
u32 reg;
|
||||
dma_addr_t dma_addr;
|
||||
|
||||
dma_addr = dma_map_single(sp->dev, buffer, length, DMA_FROM_DEVICE);
|
||||
if (dma_mapping_error(sp->dev, dma_addr)) {
|
||||
dev_err(sp->dev, "failed to map dma buffer.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
writel(from, sp->base + MTK_NOR_REG_DMA_FADR);
|
||||
writel(dma_addr, sp->base + MTK_NOR_REG_DMA_DADR);
|
||||
writel(dma_addr + length, sp->base + MTK_NOR_REG_DMA_END_DADR);
|
||||
|
||||
if (sp->has_irq) {
|
||||
reinit_completion(&sp->op_done);
|
||||
mtk_nor_rmw(sp, MTK_NOR_REG_IRQ_EN, MTK_NOR_IRQ_DMA, 0);
|
||||
}
|
||||
|
||||
mtk_nor_rmw(sp, MTK_NOR_REG_DMA_CTL, MTK_NOR_DMA_START, 0);
|
||||
|
||||
delay = CLK_TO_US(sp, (length + 5) * BITS_PER_BYTE);
|
||||
|
||||
if (sp->has_irq) {
|
||||
if (!wait_for_completion_timeout(&sp->op_done,
|
||||
(delay + 1) * 100))
|
||||
ret = -ETIMEDOUT;
|
||||
} else {
|
||||
ret = readl_poll_timeout(sp->base + MTK_NOR_REG_DMA_CTL, reg,
|
||||
!(reg & MTK_NOR_DMA_START), delay / 3,
|
||||
(delay + 1) * 100);
|
||||
}
|
||||
|
||||
dma_unmap_single(sp->dev, dma_addr, length, DMA_FROM_DEVICE);
|
||||
if (ret < 0)
|
||||
dev_err(sp->dev, "dma read timeout.\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mtk_nor_read_bounce(struct mtk_nor *sp, u32 from,
|
||||
unsigned int length, u8 *buffer)
|
||||
{
|
||||
unsigned int rdlen;
|
||||
int ret;
|
||||
|
||||
if (length & MTK_NOR_DMA_ALIGN_MASK)
|
||||
rdlen = (length + MTK_NOR_DMA_ALIGN) & ~MTK_NOR_DMA_ALIGN_MASK;
|
||||
else
|
||||
rdlen = length;
|
||||
|
||||
ret = mtk_nor_read_dma(sp, from, rdlen, sp->buffer);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
memcpy(buffer, sp->buffer, length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_nor_read_pio(struct mtk_nor *sp, const struct spi_mem_op *op)
|
||||
{
|
||||
u8 *buf = op->data.buf.in;
|
||||
int ret;
|
||||
|
||||
ret = mtk_nor_cmd_exec(sp, MTK_NOR_CMD_READ, 6 * BITS_PER_BYTE);
|
||||
if (!ret)
|
||||
buf[0] = readb(sp->base + MTK_NOR_REG_RDATA);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mtk_nor_write_buffer_enable(struct mtk_nor *sp)
|
||||
{
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
if (sp->wbuf_en)
|
||||
return 0;
|
||||
|
||||
val = readl(sp->base + MTK_NOR_REG_CFG2);
|
||||
writel(val | MTK_NOR_WR_BUF_EN, sp->base + MTK_NOR_REG_CFG2);
|
||||
ret = readl_poll_timeout(sp->base + MTK_NOR_REG_CFG2, val,
|
||||
val & MTK_NOR_WR_BUF_EN, 0, 10000);
|
||||
if (!ret)
|
||||
sp->wbuf_en = true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mtk_nor_write_buffer_disable(struct mtk_nor *sp)
|
||||
{
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
if (!sp->wbuf_en)
|
||||
return 0;
|
||||
val = readl(sp->base + MTK_NOR_REG_CFG2);
|
||||
writel(val & ~MTK_NOR_WR_BUF_EN, sp->base + MTK_NOR_REG_CFG2);
|
||||
ret = readl_poll_timeout(sp->base + MTK_NOR_REG_CFG2, val,
|
||||
!(val & MTK_NOR_WR_BUF_EN), 0, 10000);
|
||||
if (!ret)
|
||||
sp->wbuf_en = false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mtk_nor_pp_buffered(struct mtk_nor *sp, const struct spi_mem_op *op)
|
||||
{
|
||||
const u8 *buf = op->data.buf.out;
|
||||
u32 val;
|
||||
int ret, i;
|
||||
|
||||
ret = mtk_nor_write_buffer_enable(sp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < op->data.nbytes; i += 4) {
|
||||
val = buf[i + 3] << 24 | buf[i + 2] << 16 | buf[i + 1] << 8 |
|
||||
buf[i];
|
||||
writel(val, sp->base + MTK_NOR_REG_PP_DATA);
|
||||
}
|
||||
return mtk_nor_cmd_exec(sp, MTK_NOR_CMD_WRITE,
|
||||
(op->data.nbytes + 5) * BITS_PER_BYTE);
|
||||
}
|
||||
|
||||
static int mtk_nor_pp_unbuffered(struct mtk_nor *sp,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
const u8 *buf = op->data.buf.out;
|
||||
int ret;
|
||||
|
||||
ret = mtk_nor_write_buffer_disable(sp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
writeb(buf[0], sp->base + MTK_NOR_REG_WDATA);
|
||||
return mtk_nor_cmd_exec(sp, MTK_NOR_CMD_WRITE, 6 * BITS_PER_BYTE);
|
||||
}
|
||||
|
||||
int mtk_nor_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
{
|
||||
struct mtk_nor *sp = spi_controller_get_devdata(mem->spi->master);
|
||||
int ret;
|
||||
|
||||
if ((op->data.nbytes == 0) ||
|
||||
((op->addr.nbytes != 3) && (op->addr.nbytes != 4)))
|
||||
return -ENOTSUPP;
|
||||
|
||||
if (op->data.dir == SPI_MEM_DATA_OUT) {
|
||||
mtk_nor_set_addr(sp, op);
|
||||
writeb(op->cmd.opcode, sp->base + MTK_NOR_REG_PRGDATA0);
|
||||
if (op->data.nbytes == MTK_NOR_PP_SIZE)
|
||||
return mtk_nor_pp_buffered(sp, op);
|
||||
return mtk_nor_pp_unbuffered(sp, op);
|
||||
}
|
||||
|
||||
if ((op->data.dir == SPI_MEM_DATA_IN) && mtk_nor_match_read(op)) {
|
||||
ret = mtk_nor_write_buffer_disable(sp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
mtk_nor_setup_bus(sp, op);
|
||||
if (op->data.nbytes == 1) {
|
||||
mtk_nor_set_addr(sp, op);
|
||||
return mtk_nor_read_pio(sp, op);
|
||||
} else if (((ulong)(op->data.buf.in) &
|
||||
MTK_NOR_DMA_ALIGN_MASK)) {
|
||||
return mtk_nor_read_bounce(sp, op->addr.val,
|
||||
op->data.nbytes,
|
||||
op->data.buf.in);
|
||||
} else {
|
||||
return mtk_nor_read_dma(sp, op->addr.val,
|
||||
op->data.nbytes,
|
||||
op->data.buf.in);
|
||||
}
|
||||
}
|
||||
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
static int mtk_nor_setup(struct spi_device *spi)
|
||||
{
|
||||
struct mtk_nor *sp = spi_controller_get_devdata(spi->master);
|
||||
|
||||
if (spi->max_speed_hz && (spi->max_speed_hz < sp->spi_freq)) {
|
||||
dev_err(&spi->dev, "spi clock should be %u Hz.\n",
|
||||
sp->spi_freq);
|
||||
return -EINVAL;
|
||||
}
|
||||
spi->max_speed_hz = sp->spi_freq;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_nor_transfer_one_message(struct spi_controller *master,
|
||||
struct spi_message *m)
|
||||
{
|
||||
struct mtk_nor *sp = spi_controller_get_devdata(master);
|
||||
struct spi_transfer *t = NULL;
|
||||
unsigned long trx_len = 0;
|
||||
int stat = 0;
|
||||
int reg_offset = MTK_NOR_REG_PRGDATA_MAX;
|
||||
void __iomem *reg;
|
||||
const u8 *txbuf;
|
||||
u8 *rxbuf;
|
||||
int i;
|
||||
|
||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||
txbuf = t->tx_buf;
|
||||
for (i = 0; i < t->len; i++, reg_offset--) {
|
||||
reg = sp->base + MTK_NOR_REG_PRGDATA(reg_offset);
|
||||
if (txbuf)
|
||||
writeb(txbuf[i], reg);
|
||||
else
|
||||
writeb(0, reg);
|
||||
}
|
||||
trx_len += t->len;
|
||||
}
|
||||
|
||||
writel(trx_len * BITS_PER_BYTE, sp->base + MTK_NOR_REG_PRG_CNT);
|
||||
|
||||
stat = mtk_nor_cmd_exec(sp, MTK_NOR_CMD_PROGRAM,
|
||||
trx_len * BITS_PER_BYTE);
|
||||
if (stat < 0)
|
||||
goto msg_done;
|
||||
|
||||
reg_offset = trx_len - 1;
|
||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||
rxbuf = t->rx_buf;
|
||||
for (i = 0; i < t->len; i++, reg_offset--) {
|
||||
reg = sp->base + MTK_NOR_REG_SHIFT(reg_offset);
|
||||
if (rxbuf)
|
||||
rxbuf[i] = readb(reg);
|
||||
}
|
||||
}
|
||||
|
||||
m->actual_length = trx_len;
|
||||
msg_done:
|
||||
m->status = stat;
|
||||
spi_finalize_current_message(master);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mtk_nor_disable_clk(struct mtk_nor *sp)
|
||||
{
|
||||
clk_disable_unprepare(sp->spi_clk);
|
||||
clk_disable_unprepare(sp->ctlr_clk);
|
||||
}
|
||||
|
||||
static int mtk_nor_enable_clk(struct mtk_nor *sp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(sp->spi_clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(sp->ctlr_clk);
|
||||
if (ret) {
|
||||
clk_disable_unprepare(sp->spi_clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_nor_init(struct mtk_nor *sp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = mtk_nor_enable_clk(sp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
sp->spi_freq = clk_get_rate(sp->spi_clk);
|
||||
|
||||
writel(MTK_NOR_ENABLE_SF_CMD, sp->base + MTK_NOR_REG_WP);
|
||||
mtk_nor_rmw(sp, MTK_NOR_REG_CFG2, MTK_NOR_WR_CUSTOM_OP_EN, 0);
|
||||
mtk_nor_rmw(sp, MTK_NOR_REG_CFG3,
|
||||
MTK_NOR_DISABLE_WREN | MTK_NOR_DISABLE_SR_POLL, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t mtk_nor_irq_handler(int irq, void *data)
|
||||
{
|
||||
struct mtk_nor *sp = data;
|
||||
u32 irq_status, irq_enabled;
|
||||
|
||||
irq_status = readl(sp->base + MTK_NOR_REG_IRQ_STAT);
|
||||
irq_enabled = readl(sp->base + MTK_NOR_REG_IRQ_EN);
|
||||
// write status back to clear interrupt
|
||||
writel(irq_status, sp->base + MTK_NOR_REG_IRQ_STAT);
|
||||
|
||||
if (!(irq_status & irq_enabled))
|
||||
return IRQ_NONE;
|
||||
|
||||
if (irq_status & MTK_NOR_IRQ_DMA) {
|
||||
complete(&sp->op_done);
|
||||
writel(0, sp->base + MTK_NOR_REG_IRQ_EN);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static size_t mtk_max_msg_size(struct spi_device *spi)
|
||||
{
|
||||
return MTK_NOR_PRG_MAX_SIZE;
|
||||
}
|
||||
|
||||
static const struct spi_controller_mem_ops mtk_nor_mem_ops = {
|
||||
.adjust_op_size = mtk_nor_adjust_op_size,
|
||||
.supports_op = mtk_nor_supports_op,
|
||||
.exec_op = mtk_nor_exec_op
|
||||
};
|
||||
|
||||
static const struct of_device_id mtk_nor_match[] = {
|
||||
{ .compatible = "mediatek,mt8173-nor" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mtk_nor_match);
|
||||
|
||||
static int mtk_nor_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_controller *ctlr;
|
||||
struct mtk_nor *sp;
|
||||
void __iomem *base;
|
||||
u8 *buffer;
|
||||
struct clk *spi_clk, *ctlr_clk;
|
||||
int ret, irq;
|
||||
|
||||
base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
spi_clk = devm_clk_get(&pdev->dev, "spi");
|
||||
if (IS_ERR(spi_clk))
|
||||
return PTR_ERR(spi_clk);
|
||||
|
||||
ctlr_clk = devm_clk_get(&pdev->dev, "sf");
|
||||
if (IS_ERR(ctlr_clk))
|
||||
return PTR_ERR(ctlr_clk);
|
||||
|
||||
buffer = devm_kmalloc(&pdev->dev,
|
||||
MTK_NOR_BOUNCE_BUF_SIZE + MTK_NOR_DMA_ALIGN,
|
||||
GFP_KERNEL);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
if ((ulong)buffer & MTK_NOR_DMA_ALIGN_MASK)
|
||||
buffer = (u8 *)(((ulong)buffer + MTK_NOR_DMA_ALIGN) &
|
||||
~MTK_NOR_DMA_ALIGN_MASK);
|
||||
|
||||
ctlr = spi_alloc_master(&pdev->dev, sizeof(*sp));
|
||||
if (!ctlr) {
|
||||
dev_err(&pdev->dev, "failed to allocate spi controller\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ctlr->bits_per_word_mask = SPI_BPW_MASK(8);
|
||||
ctlr->dev.of_node = pdev->dev.of_node;
|
||||
ctlr->max_message_size = mtk_max_msg_size;
|
||||
ctlr->mem_ops = &mtk_nor_mem_ops;
|
||||
ctlr->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD | SPI_TX_DUAL | SPI_TX_QUAD;
|
||||
ctlr->num_chipselect = 1;
|
||||
ctlr->setup = mtk_nor_setup;
|
||||
ctlr->transfer_one_message = mtk_nor_transfer_one_message;
|
||||
|
||||
dev_set_drvdata(&pdev->dev, ctlr);
|
||||
|
||||
sp = spi_controller_get_devdata(ctlr);
|
||||
sp->base = base;
|
||||
sp->buffer = buffer;
|
||||
sp->has_irq = false;
|
||||
sp->wbuf_en = false;
|
||||
sp->ctlr = ctlr;
|
||||
sp->dev = &pdev->dev;
|
||||
sp->spi_clk = spi_clk;
|
||||
sp->ctlr_clk = ctlr_clk;
|
||||
|
||||
irq = platform_get_irq_optional(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_warn(sp->dev, "IRQ not available.");
|
||||
} else {
|
||||
writel(MTK_NOR_IRQ_MASK, base + MTK_NOR_REG_IRQ_STAT);
|
||||
writel(0, base + MTK_NOR_REG_IRQ_EN);
|
||||
ret = devm_request_irq(sp->dev, irq, mtk_nor_irq_handler, 0,
|
||||
pdev->name, sp);
|
||||
if (ret < 0) {
|
||||
dev_warn(sp->dev, "failed to request IRQ.");
|
||||
} else {
|
||||
init_completion(&sp->op_done);
|
||||
sp->has_irq = true;
|
||||
}
|
||||
}
|
||||
|
||||
ret = mtk_nor_init(sp);
|
||||
if (ret < 0) {
|
||||
kfree(ctlr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "spi frequency: %d Hz\n", sp->spi_freq);
|
||||
|
||||
return devm_spi_register_controller(&pdev->dev, ctlr);
|
||||
}
|
||||
|
||||
static int mtk_nor_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_controller *ctlr;
|
||||
struct mtk_nor *sp;
|
||||
|
||||
ctlr = dev_get_drvdata(&pdev->dev);
|
||||
sp = spi_controller_get_devdata(ctlr);
|
||||
|
||||
mtk_nor_disable_clk(sp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver mtk_nor_driver = {
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.of_match_table = mtk_nor_match,
|
||||
},
|
||||
.probe = mtk_nor_probe,
|
||||
.remove = mtk_nor_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(mtk_nor_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Mediatek SPI NOR controller driver");
|
||||
MODULE_AUTHOR("Chuanhong Guo <gch981213@gmail.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:" DRIVER_NAME);
|
|
@ -0,0 +1,187 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// General Purpose SPI multiplexer
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mux/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#define SPI_MUX_NO_CS ((unsigned int)-1)
|
||||
|
||||
/**
|
||||
* DOC: Driver description
|
||||
*
|
||||
* This driver supports a MUX on an SPI bus. This can be useful when you need
|
||||
* more chip selects than the hardware peripherals support, or than are
|
||||
* available in a particular board setup.
|
||||
*
|
||||
* The driver will create an additional SPI controller. Devices added under the
|
||||
* mux will be handled as 'chip selects' on this controller.
|
||||
*/
|
||||
|
||||
/**
|
||||
* struct spi_mux_priv - the basic spi_mux structure
|
||||
* @spi: pointer to the device struct attached to the parent
|
||||
* spi controller
|
||||
* @current_cs: The current chip select set in the mux
|
||||
* @child_msg_complete: The mux replaces the complete callback in the child's
|
||||
* message to its own callback; this field is used by the
|
||||
* driver to store the child's callback during a transfer
|
||||
* @child_msg_context: Used to store the child's context to the callback
|
||||
* @child_msg_dev: Used to store the spi_device pointer to the child
|
||||
* @mux: mux_control structure used to provide chip selects for
|
||||
* downstream spi devices
|
||||
*/
|
||||
struct spi_mux_priv {
|
||||
struct spi_device *spi;
|
||||
unsigned int current_cs;
|
||||
|
||||
void (*child_msg_complete)(void *context);
|
||||
void *child_msg_context;
|
||||
struct spi_device *child_msg_dev;
|
||||
struct mux_control *mux;
|
||||
};
|
||||
|
||||
/* should not get called when the parent controller is doing a transfer */
|
||||
static int spi_mux_select(struct spi_device *spi)
|
||||
{
|
||||
struct spi_mux_priv *priv = spi_controller_get_devdata(spi->controller);
|
||||
int ret;
|
||||
|
||||
if (priv->current_cs == spi->chip_select)
|
||||
return 0;
|
||||
|
||||
dev_dbg(&priv->spi->dev, "setting up the mux for cs %d\n",
|
||||
spi->chip_select);
|
||||
|
||||
/* copy the child device's settings except for the cs */
|
||||
priv->spi->max_speed_hz = spi->max_speed_hz;
|
||||
priv->spi->mode = spi->mode;
|
||||
priv->spi->bits_per_word = spi->bits_per_word;
|
||||
|
||||
ret = mux_control_select(priv->mux, spi->chip_select);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
priv->current_cs = spi->chip_select;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spi_mux_setup(struct spi_device *spi)
|
||||
{
|
||||
struct spi_mux_priv *priv = spi_controller_get_devdata(spi->controller);
|
||||
|
||||
/*
|
||||
* can be called multiple times, won't do a valid setup now but we will
|
||||
* change the settings when we do a transfer (necessary because we
|
||||
* can't predict from which device it will be anyway)
|
||||
*/
|
||||
return spi_setup(priv->spi);
|
||||
}
|
||||
|
||||
static void spi_mux_complete_cb(void *context)
|
||||
{
|
||||
struct spi_mux_priv *priv = (struct spi_mux_priv *)context;
|
||||
struct spi_controller *ctlr = spi_get_drvdata(priv->spi);
|
||||
struct spi_message *m = ctlr->cur_msg;
|
||||
|
||||
m->complete = priv->child_msg_complete;
|
||||
m->context = priv->child_msg_context;
|
||||
m->spi = priv->child_msg_dev;
|
||||
spi_finalize_current_message(ctlr);
|
||||
mux_control_deselect(priv->mux);
|
||||
}
|
||||
|
||||
static int spi_mux_transfer_one_message(struct spi_controller *ctlr,
|
||||
struct spi_message *m)
|
||||
{
|
||||
struct spi_mux_priv *priv = spi_controller_get_devdata(ctlr);
|
||||
struct spi_device *spi = m->spi;
|
||||
int ret;
|
||||
|
||||
ret = spi_mux_select(spi);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Replace the complete callback, context and spi_device with our own
|
||||
* pointers. Save originals
|
||||
*/
|
||||
priv->child_msg_complete = m->complete;
|
||||
priv->child_msg_context = m->context;
|
||||
priv->child_msg_dev = m->spi;
|
||||
|
||||
m->complete = spi_mux_complete_cb;
|
||||
m->context = priv;
|
||||
m->spi = priv->spi;
|
||||
|
||||
/* do the transfer */
|
||||
return spi_async(priv->spi, m);
|
||||
}
|
||||
|
||||
static int spi_mux_probe(struct spi_device *spi)
|
||||
{
|
||||
struct spi_controller *ctlr;
|
||||
struct spi_mux_priv *priv;
|
||||
int ret;
|
||||
|
||||
ctlr = spi_alloc_master(&spi->dev, sizeof(*priv));
|
||||
if (!ctlr)
|
||||
return -ENOMEM;
|
||||
|
||||
spi_set_drvdata(spi, ctlr);
|
||||
priv = spi_controller_get_devdata(ctlr);
|
||||
priv->spi = spi;
|
||||
|
||||
priv->mux = devm_mux_control_get(&spi->dev, NULL);
|
||||
if (IS_ERR(priv->mux)) {
|
||||
ret = PTR_ERR(priv->mux);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(&spi->dev, "failed to get control-mux\n");
|
||||
goto err_put_ctlr;
|
||||
}
|
||||
|
||||
priv->current_cs = SPI_MUX_NO_CS;
|
||||
|
||||
/* supported modes are the same as our parent's */
|
||||
ctlr->mode_bits = spi->controller->mode_bits;
|
||||
ctlr->flags = spi->controller->flags;
|
||||
ctlr->transfer_one_message = spi_mux_transfer_one_message;
|
||||
ctlr->setup = spi_mux_setup;
|
||||
ctlr->num_chipselect = mux_control_states(priv->mux);
|
||||
ctlr->bus_num = -1;
|
||||
ctlr->dev.of_node = spi->dev.of_node;
|
||||
|
||||
ret = devm_spi_register_controller(&spi->dev, ctlr);
|
||||
if (ret)
|
||||
goto err_put_ctlr;
|
||||
|
||||
return 0;
|
||||
|
||||
err_put_ctlr:
|
||||
spi_controller_put(ctlr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id spi_mux_of_match[] = {
|
||||
{ .compatible = "spi-mux" },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct spi_driver spi_mux_driver = {
|
||||
.probe = spi_mux_probe,
|
||||
.driver = {
|
||||
.name = "spi-mux",
|
||||
.of_match_table = spi_mux_of_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_spi_driver(spi_mux_driver);
|
||||
|
||||
MODULE_DESCRIPTION("SPI multiplexer");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -22,7 +22,6 @@
|
|||
#include <linux/ioport.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
@ -32,7 +31,7 @@
|
|||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/module.h>
|
||||
|
|
|
@ -307,6 +307,7 @@
|
|||
|
||||
#define POLL_TOUT 5000
|
||||
#define NXP_FSPI_MAX_CHIPSELECT 4
|
||||
#define NXP_FSPI_MIN_IOMAP SZ_4M
|
||||
|
||||
struct nxp_fspi_devtype_data {
|
||||
unsigned int rxfifo;
|
||||
|
@ -324,11 +325,29 @@ static const struct nxp_fspi_devtype_data lx2160a_data = {
|
|||
.little_endian = true, /* little-endian */
|
||||
};
|
||||
|
||||
static const struct nxp_fspi_devtype_data imx8mm_data = {
|
||||
.rxfifo = SZ_512, /* (64 * 64 bits) */
|
||||
.txfifo = SZ_1K, /* (128 * 64 bits) */
|
||||
.ahb_buf_size = SZ_2K, /* (256 * 64 bits) */
|
||||
.quirks = 0,
|
||||
.little_endian = true, /* little-endian */
|
||||
};
|
||||
|
||||
static const struct nxp_fspi_devtype_data imx8qxp_data = {
|
||||
.rxfifo = SZ_512, /* (64 * 64 bits) */
|
||||
.txfifo = SZ_1K, /* (128 * 64 bits) */
|
||||
.ahb_buf_size = SZ_2K, /* (256 * 64 bits) */
|
||||
.quirks = 0,
|
||||
.little_endian = true, /* little-endian */
|
||||
};
|
||||
|
||||
struct nxp_fspi {
|
||||
void __iomem *iobase;
|
||||
void __iomem *ahb_addr;
|
||||
u32 memmap_phy;
|
||||
u32 memmap_phy_size;
|
||||
u32 memmap_start;
|
||||
u32 memmap_len;
|
||||
struct clk *clk, *clk_en;
|
||||
struct device *dev;
|
||||
struct completion c;
|
||||
|
@ -641,12 +660,35 @@ static void nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi)
|
|||
f->selected = spi->chip_select;
|
||||
}
|
||||
|
||||
static void nxp_fspi_read_ahb(struct nxp_fspi *f, const struct spi_mem_op *op)
|
||||
static int nxp_fspi_read_ahb(struct nxp_fspi *f, const struct spi_mem_op *op)
|
||||
{
|
||||
u32 start = op->addr.val;
|
||||
u32 len = op->data.nbytes;
|
||||
|
||||
/* if necessary, ioremap before AHB read */
|
||||
if ((!f->ahb_addr) || start < f->memmap_start ||
|
||||
start + len > f->memmap_start + f->memmap_len) {
|
||||
if (f->ahb_addr)
|
||||
iounmap(f->ahb_addr);
|
||||
|
||||
f->memmap_start = start;
|
||||
f->memmap_len = len > NXP_FSPI_MIN_IOMAP ?
|
||||
len : NXP_FSPI_MIN_IOMAP;
|
||||
|
||||
f->ahb_addr = ioremap_wc(f->memmap_phy + f->memmap_start,
|
||||
f->memmap_len);
|
||||
|
||||
if (!f->ahb_addr) {
|
||||
dev_err(f->dev, "failed to alloc memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
/* Read out the data directly from the AHB buffer. */
|
||||
memcpy_fromio(op->data.buf.in, (f->ahb_addr + op->addr.val), len);
|
||||
memcpy_fromio(op->data.buf.in,
|
||||
f->ahb_addr + start - f->memmap_start, len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nxp_fspi_fill_txfifo(struct nxp_fspi *f,
|
||||
|
@ -806,7 +848,7 @@ static int nxp_fspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
|||
*/
|
||||
if (op->data.nbytes > (f->devtype_data->rxfifo - 4) &&
|
||||
op->data.dir == SPI_MEM_DATA_IN) {
|
||||
nxp_fspi_read_ahb(f, op);
|
||||
err = nxp_fspi_read_ahb(f, op);
|
||||
} else {
|
||||
if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_OUT)
|
||||
nxp_fspi_fill_txfifo(f, op);
|
||||
|
@ -871,8 +913,9 @@ static int nxp_fspi_default_setup(struct nxp_fspi *f)
|
|||
fspi_writel(f, FSPI_DLLBCR_OVRDEN, base + FSPI_DLLBCR);
|
||||
|
||||
/* enable module */
|
||||
fspi_writel(f, FSPI_MCR0_AHB_TIMEOUT(0xFF) | FSPI_MCR0_IP_TIMEOUT(0xFF),
|
||||
base + FSPI_MCR0);
|
||||
fspi_writel(f, FSPI_MCR0_AHB_TIMEOUT(0xFF) |
|
||||
FSPI_MCR0_IP_TIMEOUT(0xFF) | (u32) FSPI_MCR0_OCTCOMB_EN,
|
||||
base + FSPI_MCR0);
|
||||
|
||||
/*
|
||||
* Disable same device enable bit and configure all slave devices
|
||||
|
@ -976,9 +1019,8 @@ static int nxp_fspi_probe(struct platform_device *pdev)
|
|||
|
||||
/* find the resources - controller memory mapped space */
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fspi_mmap");
|
||||
f->ahb_addr = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(f->ahb_addr)) {
|
||||
ret = PTR_ERR(f->ahb_addr);
|
||||
if (!res) {
|
||||
ret = -ENODEV;
|
||||
goto err_put_ctrl;
|
||||
}
|
||||
|
||||
|
@ -1057,6 +1099,9 @@ static int nxp_fspi_remove(struct platform_device *pdev)
|
|||
|
||||
mutex_destroy(&f->lock);
|
||||
|
||||
if (f->ahb_addr)
|
||||
iounmap(f->ahb_addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1076,6 +1121,8 @@ static int nxp_fspi_resume(struct device *dev)
|
|||
|
||||
static const struct of_device_id nxp_fspi_dt_ids[] = {
|
||||
{ .compatible = "nxp,lx2160a-fspi", .data = (void *)&lx2160a_data, },
|
||||
{ .compatible = "nxp,imx8mm-fspi", .data = (void *)&imx8mm_data, },
|
||||
{ .compatible = "nxp,imx8qxp-fspi", .data = (void *)&imx8qxp_data, },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, nxp_fspi_dt_ids);
|
||||
|
|
|
@ -192,6 +192,11 @@ static bool is_quark_x1000_ssp(const struct driver_data *drv_data)
|
|||
return drv_data->ssp_type == QUARK_X1000_SSP;
|
||||
}
|
||||
|
||||
static bool is_mmp2_ssp(const struct driver_data *drv_data)
|
||||
{
|
||||
return drv_data->ssp_type == MMP2_SSP;
|
||||
}
|
||||
|
||||
static u32 pxa2xx_spi_get_ssrc1_change_mask(const struct driver_data *drv_data)
|
||||
{
|
||||
switch (drv_data->ssp_type) {
|
||||
|
@ -486,8 +491,8 @@ int pxa2xx_spi_flush(struct driver_data *drv_data)
|
|||
|
||||
static void pxa2xx_spi_off(struct driver_data *drv_data)
|
||||
{
|
||||
/* On MMP, disabling SSE seems to corrupt the rx fifo */
|
||||
if (drv_data->ssp_type == MMP2_SSP)
|
||||
/* On MMP, disabling SSE seems to corrupt the Rx FIFO */
|
||||
if (is_mmp2_ssp(drv_data))
|
||||
return;
|
||||
|
||||
pxa2xx_spi_write(drv_data, SSCR0,
|
||||
|
@ -1093,7 +1098,7 @@ static int pxa2xx_spi_transfer_one(struct spi_controller *controller,
|
|||
|| (pxa2xx_spi_read(drv_data, SSCR1) & change_mask)
|
||||
!= (cr1 & change_mask)) {
|
||||
/* stop the SSP, and update the other bits */
|
||||
if (drv_data->ssp_type != MMP2_SSP)
|
||||
if (!is_mmp2_ssp(drv_data))
|
||||
pxa2xx_spi_write(drv_data, SSCR0, cr0 & ~SSCR0_SSE);
|
||||
if (!pxa25x_ssp_comp(drv_data))
|
||||
pxa2xx_spi_write(drv_data, SSTO, chip->timeout);
|
||||
|
@ -1107,7 +1112,7 @@ static int pxa2xx_spi_transfer_one(struct spi_controller *controller,
|
|||
pxa2xx_spi_write(drv_data, SSTO, chip->timeout);
|
||||
}
|
||||
|
||||
if (drv_data->ssp_type == MMP2_SSP) {
|
||||
if (is_mmp2_ssp(drv_data)) {
|
||||
u8 tx_level = (pxa2xx_spi_read(drv_data, SSSR)
|
||||
& SSSR_TFL_MASK) >> 8;
|
||||
|
||||
|
@ -1571,18 +1576,18 @@ pxa2xx_spi_init_pdata(struct platform_device *pdev)
|
|||
else if (pcidev_id)
|
||||
type = (enum pxa_ssp_type)pcidev_id->driver_data;
|
||||
else
|
||||
return NULL;
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return NULL;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
ssp = &pdata->ssp;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
ssp->mmio_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(ssp->mmio_base))
|
||||
return NULL;
|
||||
return ERR_CAST(ssp->mmio_base);
|
||||
|
||||
ssp->phys_base = res->start;
|
||||
|
||||
|
@ -1596,11 +1601,11 @@ pxa2xx_spi_init_pdata(struct platform_device *pdev)
|
|||
|
||||
ssp->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(ssp->clk))
|
||||
return NULL;
|
||||
return ERR_CAST(ssp->clk);
|
||||
|
||||
ssp->irq = platform_get_irq(pdev, 0);
|
||||
if (ssp->irq < 0)
|
||||
return NULL;
|
||||
return ERR_PTR(ssp->irq);
|
||||
|
||||
ssp->type = type;
|
||||
ssp->dev = &pdev->dev;
|
||||
|
@ -1657,9 +1662,9 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
|
|||
platform_info = dev_get_platdata(dev);
|
||||
if (!platform_info) {
|
||||
platform_info = pxa2xx_spi_init_pdata(pdev);
|
||||
if (!platform_info) {
|
||||
if (IS_ERR(platform_info)) {
|
||||
dev_err(&pdev->dev, "missing platform data\n");
|
||||
return -ENODEV;
|
||||
return PTR_ERR(platform_info);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1907,11 +1912,7 @@ out_error_controller_alloc:
|
|||
static int pxa2xx_spi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct driver_data *drv_data = platform_get_drvdata(pdev);
|
||||
struct ssp_device *ssp;
|
||||
|
||||
if (!drv_data)
|
||||
return 0;
|
||||
ssp = drv_data->ssp;
|
||||
struct ssp_device *ssp = drv_data->ssp;
|
||||
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
|
||||
|
|
|
@ -843,14 +843,17 @@ static const struct dev_pm_ops rockchip_spi_pm = {
|
|||
};
|
||||
|
||||
static const struct of_device_id rockchip_spi_dt_match[] = {
|
||||
{ .compatible = "rockchip,rv1108-spi", },
|
||||
{ .compatible = "rockchip,px30-spi", },
|
||||
{ .compatible = "rockchip,rk3036-spi", },
|
||||
{ .compatible = "rockchip,rk3066-spi", },
|
||||
{ .compatible = "rockchip,rk3188-spi", },
|
||||
{ .compatible = "rockchip,rk3228-spi", },
|
||||
{ .compatible = "rockchip,rk3288-spi", },
|
||||
{ .compatible = "rockchip,rk3308-spi", },
|
||||
{ .compatible = "rockchip,rk3328-spi", },
|
||||
{ .compatible = "rockchip,rk3368-spi", },
|
||||
{ .compatible = "rockchip,rk3399-spi", },
|
||||
{ .compatible = "rockchip,rv1108-spi", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rockchip_spi_dt_match);
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <linux/sh_dma.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/rspi.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#define RSPI_SPCR 0x00 /* Control Register */
|
||||
#define RSPI_SSLP 0x01 /* Slave Select Polarity Register */
|
||||
|
@ -79,8 +80,7 @@
|
|||
#define SPCR_BSWAP 0x01 /* Byte Swap of read-data for DMAC */
|
||||
|
||||
/* SSLP - Slave Select Polarity Register */
|
||||
#define SSLP_SSL1P 0x02 /* SSL1 Signal Polarity Setting */
|
||||
#define SSLP_SSL0P 0x01 /* SSL0 Signal Polarity Setting */
|
||||
#define SSLP_SSLP(i) BIT(i) /* SSLi Signal Polarity Setting */
|
||||
|
||||
/* SPPCR - Pin Control Register */
|
||||
#define SPPCR_MOIFE 0x20 /* MOSI Idle Value Fixing Enable */
|
||||
|
@ -181,7 +181,9 @@ struct rspi_data {
|
|||
void __iomem *addr;
|
||||
u32 max_speed_hz;
|
||||
struct spi_controller *ctlr;
|
||||
struct platform_device *pdev;
|
||||
wait_queue_head_t wait;
|
||||
spinlock_t lock; /* Protects RMW-access to RSPI_SSLP */
|
||||
struct clk *clk;
|
||||
u16 spcmd;
|
||||
u8 spsr;
|
||||
|
@ -239,7 +241,7 @@ struct spi_ops {
|
|||
int (*set_config_register)(struct rspi_data *rspi, int access_size);
|
||||
int (*transfer_one)(struct spi_controller *ctlr,
|
||||
struct spi_device *spi, struct spi_transfer *xfer);
|
||||
u16 mode_bits;
|
||||
u16 extra_mode_bits;
|
||||
u16 flags;
|
||||
u16 fifo_size;
|
||||
u8 num_hw_ss;
|
||||
|
@ -919,6 +921,29 @@ static int qspi_setup_sequencer(struct rspi_data *rspi,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int rspi_setup(struct spi_device *spi)
|
||||
{
|
||||
struct rspi_data *rspi = spi_controller_get_devdata(spi->controller);
|
||||
u8 sslp;
|
||||
|
||||
if (spi->cs_gpiod)
|
||||
return 0;
|
||||
|
||||
pm_runtime_get_sync(&rspi->pdev->dev);
|
||||
spin_lock_irq(&rspi->lock);
|
||||
|
||||
sslp = rspi_read8(rspi, RSPI_SSLP);
|
||||
if (spi->mode & SPI_CS_HIGH)
|
||||
sslp |= SSLP_SSLP(spi->chip_select);
|
||||
else
|
||||
sslp &= ~SSLP_SSLP(spi->chip_select);
|
||||
rspi_write8(rspi, sslp, RSPI_SSLP);
|
||||
|
||||
spin_unlock_irq(&rspi->lock);
|
||||
pm_runtime_put(&rspi->pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rspi_prepare_message(struct spi_controller *ctlr,
|
||||
struct spi_message *msg)
|
||||
{
|
||||
|
@ -933,6 +958,8 @@ static int rspi_prepare_message(struct spi_controller *ctlr,
|
|||
rspi->spcmd |= SPCMD_CPOL;
|
||||
if (spi->mode & SPI_CPHA)
|
||||
rspi->spcmd |= SPCMD_CPHA;
|
||||
if (spi->mode & SPI_LSB_FIRST)
|
||||
rspi->spcmd |= SPCMD_LSBF;
|
||||
|
||||
/* Configure slave signal to assert */
|
||||
rspi->spcmd |= SPCMD_SSLA(spi->cs_gpiod ? rspi->ctlr->unused_native_cs
|
||||
|
@ -1122,7 +1149,6 @@ static int rspi_remove(struct platform_device *pdev)
|
|||
static const struct spi_ops rspi_ops = {
|
||||
.set_config_register = rspi_set_config_register,
|
||||
.transfer_one = rspi_transfer_one,
|
||||
.mode_bits = SPI_CPHA | SPI_CPOL | SPI_LOOP,
|
||||
.flags = SPI_CONTROLLER_MUST_TX,
|
||||
.fifo_size = 8,
|
||||
.num_hw_ss = 2,
|
||||
|
@ -1131,7 +1157,6 @@ static const struct spi_ops rspi_ops = {
|
|||
static const struct spi_ops rspi_rz_ops = {
|
||||
.set_config_register = rspi_rz_set_config_register,
|
||||
.transfer_one = rspi_rz_transfer_one,
|
||||
.mode_bits = SPI_CPHA | SPI_CPOL | SPI_LOOP,
|
||||
.flags = SPI_CONTROLLER_MUST_RX | SPI_CONTROLLER_MUST_TX,
|
||||
.fifo_size = 8, /* 8 for TX, 32 for RX */
|
||||
.num_hw_ss = 1,
|
||||
|
@ -1140,8 +1165,7 @@ static const struct spi_ops rspi_rz_ops = {
|
|||
static const struct spi_ops qspi_ops = {
|
||||
.set_config_register = qspi_set_config_register,
|
||||
.transfer_one = qspi_transfer_one,
|
||||
.mode_bits = SPI_CPHA | SPI_CPOL | SPI_LOOP |
|
||||
SPI_TX_DUAL | SPI_TX_QUAD |
|
||||
.extra_mode_bits = SPI_TX_DUAL | SPI_TX_QUAD |
|
||||
SPI_RX_DUAL | SPI_RX_QUAD,
|
||||
.flags = SPI_CONTROLLER_MUST_RX | SPI_CONTROLLER_MUST_TX,
|
||||
.fifo_size = 32,
|
||||
|
@ -1249,16 +1273,20 @@ static int rspi_probe(struct platform_device *pdev)
|
|||
goto error1;
|
||||
}
|
||||
|
||||
rspi->pdev = pdev;
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
init_waitqueue_head(&rspi->wait);
|
||||
spin_lock_init(&rspi->lock);
|
||||
|
||||
ctlr->bus_num = pdev->id;
|
||||
ctlr->setup = rspi_setup;
|
||||
ctlr->auto_runtime_pm = true;
|
||||
ctlr->transfer_one = ops->transfer_one;
|
||||
ctlr->prepare_message = rspi_prepare_message;
|
||||
ctlr->unprepare_message = rspi_unprepare_message;
|
||||
ctlr->mode_bits = ops->mode_bits;
|
||||
ctlr->mode_bits = SPI_CPHA | SPI_CPOL | SPI_CS_HIGH | SPI_LSB_FIRST |
|
||||
SPI_LOOP | ops->extra_mode_bits;
|
||||
ctlr->flags = ops->flags;
|
||||
ctlr->dev.of_node = pdev->dev.of_node;
|
||||
ctlr->use_gpio_descriptors = true;
|
||||
|
|
|
@ -227,7 +227,7 @@ static inline unsigned int hw_txbyte(struct s3c24xx_spi *hw, int count)
|
|||
struct spi_fiq_code {
|
||||
u32 length;
|
||||
u32 ack_offset;
|
||||
u8 data[0];
|
||||
u8 data[];
|
||||
};
|
||||
|
||||
extern struct spi_fiq_code s3c24xx_spi_fiq_txrx;
|
||||
|
|
|
@ -565,7 +565,7 @@ static int stm32_qspi_probe(struct platform_device *pdev)
|
|||
qspi->io_base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(qspi->io_base)) {
|
||||
ret = PTR_ERR(qspi->io_base);
|
||||
goto err;
|
||||
goto err_master_put;
|
||||
}
|
||||
|
||||
qspi->phys_base = res->start;
|
||||
|
@ -574,24 +574,26 @@ static int stm32_qspi_probe(struct platform_device *pdev)
|
|||
qspi->mm_base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(qspi->mm_base)) {
|
||||
ret = PTR_ERR(qspi->mm_base);
|
||||
goto err;
|
||||
goto err_master_put;
|
||||
}
|
||||
|
||||
qspi->mm_size = resource_size(res);
|
||||
if (qspi->mm_size > STM32_QSPI_MAX_MMAP_SZ) {
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
goto err_master_put;
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
if (irq < 0) {
|
||||
ret = irq;
|
||||
goto err_master_put;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(dev, irq, stm32_qspi_irq, 0,
|
||||
dev_name(dev), qspi);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to request irq\n");
|
||||
goto err;
|
||||
goto err_master_put;
|
||||
}
|
||||
|
||||
init_completion(&qspi->data_completion);
|
||||
|
@ -599,23 +601,27 @@ static int stm32_qspi_probe(struct platform_device *pdev)
|
|||
qspi->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(qspi->clk)) {
|
||||
ret = PTR_ERR(qspi->clk);
|
||||
goto err;
|
||||
goto err_master_put;
|
||||
}
|
||||
|
||||
qspi->clk_rate = clk_get_rate(qspi->clk);
|
||||
if (!qspi->clk_rate) {
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
goto err_master_put;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(qspi->clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "can not enable the clock\n");
|
||||
goto err;
|
||||
goto err_master_put;
|
||||
}
|
||||
|
||||
rstc = devm_reset_control_get_exclusive(dev, NULL);
|
||||
if (!IS_ERR(rstc)) {
|
||||
if (IS_ERR(rstc)) {
|
||||
ret = PTR_ERR(rstc);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
goto err_qspi_release;
|
||||
} else {
|
||||
reset_control_assert(rstc);
|
||||
udelay(2);
|
||||
reset_control_deassert(rstc);
|
||||
|
@ -625,7 +631,7 @@ static int stm32_qspi_probe(struct platform_device *pdev)
|
|||
platform_set_drvdata(pdev, qspi);
|
||||
ret = stm32_qspi_dma_setup(qspi);
|
||||
if (ret)
|
||||
goto err;
|
||||
goto err_qspi_release;
|
||||
|
||||
mutex_init(&qspi->lock);
|
||||
|
||||
|
@ -641,8 +647,9 @@ static int stm32_qspi_probe(struct platform_device *pdev)
|
|||
if (!ret)
|
||||
return 0;
|
||||
|
||||
err:
|
||||
err_qspi_release:
|
||||
stm32_qspi_release(qspi);
|
||||
err_master_put:
|
||||
spi_master_put(qspi->ctrl);
|
||||
|
||||
return ret;
|
||||
|
|
|
@ -175,7 +175,7 @@
|
|||
#define SPI_DMA_MIN_BYTES 16
|
||||
|
||||
/**
|
||||
* stm32_spi_reg - stm32 SPI register & bitfield desc
|
||||
* struct stm32_spi_reg - stm32 SPI register & bitfield desc
|
||||
* @reg: register offset
|
||||
* @mask: bitfield mask
|
||||
* @shift: left shift
|
||||
|
@ -187,16 +187,16 @@ struct stm32_spi_reg {
|
|||
};
|
||||
|
||||
/**
|
||||
* stm32_spi_regspec - stm32 registers definition, compatible dependent data
|
||||
* en: enable register and SPI enable bit
|
||||
* dma_rx_en: SPI DMA RX enable register end SPI DMA RX enable bit
|
||||
* dma_tx_en: SPI DMA TX enable register end SPI DMA TX enable bit
|
||||
* cpol: clock polarity register and polarity bit
|
||||
* cpha: clock phase register and phase bit
|
||||
* lsb_first: LSB transmitted first register and bit
|
||||
* br: baud rate register and bitfields
|
||||
* rx: SPI RX data register
|
||||
* tx: SPI TX data register
|
||||
* struct stm32_spi_regspec - stm32 registers definition, compatible dependent data
|
||||
* @en: enable register and SPI enable bit
|
||||
* @dma_rx_en: SPI DMA RX enable register end SPI DMA RX enable bit
|
||||
* @dma_tx_en: SPI DMA TX enable register end SPI DMA TX enable bit
|
||||
* @cpol: clock polarity register and polarity bit
|
||||
* @cpha: clock phase register and phase bit
|
||||
* @lsb_first: LSB transmitted first register and bit
|
||||
* @br: baud rate register and bitfields
|
||||
* @rx: SPI RX data register
|
||||
* @tx: SPI TX data register
|
||||
*/
|
||||
struct stm32_spi_regspec {
|
||||
const struct stm32_spi_reg en;
|
||||
|
@ -213,7 +213,7 @@ struct stm32_spi_regspec {
|
|||
struct stm32_spi;
|
||||
|
||||
/**
|
||||
* stm32_spi_cfg - stm32 compatible configuration data
|
||||
* struct stm32_spi_cfg - stm32 compatible configuration data
|
||||
* @regs: registers descriptions
|
||||
* @get_fifo_size: routine to get fifo size
|
||||
* @get_bpw_mask: routine to get bits per word mask
|
||||
|
@ -223,13 +223,13 @@ struct stm32_spi;
|
|||
* @set_mode: routine to configure registers to desired mode
|
||||
* @set_data_idleness: optional routine to configure registers to desired idle
|
||||
* time between frames (if driver has this functionality)
|
||||
* set_number_of_data: optional routine to configure registers to desired
|
||||
* @set_number_of_data: optional routine to configure registers to desired
|
||||
* number of data (if driver has this functionality)
|
||||
* @can_dma: routine to determine if the transfer is eligible for DMA use
|
||||
* @transfer_one_dma_start: routine to start transfer a single spi_transfer
|
||||
* using DMA
|
||||
* @dma_rx cb: routine to call after DMA RX channel operation is complete
|
||||
* @dma_tx cb: routine to call after DMA TX channel operation is complete
|
||||
* @dma_rx_cb: routine to call after DMA RX channel operation is complete
|
||||
* @dma_tx_cb: routine to call after DMA TX channel operation is complete
|
||||
* @transfer_one_irq: routine to configure interrupts for driver
|
||||
* @irq_handler_event: Interrupt handler for SPI controller events
|
||||
* @irq_handler_thread: thread of interrupt handler for SPI controller
|
||||
|
@ -587,6 +587,7 @@ static void stm32f4_spi_read_rx(struct stm32_spi *spi)
|
|||
/**
|
||||
* stm32h7_spi_read_rxfifo - Read bytes in Receive Data Register
|
||||
* @spi: pointer to the spi controller data structure
|
||||
* @flush: boolean indicating that FIFO should be flushed
|
||||
*
|
||||
* Write in rx_buf depends on remaining bytes to avoid to write beyond
|
||||
* rx_buf end.
|
||||
|
@ -756,6 +757,9 @@ static void stm32h7_spi_disable(struct stm32_spi *spi)
|
|||
|
||||
/**
|
||||
* stm32_spi_can_dma - Determine if the transfer is eligible for DMA use
|
||||
* @master: controller master interface
|
||||
* @spi_dev: pointer to the spi device
|
||||
* @transfer: pointer to spi transfer
|
||||
*
|
||||
* If driver has fifo and the current transfer size is greater than fifo size,
|
||||
* use DMA. Otherwise use DMA for transfer longer than defined DMA min bytes.
|
||||
|
@ -974,6 +978,8 @@ static irqreturn_t stm32h7_spi_irq_thread(int irq, void *dev_id)
|
|||
|
||||
/**
|
||||
* stm32_spi_prepare_msg - set up the controller to transfer a single message
|
||||
* @master: controller master interface
|
||||
* @msg: pointer to spi message
|
||||
*/
|
||||
static int stm32_spi_prepare_msg(struct spi_master *master,
|
||||
struct spi_message *msg)
|
||||
|
@ -1026,6 +1032,7 @@ static int stm32_spi_prepare_msg(struct spi_master *master,
|
|||
|
||||
/**
|
||||
* stm32f4_spi_dma_tx_cb - dma callback
|
||||
* @data: pointer to the spi controller data structure
|
||||
*
|
||||
* DMA callback is called when the transfer is complete for DMA TX channel.
|
||||
*/
|
||||
|
@ -1041,6 +1048,7 @@ static void stm32f4_spi_dma_tx_cb(void *data)
|
|||
|
||||
/**
|
||||
* stm32f4_spi_dma_rx_cb - dma callback
|
||||
* @data: pointer to the spi controller data structure
|
||||
*
|
||||
* DMA callback is called when the transfer is complete for DMA RX channel.
|
||||
*/
|
||||
|
@ -1054,6 +1062,7 @@ static void stm32f4_spi_dma_rx_cb(void *data)
|
|||
|
||||
/**
|
||||
* stm32h7_spi_dma_cb - dma callback
|
||||
* @data: pointer to the spi controller data structure
|
||||
*
|
||||
* DMA callback is called when the transfer is complete or when an error
|
||||
* occurs. If the transfer is complete, EOT flag is raised.
|
||||
|
@ -1079,6 +1088,9 @@ static void stm32h7_spi_dma_cb(void *data)
|
|||
/**
|
||||
* stm32_spi_dma_config - configure dma slave channel depending on current
|
||||
* transfer bits_per_word.
|
||||
* @spi: pointer to the spi controller data structure
|
||||
* @dma_conf: pointer to the dma_slave_config structure
|
||||
* @dir: direction of the dma transfer
|
||||
*/
|
||||
static void stm32_spi_dma_config(struct stm32_spi *spi,
|
||||
struct dma_slave_config *dma_conf,
|
||||
|
@ -1126,6 +1138,7 @@ static void stm32_spi_dma_config(struct stm32_spi *spi,
|
|||
/**
|
||||
* stm32f4_spi_transfer_one_irq - transfer a single spi_transfer using
|
||||
* interrupts
|
||||
* @spi: pointer to the spi controller data structure
|
||||
*
|
||||
* It must returns 0 if the transfer is finished or 1 if the transfer is still
|
||||
* in progress.
|
||||
|
@ -1166,6 +1179,7 @@ static int stm32f4_spi_transfer_one_irq(struct stm32_spi *spi)
|
|||
/**
|
||||
* stm32h7_spi_transfer_one_irq - transfer a single spi_transfer using
|
||||
* interrupts
|
||||
* @spi: pointer to the spi controller data structure
|
||||
*
|
||||
* It must returns 0 if the transfer is finished or 1 if the transfer is still
|
||||
* in progress.
|
||||
|
@ -1207,6 +1221,7 @@ static int stm32h7_spi_transfer_one_irq(struct stm32_spi *spi)
|
|||
/**
|
||||
* stm32f4_spi_transfer_one_dma_start - Set SPI driver registers to start
|
||||
* transfer using DMA
|
||||
* @spi: pointer to the spi controller data structure
|
||||
*/
|
||||
static void stm32f4_spi_transfer_one_dma_start(struct stm32_spi *spi)
|
||||
{
|
||||
|
@ -1227,6 +1242,7 @@ static void stm32f4_spi_transfer_one_dma_start(struct stm32_spi *spi)
|
|||
/**
|
||||
* stm32h7_spi_transfer_one_dma_start - Set SPI driver registers to start
|
||||
* transfer using DMA
|
||||
* @spi: pointer to the spi controller data structure
|
||||
*/
|
||||
static void stm32h7_spi_transfer_one_dma_start(struct stm32_spi *spi)
|
||||
{
|
||||
|
@ -1243,6 +1259,8 @@ static void stm32h7_spi_transfer_one_dma_start(struct stm32_spi *spi)
|
|||
|
||||
/**
|
||||
* stm32_spi_transfer_one_dma - transfer a single spi_transfer using DMA
|
||||
* @spi: pointer to the spi controller data structure
|
||||
* @xfer: pointer to the spi_transfer structure
|
||||
*
|
||||
* It must returns 0 if the transfer is finished or 1 if the transfer is still
|
||||
* in progress.
|
||||
|
@ -1405,7 +1423,7 @@ static void stm32_spi_set_mbr(struct stm32_spi *spi, u32 mbrdiv)
|
|||
/**
|
||||
* stm32_spi_communication_type - return transfer communication type
|
||||
* @spi_dev: pointer to the spi device
|
||||
* transfer: pointer to spi transfer
|
||||
* @transfer: pointer to spi transfer
|
||||
*/
|
||||
static unsigned int stm32_spi_communication_type(struct spi_device *spi_dev,
|
||||
struct spi_transfer *transfer)
|
||||
|
@ -1522,7 +1540,7 @@ static void stm32h7_spi_data_idleness(struct stm32_spi *spi, u32 len)
|
|||
/**
|
||||
* stm32h7_spi_number_of_data - configure number of data at current transfer
|
||||
* @spi: pointer to the spi controller data structure
|
||||
* @len: transfer length
|
||||
* @nb_words: transfer length (in words)
|
||||
*/
|
||||
static int stm32h7_spi_number_of_data(struct stm32_spi *spi, u32 nb_words)
|
||||
{
|
||||
|
@ -1546,6 +1564,9 @@ static int stm32h7_spi_number_of_data(struct stm32_spi *spi, u32 nb_words)
|
|||
* stm32_spi_transfer_one_setup - common setup to transfer a single
|
||||
* spi_transfer either using DMA or
|
||||
* interrupts.
|
||||
* @spi: pointer to the spi controller data structure
|
||||
* @spi_dev: pointer to the spi device
|
||||
* @transfer: pointer to spi transfer
|
||||
*/
|
||||
static int stm32_spi_transfer_one_setup(struct stm32_spi *spi,
|
||||
struct spi_device *spi_dev,
|
||||
|
@ -1625,6 +1646,9 @@ out:
|
|||
|
||||
/**
|
||||
* stm32_spi_transfer_one - transfer a single spi_transfer
|
||||
* @master: controller master interface
|
||||
* @spi_dev: pointer to the spi device
|
||||
* @transfer: pointer to spi transfer
|
||||
*
|
||||
* It must return 0 if the transfer is finished or 1 if the transfer is still
|
||||
* in progress.
|
||||
|
@ -1658,6 +1682,8 @@ static int stm32_spi_transfer_one(struct spi_master *master,
|
|||
|
||||
/**
|
||||
* stm32_spi_unprepare_msg - relax the hardware
|
||||
* @master: controller master interface
|
||||
* @msg: pointer to the spi message
|
||||
*/
|
||||
static int stm32_spi_unprepare_msg(struct spi_master *master,
|
||||
struct spi_message *msg)
|
||||
|
@ -1671,6 +1697,7 @@ static int stm32_spi_unprepare_msg(struct spi_master *master,
|
|||
|
||||
/**
|
||||
* stm32f4_spi_config - Configure SPI controller as SPI master
|
||||
* @spi: pointer to the spi controller data structure
|
||||
*/
|
||||
static int stm32f4_spi_config(struct stm32_spi *spi)
|
||||
{
|
||||
|
@ -1701,6 +1728,7 @@ static int stm32f4_spi_config(struct stm32_spi *spi)
|
|||
|
||||
/**
|
||||
* stm32h7_spi_config - Configure SPI controller as SPI master
|
||||
* @spi: pointer to the spi controller data structure
|
||||
*/
|
||||
static int stm32h7_spi_config(struct stm32_spi *spi)
|
||||
{
|
||||
|
|
|
@ -510,6 +510,7 @@ struct spi_device *spi_alloc_device(struct spi_controller *ctlr)
|
|||
spi->dev.bus = &spi_bus_type;
|
||||
spi->dev.release = spidev_release;
|
||||
spi->cs_gpio = -ENOENT;
|
||||
spi->mode = ctlr->buswidth_override_bits;
|
||||
|
||||
spin_lock_init(&spi->statistics.lock);
|
||||
|
||||
|
@ -1514,17 +1515,15 @@ void spi_take_timestamp_pre(struct spi_controller *ctlr,
|
|||
if (!xfer->ptp_sts)
|
||||
return;
|
||||
|
||||
if (xfer->timestamped_pre)
|
||||
if (xfer->timestamped)
|
||||
return;
|
||||
|
||||
if (progress < xfer->ptp_sts_word_pre)
|
||||
if (progress > xfer->ptp_sts_word_pre)
|
||||
return;
|
||||
|
||||
/* Capture the resolution of the timestamp */
|
||||
xfer->ptp_sts_word_pre = progress;
|
||||
|
||||
xfer->timestamped_pre = true;
|
||||
|
||||
if (irqs_off) {
|
||||
local_irq_save(ctlr->irq_flags);
|
||||
preempt_disable();
|
||||
|
@ -1553,7 +1552,7 @@ void spi_take_timestamp_post(struct spi_controller *ctlr,
|
|||
if (!xfer->ptp_sts)
|
||||
return;
|
||||
|
||||
if (xfer->timestamped_post)
|
||||
if (xfer->timestamped)
|
||||
return;
|
||||
|
||||
if (progress < xfer->ptp_sts_word_post)
|
||||
|
@ -1569,7 +1568,7 @@ void spi_take_timestamp_post(struct spi_controller *ctlr,
|
|||
/* Capture the resolution of the timestamp */
|
||||
xfer->ptp_sts_word_post = progress;
|
||||
|
||||
xfer->timestamped_post = true;
|
||||
xfer->timestamped = true;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spi_take_timestamp_post);
|
||||
|
||||
|
@ -1674,12 +1673,9 @@ void spi_finalize_current_message(struct spi_controller *ctlr)
|
|||
}
|
||||
}
|
||||
|
||||
if (unlikely(ctlr->ptp_sts_supported)) {
|
||||
list_for_each_entry(xfer, &mesg->transfers, transfer_list) {
|
||||
WARN_ON_ONCE(xfer->ptp_sts && !xfer->timestamped_pre);
|
||||
WARN_ON_ONCE(xfer->ptp_sts && !xfer->timestamped_post);
|
||||
}
|
||||
}
|
||||
if (unlikely(ctlr->ptp_sts_supported))
|
||||
list_for_each_entry(xfer, &mesg->transfers, transfer_list)
|
||||
WARN_ON_ONCE(xfer->ptp_sts && !xfer->timestamped);
|
||||
|
||||
spi_unmap_msg(ctlr, mesg);
|
||||
|
||||
|
@ -1955,13 +1951,8 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,
|
|||
spi->mode |= SPI_CS_HIGH;
|
||||
|
||||
/* Device speed */
|
||||
rc = of_property_read_u32(nc, "spi-max-frequency", &value);
|
||||
if (rc) {
|
||||
dev_err(&ctlr->dev,
|
||||
"%pOF has no valid 'spi-max-frequency' property (%d)\n", nc, rc);
|
||||
return rc;
|
||||
}
|
||||
spi->max_speed_hz = value;
|
||||
if (!of_property_read_u32(nc, "spi-max-frequency", &value))
|
||||
spi->max_speed_hz = value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -2181,9 +2172,10 @@ static acpi_status acpi_register_spi_device(struct spi_controller *ctlr,
|
|||
return AE_NO_MEMORY;
|
||||
}
|
||||
|
||||
|
||||
ACPI_COMPANION_SET(&spi->dev, adev);
|
||||
spi->max_speed_hz = lookup.max_speed_hz;
|
||||
spi->mode = lookup.mode;
|
||||
spi->mode |= lookup.mode;
|
||||
spi->irq = lookup.irq;
|
||||
spi->bits_per_word = lookup.bits_per_word;
|
||||
spi->chip_select = lookup.chip_select;
|
||||
|
@ -4034,7 +4026,7 @@ static struct spi_device *acpi_spi_find_device_by_adev(struct acpi_device *adev)
|
|||
struct device *dev;
|
||||
|
||||
dev = bus_find_device_by_acpi_dev(&spi_bus_type, adev);
|
||||
return dev ? to_spi_device(dev) : NULL;
|
||||
return to_spi_device(dev);
|
||||
}
|
||||
|
||||
static int acpi_spi_notify(struct notifier_block *nb, unsigned long value,
|
||||
|
|
|
@ -275,14 +275,14 @@ static int spidev_message(struct spidev_data *spidev,
|
|||
#ifdef VERBOSE
|
||||
dev_dbg(&spidev->spi->dev,
|
||||
" xfer len %u %s%s%s%dbits %u usec %u usec %uHz\n",
|
||||
u_tmp->len,
|
||||
u_tmp->rx_buf ? "rx " : "",
|
||||
u_tmp->tx_buf ? "tx " : "",
|
||||
u_tmp->cs_change ? "cs " : "",
|
||||
u_tmp->bits_per_word ? : spidev->spi->bits_per_word,
|
||||
u_tmp->delay_usecs,
|
||||
u_tmp->word_delay_usecs,
|
||||
u_tmp->speed_hz ? : spidev->spi->max_speed_hz);
|
||||
k_tmp->len,
|
||||
k_tmp->rx_buf ? "rx " : "",
|
||||
k_tmp->tx_buf ? "tx " : "",
|
||||
k_tmp->cs_change ? "cs " : "",
|
||||
k_tmp->bits_per_word ? : spidev->spi->bits_per_word,
|
||||
k_tmp->delay.value,
|
||||
k_tmp->word_delay.value,
|
||||
k_tmp->speed_hz ? : spidev->spi->max_speed_hz);
|
||||
#endif
|
||||
spi_message_add_tail(k_tmp, &msg);
|
||||
}
|
||||
|
@ -454,10 +454,11 @@ spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|||
|
||||
spi->max_speed_hz = tmp;
|
||||
retval = spi_setup(spi);
|
||||
if (retval >= 0)
|
||||
if (retval == 0) {
|
||||
spidev->speed_hz = tmp;
|
||||
else
|
||||
dev_dbg(&spi->dev, "%d Hz (max)\n", tmp);
|
||||
dev_dbg(&spi->dev, "%d Hz (max)\n",
|
||||
spidev->speed_hz);
|
||||
}
|
||||
spi->max_speed_hz = save;
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -135,6 +135,8 @@ extern int spi_delay_exec(struct spi_delay *_delay, struct spi_transfer *xfer);
|
|||
* @modalias: Name of the driver to use with this device, or an alias
|
||||
* for that name. This appears in the sysfs "modalias" attribute
|
||||
* for driver coldplugging, and in uevents used for hotplugging
|
||||
* @driver_override: If the name of a driver is written to this attribute, then
|
||||
* the device will bind to the named driver and only the named driver.
|
||||
* @cs_gpio: LEGACY: gpio number of the chipselect line (optional, -ENOENT when
|
||||
* not using a GPIO line) use cs_gpiod in new drivers by opting in on
|
||||
* the spi_master.
|
||||
|
@ -443,6 +445,7 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
|
|||
* @spi_transfer->ptp_sts_word_post were transmitted.
|
||||
* If the driver does not set this, the SPI core takes the snapshot as
|
||||
* close to the driver hand-over as possible.
|
||||
* @irq_flags: Interrupt enable state during PTP system timestamping
|
||||
*
|
||||
* Each SPI controller can communicate with one or more @spi_device
|
||||
* children. These make a small bus, sharing MOSI, MISO and SCK signals
|
||||
|
@ -481,6 +484,9 @@ struct spi_controller {
|
|||
/* spi_device.mode flags understood by this controller driver */
|
||||
u32 mode_bits;
|
||||
|
||||
/* spi_device.mode flags override flags for this controller */
|
||||
u32 buswidth_override_bits;
|
||||
|
||||
/* bitmask of supported bits_per_word for transfers */
|
||||
u32 bits_per_word_mask;
|
||||
#define SPI_BPW_MASK(bits) BIT((bits) - 1)
|
||||
|
@ -930,8 +936,7 @@ struct spi_transfer {
|
|||
|
||||
struct ptp_system_timestamp *ptp_sts;
|
||||
|
||||
bool timestamped_pre;
|
||||
bool timestamped_post;
|
||||
bool timestamped;
|
||||
|
||||
struct list_head transfer_list;
|
||||
};
|
||||
|
|
|
@ -51,7 +51,7 @@ $(OUTPUT)spidev_fdx: $(SPIDEV_FDX_IN)
|
|||
|
||||
clean:
|
||||
rm -f $(ALL_PROGRAMS)
|
||||
rm -f $(OUTPUT)include/linux/spi/spidev.h
|
||||
rm -rf $(OUTPUT)include/
|
||||
find $(if $(OUTPUT),$(OUTPUT),.) -name '*.o' -delete -o -name '\.*.d' -delete
|
||||
|
||||
install: $(ALL_PROGRAMS)
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
|
@ -26,7 +27,11 @@
|
|||
|
||||
static void pabort(const char *s)
|
||||
{
|
||||
perror(s);
|
||||
if (errno != 0)
|
||||
perror(s);
|
||||
else
|
||||
printf("%s\n", s);
|
||||
|
||||
abort();
|
||||
}
|
||||
|
||||
|
@ -283,7 +288,6 @@ static void parse_opts(int argc, char *argv[])
|
|||
break;
|
||||
default:
|
||||
print_usage(argv[0]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (mode & SPI_LOOP) {
|
||||
|
@ -405,6 +409,9 @@ int main(int argc, char *argv[])
|
|||
|
||||
parse_opts(argc, argv);
|
||||
|
||||
if (input_tx && input_file)
|
||||
pabort("only one of -p and --input may be selected");
|
||||
|
||||
fd = open(device, O_RDWR);
|
||||
if (fd < 0)
|
||||
pabort("can't open device");
|
||||
|
@ -446,9 +453,6 @@ int main(int argc, char *argv[])
|
|||
printf("bits per word: %d\n", bits);
|
||||
printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000);
|
||||
|
||||
if (input_tx && input_file)
|
||||
pabort("only one of -p and --input may be selected");
|
||||
|
||||
if (input_tx)
|
||||
transfer_escaped_string(fd, input_tx);
|
||||
else if (input_file)
|
||||
|
|
Loading…
Reference in New Issue