From patchwork Tue Aug 7 17:32:02 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ben Whitten X-Patchwork-Id: 954595 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="c1Ul996Z"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 41lM66330Wz9rvt for ; Wed, 8 Aug 2018 03:32:30 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2389702AbeHGTrv (ORCPT ); Tue, 7 Aug 2018 15:47:51 -0400 Received: from mail-wr1-f66.google.com ([209.85.221.66]:40453 "EHLO mail-wr1-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2389673AbeHGTrv (ORCPT ); Tue, 7 Aug 2018 15:47:51 -0400 Received: by mail-wr1-f66.google.com with SMTP id h15-v6so16533236wrs.7 for ; Tue, 07 Aug 2018 10:32:27 -0700 (PDT) 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=c1Ul996ZRyEIPHtMqwLtQyFXK7/w5qT4k2hZe9obCCwIMjeArPSaV9TUal2HR16Rfi ZZ84hEvXaehIFQ3+QBsyEPPZ9zkNwW5XYcxM5WlU4DLbSMPU27H2BWn0mbN7dAn5HhgB P9ej9m+XqNEN7iJQW6ynmx5/Gt5jqSyzz+Z7XrFpO9hyIDS8yRwibDCagTeE1O62aaLE 9DsIUOh5F4bdKkW+Li63C78WGe/B+Z1AiL8w3HvoTkeEqtLTk1AelrWLlOrN/slxEoN0 Js6ZyNcTdfm09Acpp1CffciTyVKwnWzARJevTV1q//8MMbpzArxgIIr53Evh0DZ/OFVW Zpqg== 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=fRcS5XWSHY2eW+JEd7TvvZZYYEg4gcOwSfifghvHNDF/RGJCNE7BfJopmHdRod9DrQ XmGuNl0JL7redBkAnOVurkLwC/gK/gEUatC4r8fNASugjr/x3cMhfRfwY4T+KuAyi1Uy YpNF6VCjHNGE6AGgwcUOrXDJZoAJxlE7nUTjsMRGVkvcMAcNi9u0du8F8HXW8sy2qZ43 8HqHFgR/dE4QPI7I7M/a0jkXjjvzQeWdqfwhnMAt5qZv87gMuFQYEkGypbHLjbi9sItv 1E8V/zhRFIDH59hZAQZHR1zBr82dcc95PjpMzfyagbGCLR2J/wrGdKRAYWl6IPcTmu4U CnCA== X-Gm-Message-State: AOUpUlG4ETiU4+P5s92gNEOgPW514SYYKMLTeSbgot5iNhHjMcXG7X1M iCtEHI7z+wVoRI0Zu9KgNsI= X-Google-Smtp-Source: AAOMgpddw6Q7cVxoV3OKFpH8XJVU62Ivb4K28uoZKE6VtmVuTh0h0UYGGmRM5d9YT8Fst9tr5P10qg== X-Received: by 2002:a5d:44c6:: with SMTP id z6-v6mr13113299wrr.236.1533663147061; Tue, 07 Aug 2018 10:32:27 -0700 (PDT) Received: from Lappy.lan (196.98.125.91.dyn.plus.net. [91.125.98.196]) by smtp.gmail.com with ESMTPSA id m207-v6sm3234263wma.31.2018.08.07.10.32.25 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Tue, 07 Aug 2018 10:32:26 -0700 (PDT) From: Ben Whitten To: afaerber@suse.de, starnight@g.ncu.edu.tw, hasnain.virk@arm.com Cc: netdev@vger.kernel.org, Ben Whitten Subject: [RFC] spi: add spi multiplexing functions for dt Date: Tue, 7 Aug 2018 18:32:02 +0100 Message-Id: <1533663131-16313-3-git-send-email-ben.whitten@gmail.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1533663131-16313-1-git-send-email-ben.whitten@gmail.com> References: <1533663131-16313-1-git-send-email-ben.whitten@gmail.com> Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org 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_ */