Merge branch 'spi-5.7' into spi-next
This commit is contained in:
commit
1ba0b52ea7
36
Documentation/devicetree/bindings/fsi/ibm,fsi2spi.yaml
Normal file
36
Documentation/devicetree/bindings/fsi/ibm,fsi2spi.yaml
Normal file
@ -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-pm8901-regulators"
|
||||||
"qcom,rpm-pm8921-regulators"
|
"qcom,rpm-pm8921-regulators"
|
||||||
"qcom,rpm-pm8018-regulators"
|
"qcom,rpm-pm8018-regulators"
|
||||||
|
"qcom,rpm-smb208-regulators"
|
||||||
|
|
||||||
- vdd_l0_l1_lvs-supply:
|
- vdd_l0_l1_lvs-supply:
|
||||||
- vdd_l2_l11_l12-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,
|
s1, s2, s3, s4, s5, , l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11,
|
||||||
l12, l14, lvs1
|
l12, l14, lvs1
|
||||||
|
|
||||||
|
smb208:
|
||||||
|
s1a, s1b, s2a, s2b
|
||||||
|
|
||||||
The content of each sub-node is defined by the standard binding for regulators -
|
The content of each sub-node is defined by the standard binding for regulators -
|
||||||
see regulator.txt - with additional custom properties described below:
|
see regulator.txt - with additional custom properties described below:
|
||||||
|
|
||||||
|
78
Documentation/devicetree/bindings/regulator/mps,mp5416.yaml
Normal file
78
Documentation/devicetree/bindings/regulator/mps,mp5416.yaml
Normal file
@ -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:
|
enum:
|
||||||
- amlogic,meson-gx-spicc # SPICC controller on Amlogic GX and compatible SoCs
|
- 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-axg-spicc # SPICC controller on Amlogic AXG and compatible SoCs
|
||||||
|
- amlogic,meson-g12a-spicc # SPICC controller on Amlogic G12A and compatible SoCs
|
||||||
|
|
||||||
interrupts:
|
interrupts:
|
||||||
maxItems: 1
|
maxItems: 1
|
||||||
@ -40,6 +41,27 @@ properties:
|
|||||||
items:
|
items:
|
||||||
- const: core
|
- 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:
|
required:
|
||||||
- compatible
|
- compatible
|
||||||
- reg
|
- reg
|
||||||
|
@ -10,7 +10,10 @@ Required properties:
|
|||||||
- "fsl,imx35-cspi" for SPI compatible with the one integrated on i.MX35
|
- "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,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,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
|
- reg : Offset and length of the register set for the device
|
||||||
- interrupts : Should contain CSPI/eCSPI interrupt
|
- interrupts : Should contain CSPI/eCSPI interrupt
|
||||||
- clocks : Clock specifiers for both ipg and per clocks.
|
- clocks : Clock specifiers for both ipg and per clocks.
|
||||||
|
41
Documentation/devicetree/bindings/spi/qca,ar934x-spi.yaml
Normal file
41
Documentation/devicetree/bindings/spi/qca,ar934x-spi.yaml
Normal file
@ -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:
|
description:
|
||||||
The SPI controller acts as a slave, instead of a master.
|
The SPI controller acts as a slave, instead of a master.
|
||||||
|
|
||||||
|
oneOf:
|
||||||
|
- required:
|
||||||
|
- "#address-cells"
|
||||||
|
- required:
|
||||||
|
- spi-slave
|
||||||
|
|
||||||
patternProperties:
|
patternProperties:
|
||||||
"^slave$":
|
"^slave$":
|
||||||
type: object
|
type: object
|
||||||
@ -114,7 +120,7 @@ patternProperties:
|
|||||||
- enum: [ 1, 2, 4, 8 ]
|
- enum: [ 1, 2, 4, 8 ]
|
||||||
- default: 1
|
- default: 1
|
||||||
description:
|
description:
|
||||||
Bus width to the SPI bus used for MISO.
|
Bus width to the SPI bus used for read transfers.
|
||||||
|
|
||||||
spi-rx-delay-us:
|
spi-rx-delay-us:
|
||||||
description:
|
description:
|
||||||
@ -126,7 +132,7 @@ patternProperties:
|
|||||||
- enum: [ 1, 2, 4, 8 ]
|
- enum: [ 1, 2, 4, 8 ]
|
||||||
- default: 1
|
- default: 1
|
||||||
description:
|
description:
|
||||||
Bus width to the SPI bus used for MOSI.
|
Bus width to the SPI bus used for write transfers.
|
||||||
|
|
||||||
spi-tx-delay-us:
|
spi-tx-delay-us:
|
||||||
description:
|
description:
|
||||||
|
@ -1,12 +1,17 @@
|
|||||||
ARM Freescale DSPI controller
|
ARM Freescale DSPI controller
|
||||||
|
|
||||||
Required properties:
|
Required properties:
|
||||||
- compatible : "fsl,vf610-dspi", "fsl,ls1021a-v1.0-dspi",
|
- compatible : must be one of:
|
||||||
"fsl,ls2085a-dspi"
|
"fsl,vf610-dspi",
|
||||||
or
|
"fsl,ls1021a-v1.0-dspi",
|
||||||
"fsl,ls2080a-dspi" followed by "fsl,ls2085a-dspi"
|
"fsl,ls1012a-dspi" (optionally followed by "fsl,ls1021a-v1.0-dspi"),
|
||||||
"fsl,ls1012a-dspi" followed by "fsl,ls1021a-v1.0-dspi"
|
"fsl,ls1028a-dspi",
|
||||||
"fsl,ls1088a-dspi" followed by "fsl,ls1021a-v1.0-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
|
- reg : Offset and length of the register set for the device
|
||||||
- interrupts : Should contain SPI controller interrupt
|
- interrupts : Should contain SPI controller interrupt
|
||||||
- clocks: from common clock binding: handle to dspi clock.
|
- 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-0: pin control group to be used for this controller.
|
||||||
- pinctrl-names: must contain a "default" entry.
|
- pinctrl-names: must contain a "default" entry.
|
||||||
- spi-num-chipselects : the number of the chipselect signals.
|
- spi-num-chipselects : the number of the chipselect signals.
|
||||||
- bus-num : the slave chip chipselect signal number.
|
|
||||||
|
|
||||||
Optional property:
|
Optional property:
|
||||||
- big-endian: If present the dspi device's registers are implemented
|
- big-endian: If present the dspi device's registers are implemented
|
||||||
in big endian mode.
|
in big endian mode.
|
||||||
|
- bus-num : the slave chip chipselect signal number.
|
||||||
|
|
||||||
Optional SPI slave node properties:
|
Optional SPI slave node properties:
|
||||||
- fsl,spi-cs-sck-delay: a delay in nanoseconds between activating chip
|
- 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:
|
Required properties:
|
||||||
- compatible: For mt8173, compatible should be "mediatek,mt8173-nor",
|
- compatible: For mt8173, compatible should be "mediatek,mt8173-nor",
|
||||||
@ -13,6 +13,7 @@ Required properties:
|
|||||||
"mediatek,mt7629-nor", "mediatek,mt8173-nor"
|
"mediatek,mt7629-nor", "mediatek,mt8173-nor"
|
||||||
"mediatek,mt8173-nor"
|
"mediatek,mt8173-nor"
|
||||||
- reg: physical base address and length of the controller's register
|
- 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
|
- clocks: the phandle of the clocks needed by the nor controller
|
||||||
- clock-names: the names of the clocks
|
- clock-names: the names of the clocks
|
||||||
the clocks should be named "spi" and "sf". "spi" is used for spi bus,
|
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>
|
- #address-cells: should be <1>
|
||||||
- #size-cells: should be <0>
|
- #size-cells: should be <0>
|
||||||
|
|
||||||
The SPI flash must be a child of the nor_flash node and must have a
|
There should be only one spi slave device following generic spi bindings.
|
||||||
compatible property. Also see jedec,spi-nor.txt.
|
It's not recommended to use this controller for devices other than SPI NOR
|
||||||
|
flash due to limited transfer capability of this controller.
|
||||||
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
|
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
nor_flash: spi@1100d000 {
|
nor_flash: spi@1100d000 {
|
||||||
compatible = "mediatek,mt8173-nor";
|
compatible = "mediatek,mt8173-nor";
|
||||||
reg = <0 0x1100d000 0 0xe0>;
|
reg = <0 0x1100d000 0 0xe0>;
|
||||||
|
interrupts = <&spi_flash_irq>;
|
||||||
clocks = <&pericfg CLK_PERI_SPI>,
|
clocks = <&pericfg CLK_PERI_SPI>,
|
||||||
<&topckgen CLK_TOP_SPINFI_IFR_SEL>;
|
<&topckgen CLK_TOP_SPINFI_IFR_SEL>;
|
||||||
clock-names = "spi", "sf";
|
clock-names = "spi", "sf";
|
89
Documentation/devicetree/bindings/spi/spi-mux.yaml
Normal file
89
Documentation/devicetree/bindings/spi/spi-mux.yaml
Normal file
@ -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:
|
Required properties:
|
||||||
- compatible : Should be "nxp,lx2160a-fspi"
|
- compatible : Should be "nxp,lx2160a-fspi"
|
||||||
|
"nxp,imx8qxp-fspi"
|
||||||
|
"nxp,imx8mm-fspi"
|
||||||
|
|
||||||
- reg : First contains the register location and length,
|
- reg : First contains the register location and length,
|
||||||
Second contains the memory mapping address and length
|
Second contains the memory mapping address and length
|
||||||
- reg-names : Should contain the resource reg names:
|
- 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";
|
|
||||||
};
|
|
107
Documentation/devicetree/bindings/spi/spi-rockchip.yaml
Normal file
107
Documentation/devicetree/bindings/spi/spi-rockchip.yaml
Normal file
@ -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
|
S: Maintained
|
||||||
F: Documentation/devicetree/bindings/i2c/i2c-rk3x.txt
|
F: Documentation/devicetree/bindings/i2c/i2c-rk3x.txt
|
||||||
F: Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.yaml
|
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/rk3*
|
||||||
F: arch/arm/boot/dts/rv1108*
|
F: arch/arm/boot/dts/rv1108*
|
||||||
F: arch/arm/mach-rockchip/
|
F: arch/arm/mach-rockchip/
|
||||||
@ -6858,6 +6859,13 @@ S: Maintained
|
|||||||
F: drivers/i2c/busses/i2c-fsi.c
|
F: drivers/i2c/busses/i2c-fsi.c
|
||||||
F: Documentation/devicetree/bindings/i2c/i2c-fsi.txt
|
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
|
FSNOTIFY: FILESYSTEM NOTIFICATION INFRASTRUCTURE
|
||||||
M: Jan Kara <jack@suse.cz>
|
M: Jan Kara <jack@suse.cz>
|
||||||
R: Amir Goldstein <amir73il@gmail.com>
|
R: Amir Goldstein <amir73il@gmail.com>
|
||||||
@ -11256,7 +11264,8 @@ F: drivers/tty/mxser.*
|
|||||||
MONOLITHIC POWER SYSTEM PMIC DRIVER
|
MONOLITHIC POWER SYSTEM PMIC DRIVER
|
||||||
M: Saravanan Sekar <sravanhome@gmail.com>
|
M: Saravanan Sekar <sravanhome@gmail.com>
|
||||||
S: Maintained
|
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.c
|
||||||
F: drivers/regulator/mpq7920.h
|
F: drivers/regulator/mpq7920.h
|
||||||
|
|
||||||
|
@ -52,14 +52,6 @@ config SPI_HISI_SFC
|
|||||||
help
|
help
|
||||||
This enables support for HiSilicon FMC SPI-NOR flash controller.
|
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
|
config SPI_NXP_SPIFI
|
||||||
tristate "NXP SPI Flash Interface (SPIFI)"
|
tristate "NXP SPI Flash Interface (SPIFI)"
|
||||||
depends on OF && (ARCH_LPC18XX || COMPILE_TEST)
|
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_ASPEED_SMC) += aspeed-smc.o
|
||||||
obj-$(CONFIG_SPI_CADENCE_QUADSPI) += cadence-quadspi.o
|
obj-$(CONFIG_SPI_CADENCE_QUADSPI) += cadence-quadspi.o
|
||||||
obj-$(CONFIG_SPI_HISI_SFC) += hisi-sfc.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_NXP_SPIFI) += nxp-spifi.o
|
||||||
obj-$(CONFIG_SPI_INTEL_SPI) += intel-spi.o
|
obj-$(CONFIG_SPI_INTEL_SPI) += intel-spi.o
|
||||||
obj-$(CONFIG_SPI_INTEL_SPI_PCI) += intel-spi-pci.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
|
config REGULATOR_ANATOP
|
||||||
tristate "Freescale i.MX on-chip ANATOP LDO regulators"
|
tristate "Freescale i.MX on-chip ANATOP LDO regulators"
|
||||||
|
depends on ARCH_MXC || COMPILE_TEST
|
||||||
depends on MFD_SYSCON
|
depends on MFD_SYSCON
|
||||||
help
|
help
|
||||||
Say y here to support Freescale i.MX on-chip ANATOP LDOs
|
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
|
through the regulator interface. In addition it enables
|
||||||
suspend-to-ram/standby transition.
|
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
|
config REGULATOR_MP8859
|
||||||
tristate "MPS MP8859 regulator driver"
|
tristate "MPS MP8859 regulator driver"
|
||||||
depends on I2C
|
depends on I2C
|
||||||
|
@ -78,6 +78,7 @@ obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o
|
|||||||
obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o
|
obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o
|
||||||
obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o
|
obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o
|
||||||
obj-$(CONFIG_REGULATOR_MCP16502) += mcp16502.o
|
obj-$(CONFIG_REGULATOR_MCP16502) += mcp16502.o
|
||||||
|
obj-$(CONFIG_REGULATOR_MP5416) += mp5416.o
|
||||||
obj-$(CONFIG_REGULATOR_MP8859) += mp8859.o
|
obj-$(CONFIG_REGULATOR_MP8859) += mp8859.o
|
||||||
obj-$(CONFIG_REGULATOR_MPQ7920) += mpq7920.o
|
obj-$(CONFIG_REGULATOR_MPQ7920) += mpq7920.o
|
||||||
obj-$(CONFIG_REGULATOR_MT6311) += mt6311-regulator.o
|
obj-$(CONFIG_REGULATOR_MT6311) += mt6311-regulator.o
|
||||||
|
@ -305,9 +305,13 @@ static int anatop_regulator_probe(struct platform_device *pdev)
|
|||||||
/* register regulator */
|
/* register regulator */
|
||||||
rdev = devm_regulator_register(dev, rdesc, &config);
|
rdev = devm_regulator_register(dev, rdesc, &config);
|
||||||
if (IS_ERR(rdev)) {
|
if (IS_ERR(rdev)) {
|
||||||
dev_err(dev, "failed to register %s\n",
|
ret = PTR_ERR(rdev);
|
||||||
rdesc->name);
|
if (ret == -EPROBE_DEFER)
|
||||||
return PTR_ERR(rdev);
|
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);
|
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 |
|
mask = AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_RATE_MASK |
|
||||||
AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_EN_MASK;
|
AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_EN_MASK;
|
||||||
enable = (ramp > 0) ?
|
enable = (ramp > 0) ?
|
||||||
AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_EN :
|
AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_EN : 0;
|
||||||
!AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_EN;
|
|
||||||
break;
|
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 |
|
mask = AXP20X_DCDC2_LDO3_V_RAMP_LDO3_RATE_MASK |
|
||||||
AXP20X_DCDC2_LDO3_V_RAMP_LDO3_EN_MASK;
|
AXP20X_DCDC2_LDO3_V_RAMP_LDO3_EN_MASK;
|
||||||
enable = (ramp > 0) ?
|
enable = (ramp > 0) ?
|
||||||
AXP20X_DCDC2_LDO3_V_RAMP_LDO3_EN :
|
AXP20X_DCDC2_LDO3_V_RAMP_LDO3_EN : 0;
|
||||||
!AXP20X_DCDC2_LDO3_V_RAMP_LDO3_EN;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,7 +73,7 @@ struct da9062_regulators {
|
|||||||
int irq_ldo_lim;
|
int irq_ldo_lim;
|
||||||
unsigned n_regulators;
|
unsigned n_regulators;
|
||||||
/* Array size to be defined during init. Keep at end. */
|
/* Array size to be defined during init. Keep at end. */
|
||||||
struct da9062_regulator regulator[0];
|
struct da9062_regulator regulator[];
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Regulator operations */
|
/* Regulator operations */
|
||||||
|
@ -66,7 +66,7 @@ struct da9063_regulator_data {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct da9063_regulators_pdata {
|
struct da9063_regulators_pdata {
|
||||||
unsigned n_regulators;
|
unsigned int n_regulators;
|
||||||
struct da9063_regulator_data *regulator_data;
|
struct da9063_regulator_data *regulator_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -131,7 +131,7 @@ struct da9063_regulator_info {
|
|||||||
/* Defines asignment of regulators info table to chip model */
|
/* Defines asignment of regulators info table to chip model */
|
||||||
struct da9063_dev_model {
|
struct da9063_dev_model {
|
||||||
const struct da9063_regulator_info *regulator_info;
|
const struct da9063_regulator_info *regulator_info;
|
||||||
unsigned n_regulators;
|
unsigned int n_regulators;
|
||||||
enum da9063_type type;
|
enum da9063_type type;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -150,9 +150,9 @@ struct da9063_regulator {
|
|||||||
|
|
||||||
/* Encapsulates all information for the regulators driver */
|
/* Encapsulates all information for the regulators driver */
|
||||||
struct da9063_regulators {
|
struct da9063_regulators {
|
||||||
unsigned n_regulators;
|
unsigned int n_regulators;
|
||||||
/* Array size to be defined during init. Keep at end. */
|
/* Array size to be defined during init. Keep at end. */
|
||||||
struct da9063_regulator regulator[0];
|
struct da9063_regulator regulator[];
|
||||||
};
|
};
|
||||||
|
|
||||||
/* BUCK modes for DA9063 */
|
/* BUCK modes for DA9063 */
|
||||||
@ -165,38 +165,46 @@ enum {
|
|||||||
|
|
||||||
/* Regulator operations */
|
/* 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[] = {
|
static const unsigned int da9063_buck_a_limits[] = {
|
||||||
500000, 600000, 700000, 800000, 900000, 1000000, 1100000, 1200000,
|
500000, 600000, 700000, 800000, 900000, 1000000, 1100000, 1200000,
|
||||||
1300000, 1400000, 1500000, 1600000, 1700000, 1800000, 1900000, 2000000
|
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[] = {
|
static const unsigned int da9063_buck_b_limits[] = {
|
||||||
1500000, 1600000, 1700000, 1800000, 1900000, 2000000, 2100000, 2200000,
|
1500000, 1600000, 1700000, 1800000, 1900000, 2000000, 2100000, 2200000,
|
||||||
2300000, 2400000, 2500000, 2600000, 2700000, 2800000, 2900000, 3000000
|
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[] = {
|
static const unsigned int da9063_bcores_merged_limits[] = {
|
||||||
1000000, 1200000, 1400000, 1600000, 1800000, 2000000, 2200000, 2400000,
|
1000000, 1200000, 1400000, 1600000, 1800000, 2000000, 2200000, 2400000,
|
||||||
2600000, 2800000, 3000000, 3200000, 3400000, 3600000, 3800000, 4000000
|
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[] = {
|
static const unsigned int da9063_bmem_bio_merged_limits[] = {
|
||||||
3000000, 3200000, 3400000, 3600000, 3800000, 4000000, 4200000, 4400000,
|
3000000, 3200000, 3400000, 3600000, 3800000, 4000000, 4200000, 4400000,
|
||||||
4600000, 4800000, 5000000, 5200000, 5400000, 5600000, 5800000, 6000000
|
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);
|
struct da9063_regulator *regl = rdev_get_drvdata(rdev);
|
||||||
unsigned val;
|
unsigned int val;
|
||||||
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case REGULATOR_MODE_FAST:
|
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.
|
* 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 da9063_regulator *regl = rdev_get_drvdata(rdev);
|
||||||
struct regmap_field *field;
|
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.
|
* 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);
|
struct da9063_regulator *regl = rdev_get_drvdata(rdev);
|
||||||
unsigned val;
|
unsigned int val;
|
||||||
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case REGULATOR_MODE_NORMAL:
|
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);
|
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 da9063_regulator *regl = rdev_get_drvdata(rdev);
|
||||||
struct regmap_field *field;
|
struct regmap_field *field;
|
||||||
@ -383,7 +391,8 @@ static int da9063_suspend_disable(struct regulator_dev *rdev)
|
|||||||
return regmap_field_write(regl->suspend, 0);
|
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);
|
struct da9063_regulator *regl = rdev_get_drvdata(rdev);
|
||||||
int val;
|
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);
|
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);
|
struct da9063_regulator *regl = rdev_get_drvdata(rdev);
|
||||||
unsigned val;
|
unsigned int val;
|
||||||
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case REGULATOR_MODE_NORMAL:
|
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_regulators *regulators = data;
|
||||||
struct da9063 *hw = regulators->regulator[0].hw;
|
struct da9063 *hw = regulators->regulator[0].hw;
|
||||||
struct da9063_regulator *regl;
|
struct da9063_regulator *regl;
|
||||||
int bits, i , ret;
|
int bits, i, ret;
|
||||||
|
|
||||||
ret = regmap_read(hw->regmap, DA9063_REG_STATUS_D, &bits);
|
ret = regmap_read(hw->regmap, DA9063_REG_STATUS_D, &bits);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
@ -605,10 +615,10 @@ static irqreturn_t da9063_ldo_lim_event(int irq, void *data)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (BIT(regl->info->oc_event.lsb) & bits) {
|
if (BIT(regl->info->oc_event.lsb) & bits) {
|
||||||
regulator_lock(regl->rdev);
|
regulator_lock(regl->rdev);
|
||||||
regulator_notifier_call_chain(regl->rdev,
|
regulator_notifier_call_chain(regl->rdev,
|
||||||
REGULATOR_EVENT_OVER_CURRENT, NULL);
|
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) {
|
if (regl->info->suspend_sleep.reg) {
|
||||||
regl->suspend_sleep = devm_regmap_field_alloc(&pdev->dev,
|
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))
|
if (IS_ERR(regl->suspend_sleep))
|
||||||
return PTR_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,
|
NULL, da9063_ldo_lim_event,
|
||||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||||
"LDO_LIM", regulators);
|
"LDO_LIM", regulators);
|
||||||
if (ret) {
|
if (ret)
|
||||||
dev_err(&pdev->dev, "Failed to request LDO_LIM IRQ.\n");
|
dev_err(&pdev->dev, "Failed to request LDO_LIM IRQ.\n");
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct platform_driver da9063_regulator_driver = {
|
static struct platform_driver da9063_regulator_driver = {
|
||||||
|
245
drivers/regulator/mp5416.c
Normal file
245
drivers/regulator/mp5416.c
Normal file
@ -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,
|
.id = 0,
|
||||||
.type = REGULATOR_VOLTAGE,
|
.type = REGULATOR_VOLTAGE,
|
||||||
.name = "mp8859_dcdc",
|
.name = "mp8859_dcdc",
|
||||||
|
.supply_name = "vin",
|
||||||
.of_match = of_match_ptr("mp8859_dcdc"),
|
.of_match = of_match_ptr("mp8859_dcdc"),
|
||||||
.n_voltages = VOL_MAX_IDX + 1,
|
.n_voltages = VOL_MAX_IDX + 1,
|
||||||
.linear_ranges = mp8859_dcdc_ranges,
|
.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);
|
drvdata->pwm = devm_pwm_get(&pdev->dev, NULL);
|
||||||
if (IS_ERR(drvdata->pwm)) {
|
if (IS_ERR(drvdata->pwm)) {
|
||||||
ret = PTR_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;
|
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[] = {
|
static const struct of_device_id rpm_of_match[] = {
|
||||||
{ .compatible = "qcom,rpm-pm8018-regulators",
|
{ .compatible = "qcom,rpm-pm8018-regulators",
|
||||||
.data = &rpm_pm8018_regulators },
|
.data = &rpm_pm8018_regulators },
|
||||||
{ .compatible = "qcom,rpm-pm8058-regulators", .data = &rpm_pm8058_regulators },
|
{ .compatible = "qcom,rpm-pm8058-regulators", .data = &rpm_pm8058_regulators },
|
||||||
{ .compatible = "qcom,rpm-pm8901-regulators", .data = &rpm_pm8901_regulators },
|
{ .compatible = "qcom,rpm-pm8901-regulators", .data = &rpm_pm8901_regulators },
|
||||||
{ .compatible = "qcom,rpm-pm8921-regulators", .data = &rpm_pm8921_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);
|
MODULE_DEVICE_TABLE(of, rpm_of_match);
|
||||||
|
@ -62,6 +62,13 @@ config SPI_ALTERA
|
|||||||
help
|
help
|
||||||
This is the driver for the Altera SPI Controller.
|
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
|
config SPI_ATH79
|
||||||
tristate "Atheros AR71XX/AR724X/AR913X SPI controller driver"
|
tristate "Atheros AR71XX/AR724X/AR913X SPI controller driver"
|
||||||
depends on ATH79 || COMPILE_TEST
|
depends on ATH79 || COMPILE_TEST
|
||||||
@ -264,6 +271,13 @@ config SPI_FALCON
|
|||||||
has only been tested with m25p80 type chips. The hardware has no
|
has only been tested with m25p80 type chips. The hardware has no
|
||||||
support for other types of SPI peripherals.
|
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
|
config SPI_FSL_LPSPI
|
||||||
tristate "Freescale i.MX LPSPI controller"
|
tristate "Freescale i.MX LPSPI controller"
|
||||||
depends on ARCH_MXC || COMPILE_TEST
|
depends on ARCH_MXC || COMPILE_TEST
|
||||||
@ -285,7 +299,6 @@ config SPI_HISI_SFC_V3XX
|
|||||||
tristate "HiSilicon SPI-NOR Flash Controller for Hi16XX chipsets"
|
tristate "HiSilicon SPI-NOR Flash Controller for Hi16XX chipsets"
|
||||||
depends on (ARM64 && ACPI) || COMPILE_TEST
|
depends on (ARM64 && ACPI) || COMPILE_TEST
|
||||||
depends on HAS_IOMEM
|
depends on HAS_IOMEM
|
||||||
select CONFIG_MTD_SPI_NOR
|
|
||||||
help
|
help
|
||||||
This enables support for HiSilicon v3xx SPI-NOR flash controller
|
This enables support for HiSilicon v3xx SPI-NOR flash controller
|
||||||
found in hi16xx chipsets.
|
found in hi16xx chipsets.
|
||||||
@ -415,6 +428,7 @@ config SPI_FSL_ESPI
|
|||||||
|
|
||||||
config SPI_MESON_SPICC
|
config SPI_MESON_SPICC
|
||||||
tristate "Amlogic Meson SPICC controller"
|
tristate "Amlogic Meson SPICC controller"
|
||||||
|
depends on COMMON_CLK
|
||||||
depends on ARCH_MESON || COMPILE_TEST
|
depends on ARCH_MESON || COMPILE_TEST
|
||||||
help
|
help
|
||||||
This enables master mode support for the SPICC (SPI communication
|
This enables master mode support for the SPICC (SPI communication
|
||||||
@ -443,6 +457,16 @@ config SPI_MT7621
|
|||||||
help
|
help
|
||||||
This selects a driver for the MediaTek MT7621 SPI Controller.
|
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
|
config SPI_NPCM_FIU
|
||||||
tristate "Nuvoton NPCM FLASH Interface Unit"
|
tristate "Nuvoton NPCM FLASH Interface Unit"
|
||||||
depends on ARCH_NPCM || COMPILE_TEST
|
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
|
# 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
|
# There are lots of SPI device types, with sensors and memory
|
||||||
# being probably the most widely used ones.
|
# being probably the most widely used ones.
|
||||||
|
@ -9,11 +9,13 @@ ccflags-$(CONFIG_SPI_DEBUG) := -DDEBUG
|
|||||||
# config declarations into driver model code
|
# config declarations into driver model code
|
||||||
obj-$(CONFIG_SPI_MASTER) += spi.o
|
obj-$(CONFIG_SPI_MASTER) += spi.o
|
||||||
obj-$(CONFIG_SPI_MEM) += spi-mem.o
|
obj-$(CONFIG_SPI_MEM) += spi-mem.o
|
||||||
|
obj-$(CONFIG_SPI_MUX) += spi-mux.o
|
||||||
obj-$(CONFIG_SPI_SPIDEV) += spidev.o
|
obj-$(CONFIG_SPI_SPIDEV) += spidev.o
|
||||||
obj-$(CONFIG_SPI_LOOPBACK_TEST) += spi-loopback-test.o
|
obj-$(CONFIG_SPI_LOOPBACK_TEST) += spi-loopback-test.o
|
||||||
|
|
||||||
# SPI master controller drivers (bus)
|
# SPI master controller drivers (bus)
|
||||||
obj-$(CONFIG_SPI_ALTERA) += spi-altera.o
|
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_ARMADA_3700) += spi-armada-3700.o
|
||||||
obj-$(CONFIG_SPI_ATMEL) += spi-atmel.o
|
obj-$(CONFIG_SPI_ATMEL) += spi-atmel.o
|
||||||
obj-$(CONFIG_SPI_ATMEL_QUADSPI) += atmel-quadspi.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_EFM32) += spi-efm32.o
|
||||||
obj-$(CONFIG_SPI_EP93XX) += spi-ep93xx.o
|
obj-$(CONFIG_SPI_EP93XX) += spi-ep93xx.o
|
||||||
obj-$(CONFIG_SPI_FALCON) += spi-falcon.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_CPM) += spi-fsl-cpm.o
|
||||||
obj-$(CONFIG_SPI_FSL_DSPI) += spi-fsl-dspi.o
|
obj-$(CONFIG_SPI_FSL_DSPI) += spi-fsl-dspi.o
|
||||||
obj-$(CONFIG_SPI_FSL_LIB) += spi-fsl-lib.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_MPC52xx) += spi-mpc52xx.o
|
||||||
obj-$(CONFIG_SPI_MT65XX) += spi-mt65xx.o
|
obj-$(CONFIG_SPI_MT65XX) += spi-mt65xx.o
|
||||||
obj-$(CONFIG_SPI_MT7621) += spi-mt7621.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_MXIC) += spi-mxic.o
|
||||||
obj-$(CONFIG_SPI_MXS) += spi-mxs.o
|
obj-$(CONFIG_SPI_MXS) += spi-mxs.o
|
||||||
obj-$(CONFIG_SPI_NPCM_FIU) += spi-npcm-fiu.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 },
|
{ 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,
|
static inline bool atmel_qspi_is_compatible(const struct spi_mem_op *op,
|
||||||
const struct atmel_qspi_mode *mode)
|
const struct atmel_qspi_mode *mode)
|
||||||
{
|
{
|
||||||
@ -293,32 +368,32 @@ static int atmel_qspi_set_cfg(struct atmel_qspi *aq,
|
|||||||
* Serial Memory Mode (SMM).
|
* Serial Memory Mode (SMM).
|
||||||
*/
|
*/
|
||||||
if (aq->mr != QSPI_MR_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;
|
aq->mr = QSPI_MR_SMM;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Clear pending interrupts */
|
/* Clear pending interrupts */
|
||||||
(void)readl_relaxed(aq->regs + QSPI_SR);
|
(void)atmel_qspi_read(aq, QSPI_SR);
|
||||||
|
|
||||||
if (aq->caps->has_ricr) {
|
if (aq->caps->has_ricr) {
|
||||||
if (!op->addr.nbytes && op->data.dir == SPI_MEM_DATA_IN)
|
if (!op->addr.nbytes && op->data.dir == SPI_MEM_DATA_IN)
|
||||||
ifr |= QSPI_IFR_APBTFRTYP_READ;
|
ifr |= QSPI_IFR_APBTFRTYP_READ;
|
||||||
|
|
||||||
/* Set QSPI Instruction Frame registers */
|
/* 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)
|
if (op->data.dir == SPI_MEM_DATA_IN)
|
||||||
writel_relaxed(icr, aq->regs + QSPI_RICR);
|
atmel_qspi_write(icr, aq, QSPI_RICR);
|
||||||
else
|
else
|
||||||
writel_relaxed(icr, aq->regs + QSPI_WICR);
|
atmel_qspi_write(icr, aq, QSPI_WICR);
|
||||||
writel_relaxed(ifr, aq->regs + QSPI_IFR);
|
atmel_qspi_write(ifr, aq, QSPI_IFR);
|
||||||
} else {
|
} else {
|
||||||
if (op->data.dir == SPI_MEM_DATA_OUT)
|
if (op->data.dir == SPI_MEM_DATA_OUT)
|
||||||
ifr |= QSPI_IFR_SAMA5D2_WRITE_TRSFR;
|
ifr |= QSPI_IFR_SAMA5D2_WRITE_TRSFR;
|
||||||
|
|
||||||
/* Set QSPI Instruction Frame registers */
|
/* Set QSPI Instruction Frame registers */
|
||||||
writel_relaxed(iar, aq->regs + QSPI_IAR);
|
atmel_qspi_write(iar, aq, QSPI_IAR);
|
||||||
writel_relaxed(icr, aq->regs + QSPI_ICR);
|
atmel_qspi_write(icr, aq, QSPI_ICR);
|
||||||
writel_relaxed(ifr, aq->regs + QSPI_IFR);
|
atmel_qspi_write(ifr, aq, QSPI_IFR);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
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 */
|
/* Skip to the final steps if there is no data */
|
||||||
if (op->data.nbytes) {
|
if (op->data.nbytes) {
|
||||||
/* Dummy read of QSPI_IFR to synchronize APB and AHB accesses */
|
/* 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 */
|
/* Send/Receive data */
|
||||||
if (op->data.dir == SPI_MEM_DATA_IN)
|
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);
|
op->data.nbytes);
|
||||||
|
|
||||||
/* Release the chip-select */
|
/* 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 */
|
/* 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)
|
if ((sr & QSPI_SR_CMD_COMPLETED) == QSPI_SR_CMD_COMPLETED)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
/* Wait for INSTRuction End interrupt */
|
/* Wait for INSTRuction End interrupt */
|
||||||
reinit_completion(&aq->cmd_completion);
|
reinit_completion(&aq->cmd_completion);
|
||||||
aq->pending = sr & QSPI_SR_CMD_COMPLETED;
|
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,
|
if (!wait_for_completion_timeout(&aq->cmd_completion,
|
||||||
msecs_to_jiffies(1000)))
|
msecs_to_jiffies(1000)))
|
||||||
err = -ETIMEDOUT;
|
err = -ETIMEDOUT;
|
||||||
writel_relaxed(QSPI_SR_CMD_COMPLETED, aq->regs + QSPI_IDR);
|
atmel_qspi_write(QSPI_SR_CMD_COMPLETED, aq, QSPI_IDR);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@ -410,7 +485,7 @@ static int atmel_qspi_setup(struct spi_device *spi)
|
|||||||
scbr--;
|
scbr--;
|
||||||
|
|
||||||
aq->scr = QSPI_SCR_SCBR(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;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -418,14 +493,14 @@ static int atmel_qspi_setup(struct spi_device *spi)
|
|||||||
static void atmel_qspi_init(struct atmel_qspi *aq)
|
static void atmel_qspi_init(struct atmel_qspi *aq)
|
||||||
{
|
{
|
||||||
/* Reset the QSPI controller */
|
/* 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 */
|
/* 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;
|
aq->mr = QSPI_MR_SMM;
|
||||||
|
|
||||||
/* Enable the QSPI controller */
|
/* 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)
|
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;
|
struct atmel_qspi *aq = dev_id;
|
||||||
u32 status, mask, pending;
|
u32 status, mask, pending;
|
||||||
|
|
||||||
status = readl_relaxed(aq->regs + QSPI_SR);
|
status = atmel_qspi_read(aq, QSPI_SR);
|
||||||
mask = readl_relaxed(aq->regs + QSPI_IMR);
|
mask = atmel_qspi_read(aq, QSPI_IMR);
|
||||||
pending = status & mask;
|
pending = status & mask;
|
||||||
|
|
||||||
if (!pending)
|
if (!pending)
|
||||||
@ -569,7 +644,7 @@ static int atmel_qspi_remove(struct platform_device *pdev)
|
|||||||
struct atmel_qspi *aq = spi_controller_get_devdata(ctrl);
|
struct atmel_qspi *aq = spi_controller_get_devdata(ctrl);
|
||||||
|
|
||||||
spi_unregister_controller(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->qspick);
|
||||||
clk_disable_unprepare(aq->pclk);
|
clk_disable_unprepare(aq->pclk);
|
||||||
return 0;
|
return 0;
|
||||||
@ -596,7 +671,7 @@ static int __maybe_unused atmel_qspi_resume(struct device *dev)
|
|||||||
|
|
||||||
atmel_qspi_init(aq);
|
atmel_qspi_init(aq);
|
||||||
|
|
||||||
writel_relaxed(aq->scr, aq->regs + QSPI_SCR);
|
atmel_qspi_write(aq->scr, aq, QSPI_SCR);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
235
drivers/spi/spi-ar934x.c
Normal file
235
drivers/spi/spi-ar934x.c
Normal file
@ -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/io.h>
|
||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
#include <linux/spi/spi_bitbang.h>
|
#include <linux/spi/spi_bitbang.h>
|
||||||
#include <linux/gpio.h>
|
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of_gpio.h>
|
|
||||||
#include <linux/platform_data/efm32-spi.h>
|
#include <linux/platform_data/efm32-spi.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
|
||||||
#define DRIVER_NAME "efm32-spi"
|
#define DRIVER_NAME "efm32-spi"
|
||||||
|
|
||||||
@ -82,9 +81,6 @@ struct efm32_spi_ddata {
|
|||||||
const u8 *tx_buf;
|
const u8 *tx_buf;
|
||||||
u8 *rx_buf;
|
u8 *rx_buf;
|
||||||
unsigned tx_len, rx_len;
|
unsigned tx_len, rx_len;
|
||||||
|
|
||||||
/* chip selects */
|
|
||||||
unsigned csgpio[];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#define ddata_to_dev(ddata) (&(ddata->bitbang.master->dev))
|
#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);
|
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,
|
static int efm32_spi_setup_transfer(struct spi_device *spi,
|
||||||
struct spi_transfer *t)
|
struct spi_transfer *t)
|
||||||
{
|
{
|
||||||
@ -320,17 +308,11 @@ static int efm32_spi_probe(struct platform_device *pdev)
|
|||||||
int ret;
|
int ret;
|
||||||
struct spi_master *master;
|
struct spi_master *master;
|
||||||
struct device_node *np = pdev->dev.of_node;
|
struct device_node *np = pdev->dev.of_node;
|
||||||
int num_cs, i;
|
|
||||||
|
|
||||||
if (!np)
|
if (!np)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
num_cs = of_gpio_named_count(np, "cs-gpios");
|
master = spi_alloc_master(&pdev->dev, sizeof(*ddata));
|
||||||
if (num_cs < 0)
|
|
||||||
return num_cs;
|
|
||||||
|
|
||||||
master = spi_alloc_master(&pdev->dev,
|
|
||||||
sizeof(*ddata) + num_cs * sizeof(unsigned));
|
|
||||||
if (!master) {
|
if (!master) {
|
||||||
dev_dbg(&pdev->dev,
|
dev_dbg(&pdev->dev,
|
||||||
"failed to allocate spi master controller\n");
|
"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->dev.of_node = pdev->dev.of_node;
|
||||||
|
|
||||||
master->num_chipselect = num_cs;
|
|
||||||
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
|
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
|
||||||
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16);
|
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16);
|
||||||
|
master->use_gpio_descriptors = true;
|
||||||
|
|
||||||
ddata = spi_master_get_devdata(master);
|
ddata = spi_master_get_devdata(master);
|
||||||
|
|
||||||
ddata->bitbang.master = master;
|
ddata->bitbang.master = master;
|
||||||
ddata->bitbang.chipselect = efm32_spi_chipselect;
|
|
||||||
ddata->bitbang.setup_transfer = efm32_spi_setup_transfer;
|
ddata->bitbang.setup_transfer = efm32_spi_setup_transfer;
|
||||||
ddata->bitbang.txrx_bufs = efm32_spi_txrx_bufs;
|
ddata->bitbang.txrx_bufs = efm32_spi_txrx_bufs;
|
||||||
|
|
||||||
@ -361,25 +342,6 @@ static int efm32_spi_probe(struct platform_device *pdev)
|
|||||||
goto err;
|
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);
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
if (!res) {
|
if (!res) {
|
||||||
ret = -ENODEV;
|
ret = -ENODEV;
|
||||||
|
558
drivers/spi/spi-fsi.c
Normal file
558
drivers/spi/spi-fsi.c
Normal file
@ -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_RXMSK BIT(19)
|
||||||
#define TCR_TXMSK BIT(18)
|
#define TCR_TXMSK BIT(18)
|
||||||
|
|
||||||
static int clkdivs[] = {1, 2, 4, 8, 16, 32, 64, 128};
|
|
||||||
|
|
||||||
struct lpspi_config {
|
struct lpspi_config {
|
||||||
u8 bpw;
|
u8 bpw;
|
||||||
u8 chip_select;
|
u8 chip_select;
|
||||||
@ -125,7 +123,7 @@ struct fsl_lpspi_data {
|
|||||||
struct completion dma_rx_completion;
|
struct completion dma_rx_completion;
|
||||||
struct completion dma_tx_completion;
|
struct completion dma_tx_completion;
|
||||||
|
|
||||||
int chipselect[0];
|
int chipselect[];
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct of_device_id fsl_lpspi_dt_ids[] = {
|
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++) {
|
for (prescale = 0; prescale < 8; prescale++) {
|
||||||
scldiv = perclk_rate /
|
scldiv = perclk_rate / config.speed_hz / (1 << prescale) - 2;
|
||||||
(clkdivs[prescale] * config.speed_hz) - 2;
|
|
||||||
if (scldiv < 256) {
|
if (scldiv < 256) {
|
||||||
fsl_lpspi->config.prescale = prescale;
|
fsl_lpspi->config.prescale = prescale;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prescale == 8 && scldiv >= 256)
|
if (scldiv >= 256)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
writel(scldiv | (scldiv << 8) | ((scldiv >> 1) << 16),
|
writel(scldiv | (scldiv << 8) | ((scldiv >> 1) << 16),
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/log2.h>
|
#include <linux/log2.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of.h>
|
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/qcom-geni-se.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;
|
struct spi_geni_master *mas;
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
|
|
||||||
irq = platform_get_irq(pdev, 0);
|
irq = platform_get_irq(pdev, 0);
|
||||||
if (irq < 0)
|
if (irq < 0)
|
||||||
@ -545,28 +545,25 @@ static int spi_geni_probe(struct platform_device *pdev)
|
|||||||
if (IS_ERR(base))
|
if (IS_ERR(base))
|
||||||
return PTR_ERR(base);
|
return PTR_ERR(base);
|
||||||
|
|
||||||
clk = devm_clk_get(&pdev->dev, "se");
|
clk = devm_clk_get(dev, "se");
|
||||||
if (IS_ERR(clk)) {
|
if (IS_ERR(clk))
|
||||||
dev_err(&pdev->dev, "Err getting SE Core clk %ld\n",
|
|
||||||
PTR_ERR(clk));
|
|
||||||
return PTR_ERR(clk);
|
return PTR_ERR(clk);
|
||||||
}
|
|
||||||
|
|
||||||
spi = spi_alloc_master(&pdev->dev, sizeof(*mas));
|
spi = spi_alloc_master(dev, sizeof(*mas));
|
||||||
if (!spi)
|
if (!spi)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
platform_set_drvdata(pdev, spi);
|
platform_set_drvdata(pdev, spi);
|
||||||
mas = spi_master_get_devdata(spi);
|
mas = spi_master_get_devdata(spi);
|
||||||
mas->irq = irq;
|
mas->irq = irq;
|
||||||
mas->dev = &pdev->dev;
|
mas->dev = dev;
|
||||||
mas->se.dev = &pdev->dev;
|
mas->se.dev = dev;
|
||||||
mas->se.wrapper = dev_get_drvdata(pdev->dev.parent);
|
mas->se.wrapper = dev_get_drvdata(dev->parent);
|
||||||
mas->se.base = base;
|
mas->se.base = base;
|
||||||
mas->se.clk = clk;
|
mas->se.clk = clk;
|
||||||
|
|
||||||
spi->bus_num = -1;
|
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->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP | SPI_CS_HIGH;
|
||||||
spi->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
|
spi->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
|
||||||
spi->num_chipselect = 4;
|
spi->num_chipselect = 4;
|
||||||
@ -579,14 +576,13 @@ static int spi_geni_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
init_completion(&mas->xfer_done);
|
init_completion(&mas->xfer_done);
|
||||||
spin_lock_init(&mas->lock);
|
spin_lock_init(&mas->lock);
|
||||||
pm_runtime_enable(&pdev->dev);
|
pm_runtime_enable(dev);
|
||||||
|
|
||||||
ret = spi_geni_init(mas);
|
ret = spi_geni_init(mas);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto spi_geni_probe_runtime_disable;
|
goto spi_geni_probe_runtime_disable;
|
||||||
|
|
||||||
ret = request_irq(mas->irq, geni_spi_isr,
|
ret = request_irq(mas->irq, geni_spi_isr, 0, dev_name(dev), spi);
|
||||||
IRQF_TRIGGER_HIGH, "spi_geni", spi);
|
|
||||||
if (ret)
|
if (ret)
|
||||||
goto spi_geni_probe_runtime_disable;
|
goto spi_geni_probe_runtime_disable;
|
||||||
|
|
||||||
@ -598,7 +594,7 @@ static int spi_geni_probe(struct platform_device *pdev)
|
|||||||
spi_geni_probe_free_irq:
|
spi_geni_probe_free_irq:
|
||||||
free_irq(mas->irq, spi);
|
free_irq(mas->irq, spi);
|
||||||
spi_geni_probe_runtime_disable:
|
spi_geni_probe_runtime_disable:
|
||||||
pm_runtime_disable(&pdev->dev);
|
pm_runtime_disable(dev);
|
||||||
spi_master_put(spi);
|
spi_master_put(spi);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include <linux/acpi.h>
|
#include <linux/acpi.h>
|
||||||
#include <linux/bitops.h>
|
#include <linux/bitops.h>
|
||||||
|
#include <linux/dmi.h>
|
||||||
#include <linux/iopoll.h>
|
#include <linux/iopoll.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
@ -17,6 +18,12 @@
|
|||||||
#define HISI_SFC_V3XX_VERSION (0x1f8)
|
#define HISI_SFC_V3XX_VERSION (0x1f8)
|
||||||
|
|
||||||
#define HISI_SFC_V3XX_CMD_CFG (0x300)
|
#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_DATA_CNT_OFF 9
|
||||||
#define HISI_SFC_V3XX_CMD_CFG_RW_MSK BIT(8)
|
#define HISI_SFC_V3XX_CMD_CFG_RW_MSK BIT(8)
|
||||||
#define HISI_SFC_V3XX_CMD_CFG_DATA_EN_MSK BIT(7)
|
#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)
|
if (op->addr.nbytes)
|
||||||
config |= HISI_SFC_V3XX_CMD_CFG_ADDR_EN_MSK;
|
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) {
|
if (op->data.dir != SPI_MEM_NO_DATA) {
|
||||||
config |= (len - 1) << HISI_SFC_V3XX_CMD_CFG_DATA_CNT_OFF;
|
config |= (len - 1) << HISI_SFC_V3XX_CMD_CFG_DATA_CNT_OFF;
|
||||||
config |= HISI_SFC_V3XX_CMD_CFG_DATA_EN_MSK;
|
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,
|
.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)
|
static int hisi_sfc_v3xx_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct device *dev = &pdev->dev;
|
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 |
|
ctlr->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD |
|
||||||
SPI_TX_DUAL | SPI_TX_QUAD;
|
SPI_TX_DUAL | SPI_TX_QUAD;
|
||||||
|
|
||||||
|
ctlr->buswidth_override_bits = hisi_sfc_v3xx_buswidth_override_bits;
|
||||||
|
|
||||||
host = spi_controller_get_devdata(ctlr);
|
host = spi_controller_get_devdata(ctlr);
|
||||||
host->dev = dev;
|
host->dev = dev;
|
||||||
|
|
||||||
@ -277,7 +361,20 @@ static struct platform_driver hisi_sfc_v3xx_spi_driver = {
|
|||||||
.probe = hisi_sfc_v3xx_probe,
|
.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_LICENSE("GPL");
|
||||||
MODULE_AUTHOR("John Garry <john.garry@huawei.com>");
|
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;
|
struct spi_controller *ctlr = mem->spi->controller;
|
||||||
size_t len;
|
size_t len;
|
||||||
|
|
||||||
len = sizeof(op->cmd.opcode) + op->addr.nbytes + op->dummy.nbytes;
|
|
||||||
|
|
||||||
if (ctlr->mem_ops && ctlr->mem_ops->adjust_op_size)
|
if (ctlr->mem_ops && ctlr->mem_ops->adjust_op_size)
|
||||||
return ctlr->mem_ops->adjust_op_size(mem, op);
|
return ctlr->mem_ops->adjust_op_size(mem, op);
|
||||||
|
|
||||||
if (!ctlr->mem_ops || !ctlr->mem_ops->exec_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))
|
if (len > spi_max_transfer_size(mem->spi))
|
||||||
return -EINVAL;
|
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
|
* 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().
|
* 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
|
* 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.
|
* doesn't have to bother implementing a fallback on his own.
|
||||||
*
|
*
|
||||||
* Return: a valid pointer in case of success, and ERR_PTR() otherwise.
|
* Return: a valid pointer in case of success, and ERR_PTR() otherwise.
|
||||||
|
@ -9,11 +9,13 @@
|
|||||||
|
|
||||||
#include <linux/bitfield.h>
|
#include <linux/bitfield.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
|
#include <linux/clk-provider.h>
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
@ -33,7 +35,6 @@
|
|||||||
* to have a CS go down over the full transfer
|
* to have a CS go down over the full transfer
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define SPICC_MAX_FREQ 30000000
|
|
||||||
#define SPICC_MAX_BURST 128
|
#define SPICC_MAX_BURST 128
|
||||||
|
|
||||||
/* Register Map */
|
/* Register Map */
|
||||||
@ -105,7 +106,21 @@
|
|||||||
#define SPICC_SWAP_RO BIT(14) /* RX FIFO Data Swap Read-Only */
|
#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_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_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_RO_MASK GENMASK(22, 21) /* FIFO Softreset Read-Only */
|
||||||
#define SPICC_FIFORST_W1_MASK GENMASK(23, 22) /* FIFO Softreset Write-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_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) \
|
#define writel_bits_relaxed(mask, val, addr) \
|
||||||
writel_relaxed((readl_relaxed(addr) & ~(mask)) | (val), addr)
|
writel_relaxed((readl_relaxed(addr) & ~(mask)) | (val), addr)
|
||||||
|
|
||||||
#define SPICC_BURST_MAX 16
|
struct meson_spicc_data {
|
||||||
#define SPICC_FIFO_HALF 10
|
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 meson_spicc_device {
|
||||||
struct spi_master *master;
|
struct spi_master *master;
|
||||||
struct platform_device *pdev;
|
struct platform_device *pdev;
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
struct clk *core;
|
struct clk *core;
|
||||||
|
struct clk *pclk;
|
||||||
|
struct clk *clk;
|
||||||
struct spi_message *message;
|
struct spi_message *message;
|
||||||
struct spi_transfer *xfer;
|
struct spi_transfer *xfer;
|
||||||
|
const struct meson_spicc_data *data;
|
||||||
u8 *tx_buf;
|
u8 *tx_buf;
|
||||||
u8 *rx_buf;
|
u8 *rx_buf;
|
||||||
unsigned int bytes_per_word;
|
unsigned int bytes_per_word;
|
||||||
unsigned long tx_remain;
|
unsigned long tx_remain;
|
||||||
unsigned long txb_remain;
|
|
||||||
unsigned long rx_remain;
|
unsigned long rx_remain;
|
||||||
unsigned long rxb_remain;
|
|
||||||
unsigned long xfer_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)
|
static inline bool meson_spicc_txfull(struct meson_spicc_device *spicc)
|
||||||
{
|
{
|
||||||
return !!FIELD_GET(SPICC_TF,
|
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)
|
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));
|
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);
|
spicc->base + SPICC_TXDATA);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline u32 meson_spicc_setup_rx_irq(struct meson_spicc_device *spicc,
|
static inline void meson_spicc_setup_burst(struct meson_spicc_device *spicc)
|
||||||
u32 irq_ctrl)
|
|
||||||
{
|
{
|
||||||
if (spicc->rx_remain > SPICC_FIFO_HALF)
|
|
||||||
irq_ctrl |= SPICC_RH_EN;
|
|
||||||
else
|
|
||||||
irq_ctrl |= SPICC_RR_EN;
|
|
||||||
|
|
||||||
return irq_ctrl;
|
unsigned int burst_len = min_t(unsigned int,
|
||||||
}
|
spicc->xfer_remain /
|
||||||
|
spicc->bytes_per_word,
|
||||||
static inline void meson_spicc_setup_burst(struct meson_spicc_device *spicc,
|
spicc->data->fifo_size);
|
||||||
unsigned int burst_len)
|
|
||||||
{
|
|
||||||
/* Setup Xfer variables */
|
/* Setup Xfer variables */
|
||||||
spicc->tx_remain = burst_len;
|
spicc->tx_remain = burst_len;
|
||||||
spicc->rx_remain = burst_len;
|
spicc->rx_remain = burst_len;
|
||||||
spicc->xfer_remain -= burst_len * spicc->bytes_per_word;
|
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 */
|
/* Setup burst length */
|
||||||
writel_bits_relaxed(SPICC_BURSTLENGTH_MASK,
|
writel_bits_relaxed(SPICC_BURSTLENGTH_MASK,
|
||||||
FIELD_PREP(SPICC_BURSTLENGTH_MASK,
|
FIELD_PREP(SPICC_BURSTLENGTH_MASK,
|
||||||
burst_len),
|
burst_len - 1),
|
||||||
spicc->base + SPICC_CONREG);
|
spicc->base + SPICC_CONREG);
|
||||||
|
|
||||||
/* Fill TX FIFO */
|
/* 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)
|
static irqreturn_t meson_spicc_irq(int irq, void *data)
|
||||||
{
|
{
|
||||||
struct meson_spicc_device *spicc = (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 */
|
/* Empty RX FIFO */
|
||||||
meson_spicc_rx(spicc);
|
meson_spicc_rx(spicc);
|
||||||
|
|
||||||
/* Enable TC interrupt since we transferred everything */
|
if (!spicc->xfer_remain) {
|
||||||
if (!spicc->tx_remain && !spicc->rx_remain) {
|
/* Disable all IRQs */
|
||||||
spicc->is_burst_end = true;
|
writel(0, spicc->base + SPICC_INTREG);
|
||||||
|
|
||||||
/* Enable TC interrupt */
|
spi_finalize_current_transfer(spicc->master);
|
||||||
ctrl |= SPICC_TC_EN;
|
|
||||||
|
|
||||||
/* Reload IRQ status */
|
return IRQ_HANDLED;
|
||||||
stat = readl_relaxed(spicc->base + SPICC_STATREG) & ctrl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check transfer complete */
|
/* Setup burst */
|
||||||
if ((stat & SPICC_TC) && spicc->is_burst_end) {
|
meson_spicc_setup_burst(spicc);
|
||||||
unsigned int burst_len;
|
|
||||||
|
|
||||||
/* Clear TC bit */
|
/* Start burst */
|
||||||
writel_relaxed(SPICC_TC, spicc->base + SPICC_STATREG);
|
writel_bits_relaxed(SPICC_XCH, SPICC_XCH, spicc->base + SPICC_CONREG);
|
||||||
|
|
||||||
/* 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);
|
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static u32 meson_spicc_setup_speed(struct meson_spicc_device *spicc, u32 conf,
|
static void meson_spicc_auto_io_delay(struct meson_spicc_device *spicc)
|
||||||
u32 speed)
|
|
||||||
{
|
{
|
||||||
unsigned long parent, value;
|
u32 div, hz;
|
||||||
unsigned int i, div;
|
u32 mi_delay, cap_delay;
|
||||||
|
u32 conf;
|
||||||
|
|
||||||
parent = clk_get_rate(spicc->core);
|
if (spicc->data->has_enhance_clk_div) {
|
||||||
|
div = FIELD_GET(SPICC_ENH_DATARATE_MASK,
|
||||||
/* Find closest inferior/equal possible speed */
|
readl_relaxed(spicc->base + SPICC_ENH_CTL0));
|
||||||
for (i = 0 ; i < 7 ; ++i) {
|
div++;
|
||||||
/* 2^(data_rate+2) */
|
div <<= 1;
|
||||||
value = parent >> (i + 2);
|
} else {
|
||||||
|
div = FIELD_GET(SPICC_DATARATE_MASK,
|
||||||
if (value <= speed)
|
readl_relaxed(spicc->base + SPICC_CONREG));
|
||||||
break;
|
div += 2;
|
||||||
|
div = 1 << div;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If provided speed it lower than max divider, use max divider */
|
mi_delay = SPICC_MI_NO_DELAY;
|
||||||
if (i > 7) {
|
cap_delay = SPICC_CAP_AHEAD_2_CYCLE;
|
||||||
div = 7;
|
hz = clk_get_rate(spicc->clk);
|
||||||
dev_warn_once(&spicc->pdev->dev, "unable to get close to speed %u\n",
|
|
||||||
speed);
|
|
||||||
} else
|
|
||||||
div = i;
|
|
||||||
|
|
||||||
dev_dbg(&spicc->pdev->dev, "parent %lu, speed %u -> %lu (%u)\n",
|
if (hz >= 100000000)
|
||||||
parent, speed, value, div);
|
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 = readl_relaxed(spicc->base + SPICC_TESTREG);
|
||||||
conf |= FIELD_PREP(SPICC_DATARATE_MASK, div);
|
conf &= ~(SPICC_MO_DELAY_MASK | SPICC_MI_DELAY_MASK
|
||||||
|
| SPICC_MI_CAP_DELAY_MASK);
|
||||||
return conf;
|
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,
|
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 */
|
/* Read original configuration */
|
||||||
conf = conf_orig = readl_relaxed(spicc->base + SPICC_CONREG);
|
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 */
|
/* Setup word width */
|
||||||
conf &= ~SPICC_BITLENGTH_MASK;
|
conf &= ~SPICC_BITLENGTH_MASK;
|
||||||
conf |= FIELD_PREP(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 */
|
/* Ignore if unchanged */
|
||||||
if (conf != conf_orig)
|
if (conf != conf_orig)
|
||||||
writel_relaxed(conf, spicc->base + SPICC_CONREG);
|
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,
|
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 spi_transfer *xfer)
|
||||||
{
|
{
|
||||||
struct meson_spicc_device *spicc = spi_master_get_devdata(master);
|
struct meson_spicc_device *spicc = spi_master_get_devdata(master);
|
||||||
unsigned int burst_len;
|
|
||||||
u32 irq = 0;
|
|
||||||
|
|
||||||
/* Store current transfer */
|
/* Store current transfer */
|
||||||
spicc->xfer = xfer;
|
spicc->xfer = xfer;
|
||||||
@ -372,22 +398,22 @@ static int meson_spicc_transfer_one(struct spi_master *master,
|
|||||||
spicc->bytes_per_word =
|
spicc->bytes_per_word =
|
||||||
DIV_ROUND_UP(spicc->xfer->bits_per_word, 8);
|
DIV_ROUND_UP(spicc->xfer->bits_per_word, 8);
|
||||||
|
|
||||||
|
if (xfer->len % spicc->bytes_per_word)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
/* Setup transfer parameters */
|
/* Setup transfer parameters */
|
||||||
meson_spicc_setup_xfer(spicc, xfer);
|
meson_spicc_setup_xfer(spicc, xfer);
|
||||||
|
|
||||||
burst_len = min_t(unsigned int,
|
meson_spicc_reset_fifo(spicc);
|
||||||
spicc->xfer_remain / spicc->bytes_per_word,
|
|
||||||
SPICC_BURST_MAX);
|
|
||||||
|
|
||||||
meson_spicc_setup_burst(spicc, burst_len);
|
/* Setup burst */
|
||||||
|
meson_spicc_setup_burst(spicc);
|
||||||
irq = meson_spicc_setup_rx_irq(spicc, irq);
|
|
||||||
|
|
||||||
/* Start burst */
|
/* Start burst */
|
||||||
writel_bits_relaxed(SPICC_XCH, SPICC_XCH, spicc->base + SPICC_CONREG);
|
writel_bits_relaxed(SPICC_XCH, SPICC_XCH, spicc->base + SPICC_CONREG);
|
||||||
|
|
||||||
/* Enable interrupts */
|
/* Enable interrupts */
|
||||||
writel_relaxed(irq, spicc->base + SPICC_INTREG);
|
writel_relaxed(SPICC_TC_EN, spicc->base + SPICC_INTREG);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -444,7 +470,7 @@ static int meson_spicc_prepare_message(struct spi_master *master,
|
|||||||
/* Setup no wait cycles by default */
|
/* Setup no wait cycles by default */
|
||||||
writel_relaxed(0, spicc->base + SPICC_PERIODREG);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -456,9 +482,6 @@ static int meson_spicc_unprepare_transfer(struct spi_master *master)
|
|||||||
/* Disable all IRQs */
|
/* Disable all IRQs */
|
||||||
writel(0, spicc->base + SPICC_INTREG);
|
writel(0, spicc->base + SPICC_INTREG);
|
||||||
|
|
||||||
/* Disable controller */
|
|
||||||
writel_bits_relaxed(SPICC_ENABLE, 0, spicc->base + SPICC_CONREG);
|
|
||||||
|
|
||||||
device_reset_optional(&spicc->pdev->dev);
|
device_reset_optional(&spicc->pdev->dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -477,11 +500,167 @@ static void meson_spicc_cleanup(struct spi_device *spi)
|
|||||||
spi->controller_state = NULL;
|
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)
|
static int meson_spicc_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct spi_master *master;
|
struct spi_master *master;
|
||||||
struct meson_spicc_device *spicc;
|
struct meson_spicc_device *spicc;
|
||||||
int ret, irq, rate;
|
int ret, irq;
|
||||||
|
|
||||||
master = spi_alloc_master(&pdev->dev, sizeof(*spicc));
|
master = spi_alloc_master(&pdev->dev, sizeof(*spicc));
|
||||||
if (!master) {
|
if (!master) {
|
||||||
@ -491,6 +670,13 @@ static int meson_spicc_probe(struct platform_device *pdev)
|
|||||||
spicc = spi_master_get_devdata(master);
|
spicc = spi_master_get_devdata(master);
|
||||||
spicc->master = 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;
|
spicc->pdev = pdev;
|
||||||
platform_set_drvdata(pdev, spicc);
|
platform_set_drvdata(pdev, spicc);
|
||||||
|
|
||||||
@ -501,6 +687,10 @@ static int meson_spicc_probe(struct platform_device *pdev)
|
|||||||
goto out_master;
|
goto out_master;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Set master mode and enable controller */
|
||||||
|
writel_relaxed(SPICC_ENABLE | SPICC_MODE_MASTER,
|
||||||
|
spicc->base + SPICC_CONREG);
|
||||||
|
|
||||||
/* Disable all IRQs */
|
/* Disable all IRQs */
|
||||||
writel_relaxed(0, spicc->base + SPICC_INTREG);
|
writel_relaxed(0, spicc->base + SPICC_INTREG);
|
||||||
|
|
||||||
@ -519,12 +709,26 @@ static int meson_spicc_probe(struct platform_device *pdev)
|
|||||||
goto out_master;
|
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);
|
ret = clk_prepare_enable(spicc->core);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev, "core clock enable failed\n");
|
dev_err(&pdev->dev, "core clock enable failed\n");
|
||||||
goto out_master;
|
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);
|
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(16) |
|
||||||
SPI_BPW_MASK(8);
|
SPI_BPW_MASK(8);
|
||||||
master->flags = (SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX);
|
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->setup = meson_spicc_setup;
|
||||||
master->cleanup = meson_spicc_cleanup;
|
master->cleanup = meson_spicc_cleanup;
|
||||||
master->prepare_message = meson_spicc_prepare_message;
|
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->transfer_one = meson_spicc_transfer_one;
|
||||||
master->use_gpio_descriptors = true;
|
master->use_gpio_descriptors = true;
|
||||||
|
|
||||||
/* Setup max rate according to the Meson GX datasheet */
|
meson_spicc_oen_enable(spicc);
|
||||||
if ((rate >> 2) > SPICC_MAX_FREQ)
|
|
||||||
master->max_speed_hz = SPICC_MAX_FREQ;
|
ret = meson_spicc_clk_init(spicc);
|
||||||
else
|
if (ret) {
|
||||||
master->max_speed_hz = rate >> 2;
|
dev_err(&pdev->dev, "clock registration failed\n");
|
||||||
|
goto out_master;
|
||||||
|
}
|
||||||
|
|
||||||
ret = devm_spi_register_master(&pdev->dev, master);
|
ret = devm_spi_register_master(&pdev->dev, master);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
@ -560,6 +767,7 @@ static int meson_spicc_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
out_clk:
|
out_clk:
|
||||||
clk_disable_unprepare(spicc->core);
|
clk_disable_unprepare(spicc->core);
|
||||||
|
clk_disable_unprepare(spicc->pclk);
|
||||||
|
|
||||||
out_master:
|
out_master:
|
||||||
spi_master_put(master);
|
spi_master_put(master);
|
||||||
@ -575,13 +783,47 @@ static int meson_spicc_remove(struct platform_device *pdev)
|
|||||||
writel(0, spicc->base + SPICC_CONREG);
|
writel(0, spicc->base + SPICC_CONREG);
|
||||||
|
|
||||||
clk_disable_unprepare(spicc->core);
|
clk_disable_unprepare(spicc->core);
|
||||||
|
clk_disable_unprepare(spicc->pclk);
|
||||||
|
|
||||||
return 0;
|
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[] = {
|
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 */ }
|
{ /* sentinel */ }
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, meson_spicc_of_match);
|
MODULE_DEVICE_TABLE(of, meson_spicc_of_match);
|
||||||
|
689
drivers/spi/spi-mtk-nor.c
Normal file
689
drivers/spi/spi-mtk-nor.c
Normal file
@ -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);
|
187
drivers/spi/spi-mux.c
Normal file
187
drivers/spi/spi-mux.c
Normal file
@ -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/ioport.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
#include <linux/of_gpio.h>
|
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
@ -32,7 +31,7 @@
|
|||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/completion.h>
|
#include <linux/completion.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/pinctrl/consumer.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
@ -307,6 +307,7 @@
|
|||||||
|
|
||||||
#define POLL_TOUT 5000
|
#define POLL_TOUT 5000
|
||||||
#define NXP_FSPI_MAX_CHIPSELECT 4
|
#define NXP_FSPI_MAX_CHIPSELECT 4
|
||||||
|
#define NXP_FSPI_MIN_IOMAP SZ_4M
|
||||||
|
|
||||||
struct nxp_fspi_devtype_data {
|
struct nxp_fspi_devtype_data {
|
||||||
unsigned int rxfifo;
|
unsigned int rxfifo;
|
||||||
@ -324,11 +325,29 @@ static const struct nxp_fspi_devtype_data lx2160a_data = {
|
|||||||
.little_endian = true, /* little-endian */
|
.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 {
|
struct nxp_fspi {
|
||||||
void __iomem *iobase;
|
void __iomem *iobase;
|
||||||
void __iomem *ahb_addr;
|
void __iomem *ahb_addr;
|
||||||
u32 memmap_phy;
|
u32 memmap_phy;
|
||||||
u32 memmap_phy_size;
|
u32 memmap_phy_size;
|
||||||
|
u32 memmap_start;
|
||||||
|
u32 memmap_len;
|
||||||
struct clk *clk, *clk_en;
|
struct clk *clk, *clk_en;
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
struct completion c;
|
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;
|
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;
|
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. */
|
/* 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,
|
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) &&
|
if (op->data.nbytes > (f->devtype_data->rxfifo - 4) &&
|
||||||
op->data.dir == SPI_MEM_DATA_IN) {
|
op->data.dir == SPI_MEM_DATA_IN) {
|
||||||
nxp_fspi_read_ahb(f, op);
|
err = nxp_fspi_read_ahb(f, op);
|
||||||
} else {
|
} else {
|
||||||
if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_OUT)
|
if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_OUT)
|
||||||
nxp_fspi_fill_txfifo(f, op);
|
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);
|
fspi_writel(f, FSPI_DLLBCR_OVRDEN, base + FSPI_DLLBCR);
|
||||||
|
|
||||||
/* enable module */
|
/* enable module */
|
||||||
fspi_writel(f, FSPI_MCR0_AHB_TIMEOUT(0xFF) | FSPI_MCR0_IP_TIMEOUT(0xFF),
|
fspi_writel(f, FSPI_MCR0_AHB_TIMEOUT(0xFF) |
|
||||||
base + FSPI_MCR0);
|
FSPI_MCR0_IP_TIMEOUT(0xFF) | (u32) FSPI_MCR0_OCTCOMB_EN,
|
||||||
|
base + FSPI_MCR0);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Disable same device enable bit and configure all slave devices
|
* 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 */
|
/* find the resources - controller memory mapped space */
|
||||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fspi_mmap");
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fspi_mmap");
|
||||||
f->ahb_addr = devm_ioremap_resource(dev, res);
|
if (!res) {
|
||||||
if (IS_ERR(f->ahb_addr)) {
|
ret = -ENODEV;
|
||||||
ret = PTR_ERR(f->ahb_addr);
|
|
||||||
goto err_put_ctrl;
|
goto err_put_ctrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1057,6 +1099,9 @@ static int nxp_fspi_remove(struct platform_device *pdev)
|
|||||||
|
|
||||||
mutex_destroy(&f->lock);
|
mutex_destroy(&f->lock);
|
||||||
|
|
||||||
|
if (f->ahb_addr)
|
||||||
|
iounmap(f->ahb_addr);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1076,6 +1121,8 @@ static int nxp_fspi_resume(struct device *dev)
|
|||||||
|
|
||||||
static const struct of_device_id nxp_fspi_dt_ids[] = {
|
static const struct of_device_id nxp_fspi_dt_ids[] = {
|
||||||
{ .compatible = "nxp,lx2160a-fspi", .data = (void *)&lx2160a_data, },
|
{ .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 */ }
|
{ /* sentinel */ }
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, nxp_fspi_dt_ids);
|
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;
|
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)
|
static u32 pxa2xx_spi_get_ssrc1_change_mask(const struct driver_data *drv_data)
|
||||||
{
|
{
|
||||||
switch (drv_data->ssp_type) {
|
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)
|
static void pxa2xx_spi_off(struct driver_data *drv_data)
|
||||||
{
|
{
|
||||||
/* On MMP, disabling SSE seems to corrupt the rx fifo */
|
/* On MMP, disabling SSE seems to corrupt the Rx FIFO */
|
||||||
if (drv_data->ssp_type == MMP2_SSP)
|
if (is_mmp2_ssp(drv_data))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
pxa2xx_spi_write(drv_data, SSCR0,
|
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)
|
|| (pxa2xx_spi_read(drv_data, SSCR1) & change_mask)
|
||||||
!= (cr1 & change_mask)) {
|
!= (cr1 & change_mask)) {
|
||||||
/* stop the SSP, and update the other bits */
|
/* 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);
|
pxa2xx_spi_write(drv_data, SSCR0, cr0 & ~SSCR0_SSE);
|
||||||
if (!pxa25x_ssp_comp(drv_data))
|
if (!pxa25x_ssp_comp(drv_data))
|
||||||
pxa2xx_spi_write(drv_data, SSTO, chip->timeout);
|
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);
|
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)
|
u8 tx_level = (pxa2xx_spi_read(drv_data, SSSR)
|
||||||
& SSSR_TFL_MASK) >> 8;
|
& SSSR_TFL_MASK) >> 8;
|
||||||
|
|
||||||
@ -1571,18 +1576,18 @@ pxa2xx_spi_init_pdata(struct platform_device *pdev)
|
|||||||
else if (pcidev_id)
|
else if (pcidev_id)
|
||||||
type = (enum pxa_ssp_type)pcidev_id->driver_data;
|
type = (enum pxa_ssp_type)pcidev_id->driver_data;
|
||||||
else
|
else
|
||||||
return NULL;
|
return ERR_PTR(-EINVAL);
|
||||||
|
|
||||||
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
|
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
|
||||||
if (!pdata)
|
if (!pdata)
|
||||||
return NULL;
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
ssp = &pdata->ssp;
|
ssp = &pdata->ssp;
|
||||||
|
|
||||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
ssp->mmio_base = devm_ioremap_resource(&pdev->dev, res);
|
ssp->mmio_base = devm_ioremap_resource(&pdev->dev, res);
|
||||||
if (IS_ERR(ssp->mmio_base))
|
if (IS_ERR(ssp->mmio_base))
|
||||||
return NULL;
|
return ERR_CAST(ssp->mmio_base);
|
||||||
|
|
||||||
ssp->phys_base = res->start;
|
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);
|
ssp->clk = devm_clk_get(&pdev->dev, NULL);
|
||||||
if (IS_ERR(ssp->clk))
|
if (IS_ERR(ssp->clk))
|
||||||
return NULL;
|
return ERR_CAST(ssp->clk);
|
||||||
|
|
||||||
ssp->irq = platform_get_irq(pdev, 0);
|
ssp->irq = platform_get_irq(pdev, 0);
|
||||||
if (ssp->irq < 0)
|
if (ssp->irq < 0)
|
||||||
return NULL;
|
return ERR_PTR(ssp->irq);
|
||||||
|
|
||||||
ssp->type = type;
|
ssp->type = type;
|
||||||
ssp->dev = &pdev->dev;
|
ssp->dev = &pdev->dev;
|
||||||
@ -1657,9 +1662,9 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
|
|||||||
platform_info = dev_get_platdata(dev);
|
platform_info = dev_get_platdata(dev);
|
||||||
if (!platform_info) {
|
if (!platform_info) {
|
||||||
platform_info = pxa2xx_spi_init_pdata(pdev);
|
platform_info = pxa2xx_spi_init_pdata(pdev);
|
||||||
if (!platform_info) {
|
if (IS_ERR(platform_info)) {
|
||||||
dev_err(&pdev->dev, "missing platform data\n");
|
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)
|
static int pxa2xx_spi_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct driver_data *drv_data = platform_get_drvdata(pdev);
|
struct driver_data *drv_data = platform_get_drvdata(pdev);
|
||||||
struct ssp_device *ssp;
|
struct ssp_device *ssp = drv_data->ssp;
|
||||||
|
|
||||||
if (!drv_data)
|
|
||||||
return 0;
|
|
||||||
ssp = drv_data->ssp;
|
|
||||||
|
|
||||||
pm_runtime_get_sync(&pdev->dev);
|
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[] = {
|
static const struct of_device_id rockchip_spi_dt_match[] = {
|
||||||
{ .compatible = "rockchip,rv1108-spi", },
|
{ .compatible = "rockchip,px30-spi", },
|
||||||
{ .compatible = "rockchip,rk3036-spi", },
|
{ .compatible = "rockchip,rk3036-spi", },
|
||||||
{ .compatible = "rockchip,rk3066-spi", },
|
{ .compatible = "rockchip,rk3066-spi", },
|
||||||
{ .compatible = "rockchip,rk3188-spi", },
|
{ .compatible = "rockchip,rk3188-spi", },
|
||||||
{ .compatible = "rockchip,rk3228-spi", },
|
{ .compatible = "rockchip,rk3228-spi", },
|
||||||
{ .compatible = "rockchip,rk3288-spi", },
|
{ .compatible = "rockchip,rk3288-spi", },
|
||||||
|
{ .compatible = "rockchip,rk3308-spi", },
|
||||||
|
{ .compatible = "rockchip,rk3328-spi", },
|
||||||
{ .compatible = "rockchip,rk3368-spi", },
|
{ .compatible = "rockchip,rk3368-spi", },
|
||||||
{ .compatible = "rockchip,rk3399-spi", },
|
{ .compatible = "rockchip,rk3399-spi", },
|
||||||
|
{ .compatible = "rockchip,rv1108-spi", },
|
||||||
{ },
|
{ },
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, rockchip_spi_dt_match);
|
MODULE_DEVICE_TABLE(of, rockchip_spi_dt_match);
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
#include <linux/sh_dma.h>
|
#include <linux/sh_dma.h>
|
||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
#include <linux/spi/rspi.h>
|
#include <linux/spi/rspi.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
|
|
||||||
#define RSPI_SPCR 0x00 /* Control Register */
|
#define RSPI_SPCR 0x00 /* Control Register */
|
||||||
#define RSPI_SSLP 0x01 /* Slave Select Polarity Register */
|
#define RSPI_SSLP 0x01 /* Slave Select Polarity Register */
|
||||||
@ -79,8 +80,7 @@
|
|||||||
#define SPCR_BSWAP 0x01 /* Byte Swap of read-data for DMAC */
|
#define SPCR_BSWAP 0x01 /* Byte Swap of read-data for DMAC */
|
||||||
|
|
||||||
/* SSLP - Slave Select Polarity Register */
|
/* SSLP - Slave Select Polarity Register */
|
||||||
#define SSLP_SSL1P 0x02 /* SSL1 Signal Polarity Setting */
|
#define SSLP_SSLP(i) BIT(i) /* SSLi Signal Polarity Setting */
|
||||||
#define SSLP_SSL0P 0x01 /* SSL0 Signal Polarity Setting */
|
|
||||||
|
|
||||||
/* SPPCR - Pin Control Register */
|
/* SPPCR - Pin Control Register */
|
||||||
#define SPPCR_MOIFE 0x20 /* MOSI Idle Value Fixing Enable */
|
#define SPPCR_MOIFE 0x20 /* MOSI Idle Value Fixing Enable */
|
||||||
@ -181,7 +181,9 @@ struct rspi_data {
|
|||||||
void __iomem *addr;
|
void __iomem *addr;
|
||||||
u32 max_speed_hz;
|
u32 max_speed_hz;
|
||||||
struct spi_controller *ctlr;
|
struct spi_controller *ctlr;
|
||||||
|
struct platform_device *pdev;
|
||||||
wait_queue_head_t wait;
|
wait_queue_head_t wait;
|
||||||
|
spinlock_t lock; /* Protects RMW-access to RSPI_SSLP */
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
u16 spcmd;
|
u16 spcmd;
|
||||||
u8 spsr;
|
u8 spsr;
|
||||||
@ -239,7 +241,7 @@ struct spi_ops {
|
|||||||
int (*set_config_register)(struct rspi_data *rspi, int access_size);
|
int (*set_config_register)(struct rspi_data *rspi, int access_size);
|
||||||
int (*transfer_one)(struct spi_controller *ctlr,
|
int (*transfer_one)(struct spi_controller *ctlr,
|
||||||
struct spi_device *spi, struct spi_transfer *xfer);
|
struct spi_device *spi, struct spi_transfer *xfer);
|
||||||
u16 mode_bits;
|
u16 extra_mode_bits;
|
||||||
u16 flags;
|
u16 flags;
|
||||||
u16 fifo_size;
|
u16 fifo_size;
|
||||||
u8 num_hw_ss;
|
u8 num_hw_ss;
|
||||||
@ -919,6 +921,29 @@ static int qspi_setup_sequencer(struct rspi_data *rspi,
|
|||||||
return 0;
|
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,
|
static int rspi_prepare_message(struct spi_controller *ctlr,
|
||||||
struct spi_message *msg)
|
struct spi_message *msg)
|
||||||
{
|
{
|
||||||
@ -933,6 +958,8 @@ static int rspi_prepare_message(struct spi_controller *ctlr,
|
|||||||
rspi->spcmd |= SPCMD_CPOL;
|
rspi->spcmd |= SPCMD_CPOL;
|
||||||
if (spi->mode & SPI_CPHA)
|
if (spi->mode & SPI_CPHA)
|
||||||
rspi->spcmd |= SPCMD_CPHA;
|
rspi->spcmd |= SPCMD_CPHA;
|
||||||
|
if (spi->mode & SPI_LSB_FIRST)
|
||||||
|
rspi->spcmd |= SPCMD_LSBF;
|
||||||
|
|
||||||
/* Configure slave signal to assert */
|
/* Configure slave signal to assert */
|
||||||
rspi->spcmd |= SPCMD_SSLA(spi->cs_gpiod ? rspi->ctlr->unused_native_cs
|
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 = {
|
static const struct spi_ops rspi_ops = {
|
||||||
.set_config_register = rspi_set_config_register,
|
.set_config_register = rspi_set_config_register,
|
||||||
.transfer_one = rspi_transfer_one,
|
.transfer_one = rspi_transfer_one,
|
||||||
.mode_bits = SPI_CPHA | SPI_CPOL | SPI_LOOP,
|
|
||||||
.flags = SPI_CONTROLLER_MUST_TX,
|
.flags = SPI_CONTROLLER_MUST_TX,
|
||||||
.fifo_size = 8,
|
.fifo_size = 8,
|
||||||
.num_hw_ss = 2,
|
.num_hw_ss = 2,
|
||||||
@ -1131,7 +1157,6 @@ static const struct spi_ops rspi_ops = {
|
|||||||
static const struct spi_ops rspi_rz_ops = {
|
static const struct spi_ops rspi_rz_ops = {
|
||||||
.set_config_register = rspi_rz_set_config_register,
|
.set_config_register = rspi_rz_set_config_register,
|
||||||
.transfer_one = rspi_rz_transfer_one,
|
.transfer_one = rspi_rz_transfer_one,
|
||||||
.mode_bits = SPI_CPHA | SPI_CPOL | SPI_LOOP,
|
|
||||||
.flags = SPI_CONTROLLER_MUST_RX | SPI_CONTROLLER_MUST_TX,
|
.flags = SPI_CONTROLLER_MUST_RX | SPI_CONTROLLER_MUST_TX,
|
||||||
.fifo_size = 8, /* 8 for TX, 32 for RX */
|
.fifo_size = 8, /* 8 for TX, 32 for RX */
|
||||||
.num_hw_ss = 1,
|
.num_hw_ss = 1,
|
||||||
@ -1140,8 +1165,7 @@ static const struct spi_ops rspi_rz_ops = {
|
|||||||
static const struct spi_ops qspi_ops = {
|
static const struct spi_ops qspi_ops = {
|
||||||
.set_config_register = qspi_set_config_register,
|
.set_config_register = qspi_set_config_register,
|
||||||
.transfer_one = qspi_transfer_one,
|
.transfer_one = qspi_transfer_one,
|
||||||
.mode_bits = SPI_CPHA | SPI_CPOL | SPI_LOOP |
|
.extra_mode_bits = SPI_TX_DUAL | SPI_TX_QUAD |
|
||||||
SPI_TX_DUAL | SPI_TX_QUAD |
|
|
||||||
SPI_RX_DUAL | SPI_RX_QUAD,
|
SPI_RX_DUAL | SPI_RX_QUAD,
|
||||||
.flags = SPI_CONTROLLER_MUST_RX | SPI_CONTROLLER_MUST_TX,
|
.flags = SPI_CONTROLLER_MUST_RX | SPI_CONTROLLER_MUST_TX,
|
||||||
.fifo_size = 32,
|
.fifo_size = 32,
|
||||||
@ -1249,16 +1273,20 @@ static int rspi_probe(struct platform_device *pdev)
|
|||||||
goto error1;
|
goto error1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rspi->pdev = pdev;
|
||||||
pm_runtime_enable(&pdev->dev);
|
pm_runtime_enable(&pdev->dev);
|
||||||
|
|
||||||
init_waitqueue_head(&rspi->wait);
|
init_waitqueue_head(&rspi->wait);
|
||||||
|
spin_lock_init(&rspi->lock);
|
||||||
|
|
||||||
ctlr->bus_num = pdev->id;
|
ctlr->bus_num = pdev->id;
|
||||||
|
ctlr->setup = rspi_setup;
|
||||||
ctlr->auto_runtime_pm = true;
|
ctlr->auto_runtime_pm = true;
|
||||||
ctlr->transfer_one = ops->transfer_one;
|
ctlr->transfer_one = ops->transfer_one;
|
||||||
ctlr->prepare_message = rspi_prepare_message;
|
ctlr->prepare_message = rspi_prepare_message;
|
||||||
ctlr->unprepare_message = rspi_unprepare_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->flags = ops->flags;
|
||||||
ctlr->dev.of_node = pdev->dev.of_node;
|
ctlr->dev.of_node = pdev->dev.of_node;
|
||||||
ctlr->use_gpio_descriptors = true;
|
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 {
|
struct spi_fiq_code {
|
||||||
u32 length;
|
u32 length;
|
||||||
u32 ack_offset;
|
u32 ack_offset;
|
||||||
u8 data[0];
|
u8 data[];
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct spi_fiq_code s3c24xx_spi_fiq_txrx;
|
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);
|
qspi->io_base = devm_ioremap_resource(dev, res);
|
||||||
if (IS_ERR(qspi->io_base)) {
|
if (IS_ERR(qspi->io_base)) {
|
||||||
ret = PTR_ERR(qspi->io_base);
|
ret = PTR_ERR(qspi->io_base);
|
||||||
goto err;
|
goto err_master_put;
|
||||||
}
|
}
|
||||||
|
|
||||||
qspi->phys_base = res->start;
|
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);
|
qspi->mm_base = devm_ioremap_resource(dev, res);
|
||||||
if (IS_ERR(qspi->mm_base)) {
|
if (IS_ERR(qspi->mm_base)) {
|
||||||
ret = PTR_ERR(qspi->mm_base);
|
ret = PTR_ERR(qspi->mm_base);
|
||||||
goto err;
|
goto err_master_put;
|
||||||
}
|
}
|
||||||
|
|
||||||
qspi->mm_size = resource_size(res);
|
qspi->mm_size = resource_size(res);
|
||||||
if (qspi->mm_size > STM32_QSPI_MAX_MMAP_SZ) {
|
if (qspi->mm_size > STM32_QSPI_MAX_MMAP_SZ) {
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto err;
|
goto err_master_put;
|
||||||
}
|
}
|
||||||
|
|
||||||
irq = platform_get_irq(pdev, 0);
|
irq = platform_get_irq(pdev, 0);
|
||||||
if (irq < 0)
|
if (irq < 0) {
|
||||||
return irq;
|
ret = irq;
|
||||||
|
goto err_master_put;
|
||||||
|
}
|
||||||
|
|
||||||
ret = devm_request_irq(dev, irq, stm32_qspi_irq, 0,
|
ret = devm_request_irq(dev, irq, stm32_qspi_irq, 0,
|
||||||
dev_name(dev), qspi);
|
dev_name(dev), qspi);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dev, "failed to request irq\n");
|
dev_err(dev, "failed to request irq\n");
|
||||||
goto err;
|
goto err_master_put;
|
||||||
}
|
}
|
||||||
|
|
||||||
init_completion(&qspi->data_completion);
|
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);
|
qspi->clk = devm_clk_get(dev, NULL);
|
||||||
if (IS_ERR(qspi->clk)) {
|
if (IS_ERR(qspi->clk)) {
|
||||||
ret = PTR_ERR(qspi->clk);
|
ret = PTR_ERR(qspi->clk);
|
||||||
goto err;
|
goto err_master_put;
|
||||||
}
|
}
|
||||||
|
|
||||||
qspi->clk_rate = clk_get_rate(qspi->clk);
|
qspi->clk_rate = clk_get_rate(qspi->clk);
|
||||||
if (!qspi->clk_rate) {
|
if (!qspi->clk_rate) {
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto err;
|
goto err_master_put;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = clk_prepare_enable(qspi->clk);
|
ret = clk_prepare_enable(qspi->clk);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dev, "can not enable the clock\n");
|
dev_err(dev, "can not enable the clock\n");
|
||||||
goto err;
|
goto err_master_put;
|
||||||
}
|
}
|
||||||
|
|
||||||
rstc = devm_reset_control_get_exclusive(dev, NULL);
|
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);
|
reset_control_assert(rstc);
|
||||||
udelay(2);
|
udelay(2);
|
||||||
reset_control_deassert(rstc);
|
reset_control_deassert(rstc);
|
||||||
@ -625,7 +631,7 @@ static int stm32_qspi_probe(struct platform_device *pdev)
|
|||||||
platform_set_drvdata(pdev, qspi);
|
platform_set_drvdata(pdev, qspi);
|
||||||
ret = stm32_qspi_dma_setup(qspi);
|
ret = stm32_qspi_dma_setup(qspi);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err_qspi_release;
|
||||||
|
|
||||||
mutex_init(&qspi->lock);
|
mutex_init(&qspi->lock);
|
||||||
|
|
||||||
@ -641,8 +647,9 @@ static int stm32_qspi_probe(struct platform_device *pdev)
|
|||||||
if (!ret)
|
if (!ret)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err:
|
err_qspi_release:
|
||||||
stm32_qspi_release(qspi);
|
stm32_qspi_release(qspi);
|
||||||
|
err_master_put:
|
||||||
spi_master_put(qspi->ctrl);
|
spi_master_put(qspi->ctrl);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -175,7 +175,7 @@
|
|||||||
#define SPI_DMA_MIN_BYTES 16
|
#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
|
* @reg: register offset
|
||||||
* @mask: bitfield mask
|
* @mask: bitfield mask
|
||||||
* @shift: left shift
|
* @shift: left shift
|
||||||
@ -187,16 +187,16 @@ struct stm32_spi_reg {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* stm32_spi_regspec - stm32 registers definition, compatible dependent data
|
* struct stm32_spi_regspec - stm32 registers definition, compatible dependent data
|
||||||
* en: enable register and SPI enable bit
|
* @en: enable register and SPI enable bit
|
||||||
* dma_rx_en: SPI DMA RX enable register end SPI DMA RX 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
|
* @dma_tx_en: SPI DMA TX enable register end SPI DMA TX enable bit
|
||||||
* cpol: clock polarity register and polarity bit
|
* @cpol: clock polarity register and polarity bit
|
||||||
* cpha: clock phase register and phase bit
|
* @cpha: clock phase register and phase bit
|
||||||
* lsb_first: LSB transmitted first register and bit
|
* @lsb_first: LSB transmitted first register and bit
|
||||||
* br: baud rate register and bitfields
|
* @br: baud rate register and bitfields
|
||||||
* rx: SPI RX data register
|
* @rx: SPI RX data register
|
||||||
* tx: SPI TX data register
|
* @tx: SPI TX data register
|
||||||
*/
|
*/
|
||||||
struct stm32_spi_regspec {
|
struct stm32_spi_regspec {
|
||||||
const struct stm32_spi_reg en;
|
const struct stm32_spi_reg en;
|
||||||
@ -213,7 +213,7 @@ struct stm32_spi_regspec {
|
|||||||
struct stm32_spi;
|
struct stm32_spi;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* stm32_spi_cfg - stm32 compatible configuration data
|
* struct stm32_spi_cfg - stm32 compatible configuration data
|
||||||
* @regs: registers descriptions
|
* @regs: registers descriptions
|
||||||
* @get_fifo_size: routine to get fifo size
|
* @get_fifo_size: routine to get fifo size
|
||||||
* @get_bpw_mask: routine to get bits per word mask
|
* @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_mode: routine to configure registers to desired mode
|
||||||
* @set_data_idleness: optional routine to configure registers to desired idle
|
* @set_data_idleness: optional routine to configure registers to desired idle
|
||||||
* time between frames (if driver has this functionality)
|
* 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)
|
* number of data (if driver has this functionality)
|
||||||
* @can_dma: routine to determine if the transfer is eligible for DMA use
|
* @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
|
* @transfer_one_dma_start: routine to start transfer a single spi_transfer
|
||||||
* using DMA
|
* using DMA
|
||||||
* @dma_rx cb: routine to call after DMA RX 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
|
* @dma_tx_cb: routine to call after DMA TX channel operation is complete
|
||||||
* @transfer_one_irq: routine to configure interrupts for driver
|
* @transfer_one_irq: routine to configure interrupts for driver
|
||||||
* @irq_handler_event: Interrupt handler for SPI controller events
|
* @irq_handler_event: Interrupt handler for SPI controller events
|
||||||
* @irq_handler_thread: thread of interrupt handler for SPI controller
|
* @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
|
* stm32h7_spi_read_rxfifo - Read bytes in Receive Data Register
|
||||||
* @spi: pointer to the spi controller data structure
|
* @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
|
* Write in rx_buf depends on remaining bytes to avoid to write beyond
|
||||||
* rx_buf end.
|
* 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
|
* 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,
|
* 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.
|
* 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
|
* 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,
|
static int stm32_spi_prepare_msg(struct spi_master *master,
|
||||||
struct spi_message *msg)
|
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
|
* 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.
|
* 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
|
* 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.
|
* 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
|
* 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
|
* DMA callback is called when the transfer is complete or when an error
|
||||||
* occurs. If the transfer is complete, EOT flag is raised.
|
* 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
|
* stm32_spi_dma_config - configure dma slave channel depending on current
|
||||||
* transfer bits_per_word.
|
* 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,
|
static void stm32_spi_dma_config(struct stm32_spi *spi,
|
||||||
struct dma_slave_config *dma_conf,
|
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
|
* stm32f4_spi_transfer_one_irq - transfer a single spi_transfer using
|
||||||
* interrupts
|
* 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
|
* It must returns 0 if the transfer is finished or 1 if the transfer is still
|
||||||
* in progress.
|
* 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
|
* stm32h7_spi_transfer_one_irq - transfer a single spi_transfer using
|
||||||
* interrupts
|
* 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
|
* It must returns 0 if the transfer is finished or 1 if the transfer is still
|
||||||
* in progress.
|
* 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
|
* stm32f4_spi_transfer_one_dma_start - Set SPI driver registers to start
|
||||||
* transfer using DMA
|
* transfer using DMA
|
||||||
|
* @spi: pointer to the spi controller data structure
|
||||||
*/
|
*/
|
||||||
static void stm32f4_spi_transfer_one_dma_start(struct stm32_spi *spi)
|
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
|
* stm32h7_spi_transfer_one_dma_start - Set SPI driver registers to start
|
||||||
* transfer using DMA
|
* transfer using DMA
|
||||||
|
* @spi: pointer to the spi controller data structure
|
||||||
*/
|
*/
|
||||||
static void stm32h7_spi_transfer_one_dma_start(struct stm32_spi *spi)
|
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
|
* 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
|
* It must returns 0 if the transfer is finished or 1 if the transfer is still
|
||||||
* in progress.
|
* 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
|
* stm32_spi_communication_type - return transfer communication type
|
||||||
* @spi_dev: pointer to the spi device
|
* @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,
|
static unsigned int stm32_spi_communication_type(struct spi_device *spi_dev,
|
||||||
struct spi_transfer *transfer)
|
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
|
* stm32h7_spi_number_of_data - configure number of data at current transfer
|
||||||
* @spi: pointer to the spi controller data structure
|
* @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)
|
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
|
* stm32_spi_transfer_one_setup - common setup to transfer a single
|
||||||
* spi_transfer either using DMA or
|
* spi_transfer either using DMA or
|
||||||
* interrupts.
|
* 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,
|
static int stm32_spi_transfer_one_setup(struct stm32_spi *spi,
|
||||||
struct spi_device *spi_dev,
|
struct spi_device *spi_dev,
|
||||||
@ -1625,6 +1646,9 @@ out:
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* stm32_spi_transfer_one - transfer a single spi_transfer
|
* 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
|
* It must return 0 if the transfer is finished or 1 if the transfer is still
|
||||||
* in progress.
|
* in progress.
|
||||||
@ -1658,6 +1682,8 @@ static int stm32_spi_transfer_one(struct spi_master *master,
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* stm32_spi_unprepare_msg - relax the hardware
|
* 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,
|
static int stm32_spi_unprepare_msg(struct spi_master *master,
|
||||||
struct spi_message *msg)
|
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
|
* 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)
|
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
|
* 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)
|
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.bus = &spi_bus_type;
|
||||||
spi->dev.release = spidev_release;
|
spi->dev.release = spidev_release;
|
||||||
spi->cs_gpio = -ENOENT;
|
spi->cs_gpio = -ENOENT;
|
||||||
|
spi->mode = ctlr->buswidth_override_bits;
|
||||||
|
|
||||||
spin_lock_init(&spi->statistics.lock);
|
spin_lock_init(&spi->statistics.lock);
|
||||||
|
|
||||||
@ -1514,17 +1515,15 @@ void spi_take_timestamp_pre(struct spi_controller *ctlr,
|
|||||||
if (!xfer->ptp_sts)
|
if (!xfer->ptp_sts)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (xfer->timestamped_pre)
|
if (xfer->timestamped)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (progress < xfer->ptp_sts_word_pre)
|
if (progress > xfer->ptp_sts_word_pre)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Capture the resolution of the timestamp */
|
/* Capture the resolution of the timestamp */
|
||||||
xfer->ptp_sts_word_pre = progress;
|
xfer->ptp_sts_word_pre = progress;
|
||||||
|
|
||||||
xfer->timestamped_pre = true;
|
|
||||||
|
|
||||||
if (irqs_off) {
|
if (irqs_off) {
|
||||||
local_irq_save(ctlr->irq_flags);
|
local_irq_save(ctlr->irq_flags);
|
||||||
preempt_disable();
|
preempt_disable();
|
||||||
@ -1553,7 +1552,7 @@ void spi_take_timestamp_post(struct spi_controller *ctlr,
|
|||||||
if (!xfer->ptp_sts)
|
if (!xfer->ptp_sts)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (xfer->timestamped_post)
|
if (xfer->timestamped)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (progress < xfer->ptp_sts_word_post)
|
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 */
|
/* Capture the resolution of the timestamp */
|
||||||
xfer->ptp_sts_word_post = progress;
|
xfer->ptp_sts_word_post = progress;
|
||||||
|
|
||||||
xfer->timestamped_post = true;
|
xfer->timestamped = true;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(spi_take_timestamp_post);
|
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)) {
|
if (unlikely(ctlr->ptp_sts_supported))
|
||||||
list_for_each_entry(xfer, &mesg->transfers, transfer_list) {
|
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);
|
||||||
WARN_ON_ONCE(xfer->ptp_sts && !xfer->timestamped_post);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
spi_unmap_msg(ctlr, mesg);
|
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;
|
spi->mode |= SPI_CS_HIGH;
|
||||||
|
|
||||||
/* Device speed */
|
/* Device speed */
|
||||||
rc = of_property_read_u32(nc, "spi-max-frequency", &value);
|
if (!of_property_read_u32(nc, "spi-max-frequency", &value))
|
||||||
if (rc) {
|
spi->max_speed_hz = value;
|
||||||
dev_err(&ctlr->dev,
|
|
||||||
"%pOF has no valid 'spi-max-frequency' property (%d)\n", nc, rc);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
spi->max_speed_hz = value;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -2181,9 +2172,10 @@ static acpi_status acpi_register_spi_device(struct spi_controller *ctlr,
|
|||||||
return AE_NO_MEMORY;
|
return AE_NO_MEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ACPI_COMPANION_SET(&spi->dev, adev);
|
ACPI_COMPANION_SET(&spi->dev, adev);
|
||||||
spi->max_speed_hz = lookup.max_speed_hz;
|
spi->max_speed_hz = lookup.max_speed_hz;
|
||||||
spi->mode = lookup.mode;
|
spi->mode |= lookup.mode;
|
||||||
spi->irq = lookup.irq;
|
spi->irq = lookup.irq;
|
||||||
spi->bits_per_word = lookup.bits_per_word;
|
spi->bits_per_word = lookup.bits_per_word;
|
||||||
spi->chip_select = lookup.chip_select;
|
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;
|
struct device *dev;
|
||||||
|
|
||||||
dev = bus_find_device_by_acpi_dev(&spi_bus_type, adev);
|
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,
|
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
|
#ifdef VERBOSE
|
||||||
dev_dbg(&spidev->spi->dev,
|
dev_dbg(&spidev->spi->dev,
|
||||||
" xfer len %u %s%s%s%dbits %u usec %u usec %uHz\n",
|
" xfer len %u %s%s%s%dbits %u usec %u usec %uHz\n",
|
||||||
u_tmp->len,
|
k_tmp->len,
|
||||||
u_tmp->rx_buf ? "rx " : "",
|
k_tmp->rx_buf ? "rx " : "",
|
||||||
u_tmp->tx_buf ? "tx " : "",
|
k_tmp->tx_buf ? "tx " : "",
|
||||||
u_tmp->cs_change ? "cs " : "",
|
k_tmp->cs_change ? "cs " : "",
|
||||||
u_tmp->bits_per_word ? : spidev->spi->bits_per_word,
|
k_tmp->bits_per_word ? : spidev->spi->bits_per_word,
|
||||||
u_tmp->delay_usecs,
|
k_tmp->delay.value,
|
||||||
u_tmp->word_delay_usecs,
|
k_tmp->word_delay.value,
|
||||||
u_tmp->speed_hz ? : spidev->spi->max_speed_hz);
|
k_tmp->speed_hz ? : spidev->spi->max_speed_hz);
|
||||||
#endif
|
#endif
|
||||||
spi_message_add_tail(k_tmp, &msg);
|
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;
|
spi->max_speed_hz = tmp;
|
||||||
retval = spi_setup(spi);
|
retval = spi_setup(spi);
|
||||||
if (retval >= 0)
|
if (retval == 0) {
|
||||||
spidev->speed_hz = tmp;
|
spidev->speed_hz = tmp;
|
||||||
else
|
dev_dbg(&spi->dev, "%d Hz (max)\n",
|
||||||
dev_dbg(&spi->dev, "%d Hz (max)\n", tmp);
|
spidev->speed_hz);
|
||||||
|
}
|
||||||
spi->max_speed_hz = save;
|
spi->max_speed_hz = save;
|
||||||
}
|
}
|
||||||
break;
|
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
|
* @modalias: Name of the driver to use with this device, or an alias
|
||||||
* for that name. This appears in the sysfs "modalias" attribute
|
* for that name. This appears in the sysfs "modalias" attribute
|
||||||
* for driver coldplugging, and in uevents used for hotplugging
|
* 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
|
* @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
|
* not using a GPIO line) use cs_gpiod in new drivers by opting in on
|
||||||
* the spi_master.
|
* 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.
|
* @spi_transfer->ptp_sts_word_post were transmitted.
|
||||||
* If the driver does not set this, the SPI core takes the snapshot as
|
* If the driver does not set this, the SPI core takes the snapshot as
|
||||||
* close to the driver hand-over as possible.
|
* 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
|
* Each SPI controller can communicate with one or more @spi_device
|
||||||
* children. These make a small bus, sharing MOSI, MISO and SCK signals
|
* 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 */
|
/* spi_device.mode flags understood by this controller driver */
|
||||||
u32 mode_bits;
|
u32 mode_bits;
|
||||||
|
|
||||||
|
/* spi_device.mode flags override flags for this controller */
|
||||||
|
u32 buswidth_override_bits;
|
||||||
|
|
||||||
/* bitmask of supported bits_per_word for transfers */
|
/* bitmask of supported bits_per_word for transfers */
|
||||||
u32 bits_per_word_mask;
|
u32 bits_per_word_mask;
|
||||||
#define SPI_BPW_MASK(bits) BIT((bits) - 1)
|
#define SPI_BPW_MASK(bits) BIT((bits) - 1)
|
||||||
@ -930,8 +936,7 @@ struct spi_transfer {
|
|||||||
|
|
||||||
struct ptp_system_timestamp *ptp_sts;
|
struct ptp_system_timestamp *ptp_sts;
|
||||||
|
|
||||||
bool timestamped_pre;
|
bool timestamped;
|
||||||
bool timestamped_post;
|
|
||||||
|
|
||||||
struct list_head transfer_list;
|
struct list_head transfer_list;
|
||||||
};
|
};
|
||||||
|
@ -51,7 +51,7 @@ $(OUTPUT)spidev_fdx: $(SPIDEV_FDX_IN)
|
|||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(ALL_PROGRAMS)
|
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
|
find $(if $(OUTPUT),$(OUTPUT),.) -name '*.o' -delete -o -name '\.*.d' -delete
|
||||||
|
|
||||||
install: $(ALL_PROGRAMS)
|
install: $(ALL_PROGRAMS)
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
@ -26,7 +27,11 @@
|
|||||||
|
|
||||||
static void pabort(const char *s)
|
static void pabort(const char *s)
|
||||||
{
|
{
|
||||||
perror(s);
|
if (errno != 0)
|
||||||
|
perror(s);
|
||||||
|
else
|
||||||
|
printf("%s\n", s);
|
||||||
|
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -283,7 +288,6 @@ static void parse_opts(int argc, char *argv[])
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
print_usage(argv[0]);
|
print_usage(argv[0]);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (mode & SPI_LOOP) {
|
if (mode & SPI_LOOP) {
|
||||||
@ -405,6 +409,9 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
parse_opts(argc, 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);
|
fd = open(device, O_RDWR);
|
||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
pabort("can't open device");
|
pabort("can't open device");
|
||||||
@ -446,9 +453,6 @@ int main(int argc, char *argv[])
|
|||||||
printf("bits per word: %d\n", bits);
|
printf("bits per word: %d\n", bits);
|
||||||
printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000);
|
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)
|
if (input_tx)
|
||||||
transfer_escaped_string(fd, input_tx);
|
transfer_escaped_string(fd, input_tx);
|
||||||
else if (input_file)
|
else if (input_file)
|
||||||
|
Loading…
Reference in New Issue
Block a user