mirror of
https://github.com/torvalds/linux.git
synced 2026-08-01 03:59:40 +02:00
Merge c3f13bbfc2 ("Merge tag 'mmc-v6.1' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc") into android-mainline
Steps on the way to 6.1-rc1 Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> Change-Id: I6b4d5947883e8d0fda9d02841558749565018119
This commit is contained in:
commit
4a591ec38b
|
|
@ -23,6 +23,8 @@ properties:
|
|||
reg: true
|
||||
reset-gpios: true
|
||||
|
||||
spi-3wire: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- power-supply
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ properties:
|
|||
reg: true
|
||||
reset-gpios: true
|
||||
|
||||
spi-3wire: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- power-supply
|
||||
|
|
|
|||
|
|
@ -24,6 +24,10 @@ properties:
|
|||
default-brightness: true
|
||||
max-brightness: true
|
||||
|
||||
spi-3wire: true
|
||||
spi-cpha: true
|
||||
spi-cpol: true
|
||||
|
||||
vdd3-supply:
|
||||
description: VDD regulator
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ title: Cadence SD/SDIO/eMMC Host Controller (SD4HC)
|
|||
|
||||
maintainers:
|
||||
- Masahiro Yamada <yamada.masahiro@socionext.com>
|
||||
- Piotr Sroka <piotrs@cadence.com>
|
||||
|
||||
allOf:
|
||||
- $ref: mmc-controller.yaml
|
||||
|
|
|
|||
|
|
@ -88,11 +88,18 @@ properties:
|
|||
default: 1
|
||||
|
||||
max-frequency:
|
||||
description:
|
||||
Maximum operating frequency of the bus.
|
||||
description: |
|
||||
Maximum operating frequency of the bus:
|
||||
- for eMMC, the maximum supported frequency is 200MHz,
|
||||
- for SD/SDIO cards the SDR104 mode has a max supported
|
||||
frequency of 208MHz,
|
||||
- some mmc host controllers do support a max frequency upto
|
||||
384MHz.
|
||||
So, lets keep the maximum supported value here.
|
||||
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 400000
|
||||
maximum: 200000000
|
||||
maximum: 384000000
|
||||
|
||||
disable-wp:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
|
|
|
|||
|
|
@ -23,8 +23,6 @@ properties:
|
|||
reg:
|
||||
maxItems: 1
|
||||
|
||||
spi-max-frequency: true
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ properties:
|
|||
- mediatek,mt2701-mmc
|
||||
- mediatek,mt2712-mmc
|
||||
- mediatek,mt6779-mmc
|
||||
- mediatek,mt6795-mmc
|
||||
- mediatek,mt7620-mmc
|
||||
- mediatek,mt7622-mmc
|
||||
- mediatek,mt8135-mmc
|
||||
|
|
|
|||
|
|
@ -89,6 +89,9 @@ properties:
|
|||
- tx
|
||||
- rx
|
||||
|
||||
iommus:
|
||||
maxItems: 1
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ properties:
|
|||
- rockchip,px30-dw-mshc
|
||||
- rockchip,rk1808-dw-mshc
|
||||
- rockchip,rk3036-dw-mshc
|
||||
- rockchip,rk3128-dw-mshc
|
||||
- rockchip,rk3228-dw-mshc
|
||||
- rockchip,rk3308-dw-mshc
|
||||
- rockchip,rk3328-dw-mshc
|
||||
|
|
|
|||
|
|
@ -51,6 +51,9 @@ properties:
|
|||
|
||||
sdhci-caps-mask: true
|
||||
|
||||
dma-coherent:
|
||||
type: boolean
|
||||
|
||||
# PHY output tap delays:
|
||||
# Used to delay the data valid window and align it to the sampling clock.
|
||||
# Binding needs to be provided for each supported speed mode otherwise the
|
||||
|
|
|
|||
|
|
@ -38,9 +38,11 @@ properties:
|
|||
- qcom,sc7180-sdhci
|
||||
- qcom,sc7280-sdhci
|
||||
- qcom,sdm630-sdhci
|
||||
- qcom,sdm670-sdhci
|
||||
- qcom,sdm845-sdhci
|
||||
- qcom,sdx55-sdhci
|
||||
- qcom,sdx65-sdhci
|
||||
- qcom,sm6115-sdhci
|
||||
- qcom,sm6125-sdhci
|
||||
- qcom,sm6350-sdhci
|
||||
- qcom,sm8150-sdhci
|
||||
|
|
@ -96,6 +98,10 @@ properties:
|
|||
description:
|
||||
Should specify pin control groups used for this controller.
|
||||
|
||||
pinctrl-1:
|
||||
description:
|
||||
Should specify sleep pin control groups used for this controller.
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,11 @@
|
|||
$id: http://devicetree.org/schemas/spi/microchip,mpfs-spi.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Microchip MPFS {Q,}SPI Controller Device Tree Bindings
|
||||
title: Microchip FPGA {Q,}SPI Controllers
|
||||
|
||||
description:
|
||||
SPI and QSPI controllers on Microchip PolarFire SoC and the "soft"/
|
||||
fabric IP cores they are based on
|
||||
|
||||
maintainers:
|
||||
- Conor Dooley <conor.dooley@microchip.com>
|
||||
|
|
@ -14,9 +18,12 @@ allOf:
|
|||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- microchip,mpfs-spi
|
||||
- microchip,mpfs-qspi
|
||||
oneOf:
|
||||
- items:
|
||||
- const: microchip,mpfs-qspi
|
||||
- const: microchip,coreqspi-rtl-v2
|
||||
- const: microchip,coreqspi-rtl-v2 #FPGA QSPI
|
||||
- const: microchip,mpfs-spi
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@ Nuvoton NPCM Peripheral Serial Peripheral Interface(PSPI) controller driver
|
|||
Nuvoton NPCM7xx SOC support two PSPI channels.
|
||||
|
||||
Required properties:
|
||||
- compatible : "nuvoton,npcm750-pspi" for NPCM7XX BMC
|
||||
- compatible : "nuvoton,npcm750-pspi" for Poleg NPCM7XX.
|
||||
"nuvoton,npcm845-pspi" for Arbel NPCM8XX.
|
||||
- #address-cells : should be 1. see spi-bus.txt
|
||||
- #size-cells : should be 0. see spi-bus.txt
|
||||
- specifies physical base address and size of the register.
|
||||
|
|
|
|||
|
|
@ -29,5 +29,4 @@ properties:
|
|||
minimum: 0
|
||||
maximum: 255
|
||||
|
||||
unevaluatedProperties: true
|
||||
|
||||
additionalProperties: true
|
||||
|
|
|
|||
61
Documentation/devicetree/bindings/spi/ralink,mt7621-spi.yaml
Normal file
61
Documentation/devicetree/bindings/spi/ralink,mt7621-spi.yaml
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/spi/ralink,mt7621-spi.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
maintainers:
|
||||
- Sergio Paracuellos <sergio.paracuellos@gmail.com>
|
||||
|
||||
title: Mediatek MT7621/MT7628 SPI controller
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/spi/spi-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: ralink,mt7621-spi
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
clock-names:
|
||||
const: spi
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
reset-names:
|
||||
const: spi
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- resets
|
||||
- "#address-cells"
|
||||
- "#size-cells"
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/mt7621-clk.h>
|
||||
#include <dt-bindings/reset/mt7621-reset.h>
|
||||
|
||||
spi@b00 {
|
||||
compatible = "ralink,mt7621-spi";
|
||||
reg = <0xb00 0x100>;
|
||||
clocks = <&sysc MT7621_CLK_SPI>;
|
||||
clock-names = "spi";
|
||||
resets = <&sysc MT7621_RST_SPI>;
|
||||
reset-names = "spi";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&spi_pins>;
|
||||
};
|
||||
|
|
@ -47,9 +47,15 @@ properties:
|
|||
- renesas,msiof-r8a77980 # R-Car V3H
|
||||
- renesas,msiof-r8a77990 # R-Car E3
|
||||
- renesas,msiof-r8a77995 # R-Car D3
|
||||
- renesas,msiof-r8a779a0 # R-Car V3U
|
||||
- const: renesas,rcar-gen3-msiof # generic R-Car Gen3 and RZ/G2
|
||||
# compatible device
|
||||
- items:
|
||||
- enum:
|
||||
- renesas,msiof-r8a779a0 # R-Car V3U
|
||||
- renesas,msiof-r8a779f0 # R-Car S4-8
|
||||
- renesas,msiof-r8a779g0 # R-Car V4H
|
||||
- const: renesas,rcar-gen4-msiof # generic R-Car Gen4
|
||||
# compatible device
|
||||
- items:
|
||||
- const: renesas,sh-msiof # deprecated
|
||||
|
||||
|
|
@ -69,6 +75,12 @@ properties:
|
|||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
num-cs:
|
||||
description: |
|
||||
Total number of chip selects (default is 1).
|
||||
|
|
|
|||
|
|
@ -104,7 +104,6 @@ properties:
|
|||
const: spi
|
||||
|
||||
reg-io-width:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: I/O register width (in bytes) implemented by this device
|
||||
default: 4
|
||||
enum: [ 2, 4 ]
|
||||
|
|
|
|||
|
|
@ -96,6 +96,11 @@ patternProperties:
|
|||
$ref: spi-peripheral-props.yaml
|
||||
|
||||
properties:
|
||||
spi-3wire:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description:
|
||||
The device requires 3-wire mode.
|
||||
|
||||
spi-cpha:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description:
|
||||
|
|
|
|||
|
|
@ -19,7 +19,9 @@ properties:
|
|||
- fsl,imx7ulp-spi
|
||||
- fsl,imx8qxp-spi
|
||||
- items:
|
||||
- const: fsl,imx8ulp-spi
|
||||
- enum:
|
||||
- fsl,imx8ulp-spi
|
||||
- fsl,imx93-spi
|
||||
- const: fsl,imx7ulp-spi
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
|
@ -37,6 +39,16 @@ properties:
|
|||
- const: per
|
||||
- const: ipg
|
||||
|
||||
dmas:
|
||||
items:
|
||||
- description: TX DMA Channel
|
||||
- description: RX DMA Channel
|
||||
|
||||
dma-names:
|
||||
items:
|
||||
- const: tx
|
||||
- const: rx
|
||||
|
||||
fsl,spi-only-use-cs1-sel:
|
||||
description:
|
||||
spi common code does not support use of CS signals discontinuously.
|
||||
|
|
|
|||
|
|
@ -1,26 +0,0 @@
|
|||
Binding for MTK SPI controller (MT7621 MIPS)
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be one of the following:
|
||||
- "ralink,mt7621-spi": for mt7621/mt7628/mt7688 platforms
|
||||
- #address-cells: should be 1.
|
||||
- #size-cells: should be 0.
|
||||
- reg: Address and length of the register set for the device
|
||||
- resets: phandle to the reset controller asserting this device in
|
||||
reset
|
||||
See ../reset/reset.txt for details.
|
||||
|
||||
Optional properties:
|
||||
- cs-gpios: see spi-bus.txt.
|
||||
|
||||
Example:
|
||||
|
||||
- SoC Specific Portion:
|
||||
spi0: spi@b00 {
|
||||
compatible = "ralink,mt7621-spi";
|
||||
reg = <0xb00 0x100>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
resets = <&rstctrl 18>;
|
||||
reset-names = "spi";
|
||||
};
|
||||
|
|
@ -29,11 +29,6 @@ properties:
|
|||
description:
|
||||
Chip select used by the device.
|
||||
|
||||
spi-3wire:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description:
|
||||
The device requires 3-wire mode.
|
||||
|
||||
spi-cs-high:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description:
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ properties:
|
|||
- items:
|
||||
- enum:
|
||||
- rockchip,px30-spi
|
||||
- rockchip,rk3128-spi
|
||||
- rockchip,rk3188-spi
|
||||
- rockchip,rk3288-spi
|
||||
- rockchip,rk3308-spi
|
||||
|
|
@ -34,6 +35,7 @@ properties:
|
|||
- rockchip,rk3368-spi
|
||||
- rockchip,rk3399-spi
|
||||
- rockchip,rk3568-spi
|
||||
- rockchip,rk3588-spi
|
||||
- rockchip,rv1126-spi
|
||||
- const: rockchip,rk3066-spi
|
||||
|
||||
|
|
@ -80,6 +82,9 @@ properties:
|
|||
where the "sleep" configuration may describe the state
|
||||
the pins should be in during system suspend.
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
|
|
|||
|
|
@ -7536,7 +7536,7 @@ M: Adrian Hunter <adrian.hunter@intel.com>
|
|||
M: Ritesh Harjani <riteshh@codeaurora.org>
|
||||
M: Asutosh Das <asutoshd@codeaurora.org>
|
||||
L: linux-mmc@vger.kernel.org
|
||||
S: Maintained
|
||||
S: Supported
|
||||
F: drivers/mmc/host/cqhci*
|
||||
|
||||
EMULEX 10Gbps iSCSI - OneConnect DRIVER
|
||||
|
|
@ -17575,6 +17575,7 @@ F: drivers/mailbox/mailbox-mpfs.c
|
|||
F: drivers/pci/controller/pcie-microchip-host.c
|
||||
F: drivers/rtc/rtc-mpfs.c
|
||||
F: drivers/soc/microchip/
|
||||
F: drivers/spi/spi-microchip-core-qspi.c
|
||||
F: drivers/spi/spi-microchip-core.c
|
||||
F: drivers/usb/musb/mpfs.c
|
||||
F: include/soc/microchip/mpfs.h
|
||||
|
|
@ -18322,7 +18323,7 @@ F: drivers/mmc/host/sdhci-brcmstb*
|
|||
SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) DRIVER
|
||||
M: Adrian Hunter <adrian.hunter@intel.com>
|
||||
L: linux-mmc@vger.kernel.org
|
||||
S: Maintained
|
||||
S: Supported
|
||||
F: drivers/mmc/host/sdhci*
|
||||
|
||||
SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) MICROCHIP DRIVER
|
||||
|
|
@ -18345,7 +18346,7 @@ S: Maintained
|
|||
F: drivers/mmc/host/sdhci-spear.c
|
||||
|
||||
SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) TI OMAP DRIVER
|
||||
M: Kishon Vijay Abraham I <kishon@ti.com>
|
||||
M: Vignesh Raghavendra <vigneshr@ti.com>
|
||||
L: linux-mmc@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/mmc/host/sdhci-omap.c
|
||||
|
|
|
|||
|
|
@ -565,7 +565,7 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
|
|||
spin_lock_init(&host->lock);
|
||||
init_waitqueue_head(&host->wq);
|
||||
INIT_DELAYED_WORK(&host->detect, mmc_rescan);
|
||||
INIT_DELAYED_WORK(&host->sdio_irq_work, sdio_irq_work);
|
||||
INIT_WORK(&host->sdio_irq_work, sdio_irq_work);
|
||||
timer_setup(&host->retune_timer, mmc_retune_timer, 0);
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -1043,7 +1043,7 @@ static int mmc_sdio_suspend(struct mmc_host *host)
|
|||
|
||||
/* Prevent processing of SDIO IRQs in suspended state. */
|
||||
mmc_card_set_suspended(host->card);
|
||||
cancel_delayed_work_sync(&host->sdio_irq_work);
|
||||
cancel_work_sync(&host->sdio_irq_work);
|
||||
|
||||
mmc_claim_host(host);
|
||||
|
||||
|
|
@ -1103,7 +1103,7 @@ static int mmc_sdio_resume(struct mmc_host *host)
|
|||
if (!(host->caps2 & MMC_CAP2_SDIO_IRQ_NOTHREAD))
|
||||
wake_up_process(host->sdio_irq_thread);
|
||||
else if (host->caps & MMC_CAP_SDIO_IRQ)
|
||||
queue_delayed_work(system_wq, &host->sdio_irq_work, 0);
|
||||
schedule_work(&host->sdio_irq_work);
|
||||
}
|
||||
|
||||
out:
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ static void sdio_run_irqs(struct mmc_host *host)
|
|||
void sdio_irq_work(struct work_struct *work)
|
||||
{
|
||||
struct mmc_host *host =
|
||||
container_of(work, struct mmc_host, sdio_irq_work.work);
|
||||
container_of(work, struct mmc_host, sdio_irq_work);
|
||||
|
||||
sdio_run_irqs(host);
|
||||
}
|
||||
|
|
@ -132,7 +132,7 @@ void sdio_irq_work(struct work_struct *work)
|
|||
void sdio_signal_irq(struct mmc_host *host)
|
||||
{
|
||||
host->sdio_irq_pending = true;
|
||||
queue_delayed_work(system_wq, &host->sdio_irq_work, 0);
|
||||
schedule_work(&host->sdio_irq_work);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sdio_signal_irq);
|
||||
|
||||
|
|
|
|||
|
|
@ -157,6 +157,7 @@ config MMC_SDHCI_OF_ARASAN
|
|||
|
||||
config MMC_SDHCI_OF_ASPEED
|
||||
tristate "SDHCI OF support for the ASPEED SDHCI controller"
|
||||
depends on ARCH_ASPEED || COMPILE_TEST
|
||||
depends on MMC_SDHCI_PLTFM
|
||||
depends on OF && OF_ADDRESS
|
||||
select MMC_SDHCI_IO_ACCESSORS
|
||||
|
|
|
|||
|
|
@ -1097,8 +1097,9 @@ static int au1xmmc_probe(struct platform_device *pdev)
|
|||
if (host->platdata && host->platdata->cd_setup &&
|
||||
!(mmc->caps & MMC_CAP_NEEDS_POLL))
|
||||
host->platdata->cd_setup(mmc, 0);
|
||||
out_clk:
|
||||
|
||||
clk_disable_unprepare(host->clk);
|
||||
out_clk:
|
||||
clk_put(host->clk);
|
||||
out_irq:
|
||||
free_irq(host->irq, host);
|
||||
|
|
|
|||
|
|
@ -298,7 +298,7 @@ static int jz4740_mmc_prepare_dma_data(struct jz4740_mmc_host *host,
|
|||
{
|
||||
struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data);
|
||||
enum dma_data_direction dir = mmc_get_dma_dir(data);
|
||||
int sg_count;
|
||||
unsigned int sg_count;
|
||||
|
||||
if (data->host_cookie == COOKIE_PREMAPPED)
|
||||
return data->sg_count;
|
||||
|
|
@ -308,7 +308,7 @@ static int jz4740_mmc_prepare_dma_data(struct jz4740_mmc_host *host,
|
|||
data->sg_len,
|
||||
dir);
|
||||
|
||||
if (sg_count <= 0) {
|
||||
if (!sg_count) {
|
||||
dev_err(mmc_dev(host->mmc),
|
||||
"Failed to map scatterlist for DMA operation\n");
|
||||
return -EINVAL;
|
||||
|
|
|
|||
|
|
@ -41,14 +41,17 @@
|
|||
#define CLK_V2_TX_DELAY_MASK GENMASK(19, 16)
|
||||
#define CLK_V2_RX_DELAY_MASK GENMASK(23, 20)
|
||||
#define CLK_V2_ALWAYS_ON BIT(24)
|
||||
#define CLK_V2_IRQ_SDIO_SLEEP BIT(25)
|
||||
|
||||
#define CLK_V3_TX_DELAY_MASK GENMASK(21, 16)
|
||||
#define CLK_V3_RX_DELAY_MASK GENMASK(27, 22)
|
||||
#define CLK_V3_ALWAYS_ON BIT(28)
|
||||
#define CLK_V3_IRQ_SDIO_SLEEP BIT(29)
|
||||
|
||||
#define CLK_TX_DELAY_MASK(h) (h->data->tx_delay_mask)
|
||||
#define CLK_RX_DELAY_MASK(h) (h->data->rx_delay_mask)
|
||||
#define CLK_ALWAYS_ON(h) (h->data->always_on)
|
||||
#define CLK_IRQ_SDIO_SLEEP(h) (h->data->irq_sdio_sleep)
|
||||
|
||||
#define SD_EMMC_DELAY 0x4
|
||||
#define SD_EMMC_ADJUST 0x8
|
||||
|
|
@ -101,8 +104,7 @@
|
|||
#define IRQ_RESP_STATUS BIT(14)
|
||||
#define IRQ_SDIO BIT(15)
|
||||
#define IRQ_EN_MASK \
|
||||
(IRQ_CRC_ERR | IRQ_TIMEOUTS | IRQ_END_OF_CHAIN | IRQ_RESP_STATUS |\
|
||||
IRQ_SDIO)
|
||||
(IRQ_CRC_ERR | IRQ_TIMEOUTS | IRQ_END_OF_CHAIN)
|
||||
|
||||
#define SD_EMMC_CMD_CFG 0x50
|
||||
#define SD_EMMC_CMD_ARG 0x54
|
||||
|
|
@ -136,6 +138,7 @@ struct meson_mmc_data {
|
|||
unsigned int rx_delay_mask;
|
||||
unsigned int always_on;
|
||||
unsigned int adjust;
|
||||
unsigned int irq_sdio_sleep;
|
||||
};
|
||||
|
||||
struct sd_emmc_desc {
|
||||
|
|
@ -175,6 +178,7 @@ struct meson_host {
|
|||
bool vqmmc_enabled;
|
||||
bool needs_pre_post_req;
|
||||
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
#define CMD_CFG_LENGTH_MASK GENMASK(8, 0)
|
||||
|
|
@ -431,6 +435,7 @@ static int meson_mmc_clk_init(struct meson_host *host)
|
|||
clk_reg |= FIELD_PREP(CLK_CORE_PHASE_MASK, CLK_PHASE_180);
|
||||
clk_reg |= FIELD_PREP(CLK_TX_PHASE_MASK, CLK_PHASE_0);
|
||||
clk_reg |= FIELD_PREP(CLK_RX_PHASE_MASK, CLK_PHASE_0);
|
||||
clk_reg |= CLK_IRQ_SDIO_SLEEP(host);
|
||||
writel(clk_reg, host->regs + SD_EMMC_CLOCK);
|
||||
|
||||
/* get the mux parents */
|
||||
|
|
@ -929,33 +934,54 @@ static void meson_mmc_read_resp(struct mmc_host *mmc, struct mmc_command *cmd)
|
|||
}
|
||||
}
|
||||
|
||||
static void __meson_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
|
||||
{
|
||||
struct meson_host *host = mmc_priv(mmc);
|
||||
u32 reg_irqen = IRQ_EN_MASK;
|
||||
|
||||
if (enable)
|
||||
reg_irqen |= IRQ_SDIO;
|
||||
writel(reg_irqen, host->regs + SD_EMMC_IRQ_EN);
|
||||
}
|
||||
|
||||
static irqreturn_t meson_mmc_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct meson_host *host = dev_id;
|
||||
struct mmc_command *cmd;
|
||||
struct mmc_data *data;
|
||||
u32 irq_en, status, raw_status;
|
||||
u32 status, raw_status;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
|
||||
irq_en = readl(host->regs + SD_EMMC_IRQ_EN);
|
||||
raw_status = readl(host->regs + SD_EMMC_STATUS);
|
||||
status = raw_status & irq_en;
|
||||
status = raw_status & (IRQ_EN_MASK | IRQ_SDIO);
|
||||
|
||||
if (!status) {
|
||||
dev_dbg(host->dev,
|
||||
"Unexpected IRQ! irq_en 0x%08x - status 0x%08x\n",
|
||||
irq_en, raw_status);
|
||||
"Unexpected IRQ! irq_en 0x%08lx - status 0x%08x\n",
|
||||
IRQ_EN_MASK | IRQ_SDIO, raw_status);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
if (WARN_ON(!host) || WARN_ON(!host->cmd))
|
||||
if (WARN_ON(!host))
|
||||
return IRQ_NONE;
|
||||
|
||||
/* ack all raised interrupts */
|
||||
writel(status, host->regs + SD_EMMC_STATUS);
|
||||
|
||||
cmd = host->cmd;
|
||||
data = cmd->data;
|
||||
|
||||
if (status & IRQ_SDIO) {
|
||||
spin_lock(&host->lock);
|
||||
__meson_mmc_enable_sdio_irq(host->mmc, 0);
|
||||
sdio_signal_irq(host->mmc);
|
||||
spin_unlock(&host->lock);
|
||||
status &= ~IRQ_SDIO;
|
||||
if (!status)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (WARN_ON(!cmd))
|
||||
return IRQ_NONE;
|
||||
|
||||
cmd->error = 0;
|
||||
if (status & IRQ_CRC_ERR) {
|
||||
dev_dbg(host->dev, "CRC Error - status 0x%08x\n", status);
|
||||
|
|
@ -973,12 +999,9 @@ static irqreturn_t meson_mmc_irq(int irq, void *dev_id)
|
|||
|
||||
meson_mmc_read_resp(host->mmc, cmd);
|
||||
|
||||
if (status & IRQ_SDIO) {
|
||||
dev_dbg(host->dev, "IRQ: SDIO TODO.\n");
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (status & (IRQ_END_OF_CHAIN | IRQ_RESP_STATUS)) {
|
||||
struct mmc_data *data = cmd->data;
|
||||
|
||||
if (data && !cmd->error)
|
||||
data->bytes_xfered = data->blksz * data->blocks;
|
||||
if (meson_mmc_bounce_buf_read(data) ||
|
||||
|
|
@ -1121,6 +1144,21 @@ static int meson_mmc_voltage_switch(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void meson_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
|
||||
{
|
||||
struct meson_host *host = mmc_priv(mmc);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
__meson_mmc_enable_sdio_irq(mmc, enable);
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
}
|
||||
|
||||
static void meson_mmc_ack_sdio_irq(struct mmc_host *mmc)
|
||||
{
|
||||
meson_mmc_enable_sdio_irq(mmc, 1);
|
||||
}
|
||||
|
||||
static const struct mmc_host_ops meson_mmc_ops = {
|
||||
.request = meson_mmc_request,
|
||||
.set_ios = meson_mmc_set_ios,
|
||||
|
|
@ -1130,6 +1168,8 @@ static const struct mmc_host_ops meson_mmc_ops = {
|
|||
.execute_tuning = meson_mmc_resampling_tuning,
|
||||
.card_busy = meson_mmc_card_busy,
|
||||
.start_signal_voltage_switch = meson_mmc_voltage_switch,
|
||||
.enable_sdio_irq = meson_mmc_enable_sdio_irq,
|
||||
.ack_sdio_irq = meson_mmc_ack_sdio_irq,
|
||||
};
|
||||
|
||||
static int meson_mmc_probe(struct platform_device *pdev)
|
||||
|
|
@ -1226,10 +1266,8 @@ static int meson_mmc_probe(struct platform_device *pdev)
|
|||
|
||||
/* clear, ack and enable interrupts */
|
||||
writel(0, host->regs + SD_EMMC_IRQ_EN);
|
||||
writel(IRQ_CRC_ERR | IRQ_TIMEOUTS | IRQ_END_OF_CHAIN,
|
||||
host->regs + SD_EMMC_STATUS);
|
||||
writel(IRQ_CRC_ERR | IRQ_TIMEOUTS | IRQ_END_OF_CHAIN,
|
||||
host->regs + SD_EMMC_IRQ_EN);
|
||||
writel(IRQ_EN_MASK, host->regs + SD_EMMC_STATUS);
|
||||
writel(IRQ_EN_MASK, host->regs + SD_EMMC_IRQ_EN);
|
||||
|
||||
ret = request_threaded_irq(host->irq, meson_mmc_irq,
|
||||
meson_mmc_irq_thread, IRQF_ONESHOT,
|
||||
|
|
@ -1237,7 +1275,13 @@ static int meson_mmc_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
goto err_init_clk;
|
||||
|
||||
spin_lock_init(&host->lock);
|
||||
|
||||
mmc->caps |= MMC_CAP_CMD23;
|
||||
|
||||
if (mmc->caps & MMC_CAP_SDIO_IRQ)
|
||||
mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD;
|
||||
|
||||
if (host->dram_access_quirk) {
|
||||
/* Limit segments to 1 due to low available sram memory */
|
||||
mmc->max_segs = 1;
|
||||
|
|
@ -1328,6 +1372,7 @@ static const struct meson_mmc_data meson_gx_data = {
|
|||
.rx_delay_mask = CLK_V2_RX_DELAY_MASK,
|
||||
.always_on = CLK_V2_ALWAYS_ON,
|
||||
.adjust = SD_EMMC_ADJUST,
|
||||
.irq_sdio_sleep = CLK_V2_IRQ_SDIO_SLEEP,
|
||||
};
|
||||
|
||||
static const struct meson_mmc_data meson_axg_data = {
|
||||
|
|
@ -1335,6 +1380,7 @@ static const struct meson_mmc_data meson_axg_data = {
|
|||
.rx_delay_mask = CLK_V3_RX_DELAY_MASK,
|
||||
.always_on = CLK_V3_ALWAYS_ON,
|
||||
.adjust = SD_EMMC_V3_ADJUST,
|
||||
.irq_sdio_sleep = CLK_V3_IRQ_SDIO_SLEEP,
|
||||
};
|
||||
|
||||
static const struct of_device_id meson_mmc_of_match[] = {
|
||||
|
|
|
|||
|
|
@ -381,14 +381,14 @@ static void meson_mx_sdhc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
static int meson_mx_sdhc_map_dma(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||
{
|
||||
struct mmc_data *data = mrq->data;
|
||||
int dma_len;
|
||||
unsigned int dma_len;
|
||||
|
||||
if (!data)
|
||||
return 0;
|
||||
|
||||
dma_len = dma_map_sg(mmc_dev(mmc), data->sg, data->sg_len,
|
||||
mmc_get_dma_dir(data));
|
||||
if (dma_len <= 0) {
|
||||
if (!dma_len) {
|
||||
dev_err(mmc_dev(mmc), "dma_map_sg failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -474,6 +474,84 @@ struct msdc_host {
|
|||
struct cqhci_host *cq_host;
|
||||
};
|
||||
|
||||
static const struct mtk_mmc_compatible mt2701_compat = {
|
||||
.clk_div_bits = 12,
|
||||
.recheck_sdio_irq = true,
|
||||
.hs400_tune = false,
|
||||
.pad_tune_reg = MSDC_PAD_TUNE0,
|
||||
.async_fifo = true,
|
||||
.data_tune = true,
|
||||
.busy_check = false,
|
||||
.stop_clk_fix = false,
|
||||
.enhance_rx = false,
|
||||
.support_64g = false,
|
||||
};
|
||||
|
||||
static const struct mtk_mmc_compatible mt2712_compat = {
|
||||
.clk_div_bits = 12,
|
||||
.recheck_sdio_irq = false,
|
||||
.hs400_tune = false,
|
||||
.pad_tune_reg = MSDC_PAD_TUNE0,
|
||||
.async_fifo = true,
|
||||
.data_tune = true,
|
||||
.busy_check = true,
|
||||
.stop_clk_fix = true,
|
||||
.enhance_rx = true,
|
||||
.support_64g = true,
|
||||
};
|
||||
|
||||
static const struct mtk_mmc_compatible mt6779_compat = {
|
||||
.clk_div_bits = 12,
|
||||
.recheck_sdio_irq = false,
|
||||
.hs400_tune = false,
|
||||
.pad_tune_reg = MSDC_PAD_TUNE0,
|
||||
.async_fifo = true,
|
||||
.data_tune = true,
|
||||
.busy_check = true,
|
||||
.stop_clk_fix = true,
|
||||
.enhance_rx = true,
|
||||
.support_64g = true,
|
||||
};
|
||||
|
||||
static const struct mtk_mmc_compatible mt6795_compat = {
|
||||
.clk_div_bits = 8,
|
||||
.recheck_sdio_irq = false,
|
||||
.hs400_tune = true,
|
||||
.pad_tune_reg = MSDC_PAD_TUNE,
|
||||
.async_fifo = false,
|
||||
.data_tune = false,
|
||||
.busy_check = false,
|
||||
.stop_clk_fix = false,
|
||||
.enhance_rx = false,
|
||||
.support_64g = false,
|
||||
};
|
||||
|
||||
static const struct mtk_mmc_compatible mt7620_compat = {
|
||||
.clk_div_bits = 8,
|
||||
.recheck_sdio_irq = true,
|
||||
.hs400_tune = false,
|
||||
.pad_tune_reg = MSDC_PAD_TUNE,
|
||||
.async_fifo = false,
|
||||
.data_tune = false,
|
||||
.busy_check = false,
|
||||
.stop_clk_fix = false,
|
||||
.enhance_rx = false,
|
||||
.use_internal_cd = true,
|
||||
};
|
||||
|
||||
static const struct mtk_mmc_compatible mt7622_compat = {
|
||||
.clk_div_bits = 12,
|
||||
.recheck_sdio_irq = true,
|
||||
.hs400_tune = false,
|
||||
.pad_tune_reg = MSDC_PAD_TUNE0,
|
||||
.async_fifo = true,
|
||||
.data_tune = true,
|
||||
.busy_check = true,
|
||||
.stop_clk_fix = true,
|
||||
.enhance_rx = true,
|
||||
.support_64g = false,
|
||||
};
|
||||
|
||||
static const struct mtk_mmc_compatible mt8135_compat = {
|
||||
.clk_div_bits = 8,
|
||||
.recheck_sdio_irq = true,
|
||||
|
|
@ -513,45 +591,6 @@ static const struct mtk_mmc_compatible mt8183_compat = {
|
|||
.support_64g = true,
|
||||
};
|
||||
|
||||
static const struct mtk_mmc_compatible mt2701_compat = {
|
||||
.clk_div_bits = 12,
|
||||
.recheck_sdio_irq = true,
|
||||
.hs400_tune = false,
|
||||
.pad_tune_reg = MSDC_PAD_TUNE0,
|
||||
.async_fifo = true,
|
||||
.data_tune = true,
|
||||
.busy_check = false,
|
||||
.stop_clk_fix = false,
|
||||
.enhance_rx = false,
|
||||
.support_64g = false,
|
||||
};
|
||||
|
||||
static const struct mtk_mmc_compatible mt2712_compat = {
|
||||
.clk_div_bits = 12,
|
||||
.recheck_sdio_irq = false,
|
||||
.hs400_tune = false,
|
||||
.pad_tune_reg = MSDC_PAD_TUNE0,
|
||||
.async_fifo = true,
|
||||
.data_tune = true,
|
||||
.busy_check = true,
|
||||
.stop_clk_fix = true,
|
||||
.enhance_rx = true,
|
||||
.support_64g = true,
|
||||
};
|
||||
|
||||
static const struct mtk_mmc_compatible mt7622_compat = {
|
||||
.clk_div_bits = 12,
|
||||
.recheck_sdio_irq = true,
|
||||
.hs400_tune = false,
|
||||
.pad_tune_reg = MSDC_PAD_TUNE0,
|
||||
.async_fifo = true,
|
||||
.data_tune = true,
|
||||
.busy_check = true,
|
||||
.stop_clk_fix = true,
|
||||
.enhance_rx = true,
|
||||
.support_64g = false,
|
||||
};
|
||||
|
||||
static const struct mtk_mmc_compatible mt8516_compat = {
|
||||
.clk_div_bits = 12,
|
||||
.recheck_sdio_irq = true,
|
||||
|
|
@ -563,42 +602,18 @@ static const struct mtk_mmc_compatible mt8516_compat = {
|
|||
.stop_clk_fix = true,
|
||||
};
|
||||
|
||||
static const struct mtk_mmc_compatible mt7620_compat = {
|
||||
.clk_div_bits = 8,
|
||||
.recheck_sdio_irq = true,
|
||||
.hs400_tune = false,
|
||||
.pad_tune_reg = MSDC_PAD_TUNE,
|
||||
.async_fifo = false,
|
||||
.data_tune = false,
|
||||
.busy_check = false,
|
||||
.stop_clk_fix = false,
|
||||
.enhance_rx = false,
|
||||
.use_internal_cd = true,
|
||||
};
|
||||
|
||||
static const struct mtk_mmc_compatible mt6779_compat = {
|
||||
.clk_div_bits = 12,
|
||||
.recheck_sdio_irq = false,
|
||||
.hs400_tune = false,
|
||||
.pad_tune_reg = MSDC_PAD_TUNE0,
|
||||
.async_fifo = true,
|
||||
.data_tune = true,
|
||||
.busy_check = true,
|
||||
.stop_clk_fix = true,
|
||||
.enhance_rx = true,
|
||||
.support_64g = true,
|
||||
};
|
||||
|
||||
static const struct of_device_id msdc_of_ids[] = {
|
||||
{ .compatible = "mediatek,mt2701-mmc", .data = &mt2701_compat},
|
||||
{ .compatible = "mediatek,mt2712-mmc", .data = &mt2712_compat},
|
||||
{ .compatible = "mediatek,mt6779-mmc", .data = &mt6779_compat},
|
||||
{ .compatible = "mediatek,mt6795-mmc", .data = &mt6795_compat},
|
||||
{ .compatible = "mediatek,mt7620-mmc", .data = &mt7620_compat},
|
||||
{ .compatible = "mediatek,mt7622-mmc", .data = &mt7622_compat},
|
||||
{ .compatible = "mediatek,mt8135-mmc", .data = &mt8135_compat},
|
||||
{ .compatible = "mediatek,mt8173-mmc", .data = &mt8173_compat},
|
||||
{ .compatible = "mediatek,mt8183-mmc", .data = &mt8183_compat},
|
||||
{ .compatible = "mediatek,mt2701-mmc", .data = &mt2701_compat},
|
||||
{ .compatible = "mediatek,mt2712-mmc", .data = &mt2712_compat},
|
||||
{ .compatible = "mediatek,mt7622-mmc", .data = &mt7622_compat},
|
||||
{ .compatible = "mediatek,mt8516-mmc", .data = &mt8516_compat},
|
||||
{ .compatible = "mediatek,mt7620-mmc", .data = &mt7620_compat},
|
||||
{ .compatible = "mediatek,mt6779-mmc", .data = &mt6779_compat},
|
||||
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, msdc_of_ids);
|
||||
|
|
|
|||
|
|
@ -1042,7 +1042,6 @@ static int sd_set_timing(struct rtsx_usb_sdmmc *host,
|
|||
unsigned char timing, bool *ddr_mode)
|
||||
{
|
||||
struct rtsx_ucr *ucr = host->ucr;
|
||||
int err;
|
||||
|
||||
*ddr_mode = false;
|
||||
|
||||
|
|
@ -1097,9 +1096,7 @@ static int sd_set_timing(struct rtsx_usb_sdmmc *host,
|
|||
break;
|
||||
}
|
||||
|
||||
err = rtsx_usb_send_cmd(ucr, MODE_C, 100);
|
||||
|
||||
return err;
|
||||
return rtsx_usb_send_cmd(ucr, MODE_C, 100);
|
||||
}
|
||||
|
||||
static void sdmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
|
|
|
|||
|
|
@ -2441,6 +2441,7 @@ static const struct of_device_id sdhci_msm_dt_match[] = {
|
|||
*/
|
||||
{.compatible = "qcom,sdhci-msm-v4", .data = &sdhci_msm_mci_var},
|
||||
{.compatible = "qcom,sdhci-msm-v5", .data = &sdhci_msm_v5_var},
|
||||
{.compatible = "qcom,sdm670-sdhci", .data = &sdm845_sdhci_var},
|
||||
{.compatible = "qcom,sdm845-sdhci", .data = &sdm845_sdhci_var},
|
||||
{.compatible = "qcom,sc7180-sdhci", .data = &sdm845_sdhci_var},
|
||||
{},
|
||||
|
|
|
|||
|
|
@ -297,6 +297,27 @@ static const struct sdhci_pci_fixes sdhci_ricoh_mmc = {
|
|||
SDHCI_QUIRK_MISSING_CAPS
|
||||
};
|
||||
|
||||
static void ene_714_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
{
|
||||
struct sdhci_host *host = mmc_priv(mmc);
|
||||
|
||||
sdhci_set_ios(mmc, ios);
|
||||
|
||||
/*
|
||||
* Some (ENE) controllers misbehave on some ios operations,
|
||||
* signalling timeout and CRC errors even on CMD0. Resetting
|
||||
* it on each ios seems to solve the problem.
|
||||
*/
|
||||
if (!(host->flags & SDHCI_DEVICE_DEAD))
|
||||
sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
|
||||
}
|
||||
|
||||
static int ene_714_probe_slot(struct sdhci_pci_slot *slot)
|
||||
{
|
||||
slot->host->mmc_host_ops.set_ios = ene_714_set_ios;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct sdhci_pci_fixes sdhci_ene_712 = {
|
||||
.quirks = SDHCI_QUIRK_SINGLE_POWER_WRITE |
|
||||
SDHCI_QUIRK_BROKEN_DMA,
|
||||
|
|
@ -304,8 +325,8 @@ static const struct sdhci_pci_fixes sdhci_ene_712 = {
|
|||
|
||||
static const struct sdhci_pci_fixes sdhci_ene_714 = {
|
||||
.quirks = SDHCI_QUIRK_SINGLE_POWER_WRITE |
|
||||
SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS |
|
||||
SDHCI_QUIRK_BROKEN_DMA,
|
||||
.probe_slot = ene_714_probe_slot,
|
||||
};
|
||||
|
||||
static const struct sdhci_pci_fixes sdhci_cafe = {
|
||||
|
|
|
|||
|
|
@ -317,11 +317,12 @@ static int sdhci_o2_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|||
u32 reg_val;
|
||||
|
||||
/*
|
||||
* This handler only implements the eMMC tuning that is specific to
|
||||
* This handler implements the hardware tuning that is specific to
|
||||
* this controller. Fall back to the standard method for other TIMING.
|
||||
*/
|
||||
if ((host->timing != MMC_TIMING_MMC_HS200) &&
|
||||
(host->timing != MMC_TIMING_UHS_SDR104))
|
||||
(host->timing != MMC_TIMING_UHS_SDR104) &&
|
||||
(host->timing != MMC_TIMING_UHS_SDR50))
|
||||
return sdhci_execute_tuning(mmc, opcode);
|
||||
|
||||
if (WARN_ON((opcode != MMC_SEND_TUNING_BLOCK_HS200) &&
|
||||
|
|
@ -631,6 +632,8 @@ static int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot)
|
|||
if (reg & 0x1)
|
||||
host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12;
|
||||
|
||||
host->quirks2 |= SDHCI_QUIRK2_BROKEN_DDR50;
|
||||
|
||||
sdhci_pci_o2_enable_msi(chip, host);
|
||||
|
||||
if (chip->pdev->device == PCI_DEVICE_ID_O2_SEABIRD0) {
|
||||
|
|
|
|||
|
|
@ -205,14 +205,14 @@ static inline u32 sdhci_sprd_calc_div(u32 base_clk, u32 clk)
|
|||
if ((base_clk / div) > (clk * 2))
|
||||
div++;
|
||||
|
||||
if (div > SDHCI_SPRD_CLK_MAX_DIV)
|
||||
div = SDHCI_SPRD_CLK_MAX_DIV;
|
||||
|
||||
if (div % 2)
|
||||
div = (div + 1) / 2;
|
||||
else
|
||||
div = div / 2;
|
||||
|
||||
if (div > SDHCI_SPRD_CLK_MAX_DIV)
|
||||
div = SDHCI_SPRD_CLK_MAX_DIV;
|
||||
|
||||
return div;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -233,28 +233,62 @@ void sdhci_reset(struct sdhci_host *host, u8 mask)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(sdhci_reset);
|
||||
|
||||
static void sdhci_do_reset(struct sdhci_host *host, u8 mask)
|
||||
static bool sdhci_do_reset(struct sdhci_host *host, u8 mask)
|
||||
{
|
||||
if (host->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) {
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
|
||||
if (!mmc->ops->get_cd(mmc))
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
host->ops->reset(host, mask);
|
||||
|
||||
if (mask & SDHCI_RESET_ALL) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static void sdhci_reset_for_all(struct sdhci_host *host)
|
||||
{
|
||||
if (sdhci_do_reset(host, SDHCI_RESET_ALL)) {
|
||||
if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
|
||||
if (host->ops->enable_dma)
|
||||
host->ops->enable_dma(host);
|
||||
}
|
||||
|
||||
/* Resetting the controller clears many */
|
||||
host->preset_enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
enum sdhci_reset_reason {
|
||||
SDHCI_RESET_FOR_INIT,
|
||||
SDHCI_RESET_FOR_REQUEST_ERROR,
|
||||
SDHCI_RESET_FOR_REQUEST_ERROR_DATA_ONLY,
|
||||
SDHCI_RESET_FOR_TUNING_ABORT,
|
||||
SDHCI_RESET_FOR_CARD_REMOVED,
|
||||
SDHCI_RESET_FOR_CQE_RECOVERY,
|
||||
};
|
||||
|
||||
static void sdhci_reset_for_reason(struct sdhci_host *host, enum sdhci_reset_reason reason)
|
||||
{
|
||||
switch (reason) {
|
||||
case SDHCI_RESET_FOR_INIT:
|
||||
sdhci_do_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
|
||||
break;
|
||||
case SDHCI_RESET_FOR_REQUEST_ERROR:
|
||||
case SDHCI_RESET_FOR_TUNING_ABORT:
|
||||
case SDHCI_RESET_FOR_CARD_REMOVED:
|
||||
case SDHCI_RESET_FOR_CQE_RECOVERY:
|
||||
sdhci_do_reset(host, SDHCI_RESET_CMD);
|
||||
sdhci_do_reset(host, SDHCI_RESET_DATA);
|
||||
break;
|
||||
case SDHCI_RESET_FOR_REQUEST_ERROR_DATA_ONLY:
|
||||
sdhci_do_reset(host, SDHCI_RESET_DATA);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#define sdhci_reset_for(h, r) sdhci_reset_for_reason((h), SDHCI_RESET_FOR_##r)
|
||||
|
||||
static void sdhci_set_default_irqs(struct sdhci_host *host)
|
||||
{
|
||||
host->ier = SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT |
|
||||
|
|
@ -323,9 +357,9 @@ static void sdhci_init(struct sdhci_host *host, int soft)
|
|||
unsigned long flags;
|
||||
|
||||
if (soft)
|
||||
sdhci_do_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
|
||||
sdhci_reset_for(host, INIT);
|
||||
else
|
||||
sdhci_do_reset(host, SDHCI_RESET_ALL);
|
||||
sdhci_reset_for_all(host);
|
||||
|
||||
if (host->v4_mode)
|
||||
sdhci_do_enable_v4_mode(host);
|
||||
|
|
@ -1538,8 +1572,9 @@ static void __sdhci_finish_data(struct sdhci_host *host, bool sw_data_timeout)
|
|||
*/
|
||||
if (data->error) {
|
||||
if (!host->cmd || host->cmd == data_cmd)
|
||||
sdhci_do_reset(host, SDHCI_RESET_CMD);
|
||||
sdhci_do_reset(host, SDHCI_RESET_DATA);
|
||||
sdhci_reset_for(host, REQUEST_ERROR);
|
||||
else
|
||||
sdhci_reset_for(host, REQUEST_ERROR_DATA_ONLY);
|
||||
}
|
||||
|
||||
if ((host->flags & (SDHCI_REQ_USE_DMA | SDHCI_USE_ADMA)) ==
|
||||
|
|
@ -2403,14 +2438,6 @@ void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
host->ops->set_clock(host, host->clock);
|
||||
} else
|
||||
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
|
||||
|
||||
/*
|
||||
* Some (ENE) controllers go apeshit on some ios operation,
|
||||
* signalling timeout and CRC errors even on CMD0. Resetting
|
||||
* it on each ios seems to solve the problem.
|
||||
*/
|
||||
if (host->quirks & SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS)
|
||||
sdhci_do_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sdhci_set_ios);
|
||||
|
||||
|
|
@ -2718,8 +2745,7 @@ void sdhci_abort_tuning(struct sdhci_host *host, u32 opcode)
|
|||
{
|
||||
sdhci_reset_tuning(host);
|
||||
|
||||
sdhci_do_reset(host, SDHCI_RESET_CMD);
|
||||
sdhci_do_reset(host, SDHCI_RESET_DATA);
|
||||
sdhci_reset_for(host, TUNING_ABORT);
|
||||
|
||||
sdhci_end_tuning(host);
|
||||
|
||||
|
|
@ -2987,8 +3013,7 @@ static void sdhci_card_event(struct mmc_host *mmc)
|
|||
pr_err("%s: Resetting controller.\n",
|
||||
mmc_hostname(mmc));
|
||||
|
||||
sdhci_do_reset(host, SDHCI_RESET_CMD);
|
||||
sdhci_do_reset(host, SDHCI_RESET_DATA);
|
||||
sdhci_reset_for(host, CARD_REMOVED);
|
||||
|
||||
sdhci_error_out_mrqs(host, -ENOMEDIUM);
|
||||
}
|
||||
|
|
@ -3059,12 +3084,7 @@ static bool sdhci_request_done(struct sdhci_host *host)
|
|||
/* This is to force an update */
|
||||
host->ops->set_clock(host, host->clock);
|
||||
|
||||
/*
|
||||
* Spec says we should do both at the same time, but Ricoh
|
||||
* controllers do not like that.
|
||||
*/
|
||||
sdhci_do_reset(host, SDHCI_RESET_CMD);
|
||||
sdhci_do_reset(host, SDHCI_RESET_DATA);
|
||||
sdhci_reset_for(host, REQUEST_ERROR);
|
||||
|
||||
host->pending_reset = false;
|
||||
}
|
||||
|
|
@ -3905,10 +3925,8 @@ void sdhci_cqe_disable(struct mmc_host *mmc, bool recovery)
|
|||
|
||||
host->cqe_on = false;
|
||||
|
||||
if (recovery) {
|
||||
sdhci_do_reset(host, SDHCI_RESET_CMD);
|
||||
sdhci_do_reset(host, SDHCI_RESET_DATA);
|
||||
}
|
||||
if (recovery)
|
||||
sdhci_reset_for(host, CQE_RECOVERY);
|
||||
|
||||
pr_debug("%s: sdhci: CQE off, IRQ mask %#x, IRQ status %#x\n",
|
||||
mmc_hostname(mmc), host->ier,
|
||||
|
|
@ -4066,7 +4084,7 @@ void __sdhci_read_caps(struct sdhci_host *host, const u16 *ver,
|
|||
if (debug_quirks2)
|
||||
host->quirks2 = debug_quirks2;
|
||||
|
||||
sdhci_do_reset(host, SDHCI_RESET_ALL);
|
||||
sdhci_reset_for_all(host);
|
||||
|
||||
if (host->v4_mode)
|
||||
sdhci_do_enable_v4_mode(host);
|
||||
|
|
@ -4807,7 +4825,7 @@ int __sdhci_add_host(struct sdhci_host *host)
|
|||
unled:
|
||||
sdhci_led_unregister(host);
|
||||
unirq:
|
||||
sdhci_do_reset(host, SDHCI_RESET_ALL);
|
||||
sdhci_reset_for_all(host);
|
||||
sdhci_writel(host, 0, SDHCI_INT_ENABLE);
|
||||
sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE);
|
||||
free_irq(host->irq, host);
|
||||
|
|
@ -4865,7 +4883,7 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
|
|||
sdhci_led_unregister(host);
|
||||
|
||||
if (!dead)
|
||||
sdhci_do_reset(host, SDHCI_RESET_ALL);
|
||||
sdhci_reset_for_all(host);
|
||||
|
||||
sdhci_writel(host, 0, SDHCI_INT_ENABLE);
|
||||
sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE);
|
||||
|
|
|
|||
|
|
@ -379,8 +379,6 @@ struct sdhci_host {
|
|||
#define SDHCI_QUIRK_NO_CARD_NO_RESET (1<<2)
|
||||
/* Controller doesn't like clearing the power reg before a change */
|
||||
#define SDHCI_QUIRK_SINGLE_POWER_WRITE (1<<3)
|
||||
/* Controller has flaky internal state so reset it on each ios change */
|
||||
#define SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS (1<<4)
|
||||
/* Controller has an unusable DMA engine */
|
||||
#define SDHCI_QUIRK_BROKEN_DMA (1<<5)
|
||||
/* Controller has an unusable ADMA engine */
|
||||
|
|
|
|||
|
|
@ -554,7 +554,6 @@ static const struct cqhci_host_ops sdhci_am654_cqhci_ops = {
|
|||
static int sdhci_am654_cqe_add_host(struct sdhci_host *host)
|
||||
{
|
||||
struct cqhci_host *cq_host;
|
||||
int ret;
|
||||
|
||||
cq_host = devm_kzalloc(mmc_dev(host->mmc), sizeof(struct cqhci_host),
|
||||
GFP_KERNEL);
|
||||
|
|
@ -568,9 +567,7 @@ static int sdhci_am654_cqe_add_host(struct sdhci_host *host)
|
|||
|
||||
host->mmc->caps2 |= MMC_CAP2_CQE;
|
||||
|
||||
ret = cqhci_init(cq_host, host->mmc, 1);
|
||||
|
||||
return ret;
|
||||
return cqhci_init(cq_host, host->mmc, 1);
|
||||
}
|
||||
|
||||
static int sdhci_am654_get_otap_delay(struct sdhci_host *host,
|
||||
|
|
|
|||
|
|
@ -846,7 +846,7 @@ static int wmt_mci_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(priv->clk_sdmmc)) {
|
||||
dev_err(&pdev->dev, "Error getting clock\n");
|
||||
ret = PTR_ERR(priv->clk_sdmmc);
|
||||
goto fail5;
|
||||
goto fail5_and_a_half;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(priv->clk_sdmmc);
|
||||
|
|
@ -863,6 +863,9 @@ static int wmt_mci_probe(struct platform_device *pdev)
|
|||
return 0;
|
||||
fail6:
|
||||
clk_put(priv->clk_sdmmc);
|
||||
fail5_and_a_half:
|
||||
dma_free_coherent(&pdev->dev, mmc->max_blk_count * 16,
|
||||
priv->dma_desc_buffer, priv->dma_desc_device_addr);
|
||||
fail5:
|
||||
free_irq(dma_irq, priv);
|
||||
fail4:
|
||||
|
|
|
|||
|
|
@ -240,10 +240,6 @@ config PCMCIA_PROBE
|
|||
bool
|
||||
default y if ISA && !ARCH_SA1100 && !PARISC
|
||||
|
||||
config PCMCIA_VRC4171
|
||||
tristate "NEC VRC4171 Card Controllers support"
|
||||
depends on CPU_VR41XX && ISA && PCMCIA
|
||||
|
||||
config OMAP_CF
|
||||
tristate "OMAP CompactFlash Controller"
|
||||
depends on PCMCIA
|
||||
|
|
@ -252,15 +248,6 @@ config OMAP_CF
|
|||
Say Y here to support the CompactFlash controller on OMAP.
|
||||
Note that this doesn't support "True IDE" mode.
|
||||
|
||||
config AT91_CF
|
||||
tristate "AT91 CompactFlash Controller"
|
||||
depends on PCI
|
||||
depends on OF
|
||||
depends on PCMCIA && ARCH_AT91
|
||||
help
|
||||
Say Y here to support the CompactFlash controller on AT91 chips.
|
||||
Or choose M to compile the driver as a module named "at91_cf".
|
||||
|
||||
config ELECTRA_CF
|
||||
tristate "Electra CompactFlash Controller"
|
||||
depends on PCMCIA && PPC_PASEMI
|
||||
|
|
|
|||
|
|
@ -29,9 +29,7 @@ obj-$(CONFIG_PCMCIA_SA11XX_BASE) += sa11xx_base.o
|
|||
obj-$(CONFIG_PCMCIA_SA1100) += sa1100_cs.o
|
||||
obj-$(CONFIG_PCMCIA_SA1111) += sa1111_cs.o
|
||||
obj-$(CONFIG_PCMCIA_BCM63XX) += bcm63xx_pcmcia.o
|
||||
obj-$(CONFIG_PCMCIA_VRC4171) += vrc4171_card.o
|
||||
obj-$(CONFIG_OMAP_CF) += omap_cf.o
|
||||
obj-$(CONFIG_AT91_CF) += at91_cf.o
|
||||
obj-$(CONFIG_ELECTRA_CF) += electra_cf.o
|
||||
obj-$(CONFIG_PCMCIA_ALCHEMY_DEVBOARD) += db1xxx_ss.o
|
||||
obj-$(CONFIG_PCMCIA_MAX1600) += max1600.o
|
||||
|
|
|
|||
|
|
@ -1,407 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* at91_cf.c -- AT91 CompactFlash controller driver
|
||||
*
|
||||
* Copyright (C) 2005 David Brownell
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/mfd/syscon/atmel-mc.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <pcmcia/ss.h>
|
||||
|
||||
/*
|
||||
* A0..A10 work in each range; A23 indicates I/O space; A25 is CFRNW;
|
||||
* some other bit in {A24,A22..A11} is nREG to flag memory access
|
||||
* (vs attributes). So more than 2KB/region would just be waste.
|
||||
* Note: These are offsets from the physical base address.
|
||||
*/
|
||||
#define CF_ATTR_PHYS (0)
|
||||
#define CF_IO_PHYS (1 << 23)
|
||||
#define CF_MEM_PHYS (0x017ff800)
|
||||
|
||||
struct at91_cf_data {
|
||||
int irq_pin; /* I/O IRQ */
|
||||
int det_pin; /* Card detect */
|
||||
int vcc_pin; /* power switching */
|
||||
int rst_pin; /* card reset */
|
||||
u8 chipselect; /* EBI Chip Select number */
|
||||
u8 flags;
|
||||
#define AT91_CF_TRUE_IDE 0x01
|
||||
#define AT91_IDE_SWAP_A0_A2 0x02
|
||||
};
|
||||
|
||||
struct regmap *mc;
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
struct at91_cf_socket {
|
||||
struct pcmcia_socket socket;
|
||||
|
||||
unsigned present:1;
|
||||
|
||||
struct platform_device *pdev;
|
||||
struct at91_cf_data *board;
|
||||
|
||||
unsigned long phys_baseaddr;
|
||||
};
|
||||
|
||||
static inline int at91_cf_present(struct at91_cf_socket *cf)
|
||||
{
|
||||
return !gpio_get_value(cf->board->det_pin);
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
static int at91_cf_ss_init(struct pcmcia_socket *s)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t at91_cf_irq(int irq, void *_cf)
|
||||
{
|
||||
struct at91_cf_socket *cf = _cf;
|
||||
|
||||
if (irq == gpio_to_irq(cf->board->det_pin)) {
|
||||
unsigned present = at91_cf_present(cf);
|
||||
|
||||
/* kick pccard as needed */
|
||||
if (present != cf->present) {
|
||||
cf->present = present;
|
||||
dev_dbg(&cf->pdev->dev, "card %s\n",
|
||||
present ? "present" : "gone");
|
||||
pcmcia_parse_events(&cf->socket, SS_DETECT);
|
||||
}
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int at91_cf_get_status(struct pcmcia_socket *s, u_int *sp)
|
||||
{
|
||||
struct at91_cf_socket *cf;
|
||||
|
||||
if (!sp)
|
||||
return -EINVAL;
|
||||
|
||||
cf = container_of(s, struct at91_cf_socket, socket);
|
||||
|
||||
/* NOTE: CF is always 3VCARD */
|
||||
if (at91_cf_present(cf)) {
|
||||
int rdy = gpio_is_valid(cf->board->irq_pin); /* RDY/nIRQ */
|
||||
int vcc = gpio_is_valid(cf->board->vcc_pin);
|
||||
|
||||
*sp = SS_DETECT | SS_3VCARD;
|
||||
if (!rdy || gpio_get_value(cf->board->irq_pin))
|
||||
*sp |= SS_READY;
|
||||
if (!vcc || gpio_get_value(cf->board->vcc_pin))
|
||||
*sp |= SS_POWERON;
|
||||
} else
|
||||
*sp = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
at91_cf_set_socket(struct pcmcia_socket *sock, struct socket_state_t *s)
|
||||
{
|
||||
struct at91_cf_socket *cf;
|
||||
|
||||
cf = container_of(sock, struct at91_cf_socket, socket);
|
||||
|
||||
/* switch Vcc if needed and possible */
|
||||
if (gpio_is_valid(cf->board->vcc_pin)) {
|
||||
switch (s->Vcc) {
|
||||
case 0:
|
||||
gpio_set_value(cf->board->vcc_pin, 0);
|
||||
break;
|
||||
case 33:
|
||||
gpio_set_value(cf->board->vcc_pin, 1);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* toggle reset if needed */
|
||||
gpio_set_value(cf->board->rst_pin, s->flags & SS_RESET);
|
||||
|
||||
dev_dbg(&cf->pdev->dev, "Vcc %d, io_irq %d, flags %04x csc %04x\n",
|
||||
s->Vcc, s->io_irq, s->flags, s->csc_mask);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int at91_cf_ss_suspend(struct pcmcia_socket *s)
|
||||
{
|
||||
return at91_cf_set_socket(s, &dead_socket);
|
||||
}
|
||||
|
||||
/* we already mapped the I/O region */
|
||||
static int at91_cf_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io)
|
||||
{
|
||||
struct at91_cf_socket *cf;
|
||||
u32 csr;
|
||||
|
||||
cf = container_of(s, struct at91_cf_socket, socket);
|
||||
io->flags &= (MAP_ACTIVE | MAP_16BIT | MAP_AUTOSZ);
|
||||
|
||||
/*
|
||||
* Use 16 bit accesses unless/until we need 8-bit i/o space.
|
||||
*
|
||||
* NOTE: this CF controller ignores IOIS16, so we can't really do
|
||||
* MAP_AUTOSZ. The 16bit mode allows single byte access on either
|
||||
* D0-D7 (even addr) or D8-D15 (odd), so it's close enough for many
|
||||
* purposes (and handles ide-cs).
|
||||
*
|
||||
* The 8bit mode is needed for odd byte access on D0-D7. It seems
|
||||
* some cards only like that way to get at the odd byte, despite
|
||||
* CF 3.0 spec table 35 also giving the D8-D15 option.
|
||||
*/
|
||||
if (!(io->flags & (MAP_16BIT | MAP_AUTOSZ))) {
|
||||
csr = AT91_MC_SMC_DBW_8;
|
||||
dev_dbg(&cf->pdev->dev, "8bit i/o bus\n");
|
||||
} else {
|
||||
csr = AT91_MC_SMC_DBW_16;
|
||||
dev_dbg(&cf->pdev->dev, "16bit i/o bus\n");
|
||||
}
|
||||
regmap_update_bits(mc, AT91_MC_SMC_CSR(cf->board->chipselect),
|
||||
AT91_MC_SMC_DBW, csr);
|
||||
|
||||
io->start = cf->socket.io_offset;
|
||||
io->stop = io->start + SZ_2K - 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* pcmcia layer maps/unmaps mem regions */
|
||||
static int
|
||||
at91_cf_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *map)
|
||||
{
|
||||
struct at91_cf_socket *cf;
|
||||
|
||||
if (map->card_start)
|
||||
return -EINVAL;
|
||||
|
||||
cf = container_of(s, struct at91_cf_socket, socket);
|
||||
|
||||
map->flags &= (MAP_ACTIVE | MAP_ATTRIB | MAP_16BIT);
|
||||
if (map->flags & MAP_ATTRIB)
|
||||
map->static_start = cf->phys_baseaddr + CF_ATTR_PHYS;
|
||||
else
|
||||
map->static_start = cf->phys_baseaddr + CF_MEM_PHYS;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct pccard_operations at91_cf_ops = {
|
||||
.init = at91_cf_ss_init,
|
||||
.suspend = at91_cf_ss_suspend,
|
||||
.get_status = at91_cf_get_status,
|
||||
.set_socket = at91_cf_set_socket,
|
||||
.set_io_map = at91_cf_set_io_map,
|
||||
.set_mem_map = at91_cf_set_mem_map,
|
||||
};
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
static const struct of_device_id at91_cf_dt_ids[] = {
|
||||
{ .compatible = "atmel,at91rm9200-cf" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, at91_cf_dt_ids);
|
||||
|
||||
static int at91_cf_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct at91_cf_socket *cf;
|
||||
struct at91_cf_data *board;
|
||||
struct resource *io;
|
||||
struct resource realio;
|
||||
int status;
|
||||
|
||||
board = devm_kzalloc(&pdev->dev, sizeof(*board), GFP_KERNEL);
|
||||
if (!board)
|
||||
return -ENOMEM;
|
||||
|
||||
board->irq_pin = of_get_gpio(pdev->dev.of_node, 0);
|
||||
board->det_pin = of_get_gpio(pdev->dev.of_node, 1);
|
||||
board->vcc_pin = of_get_gpio(pdev->dev.of_node, 2);
|
||||
board->rst_pin = of_get_gpio(pdev->dev.of_node, 3);
|
||||
|
||||
mc = syscon_regmap_lookup_by_compatible("atmel,at91rm9200-sdramc");
|
||||
if (IS_ERR(mc))
|
||||
return PTR_ERR(mc);
|
||||
|
||||
if (!gpio_is_valid(board->det_pin) || !gpio_is_valid(board->rst_pin))
|
||||
return -ENODEV;
|
||||
|
||||
io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!io)
|
||||
return -ENODEV;
|
||||
|
||||
cf = devm_kzalloc(&pdev->dev, sizeof(*cf), GFP_KERNEL);
|
||||
if (!cf)
|
||||
return -ENOMEM;
|
||||
|
||||
cf->board = board;
|
||||
cf->pdev = pdev;
|
||||
cf->phys_baseaddr = io->start;
|
||||
platform_set_drvdata(pdev, cf);
|
||||
|
||||
/* must be a GPIO; ergo must trigger on both edges */
|
||||
status = devm_gpio_request(&pdev->dev, board->det_pin, "cf_det");
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
status = devm_request_irq(&pdev->dev, gpio_to_irq(board->det_pin),
|
||||
at91_cf_irq, 0, "at91_cf detect", cf);
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
|
||||
status = devm_gpio_request(&pdev->dev, board->rst_pin, "cf_rst");
|
||||
if (status < 0)
|
||||
goto fail0a;
|
||||
|
||||
if (gpio_is_valid(board->vcc_pin)) {
|
||||
status = devm_gpio_request(&pdev->dev, board->vcc_pin, "cf_vcc");
|
||||
if (status < 0)
|
||||
goto fail0a;
|
||||
}
|
||||
|
||||
/*
|
||||
* The card driver will request this irq later as needed.
|
||||
* but it causes lots of "irqNN: nobody cared" messages
|
||||
* unless we report that we handle everything (sigh).
|
||||
* (Note: DK board doesn't wire the IRQ pin...)
|
||||
*/
|
||||
if (gpio_is_valid(board->irq_pin)) {
|
||||
status = devm_gpio_request(&pdev->dev, board->irq_pin, "cf_irq");
|
||||
if (status < 0)
|
||||
goto fail0a;
|
||||
|
||||
status = devm_request_irq(&pdev->dev, gpio_to_irq(board->irq_pin),
|
||||
at91_cf_irq, IRQF_SHARED, "at91_cf", cf);
|
||||
if (status < 0)
|
||||
goto fail0a;
|
||||
cf->socket.pci_irq = gpio_to_irq(board->irq_pin);
|
||||
} else
|
||||
cf->socket.pci_irq = nr_irqs + 1;
|
||||
|
||||
/*
|
||||
* pcmcia layer only remaps "real" memory not iospace
|
||||
* io_offset is set to 0x10000 to avoid the check in static_find_io().
|
||||
* */
|
||||
cf->socket.io_offset = 0x10000;
|
||||
realio.start = cf->socket.io_offset;
|
||||
realio.end = realio.start + SZ_64K - 1;
|
||||
status = pci_remap_iospace(&realio, cf->phys_baseaddr + CF_IO_PHYS);
|
||||
if (status)
|
||||
goto fail0a;
|
||||
|
||||
/* reserve chip-select regions */
|
||||
if (!devm_request_mem_region(&pdev->dev, io->start, resource_size(io), "at91_cf")) {
|
||||
status = -ENXIO;
|
||||
goto fail0a;
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "irqs det #%d, io #%d\n",
|
||||
gpio_to_irq(board->det_pin), gpio_to_irq(board->irq_pin));
|
||||
|
||||
cf->socket.owner = THIS_MODULE;
|
||||
cf->socket.dev.parent = &pdev->dev;
|
||||
cf->socket.ops = &at91_cf_ops;
|
||||
cf->socket.resource_ops = &pccard_static_ops;
|
||||
cf->socket.features = SS_CAP_PCCARD | SS_CAP_STATIC_MAP
|
||||
| SS_CAP_MEM_ALIGN;
|
||||
cf->socket.map_size = SZ_2K;
|
||||
cf->socket.io[0].res = io;
|
||||
|
||||
status = pcmcia_register_socket(&cf->socket);
|
||||
if (status < 0)
|
||||
goto fail0a;
|
||||
|
||||
return 0;
|
||||
|
||||
fail0a:
|
||||
device_init_wakeup(&pdev->dev, 0);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int at91_cf_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct at91_cf_socket *cf = platform_get_drvdata(pdev);
|
||||
|
||||
pcmcia_unregister_socket(&cf->socket);
|
||||
device_init_wakeup(&pdev->dev, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
static int at91_cf_suspend(struct platform_device *pdev, pm_message_t mesg)
|
||||
{
|
||||
struct at91_cf_socket *cf = platform_get_drvdata(pdev);
|
||||
struct at91_cf_data *board = cf->board;
|
||||
|
||||
if (device_may_wakeup(&pdev->dev)) {
|
||||
enable_irq_wake(gpio_to_irq(board->det_pin));
|
||||
if (gpio_is_valid(board->irq_pin))
|
||||
enable_irq_wake(gpio_to_irq(board->irq_pin));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int at91_cf_resume(struct platform_device *pdev)
|
||||
{
|
||||
struct at91_cf_socket *cf = platform_get_drvdata(pdev);
|
||||
struct at91_cf_data *board = cf->board;
|
||||
|
||||
if (device_may_wakeup(&pdev->dev)) {
|
||||
disable_irq_wake(gpio_to_irq(board->det_pin));
|
||||
if (gpio_is_valid(board->irq_pin))
|
||||
disable_irq_wake(gpio_to_irq(board->irq_pin));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
#define at91_cf_suspend NULL
|
||||
#define at91_cf_resume NULL
|
||||
#endif
|
||||
|
||||
static struct platform_driver at91_cf_driver = {
|
||||
.driver = {
|
||||
.name = "at91_cf",
|
||||
.of_match_table = at91_cf_dt_ids,
|
||||
},
|
||||
.probe = at91_cf_probe,
|
||||
.remove = at91_cf_remove,
|
||||
.suspend = at91_cf_suspend,
|
||||
.resume = at91_cf_resume,
|
||||
};
|
||||
|
||||
module_platform_driver(at91_cf_driver);
|
||||
|
||||
MODULE_DESCRIPTION("AT91 Compact Flash Driver");
|
||||
MODULE_AUTHOR("David Brownell");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:at91_cf");
|
||||
|
|
@ -661,12 +661,12 @@ static int i82092aa_set_mem_map(struct pcmcia_socket *socket,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int i82092aa_module_init(void)
|
||||
static int __init i82092aa_module_init(void)
|
||||
{
|
||||
return pci_register_driver(&i82092aa_pci_driver);
|
||||
}
|
||||
|
||||
static void i82092aa_module_exit(void)
|
||||
static void __exit i82092aa_module_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&i82092aa_pci_driver);
|
||||
if (sockets[0].io_base > 0)
|
||||
|
|
|
|||
|
|
@ -124,8 +124,6 @@ static int omap_cf_get_status(struct pcmcia_socket *s, u_int *sp)
|
|||
static int
|
||||
omap_cf_set_socket(struct pcmcia_socket *sock, struct socket_state_t *s)
|
||||
{
|
||||
u16 control;
|
||||
|
||||
/* REVISIT some non-OSK boards may support power switching */
|
||||
switch (s->Vcc) {
|
||||
case 0:
|
||||
|
|
@ -135,7 +133,7 @@ omap_cf_set_socket(struct pcmcia_socket *sock, struct socket_state_t *s)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
control = omap_readw(CF_CONTROL);
|
||||
omap_readw(CF_CONTROL);
|
||||
if (s->flags & SS_RESET)
|
||||
omap_writew(CF_CONTROL_RESET, CF_CONTROL);
|
||||
else
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ static int sa11x0_drv_pcmcia_legacy_probe(struct platform_device *dev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int sa11x0_drv_pcmcia_legacy_remove(struct platform_device *dev)
|
||||
static void sa11x0_drv_pcmcia_legacy_remove(struct platform_device *dev)
|
||||
{
|
||||
struct skt_dev_info *sinfo = platform_get_drvdata(dev);
|
||||
int i;
|
||||
|
|
@ -134,8 +134,6 @@ static int sa11x0_drv_pcmcia_legacy_remove(struct platform_device *dev)
|
|||
|
||||
for (i = 0; i < sinfo->nskt; i++)
|
||||
soc_pcmcia_remove_one(&sinfo->skt[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sa11x0_drv_pcmcia_probe(struct platform_device *pdev)
|
||||
|
|
@ -167,8 +165,10 @@ static int sa11x0_drv_pcmcia_remove(struct platform_device *dev)
|
|||
{
|
||||
struct soc_pcmcia_socket *skt;
|
||||
|
||||
if (dev->id == -1)
|
||||
return sa11x0_drv_pcmcia_legacy_remove(dev);
|
||||
if (dev->id == -1) {
|
||||
sa11x0_drv_pcmcia_legacy_remove(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
skt = platform_get_drvdata(dev);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,745 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* vrc4171_card.c, NEC VRC4171 Card Controller driver for Socket Services.
|
||||
*
|
||||
* Copyright (C) 2003-2005 Yoichi Yuasa <yuasa@linux-mips.org>
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
#include <pcmcia/ss.h>
|
||||
|
||||
#include "i82365.h"
|
||||
|
||||
MODULE_DESCRIPTION("NEC VRC4171 Card Controllers driver for Socket Services");
|
||||
MODULE_AUTHOR("Yoichi Yuasa <yuasa@linux-mips.org>");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define CARD_MAX_SLOTS 2
|
||||
#define CARD_SLOTA 0
|
||||
#define CARD_SLOTB 1
|
||||
#define CARD_SLOTB_OFFSET 0x40
|
||||
|
||||
#define CARD_MEM_START 0x10000000
|
||||
#define CARD_MEM_END 0x13ffffff
|
||||
#define CARD_MAX_MEM_OFFSET 0x3ffffff
|
||||
#define CARD_MAX_MEM_SPEED 1000
|
||||
|
||||
#define CARD_CONTROLLER_INDEX 0x03e0
|
||||
#define CARD_CONTROLLER_DATA 0x03e1
|
||||
/* Power register */
|
||||
#define VPP_GET_VCC 0x01
|
||||
#define POWER_ENABLE 0x10
|
||||
#define CARD_VOLTAGE_SENSE 0x1f
|
||||
#define VCC_3VORXV_CAPABLE 0x00
|
||||
#define VCC_XV_ONLY 0x01
|
||||
#define VCC_3V_CAPABLE 0x02
|
||||
#define VCC_5V_ONLY 0x03
|
||||
#define CARD_VOLTAGE_SELECT 0x2f
|
||||
#define VCC_3V 0x01
|
||||
#define VCC_5V 0x00
|
||||
#define VCC_XV 0x02
|
||||
#define VCC_STATUS_3V 0x02
|
||||
#define VCC_STATUS_5V 0x01
|
||||
#define VCC_STATUS_XV 0x03
|
||||
#define GLOBAL_CONTROL 0x1e
|
||||
#define EXWRBK 0x04
|
||||
#define IRQPM_EN 0x08
|
||||
#define CLRPMIRQ 0x10
|
||||
|
||||
#define INTERRUPT_STATUS 0x05fa
|
||||
#define IRQ_A 0x02
|
||||
#define IRQ_B 0x04
|
||||
|
||||
#define CONFIGURATION1 0x05fe
|
||||
#define SLOTB_CONFIG 0xc000
|
||||
#define SLOTB_NONE 0x0000
|
||||
#define SLOTB_PCCARD 0x4000
|
||||
#define SLOTB_CF 0x8000
|
||||
#define SLOTB_FLASHROM 0xc000
|
||||
|
||||
#define CARD_CONTROLLER_START CARD_CONTROLLER_INDEX
|
||||
#define CARD_CONTROLLER_END CARD_CONTROLLER_DATA
|
||||
|
||||
#define IO_MAX_MAPS 2
|
||||
#define MEM_MAX_MAPS 5
|
||||
|
||||
enum vrc4171_slot {
|
||||
SLOT_PROBE = 0,
|
||||
SLOT_NOPROBE_IO,
|
||||
SLOT_NOPROBE_MEM,
|
||||
SLOT_NOPROBE_ALL,
|
||||
SLOT_INITIALIZED,
|
||||
};
|
||||
|
||||
enum vrc4171_slotb {
|
||||
SLOTB_IS_NONE,
|
||||
SLOTB_IS_PCCARD,
|
||||
SLOTB_IS_CF,
|
||||
SLOTB_IS_FLASHROM,
|
||||
};
|
||||
|
||||
struct vrc4171_socket {
|
||||
enum vrc4171_slot slot;
|
||||
struct pcmcia_socket pcmcia_socket;
|
||||
char name[24];
|
||||
int csc_irq;
|
||||
int io_irq;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
static struct vrc4171_socket vrc4171_sockets[CARD_MAX_SLOTS];
|
||||
static enum vrc4171_slotb vrc4171_slotb = SLOTB_IS_NONE;
|
||||
static char vrc4171_card_name[] = "NEC VRC4171 Card Controller";
|
||||
static unsigned int vrc4171_irq;
|
||||
static uint16_t vrc4171_irq_mask = 0xdeb8;
|
||||
|
||||
static struct resource vrc4171_card_resource[3] = {
|
||||
{ .name = vrc4171_card_name,
|
||||
.start = CARD_CONTROLLER_START,
|
||||
.end = CARD_CONTROLLER_END,
|
||||
.flags = IORESOURCE_IO, },
|
||||
{ .name = vrc4171_card_name,
|
||||
.start = INTERRUPT_STATUS,
|
||||
.end = INTERRUPT_STATUS,
|
||||
.flags = IORESOURCE_IO, },
|
||||
{ .name = vrc4171_card_name,
|
||||
.start = CONFIGURATION1,
|
||||
.end = CONFIGURATION1,
|
||||
.flags = IORESOURCE_IO, },
|
||||
};
|
||||
|
||||
static struct platform_device vrc4171_card_device = {
|
||||
.name = vrc4171_card_name,
|
||||
.id = 0,
|
||||
.num_resources = 3,
|
||||
.resource = vrc4171_card_resource,
|
||||
};
|
||||
|
||||
static inline uint16_t vrc4171_get_irq_status(void)
|
||||
{
|
||||
return inw(INTERRUPT_STATUS);
|
||||
}
|
||||
|
||||
static inline void vrc4171_set_multifunction_pin(enum vrc4171_slotb config)
|
||||
{
|
||||
uint16_t config1;
|
||||
|
||||
config1 = inw(CONFIGURATION1);
|
||||
config1 &= ~SLOTB_CONFIG;
|
||||
|
||||
switch (config) {
|
||||
case SLOTB_IS_NONE:
|
||||
config1 |= SLOTB_NONE;
|
||||
break;
|
||||
case SLOTB_IS_PCCARD:
|
||||
config1 |= SLOTB_PCCARD;
|
||||
break;
|
||||
case SLOTB_IS_CF:
|
||||
config1 |= SLOTB_CF;
|
||||
break;
|
||||
case SLOTB_IS_FLASHROM:
|
||||
config1 |= SLOTB_FLASHROM;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
outw(config1, CONFIGURATION1);
|
||||
}
|
||||
|
||||
static inline uint8_t exca_read_byte(int slot, uint8_t index)
|
||||
{
|
||||
if (slot == CARD_SLOTB)
|
||||
index += CARD_SLOTB_OFFSET;
|
||||
|
||||
outb(index, CARD_CONTROLLER_INDEX);
|
||||
return inb(CARD_CONTROLLER_DATA);
|
||||
}
|
||||
|
||||
static inline uint16_t exca_read_word(int slot, uint8_t index)
|
||||
{
|
||||
uint16_t data;
|
||||
|
||||
if (slot == CARD_SLOTB)
|
||||
index += CARD_SLOTB_OFFSET;
|
||||
|
||||
outb(index++, CARD_CONTROLLER_INDEX);
|
||||
data = inb(CARD_CONTROLLER_DATA);
|
||||
|
||||
outb(index, CARD_CONTROLLER_INDEX);
|
||||
data |= ((uint16_t)inb(CARD_CONTROLLER_DATA)) << 8;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static inline uint8_t exca_write_byte(int slot, uint8_t index, uint8_t data)
|
||||
{
|
||||
if (slot == CARD_SLOTB)
|
||||
index += CARD_SLOTB_OFFSET;
|
||||
|
||||
outb(index, CARD_CONTROLLER_INDEX);
|
||||
outb(data, CARD_CONTROLLER_DATA);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static inline uint16_t exca_write_word(int slot, uint8_t index, uint16_t data)
|
||||
{
|
||||
if (slot == CARD_SLOTB)
|
||||
index += CARD_SLOTB_OFFSET;
|
||||
|
||||
outb(index++, CARD_CONTROLLER_INDEX);
|
||||
outb(data, CARD_CONTROLLER_DATA);
|
||||
|
||||
outb(index, CARD_CONTROLLER_INDEX);
|
||||
outb((uint8_t)(data >> 8), CARD_CONTROLLER_DATA);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static inline int search_nonuse_irq(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
if (vrc4171_irq_mask & (1 << i)) {
|
||||
vrc4171_irq_mask &= ~(1 << i);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int pccard_init(struct pcmcia_socket *sock)
|
||||
{
|
||||
struct vrc4171_socket *socket;
|
||||
unsigned int slot;
|
||||
|
||||
sock->features |= SS_CAP_PCCARD | SS_CAP_PAGE_REGS;
|
||||
sock->irq_mask = 0;
|
||||
sock->map_size = 0x1000;
|
||||
sock->pci_irq = vrc4171_irq;
|
||||
|
||||
slot = sock->sock;
|
||||
socket = &vrc4171_sockets[slot];
|
||||
socket->csc_irq = search_nonuse_irq();
|
||||
socket->io_irq = search_nonuse_irq();
|
||||
spin_lock_init(&socket->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pccard_get_status(struct pcmcia_socket *sock, u_int *value)
|
||||
{
|
||||
unsigned int slot;
|
||||
uint8_t status, sense;
|
||||
u_int val = 0;
|
||||
|
||||
if (sock == NULL || sock->sock >= CARD_MAX_SLOTS || value == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
slot = sock->sock;
|
||||
|
||||
status = exca_read_byte(slot, I365_STATUS);
|
||||
if (exca_read_byte(slot, I365_INTCTL) & I365_PC_IOCARD) {
|
||||
if (status & I365_CS_STSCHG)
|
||||
val |= SS_STSCHG;
|
||||
} else {
|
||||
if (!(status & I365_CS_BVD1))
|
||||
val |= SS_BATDEAD;
|
||||
else if ((status & (I365_CS_BVD1 | I365_CS_BVD2)) == I365_CS_BVD1)
|
||||
val |= SS_BATWARN;
|
||||
}
|
||||
if ((status & I365_CS_DETECT) == I365_CS_DETECT)
|
||||
val |= SS_DETECT;
|
||||
if (status & I365_CS_WRPROT)
|
||||
val |= SS_WRPROT;
|
||||
if (status & I365_CS_READY)
|
||||
val |= SS_READY;
|
||||
if (status & I365_CS_POWERON)
|
||||
val |= SS_POWERON;
|
||||
|
||||
sense = exca_read_byte(slot, CARD_VOLTAGE_SENSE);
|
||||
switch (sense) {
|
||||
case VCC_3VORXV_CAPABLE:
|
||||
val |= SS_3VCARD | SS_XVCARD;
|
||||
break;
|
||||
case VCC_XV_ONLY:
|
||||
val |= SS_XVCARD;
|
||||
break;
|
||||
case VCC_3V_CAPABLE:
|
||||
val |= SS_3VCARD;
|
||||
break;
|
||||
default:
|
||||
/* 5V only */
|
||||
break;
|
||||
}
|
||||
|
||||
*value = val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline uint8_t set_Vcc_value(u_char Vcc)
|
||||
{
|
||||
switch (Vcc) {
|
||||
case 33:
|
||||
return VCC_3V;
|
||||
case 50:
|
||||
return VCC_5V;
|
||||
}
|
||||
|
||||
/* Small voltage is chosen for safety. */
|
||||
return VCC_3V;
|
||||
}
|
||||
|
||||
static int pccard_set_socket(struct pcmcia_socket *sock, socket_state_t *state)
|
||||
{
|
||||
struct vrc4171_socket *socket;
|
||||
unsigned int slot;
|
||||
uint8_t voltage, power, control, cscint;
|
||||
|
||||
if (sock == NULL || sock->sock >= CARD_MAX_SLOTS ||
|
||||
(state->Vpp != state->Vcc && state->Vpp != 0) ||
|
||||
(state->Vcc != 50 && state->Vcc != 33 && state->Vcc != 0))
|
||||
return -EINVAL;
|
||||
|
||||
slot = sock->sock;
|
||||
socket = &vrc4171_sockets[slot];
|
||||
|
||||
spin_lock_irq(&socket->lock);
|
||||
|
||||
voltage = set_Vcc_value(state->Vcc);
|
||||
exca_write_byte(slot, CARD_VOLTAGE_SELECT, voltage);
|
||||
|
||||
power = POWER_ENABLE;
|
||||
if (state->Vpp == state->Vcc)
|
||||
power |= VPP_GET_VCC;
|
||||
if (state->flags & SS_OUTPUT_ENA)
|
||||
power |= I365_PWR_OUT;
|
||||
exca_write_byte(slot, I365_POWER, power);
|
||||
|
||||
control = 0;
|
||||
if (state->io_irq != 0)
|
||||
control |= socket->io_irq;
|
||||
if (state->flags & SS_IOCARD)
|
||||
control |= I365_PC_IOCARD;
|
||||
if (state->flags & SS_RESET)
|
||||
control &= ~I365_PC_RESET;
|
||||
else
|
||||
control |= I365_PC_RESET;
|
||||
exca_write_byte(slot, I365_INTCTL, control);
|
||||
|
||||
cscint = 0;
|
||||
exca_write_byte(slot, I365_CSCINT, cscint);
|
||||
exca_read_byte(slot, I365_CSC); /* clear CardStatus change */
|
||||
if (state->csc_mask != 0)
|
||||
cscint |= socket->csc_irq << 8;
|
||||
if (state->flags & SS_IOCARD) {
|
||||
if (state->csc_mask & SS_STSCHG)
|
||||
cscint |= I365_CSC_STSCHG;
|
||||
} else {
|
||||
if (state->csc_mask & SS_BATDEAD)
|
||||
cscint |= I365_CSC_BVD1;
|
||||
if (state->csc_mask & SS_BATWARN)
|
||||
cscint |= I365_CSC_BVD2;
|
||||
}
|
||||
if (state->csc_mask & SS_READY)
|
||||
cscint |= I365_CSC_READY;
|
||||
if (state->csc_mask & SS_DETECT)
|
||||
cscint |= I365_CSC_DETECT;
|
||||
exca_write_byte(slot, I365_CSCINT, cscint);
|
||||
|
||||
spin_unlock_irq(&socket->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pccard_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *io)
|
||||
{
|
||||
unsigned int slot;
|
||||
uint8_t ioctl, addrwin;
|
||||
u_char map;
|
||||
|
||||
if (sock == NULL || sock->sock >= CARD_MAX_SLOTS ||
|
||||
io == NULL || io->map >= IO_MAX_MAPS ||
|
||||
io->start > 0xffff || io->stop > 0xffff || io->start > io->stop)
|
||||
return -EINVAL;
|
||||
|
||||
slot = sock->sock;
|
||||
map = io->map;
|
||||
|
||||
addrwin = exca_read_byte(slot, I365_ADDRWIN);
|
||||
if (addrwin & I365_ENA_IO(map)) {
|
||||
addrwin &= ~I365_ENA_IO(map);
|
||||
exca_write_byte(slot, I365_ADDRWIN, addrwin);
|
||||
}
|
||||
|
||||
exca_write_word(slot, I365_IO(map)+I365_W_START, io->start);
|
||||
exca_write_word(slot, I365_IO(map)+I365_W_STOP, io->stop);
|
||||
|
||||
ioctl = 0;
|
||||
if (io->speed > 0)
|
||||
ioctl |= I365_IOCTL_WAIT(map);
|
||||
if (io->flags & MAP_16BIT)
|
||||
ioctl |= I365_IOCTL_16BIT(map);
|
||||
if (io->flags & MAP_AUTOSZ)
|
||||
ioctl |= I365_IOCTL_IOCS16(map);
|
||||
if (io->flags & MAP_0WS)
|
||||
ioctl |= I365_IOCTL_0WS(map);
|
||||
exca_write_byte(slot, I365_IOCTL, ioctl);
|
||||
|
||||
if (io->flags & MAP_ACTIVE) {
|
||||
addrwin |= I365_ENA_IO(map);
|
||||
exca_write_byte(slot, I365_ADDRWIN, addrwin);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pccard_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map *mem)
|
||||
{
|
||||
unsigned int slot;
|
||||
uint16_t start, stop, offset;
|
||||
uint8_t addrwin;
|
||||
u_char map;
|
||||
|
||||
if (sock == NULL || sock->sock >= CARD_MAX_SLOTS ||
|
||||
mem == NULL || mem->map >= MEM_MAX_MAPS ||
|
||||
mem->res->start < CARD_MEM_START || mem->res->start > CARD_MEM_END ||
|
||||
mem->res->end < CARD_MEM_START || mem->res->end > CARD_MEM_END ||
|
||||
mem->res->start > mem->res->end ||
|
||||
mem->card_start > CARD_MAX_MEM_OFFSET ||
|
||||
mem->speed > CARD_MAX_MEM_SPEED)
|
||||
return -EINVAL;
|
||||
|
||||
slot = sock->sock;
|
||||
map = mem->map;
|
||||
|
||||
addrwin = exca_read_byte(slot, I365_ADDRWIN);
|
||||
if (addrwin & I365_ENA_MEM(map)) {
|
||||
addrwin &= ~I365_ENA_MEM(map);
|
||||
exca_write_byte(slot, I365_ADDRWIN, addrwin);
|
||||
}
|
||||
|
||||
start = (mem->res->start >> 12) & 0x3fff;
|
||||
if (mem->flags & MAP_16BIT)
|
||||
start |= I365_MEM_16BIT;
|
||||
exca_write_word(slot, I365_MEM(map)+I365_W_START, start);
|
||||
|
||||
stop = (mem->res->end >> 12) & 0x3fff;
|
||||
switch (mem->speed) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
stop |= I365_MEM_WS0;
|
||||
break;
|
||||
case 2:
|
||||
stop |= I365_MEM_WS1;
|
||||
break;
|
||||
default:
|
||||
stop |= I365_MEM_WS0 | I365_MEM_WS1;
|
||||
break;
|
||||
}
|
||||
exca_write_word(slot, I365_MEM(map)+I365_W_STOP, stop);
|
||||
|
||||
offset = (mem->card_start >> 12) & 0x3fff;
|
||||
if (mem->flags & MAP_ATTRIB)
|
||||
offset |= I365_MEM_REG;
|
||||
if (mem->flags & MAP_WRPROT)
|
||||
offset |= I365_MEM_WRPROT;
|
||||
exca_write_word(slot, I365_MEM(map)+I365_W_OFF, offset);
|
||||
|
||||
if (mem->flags & MAP_ACTIVE) {
|
||||
addrwin |= I365_ENA_MEM(map);
|
||||
exca_write_byte(slot, I365_ADDRWIN, addrwin);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct pccard_operations vrc4171_pccard_operations = {
|
||||
.init = pccard_init,
|
||||
.get_status = pccard_get_status,
|
||||
.set_socket = pccard_set_socket,
|
||||
.set_io_map = pccard_set_io_map,
|
||||
.set_mem_map = pccard_set_mem_map,
|
||||
};
|
||||
|
||||
static inline unsigned int get_events(int slot)
|
||||
{
|
||||
unsigned int events = 0;
|
||||
uint8_t status, csc;
|
||||
|
||||
status = exca_read_byte(slot, I365_STATUS);
|
||||
csc = exca_read_byte(slot, I365_CSC);
|
||||
|
||||
if (exca_read_byte(slot, I365_INTCTL) & I365_PC_IOCARD) {
|
||||
if ((csc & I365_CSC_STSCHG) && (status & I365_CS_STSCHG))
|
||||
events |= SS_STSCHG;
|
||||
} else {
|
||||
if (csc & (I365_CSC_BVD1 | I365_CSC_BVD2)) {
|
||||
if (!(status & I365_CS_BVD1))
|
||||
events |= SS_BATDEAD;
|
||||
else if ((status & (I365_CS_BVD1 | I365_CS_BVD2)) == I365_CS_BVD1)
|
||||
events |= SS_BATWARN;
|
||||
}
|
||||
}
|
||||
if ((csc & I365_CSC_READY) && (status & I365_CS_READY))
|
||||
events |= SS_READY;
|
||||
if ((csc & I365_CSC_DETECT) && ((status & I365_CS_DETECT) == I365_CS_DETECT))
|
||||
events |= SS_DETECT;
|
||||
|
||||
return events;
|
||||
}
|
||||
|
||||
static irqreturn_t pccard_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct vrc4171_socket *socket;
|
||||
unsigned int events;
|
||||
irqreturn_t retval = IRQ_NONE;
|
||||
uint16_t status;
|
||||
|
||||
status = vrc4171_get_irq_status();
|
||||
if (status & IRQ_A) {
|
||||
socket = &vrc4171_sockets[CARD_SLOTA];
|
||||
if (socket->slot == SLOT_INITIALIZED) {
|
||||
if (status & (1 << socket->csc_irq)) {
|
||||
events = get_events(CARD_SLOTA);
|
||||
if (events != 0) {
|
||||
pcmcia_parse_events(&socket->pcmcia_socket, events);
|
||||
retval = IRQ_HANDLED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (status & IRQ_B) {
|
||||
socket = &vrc4171_sockets[CARD_SLOTB];
|
||||
if (socket->slot == SLOT_INITIALIZED) {
|
||||
if (status & (1 << socket->csc_irq)) {
|
||||
events = get_events(CARD_SLOTB);
|
||||
if (events != 0) {
|
||||
pcmcia_parse_events(&socket->pcmcia_socket, events);
|
||||
retval = IRQ_HANDLED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static inline void reserve_using_irq(int slot)
|
||||
{
|
||||
unsigned int irq;
|
||||
|
||||
irq = exca_read_byte(slot, I365_INTCTL);
|
||||
irq &= 0x0f;
|
||||
vrc4171_irq_mask &= ~(1 << irq);
|
||||
|
||||
irq = exca_read_byte(slot, I365_CSCINT);
|
||||
irq = (irq & 0xf0) >> 4;
|
||||
vrc4171_irq_mask &= ~(1 << irq);
|
||||
}
|
||||
|
||||
static int vrc4171_add_sockets(void)
|
||||
{
|
||||
struct vrc4171_socket *socket;
|
||||
int slot, retval;
|
||||
|
||||
for (slot = 0; slot < CARD_MAX_SLOTS; slot++) {
|
||||
if (slot == CARD_SLOTB && vrc4171_slotb == SLOTB_IS_NONE)
|
||||
continue;
|
||||
|
||||
socket = &vrc4171_sockets[slot];
|
||||
if (socket->slot != SLOT_PROBE) {
|
||||
uint8_t addrwin;
|
||||
|
||||
switch (socket->slot) {
|
||||
case SLOT_NOPROBE_MEM:
|
||||
addrwin = exca_read_byte(slot, I365_ADDRWIN);
|
||||
addrwin &= 0x1f;
|
||||
exca_write_byte(slot, I365_ADDRWIN, addrwin);
|
||||
break;
|
||||
case SLOT_NOPROBE_IO:
|
||||
addrwin = exca_read_byte(slot, I365_ADDRWIN);
|
||||
addrwin &= 0xc0;
|
||||
exca_write_byte(slot, I365_ADDRWIN, addrwin);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
reserve_using_irq(slot);
|
||||
continue;
|
||||
}
|
||||
|
||||
sprintf(socket->name, "NEC VRC4171 Card Slot %1c", 'A' + slot);
|
||||
socket->pcmcia_socket.dev.parent = &vrc4171_card_device.dev;
|
||||
socket->pcmcia_socket.ops = &vrc4171_pccard_operations;
|
||||
socket->pcmcia_socket.owner = THIS_MODULE;
|
||||
|
||||
retval = pcmcia_register_socket(&socket->pcmcia_socket);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
exca_write_byte(slot, I365_ADDRWIN, 0);
|
||||
exca_write_byte(slot, GLOBAL_CONTROL, 0);
|
||||
|
||||
socket->slot = SLOT_INITIALIZED;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vrc4171_remove_sockets(void)
|
||||
{
|
||||
struct vrc4171_socket *socket;
|
||||
int slot;
|
||||
|
||||
for (slot = 0; slot < CARD_MAX_SLOTS; slot++) {
|
||||
if (slot == CARD_SLOTB && vrc4171_slotb == SLOTB_IS_NONE)
|
||||
continue;
|
||||
|
||||
socket = &vrc4171_sockets[slot];
|
||||
if (socket->slot == SLOT_INITIALIZED)
|
||||
pcmcia_unregister_socket(&socket->pcmcia_socket);
|
||||
|
||||
socket->slot = SLOT_PROBE;
|
||||
}
|
||||
}
|
||||
|
||||
static int vrc4171_card_setup(char *options)
|
||||
{
|
||||
if (options == NULL || *options == '\0')
|
||||
return 1;
|
||||
|
||||
if (strncmp(options, "irq:", 4) == 0) {
|
||||
int irq;
|
||||
options += 4;
|
||||
irq = simple_strtoul(options, &options, 0);
|
||||
if (irq >= 0 && irq < nr_irqs)
|
||||
vrc4171_irq = irq;
|
||||
|
||||
if (*options != ',')
|
||||
return 1;
|
||||
options++;
|
||||
}
|
||||
|
||||
if (strncmp(options, "slota:", 6) == 0) {
|
||||
options += 6;
|
||||
if (*options != '\0') {
|
||||
if (strncmp(options, "memnoprobe", 10) == 0) {
|
||||
vrc4171_sockets[CARD_SLOTA].slot = SLOT_NOPROBE_MEM;
|
||||
options += 10;
|
||||
} else if (strncmp(options, "ionoprobe", 9) == 0) {
|
||||
vrc4171_sockets[CARD_SLOTA].slot = SLOT_NOPROBE_IO;
|
||||
options += 9;
|
||||
} else if ( strncmp(options, "noprobe", 7) == 0) {
|
||||
vrc4171_sockets[CARD_SLOTA].slot = SLOT_NOPROBE_ALL;
|
||||
options += 7;
|
||||
}
|
||||
|
||||
if (*options != ',')
|
||||
return 1;
|
||||
options++;
|
||||
} else
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
if (strncmp(options, "slotb:", 6) == 0) {
|
||||
options += 6;
|
||||
if (*options != '\0') {
|
||||
if (strncmp(options, "pccard", 6) == 0) {
|
||||
vrc4171_slotb = SLOTB_IS_PCCARD;
|
||||
options += 6;
|
||||
} else if (strncmp(options, "cf", 2) == 0) {
|
||||
vrc4171_slotb = SLOTB_IS_CF;
|
||||
options += 2;
|
||||
} else if (strncmp(options, "flashrom", 8) == 0) {
|
||||
vrc4171_slotb = SLOTB_IS_FLASHROM;
|
||||
options += 8;
|
||||
} else if (strncmp(options, "none", 4) == 0) {
|
||||
vrc4171_slotb = SLOTB_IS_NONE;
|
||||
options += 4;
|
||||
}
|
||||
|
||||
if (*options != ',')
|
||||
return 1;
|
||||
options++;
|
||||
|
||||
if (strncmp(options, "memnoprobe", 10) == 0)
|
||||
vrc4171_sockets[CARD_SLOTB].slot = SLOT_NOPROBE_MEM;
|
||||
if (strncmp(options, "ionoprobe", 9) == 0)
|
||||
vrc4171_sockets[CARD_SLOTB].slot = SLOT_NOPROBE_IO;
|
||||
if (strncmp(options, "noprobe", 7) == 0)
|
||||
vrc4171_sockets[CARD_SLOTB].slot = SLOT_NOPROBE_ALL;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
__setup("vrc4171_card=", vrc4171_card_setup);
|
||||
|
||||
static struct platform_driver vrc4171_card_driver = {
|
||||
.driver = {
|
||||
.name = vrc4171_card_name,
|
||||
},
|
||||
};
|
||||
|
||||
static int vrc4171_card_init(void)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = platform_driver_register(&vrc4171_card_driver);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
retval = platform_device_register(&vrc4171_card_device);
|
||||
if (retval < 0) {
|
||||
platform_driver_unregister(&vrc4171_card_driver);
|
||||
return retval;
|
||||
}
|
||||
|
||||
vrc4171_set_multifunction_pin(vrc4171_slotb);
|
||||
|
||||
retval = vrc4171_add_sockets();
|
||||
if (retval == 0)
|
||||
retval = request_irq(vrc4171_irq, pccard_interrupt, IRQF_SHARED,
|
||||
vrc4171_card_name, vrc4171_sockets);
|
||||
|
||||
if (retval < 0) {
|
||||
vrc4171_remove_sockets();
|
||||
platform_device_unregister(&vrc4171_card_device);
|
||||
platform_driver_unregister(&vrc4171_card_driver);
|
||||
return retval;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "%s, connected to IRQ %d\n",
|
||||
vrc4171_card_driver.driver.name, vrc4171_irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vrc4171_card_exit(void)
|
||||
{
|
||||
free_irq(vrc4171_irq, vrc4171_sockets);
|
||||
vrc4171_remove_sockets();
|
||||
platform_device_unregister(&vrc4171_card_device);
|
||||
platform_driver_unregister(&vrc4171_card_driver);
|
||||
}
|
||||
|
||||
module_init(vrc4171_card_init);
|
||||
module_exit(vrc4171_card_exit);
|
||||
|
|
@ -591,6 +591,15 @@ config SPI_MICROCHIP_CORE
|
|||
PolarFire SoC.
|
||||
If built as a module, it will be called spi-microchip-core.
|
||||
|
||||
config SPI_MICROCHIP_CORE_QSPI
|
||||
tristate "Microchip FPGA QSPI controllers"
|
||||
depends on SPI_MASTER
|
||||
help
|
||||
This enables the QSPI driver for Microchip FPGA QSPI controllers.
|
||||
Say Y or M here if you want to use the QSPI controllers on
|
||||
PolarFire SoC.
|
||||
If built as a module, it will be called spi-microchip-core-qspi.
|
||||
|
||||
config SPI_MT65XX
|
||||
tristate "MediaTek SPI controller"
|
||||
depends on ARCH_MEDIATEK || COMPILE_TEST
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ obj-$(CONFIG_SPI_LP8841_RTC) += spi-lp8841-rtc.o
|
|||
obj-$(CONFIG_SPI_MESON_SPICC) += spi-meson-spicc.o
|
||||
obj-$(CONFIG_SPI_MESON_SPIFC) += spi-meson-spifc.o
|
||||
obj-$(CONFIG_SPI_MICROCHIP_CORE) += spi-microchip-core.o
|
||||
obj-$(CONFIG_SPI_MICROCHIP_CORE_QSPI) += spi-microchip-core-qspi.o
|
||||
obj-$(CONFIG_SPI_MPC512x_PSC) += spi-mpc512x-psc.o
|
||||
obj-$(CONFIG_SPI_MPC52xx_PSC) += spi-mpc52xx-psc.o
|
||||
obj-$(CONFIG_SPI_MPC52xx) += spi-mpc52xx.o
|
||||
|
|
|
|||
|
|
@ -36,9 +36,17 @@
|
|||
#define AMD_SPI_FIFO_SIZE 70
|
||||
#define AMD_SPI_MEM_SIZE 200
|
||||
|
||||
/* M_CMD OP codes for SPI */
|
||||
#define AMD_SPI_XFER_TX 1
|
||||
#define AMD_SPI_XFER_RX 2
|
||||
#define AMD_SPI_ENA_REG 0x20
|
||||
#define AMD_SPI_ALT_SPD_SHIFT 20
|
||||
#define AMD_SPI_ALT_SPD_MASK GENMASK(23, AMD_SPI_ALT_SPD_SHIFT)
|
||||
#define AMD_SPI_SPI100_SHIFT 0
|
||||
#define AMD_SPI_SPI100_MASK GENMASK(AMD_SPI_SPI100_SHIFT, AMD_SPI_SPI100_SHIFT)
|
||||
#define AMD_SPI_SPEED_REG 0x6C
|
||||
#define AMD_SPI_SPD7_SHIFT 8
|
||||
#define AMD_SPI_SPD7_MASK GENMASK(13, AMD_SPI_SPD7_SHIFT)
|
||||
|
||||
#define AMD_SPI_MAX_HZ 100000000
|
||||
#define AMD_SPI_MIN_HZ 800000
|
||||
|
||||
/**
|
||||
* enum amd_spi_versions - SPI controller versions
|
||||
|
|
@ -50,14 +58,41 @@ enum amd_spi_versions {
|
|||
AMD_SPI_V2,
|
||||
};
|
||||
|
||||
enum amd_spi_speed {
|
||||
F_66_66MHz,
|
||||
F_33_33MHz,
|
||||
F_22_22MHz,
|
||||
F_16_66MHz,
|
||||
F_100MHz,
|
||||
F_800KHz,
|
||||
SPI_SPD7,
|
||||
F_50MHz = 0x4,
|
||||
F_4MHz = 0x32,
|
||||
F_3_17MHz = 0x3F
|
||||
};
|
||||
|
||||
/**
|
||||
* struct amd_spi_freq - Matches device speed with values to write in regs
|
||||
* @speed_hz: Device frequency
|
||||
* @enable_val: Value to be written to "enable register"
|
||||
* @spd7_val: Some frequencies requires to have a value written at SPISPEED register
|
||||
*/
|
||||
struct amd_spi_freq {
|
||||
u32 speed_hz;
|
||||
u32 enable_val;
|
||||
u32 spd7_val;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct amd_spi - SPI driver instance
|
||||
* @io_remap_addr: Start address of the SPI controller registers
|
||||
* @version: SPI controller hardware version
|
||||
* @speed_hz: Device frequency
|
||||
*/
|
||||
struct amd_spi {
|
||||
void __iomem *io_remap_addr;
|
||||
enum amd_spi_versions version;
|
||||
unsigned int speed_hz;
|
||||
};
|
||||
|
||||
static inline u8 amd_spi_readreg8(struct amd_spi *amd_spi, int idx)
|
||||
|
|
@ -189,65 +224,125 @@ static int amd_spi_master_setup(struct spi_device *spi)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct amd_spi_freq amd_spi_freq[] = {
|
||||
{ AMD_SPI_MAX_HZ, F_100MHz, 0},
|
||||
{ 66660000, F_66_66MHz, 0},
|
||||
{ 50000000, SPI_SPD7, F_50MHz},
|
||||
{ 33330000, F_33_33MHz, 0},
|
||||
{ 22220000, F_22_22MHz, 0},
|
||||
{ 16660000, F_16_66MHz, 0},
|
||||
{ 4000000, SPI_SPD7, F_4MHz},
|
||||
{ 3170000, SPI_SPD7, F_3_17MHz},
|
||||
{ AMD_SPI_MIN_HZ, F_800KHz, 0},
|
||||
};
|
||||
|
||||
static int amd_set_spi_freq(struct amd_spi *amd_spi, u32 speed_hz)
|
||||
{
|
||||
unsigned int i, spd7_val, alt_spd;
|
||||
|
||||
if (speed_hz < AMD_SPI_MIN_HZ)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(amd_spi_freq); i++)
|
||||
if (speed_hz >= amd_spi_freq[i].speed_hz)
|
||||
break;
|
||||
|
||||
if (amd_spi->speed_hz == amd_spi_freq[i].speed_hz)
|
||||
return 0;
|
||||
|
||||
amd_spi->speed_hz = amd_spi_freq[i].speed_hz;
|
||||
|
||||
alt_spd = (amd_spi_freq[i].enable_val << AMD_SPI_ALT_SPD_SHIFT)
|
||||
& AMD_SPI_ALT_SPD_MASK;
|
||||
amd_spi_setclear_reg32(amd_spi, AMD_SPI_ENA_REG, alt_spd,
|
||||
AMD_SPI_ALT_SPD_MASK);
|
||||
|
||||
if (amd_spi->speed_hz == AMD_SPI_MAX_HZ)
|
||||
amd_spi_setclear_reg32(amd_spi, AMD_SPI_ENA_REG, 1,
|
||||
AMD_SPI_SPI100_MASK);
|
||||
|
||||
if (amd_spi_freq[i].spd7_val) {
|
||||
spd7_val = (amd_spi_freq[i].spd7_val << AMD_SPI_SPD7_SHIFT)
|
||||
& AMD_SPI_SPD7_MASK;
|
||||
amd_spi_setclear_reg32(amd_spi, AMD_SPI_SPEED_REG, spd7_val,
|
||||
AMD_SPI_SPD7_MASK);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int amd_spi_fifo_xfer(struct amd_spi *amd_spi,
|
||||
struct spi_master *master,
|
||||
struct spi_message *message)
|
||||
{
|
||||
struct spi_transfer *xfer = NULL;
|
||||
u8 cmd_opcode;
|
||||
struct spi_device *spi = message->spi;
|
||||
u8 cmd_opcode = 0, fifo_pos = AMD_SPI_FIFO_BASE;
|
||||
u8 *buf = NULL;
|
||||
u32 m_cmd = 0;
|
||||
u32 i = 0;
|
||||
u32 tx_len = 0, rx_len = 0;
|
||||
|
||||
list_for_each_entry(xfer, &message->transfers,
|
||||
transfer_list) {
|
||||
if (xfer->rx_buf)
|
||||
m_cmd = AMD_SPI_XFER_RX;
|
||||
if (xfer->tx_buf)
|
||||
m_cmd = AMD_SPI_XFER_TX;
|
||||
if (xfer->speed_hz)
|
||||
amd_set_spi_freq(amd_spi, xfer->speed_hz);
|
||||
else
|
||||
amd_set_spi_freq(amd_spi, spi->max_speed_hz);
|
||||
|
||||
if (m_cmd & AMD_SPI_XFER_TX) {
|
||||
if (xfer->tx_buf) {
|
||||
buf = (u8 *)xfer->tx_buf;
|
||||
tx_len = xfer->len - 1;
|
||||
cmd_opcode = *(u8 *)xfer->tx_buf;
|
||||
buf++;
|
||||
amd_spi_set_opcode(amd_spi, cmd_opcode);
|
||||
if (!tx_len) {
|
||||
cmd_opcode = *(u8 *)xfer->tx_buf;
|
||||
buf++;
|
||||
xfer->len--;
|
||||
}
|
||||
tx_len += xfer->len;
|
||||
|
||||
/* Write data into the FIFO. */
|
||||
for (i = 0; i < tx_len; i++) {
|
||||
iowrite8(buf[i], ((u8 __iomem *)amd_spi->io_remap_addr +
|
||||
AMD_SPI_FIFO_BASE + i));
|
||||
}
|
||||
for (i = 0; i < xfer->len; i++)
|
||||
amd_spi_writereg8(amd_spi, fifo_pos + i, buf[i]);
|
||||
|
||||
amd_spi_set_tx_count(amd_spi, tx_len);
|
||||
amd_spi_clear_fifo_ptr(amd_spi);
|
||||
/* Execute command */
|
||||
amd_spi_execute_opcode(amd_spi);
|
||||
}
|
||||
if (m_cmd & AMD_SPI_XFER_RX) {
|
||||
/*
|
||||
* Store no. of bytes to be received from
|
||||
* FIFO
|
||||
*/
|
||||
rx_len = xfer->len;
|
||||
buf = (u8 *)xfer->rx_buf;
|
||||
amd_spi_set_rx_count(amd_spi, rx_len);
|
||||
amd_spi_clear_fifo_ptr(amd_spi);
|
||||
/* Execute command */
|
||||
amd_spi_execute_opcode(amd_spi);
|
||||
amd_spi_busy_wait(amd_spi);
|
||||
/* Read data from FIFO to receive buffer */
|
||||
for (i = 0; i < rx_len; i++)
|
||||
buf[i] = amd_spi_readreg8(amd_spi, AMD_SPI_FIFO_BASE + tx_len + i);
|
||||
fifo_pos += xfer->len;
|
||||
}
|
||||
|
||||
/* Store no. of bytes to be received from FIFO */
|
||||
if (xfer->rx_buf)
|
||||
rx_len += xfer->len;
|
||||
}
|
||||
|
||||
if (!buf) {
|
||||
message->status = -EINVAL;
|
||||
goto fin_msg;
|
||||
}
|
||||
|
||||
amd_spi_set_opcode(amd_spi, cmd_opcode);
|
||||
amd_spi_set_tx_count(amd_spi, tx_len);
|
||||
amd_spi_set_rx_count(amd_spi, rx_len);
|
||||
|
||||
/* Execute command */
|
||||
message->status = amd_spi_execute_opcode(amd_spi);
|
||||
if (message->status)
|
||||
goto fin_msg;
|
||||
|
||||
if (rx_len) {
|
||||
message->status = amd_spi_busy_wait(amd_spi);
|
||||
if (message->status)
|
||||
goto fin_msg;
|
||||
|
||||
list_for_each_entry(xfer, &message->transfers, transfer_list)
|
||||
if (xfer->rx_buf) {
|
||||
buf = (u8 *)xfer->rx_buf;
|
||||
/* Read data from FIFO to receive buffer */
|
||||
for (i = 0; i < xfer->len; i++)
|
||||
buf[i] = amd_spi_readreg8(amd_spi, fifo_pos + i);
|
||||
fifo_pos += xfer->len;
|
||||
}
|
||||
}
|
||||
|
||||
/* Update statistics */
|
||||
message->actual_length = tx_len + rx_len + 1;
|
||||
/* complete the transaction */
|
||||
message->status = 0;
|
||||
|
||||
fin_msg:
|
||||
switch (amd_spi->version) {
|
||||
case AMD_SPI_V1:
|
||||
break;
|
||||
|
|
@ -260,7 +355,7 @@ static inline int amd_spi_fifo_xfer(struct amd_spi *amd_spi,
|
|||
|
||||
spi_finalize_current_message(master);
|
||||
|
||||
return 0;
|
||||
return message->status;
|
||||
}
|
||||
|
||||
static int amd_spi_master_transfer(struct spi_master *master,
|
||||
|
|
@ -275,9 +370,7 @@ static int amd_spi_master_transfer(struct spi_master *master,
|
|||
* Extract spi_transfers from the spi message and
|
||||
* program the controller.
|
||||
*/
|
||||
amd_spi_fifo_xfer(amd_spi, master, msg);
|
||||
|
||||
return 0;
|
||||
return amd_spi_fifo_xfer(amd_spi, master, msg);
|
||||
}
|
||||
|
||||
static size_t amd_spi_max_transfer_size(struct spi_device *spi)
|
||||
|
|
@ -312,6 +405,8 @@ static int amd_spi_probe(struct platform_device *pdev)
|
|||
master->num_chipselect = 4;
|
||||
master->mode_bits = 0;
|
||||
master->flags = SPI_MASTER_HALF_DUPLEX;
|
||||
master->max_speed_hz = AMD_SPI_MAX_HZ;
|
||||
master->min_speed_hz = AMD_SPI_MIN_HZ;
|
||||
master->setup = amd_spi_master_setup;
|
||||
master->transfer_one_message = amd_spi_master_transfer;
|
||||
master->max_transfer_size = amd_spi_max_transfer_size;
|
||||
|
|
|
|||
|
|
@ -736,10 +736,8 @@ static int aspeed_spi_probe(struct platform_device *pdev)
|
|||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
aspi->regs = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(aspi->regs)) {
|
||||
dev_err(dev, "missing AHB register window\n");
|
||||
if (IS_ERR(aspi->regs))
|
||||
return PTR_ERR(aspi->regs);
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
aspi->ahb_base = devm_ioremap_resource(dev, res);
|
||||
|
|
|
|||
|
|
@ -1645,7 +1645,7 @@ static int cqspi_probe(struct platform_device *pdev)
|
|||
pm_runtime_enable(dev);
|
||||
ret = pm_runtime_resume_and_get(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto probe_pm_failed;
|
||||
|
||||
ret = clk_prepare_enable(cqspi->clk);
|
||||
if (ret) {
|
||||
|
|
@ -1740,6 +1740,7 @@ static int cqspi_probe(struct platform_device *pdev)
|
|||
clk_disable_unprepare(cqspi->clk);
|
||||
probe_clk_failed:
|
||||
pm_runtime_put_sync(dev);
|
||||
probe_pm_failed:
|
||||
pm_runtime_disable(dev);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -565,10 +565,8 @@ static int cdns_xspi_probe(struct platform_device *pdev)
|
|||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sdma");
|
||||
cdns_xspi->sdmabase = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(cdns_xspi->sdmabase)) {
|
||||
dev_err(dev, "Failed to remap SDMA address\n");
|
||||
if (IS_ERR(cdns_xspi->sdmabase))
|
||||
return PTR_ERR(cdns_xspi->sdmabase);
|
||||
}
|
||||
cdns_xspi->sdmasize = resource_size(res);
|
||||
|
||||
cdns_xspi->auxbase = devm_platform_ioremap_resource_byname(pdev, "aux");
|
||||
|
|
|
|||
|
|
@ -293,8 +293,10 @@ static int dw_spi_bt1_probe(struct platform_device *pdev)
|
|||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
ret = dw_spi_add_host(&pdev->dev, dws);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
goto err_disable_clk;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, dwsbt1);
|
||||
|
||||
|
|
|
|||
|
|
@ -955,7 +955,7 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
|
|||
|
||||
ret = spi_register_controller(master);
|
||||
if (ret) {
|
||||
dev_err(&master->dev, "problem registering spi master\n");
|
||||
dev_err_probe(dev, ret, "problem registering spi master\n");
|
||||
goto err_dma_exit;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1294,8 +1294,7 @@ static int dspi_probe(struct platform_device *pdev)
|
|||
else
|
||||
ctlr->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(&pdev->dev, res);
|
||||
base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
|
||||
if (IS_ERR(base)) {
|
||||
ret = PTR_ERR(base);
|
||||
goto out_ctlr_put;
|
||||
|
|
|
|||
|
|
@ -855,8 +855,7 @@ static int fsl_lpspi_probe(struct platform_device *pdev)
|
|||
|
||||
init_completion(&fsl_lpspi->xfer_done);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
fsl_lpspi->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
fsl_lpspi->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
|
||||
if (IS_ERR(fsl_lpspi->base)) {
|
||||
ret = PTR_ERR(fsl_lpspi->base);
|
||||
goto out_controller_put;
|
||||
|
|
@ -912,7 +911,7 @@ static int fsl_lpspi_probe(struct platform_device *pdev)
|
|||
|
||||
ret = devm_spi_register_controller(&pdev->dev, controller);
|
||||
if (ret < 0) {
|
||||
dev_err_probe(&pdev->dev, ret, "spi_register_controller error: %i\n", ret);
|
||||
dev_err_probe(&pdev->dev, ret, "spi_register_controller error\n");
|
||||
goto free_dma;
|
||||
}
|
||||
|
||||
|
|
@ -947,11 +946,8 @@ static int fsl_lpspi_remove(struct platform_device *pdev)
|
|||
|
||||
static int __maybe_unused fsl_lpspi_suspend(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
pinctrl_pm_select_sleep_state(dev);
|
||||
ret = pm_runtime_force_suspend(dev);
|
||||
return ret;
|
||||
return pm_runtime_force_suspend(dev);
|
||||
}
|
||||
|
||||
static int __maybe_unused fsl_lpspi_resume(struct device *dev)
|
||||
|
|
|
|||
|
|
@ -867,8 +867,7 @@ static int fsl_qspi_probe(struct platform_device *pdev)
|
|||
platform_set_drvdata(pdev, q);
|
||||
|
||||
/* find the resources */
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "QuadSPI");
|
||||
q->iobase = devm_ioremap_resource(dev, res);
|
||||
q->iobase = devm_platform_ioremap_resource_byname(pdev, "QuadSPI");
|
||||
if (IS_ERR(q->iobase)) {
|
||||
ret = PTR_ERR(q->iobase);
|
||||
goto err_put_ctrl;
|
||||
|
|
|
|||
|
|
@ -111,32 +111,6 @@ static void fsl_spi_change_mode(struct spi_device *spi)
|
|||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
static void fsl_spi_chipselect(struct spi_device *spi, int value)
|
||||
{
|
||||
struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
|
||||
struct fsl_spi_platform_data *pdata;
|
||||
struct spi_mpc8xxx_cs *cs = spi->controller_state;
|
||||
|
||||
pdata = spi->dev.parent->parent->platform_data;
|
||||
|
||||
if (value == BITBANG_CS_INACTIVE) {
|
||||
if (pdata->cs_control)
|
||||
pdata->cs_control(spi, false);
|
||||
}
|
||||
|
||||
if (value == BITBANG_CS_ACTIVE) {
|
||||
mpc8xxx_spi->rx_shift = cs->rx_shift;
|
||||
mpc8xxx_spi->tx_shift = cs->tx_shift;
|
||||
mpc8xxx_spi->get_rx = cs->get_rx;
|
||||
mpc8xxx_spi->get_tx = cs->get_tx;
|
||||
|
||||
fsl_spi_change_mode(spi);
|
||||
|
||||
if (pdata->cs_control)
|
||||
pdata->cs_control(spi, true);
|
||||
}
|
||||
}
|
||||
|
||||
static void fsl_spi_qe_cpu_set_shifts(u32 *rx_shift, u32 *tx_shift,
|
||||
int bits_per_word, int msb_first)
|
||||
{
|
||||
|
|
@ -354,15 +328,11 @@ static int fsl_spi_bufs(struct spi_device *spi, struct spi_transfer *t,
|
|||
return mpc8xxx_spi->count;
|
||||
}
|
||||
|
||||
static int fsl_spi_do_one_msg(struct spi_master *master,
|
||||
struct spi_message *m)
|
||||
static int fsl_spi_prepare_message(struct spi_controller *ctlr,
|
||||
struct spi_message *m)
|
||||
{
|
||||
struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(master);
|
||||
struct spi_device *spi = m->spi;
|
||||
struct spi_transfer *t, *first;
|
||||
unsigned int cs_change;
|
||||
const int nsecs = 50;
|
||||
int status, last_bpw;
|
||||
struct mpc8xxx_spi *mpc8xxx_spi = spi_controller_get_devdata(ctlr);
|
||||
struct spi_transfer *t;
|
||||
|
||||
/*
|
||||
* In CPU mode, optimize large byte transfers to use larger
|
||||
|
|
@ -378,64 +348,32 @@ static int fsl_spi_do_one_msg(struct spi_master *master,
|
|||
t->bits_per_word = 16;
|
||||
}
|
||||
}
|
||||
|
||||
/* Don't allow changes if CS is active */
|
||||
cs_change = 1;
|
||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||
if (cs_change)
|
||||
first = t;
|
||||
cs_change = t->cs_change;
|
||||
if (first->speed_hz != t->speed_hz) {
|
||||
dev_err(&spi->dev,
|
||||
"speed_hz cannot change while CS is active\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
last_bpw = -1;
|
||||
cs_change = 1;
|
||||
status = -EINVAL;
|
||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||
if (cs_change || last_bpw != t->bits_per_word)
|
||||
status = fsl_spi_setup_transfer(spi, t);
|
||||
if (status < 0)
|
||||
break;
|
||||
last_bpw = t->bits_per_word;
|
||||
|
||||
if (cs_change) {
|
||||
fsl_spi_chipselect(spi, BITBANG_CS_ACTIVE);
|
||||
ndelay(nsecs);
|
||||
}
|
||||
cs_change = t->cs_change;
|
||||
if (t->len)
|
||||
status = fsl_spi_bufs(spi, t, m->is_dma_mapped);
|
||||
if (status) {
|
||||
status = -EMSGSIZE;
|
||||
break;
|
||||
}
|
||||
m->actual_length += t->len;
|
||||
|
||||
spi_transfer_delay_exec(t);
|
||||
|
||||
if (cs_change) {
|
||||
ndelay(nsecs);
|
||||
fsl_spi_chipselect(spi, BITBANG_CS_INACTIVE);
|
||||
ndelay(nsecs);
|
||||
}
|
||||
}
|
||||
|
||||
m->status = status;
|
||||
|
||||
if (status || !cs_change) {
|
||||
ndelay(nsecs);
|
||||
fsl_spi_chipselect(spi, BITBANG_CS_INACTIVE);
|
||||
}
|
||||
|
||||
fsl_spi_setup_transfer(spi, NULL);
|
||||
spi_finalize_current_message(master);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_spi_transfer_one(struct spi_controller *controller,
|
||||
struct spi_device *spi,
|
||||
struct spi_transfer *t)
|
||||
{
|
||||
int status;
|
||||
|
||||
status = fsl_spi_setup_transfer(spi, t);
|
||||
if (status < 0)
|
||||
return status;
|
||||
if (t->len)
|
||||
status = fsl_spi_bufs(spi, t, !!t->tx_dma || !!t->rx_dma);
|
||||
if (status > 0)
|
||||
return -EMSGSIZE;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int fsl_spi_unprepare_message(struct spi_controller *controller,
|
||||
struct spi_message *msg)
|
||||
{
|
||||
return fsl_spi_setup_transfer(msg->spi, NULL);
|
||||
}
|
||||
|
||||
static int fsl_spi_setup(struct spi_device *spi)
|
||||
{
|
||||
struct mpc8xxx_spi *mpc8xxx_spi;
|
||||
|
|
@ -482,9 +420,6 @@ static int fsl_spi_setup(struct spi_device *spi)
|
|||
return retval;
|
||||
}
|
||||
|
||||
/* Initialize chipselect - might be active for SPI_CS_HIGH mode */
|
||||
fsl_spi_chipselect(spi, BITBANG_CS_INACTIVE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -557,9 +492,7 @@ static void fsl_spi_grlib_cs_control(struct spi_device *spi, bool on)
|
|||
u32 slvsel;
|
||||
u16 cs = spi->chip_select;
|
||||
|
||||
if (spi->cs_gpiod) {
|
||||
gpiod_set_value(spi->cs_gpiod, on);
|
||||
} else if (cs < mpc8xxx_spi->native_chipselects) {
|
||||
if (cs < mpc8xxx_spi->native_chipselects) {
|
||||
slvsel = mpc8xxx_spi_read_reg(®_base->slvsel);
|
||||
slvsel = on ? (slvsel | (1 << cs)) : (slvsel & ~(1 << cs));
|
||||
mpc8xxx_spi_write_reg(®_base->slvsel, slvsel);
|
||||
|
|
@ -568,7 +501,6 @@ static void fsl_spi_grlib_cs_control(struct spi_device *spi, bool on)
|
|||
|
||||
static void fsl_spi_grlib_probe(struct device *dev)
|
||||
{
|
||||
struct fsl_spi_platform_data *pdata = dev_get_platdata(dev);
|
||||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(master);
|
||||
struct fsl_spi_reg __iomem *reg_base = mpc8xxx_spi->reg_base;
|
||||
|
|
@ -588,7 +520,18 @@ static void fsl_spi_grlib_probe(struct device *dev)
|
|||
mpc8xxx_spi_write_reg(®_base->slvsel, 0xffffffff);
|
||||
}
|
||||
master->num_chipselect = mpc8xxx_spi->native_chipselects;
|
||||
pdata->cs_control = fsl_spi_grlib_cs_control;
|
||||
master->set_cs = fsl_spi_grlib_cs_control;
|
||||
}
|
||||
|
||||
static void fsl_spi_cs_control(struct spi_device *spi, bool on)
|
||||
{
|
||||
struct device *dev = spi->dev.parent->parent;
|
||||
struct fsl_spi_platform_data *pdata = dev_get_platdata(dev);
|
||||
struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(pdata);
|
||||
|
||||
if (WARN_ON_ONCE(!pinfo->immr_spi_cs))
|
||||
return;
|
||||
iowrite32be(on ? 0 : SPI_BOOT_SEL_BIT, pinfo->immr_spi_cs);
|
||||
}
|
||||
|
||||
static struct spi_master *fsl_spi_probe(struct device *dev,
|
||||
|
|
@ -613,8 +556,11 @@ static struct spi_master *fsl_spi_probe(struct device *dev,
|
|||
|
||||
master->setup = fsl_spi_setup;
|
||||
master->cleanup = fsl_spi_cleanup;
|
||||
master->transfer_one_message = fsl_spi_do_one_msg;
|
||||
master->prepare_message = fsl_spi_prepare_message;
|
||||
master->transfer_one = fsl_spi_transfer_one;
|
||||
master->unprepare_message = fsl_spi_unprepare_message;
|
||||
master->use_gpio_descriptors = true;
|
||||
master->set_cs = fsl_spi_cs_control;
|
||||
|
||||
mpc8xxx_spi = spi_master_get_devdata(master);
|
||||
mpc8xxx_spi->max_bits_per_word = 32;
|
||||
|
|
@ -688,21 +634,6 @@ static struct spi_master *fsl_spi_probe(struct device *dev,
|
|||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static void fsl_spi_cs_control(struct spi_device *spi, bool on)
|
||||
{
|
||||
if (spi->cs_gpiod) {
|
||||
gpiod_set_value(spi->cs_gpiod, on);
|
||||
} else {
|
||||
struct device *dev = spi->dev.parent->parent;
|
||||
struct fsl_spi_platform_data *pdata = dev_get_platdata(dev);
|
||||
struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(pdata);
|
||||
|
||||
if (WARN_ON_ONCE(!pinfo->immr_spi_cs))
|
||||
return;
|
||||
iowrite32be(on ? 0 : SPI_BOOT_SEL_BIT, pinfo->immr_spi_cs);
|
||||
}
|
||||
}
|
||||
|
||||
static int of_fsl_spi_probe(struct platform_device *ofdev)
|
||||
{
|
||||
struct device *dev = &ofdev->dev;
|
||||
|
|
@ -744,12 +675,10 @@ static int of_fsl_spi_probe(struct platform_device *ofdev)
|
|||
ret = gpiod_count(dev, "cs");
|
||||
if (ret < 0)
|
||||
ret = 0;
|
||||
if (ret == 0 && !spisel_boot) {
|
||||
if (ret == 0 && !spisel_boot)
|
||||
pdata->max_chipselect = 1;
|
||||
} else {
|
||||
else
|
||||
pdata->max_chipselect = ret + spisel_boot;
|
||||
pdata->cs_control = fsl_spi_cs_control;
|
||||
}
|
||||
}
|
||||
|
||||
ret = of_address_to_resource(np, 0, &mem);
|
||||
|
|
|
|||
|
|
@ -254,7 +254,6 @@ static int gxp_spifi_probe(struct platform_device *pdev)
|
|||
const struct gxp_spi_data *data;
|
||||
struct spi_controller *ctlr;
|
||||
struct gxp_spi *spifi;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
data = of_device_get_match_data(&pdev->dev);
|
||||
|
|
@ -269,18 +268,15 @@ static int gxp_spifi_probe(struct platform_device *pdev)
|
|||
spifi->data = data;
|
||||
spifi->dev = dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
spifi->reg_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
spifi->reg_base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(spifi->reg_base))
|
||||
return PTR_ERR(spifi->reg_base);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
spifi->dat_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
spifi->dat_base = devm_platform_ioremap_resource(pdev, 1);
|
||||
if (IS_ERR(spifi->dat_base))
|
||||
return PTR_ERR(spifi->dat_base);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
|
||||
spifi->dir_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
spifi->dir_base = devm_platform_ioremap_resource(pdev, 2);
|
||||
if (IS_ERR(spifi->dir_base))
|
||||
return PTR_ERR(spifi->dir_base);
|
||||
|
||||
|
|
|
|||
|
|
@ -730,11 +730,9 @@ static int img_spfi_resume(struct device *dev)
|
|||
struct img_spfi *spfi = spi_master_get_devdata(master);
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0) {
|
||||
pm_runtime_put_noidle(dev);
|
||||
ret = pm_runtime_resume_and_get(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
spfi_reset(spfi);
|
||||
pm_runtime_put(dev);
|
||||
|
||||
|
|
|
|||
|
|
@ -116,6 +116,22 @@
|
|||
#define ERASE_64K_OPCODE_SHIFT 16
|
||||
#define ERASE_64K_OPCODE_MASK (0xff << ERASE_OPCODE_SHIFT)
|
||||
|
||||
/* Flash descriptor fields */
|
||||
#define FLVALSIG_MAGIC 0x0ff0a55a
|
||||
#define FLMAP0_NC_MASK GENMASK(9, 8)
|
||||
#define FLMAP0_NC_SHIFT 8
|
||||
#define FLMAP0_FCBA_MASK GENMASK(7, 0)
|
||||
|
||||
#define FLCOMP_C0DEN_MASK GENMASK(3, 0)
|
||||
#define FLCOMP_C0DEN_512K 0x00
|
||||
#define FLCOMP_C0DEN_1M 0x01
|
||||
#define FLCOMP_C0DEN_2M 0x02
|
||||
#define FLCOMP_C0DEN_4M 0x03
|
||||
#define FLCOMP_C0DEN_8M 0x04
|
||||
#define FLCOMP_C0DEN_16M 0x05
|
||||
#define FLCOMP_C0DEN_32M 0x06
|
||||
#define FLCOMP_C0DEN_64M 0x07
|
||||
|
||||
#define INTEL_SPI_TIMEOUT 5000 /* ms */
|
||||
#define INTEL_SPI_FIFO_SZ 64
|
||||
|
||||
|
|
@ -129,6 +145,7 @@
|
|||
* @master: Pointer to the SPI controller structure
|
||||
* @nregions: Maximum number of regions
|
||||
* @pr_num: Maximum number of protected range registers
|
||||
* @chip0_size: Size of the first flash chip in bytes
|
||||
* @locked: Is SPI setting locked
|
||||
* @swseq_reg: Use SW sequencer in register reads/writes
|
||||
* @swseq_erase: Use SW sequencer in erase operation
|
||||
|
|
@ -146,6 +163,7 @@ struct intel_spi {
|
|||
struct spi_controller *master;
|
||||
size_t nregions;
|
||||
size_t pr_num;
|
||||
size_t chip0_size;
|
||||
bool locked;
|
||||
bool swseq_reg;
|
||||
bool swseq_erase;
|
||||
|
|
@ -158,6 +176,7 @@ struct intel_spi_mem_op {
|
|||
struct spi_mem_op mem_op;
|
||||
u32 replacement_op;
|
||||
int (*exec_op)(struct intel_spi *ispi,
|
||||
const struct spi_mem *mem,
|
||||
const struct intel_spi_mem_op *iop,
|
||||
const struct spi_mem_op *op);
|
||||
};
|
||||
|
|
@ -441,7 +460,16 @@ static int intel_spi_sw_cycle(struct intel_spi *ispi, u8 opcode, size_t len,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int intel_spi_read_reg(struct intel_spi *ispi,
|
||||
static u32 intel_spi_chip_addr(const struct intel_spi *ispi,
|
||||
const struct spi_mem *mem)
|
||||
{
|
||||
/* Pick up the correct start address */
|
||||
if (!mem)
|
||||
return 0;
|
||||
return mem->spi->chip_select == 1 ? ispi->chip0_size : 0;
|
||||
}
|
||||
|
||||
static int intel_spi_read_reg(struct intel_spi *ispi, const struct spi_mem *mem,
|
||||
const struct intel_spi_mem_op *iop,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
|
|
@ -449,8 +477,7 @@ static int intel_spi_read_reg(struct intel_spi *ispi,
|
|||
u8 opcode = op->cmd.opcode;
|
||||
int ret;
|
||||
|
||||
/* Address of the first chip */
|
||||
writel(0, ispi->base + FADDR);
|
||||
writel(intel_spi_chip_addr(ispi, mem), ispi->base + FADDR);
|
||||
|
||||
if (ispi->swseq_reg)
|
||||
ret = intel_spi_sw_cycle(ispi, opcode, nbytes,
|
||||
|
|
@ -464,7 +491,7 @@ static int intel_spi_read_reg(struct intel_spi *ispi,
|
|||
return intel_spi_read_block(ispi, op->data.buf.in, nbytes);
|
||||
}
|
||||
|
||||
static int intel_spi_write_reg(struct intel_spi *ispi,
|
||||
static int intel_spi_write_reg(struct intel_spi *ispi, const struct spi_mem *mem,
|
||||
const struct intel_spi_mem_op *iop,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
|
|
@ -511,7 +538,7 @@ static int intel_spi_write_reg(struct intel_spi *ispi,
|
|||
if (opcode == SPINOR_OP_WRDI)
|
||||
return 0;
|
||||
|
||||
writel(0, ispi->base + FADDR);
|
||||
writel(intel_spi_chip_addr(ispi, mem), ispi->base + FADDR);
|
||||
|
||||
/* Write the value beforehand */
|
||||
ret = intel_spi_write_block(ispi, op->data.buf.out, nbytes);
|
||||
|
|
@ -524,13 +551,13 @@ static int intel_spi_write_reg(struct intel_spi *ispi,
|
|||
return intel_spi_hw_cycle(ispi, opcode, nbytes);
|
||||
}
|
||||
|
||||
static int intel_spi_read(struct intel_spi *ispi,
|
||||
static int intel_spi_read(struct intel_spi *ispi, const struct spi_mem *mem,
|
||||
const struct intel_spi_mem_op *iop,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
void *read_buf = op->data.buf.in;
|
||||
u32 addr = intel_spi_chip_addr(ispi, mem) + op->addr.val;
|
||||
size_t block_size, nbytes = op->data.nbytes;
|
||||
u32 addr = op->addr.val;
|
||||
void *read_buf = op->data.buf.in;
|
||||
u32 val, status;
|
||||
int ret;
|
||||
|
||||
|
|
@ -585,13 +612,13 @@ static int intel_spi_read(struct intel_spi *ispi,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int intel_spi_write(struct intel_spi *ispi,
|
||||
static int intel_spi_write(struct intel_spi *ispi, const struct spi_mem *mem,
|
||||
const struct intel_spi_mem_op *iop,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
u32 addr = intel_spi_chip_addr(ispi, mem) + op->addr.val;
|
||||
size_t block_size, nbytes = op->data.nbytes;
|
||||
const void *write_buf = op->data.buf.out;
|
||||
u32 addr = op->addr.val;
|
||||
u32 val, status;
|
||||
int ret;
|
||||
|
||||
|
|
@ -648,12 +675,12 @@ static int intel_spi_write(struct intel_spi *ispi,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int intel_spi_erase(struct intel_spi *ispi,
|
||||
static int intel_spi_erase(struct intel_spi *ispi, const struct spi_mem *mem,
|
||||
const struct intel_spi_mem_op *iop,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
u32 addr = intel_spi_chip_addr(ispi, mem) + op->addr.val;
|
||||
u8 opcode = op->cmd.opcode;
|
||||
u32 addr = op->addr.val;
|
||||
u32 val, status;
|
||||
int ret;
|
||||
|
||||
|
|
@ -765,7 +792,7 @@ static int intel_spi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *o
|
|||
if (!iop)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return iop->exec_op(ispi, iop, op);
|
||||
return iop->exec_op(ispi, mem, iop, op);
|
||||
}
|
||||
|
||||
static const char *intel_spi_get_name(struct spi_mem *mem)
|
||||
|
|
@ -805,7 +832,7 @@ static ssize_t intel_spi_dirmap_read(struct spi_mem_dirmap_desc *desc, u64 offs,
|
|||
op.data.nbytes = len;
|
||||
op.data.buf.in = buf;
|
||||
|
||||
ret = iop->exec_op(ispi, iop, &op);
|
||||
ret = iop->exec_op(ispi, desc->mem, iop, &op);
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
|
|
@ -821,7 +848,7 @@ static ssize_t intel_spi_dirmap_write(struct spi_mem_dirmap_desc *desc, u64 offs
|
|||
op.data.nbytes = len;
|
||||
op.data.buf.out = buf;
|
||||
|
||||
ret = iop->exec_op(ispi, iop, &op);
|
||||
ret = iop->exec_op(ispi, desc->mem, iop, &op);
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
|
|
@ -1073,6 +1100,7 @@ static int intel_spi_init(struct intel_spi *ispi)
|
|||
ispi->pregs = ispi->base + CNL_PR;
|
||||
ispi->nregions = CNL_FREG_NUM;
|
||||
ispi->pr_num = CNL_PR_NUM;
|
||||
erase_64k = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -1226,10 +1254,98 @@ static void intel_spi_fill_partition(struct intel_spi *ispi,
|
|||
}
|
||||
}
|
||||
|
||||
static int intel_spi_read_desc(struct intel_spi *ispi)
|
||||
{
|
||||
struct spi_mem_op op =
|
||||
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_READ, 0),
|
||||
SPI_MEM_OP_ADDR(3, 0, 0),
|
||||
SPI_MEM_OP_NO_DUMMY,
|
||||
SPI_MEM_OP_DATA_IN(0, NULL, 0));
|
||||
u32 buf[2], nc, fcba, flcomp;
|
||||
ssize_t ret;
|
||||
|
||||
op.addr.val = 0x10;
|
||||
op.data.buf.in = buf;
|
||||
op.data.nbytes = sizeof(buf);
|
||||
|
||||
ret = intel_spi_read(ispi, NULL, NULL, &op);
|
||||
if (ret) {
|
||||
dev_warn(ispi->dev, "failed to read descriptor\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_dbg(ispi->dev, "FLVALSIG=0x%08x\n", buf[0]);
|
||||
dev_dbg(ispi->dev, "FLMAP0=0x%08x\n", buf[1]);
|
||||
|
||||
if (buf[0] != FLVALSIG_MAGIC) {
|
||||
dev_warn(ispi->dev, "descriptor signature not valid\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
fcba = (buf[1] & FLMAP0_FCBA_MASK) << 4;
|
||||
dev_dbg(ispi->dev, "FCBA=%#x\n", fcba);
|
||||
|
||||
op.addr.val = fcba;
|
||||
op.data.buf.in = &flcomp;
|
||||
op.data.nbytes = sizeof(flcomp);
|
||||
|
||||
ret = intel_spi_read(ispi, NULL, NULL, &op);
|
||||
if (ret) {
|
||||
dev_warn(ispi->dev, "failed to read FLCOMP\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dev_dbg(ispi->dev, "FLCOMP=0x%08x\n", flcomp);
|
||||
|
||||
switch (flcomp & FLCOMP_C0DEN_MASK) {
|
||||
case FLCOMP_C0DEN_512K:
|
||||
ispi->chip0_size = SZ_512K;
|
||||
break;
|
||||
case FLCOMP_C0DEN_1M:
|
||||
ispi->chip0_size = SZ_1M;
|
||||
break;
|
||||
case FLCOMP_C0DEN_2M:
|
||||
ispi->chip0_size = SZ_2M;
|
||||
break;
|
||||
case FLCOMP_C0DEN_4M:
|
||||
ispi->chip0_size = SZ_4M;
|
||||
break;
|
||||
case FLCOMP_C0DEN_8M:
|
||||
ispi->chip0_size = SZ_8M;
|
||||
break;
|
||||
case FLCOMP_C0DEN_16M:
|
||||
ispi->chip0_size = SZ_16M;
|
||||
break;
|
||||
case FLCOMP_C0DEN_32M:
|
||||
ispi->chip0_size = SZ_32M;
|
||||
break;
|
||||
case FLCOMP_C0DEN_64M:
|
||||
ispi->chip0_size = SZ_64M;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_dbg(ispi->dev, "chip0 size %zd KB\n", ispi->chip0_size / SZ_1K);
|
||||
|
||||
nc = (buf[1] & FLMAP0_NC_MASK) >> FLMAP0_NC_SHIFT;
|
||||
if (!nc)
|
||||
ispi->master->num_chipselect = 1;
|
||||
else if (nc == 1)
|
||||
ispi->master->num_chipselect = 2;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
dev_dbg(ispi->dev, "%u flash components found\n",
|
||||
ispi->master->num_chipselect);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_spi_populate_chip(struct intel_spi *ispi)
|
||||
{
|
||||
struct flash_platform_data *pdata;
|
||||
struct spi_board_info chip;
|
||||
int ret;
|
||||
|
||||
pdata = devm_kzalloc(ispi->dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
|
|
@ -1247,7 +1363,23 @@ static int intel_spi_populate_chip(struct intel_spi *ispi)
|
|||
snprintf(chip.modalias, 8, "spi-nor");
|
||||
chip.platform_data = pdata;
|
||||
|
||||
return spi_new_device(ispi->master, &chip) ? 0 : -ENODEV;
|
||||
if (!spi_new_device(ispi->master, &chip))
|
||||
return -ENODEV;
|
||||
|
||||
/* Add the second chip if present */
|
||||
if (ispi->master->num_chipselect < 2)
|
||||
return 0;
|
||||
|
||||
ret = intel_spi_read_desc(ispi);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
chip.platform_data = NULL;
|
||||
chip.chip_select = 1;
|
||||
|
||||
if (!spi_new_device(ispi->master, &chip))
|
||||
return -ENODEV;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -313,6 +313,33 @@ static struct spi_test spi_tests[] = {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
.description = "three tx+rx transfers with overlapping cache lines",
|
||||
.fill_option = FILL_COUNT_8,
|
||||
/*
|
||||
* This should be large enough for the controller driver to
|
||||
* choose to transfer it with DMA.
|
||||
*/
|
||||
.iterate_len = { 512, -1 },
|
||||
.iterate_transfer_mask = BIT(1),
|
||||
.transfer_count = 3,
|
||||
.transfers = {
|
||||
{
|
||||
.len = 1,
|
||||
.tx_buf = TX(0),
|
||||
.rx_buf = RX(0),
|
||||
},
|
||||
{
|
||||
.tx_buf = TX(1),
|
||||
.rx_buf = RX(1),
|
||||
},
|
||||
{
|
||||
.len = 1,
|
||||
.tx_buf = TX(513),
|
||||
.rx_buf = RX(513),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
{ /* end of tests sequence */ }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -537,7 +537,7 @@ static unsigned long meson_spicc_pow2_recalc_rate(struct clk_hw *hw,
|
|||
struct clk_divider *divider = to_clk_divider(hw);
|
||||
struct meson_spicc_device *spicc = pow2_clk_to_spicc(divider);
|
||||
|
||||
if (!spicc->master->cur_msg || !spicc->master->busy)
|
||||
if (!spicc->master->cur_msg)
|
||||
return 0;
|
||||
|
||||
return clk_divider_ops.recalc_rate(hw, parent_rate);
|
||||
|
|
@ -549,7 +549,7 @@ static int meson_spicc_pow2_determine_rate(struct clk_hw *hw,
|
|||
struct clk_divider *divider = to_clk_divider(hw);
|
||||
struct meson_spicc_device *spicc = pow2_clk_to_spicc(divider);
|
||||
|
||||
if (!spicc->master->cur_msg || !spicc->master->busy)
|
||||
if (!spicc->master->cur_msg)
|
||||
return -EINVAL;
|
||||
|
||||
return clk_divider_ops.determine_rate(hw, req);
|
||||
|
|
@ -561,13 +561,13 @@ static int meson_spicc_pow2_set_rate(struct clk_hw *hw, unsigned long rate,
|
|||
struct clk_divider *divider = to_clk_divider(hw);
|
||||
struct meson_spicc_device *spicc = pow2_clk_to_spicc(divider);
|
||||
|
||||
if (!spicc->master->cur_msg || !spicc->master->busy)
|
||||
if (!spicc->master->cur_msg)
|
||||
return -EINVAL;
|
||||
|
||||
return clk_divider_ops.set_rate(hw, rate, parent_rate);
|
||||
}
|
||||
|
||||
const struct clk_ops meson_spicc_pow2_clk_ops = {
|
||||
static const struct clk_ops meson_spicc_pow2_clk_ops = {
|
||||
.recalc_rate = meson_spicc_pow2_recalc_rate,
|
||||
.determine_rate = meson_spicc_pow2_determine_rate,
|
||||
.set_rate = meson_spicc_pow2_set_rate,
|
||||
|
|
|
|||
600
drivers/spi/spi-microchip-core-qspi.c
Normal file
600
drivers/spi/spi-microchip-core-qspi.c
Normal file
|
|
@ -0,0 +1,600 @@
|
|||
// SPDX-License-Identifier: (GPL-2.0)
|
||||
/*
|
||||
* Microchip coreQSPI QSPI controller driver
|
||||
*
|
||||
* Copyright (C) 2018-2022 Microchip Technology Inc. and its subsidiaries
|
||||
*
|
||||
* Author: Naga Sureshkumar Relli <nagasuresh.relli@microchip.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/spi-mem.h>
|
||||
|
||||
/*
|
||||
* QSPI Control register mask defines
|
||||
*/
|
||||
#define CONTROL_ENABLE BIT(0)
|
||||
#define CONTROL_MASTER BIT(1)
|
||||
#define CONTROL_XIP BIT(2)
|
||||
#define CONTROL_XIPADDR BIT(3)
|
||||
#define CONTROL_CLKIDLE BIT(10)
|
||||
#define CONTROL_SAMPLE_MASK GENMASK(12, 11)
|
||||
#define CONTROL_MODE0 BIT(13)
|
||||
#define CONTROL_MODE12_MASK GENMASK(15, 14)
|
||||
#define CONTROL_MODE12_EX_RO BIT(14)
|
||||
#define CONTROL_MODE12_EX_RW BIT(15)
|
||||
#define CONTROL_MODE12_FULL GENMASK(15, 14)
|
||||
#define CONTROL_FLAGSX4 BIT(16)
|
||||
#define CONTROL_CLKRATE_MASK GENMASK(27, 24)
|
||||
#define CONTROL_CLKRATE_SHIFT 24
|
||||
|
||||
/*
|
||||
* QSPI Frames register mask defines
|
||||
*/
|
||||
#define FRAMES_TOTALBYTES_MASK GENMASK(15, 0)
|
||||
#define FRAMES_CMDBYTES_MASK GENMASK(24, 16)
|
||||
#define FRAMES_CMDBYTES_SHIFT 16
|
||||
#define FRAMES_SHIFT 25
|
||||
#define FRAMES_IDLE_MASK GENMASK(29, 26)
|
||||
#define FRAMES_IDLE_SHIFT 26
|
||||
#define FRAMES_FLAGBYTE BIT(30)
|
||||
#define FRAMES_FLAGWORD BIT(31)
|
||||
|
||||
/*
|
||||
* QSPI Interrupt Enable register mask defines
|
||||
*/
|
||||
#define IEN_TXDONE BIT(0)
|
||||
#define IEN_RXDONE BIT(1)
|
||||
#define IEN_RXAVAILABLE BIT(2)
|
||||
#define IEN_TXAVAILABLE BIT(3)
|
||||
#define IEN_RXFIFOEMPTY BIT(4)
|
||||
#define IEN_TXFIFOFULL BIT(5)
|
||||
|
||||
/*
|
||||
* QSPI Status register mask defines
|
||||
*/
|
||||
#define STATUS_TXDONE BIT(0)
|
||||
#define STATUS_RXDONE BIT(1)
|
||||
#define STATUS_RXAVAILABLE BIT(2)
|
||||
#define STATUS_TXAVAILABLE BIT(3)
|
||||
#define STATUS_RXFIFOEMPTY BIT(4)
|
||||
#define STATUS_TXFIFOFULL BIT(5)
|
||||
#define STATUS_READY BIT(7)
|
||||
#define STATUS_FLAGSX4 BIT(8)
|
||||
#define STATUS_MASK GENMASK(8, 0)
|
||||
|
||||
#define BYTESUPPER_MASK GENMASK(31, 16)
|
||||
#define BYTESLOWER_MASK GENMASK(15, 0)
|
||||
|
||||
#define MAX_DIVIDER 16
|
||||
#define MIN_DIVIDER 0
|
||||
#define MAX_DATA_CMD_LEN 256
|
||||
|
||||
/* QSPI ready time out value */
|
||||
#define TIMEOUT_MS 500
|
||||
|
||||
/*
|
||||
* QSPI Register offsets.
|
||||
*/
|
||||
#define REG_CONTROL (0x00)
|
||||
#define REG_FRAMES (0x04)
|
||||
#define REG_IEN (0x0c)
|
||||
#define REG_STATUS (0x10)
|
||||
#define REG_DIRECT_ACCESS (0x14)
|
||||
#define REG_UPPER_ACCESS (0x18)
|
||||
#define REG_RX_DATA (0x40)
|
||||
#define REG_TX_DATA (0x44)
|
||||
#define REG_X4_RX_DATA (0x48)
|
||||
#define REG_X4_TX_DATA (0x4c)
|
||||
#define REG_FRAMESUP (0x50)
|
||||
|
||||
/**
|
||||
* struct mchp_coreqspi - Defines qspi driver instance
|
||||
* @regs: Virtual address of the QSPI controller registers
|
||||
* @clk: QSPI Operating clock
|
||||
* @data_completion: completion structure
|
||||
* @op_lock: lock access to the device
|
||||
* @txbuf: TX buffer
|
||||
* @rxbuf: RX buffer
|
||||
* @irq: IRQ number
|
||||
* @tx_len: Number of bytes left to transfer
|
||||
* @rx_len: Number of bytes left to receive
|
||||
*/
|
||||
struct mchp_coreqspi {
|
||||
void __iomem *regs;
|
||||
struct clk *clk;
|
||||
struct completion data_completion;
|
||||
struct mutex op_lock; /* lock access to the device */
|
||||
u8 *txbuf;
|
||||
u8 *rxbuf;
|
||||
int irq;
|
||||
int tx_len;
|
||||
int rx_len;
|
||||
};
|
||||
|
||||
static int mchp_coreqspi_set_mode(struct mchp_coreqspi *qspi, const struct spi_mem_op *op)
|
||||
{
|
||||
u32 control = readl_relaxed(qspi->regs + REG_CONTROL);
|
||||
|
||||
/*
|
||||
* The operating mode can be configured based on the command that needs to be send.
|
||||
* bits[15:14]: Sets whether multiple bit SPI operates in normal, extended or full modes.
|
||||
* 00: Normal (single DQ0 TX and single DQ1 RX lines)
|
||||
* 01: Extended RO (command and address bytes on DQ0 only)
|
||||
* 10: Extended RW (command byte on DQ0 only)
|
||||
* 11: Full. (command and address are on all DQ lines)
|
||||
* bit[13]: Sets whether multiple bit SPI uses 2 or 4 bits of data
|
||||
* 0: 2-bits (BSPI)
|
||||
* 1: 4-bits (QSPI)
|
||||
*/
|
||||
if (op->data.buswidth == 4 || op->data.buswidth == 2) {
|
||||
control &= ~CONTROL_MODE12_MASK;
|
||||
if (op->cmd.buswidth == 1 && (op->addr.buswidth == 1 || op->addr.buswidth == 0))
|
||||
control |= CONTROL_MODE12_EX_RO;
|
||||
else if (op->cmd.buswidth == 1)
|
||||
control |= CONTROL_MODE12_EX_RW;
|
||||
else
|
||||
control |= CONTROL_MODE12_FULL;
|
||||
|
||||
control |= CONTROL_MODE0;
|
||||
} else {
|
||||
control &= ~(CONTROL_MODE12_MASK |
|
||||
CONTROL_MODE0);
|
||||
}
|
||||
|
||||
writel_relaxed(control, qspi->regs + REG_CONTROL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void mchp_coreqspi_read_op(struct mchp_coreqspi *qspi)
|
||||
{
|
||||
u32 control, data;
|
||||
|
||||
if (!qspi->rx_len)
|
||||
return;
|
||||
|
||||
control = readl_relaxed(qspi->regs + REG_CONTROL);
|
||||
|
||||
/*
|
||||
* Read 4-bytes from the SPI FIFO in single transaction and then read
|
||||
* the reamaining data byte wise.
|
||||
*/
|
||||
control |= CONTROL_FLAGSX4;
|
||||
writel_relaxed(control, qspi->regs + REG_CONTROL);
|
||||
|
||||
while (qspi->rx_len >= 4) {
|
||||
while (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_RXFIFOEMPTY)
|
||||
;
|
||||
data = readl_relaxed(qspi->regs + REG_X4_RX_DATA);
|
||||
*(u32 *)qspi->rxbuf = data;
|
||||
qspi->rxbuf += 4;
|
||||
qspi->rx_len -= 4;
|
||||
}
|
||||
|
||||
control &= ~CONTROL_FLAGSX4;
|
||||
writel_relaxed(control, qspi->regs + REG_CONTROL);
|
||||
|
||||
while (qspi->rx_len--) {
|
||||
while (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_RXFIFOEMPTY)
|
||||
;
|
||||
data = readl_relaxed(qspi->regs + REG_RX_DATA);
|
||||
*qspi->rxbuf++ = (data & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void mchp_coreqspi_write_op(struct mchp_coreqspi *qspi, bool word)
|
||||
{
|
||||
u32 control, data;
|
||||
|
||||
control = readl_relaxed(qspi->regs + REG_CONTROL);
|
||||
control |= CONTROL_FLAGSX4;
|
||||
writel_relaxed(control, qspi->regs + REG_CONTROL);
|
||||
|
||||
while (qspi->tx_len >= 4) {
|
||||
while (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_TXFIFOFULL)
|
||||
;
|
||||
data = *(u32 *)qspi->txbuf;
|
||||
qspi->txbuf += 4;
|
||||
qspi->tx_len -= 4;
|
||||
writel_relaxed(data, qspi->regs + REG_X4_TX_DATA);
|
||||
}
|
||||
|
||||
control &= ~CONTROL_FLAGSX4;
|
||||
writel_relaxed(control, qspi->regs + REG_CONTROL);
|
||||
|
||||
while (qspi->tx_len--) {
|
||||
while (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_TXFIFOFULL)
|
||||
;
|
||||
data = *qspi->txbuf++;
|
||||
writel_relaxed(data, qspi->regs + REG_TX_DATA);
|
||||
}
|
||||
}
|
||||
|
||||
static void mchp_coreqspi_enable_ints(struct mchp_coreqspi *qspi)
|
||||
{
|
||||
u32 mask = IEN_TXDONE |
|
||||
IEN_RXDONE |
|
||||
IEN_RXAVAILABLE;
|
||||
|
||||
writel_relaxed(mask, qspi->regs + REG_IEN);
|
||||
}
|
||||
|
||||
static void mchp_coreqspi_disable_ints(struct mchp_coreqspi *qspi)
|
||||
{
|
||||
writel_relaxed(0, qspi->regs + REG_IEN);
|
||||
}
|
||||
|
||||
static irqreturn_t mchp_coreqspi_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct mchp_coreqspi *qspi = (struct mchp_coreqspi *)dev_id;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
int intfield = readl_relaxed(qspi->regs + REG_STATUS) & STATUS_MASK;
|
||||
|
||||
if (intfield == 0)
|
||||
return ret;
|
||||
|
||||
if (intfield & IEN_TXDONE) {
|
||||
writel_relaxed(IEN_TXDONE, qspi->regs + REG_STATUS);
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (intfield & IEN_RXAVAILABLE) {
|
||||
writel_relaxed(IEN_RXAVAILABLE, qspi->regs + REG_STATUS);
|
||||
mchp_coreqspi_read_op(qspi);
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (intfield & IEN_RXDONE) {
|
||||
writel_relaxed(IEN_RXDONE, qspi->regs + REG_STATUS);
|
||||
complete(&qspi->data_completion);
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mchp_coreqspi_setup_clock(struct mchp_coreqspi *qspi, struct spi_device *spi)
|
||||
{
|
||||
unsigned long clk_hz;
|
||||
u32 control, baud_rate_val = 0;
|
||||
|
||||
clk_hz = clk_get_rate(qspi->clk);
|
||||
if (!clk_hz)
|
||||
return -EINVAL;
|
||||
|
||||
baud_rate_val = DIV_ROUND_UP(clk_hz, 2 * spi->max_speed_hz);
|
||||
if (baud_rate_val > MAX_DIVIDER || baud_rate_val < MIN_DIVIDER) {
|
||||
dev_err(&spi->dev,
|
||||
"could not configure the clock for spi clock %d Hz & system clock %ld Hz\n",
|
||||
spi->max_speed_hz, clk_hz);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
control = readl_relaxed(qspi->regs + REG_CONTROL);
|
||||
control |= baud_rate_val << CONTROL_CLKRATE_SHIFT;
|
||||
writel_relaxed(control, qspi->regs + REG_CONTROL);
|
||||
control = readl_relaxed(qspi->regs + REG_CONTROL);
|
||||
|
||||
if ((spi->mode & SPI_CPOL) && (spi->mode & SPI_CPHA))
|
||||
control |= CONTROL_CLKIDLE;
|
||||
else
|
||||
control &= ~CONTROL_CLKIDLE;
|
||||
|
||||
writel_relaxed(control, qspi->regs + REG_CONTROL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mchp_coreqspi_setup_op(struct spi_device *spi_dev)
|
||||
{
|
||||
struct spi_controller *ctlr = spi_dev->master;
|
||||
struct mchp_coreqspi *qspi = spi_controller_get_devdata(ctlr);
|
||||
u32 control = readl_relaxed(qspi->regs + REG_CONTROL);
|
||||
|
||||
control |= (CONTROL_MASTER | CONTROL_ENABLE);
|
||||
control &= ~CONTROL_CLKIDLE;
|
||||
writel_relaxed(control, qspi->regs + REG_CONTROL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void mchp_coreqspi_config_op(struct mchp_coreqspi *qspi, const struct spi_mem_op *op)
|
||||
{
|
||||
u32 idle_cycles = 0;
|
||||
int total_bytes, cmd_bytes, frames, ctrl;
|
||||
|
||||
cmd_bytes = op->cmd.nbytes + op->addr.nbytes;
|
||||
total_bytes = cmd_bytes + op->data.nbytes;
|
||||
|
||||
/*
|
||||
* As per the coreQSPI IP spec,the number of command and data bytes are
|
||||
* controlled by the frames register for each SPI sequence. This supports
|
||||
* the SPI flash memory read and writes sequences as below. so configure
|
||||
* the cmd and total bytes accordingly.
|
||||
* ---------------------------------------------------------------------
|
||||
* TOTAL BYTES | CMD BYTES | What happens |
|
||||
* ______________________________________________________________________
|
||||
* | | |
|
||||
* 1 | 1 | The SPI core will transmit a single byte |
|
||||
* | | and receive data is discarded |
|
||||
* | | |
|
||||
* 1 | 0 | The SPI core will transmit a single byte |
|
||||
* | | and return a single byte |
|
||||
* | | |
|
||||
* 10 | 4 | The SPI core will transmit 4 command |
|
||||
* | | bytes discarding the receive data and |
|
||||
* | | transmits 6 dummy bytes returning the 6 |
|
||||
* | | received bytes and return a single byte |
|
||||
* | | |
|
||||
* 10 | 10 | The SPI core will transmit 10 command |
|
||||
* | | |
|
||||
* 10 | 0 | The SPI core will transmit 10 command |
|
||||
* | | bytes and returning 10 received bytes |
|
||||
* ______________________________________________________________________
|
||||
*/
|
||||
if (!(op->data.dir == SPI_MEM_DATA_IN))
|
||||
cmd_bytes = total_bytes;
|
||||
|
||||
frames = total_bytes & BYTESUPPER_MASK;
|
||||
writel_relaxed(frames, qspi->regs + REG_FRAMESUP);
|
||||
frames = total_bytes & BYTESLOWER_MASK;
|
||||
frames |= cmd_bytes << FRAMES_CMDBYTES_SHIFT;
|
||||
|
||||
if (op->dummy.buswidth)
|
||||
idle_cycles = op->dummy.nbytes * 8 / op->dummy.buswidth;
|
||||
|
||||
frames |= idle_cycles << FRAMES_IDLE_SHIFT;
|
||||
ctrl = readl_relaxed(qspi->regs + REG_CONTROL);
|
||||
|
||||
if (ctrl & CONTROL_MODE12_MASK)
|
||||
frames |= (1 << FRAMES_SHIFT);
|
||||
|
||||
frames |= FRAMES_FLAGWORD;
|
||||
writel_relaxed(frames, qspi->regs + REG_FRAMES);
|
||||
}
|
||||
|
||||
static int mchp_qspi_wait_for_ready(struct spi_mem *mem)
|
||||
{
|
||||
struct mchp_coreqspi *qspi = spi_controller_get_devdata
|
||||
(mem->spi->master);
|
||||
u32 status;
|
||||
int ret;
|
||||
|
||||
ret = readl_poll_timeout(qspi->regs + REG_STATUS, status,
|
||||
(status & STATUS_READY), 0,
|
||||
TIMEOUT_MS);
|
||||
if (ret) {
|
||||
dev_err(&mem->spi->dev,
|
||||
"Timeout waiting on QSPI ready.\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mchp_coreqspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
{
|
||||
struct mchp_coreqspi *qspi = spi_controller_get_devdata
|
||||
(mem->spi->master);
|
||||
u32 address = op->addr.val;
|
||||
u8 opcode = op->cmd.opcode;
|
||||
u8 opaddr[5];
|
||||
int err, i;
|
||||
|
||||
mutex_lock(&qspi->op_lock);
|
||||
err = mchp_qspi_wait_for_ready(mem);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
err = mchp_coreqspi_setup_clock(qspi, mem->spi);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
err = mchp_coreqspi_set_mode(qspi, op);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
reinit_completion(&qspi->data_completion);
|
||||
mchp_coreqspi_config_op(qspi, op);
|
||||
if (op->cmd.opcode) {
|
||||
qspi->txbuf = &opcode;
|
||||
qspi->rxbuf = NULL;
|
||||
qspi->tx_len = op->cmd.nbytes;
|
||||
qspi->rx_len = 0;
|
||||
mchp_coreqspi_write_op(qspi, false);
|
||||
}
|
||||
|
||||
qspi->txbuf = &opaddr[0];
|
||||
if (op->addr.nbytes) {
|
||||
for (i = 0; i < op->addr.nbytes; i++)
|
||||
qspi->txbuf[i] = address >> (8 * (op->addr.nbytes - i - 1));
|
||||
|
||||
qspi->rxbuf = NULL;
|
||||
qspi->tx_len = op->addr.nbytes;
|
||||
qspi->rx_len = 0;
|
||||
mchp_coreqspi_write_op(qspi, false);
|
||||
}
|
||||
|
||||
if (op->data.nbytes) {
|
||||
if (op->data.dir == SPI_MEM_DATA_OUT) {
|
||||
qspi->txbuf = (u8 *)op->data.buf.out;
|
||||
qspi->rxbuf = NULL;
|
||||
qspi->rx_len = 0;
|
||||
qspi->tx_len = op->data.nbytes;
|
||||
mchp_coreqspi_write_op(qspi, true);
|
||||
} else {
|
||||
qspi->txbuf = NULL;
|
||||
qspi->rxbuf = (u8 *)op->data.buf.in;
|
||||
qspi->rx_len = op->data.nbytes;
|
||||
qspi->tx_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
mchp_coreqspi_enable_ints(qspi);
|
||||
|
||||
if (!wait_for_completion_timeout(&qspi->data_completion, msecs_to_jiffies(1000)))
|
||||
err = -ETIMEDOUT;
|
||||
|
||||
error:
|
||||
mutex_unlock(&qspi->op_lock);
|
||||
mchp_coreqspi_disable_ints(qspi);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static bool mchp_coreqspi_supports_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
{
|
||||
if (!spi_mem_default_supports_op(mem, op))
|
||||
return false;
|
||||
|
||||
if ((op->data.buswidth == 4 || op->data.buswidth == 2) &&
|
||||
(op->cmd.buswidth == 1 && (op->addr.buswidth == 1 || op->addr.buswidth == 0))) {
|
||||
/*
|
||||
* If the command and address are on DQ0 only, then this
|
||||
* controller doesn't support sending data on dual and
|
||||
* quad lines. but it supports reading data on dual and
|
||||
* quad lines with same configuration as command and
|
||||
* address on DQ0.
|
||||
* i.e. The control register[15:13] :EX_RO(read only) is
|
||||
* meant only for the command and address are on DQ0 but
|
||||
* not to write data, it is just to read.
|
||||
* Ex: 0x34h is Quad Load Program Data which is not
|
||||
* supported. Then the spi-mem layer will iterate over
|
||||
* each command and it will chose the supported one.
|
||||
*/
|
||||
if (op->data.dir == SPI_MEM_DATA_OUT)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int mchp_coreqspi_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
|
||||
{
|
||||
if (op->data.dir == SPI_MEM_DATA_OUT || op->data.dir == SPI_MEM_DATA_IN) {
|
||||
if (op->data.nbytes > MAX_DATA_CMD_LEN)
|
||||
op->data.nbytes = MAX_DATA_CMD_LEN;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_controller_mem_ops mchp_coreqspi_mem_ops = {
|
||||
.adjust_op_size = mchp_coreqspi_adjust_op_size,
|
||||
.supports_op = mchp_coreqspi_supports_op,
|
||||
.exec_op = mchp_coreqspi_exec_op,
|
||||
};
|
||||
|
||||
static int mchp_coreqspi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_controller *ctlr;
|
||||
struct mchp_coreqspi *qspi;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
int ret;
|
||||
|
||||
ctlr = devm_spi_alloc_master(&pdev->dev, sizeof(*qspi));
|
||||
if (!ctlr)
|
||||
return dev_err_probe(&pdev->dev, -ENOMEM,
|
||||
"unable to allocate master for QSPI controller\n");
|
||||
|
||||
qspi = spi_controller_get_devdata(ctlr);
|
||||
platform_set_drvdata(pdev, qspi);
|
||||
|
||||
qspi->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(qspi->regs))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(qspi->regs),
|
||||
"failed to map registers\n");
|
||||
|
||||
qspi->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(qspi->clk))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(qspi->clk),
|
||||
"could not get clock\n");
|
||||
|
||||
ret = clk_prepare_enable(qspi->clk);
|
||||
if (ret)
|
||||
return dev_err_probe(&pdev->dev, ret,
|
||||
"failed to enable clock\n");
|
||||
|
||||
init_completion(&qspi->data_completion);
|
||||
mutex_init(&qspi->op_lock);
|
||||
|
||||
qspi->irq = platform_get_irq(pdev, 0);
|
||||
if (qspi->irq < 0) {
|
||||
ret = qspi->irq;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, qspi->irq, mchp_coreqspi_isr,
|
||||
IRQF_SHARED, pdev->name, qspi);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "request_irq failed %d\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ctlr->bits_per_word_mask = SPI_BPW_MASK(8);
|
||||
ctlr->mem_ops = &mchp_coreqspi_mem_ops;
|
||||
ctlr->setup = mchp_coreqspi_setup_op;
|
||||
ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_RX_DUAL | SPI_RX_QUAD |
|
||||
SPI_TX_DUAL | SPI_TX_QUAD;
|
||||
ctlr->dev.of_node = np;
|
||||
|
||||
ret = devm_spi_register_controller(&pdev->dev, ctlr);
|
||||
if (ret) {
|
||||
dev_err_probe(&pdev->dev, ret,
|
||||
"spi_register_controller failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
clk_disable_unprepare(qspi->clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mchp_coreqspi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mchp_coreqspi *qspi = platform_get_drvdata(pdev);
|
||||
u32 control = readl_relaxed(qspi->regs + REG_CONTROL);
|
||||
|
||||
mchp_coreqspi_disable_ints(qspi);
|
||||
control &= ~CONTROL_ENABLE;
|
||||
writel_relaxed(control, qspi->regs + REG_CONTROL);
|
||||
clk_disable_unprepare(qspi->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id mchp_coreqspi_of_match[] = {
|
||||
{ .compatible = "microchip,coreqspi-rtl-v2" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mchp_coreqspi_of_match);
|
||||
|
||||
static struct platform_driver mchp_coreqspi_driver = {
|
||||
.probe = mchp_coreqspi_probe,
|
||||
.driver = {
|
||||
.name = "microchip,coreqspi",
|
||||
.of_match_table = mchp_coreqspi_of_match,
|
||||
},
|
||||
.remove = mchp_coreqspi_remove,
|
||||
};
|
||||
module_platform_driver(mchp_coreqspi_driver);
|
||||
|
||||
MODULE_AUTHOR("Naga Sureshkumar Relli <nagasuresh.relli@microchip.com");
|
||||
MODULE_DESCRIPTION("Microchip coreQSPI QSPI controller driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -548,12 +548,12 @@ static int mchp_corespi_probe(struct platform_device *pdev)
|
|||
IRQF_SHARED, dev_name(&pdev->dev), master);
|
||||
if (ret)
|
||||
return dev_err_probe(&pdev->dev, ret,
|
||||
"could not request irq: %d\n", ret);
|
||||
"could not request irq\n");
|
||||
|
||||
spi->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(spi->clk))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(spi->clk),
|
||||
"could not get clk: %d\n", ret);
|
||||
"could not get clk\n");
|
||||
|
||||
ret = clk_prepare_enable(spi->clk);
|
||||
if (ret)
|
||||
|
|
|
|||
|
|
@ -11,13 +11,14 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
|
|
@ -89,7 +90,7 @@ struct mpc52xx_spi {
|
|||
const u8 *tx_buf;
|
||||
int cs_change;
|
||||
int gpio_cs_count;
|
||||
unsigned int *gpio_cs;
|
||||
struct gpio_desc **gpio_cs;
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
@ -101,9 +102,10 @@ static void mpc52xx_spi_chipsel(struct mpc52xx_spi *ms, int value)
|
|||
|
||||
if (ms->gpio_cs_count > 0) {
|
||||
cs = ms->message->spi->chip_select;
|
||||
gpio_set_value(ms->gpio_cs[cs], value ? 0 : 1);
|
||||
} else
|
||||
gpiod_set_value(ms->gpio_cs[cs], value);
|
||||
} else {
|
||||
out_8(ms->regs + SPI_PORTDATA, value ? 0 : 0x08);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -385,10 +387,10 @@ static int mpc52xx_spi_probe(struct platform_device *op)
|
|||
{
|
||||
struct spi_master *master;
|
||||
struct mpc52xx_spi *ms;
|
||||
struct gpio_desc *gpio_cs;
|
||||
void __iomem *regs;
|
||||
u8 ctrl1;
|
||||
int rc, i = 0;
|
||||
int gpio_cs;
|
||||
|
||||
/* MMIO registers */
|
||||
dev_dbg(&op->dev, "probing mpc5200 SPI device\n");
|
||||
|
|
@ -438,7 +440,7 @@ static int mpc52xx_spi_probe(struct platform_device *op)
|
|||
ms->irq1 = irq_of_parse_and_map(op->dev.of_node, 1);
|
||||
ms->state = mpc52xx_spi_fsmstate_idle;
|
||||
ms->ipb_freq = mpc5xxx_get_bus_frequency(&op->dev);
|
||||
ms->gpio_cs_count = of_gpio_count(op->dev.of_node);
|
||||
ms->gpio_cs_count = gpiod_count(&op->dev, NULL);
|
||||
if (ms->gpio_cs_count > 0) {
|
||||
master->num_chipselect = ms->gpio_cs_count;
|
||||
ms->gpio_cs = kmalloc_array(ms->gpio_cs_count,
|
||||
|
|
@ -450,23 +452,16 @@ static int mpc52xx_spi_probe(struct platform_device *op)
|
|||
}
|
||||
|
||||
for (i = 0; i < ms->gpio_cs_count; i++) {
|
||||
gpio_cs = of_get_gpio(op->dev.of_node, i);
|
||||
if (!gpio_is_valid(gpio_cs)) {
|
||||
dev_err(&op->dev,
|
||||
"could not parse the gpio field in oftree\n");
|
||||
rc = -ENODEV;
|
||||
goto err_gpio;
|
||||
}
|
||||
|
||||
rc = gpio_request(gpio_cs, dev_name(&op->dev));
|
||||
gpio_cs = gpiod_get_index(&op->dev,
|
||||
NULL, i, GPIOD_OUT_LOW);
|
||||
rc = PTR_ERR_OR_ZERO(gpio_cs);
|
||||
if (rc) {
|
||||
dev_err(&op->dev,
|
||||
"can't request spi cs gpio #%d on gpio line %d\n",
|
||||
i, gpio_cs);
|
||||
"failed to get spi cs gpio #%d: %d\n",
|
||||
i, rc);
|
||||
goto err_gpio;
|
||||
}
|
||||
|
||||
gpio_direction_output(gpio_cs, 1);
|
||||
ms->gpio_cs[i] = gpio_cs;
|
||||
}
|
||||
}
|
||||
|
|
@ -507,7 +502,7 @@ static int mpc52xx_spi_probe(struct platform_device *op)
|
|||
dev_err(&ms->master->dev, "initialization failed\n");
|
||||
err_gpio:
|
||||
while (i-- > 0)
|
||||
gpio_free(ms->gpio_cs[i]);
|
||||
gpiod_put(ms->gpio_cs[i]);
|
||||
|
||||
kfree(ms->gpio_cs);
|
||||
err_alloc_gpio:
|
||||
|
|
@ -528,7 +523,7 @@ static int mpc52xx_spi_remove(struct platform_device *op)
|
|||
free_irq(ms->irq1, ms);
|
||||
|
||||
for (i = 0; i < ms->gpio_cs_count; i++)
|
||||
gpio_free(ms->gpio_cs[i]);
|
||||
gpiod_put(ms->gpio_cs[i]);
|
||||
|
||||
kfree(ms->gpio_cs);
|
||||
spi_unregister_master(master);
|
||||
|
|
|
|||
|
|
@ -1184,6 +1184,11 @@ static int mtk_spi_probe(struct platform_device *pdev)
|
|||
if (!dev->dma_mask)
|
||||
dev->dma_mask = &dev->coherent_dma_mask;
|
||||
|
||||
if (mdata->dev_comp->ipm_design)
|
||||
dma_set_max_seg_size(dev, SZ_16M);
|
||||
else
|
||||
dma_set_max_seg_size(dev, SZ_256K);
|
||||
|
||||
ret = devm_request_irq(dev, irq, mtk_spi_interrupt,
|
||||
IRQF_TRIGGER_NONE, dev_name(dev), master);
|
||||
if (ret)
|
||||
|
|
|
|||
|
|
@ -55,7 +55,6 @@ struct mt7621_spi {
|
|||
void __iomem *base;
|
||||
unsigned int sys_freq;
|
||||
unsigned int speed;
|
||||
struct clk *clk;
|
||||
int pending_write;
|
||||
};
|
||||
|
||||
|
|
@ -327,7 +326,6 @@ static int mt7621_spi_probe(struct platform_device *pdev)
|
|||
struct spi_controller *master;
|
||||
struct mt7621_spi *rs;
|
||||
void __iomem *base;
|
||||
int status = 0;
|
||||
struct clk *clk;
|
||||
int ret;
|
||||
|
||||
|
|
@ -339,21 +337,14 @@ static int mt7621_spi_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(&pdev->dev, "unable to get SYS clock, err=%d\n",
|
||||
status);
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
|
||||
status = clk_prepare_enable(clk);
|
||||
if (status)
|
||||
return status;
|
||||
clk = devm_clk_get_enabled(&pdev->dev, NULL);
|
||||
if (IS_ERR(clk))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(clk),
|
||||
"unable to get SYS clock\n");
|
||||
|
||||
master = devm_spi_alloc_master(&pdev->dev, sizeof(*rs));
|
||||
if (!master) {
|
||||
dev_info(&pdev->dev, "master allocation failed\n");
|
||||
clk_disable_unprepare(clk);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
|
|
@ -369,38 +360,18 @@ static int mt7621_spi_probe(struct platform_device *pdev)
|
|||
|
||||
rs = spi_controller_get_devdata(master);
|
||||
rs->base = base;
|
||||
rs->clk = clk;
|
||||
rs->master = master;
|
||||
rs->sys_freq = clk_get_rate(rs->clk);
|
||||
rs->sys_freq = clk_get_rate(clk);
|
||||
rs->pending_write = 0;
|
||||
dev_info(&pdev->dev, "sys_freq: %u\n", rs->sys_freq);
|
||||
|
||||
ret = device_reset(&pdev->dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "SPI reset failed!\n");
|
||||
clk_disable_unprepare(clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = spi_register_controller(master);
|
||||
if (ret)
|
||||
clk_disable_unprepare(clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mt7621_spi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_controller *master;
|
||||
struct mt7621_spi *rs;
|
||||
|
||||
master = dev_get_drvdata(&pdev->dev);
|
||||
rs = spi_controller_get_devdata(master);
|
||||
|
||||
spi_unregister_controller(master);
|
||||
clk_disable_unprepare(rs->clk);
|
||||
|
||||
return 0;
|
||||
return devm_spi_register_controller(&pdev->dev, master);
|
||||
}
|
||||
|
||||
MODULE_ALIAS("platform:" DRIVER_NAME);
|
||||
|
|
@ -411,7 +382,6 @@ static struct platform_driver mt7621_spi_driver = {
|
|||
.of_match_table = mt7621_spi_match,
|
||||
},
|
||||
.probe = mt7621_spi_probe,
|
||||
.remove = mt7621_spi_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(mt7621_spi_driver);
|
||||
|
|
|
|||
|
|
@ -443,6 +443,7 @@ static int npcm_pspi_remove(struct platform_device *pdev)
|
|||
|
||||
static const struct of_device_id npcm_pspi_match[] = {
|
||||
{ .compatible = "nuvoton,npcm750-pspi", .data = NULL },
|
||||
{ .compatible = "nuvoton,npcm845-pspi", .data = NULL },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, npcm_pspi_match);
|
||||
|
|
|
|||
|
|
@ -588,7 +588,7 @@ static int nxp_fspi_clk_prep_enable(struct nxp_fspi *f)
|
|||
{
|
||||
int ret;
|
||||
|
||||
if (is_acpi_node(f->dev->fwnode))
|
||||
if (is_acpi_node(dev_fwnode(f->dev)))
|
||||
return 0;
|
||||
|
||||
ret = clk_prepare_enable(f->clk_en);
|
||||
|
|
@ -606,7 +606,7 @@ static int nxp_fspi_clk_prep_enable(struct nxp_fspi *f)
|
|||
|
||||
static int nxp_fspi_clk_disable_unprep(struct nxp_fspi *f)
|
||||
{
|
||||
if (is_acpi_node(f->dev->fwnode))
|
||||
if (is_acpi_node(dev_fwnode(f->dev)))
|
||||
return 0;
|
||||
|
||||
clk_disable_unprepare(f->clk);
|
||||
|
|
@ -1100,7 +1100,7 @@ static int nxp_fspi_probe(struct platform_device *pdev)
|
|||
platform_set_drvdata(pdev, f);
|
||||
|
||||
/* find the resources - configuration register address space */
|
||||
if (is_acpi_node(f->dev->fwnode))
|
||||
if (is_acpi_node(dev_fwnode(f->dev)))
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
else
|
||||
res = platform_get_resource_byname(pdev,
|
||||
|
|
@ -1113,7 +1113,7 @@ static int nxp_fspi_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
/* find the resources - controller memory mapped space */
|
||||
if (is_acpi_node(f->dev->fwnode))
|
||||
if (is_acpi_node(dev_fwnode(f->dev)))
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
else
|
||||
res = platform_get_resource_byname(pdev,
|
||||
|
|
|
|||
|
|
@ -412,6 +412,7 @@ static int omap1_spi100k_probe(struct platform_device *pdev)
|
|||
return status;
|
||||
|
||||
err_fck:
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
clk_disable_unprepare(spi100k->fck);
|
||||
err_ick:
|
||||
clk_disable_unprepare(spi100k->ick);
|
||||
|
|
|
|||
|
|
@ -1509,10 +1509,8 @@ static int omap2_mcspi_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
status = platform_get_irq(pdev, 0);
|
||||
if (status == -EPROBE_DEFER)
|
||||
goto free_master;
|
||||
if (status < 0) {
|
||||
dev_err(&pdev->dev, "no irq resource found\n");
|
||||
dev_err_probe(&pdev->dev, status, "no irq resource found\n");
|
||||
goto free_master;
|
||||
}
|
||||
init_completion(&mcspi->txdone);
|
||||
|
|
|
|||
|
|
@ -1856,10 +1856,8 @@ static int pxa2xx_spi_runtime_suspend(struct device *dev)
|
|||
static int pxa2xx_spi_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct driver_data *drv_data = dev_get_drvdata(dev);
|
||||
int status;
|
||||
|
||||
status = clk_prepare_enable(drv_data->ssp->clk);
|
||||
return status;
|
||||
return clk_prepare_enable(drv_data->ssp->clk);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -1198,8 +1198,10 @@ static int spi_qup_pm_resume_runtime(struct device *device)
|
|||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(controller->cclk);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
clk_disable_unprepare(controller->iclk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Disable clocks auto gaiting */
|
||||
config = readl_relaxed(controller->base + QUP_CONFIG);
|
||||
|
|
@ -1245,14 +1247,25 @@ static int spi_qup_resume(struct device *device)
|
|||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(controller->cclk);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
clk_disable_unprepare(controller->iclk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = spi_qup_set_state(controller, QUP_STATE_RESET);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto disable_clk;
|
||||
|
||||
return spi_master_resume(master);
|
||||
ret = spi_master_resume(master);
|
||||
if (ret)
|
||||
goto disable_clk;
|
||||
|
||||
return 0;
|
||||
|
||||
disable_clk:
|
||||
clk_disable_unprepare(controller->cclk);
|
||||
clk_disable_unprepare(controller->iclk);
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
|
|
|
|||
|
|
@ -449,7 +449,7 @@ static int s3c24xx_spi_probe(struct platform_device *pdev)
|
|||
struct spi_master *master;
|
||||
int err = 0;
|
||||
|
||||
master = spi_alloc_master(&pdev->dev, sizeof(struct s3c24xx_spi));
|
||||
master = devm_spi_alloc_master(&pdev->dev, sizeof(struct s3c24xx_spi));
|
||||
if (master == NULL) {
|
||||
dev_err(&pdev->dev, "No memory for spi_master\n");
|
||||
return -ENOMEM;
|
||||
|
|
@ -463,8 +463,7 @@ static int s3c24xx_spi_probe(struct platform_device *pdev)
|
|||
|
||||
if (pdata == NULL) {
|
||||
dev_err(&pdev->dev, "No platform data supplied\n");
|
||||
err = -ENOENT;
|
||||
goto err_no_pdata;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, hw);
|
||||
|
|
@ -499,29 +498,24 @@ static int s3c24xx_spi_probe(struct platform_device *pdev)
|
|||
|
||||
/* find and map our resources */
|
||||
hw->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(hw->regs)) {
|
||||
err = PTR_ERR(hw->regs);
|
||||
goto err_no_pdata;
|
||||
}
|
||||
if (IS_ERR(hw->regs))
|
||||
return PTR_ERR(hw->regs);
|
||||
|
||||
hw->irq = platform_get_irq(pdev, 0);
|
||||
if (hw->irq < 0) {
|
||||
err = -ENOENT;
|
||||
goto err_no_pdata;
|
||||
}
|
||||
if (hw->irq < 0)
|
||||
return -ENOENT;
|
||||
|
||||
err = devm_request_irq(&pdev->dev, hw->irq, s3c24xx_spi_irq, 0,
|
||||
pdev->name, hw);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "Cannot claim IRQ\n");
|
||||
goto err_no_pdata;
|
||||
return err;
|
||||
}
|
||||
|
||||
hw->clk = devm_clk_get(&pdev->dev, "spi");
|
||||
if (IS_ERR(hw->clk)) {
|
||||
dev_err(&pdev->dev, "No clock for device\n");
|
||||
err = PTR_ERR(hw->clk);
|
||||
goto err_no_pdata;
|
||||
return PTR_ERR(hw->clk);
|
||||
}
|
||||
|
||||
s3c24xx_spi_initialsetup(hw);
|
||||
|
|
@ -539,8 +533,6 @@ static int s3c24xx_spi_probe(struct platform_device *pdev)
|
|||
err_register:
|
||||
clk_disable(hw->clk);
|
||||
|
||||
err_no_pdata:
|
||||
spi_master_put(hw->master);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -84,6 +84,7 @@
|
|||
#define S3C64XX_SPI_ST_TX_FIFORDY (1<<0)
|
||||
|
||||
#define S3C64XX_SPI_PACKET_CNT_EN (1<<16)
|
||||
#define S3C64XX_SPI_PACKET_CNT_MASK GENMASK(15, 0)
|
||||
|
||||
#define S3C64XX_SPI_PND_TX_UNDERRUN_CLR (1<<4)
|
||||
#define S3C64XX_SPI_PND_TX_OVERRUN_CLR (1<<3)
|
||||
|
|
@ -389,8 +390,8 @@ static int s3c64xx_spi_unprepare_transfer(struct spi_master *spi)
|
|||
if (sdd->rx_dma.ch && sdd->tx_dma.ch) {
|
||||
dma_release_channel(sdd->rx_dma.ch);
|
||||
dma_release_channel(sdd->tx_dma.ch);
|
||||
sdd->rx_dma.ch = 0;
|
||||
sdd->tx_dma.ch = 0;
|
||||
sdd->rx_dma.ch = NULL;
|
||||
sdd->tx_dma.ch = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -711,6 +712,13 @@ static int s3c64xx_spi_prepare_message(struct spi_master *master,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static size_t s3c64xx_spi_max_transfer_size(struct spi_device *spi)
|
||||
{
|
||||
struct spi_controller *ctlr = spi->controller;
|
||||
|
||||
return ctlr->can_dma ? S3C64XX_SPI_PACKET_CNT_MASK : SIZE_MAX;
|
||||
}
|
||||
|
||||
static int s3c64xx_spi_transfer_one(struct spi_master *master,
|
||||
struct spi_device *spi,
|
||||
struct spi_transfer *xfer)
|
||||
|
|
@ -1152,6 +1160,7 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
|
|||
master->unprepare_transfer_hardware = s3c64xx_spi_unprepare_transfer;
|
||||
master->prepare_message = s3c64xx_spi_prepare_message;
|
||||
master->transfer_one = s3c64xx_spi_transfer_one;
|
||||
master->max_transfer_size = s3c64xx_spi_max_transfer_size;
|
||||
master->num_chipselect = sci->num_cs;
|
||||
master->use_gpio_descriptors = true;
|
||||
master->dma_alignment = 8;
|
||||
|
|
|
|||
|
|
@ -1085,6 +1085,7 @@ static const struct of_device_id sh_msiof_match[] = {
|
|||
{ .compatible = "renesas,rcar-gen2-msiof", .data = &rcar_gen2_data },
|
||||
{ .compatible = "renesas,msiof-r8a7796", .data = &rcar_gen3_data },
|
||||
{ .compatible = "renesas,rcar-gen3-msiof", .data = &rcar_gen3_data },
|
||||
{ .compatible = "renesas,rcar-gen4-msiof", .data = &rcar_gen3_data },
|
||||
{ .compatible = "renesas,sh-msiof", .data = &sh_data }, /* Deprecated */
|
||||
{},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
|
@ -355,10 +356,10 @@ static int stm32_qspi_get_mode(u8 buswidth)
|
|||
return buswidth;
|
||||
}
|
||||
|
||||
static int stm32_qspi_send(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
static int stm32_qspi_send(struct spi_device *spi, const struct spi_mem_op *op)
|
||||
{
|
||||
struct stm32_qspi *qspi = spi_controller_get_devdata(mem->spi->master);
|
||||
struct stm32_qspi_flash *flash = &qspi->flash[mem->spi->chip_select];
|
||||
struct stm32_qspi *qspi = spi_controller_get_devdata(spi->master);
|
||||
struct stm32_qspi_flash *flash = &qspi->flash[spi->chip_select];
|
||||
u32 ccr, cr;
|
||||
int timeout, err = 0, err_poll_status = 0;
|
||||
|
||||
|
|
@ -465,7 +466,7 @@ static int stm32_qspi_poll_status(struct spi_mem *mem, const struct spi_mem_op *
|
|||
qspi->fmode = CCR_FMODE_APM;
|
||||
qspi->status_timeout = timeout_ms;
|
||||
|
||||
ret = stm32_qspi_send(mem, op);
|
||||
ret = stm32_qspi_send(mem->spi, op);
|
||||
mutex_unlock(&qspi->lock);
|
||||
|
||||
pm_runtime_mark_last_busy(qspi->dev);
|
||||
|
|
@ -489,7 +490,7 @@ static int stm32_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
|||
else
|
||||
qspi->fmode = CCR_FMODE_INDW;
|
||||
|
||||
ret = stm32_qspi_send(mem, op);
|
||||
ret = stm32_qspi_send(mem->spi, op);
|
||||
mutex_unlock(&qspi->lock);
|
||||
|
||||
pm_runtime_mark_last_busy(qspi->dev);
|
||||
|
|
@ -545,7 +546,7 @@ static ssize_t stm32_qspi_dirmap_read(struct spi_mem_dirmap_desc *desc,
|
|||
else
|
||||
qspi->fmode = CCR_FMODE_INDR;
|
||||
|
||||
ret = stm32_qspi_send(desc->mem, &op);
|
||||
ret = stm32_qspi_send(desc->mem->spi, &op);
|
||||
mutex_unlock(&qspi->lock);
|
||||
|
||||
pm_runtime_mark_last_busy(qspi->dev);
|
||||
|
|
@ -554,12 +555,96 @@ static ssize_t stm32_qspi_dirmap_read(struct spi_mem_dirmap_desc *desc,
|
|||
return ret ?: len;
|
||||
}
|
||||
|
||||
static int stm32_qspi_transfer_one_message(struct spi_controller *ctrl,
|
||||
struct spi_message *msg)
|
||||
{
|
||||
struct stm32_qspi *qspi = spi_controller_get_devdata(ctrl);
|
||||
struct spi_transfer *transfer;
|
||||
struct spi_device *spi = msg->spi;
|
||||
struct spi_mem_op op;
|
||||
int ret = 0;
|
||||
|
||||
if (!spi->cs_gpiod)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
ret = pm_runtime_resume_and_get(qspi->dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&qspi->lock);
|
||||
|
||||
gpiod_set_value_cansleep(spi->cs_gpiod, true);
|
||||
|
||||
list_for_each_entry(transfer, &msg->transfers, transfer_list) {
|
||||
u8 dummy_bytes = 0;
|
||||
|
||||
memset(&op, 0, sizeof(op));
|
||||
|
||||
dev_dbg(qspi->dev, "tx_buf:%p tx_nbits:%d rx_buf:%p rx_nbits:%d len:%d dummy_data:%d\n",
|
||||
transfer->tx_buf, transfer->tx_nbits,
|
||||
transfer->rx_buf, transfer->rx_nbits,
|
||||
transfer->len, transfer->dummy_data);
|
||||
|
||||
/*
|
||||
* QSPI hardware supports dummy bytes transfer.
|
||||
* If current transfer is dummy byte, merge it with the next
|
||||
* transfer in order to take into account QSPI block constraint
|
||||
*/
|
||||
if (transfer->dummy_data) {
|
||||
op.dummy.buswidth = transfer->tx_nbits;
|
||||
op.dummy.nbytes = transfer->len;
|
||||
dummy_bytes = transfer->len;
|
||||
|
||||
/* if happens, means that message is not correctly built */
|
||||
if (list_is_last(&transfer->transfer_list, &msg->transfers)) {
|
||||
ret = -EINVAL;
|
||||
goto end_of_transfer;
|
||||
}
|
||||
|
||||
transfer = list_next_entry(transfer, transfer_list);
|
||||
}
|
||||
|
||||
op.data.nbytes = transfer->len;
|
||||
|
||||
if (transfer->rx_buf) {
|
||||
qspi->fmode = CCR_FMODE_INDR;
|
||||
op.data.buswidth = transfer->rx_nbits;
|
||||
op.data.dir = SPI_MEM_DATA_IN;
|
||||
op.data.buf.in = transfer->rx_buf;
|
||||
} else {
|
||||
qspi->fmode = CCR_FMODE_INDW;
|
||||
op.data.buswidth = transfer->tx_nbits;
|
||||
op.data.dir = SPI_MEM_DATA_OUT;
|
||||
op.data.buf.out = transfer->tx_buf;
|
||||
}
|
||||
|
||||
ret = stm32_qspi_send(spi, &op);
|
||||
if (ret)
|
||||
goto end_of_transfer;
|
||||
|
||||
msg->actual_length += transfer->len + dummy_bytes;
|
||||
}
|
||||
|
||||
end_of_transfer:
|
||||
gpiod_set_value_cansleep(spi->cs_gpiod, false);
|
||||
|
||||
mutex_unlock(&qspi->lock);
|
||||
|
||||
msg->status = ret;
|
||||
spi_finalize_current_message(ctrl);
|
||||
|
||||
pm_runtime_mark_last_busy(qspi->dev);
|
||||
pm_runtime_put_autosuspend(qspi->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stm32_qspi_setup(struct spi_device *spi)
|
||||
{
|
||||
struct spi_controller *ctrl = spi->master;
|
||||
struct stm32_qspi *qspi = spi_controller_get_devdata(ctrl);
|
||||
struct stm32_qspi_flash *flash;
|
||||
u32 presc;
|
||||
u32 presc, mode;
|
||||
int ret;
|
||||
|
||||
if (ctrl->busy)
|
||||
|
|
@ -568,6 +653,16 @@ static int stm32_qspi_setup(struct spi_device *spi)
|
|||
if (!spi->max_speed_hz)
|
||||
return -EINVAL;
|
||||
|
||||
mode = spi->mode & (SPI_TX_OCTAL | SPI_RX_OCTAL);
|
||||
if ((mode == SPI_TX_OCTAL || mode == SPI_RX_OCTAL) ||
|
||||
((mode == (SPI_TX_OCTAL | SPI_RX_OCTAL)) &&
|
||||
gpiod_count(qspi->dev, "cs") == -ENOENT)) {
|
||||
dev_err(qspi->dev, "spi-rx-bus-width\\/spi-tx-bus-width\\/cs-gpios\n");
|
||||
dev_err(qspi->dev, "configuration not supported\n");
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = pm_runtime_resume_and_get(qspi->dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
|
@ -580,6 +675,16 @@ static int stm32_qspi_setup(struct spi_device *spi)
|
|||
|
||||
mutex_lock(&qspi->lock);
|
||||
qspi->cr_reg = CR_APMS | 3 << CR_FTHRES_SHIFT | CR_SSHIFT | CR_EN;
|
||||
|
||||
/*
|
||||
* Dual flash mode is only enable in case SPI_TX_OCTAL and SPI_TX_OCTAL
|
||||
* are both set in spi->mode and "cs-gpios" properties is found in DT
|
||||
*/
|
||||
if (mode == (SPI_TX_OCTAL | SPI_RX_OCTAL)) {
|
||||
qspi->cr_reg |= CR_DFM;
|
||||
dev_dbg(qspi->dev, "Dual flash mode enable");
|
||||
}
|
||||
|
||||
writel_relaxed(qspi->cr_reg, qspi->io_base + QSPI_CR);
|
||||
|
||||
/* set dcr fsize to max address */
|
||||
|
|
@ -741,11 +846,13 @@ static int stm32_qspi_probe(struct platform_device *pdev)
|
|||
|
||||
mutex_init(&qspi->lock);
|
||||
|
||||
ctrl->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD
|
||||
| SPI_TX_DUAL | SPI_TX_QUAD;
|
||||
ctrl->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD | SPI_TX_OCTAL
|
||||
| SPI_TX_DUAL | SPI_TX_QUAD | SPI_RX_OCTAL;
|
||||
ctrl->setup = stm32_qspi_setup;
|
||||
ctrl->bus_num = -1;
|
||||
ctrl->mem_ops = &stm32_qspi_mem_ops;
|
||||
ctrl->use_gpio_descriptors = true;
|
||||
ctrl->transfer_one_message = stm32_qspi_transfer_one_message;
|
||||
ctrl->num_chipselect = STM32_QSPI_MAX_NORCHIP;
|
||||
ctrl->dev.of_node = dev->of_node;
|
||||
|
||||
|
|
|
|||
|
|
@ -421,7 +421,7 @@ static int xilinx_spi_probe(struct platform_device *pdev)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
master = spi_alloc_master(&pdev->dev, sizeof(struct xilinx_spi));
|
||||
master = devm_spi_alloc_master(&pdev->dev, sizeof(struct xilinx_spi));
|
||||
if (!master)
|
||||
return -ENODEV;
|
||||
|
||||
|
|
@ -439,10 +439,8 @@ static int xilinx_spi_probe(struct platform_device *pdev)
|
|||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
xspi->regs = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(xspi->regs)) {
|
||||
ret = PTR_ERR(xspi->regs);
|
||||
goto put_master;
|
||||
}
|
||||
if (IS_ERR(xspi->regs))
|
||||
return PTR_ERR(xspi->regs);
|
||||
|
||||
master->bus_num = pdev->id;
|
||||
master->num_chipselect = num_cs;
|
||||
|
|
@ -472,14 +470,13 @@ static int xilinx_spi_probe(struct platform_device *pdev)
|
|||
|
||||
xspi->irq = platform_get_irq(pdev, 0);
|
||||
if (xspi->irq < 0 && xspi->irq != -ENXIO) {
|
||||
ret = xspi->irq;
|
||||
goto put_master;
|
||||
return xspi->irq;
|
||||
} else if (xspi->irq >= 0) {
|
||||
/* Register for SPI Interrupt */
|
||||
ret = devm_request_irq(&pdev->dev, xspi->irq, xilinx_spi_irq, 0,
|
||||
dev_name(&pdev->dev), xspi);
|
||||
if (ret)
|
||||
goto put_master;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* SPI controller initializations */
|
||||
|
|
@ -488,7 +485,7 @@ static int xilinx_spi_probe(struct platform_device *pdev)
|
|||
ret = spi_bitbang_start(&xspi->bitbang);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "spi_bitbang_start FAILED\n");
|
||||
goto put_master;
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "at %pR, irq=%d\n", res, xspi->irq);
|
||||
|
|
@ -500,11 +497,6 @@ static int xilinx_spi_probe(struct platform_device *pdev)
|
|||
|
||||
platform_set_drvdata(pdev, master);
|
||||
return 0;
|
||||
|
||||
put_master:
|
||||
spi_master_put(master);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int xilinx_spi_remove(struct platform_device *pdev)
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ static int xtfpga_spi_probe(struct platform_device *pdev)
|
|||
int ret;
|
||||
struct spi_master *master;
|
||||
|
||||
master = spi_alloc_master(&pdev->dev, sizeof(struct xtfpga_spi));
|
||||
master = devm_spi_alloc_master(&pdev->dev, sizeof(struct xtfpga_spi));
|
||||
if (!master)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
@ -97,30 +97,24 @@ static int xtfpga_spi_probe(struct platform_device *pdev)
|
|||
xspi->bitbang.chipselect = xtfpga_spi_chipselect;
|
||||
xspi->bitbang.txrx_word[SPI_MODE_0] = xtfpga_spi_txrx_word;
|
||||
xspi->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(xspi->regs)) {
|
||||
ret = PTR_ERR(xspi->regs);
|
||||
goto err;
|
||||
}
|
||||
if (IS_ERR(xspi->regs))
|
||||
return PTR_ERR(xspi->regs);
|
||||
|
||||
xtfpga_spi_write32(xspi, XTFPGA_SPI_START, 0);
|
||||
usleep_range(1000, 2000);
|
||||
if (xtfpga_spi_read32(xspi, XTFPGA_SPI_BUSY)) {
|
||||
dev_err(&pdev->dev, "Device stuck in busy state\n");
|
||||
ret = -EBUSY;
|
||||
goto err;
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
ret = spi_bitbang_start(&xspi->bitbang);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "spi_bitbang_start failed\n");
|
||||
goto err;
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, master);
|
||||
return 0;
|
||||
err:
|
||||
spi_master_put(master);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int xtfpga_spi_remove(struct platform_device *pdev)
|
||||
|
|
|
|||
|
|
@ -753,7 +753,7 @@ struct spi_device *spi_new_device(struct spi_controller *ctlr,
|
|||
proxy->max_speed_hz = chip->max_speed_hz;
|
||||
proxy->mode = chip->mode;
|
||||
proxy->irq = chip->irq;
|
||||
strlcpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias));
|
||||
strscpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias));
|
||||
proxy->dev.platform_data = (void *) chip->platform_data;
|
||||
proxy->controller_data = chip->controller_data;
|
||||
proxy->controller_state = NULL;
|
||||
|
|
@ -1010,9 +1010,9 @@ static void spi_set_cs(struct spi_device *spi, bool enable, bool force)
|
|||
}
|
||||
|
||||
#ifdef CONFIG_HAS_DMA
|
||||
int spi_map_buf(struct spi_controller *ctlr, struct device *dev,
|
||||
struct sg_table *sgt, void *buf, size_t len,
|
||||
enum dma_data_direction dir)
|
||||
static int spi_map_buf_attrs(struct spi_controller *ctlr, struct device *dev,
|
||||
struct sg_table *sgt, void *buf, size_t len,
|
||||
enum dma_data_direction dir, unsigned long attrs)
|
||||
{
|
||||
const bool vmalloced_buf = is_vmalloc_addr(buf);
|
||||
unsigned int max_seg_size = dma_get_max_seg_size(dev);
|
||||
|
|
@ -1078,26 +1078,39 @@ int spi_map_buf(struct spi_controller *ctlr, struct device *dev,
|
|||
sg = sg_next(sg);
|
||||
}
|
||||
|
||||
ret = dma_map_sg(dev, sgt->sgl, sgt->nents, dir);
|
||||
if (!ret)
|
||||
ret = -ENOMEM;
|
||||
ret = dma_map_sgtable(dev, sgt, dir, attrs);
|
||||
if (ret < 0) {
|
||||
sg_free_table(sgt);
|
||||
return ret;
|
||||
}
|
||||
|
||||
sgt->nents = ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int spi_map_buf(struct spi_controller *ctlr, struct device *dev,
|
||||
struct sg_table *sgt, void *buf, size_t len,
|
||||
enum dma_data_direction dir)
|
||||
{
|
||||
return spi_map_buf_attrs(ctlr, dev, sgt, buf, len, dir, 0);
|
||||
}
|
||||
|
||||
static void spi_unmap_buf_attrs(struct spi_controller *ctlr,
|
||||
struct device *dev, struct sg_table *sgt,
|
||||
enum dma_data_direction dir,
|
||||
unsigned long attrs)
|
||||
{
|
||||
if (sgt->orig_nents) {
|
||||
dma_unmap_sgtable(dev, sgt, dir, attrs);
|
||||
sg_free_table(sgt);
|
||||
sgt->orig_nents = 0;
|
||||
sgt->nents = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void spi_unmap_buf(struct spi_controller *ctlr, struct device *dev,
|
||||
struct sg_table *sgt, enum dma_data_direction dir)
|
||||
{
|
||||
if (sgt->orig_nents) {
|
||||
dma_unmap_sg(dev, sgt->sgl, sgt->orig_nents, dir);
|
||||
sg_free_table(sgt);
|
||||
}
|
||||
spi_unmap_buf_attrs(ctlr, dev, sgt, dir, 0);
|
||||
}
|
||||
|
||||
static int __spi_map_msg(struct spi_controller *ctlr, struct spi_message *msg)
|
||||
|
|
@ -1124,29 +1137,37 @@ static int __spi_map_msg(struct spi_controller *ctlr, struct spi_message *msg)
|
|||
rx_dev = ctlr->dev.parent;
|
||||
|
||||
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
||||
/* The sync is done before each transfer. */
|
||||
unsigned long attrs = DMA_ATTR_SKIP_CPU_SYNC;
|
||||
|
||||
if (!ctlr->can_dma(ctlr, msg->spi, xfer))
|
||||
continue;
|
||||
|
||||
if (xfer->tx_buf != NULL) {
|
||||
ret = spi_map_buf(ctlr, tx_dev, &xfer->tx_sg,
|
||||
(void *)xfer->tx_buf, xfer->len,
|
||||
DMA_TO_DEVICE);
|
||||
ret = spi_map_buf_attrs(ctlr, tx_dev, &xfer->tx_sg,
|
||||
(void *)xfer->tx_buf,
|
||||
xfer->len, DMA_TO_DEVICE,
|
||||
attrs);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (xfer->rx_buf != NULL) {
|
||||
ret = spi_map_buf(ctlr, rx_dev, &xfer->rx_sg,
|
||||
xfer->rx_buf, xfer->len,
|
||||
DMA_FROM_DEVICE);
|
||||
ret = spi_map_buf_attrs(ctlr, rx_dev, &xfer->rx_sg,
|
||||
xfer->rx_buf, xfer->len,
|
||||
DMA_FROM_DEVICE, attrs);
|
||||
if (ret != 0) {
|
||||
spi_unmap_buf(ctlr, tx_dev, &xfer->tx_sg,
|
||||
DMA_TO_DEVICE);
|
||||
spi_unmap_buf_attrs(ctlr, tx_dev,
|
||||
&xfer->tx_sg, DMA_TO_DEVICE,
|
||||
attrs);
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctlr->cur_rx_dma_dev = rx_dev;
|
||||
ctlr->cur_tx_dma_dev = tx_dev;
|
||||
ctlr->cur_msg_mapped = true;
|
||||
|
||||
return 0;
|
||||
|
|
@ -1154,38 +1175,60 @@ static int __spi_map_msg(struct spi_controller *ctlr, struct spi_message *msg)
|
|||
|
||||
static int __spi_unmap_msg(struct spi_controller *ctlr, struct spi_message *msg)
|
||||
{
|
||||
struct device *rx_dev = ctlr->cur_rx_dma_dev;
|
||||
struct device *tx_dev = ctlr->cur_tx_dma_dev;
|
||||
struct spi_transfer *xfer;
|
||||
struct device *tx_dev, *rx_dev;
|
||||
|
||||
if (!ctlr->cur_msg_mapped || !ctlr->can_dma)
|
||||
return 0;
|
||||
|
||||
if (ctlr->dma_tx)
|
||||
tx_dev = ctlr->dma_tx->device->dev;
|
||||
else if (ctlr->dma_map_dev)
|
||||
tx_dev = ctlr->dma_map_dev;
|
||||
else
|
||||
tx_dev = ctlr->dev.parent;
|
||||
|
||||
if (ctlr->dma_rx)
|
||||
rx_dev = ctlr->dma_rx->device->dev;
|
||||
else if (ctlr->dma_map_dev)
|
||||
rx_dev = ctlr->dma_map_dev;
|
||||
else
|
||||
rx_dev = ctlr->dev.parent;
|
||||
|
||||
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
||||
/* The sync has already been done after each transfer. */
|
||||
unsigned long attrs = DMA_ATTR_SKIP_CPU_SYNC;
|
||||
|
||||
if (!ctlr->can_dma(ctlr, msg->spi, xfer))
|
||||
continue;
|
||||
|
||||
spi_unmap_buf(ctlr, rx_dev, &xfer->rx_sg, DMA_FROM_DEVICE);
|
||||
spi_unmap_buf(ctlr, tx_dev, &xfer->tx_sg, DMA_TO_DEVICE);
|
||||
spi_unmap_buf_attrs(ctlr, rx_dev, &xfer->rx_sg,
|
||||
DMA_FROM_DEVICE, attrs);
|
||||
spi_unmap_buf_attrs(ctlr, tx_dev, &xfer->tx_sg,
|
||||
DMA_TO_DEVICE, attrs);
|
||||
}
|
||||
|
||||
ctlr->cur_msg_mapped = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void spi_dma_sync_for_device(struct spi_controller *ctlr,
|
||||
struct spi_transfer *xfer)
|
||||
{
|
||||
struct device *rx_dev = ctlr->cur_rx_dma_dev;
|
||||
struct device *tx_dev = ctlr->cur_tx_dma_dev;
|
||||
|
||||
if (!ctlr->cur_msg_mapped)
|
||||
return;
|
||||
|
||||
if (xfer->tx_sg.orig_nents)
|
||||
dma_sync_sgtable_for_device(tx_dev, &xfer->tx_sg, DMA_TO_DEVICE);
|
||||
if (xfer->rx_sg.orig_nents)
|
||||
dma_sync_sgtable_for_device(rx_dev, &xfer->rx_sg, DMA_FROM_DEVICE);
|
||||
}
|
||||
|
||||
static void spi_dma_sync_for_cpu(struct spi_controller *ctlr,
|
||||
struct spi_transfer *xfer)
|
||||
{
|
||||
struct device *rx_dev = ctlr->cur_rx_dma_dev;
|
||||
struct device *tx_dev = ctlr->cur_tx_dma_dev;
|
||||
|
||||
if (!ctlr->cur_msg_mapped)
|
||||
return;
|
||||
|
||||
if (xfer->rx_sg.orig_nents)
|
||||
dma_sync_sgtable_for_cpu(rx_dev, &xfer->rx_sg, DMA_FROM_DEVICE);
|
||||
if (xfer->tx_sg.orig_nents)
|
||||
dma_sync_sgtable_for_cpu(tx_dev, &xfer->tx_sg, DMA_TO_DEVICE);
|
||||
}
|
||||
#else /* !CONFIG_HAS_DMA */
|
||||
static inline int __spi_map_msg(struct spi_controller *ctlr,
|
||||
struct spi_message *msg)
|
||||
|
|
@ -1198,6 +1241,16 @@ static inline int __spi_unmap_msg(struct spi_controller *ctlr,
|
|||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void spi_dma_sync_for_device(struct spi_controller *ctrl,
|
||||
struct spi_transfer *xfer)
|
||||
{
|
||||
}
|
||||
|
||||
static void spi_dma_sync_for_cpu(struct spi_controller *ctrl,
|
||||
struct spi_transfer *xfer)
|
||||
{
|
||||
}
|
||||
#endif /* !CONFIG_HAS_DMA */
|
||||
|
||||
static inline int spi_unmap_msg(struct spi_controller *ctlr,
|
||||
|
|
@ -1435,7 +1488,8 @@ static int spi_transfer_one_message(struct spi_controller *ctlr,
|
|||
struct spi_statistics __percpu *statm = ctlr->pcpu_statistics;
|
||||
struct spi_statistics __percpu *stats = msg->spi->pcpu_statistics;
|
||||
|
||||
spi_set_cs(msg->spi, true, false);
|
||||
xfer = list_first_entry(&msg->transfers, struct spi_transfer, transfer_list);
|
||||
spi_set_cs(msg->spi, !xfer->cs_off, false);
|
||||
|
||||
SPI_STATISTICS_INCREMENT_FIELD(statm, messages);
|
||||
SPI_STATISTICS_INCREMENT_FIELD(stats, messages);
|
||||
|
|
@ -1455,8 +1509,11 @@ static int spi_transfer_one_message(struct spi_controller *ctlr,
|
|||
reinit_completion(&ctlr->xfer_completion);
|
||||
|
||||
fallback_pio:
|
||||
spi_dma_sync_for_device(ctlr, xfer);
|
||||
ret = ctlr->transfer_one(ctlr, msg->spi, xfer);
|
||||
if (ret < 0) {
|
||||
spi_dma_sync_for_cpu(ctlr, xfer);
|
||||
|
||||
if (ctlr->cur_msg_mapped &&
|
||||
(xfer->error & SPI_TRANS_FAIL_NO_START)) {
|
||||
__spi_unmap_msg(ctlr, msg);
|
||||
|
|
@ -1479,6 +1536,8 @@ static int spi_transfer_one_message(struct spi_controller *ctlr,
|
|||
if (ret < 0)
|
||||
msg->status = ret;
|
||||
}
|
||||
|
||||
spi_dma_sync_for_cpu(ctlr, xfer);
|
||||
} else {
|
||||
if (xfer->len)
|
||||
dev_err(&msg->spi->dev,
|
||||
|
|
@ -1503,10 +1562,15 @@ static int spi_transfer_one_message(struct spi_controller *ctlr,
|
|||
&msg->transfers)) {
|
||||
keep_cs = true;
|
||||
} else {
|
||||
spi_set_cs(msg->spi, false, false);
|
||||
if (!xfer->cs_off)
|
||||
spi_set_cs(msg->spi, false, false);
|
||||
_spi_transfer_cs_change_delay(msg, xfer);
|
||||
spi_set_cs(msg->spi, true, false);
|
||||
if (!list_next_entry(xfer, transfer_list)->cs_off)
|
||||
spi_set_cs(msg->spi, true, false);
|
||||
}
|
||||
} else if (!list_is_last(&xfer->transfer_list, &msg->transfers) &&
|
||||
xfer->cs_off != list_next_entry(xfer, transfer_list)->cs_off) {
|
||||
spi_set_cs(msg->spi, xfer->cs_off, false);
|
||||
}
|
||||
|
||||
msg->actual_length += xfer->len;
|
||||
|
|
@ -1587,6 +1651,15 @@ static int __spi_pump_transfer_message(struct spi_controller *ctlr,
|
|||
|
||||
trace_spi_message_start(msg);
|
||||
|
||||
ret = spi_split_transfers_maxsize(ctlr, msg,
|
||||
spi_max_transfer_size(msg->spi),
|
||||
GFP_KERNEL | GFP_DMA);
|
||||
if (ret) {
|
||||
msg->status = ret;
|
||||
spi_finalize_current_message(ctlr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (ctlr->prepare_message) {
|
||||
ret = ctlr->prepare_message(ctlr, msg);
|
||||
if (ret) {
|
||||
|
|
@ -2329,7 +2402,7 @@ struct spi_device *spi_new_ancillary_device(struct spi_device *spi,
|
|||
goto err_out;
|
||||
}
|
||||
|
||||
strlcpy(ancillary->modalias, "dummy", sizeof(ancillary->modalias));
|
||||
strscpy(ancillary->modalias, "dummy", sizeof(ancillary->modalias));
|
||||
|
||||
/* Use provided chip-select for ancillary device */
|
||||
ancillary->chip_select = chip_select;
|
||||
|
|
@ -2725,7 +2798,7 @@ static ssize_t slave_store(struct device *dev, struct device_attribute *attr,
|
|||
if (!spi)
|
||||
return -ENOMEM;
|
||||
|
||||
strlcpy(spi->modalias, name, sizeof(spi->modalias));
|
||||
strscpy(spi->modalias, name, sizeof(spi->modalias));
|
||||
|
||||
rc = spi_add_device(spi);
|
||||
if (rc) {
|
||||
|
|
|
|||
|
|
@ -476,7 +476,7 @@ struct mmc_host {
|
|||
|
||||
unsigned int sdio_irqs;
|
||||
struct task_struct *sdio_irq_thread;
|
||||
struct delayed_work sdio_irq_work;
|
||||
struct work_struct sdio_irq_work;
|
||||
bool sdio_irq_pending;
|
||||
atomic_t sdio_irq_thread_abort;
|
||||
|
||||
|
|
|
|||
|
|
@ -378,6 +378,8 @@ extern struct spi_device *spi_new_ancillary_device(struct spi_device *spi, u8 ch
|
|||
* @cleanup: frees controller-specific state
|
||||
* @can_dma: determine whether this controller supports DMA
|
||||
* @dma_map_dev: device which can be used for DMA mapping
|
||||
* @cur_rx_dma_dev: device which is currently used for RX DMA mapping
|
||||
* @cur_tx_dma_dev: device which is currently used for TX DMA mapping
|
||||
* @queued: whether this controller is providing an internal message queue
|
||||
* @kworker: pointer to thread struct for message pump
|
||||
* @pump_messages: work struct for scheduling work to the message pump
|
||||
|
|
@ -610,6 +612,8 @@ struct spi_controller {
|
|||
struct spi_device *spi,
|
||||
struct spi_transfer *xfer);
|
||||
struct device *dma_map_dev;
|
||||
struct device *cur_rx_dma_dev;
|
||||
struct device *cur_tx_dma_dev;
|
||||
|
||||
/*
|
||||
* These hooks are for drivers that want to use the generic
|
||||
|
|
@ -848,6 +852,7 @@ struct spi_res {
|
|||
* @bits_per_word: select a bits_per_word other than the device default
|
||||
* for this transfer. If 0 the default (from @spi_device) is used.
|
||||
* @dummy_data: indicates transfer is dummy bytes transfer.
|
||||
* @cs_off: performs the transfer with chipselect off.
|
||||
* @cs_change: affects chipselect after this transfer completes
|
||||
* @cs_change_delay: delay between cs deassert and assert when
|
||||
* @cs_change is set and @spi_transfer is not the last in @spi_message
|
||||
|
|
@ -958,6 +963,7 @@ struct spi_transfer {
|
|||
struct sg_table rx_sg;
|
||||
|
||||
unsigned dummy_data:1;
|
||||
unsigned cs_off:1;
|
||||
unsigned cs_change:1;
|
||||
unsigned tx_nbits:3;
|
||||
unsigned rx_nbits:3;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user