From patchwork Tue Mar 1 14:43:15 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oleksij Rempel X-Patchwork-Id: 8465641 Return-Path: X-Original-To: patchwork-linux-spi@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id E38D1C0553 for ; Tue, 1 Mar 2016 14:45:11 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 84CCA20154 for ; Tue, 1 Mar 2016 14:45:10 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 0A0AA20295 for ; Tue, 1 Mar 2016 14:45:09 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753981AbcCAOpE (ORCPT ); Tue, 1 Mar 2016 09:45:04 -0500 Received: from mout.gmx.net ([212.227.15.18]:62077 "EHLO mout.gmx.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754299AbcCAOpC (ORCPT ); Tue, 1 Mar 2016 09:45:02 -0500 Received: from localhost.localdomain ([80.187.99.114]) by mail.gmx.com (mrgmx002) with ESMTPSA (Nemesis) id 0MfVU3-1aHJmD2FsR-00P6fA; Tue, 01 Mar 2016 15:44:53 +0100 From: Oleksij Rempel To: fixed-term.Oleksij.Rempel@de.bosch.com, geert@linux-m68k.org, dirk.behme@de.bosch.com, broonie@kernel.org, linux-spi@vger.kernel.org, devicetree@vger.kernel.org, robh+dt@kernel.org Cc: Oleksij Rempel Subject: [PATCH 1/6] spi: add flow controll support Date: Tue, 1 Mar 2016 15:43:15 +0100 Message-Id: <1456843400-20696-1-git-send-email-linux@rempel-privat.de> X-Mailer: git-send-email 2.5.0 In-Reply-To: <56D448E1.6090006@de.bosch.com> References: <56D448E1.6090006@de.bosch.com> X-Provags-ID: V03:K0:jH6It3q2L8shy0LFc8HWin9jSB3u15Ldf0JtSZvkzACHSFEAQxk u3cGyQzr5Yg4mqKsvHhckKODtVu0Vspqd9m3VNrZzLX/v2d6axHLUjiIORjv6ic/BMx9lxA Jg8MNl1SajjakmgkvEf1yG/M/7Z5TJ5zMFhmbQ5UNIkDKcqjcYZhl7T462hryIYY3sw418W SpdhiIRV1qfr2dnMUlDYA== X-UI-Out-Filterresults: notjunk:1; V01:K0:SSgSLRjRIPw=:ssw6hJN4pLP36rJh5Pxwtm dNCy98G1EsonGHLCWaEOgMvXCgqu0alD9loy6J2kzwOeydyrhoKk5l2UHlcR57bSVJjXMpDPR +kv0NbgUnUeOoKvnAaXrafADcvtrycv/00tEkDOVAkkwbbEqW55g+pjTI0x85YNyuD1A1J2bl vY9VodQ7QTyDFzuTgn5aND2ldGe3Aub1vcbd0OVg5p9zxiDTH+6aCtx0mK3xX6XeCks8HqmJa y27UoOaLn41+rIdaHts3GhCHy6hfmrmIJHltJF5yCWmxqv8dWEjHEee8tlngvEmGQwusXvrDF evZIc7cL60G6y3j01qcz6gsuon+ultsNMh7UTiNLme8qaSTN0wwDms10k8RzPUj2JLO+xz/sq +wLLV7smL+pdm7HKNNjyytIiuJEVD/HNDYXqqqHj2O2vyiOrpLTLdxcBxLJcKK56D5nDNl/M1 4GKvqiFG8+Wcts4LA+svm2f/PRiOg5U3yfMzh30zsm0zvoRLi2gNriJQVmR61JhhK/KqtMRuE kkIRHQ8ZAxikyJhgdi55xExxpiSH0+Rlsb/tSA2srD4bN6P7/AlhqLRGbybgFTJn+2sBPmzTF B3MDILooWWAPHMAuwCCmE9IEESdH++Yb9VwaLal/48Rfr4sCcVvk/NMwqe/ypoMvJdyELroCu fuNI7SCOS/0TGkzi7bdcBA3UOa/GBFi/q5w1tpXv4VeMW9o4udndNeOeHaelrF1KkEo53QGU3 Xwtg8inc17bNW/y2o4QC57pJumr9PwvNK4iO7k5vpfCrFKHcWbGDvv0wYs7GGq9xUFC0MBSSn 4ov+15Y Sender: linux-spi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-spi@vger.kernel.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Different HW implement different variants of SPI based flow control (FC). To flexible FC implementation a spited it to fallowing common parts: Flow control: Request Sequence Master CS |-------2\_____________________| Slave FC |-----1\_______________________| DATA |-----------3\_________________| Flow control: Ready Sequence Master CS |-----1\_______________________| Slave FC |--------2\____________________| DATA |-----------3\_________________| Flow control: ACK End of Data Master CS |______________________/2------| Slave FC |________________________/3----| DATA |__________________/1----------| Flow control: Pause Master CS |_______________________/------| Slave FC |_______1/-----\3______/-------| DATA |________2/------\4___/--------| Flow control: Ready signal on MISO Master CS |-----1\_______________________| MISO/DATA |------2\____3/----------------| CONV START | ^ | DATA READY | ^ | Signed-off-by: Oleksij Rempel --- Documentation/devicetree/bindings/spi/spi-bus.txt | 7 + drivers/spi/spi.c | 191 +++++++++++++++++++++- include/linux/spi/spi.h | 68 +++++++- 3 files changed, 261 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/spi/spi-bus.txt b/Documentation/devicetree/bindings/spi/spi-bus.txt index bbaa857..dfefc86 100644 --- a/Documentation/devicetree/bindings/spi/spi-bus.txt +++ b/Documentation/devicetree/bindings/spi/spi-bus.txt @@ -61,6 +61,13 @@ contain the following properties. used for MOSI. Defaults to 1 if not present. - spi-rx-bus-width - (optional) The bus width(number of data wires) that used for MISO. Defaults to 1 if not present. +- spi-fc-ready - (optional) flow control line used for ready signal. +- spi-fc-stop-ack - (optional) flow control line used to ACK end of transfer. +- spi-fc-pause - (optional) flow control line used for to pause transfer + at any time. +- spi-fc-request - (optional) flow control line used for request signal. +- spi-fc-miso-ready - (optional) MISO used for flow control ready signal. +- fc-gpio - (optional) gpio for flow control. Some SPI controllers and devices support Dual and Quad SPI transfer mode. It allows data in the SPI system to be transferred in 2 wires(DUAL) or 4 wires(QUAD). diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 47eff80..1140665 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -37,6 +37,8 @@ #include #include #include +#include +#include #define CREATE_TRACE_POINTS #include @@ -396,6 +398,156 @@ int __spi_register_driver(struct module *owner, struct spi_driver *sdrv) EXPORT_SYMBOL_GPL(__spi_register_driver); /*-------------------------------------------------------------------------*/ +/* + * SPI flow control + */ +static int spi_fc_wait_miso(struct spi_device *spi) +{ + struct spi_master *master = spi->master; + int count = 100; /* 10 * 100 = 10 msec */ + + if (!master->get_miso_level) + return count; + + while (count > 0) { + count--; + if (master->get_miso_level(spi)) + break; + + usleep_range(100, 200); + } + + return count; +} + + +static int spi_fc_equal_cs(struct spi_device *spi) +{ + int fc_enabled; + + fc_enabled = gpiod_get_value(spi->fc_gpio); + return (spi->cs_enabled == fc_enabled); +} + +int spi_fc_wait_rq(struct spi_device *spi, u32 fc_state) +{ + unsigned long timeout = msecs_to_jiffies(10); + int ret = 1; + + if (!(spi->mode & fc_state)) + return ret; + + if (spi->mode & SPI_FC_MISO_READY) + return spi_fc_wait_miso(spi); + + if (atomic_read(&spi->active_rq)) { + if (!spi_fc_equal_cs(spi)) + dev_warn(&spi->dev, "Got request, but device is not ready\n"); + atomic_set(&spi->active_rq, 0); + return ret; + } + + if (spi->fc_irq >= 0) { + ret = wait_for_completion_io_timeout(&spi->fc_complete, + timeout); + } else { + int count = 100; /* 10 * 100 = 10-20 msec */ + + while (count > 0) { + count--; + if (spi_fc_equal_cs(spi)) + break; + + usleep_range(100, 200); + } + ret = count; + } + + if (!ret) + dev_warn(&spi->dev, "FC timeout: requested state: 0x%x\n", fc_state); + + return ret; +} + +static irqreturn_t spi_fc_rq(int irq, void *dev_id) +{ + struct spi_device *spi = (struct spi_device *)dev_id; + int fc_enabled; + + fc_enabled = gpiod_get_value(spi->fc_gpio); + + if (spi->mode | SPI_FC_REQUEST && + !spi->cs_enabled && fc_enabled) { + atomic_set(&spi->active_rq, 1); + if (spi->request_cb) + spi->request_cb(spi); + } else if (spi->mode | SPI_FC_STOP_ACK && + !spi->cs_enabled && !fc_enabled) { + complete(&spi->fc_complete); + } else if (spi->mode | SPI_FC_READY && + spi->cs_enabled && fc_enabled) { + complete(&spi->fc_complete); + } else { + dev_warn(&spi->dev, "Wrong FC State. CS:%i, FC:%i, Mode:0x%x\n", + spi->cs_enabled, fc_enabled, spi->mode); + } + + return IRQ_HANDLED; +} + +/** + * spi_fc_probe - Configure Flow Control for called slave device. + * @spi: spi_device to register. + * + * Companion function to spi_setup. Devices which support Flow Control + * functionality need to call this function to be able to use it. + * + * Return: 0 on success; negative errno on failure. + */ +int spi_fc_probe(struct spi_device *spi) +{ + struct device_node *np = spi->dev.of_node; + int ret; + + if (!np) + return 0; + + if (!(spi->mode & SPI_FC_MASK) || spi->mode & SPI_FC_HW_ONLY) + return 0; + + spi->fc_gpio = devm_gpiod_get(&spi->dev, "fc", GPIOD_IN); + if (IS_ERR(spi->fc_gpio)) { + ret = PTR_ERR(spi->fc_gpio); + dev_err(&spi->dev, "Failed to request FC GPIO: %d\n", ret); + return ret; + } + + init_completion(&spi->fc_complete); + + spi->fc_irq = gpiod_to_irq(spi->fc_gpio); + if (spi->fc_irq < 0) { + /* + * This will dramtically affect transfer speed, + * so it is not recommended, but possible use case. + */ + dev_warn(&spi->dev, "gpio-fc, filed to get irq for configured pin, use slow polling instead\n"); + } else { + snprintf(spi->fc_irq_name, sizeof(spi->fc_irq_name), "spi-fc-%s", + dev_name(&spi->dev)); + ret = devm_request_irq(&spi->dev, spi->fc_irq, spi_fc_rq, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + spi->fc_irq_name, spi); + if (ret) { + dev_err(&spi->dev, "Failed to request FC IRQ\n"); + return ret; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(spi_fc_probe); + +/*-------------------------------------------------------------------------*/ /* SPI devices should normally not be created by SPI device drivers; that * would make them board-specific. Similarly with SPI master drivers. @@ -687,6 +839,9 @@ int spi_register_board_info(struct spi_board_info const *info, unsigned n) static void spi_set_cs(struct spi_device *spi, bool enable) { + spi->cs_enabled = enable; + reinit_completion(&spi->fc_complete); + if (spi->mode & SPI_CS_HIGH) enable = !enable; @@ -941,9 +1096,15 @@ static int spi_transfer_one_message(struct spi_master *master, unsigned long ms = 1; struct spi_statistics *statm = &master->statistics; struct spi_statistics *stats = &msg->spi->statistics; + struct spi_device *spi = msg->spi; spi_set_cs(msg->spi, true); + if (!spi_fc_wait_rq(spi, SPI_FC_READY | SPI_FC_MISO_READY)) { + ret = -EREMOTEIO; + goto out; + } + SPI_STATISTICS_INCREMENT_FIELD(statm, messages); SPI_STATISTICS_INCREMENT_FIELD(stats, messages); @@ -1006,8 +1167,20 @@ static int spi_transfer_one_message(struct spi_master *master, keep_cs = true; } else { spi_set_cs(msg->spi, false); - udelay(10); + + if (!spi_fc_wait_rq(spi, SPI_FC_STOP_ACK)) { + ret = -EREMOTEIO; + break; + } + + if (!(spi->mode & SPI_FC_STOP_ACK)) + udelay(10); spi_set_cs(msg->spi, true); + + if (!spi_fc_wait_rq(spi, SPI_FC_READY)) { + ret = -EREMOTEIO; + break; + } } } @@ -1015,8 +1188,12 @@ static int spi_transfer_one_message(struct spi_master *master, } out: - if (ret != 0 || !keep_cs) + + if (ret != 0 || !keep_cs) { spi_set_cs(msg->spi, false); + if (ret != -EREMOTEIO && !spi_fc_wait_rq(spi, SPI_FC_STOP_ACK)) + ret = -EREMOTEIO; + } if (msg->status == -EINPROGRESS) msg->status = ret; @@ -1483,6 +1660,16 @@ of_register_spi_device(struct spi_master *master, struct device_node *nc) spi->mode |= SPI_3WIRE; if (of_find_property(nc, "spi-lsb-first", NULL)) spi->mode |= SPI_LSB_FIRST; + if (of_find_property(nc, "spi-fc-ready", NULL)) + spi->mode |= SPI_FC_READY; + if (of_find_property(nc, "spi-fc-stop-ack", NULL)) + spi->mode |= SPI_FC_STOP_ACK; + if (of_find_property(nc, "spi-fc-pause", NULL)) + spi->mode |= SPI_FC_PAUSE; + if (of_find_property(nc, "spi-fc-request", NULL)) + spi->mode |= SPI_FC_REQUEST; + if (of_find_property(nc, "spi-fc-miso-ready", NULL)) + spi->mode |= SPI_FC_MISO_READY; /* Device DUAL/QUAD mode */ if (!of_property_read_u32(nc, "spi-tx-bus-width", &value)) { diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 53be3a4..07803f8 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -136,7 +136,7 @@ struct spi_device { u32 max_speed_hz; u8 chip_select; u8 bits_per_word; - u16 mode; + u32 mode; #define SPI_CPHA 0x01 /* clock phase */ #define SPI_CPOL 0x02 /* clock polarity */ #define SPI_MODE_0 (0|0) /* (original MicroWire) */ @@ -148,16 +148,65 @@ struct spi_device { #define SPI_3WIRE 0x10 /* SI/SO signals shared */ #define SPI_LOOP 0x20 /* loopback mode */ #define SPI_NO_CS 0x40 /* 1 dev/bus, no chipselect */ -#define SPI_READY 0x80 /* slave pulls low to pause */ +/* + * Flow control: Ready Sequence (SPI_FC_READY) + * Master CS |-----1\_______________________| + * Slave FC |--------2\____________________| + * DATA |-----------3\_________________| + * 1. Chips Select set to active by Master. + * 2. Flow Control set to active by Slave. + * 3. Master starting Data transmission. + */ +#define SPI_FC_READY 0x80 #define SPI_TX_DUAL 0x100 /* transmit with 2 wires */ #define SPI_TX_QUAD 0x200 /* transmit with 4 wires */ #define SPI_RX_DUAL 0x400 /* receive with 2 wires */ #define SPI_RX_QUAD 0x800 /* receive with 4 wires */ +/* + * Flow control: Pause (SPI_FC_PAUSE) + * Master CS |_______________________/------| + * Slave FC |_______1/-----\3______/-------| + * DATA |________2/------\4_____/------| + */ +#define SPI_FC_PAUSE 0x1000 +/* + * Flow control: ACK End of Data (SPI_FC_STOP_ACK) + * Master CS |______________________/2------| + * Slave FC |________________________/3----| + * DATA |__________________/1----------| + */ +#define SPI_FC_STOP_ACK 0x2000 +/* + * Flow control: Request Sequence (SPI_FC_REQUEST) + * Master CS |-------2\_____________________| + * Slave FC |-----1\_______________________| + * DATA |-----------3\_________________| + */ +#define SPI_FC_REQUEST 0x4000 +/* If complete FC is done by HW or controller driver, set this flag */ +#define SPI_FC_HW_ONLY 0x8000 +/* + * Flow control: Ready signal on MISO (SPI_FC_MISO_READY) + * Master CS |-----1\_______________________| + * MISO/DATA |------2\____3/----------------| + * CONV START | ^ | + * DATA READY | ^ | + * When CS get asserted (1), MISO/DATA becomes dominant low (2) + * and the ADC conversion starts (within 100ns of (1)). + * When MISO/DATA goes dominant high (3) then the conversion has finished + * and the transfer may start. + * Example: MAX187/189 SPI-ADC. + */ +#define SPI_FC_MISO_READY 0x10000 +#define SPI_FC_MASK (SPI_FC_READY | SPI_FC_PAUSE | \ + SPI_FC_STOP_ACK | SPI_FC_REQUEST) + int irq; void *controller_state; void *controller_data; char modalias[SPI_NAME_SIZE]; int cs_gpio; /* chip select gpio */ + int cs_enabled; /* the statistics */ struct spi_statistics statistics; @@ -171,6 +220,13 @@ struct spi_device { * - chipselect delays * - ... */ + + void (*request_cb)(struct spi_device *spi); + atomic_t active_rq; + struct completion fc_complete; + struct gpio_desc *fc_gpio; /* flow control */ + int fc_irq; + char fc_irq_name[32]; }; static inline struct spi_device *to_spi_device(struct device *dev) @@ -282,7 +338,6 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv) #define module_spi_driver(__spi_driver) \ module_driver(__spi_driver, spi_register_driver, \ spi_unregister_driver) - /** * struct spi_master - interface to SPI master controller * @dev: device interface to this driver @@ -537,6 +592,10 @@ struct spi_master { /* dummy data for full duplex devices */ void *dummy_rx; void *dummy_tx; + + int (*wait_for_rq)(struct spi_device *spi); + /* some devices use MISO for flow control, see SPI_FC_MISO_READY */ + int (*get_miso_level)(struct spi_device *spi); }; static inline void *spi_master_get_devdata(struct spi_master *master) @@ -1146,4 +1205,7 @@ spi_transfer_is_last(struct spi_master *master, struct spi_transfer *xfer) return list_is_last(&xfer->transfer_list, &master->cur_msg->transfers); } +int spi_fc_wait_rq(struct spi_device *spi, u32 s5w_state); +int spi_fc_probe(struct spi_device *spi); + #endif /* __LINUX_SPI_H */