summaryrefslogtreecommitdiff
path: root/drivers/spi
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2024-07-15 18:03:43 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2024-07-15 18:03:43 -0700
commite23dd95cfd063632fb212390740940f2761e322d (patch)
tree45c543aa5bf9926db9b33391351205e54d03bd2d /drivers/spi
parent584aeccc0b717f447505cc738d8c2f292d9d1a66 (diff)
parent3048dc8ba46b7ba11581f2a7e06849af0df13136 (diff)
downloadlinux-e23dd95cfd063632fb212390740940f2761e322d.tar.gz
linux-e23dd95cfd063632fb212390740940f2761e322d.tar.bz2
linux-e23dd95cfd063632fb212390740940f2761e322d.zip
Merge tag 'spi-v6.11' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi
Pull spi updates from Mark Brown: "There's some quite exciting core work in this release, we've got the beginnings of support for hardware initiated transfers which is itself independently useful for optimising fast paths in existing drivers. We also have a rework of the DMA mapping which allows finer grained decisions about DMA mapping messages and also helps remove some bodges that we'd had. Otherwise it's a fairly quiet release, a few new drivers and features for existing drivers, together with various cleanups and DT binding conversions. One regmap SPI fix made it's way in here too which I should probably have sent as a regmap fix instead. Summary: - Support for pre-optimising messages, reducing the overhead for messages that are repeatedly used (eg, reading the interrupt status from a device). This will also be used for hardware initiated transfers in future. - A reworking of how DMA mapping is done, introducing a new helper and allowing the DMA mapping decision to be done per transfer instead of per message. - Support for Atmel SAMA7D64, Freescale LX2160A DSPI and WCH CH341A" * tag 'spi-v6.11' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: (72 commits) spi: dt-bindings: at91: Add sama7d65 compatible string spi: add ch341a usb2spi driver spi: dt-bindings: fsl-dspi: add compatible string 'fsl,lx2160a-dspi' spi: dt-bindings: fsl-dspi: add dmas and dma-names properties spi: spi: Remove unnecessary ‘0’ values from status spi: spi: Remove unnecessary ‘0’ values from rc spi: xcomm: fix coding style spi: xcomm: remove i2c_set_clientdata() spi: xcomm: make use of devm_spi_alloc_host() spi: xcomm: add gpiochip support spi: dt-bindings: snps,dw-apb-ssi.yaml: update compatible property spi: dt-bindings: fsl-dspi: Convert to yaml format spi: fsl-dspi: use common proptery 'spi-cs-setup(hold)-delay-ns' spi: axi-spi-engine: remove platform_set_drvdata() spi: spi-fsl-lpspi: Pass pm_ptr() spi: spi-imx: Pass pm_ptr() spi: spi-fsl-lpspi: Switch to SYSTEM_SLEEP_PM_OPS() spi: spi-imx: Switch to RUNTIME_PM_OPS/SYSTEM_SLEEP_PM_OPS() spi: add EXPORT_SYMBOL_GPL(devm_spi_optimize_message) spi: add devm_spi_optimize_message() helper ...
Diffstat (limited to 'drivers/spi')
-rw-r--r--drivers/spi/Kconfig6
-rw-r--r--drivers/spi/Makefile4
-rw-r--r--drivers/spi/atmel-quadspi.c11
-rw-r--r--drivers/spi/internals.h8
-rw-r--r--drivers/spi/spi-altera-core.c1
-rw-r--r--drivers/spi/spi-axi-spi-engine.c42
-rw-r--r--drivers/spi/spi-bitbang.c73
-rw-r--r--drivers/spi/spi-cadence.c27
-rw-r--r--drivers/spi/spi-ch341.c241
-rw-r--r--drivers/spi/spi-cs42l43.c95
-rw-r--r--drivers/spi/spi-dw-bt1.c10
-rw-r--r--drivers/spi/spi-dw-core.c4
-rw-r--r--drivers/spi/spi-fsl-cpm.c1
-rw-r--r--drivers/spi/spi-fsl-dspi.c19
-rw-r--r--drivers/spi/spi-fsl-lib.c1
-rw-r--r--drivers/spi/spi-fsl-lpspi.c8
-rw-r--r--drivers/spi/spi-gpio.c66
-rw-r--r--drivers/spi/spi-imx.c20
-rw-r--r--drivers/spi/spi-ingenic.c4
-rw-r--r--drivers/spi/spi-meson-spicc.c22
-rw-r--r--drivers/spi/spi-microchip-core.c6
-rw-r--r--drivers/spi/spi-mxic.c2
-rw-r--r--drivers/spi/spi-omap-uwire.c1
-rw-r--r--drivers/spi/spi-omap2-mcspi.c9
-rw-r--r--drivers/spi/spi-pci1xxxx.c5
-rw-r--r--drivers/spi/spi-pxa2xx-pci.c39
-rw-r--r--drivers/spi/spi-pxa2xx-platform.c214
-rw-r--r--drivers/spi/spi-pxa2xx.c259
-rw-r--r--drivers/spi/spi-pxa2xx.h6
-rw-r--r--drivers/spi/spi-qup.c10
-rw-r--r--drivers/spi/spi-rpc-if.c12
-rw-r--r--drivers/spi/spi-wpcm-fiu.c6
-rw-r--r--drivers/spi/spi-xcomm.c75
-rw-r--r--drivers/spi/spi.c134
34 files changed, 911 insertions, 530 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index a2c99ff33e0a..ec1550c698d5 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -277,6 +277,12 @@ config SPI_CADENCE_XSPI
device with a Cadence XSPI controller and want to access the
Flash as an MTD device.
+config SPI_CH341
+ tristate "CH341 USB2SPI adapter"
+ depends on SPI_MASTER && USB
+ help
+ Enables the SPI controller on the CH341a USB to serial chip
+
config SPI_CLPS711X
tristate "CLPS711X host SPI controller"
depends on ARCH_CLPS711X || COMPILE_TEST
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index e694254dec04..a9b1bc259b68 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_SPI_BUTTERFLY) += spi-butterfly.o
obj-$(CONFIG_SPI_CADENCE) += spi-cadence.o
obj-$(CONFIG_SPI_CADENCE_QUADSPI) += spi-cadence-quadspi.o
obj-$(CONFIG_SPI_CADENCE_XSPI) += spi-cadence-xspi.o
+obj-$(CONFIG_SPI_CH341) += spi-ch341.o
obj-$(CONFIG_SPI_CLPS711X) += spi-clps711x.o
obj-$(CONFIG_SPI_COLDFIRE_QSPI) += spi-coldfire-qspi.o
obj-$(CONFIG_SPI_CS42L43) += spi-cs42l43.o
@@ -107,7 +108,8 @@ obj-$(CONFIG_SPI_PIC32) += spi-pic32.o
obj-$(CONFIG_SPI_PIC32_SQI) += spi-pic32-sqi.o
obj-$(CONFIG_SPI_PL022) += spi-pl022.o
obj-$(CONFIG_SPI_PPC4xx) += spi-ppc4xx.o
-spi-pxa2xx-platform-objs := spi-pxa2xx.o spi-pxa2xx-dma.o
+obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx-core.o
+spi-pxa2xx-core-y := spi-pxa2xx.o spi-pxa2xx-dma.o
obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx-platform.o
obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o
obj-$(CONFIG_SPI_QCOM_GENI) += spi-geni-qcom.o
diff --git a/drivers/spi/atmel-quadspi.c b/drivers/spi/atmel-quadspi.c
index 370c4d1572ed..5aaff3bee1b7 100644
--- a/drivers/spi/atmel-quadspi.c
+++ b/drivers/spi/atmel-quadspi.c
@@ -756,8 +756,15 @@ static int __maybe_unused atmel_qspi_resume(struct device *dev)
struct atmel_qspi *aq = spi_controller_get_devdata(ctrl);
int ret;
- clk_prepare(aq->pclk);
- clk_prepare(aq->qspick);
+ ret = clk_prepare(aq->pclk);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare(aq->qspick);
+ if (ret) {
+ clk_unprepare(aq->pclk);
+ return ret;
+ }
ret = pm_runtime_force_resume(dev);
if (ret < 0)
diff --git a/drivers/spi/internals.h b/drivers/spi/internals.h
index 4a28a8395552..1f459b895891 100644
--- a/drivers/spi/internals.h
+++ b/drivers/spi/internals.h
@@ -40,4 +40,12 @@ static inline void spi_unmap_buf(struct spi_controller *ctlr,
}
#endif /* CONFIG_HAS_DMA */
+static inline bool spi_xfer_is_dma_mapped(struct spi_controller *ctlr,
+ struct spi_device *spi,
+ struct spi_transfer *xfer)
+{
+ return ctlr->can_dma && ctlr->can_dma(ctlr, spi, xfer) &&
+ (xfer->tx_sg_mapped || xfer->rx_sg_mapped);
+}
+
#endif /* __LINUX_SPI_INTERNALS_H */
diff --git a/drivers/spi/spi-altera-core.c b/drivers/spi/spi-altera-core.c
index 87e37f48f196..7af097929116 100644
--- a/drivers/spi/spi-altera-core.c
+++ b/drivers/spi/spi-altera-core.c
@@ -219,4 +219,5 @@ void altera_spi_init_host(struct spi_controller *host)
}
EXPORT_SYMBOL_GPL(altera_spi_init_host);
+MODULE_DESCRIPTION("Altera SPI Controller driver core");
MODULE_LICENSE("GPL");
diff --git a/drivers/spi/spi-axi-spi-engine.c b/drivers/spi/spi-axi-spi-engine.c
index 96a524772549..447e5a962dee 100644
--- a/drivers/spi/spi-axi-spi-engine.c
+++ b/drivers/spi/spi-axi-spi-engine.c
@@ -46,6 +46,7 @@
#define SPI_ENGINE_INST_ASSERT 0x1
#define SPI_ENGINE_INST_WRITE 0x2
#define SPI_ENGINE_INST_MISC 0x3
+#define SPI_ENGINE_INST_CS_INV 0x4
#define SPI_ENGINE_CMD_REG_CLK_DIV 0x0
#define SPI_ENGINE_CMD_REG_CONFIG 0x1
@@ -73,6 +74,8 @@
SPI_ENGINE_CMD(SPI_ENGINE_INST_MISC, SPI_ENGINE_MISC_SLEEP, (delay))
#define SPI_ENGINE_CMD_SYNC(id) \
SPI_ENGINE_CMD(SPI_ENGINE_INST_MISC, SPI_ENGINE_MISC_SYNC, (id))
+#define SPI_ENGINE_CMD_CS_INV(flags) \
+ SPI_ENGINE_CMD(SPI_ENGINE_INST_CS_INV, 0, (flags))
struct spi_engine_program {
unsigned int length;
@@ -111,6 +114,8 @@ struct spi_engine {
struct spi_engine_message_state msg_state;
struct completion msg_complete;
unsigned int int_enable;
+ /* shadows hardware CS inversion flag state */
+ u8 cs_inv;
};
static void spi_engine_program_add_cmd(struct spi_engine_program *p,
@@ -540,6 +545,29 @@ static int spi_engine_unoptimize_message(struct spi_message *msg)
return 0;
}
+static int spi_engine_setup(struct spi_device *device)
+{
+ struct spi_controller *host = device->controller;
+ struct spi_engine *spi_engine = spi_controller_get_devdata(host);
+
+ if (device->mode & SPI_CS_HIGH)
+ spi_engine->cs_inv |= BIT(spi_get_chipselect(device, 0));
+ else
+ spi_engine->cs_inv &= ~BIT(spi_get_chipselect(device, 0));
+
+ writel_relaxed(SPI_ENGINE_CMD_CS_INV(spi_engine->cs_inv),
+ spi_engine->base + SPI_ENGINE_REG_CMD_FIFO);
+
+ /*
+ * In addition to setting the flags, we have to do a CS assert command
+ * to make the new setting actually take effect.
+ */
+ writel_relaxed(SPI_ENGINE_CMD_ASSERT(0, 0xff),
+ spi_engine->base + SPI_ENGINE_REG_CMD_FIFO);
+
+ return 0;
+}
+
static int spi_engine_transfer_one_message(struct spi_controller *host,
struct spi_message *msg)
{
@@ -663,16 +691,16 @@ static int spi_engine_probe(struct platform_device *pdev)
host->unoptimize_message = spi_engine_unoptimize_message;
host->num_chipselect = 8;
+ /* Some features depend of the IP core version. */
+ if (ADI_AXI_PCORE_VER_MINOR(version) >= 2) {
+ host->mode_bits |= SPI_CS_HIGH;
+ host->setup = spi_engine_setup;
+ }
+
if (host->max_speed_hz == 0)
return dev_err_probe(&pdev->dev, -EINVAL, "spi_clk rate is 0");
- ret = devm_spi_register_controller(&pdev->dev, host);
- if (ret)
- return ret;
-
- platform_set_drvdata(pdev, host);
-
- return 0;
+ return devm_spi_register_controller(&pdev->dev, host);
}
static const struct of_device_id spi_engine_match_table[] = {
diff --git a/drivers/spi/spi-bitbang.c b/drivers/spi/spi-bitbang.c
index ca5cc67555c5..afb1b1105ec2 100644
--- a/drivers/spi/spi-bitbang.c
+++ b/drivers/spi/spi-bitbang.c
@@ -38,33 +38,24 @@
* working quickly, or testing for differences that aren't speed related.
*/
+typedef unsigned int (*spi_bb_txrx_bufs_fn)(struct spi_device *, spi_bb_txrx_word_fn,
+ unsigned int, struct spi_transfer *,
+ unsigned int);
+
struct spi_bitbang_cs {
- unsigned nsecs; /* (clock cycle time)/2 */
- u32 (*txrx_word)(struct spi_device *spi, unsigned nsecs,
- u32 word, u8 bits, unsigned flags);
- unsigned (*txrx_bufs)(struct spi_device *,
- u32 (*txrx_word)(
- struct spi_device *spi,
- unsigned nsecs,
- u32 word, u8 bits,
- unsigned flags),
- unsigned, struct spi_transfer *,
- unsigned);
+ unsigned int nsecs; /* (clock cycle time) / 2 */
+ spi_bb_txrx_word_fn txrx_word;
+ spi_bb_txrx_bufs_fn txrx_bufs;
};
-static unsigned bitbang_txrx_8(
- struct spi_device *spi,
- u32 (*txrx_word)(struct spi_device *spi,
- unsigned nsecs,
- u32 word, u8 bits,
- unsigned flags),
- unsigned ns,
+static unsigned int bitbang_txrx_8(struct spi_device *spi,
+ spi_bb_txrx_word_fn txrx_word,
+ unsigned int ns,
struct spi_transfer *t,
- unsigned flags
-)
+ unsigned int flags)
{
- unsigned bits = t->bits_per_word;
- unsigned count = t->len;
+ unsigned int bits = t->bits_per_word;
+ unsigned int count = t->len;
const u8 *tx = t->tx_buf;
u8 *rx = t->rx_buf;
@@ -81,19 +72,14 @@ static unsigned bitbang_txrx_8(
return t->len - count;
}
-static unsigned bitbang_txrx_16(
- struct spi_device *spi,
- u32 (*txrx_word)(struct spi_device *spi,
- unsigned nsecs,
- u32 word, u8 bits,
- unsigned flags),
- unsigned ns,
+static unsigned int bitbang_txrx_16(struct spi_device *spi,
+ spi_bb_txrx_word_fn txrx_word,
+ unsigned int ns,
struct spi_transfer *t,
- unsigned flags
-)
+ unsigned int flags)
{
- unsigned bits = t->bits_per_word;
- unsigned count = t->len;
+ unsigned int bits = t->bits_per_word;
+ unsigned int count = t->len;
const u16 *tx = t->tx_buf;
u16 *rx = t->rx_buf;
@@ -110,19 +96,14 @@ static unsigned bitbang_txrx_16(
return t->len - count;
}
-static unsigned bitbang_txrx_32(
- struct spi_device *spi,
- u32 (*txrx_word)(struct spi_device *spi,
- unsigned nsecs,
- u32 word, u8 bits,
- unsigned flags),
- unsigned ns,
+static unsigned int bitbang_txrx_32(struct spi_device *spi,
+ spi_bb_txrx_word_fn txrx_word,
+ unsigned int ns,
struct spi_transfer *t,
- unsigned flags
-)
+ unsigned int flags)
{
- unsigned bits = t->bits_per_word;
- unsigned count = t->len;
+ unsigned int bits = t->bits_per_word;
+ unsigned int count = t->len;
const u32 *tx = t->tx_buf;
u32 *rx = t->rx_buf;
@@ -234,7 +215,7 @@ EXPORT_SYMBOL_GPL(spi_bitbang_cleanup);
static int spi_bitbang_bufs(struct spi_device *spi, struct spi_transfer *t)
{
struct spi_bitbang_cs *cs = spi->controller_state;
- unsigned nsecs = cs->nsecs;
+ unsigned int nsecs = cs->nsecs;
struct spi_bitbang *bitbang;
bitbang = spi_controller_get_devdata(spi->controller);
@@ -247,7 +228,7 @@ static int spi_bitbang_bufs(struct spi_device *spi, struct spi_transfer *t)
}
if (spi->mode & SPI_3WIRE) {
- unsigned flags;
+ unsigned int flags;
flags = t->tx_buf ? SPI_CONTROLLER_NO_RX : SPI_CONTROLLER_NO_TX;
return cs->txrx_bufs(spi, cs->txrx_word, nsecs, t, flags);
diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c
index e5140532071d..e07e081de5ea 100644
--- a/drivers/spi/spi-cadence.c
+++ b/drivers/spi/spi-cadence.c
@@ -18,6 +18,7 @@
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <linux/reset.h>
#include <linux/spi/spi.h>
/* Name of this driver */
@@ -111,6 +112,7 @@
* @dev_busy: Device busy flag
* @is_decoded_cs: Flag for decoder property set or not
* @tx_fifo_depth: Depth of the TX FIFO
+ * @rstc: Optional reset control for SPI controller
*/
struct cdns_spi {
void __iomem *regs;
@@ -125,6 +127,7 @@ struct cdns_spi {
u8 dev_busy;
u32 is_decoded_cs;
unsigned int tx_fifo_depth;
+ struct reset_control *rstc;
};
/* Macros for the SPI controller read/write */
@@ -588,14 +591,24 @@ static int cdns_spi_probe(struct platform_device *pdev)
goto remove_ctlr;
}
- if (!spi_controller_is_target(ctlr)) {
- xspi->ref_clk = devm_clk_get_enabled(&pdev->dev, "ref_clk");
- if (IS_ERR(xspi->ref_clk)) {
- dev_err(&pdev->dev, "ref_clk clock not found.\n");
- ret = PTR_ERR(xspi->ref_clk);
- goto remove_ctlr;
- }
+ xspi->rstc = devm_reset_control_get_optional_exclusive(&pdev->dev, "spi");
+ if (IS_ERR(xspi->rstc)) {
+ ret = dev_err_probe(&pdev->dev, PTR_ERR(xspi->rstc),
+ "Cannot get SPI reset.\n");
+ goto remove_ctlr;
+ }
+
+ reset_control_assert(xspi->rstc);
+ reset_control_deassert(xspi->rstc);
+ xspi->ref_clk = devm_clk_get_enabled(&pdev->dev, "ref_clk");
+ if (IS_ERR(xspi->ref_clk)) {
+ dev_err(&pdev->dev, "ref_clk clock not found.\n");
+ ret = PTR_ERR(xspi->ref_clk);
+ goto remove_ctlr;
+ }
+
+ if (!spi_controller_is_target(ctlr)) {
pm_runtime_use_autosuspend(&pdev->dev);
pm_runtime_set_autosuspend_delay(&pdev->dev, SPI_AUTOSUSPEND_TIMEOUT);
pm_runtime_get_noresume(&pdev->dev);
diff --git a/drivers/spi/spi-ch341.c b/drivers/spi/spi-ch341.c
new file mode 100644
index 000000000000..d2351812d310
--- /dev/null
+++ b/drivers/spi/spi-ch341.c
@@ -0,0 +1,241 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// QiHeng Electronics ch341a USB-to-SPI adapter driver
+//
+// Copyright (C) 2024 Johannes Thumshirn <jth@kernel.org>
+//
+// Based on ch341a_spi.c from the flashrom project.
+
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/spi/spi.h>
+
+#define CH341_PACKET_LENGTH 32
+#define CH341_DEFAULT_TIMEOUT 1000
+
+#define CH341A_CMD_UIO_STREAM 0xab
+
+#define CH341A_CMD_UIO_STM_END 0x20
+#define CH341A_CMD_UIO_STM_DIR 0x40
+#define CH341A_CMD_UIO_STM_OUT 0x80
+
+#define CH341A_CMD_I2C_STREAM 0xaa
+#define CH341A_CMD_I2C_STM_SET 0x60
+#define CH341A_CMD_I2C_STM_END 0x00
+
+#define CH341A_CMD_SPI_STREAM 0xa8
+
+#define CH341A_STM_I2C_100K 0x01
+
+struct ch341_spi_dev {
+ struct spi_controller *ctrl;
+ struct usb_device *udev;
+ unsigned int write_pipe;
+ unsigned int read_pipe;
+ int rx_len;
+ void *rx_buf;
+ u8 *tx_buf;
+ struct urb *rx_urb;
+ struct spi_device *spidev;
+};
+
+static void ch341_set_cs(struct spi_device *spi, bool is_high)
+{
+ struct ch341_spi_dev *ch341 =
+ spi_controller_get_devdata(spi->controller);
+ int err;
+
+ memset(ch341->tx_buf, 0, CH341_PACKET_LENGTH);
+ ch341->tx_buf[0] = CH341A_CMD_UIO_STREAM;
+ ch341->tx_buf[1] = CH341A_CMD_UIO_STM_OUT | (is_high ? 0x36 : 0x37);
+
+ if (is_high) {
+ ch341->tx_buf[2] = CH341A_CMD_UIO_STM_DIR | 0x3f;
+ ch341->tx_buf[3] = CH341A_CMD_UIO_STM_END;
+ } else {
+ ch341->tx_buf[2] = CH341A_CMD_UIO_STM_END;
+ }
+
+ err = usb_bulk_msg(ch341->udev, ch341->write_pipe, ch341->tx_buf,
+ (is_high ? 4 : 3), NULL, CH341_DEFAULT_TIMEOUT);
+ if (err)
+ dev_err(&spi->dev,
+ "error sending USB message for setting CS (%d)\n", err);
+}
+
+static int ch341_transfer_one(struct spi_controller *host,
+ struct spi_device *spi,
+ struct spi_transfer *trans)
+{
+ struct ch341_spi_dev *ch341 =
+ spi_controller_get_devdata(spi->controller);
+ int len;
+ int ret;
+
+ len = min(CH341_PACKET_LENGTH, trans->len + 1);
+
+ memset(ch341->tx_buf, 0, CH341_PACKET_LENGTH);
+
+ ch341->tx_buf[0] = CH341A_CMD_SPI_STREAM;
+
+ memcpy(ch341->tx_buf + 1, trans->tx_buf, len);
+
+ ret = usb_bulk_msg(ch341->udev, ch341->write_pipe, ch341->tx_buf, len,
+ NULL, CH341_DEFAULT_TIMEOUT);
+ if (ret)
+ return ret;
+
+ return usb_bulk_msg(ch341->udev, ch341->read_pipe, trans->rx_buf,
+ len - 1, NULL, CH341_DEFAULT_TIMEOUT);
+}
+
+static void ch341_recv(struct urb *urb)
+{
+ struct ch341_spi_dev *ch341 = urb->context;
+ struct usb_device *udev = ch341->udev;
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -ENOENT:
+ case -ECONNRESET:
+ case -EPIPE:
+ case -ESHUTDOWN:
+ dev_dbg(&udev->dev, "rx urb terminated with status: %d\n",
+ urb->status);
+ return;
+ default:
+ dev_dbg(&udev->dev, "rx urb error: %d\n", urb->status);
+ break;
+ }
+}
+
+static int ch341_config_stream(struct ch341_spi_dev *ch341)
+{
+ memset(ch341->tx_buf, 0, CH341_PACKET_LENGTH);
+ ch341->tx_buf[0] = CH341A_CMD_I2C_STREAM;
+ ch341->tx_buf[1] = CH341A_CMD_I2C_STM_SET | CH341A_STM_I2C_100K;
+ ch341->tx_buf[2] = CH341A_CMD_I2C_STM_END;
+
+ return usb_bulk_msg(ch341->udev, ch341->write_pipe, ch341->tx_buf, 3,
+ NULL, CH341_DEFAULT_TIMEOUT);
+}
+
+static int ch341_enable_pins(struct ch341_spi_dev *ch341, bool enable)
+{
+ memset(ch341->tx_buf, 0, CH341_PACKET_LENGTH);
+ ch341->tx_buf[0] = CH341A_CMD_UIO_STREAM;
+ ch341->tx_buf[1] = CH341A_CMD_UIO_STM_OUT | 0x37;
+ ch341->tx_buf[2] = CH341A_CMD_UIO_STM_DIR | (enable ? 0x3f : 0x00);
+ ch341->tx_buf[3] = CH341A_CMD_UIO_STM_END;
+
+ return usb_bulk_msg(ch341->udev, ch341->write_pipe, ch341->tx_buf, 4,
+ NULL, CH341_DEFAULT_TIMEOUT);
+}
+
+static struct spi_board_info chip = {
+ .modalias = "spi-ch341a",
+};
+
+static int ch341_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct usb_endpoint_descriptor *in, *out;
+ struct ch341_spi_dev *ch341;
+ struct spi_controller *ctrl;
+ int ret;
+
+ ret = usb_find_common_endpoints(intf->cur_altsetting, &in, &out, NULL,
+ NULL);
+ if (ret)
+ return ret;
+
+ ctrl = devm_spi_alloc_master(&udev->dev, sizeof(struct ch341_spi_dev));
+ if (!ctrl)
+ return -ENOMEM;
+
+ ch341 = spi_controller_get_devdata(ctrl);
+ ch341->ctrl = ctrl;
+ ch341->udev = udev;
+ ch341->write_pipe = usb_sndbulkpipe(udev, usb_endpoint_num(out));
+ ch341->read_pipe = usb_rcvbulkpipe(udev, usb_endpoint_num(in));
+
+ ch341->rx_len = usb_endpoint_maxp(in);
+ ch341->rx_buf = devm_kzalloc(&udev->dev, ch341->rx_len, GFP_KERNEL);
+ if (!ch341->rx_buf)
+ return -ENOMEM;
+
+ ch341->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!ch341->rx_urb)
+ return -ENOMEM;
+
+ ch341->tx_buf =
+ devm_kzalloc(&udev->dev, CH341_PACKET_LENGTH, GFP_KERNEL);
+ if (!ch341->tx_buf)
+ return -ENOMEM;
+
+ usb_fill_bulk_urb(ch341->rx_urb, udev, ch341->read_pipe, ch341->rx_buf,
+ ch341->rx_len, ch341_recv, ch341);
+
+ ret = usb_submit_urb(ch341->rx_urb, GFP_KERNEL);
+ if (ret) {
+ usb_free_urb(ch341->rx_urb);
+ return -ENOMEM;
+ }
+
+ ctrl->bus_num = -1;
+ ctrl->mode_bits = SPI_CPHA;
+ ctrl->transfer_one = ch341_transfer_one;
+ ctrl->set_cs = ch341_set_cs;
+ ctrl->auto_runtime_pm = false;
+
+ usb_set_intfdata(intf, ch341);
+
+ ret = ch341_config_stream(ch341);
+ if (ret)
+ return ret;
+
+ ret = ch341_enable_pins(ch341, true);
+ if (ret)
+ return ret;
+
+ ret = spi_register_controller(ctrl);
+ if (ret)
+ return ret;
+
+ ch341->spidev = spi_new_device(ctrl, &chip);
+ if (!ch341->spidev)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void ch341_disconnect(struct usb_interface *intf)
+{
+ struct ch341_spi_dev *ch341 = usb_get_intfdata(intf);
+
+ spi_unregister_device(ch341->spidev);
+ spi_unregister_controller(ch341->ctrl);
+ ch341_enable_pins(ch341, false);
+ usb_free_urb(ch341->rx_urb);
+}
+
+static const struct usb_device_id ch341_id_table[] = {
+ { USB_DEVICE(0x1a86, 0x5512) },
+ { }
+};
+MODULE_DEVICE_TABLE(usb, ch341_id_table);
+
+static struct usb_driver ch341a_usb_driver = {
+ .name = "spi-ch341",
+ .probe = ch341_probe,
+ .disconnect = ch341_disconnect,
+ .id_table = ch341_id_table,
+};
+module_usb_driver(ch341a_usb_driver);
+
+MODULE_AUTHOR("Johannes Thumshirn <jth@kernel.org>");
+MODULE_DESCRIPTION("QiHeng Electronics ch341 USB2SPI");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/spi/spi-cs42l43.c b/drivers/spi/spi-cs42l43.c
index 8b618ef0f711..5b8ed65f8094 100644
--- a/drivers/spi/spi-cs42l43.c
+++ b/drivers/spi/spi-cs42l43.c
@@ -9,6 +9,7 @@
#include <linux/array_size.h>
#include <linux/bits.h>
#include <linux/bitfield.h>
+#include <linux/cleanup.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/gpio/machine.h>
@@ -44,28 +45,10 @@ static const unsigned int cs42l43_clock_divs[] = {
2, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30
};
-static const struct software_node ampl = {
- .name = "cs35l56-left",
-};
-
-static const struct software_node ampr = {
- .name = "cs35l56-right",
-};
-
-static struct spi_board_info ampl_info = {
- .modalias = "cs35l56",
- .max_speed_hz = 11 * HZ_PER_MHZ,
- .chip_select = 0,
- .mode = SPI_MODE_0,
- .swnode = &ampl,
-};
-
-static struct spi_board_info ampr_info = {
+static struct spi_board_info amp_info_template = {
.modalias = "cs35l56",
.max_speed_hz = 11 * HZ_PER_MHZ,
- .chip_select = 1,
.mode = SPI_MODE_0,
- .swnode = &ampr,
};
static const struct software_node cs42l43_gpiochip_swnode = {
@@ -246,11 +229,10 @@ static size_t cs42l43_spi_max_length(struct spi_device *spi)
return CS42L43_SPI_MAX_LENGTH;
}
-static bool cs42l43_has_sidecar(struct fwnode_handle *fwnode)
+static struct fwnode_handle *cs42l43_find_xu_node(struct fwnode_handle *fwnode)
{
static const u32 func_smart_amp = 0x1;
struct fwnode_handle *child_fwnode, *ext_fwnode;
- unsigned int val;
u32 function;
int ret;
@@ -266,21 +248,45 @@ static bool cs42l43_has_sidecar(struct fwnode_handle *fwnode)
if (!ext_fwnode)
continue;
- ret = fwnode_property_read_u32(ext_fwnode,
- "01fa-sidecar-instances",
- &val);
+ fwnode_handle_put(child_fwnode);
- fwnode_handle_put(ext_fwnode);
+ return ext_fwnode;
+ }
- if (ret)
- continue;
+ return NULL;
+}
- fwnode_handle_put(child_fwnode);
+static struct spi_board_info *cs42l43_create_bridge_amp(struct cs42l43_spi *priv,
+ const char * const name,
+ int cs, int spkid)
+{
+ struct property_entry *props = NULL;
+ struct software_node *swnode;
+ struct spi_board_info *info;
- return !!val;
+ if (spkid >= 0) {
+ props = devm_kmalloc(priv->dev, sizeof(*props), GFP_KERNEL);
+ if (!props)
+ return NULL;
+
+ *props = PROPERTY_ENTRY_U32("cirrus,speaker-id", spkid);
}
- return false;
+ swnode = devm_kmalloc(priv->dev, sizeof(*swnode), GFP_KERNEL);
+ if (!swnode)
+ return NULL;
+
+ *swnode = SOFTWARE_NODE(name, props, NULL);
+
+ info = devm_kmemdup(priv->dev, &amp_info_template,
+ sizeof(amp_info_template), GFP_KERNEL);
+ if (!info)
+ return NULL;
+
+ info->chip_select = cs;
+ info->swnode = swnode;
+
+ return info;
}
static void cs42l43_release_of_node(void *data)
@@ -298,7 +304,8 @@ static int cs42l43_spi_probe(struct platform_device *pdev)
struct cs42l43 *cs42l43 = dev_get_drvdata(pdev->dev.parent);
struct cs42l43_spi *priv;
struct fwnode_handle *fwnode = dev_fwnode(cs42l43->dev);
- bool has_sidecar = cs42l43_has_sidecar(fwnode);
+ struct fwnode_handle *xu_fwnode __free(fwnode_handle) = cs42l43_find_xu_node(fwnode);
+ int nsidecars = 0;
int ret;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
@@ -350,7 +357,9 @@ static int cs42l43_spi_probe(struct platform_device *pdev)
return ret;
}
- if (has_sidecar) {
+ fwnode_property_read_u32(xu_fwnode, "01fa-sidecar-instances", &nsidecars);
+
+ if (nsidecars) {
ret = software_node_register(&cs42l43_gpiochip_swnode);
if (ret)
return dev_err_probe(priv->dev, ret,
@@ -373,12 +382,28 @@ static int cs42l43_spi_probe(struct platform_device *pdev)
return dev_err_probe(priv->dev, ret,
"Failed to register SPI controller\n");
- if (has_sidecar) {
- if (!spi_new_device(priv->ctlr, &ampl_info))
+ if (nsidecars) {
+ struct spi_board_info *ampl_info;
+ struct spi_board_info *ampr_info;
+ int spkid = -EINVAL;
+
+ fwnode_property_read_u32(xu_fwnode, "01fa-spk-id-val", &spkid);
+
+ dev_dbg(priv->dev, "Found speaker ID %d\n", spkid);
+
+ ampl_info = cs42l43_create_bridge_amp(priv, "cs35l56-left", 0, spkid);
+ if (!ampl_info)
+ return -ENOMEM;
+
+ ampr_info = cs42l43_create_bridge_amp(priv, "cs35l56-right", 1, spkid);
+ if (!ampr_info)
+ return -ENOMEM;
+
+ if (!spi_new_device(priv->ctlr, ampl_info))
return dev_err_probe(priv->dev, -ENODEV,
"Failed to create left amp slave\n");
- if (!spi_new_device(priv->ctlr, &ampr_info))
+ if (!spi_new_device(priv->ctlr, ampr_info))
return dev_err_probe(priv->dev, -ENODEV,
"Failed to create right amp slave\n");
}
diff --git a/drivers/spi/spi-dw-bt1.c