Received: by 10.223.176.46 with SMTP id f43csp787903wra; Wed, 24 Jan 2018 06:02:42 -0800 (PST) X-Google-Smtp-Source: AH8x227tCrCPqMjiZrWit4pkxC1BcmMqX/p+nxHXVR9v1byY8var3m+SRvmDduiR9NapFXcmUKlp X-Received: by 2002:a17:902:208:: with SMTP id 8-v6mr8330061plc.359.1516802562152; Wed, 24 Jan 2018 06:02:42 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1516802562; cv=none; d=google.com; s=arc-20160816; b=N1L3Ff5GnF+2BwNqfLT4otcy6TcxEnXxJSBEsMvNAQ1skpDv9X7tIbwIHj07ZN5NAz eYS9wGiRIfJj7J9xLdbrRH7vngV3AZfd+JxFM4/i6vSkXwW02QBB1/VGsiOEPQtWI3TL uMYGz6gpgvYEuNO7BZe5sXKgtYpBAov2VberzlFMcobeXPDYpDZq3d1BSwloUJh6vxEV OEkKptk8Ucmmnqv/ih1UU8KvOB2gzzgekqixW0qU0TWaa3CEsFmkuFnETvrL+NgmlUdH PromijBTD2YWP4ZBaYfeAGgzIPwNxeWvmBGBe+xRiwZiOfmXCY5jjtOndS5gItgUzhum +eOA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:cc:to:subject:message-id:date:from :references:in-reply-to:mime-version:dkim-signature :arc-authentication-results; bh=gCBzxsx72EX1YXZ6LmY//z3vJVp16FVCcXoJ5gcVes4=; b=ALtcSM/ZAEkQwHcLYBNg8sR0S+/9i3R2B8KN/dw31eKSNBsttinwAHrl6qkDdaPZuf AaUIbGmTV4X26onPebe0zV230U4yZjMaCOw5x8M1o816z8SUxIZNs+AQwBdSSm9aXLdK eFyzF7fuCXdVjJaeQ4v0NM0biALKpV3HolgvUJlHBj5GSP3Dkn9Ln4wKbu9dAmp60BJ8 W2XuCTsCj3CtnSP3xspXyjl1+k5l/yDzEs6kQTd1Xpewd5k8/+Y1X6nOllMhxlj3+T/W ixDxJ3BKXW+EYSSQtxOxOBOBfrT7tDEYb+A0lXGYXTrwwK0aICNFZnBp6DaeGF3p647P qFYg== ARC-Authentication-Results: i=1; mx.google.com; dkim=fail header.i=@gmail.com header.s=20161025 header.b=W1Lw7ydf; 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 e1-v6si257729pln.165.2018.01.24.06.02.28; Wed, 24 Jan 2018 06:02:42 -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=W1Lw7ydf; 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 S933936AbeAXOCB (ORCPT + 99 others); Wed, 24 Jan 2018 09:02:01 -0500 Received: from mail-lf0-f67.google.com ([209.85.215.67]:41342 "EHLO mail-lf0-f67.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S933642AbeAXOB6 (ORCPT ); Wed, 24 Jan 2018 09:01:58 -0500 Received: by mail-lf0-f67.google.com with SMTP id f136so5358056lff.8; Wed, 24 Jan 2018 06:01:57 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=mime-version:sender:in-reply-to:references:from:date:message-id :subject:to:cc; bh=gCBzxsx72EX1YXZ6LmY//z3vJVp16FVCcXoJ5gcVes4=; b=W1Lw7ydfveE69ZyGCMjbAryF1fHigmsDxrEMGrPScvAfTQ10B9rix0GsHTmhE6J8nx BN/ro0j2APndf973smR+uN6gQ/OSTFNjhSabZMF4IJEi2l8gDAhrvz0Uf1d/jWjpJ30a sa6LXplfP5P/JRQ4jVvGfZucmapA9ZjgPnSvCQRlb85FSiubr+pIJ649w7j+xKVSNpJb x/+g3RusfGJrY285CoFtpVAK4PGHG6Mn7Jr7ZLdptcBgf0RkW8Rl/zRgxIEKyceLVEar BMV9UfZQlrB4oiaVwqzASuLbZMZwrHNf9wLWf1wX9N93V2Sb3u48b5sKqL49Jfqro6Ef HIcw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:sender:in-reply-to:references:from :date:message-id:subject:to:cc; bh=gCBzxsx72EX1YXZ6LmY//z3vJVp16FVCcXoJ5gcVes4=; b=YeOY0zEytWD11qtM8RhFob68WKY4sG7LM7A7w7m7fM6DExjQ2TtTVo6/AWpKkPe3Cq AGEE4875gnnL7d/YJvlXU2klFV4x/zVKGueejYmBFrJkkYSyPcUhfElh0+ml0WOKDuZg hyGwL6fXJAtWJpeIi8Ld6fZ7C1KThkumOXFf2K1Au5R9ItnQuv/FcjxGuOj5wmKTRu3i x4lZIxjVx8F+a0oOSm4x9+n9J9POZc0HTh8VIVgjaxefhBaC7IU0Ud1gsKHkBgX0jlVo 0c/ogiURTIGA8xaPcAZEukazNuPm3o3tWH+iT0Jph/gcxt7X+8DceEo5KiTPKpc0EDMK zE2g== X-Gm-Message-State: AKwxyteWwhqmiNKZmW5B3myjjZBPa4I3TuE92hizqwHJLq7Dr8eoRrO0 oILYAY7sLgIo7YriEypMuA/aK3W9J8cdqUqTGjUnLyzf X-Received: by 10.46.60.15 with SMTP id j15mr3786931lja.140.1516802516673; Wed, 24 Jan 2018 06:01:56 -0800 (PST) MIME-Version: 1.0 Received: by 10.46.56.20 with HTTP; Wed, 24 Jan 2018 06:01:55 -0800 (PST) In-Reply-To: <20180123111123.GB7600@sirena.org.uk> References: <1516661472-16452-1-git-send-email-ben.whitten@gmail.com> <1516661472-16452-2-git-send-email-ben.whitten@gmail.com> <20180123111123.GB7600@sirena.org.uk> From: Ben Whitten Date: Wed, 24 Jan 2018 14:01:55 +0000 X-Google-Sender-Auth: ZReHfm0ycE1dRLqf0GLChNfGgT0 Message-ID: Subject: Re: [RFC] spi: add spi multiplexing functions for dt To: Mark Brown Cc: linux-spi@vger.kernel.org, LKML Content-Type: text/plain; charset="UTF-8" Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On 23 January 2018 at 11:11, Mark Brown wrote: > On Mon, Jan 22, 2018 at 10:51:12PM +0000, Ben Whitten wrote: > >> 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. > > Why is this not sitting at either the chip select level or just having > the individual devices be SPI controllers? In the situation I have, the end devices are not attached to an SPI bus that attaches to the host so I dont think I could just override the hosts chip select. This module is a set of helpers for my 'mux' SPI device to register itself as a series of SPI controllers, one for each bus leaf. It could also have been just one SPI controller using its own chip select to switch busses, but I used the design patern from I2C. >> 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 > > No binding documentation... Appologies, will be included in the next itteration. Essentially it's presented like this in DT, where my-mux is a device using the controller registration helpers in this module; my-mux: muxdevice@0 { spi@0 { muxed-devices@0 { ..... }; }; spi@1 { ....... }; }; >> +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); > > This all looks pretty much like the normal set of controller operations > but perhaps I'm missing something - the only extra thing I'm seeing is > an ability to have something sitting in front of the chip select lines > which seems like it'd be more effective if implemented directly at that > level. Things that have their own transfer function would be better off > just being first order SPI controllers I think so that they get access > to everything the framework offers and can correctly advertise > capabilities and so on. This runs as a very simple fake SPI controller per bus that the mux is controlling. Having this custom transfer message allowed me to pop in the way my device exposes the downstream devices, through its regmap. However if a mux device didn't register a custom transfer with this module then it will select the downstream bus then pass the message back to the host SPI for transfer. >> + >> + 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 >>