From patchwork Mon Jan 22 22:51:12 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ben Whitten X-Patchwork-Id: 10179421 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 190FD600F5 for ; Mon, 22 Jan 2018 22:52:02 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 08A6B285AF for ; Mon, 22 Jan 2018 22:52:02 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id F0D1D285B5; Mon, 22 Jan 2018 22:52:01 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.8 required=2.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED,FREEMAIL_FROM,RCVD_IN_DNSWL_HI,T_DKIM_INVALID autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 66E94285AF for ; Mon, 22 Jan 2018 22:52:01 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751049AbeAVWvs (ORCPT ); Mon, 22 Jan 2018 17:51:48 -0500 Received: from mail-wm0-f65.google.com ([74.125.82.65]:41915 "EHLO mail-wm0-f65.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751059AbeAVWvq (ORCPT ); Mon, 22 Jan 2018 17:51:46 -0500 Received: by mail-wm0-f65.google.com with SMTP id f71so19275012wmf.0; Mon, 22 Jan 2018 14:51:45 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references; bh=f79WZiMNTI1ebtDfHkCgYTybUO0AgwDMqgqRO01Bspw=; b=VQkZPuLv/tcA6CoToW5KOsXe5XZQBAkflErZGz94YZ5C3Aw0y2TNBXPiIkXSdyBV+d 1+0p9WRb3gpzlkAhnYPDd9rugY8geG0q0LMZDXWDYFwdCLrhVyDnzV3WyLNheaacaT7H +Yjh9sYFBJ2MO90T0oZMgIW+zSuRC55ZP8wdVofUxYdBenqY04vY+KTu70Qv+lf/Gp/1 GdPvAGDogOayVJvyk3yBupgFDrQU+xoGmLs9pZwwpDDBJpBBmLGGakLCuRRGBgKZLAE/ G0Xn7sSpJ/rYzSdBB7M/ZFzqZh9JKOoPOCEkOJQxveCX2eZ9X4nGn+9G3fBqYzLsSCgs ETqA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:from:to:cc:subject:date:message-id :in-reply-to:references; bh=f79WZiMNTI1ebtDfHkCgYTybUO0AgwDMqgqRO01Bspw=; b=WeTWVq054z4gKVRujkSBgGYuqaGwaQjazcz2zVpyQ2e2DpL37ZU0A+JmtOhQZK/2qN 6Jq2B1OtyTIiFZlQ8CoAx8tQVU/YgDhGszZQhj6H9XrLHpqgLs2Ss3tnMyjqfJIM5t6x gCFpnCNgI94QveQOW0SDpemNSeWWJ9m2Cdr1OH/ktVFYIw4Ukic5+FA5ZweXr2urqd/S MnP6mMK3i3XQ1cQoLGgZF85H/nsw7k28FD/sbeXN10+XZcXk1bJ7/EMjcuSsiqMHN82g yiOeODpsxRN5kCnVhEa4bhF03kAvaOKT/2hkVgKejpaej2a/3QGRMYhsdAFcrjm+xBy+ /Lxg== X-Gm-Message-State: AKwxytej63XOsIHMBUvVVGU2HRWSn5KG/cUa5aKYYAMbtoNPwW63gPYx 0RhParWN7BneHoi2r7oQOTc= X-Google-Smtp-Source: AH8x224xw7WekfHshdb0VjlOPzStQSto/ujq5HMLJJQrGCX8Vp/uSIarEWKHpMRSGECysISK2HCLVQ== X-Received: by 10.28.19.210 with SMTP id 201mr237159wmt.79.1516661504713; Mon, 22 Jan 2018 14:51:44 -0800 (PST) Received: from Lappy.lan ([185.180.15.220]) by smtp.gmail.com with ESMTPSA id f5sm12893179wrh.28.2018.01.22.14.51.43 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 22 Jan 2018 14:51:43 -0800 (PST) From: Ben Whitten To: broonie@kernel.org Cc: linux-spi@vger.kernel.org, linux-kernel@vger.kernel.org, Ben Whitten Subject: [RFC] spi: add spi multiplexing functions for dt Date: Mon, 22 Jan 2018 22:51:12 +0000 Message-Id: <1516661472-16452-2-git-send-email-ben.whitten@gmail.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1516661472-16452-1-git-send-email-ben.whitten@gmail.com> References: <1516661472-16452-1-git-send-email-ben.whitten@gmail.com> Sender: linux-spi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-spi@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Like I2C busses SPI devices can also sit behind multiplexers. This patch adds is based off the I2C implementation and allows description in the devicetree. Signed-off-by: Ben Whitten --- drivers/spi/Kconfig | 10 +++ drivers/spi/Makefile | 3 + drivers/spi/spi-mux.c | 181 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/spi-mux.h | 55 +++++++++++++++ 4 files changed, 249 insertions(+) create mode 100644 drivers/spi/spi-mux.c create mode 100644 include/linux/spi-mux.h diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index a75f2a2..58eba70 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -51,6 +51,16 @@ config SPI_MASTER if SPI_MASTER +config SPI_MUX + tristate "SPI bus multiplexing support" + help + Say Y here if you want the SPI core to support the ability to + handle multiplexed SPI bus topologies, by presenting each + multiplexed segment as an SPI controller. + + This support is also available as a module. If so, the module + will be called spi-mux. + comment "SPI Master Controller Drivers" config SPI_ALTERA diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 8e0cda7..ef525fe 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -109,6 +109,9 @@ obj-$(CONFIG_SPI_XLP) += spi-xlp.o obj-$(CONFIG_SPI_XTENSA_XTFPGA) += spi-xtensa-xtfpga.o obj-$(CONFIG_SPI_ZYNQMP_GQSPI) += spi-zynqmp-gqspi.o +# SPI muxs +obj-$(CONFIG_SPI_MUX) += spi-mux.o + # SPI slave protocol handlers obj-$(CONFIG_SPI_SLAVE_TIME) += spi-slave-time.o obj-$(CONFIG_SPI_SLAVE_SYSTEM_CONTROL) += spi-slave-system-control.o diff --git a/drivers/spi/spi-mux.c b/drivers/spi/spi-mux.c new file mode 100644 index 0000000..a2008c1 --- /dev/null +++ b/drivers/spi/spi-mux.c @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for an SPI multiplexer + * + * Copyright (c) 2018 Ben Whitten + */ + +#include +#include +#include +#include +#include + +struct spi_mux_priv { + struct spi_controller *controller; + struct spi_mux_core *muxc; + u32 chan_id; +}; + +struct spi_mux_core *spi_mux_alloc(struct spi_controller *parent, + struct device *dev, + int max_controllers, + int sizeof_priv, + int (*select)(struct spi_mux_core *, u32), + int (*deselect)(struct spi_mux_core *, u32), + int (*transfer_one_message) + (struct spi_controller *controller, + struct spi_message *msg)) +{ + struct spi_mux_core *muxc; + + muxc = devm_kzalloc(dev, sizeof(*muxc) + + max_controllers * sizeof(muxc->controller[0]) + + sizeof_priv, GFP_KERNEL); + if (!muxc) + return NULL; + if (sizeof_priv) + muxc->priv = &muxc->controller[max_controllers]; + + muxc->parent = parent; + muxc->dev = dev; + + muxc->select = select; + muxc->deselect = deselect; + muxc->transfer_one_message = transfer_one_message; + muxc->max_controllers = max_controllers; + + return muxc; +} +EXPORT_SYMBOL_GPL(spi_mux_alloc); + +u32 spi_mux_get_chan_id(struct spi_controller *controller) +{ + struct spi_mux_priv *priv = spi_controller_get_devdata(controller); + + return priv->chan_id; +} +EXPORT_SYMBOL_GPL(spi_mux_get_chan_id); + +static int spi_mux_transfer_one_message(struct spi_controller *controller, + struct spi_message *msg) +{ + struct spi_mux_priv *priv = spi_controller_get_devdata(controller); + struct spi_mux_core *muxc = priv->muxc; + struct spi_device *spi = to_spi_device(muxc->dev); + int ret; + + ret = muxc->select(muxc, priv->chan_id); + if (ret < 0) + return ret; + + /* If we have a custom transfer, use it */ + if (muxc->transfer_one_message) + ret = muxc->transfer_one_message(controller, msg); + else + ret = spi_sync(spi, msg); + + if (muxc->deselect) + muxc->deselect(muxc, priv->chan_id); + + return ret; +} + +static int spi_mux_add_controller(struct spi_mux_core *muxc, u32 chan_id) +{ + struct spi_controller *controller; + struct spi_mux_priv *priv; + int ret; + + if (muxc->num_controllers >= muxc->max_controllers) { + dev_err(muxc->dev, "No room for more spi-mux controllers"); + return -EINVAL; + } + + controller = spi_alloc_master(muxc->dev, sizeof(*priv)); + if (!controller) + return -ENOMEM; + priv = spi_controller_get_devdata(controller); + + /* Setup private controller data */ + priv->muxc = muxc; + priv->controller = controller; + priv->chan_id = chan_id; + + priv->controller->transfer_one_message = spi_mux_transfer_one_message; + + /* Look for the child of this controller */ + if (muxc->dev->of_node) { + struct device_node *dev_node = muxc->dev->of_node; + struct device_node *mux_node, *child = NULL; + u32 reg; + + mux_node = of_get_child_by_name(dev_node, "spi-mux"); + if (!mux_node) + mux_node = of_node_get(dev_node); + + for_each_child_of_node(mux_node, child) { + ret = of_property_read_u32(child, "reg", ®); + if (ret) + continue; + if (chan_id == reg) + break; + } + + priv->controller->dev.of_node = child; + of_node_put(mux_node); + } + + ret = devm_spi_register_controller(muxc->dev, priv->controller); + if (ret) { + spi_controller_put(priv->controller); + dev_err(muxc->dev, "Problem registering spi controller: %d\n", + ret); + return ret; + } + + muxc->controller[muxc->num_controllers++] = priv->controller; + + return ret; +} + +static void spi_mux_del_controllers(struct spi_mux_core *muxc) +{ + struct spi_controller *controller = + muxc->controller[--muxc->num_controllers]; + struct device_node *np = controller->dev.of_node; + + muxc->controller[muxc->num_controllers] = NULL; + of_node_put(np); +} + +static void devm_spi_mux_del_controllers(struct device *dev, void *res) +{ + spi_mux_del_controllers(*(struct spi_mux_core **)res); +} + +int devm_spi_mux_add_controller(struct spi_mux_core *muxc, u32 chan_id) +{ + struct spi_mux_core **ptr; + int ret; + + ptr = devres_alloc(devm_spi_mux_del_controllers, sizeof(*ptr), + GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + ret = spi_mux_add_controller(muxc, chan_id); + if (!ret) { + *ptr = muxc; + devres_add(muxc->dev, ptr); + } else { + devres_free(ptr); + } + + return ret; +} +EXPORT_SYMBOL_GPL(devm_spi_mux_add_controller); + +MODULE_AUTHOR("Ben Whitten "); +MODULE_DESCRIPTION("SPI driver for multiplexed SPI busses"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/spi-mux.h b/include/linux/spi-mux.h new file mode 100644 index 0000000..5978f86 --- /dev/null +++ b/include/linux/spi-mux.h @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for an SPI multiplexer + * + * Copyright (c) 2018 Ben Whitten + */ + +#ifndef _LINUX_SPI_MUX_H_ +#define _LINUX_SPI_MUX_H_ + +#ifdef __KERNEL__ + +struct spi_mux_core { + struct spi_controller *parent; + struct device *dev; + + void *priv; + + int (*select)(struct spi_mux_core *, u32 chan_id); + int (*deselect)(struct spi_mux_core *, u32 chan_id); + int (*transfer_one_message)(struct spi_controller *controller, + struct spi_message *msg); + + int num_controllers; + int max_controllers; + struct spi_controller *controller[0]; +}; + +struct spi_mux_core *spi_mux_alloc(struct spi_controller *parent, + struct device *dev, + int max_controllers, + int sizeof_priv, + int (*select)(struct spi_mux_core *, u32), + int (*deselect)(struct spi_mux_core *, u32), + int (*transfer_one_message) + (struct spi_controller *controller, + struct spi_message *msg)); + +static inline void *spi_mux_priv(struct spi_mux_core *muxc) +{ + return muxc->priv; +} + +u32 spi_mux_get_chan_id(struct spi_controller *controller); + +/* + * Called to create an spi bus on a multiplexed bus segment. + * The chan_id parameter is passed to the select and deselect + * callback functions to perform hardware-specific mux control. + */ +int devm_spi_mux_add_controller(struct spi_mux_core *muxc, u32 chan_id); + +#endif /* __KERNEL__ */ + +#endif /* _LINUX_SPI_MUX_H_ */