Received: by 10.223.176.46 with SMTP id f43csp3460244wra; Mon, 22 Jan 2018 14:52:27 -0800 (PST) X-Google-Smtp-Source: AH8x226fSTNUqNKcyuqAno/V9vCGYvHanZQEm9ac/45EiVgFDJ/zOilMxdi0Ke+kVrpMb1NUCZAQ X-Received: by 10.36.190.15 with SMTP id i15mr721261itf.109.1516661546962; Mon, 22 Jan 2018 14:52:26 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1516661546; cv=none; d=google.com; s=arc-20160816; b=f1rJ+Skt2PnIEVIeTulktT8TbnHp4MR0ezU+0F/YKJu/774HW/nVcOEXyxyJMNS7qd TwnBQgAm7kCMSmLcK5SY6cjb9aEMp31CmKvtoSYwFCHWak1Oo6Dr/5y7ioYyXrfHxeHu XaX5Ysy2+hFTEwd/7809gBrzQ9b5bn5jFKQJL9UZ3nYqslShZXpWxjHfdAiTlC2Fu8yo +Y1RPB9gzMOa7OhmaAysLa/WVFxDSRZJvlyo7+CmZN1xKBSZuH1R/ZLUGFJ+RUlqN6mW kTQcXgTtQDe1xtZWE9RQdEGo/BarZtBkWx7zVHIBVi+L+FmuJqfSGdLQOLFy9IV3A3Lp dmrg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature:arc-authentication-results; bh=f79WZiMNTI1ebtDfHkCgYTybUO0AgwDMqgqRO01Bspw=; b=QabcmE24dD/+AKMf36dmHehHbibhwxx9eC+arTYalh/isgOmSYtT4r8hqMbYXw9Bh9 Wx8Kf7nA5syESBr6pQHacdwjCqifMQGY7xcKD6v3jeVt4n+Gt+hgGPsCoduu9apPtIc1 s4C+iBzc7EstCsMI+9xRwACOxHoiAe/VI721fbu6KzeMpqfeLAvdQg0wYyFPQBD8Tb9O VAg0TklHKgF+yPdywk/18uc4obciYsM3ci15ExjSbYAMR+iEBT/me3X3rlrULwJ7SsGY Yl4WpcL0vUaZLediv2CYkskqCozRwW+T9tousZ3MbbtJoWxFT1+5LEwDKRV/2fZMVkyF RJsA== ARC-Authentication-Results: i=1; mx.google.com; dkim=fail header.i=@gmail.com header.s=20161025 header.b=VQkZPuLv; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=gmail.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id t194si6899811itb.65.2018.01.22.14.52.14; Mon, 22 Jan 2018 14:52:26 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=fail header.i=@gmail.com header.s=20161025 header.b=VQkZPuLv; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=gmail.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751242AbeAVWvt (ORCPT + 99 others); Mon, 22 Jan 2018 17:51:49 -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-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-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@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_ */ -- 2.7.4