From patchwork Thu Jun 30 03:54:24 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrey Pronin X-Patchwork-Id: 9206589 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 96F7A60752 for ; Thu, 30 Jun 2016 03:55:07 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 7482228161 for ; Thu, 30 Jun 2016 03:55:07 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 68EFB28673; Thu, 30 Jun 2016 03:55:07 +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_SIGNED, RCVD_IN_DNSWL_HI,T_DKIM_INVALID autolearn=ham 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 97B9228161 for ; Thu, 30 Jun 2016 03:55:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751846AbcF3Dyt (ORCPT ); Wed, 29 Jun 2016 23:54:49 -0400 Received: from mail-pa0-f51.google.com ([209.85.220.51]:36414 "EHLO mail-pa0-f51.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751550AbcF3Dyr (ORCPT ); Wed, 29 Jun 2016 23:54:47 -0400 Received: by mail-pa0-f51.google.com with SMTP id wo6so23916247pac.3 for ; Wed, 29 Jun 2016 20:54:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=from:to:cc:subject:date:message-id; bh=bwhKeEXSSWtjKbXz5Gai6EoxAvZfybLEBJ/OMjQzNbc=; b=VRPuRPPelHz7Wsbxm/L6NUE7yFyiQme5CmqfsqfqRzacZvs/pEN/LA4bQbOVQEwIYb qOwf/ur1Fv7G+Rma2dgZYNxJ1uaw5Tci3j7jBn9umcrPJ0kyyrlShel8QJ+02FwIWTii Q+N3TcNOYr8WOU3jHnzVSeOyxlVV4dOo0q3gU= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=bwhKeEXSSWtjKbXz5Gai6EoxAvZfybLEBJ/OMjQzNbc=; b=lgG7HTj8aH7YNKRd3ly+jVd6hyyjP5gAs4X0rlaz5vXB+7Ye1ZiTBqayp0/y+AUX1S mAcvnPobQvYJ6xNrzPkMGcle+zAbUDnEjgvHERQCoqADUwIR8Uc2sjtN4rfCX0Xeuntr Kfib2/oLXz/IPCgJXA5HFEytiI2Zpjr1/4jIv+OZHqFYISnLYtldHRdq1RDzIcgBKV39 AxQD0mILNX8ciXJ2i5O42UPdDHxDXecgLgp07V83kz2AVzzNEBRrk5PMZMbQPpqAnuHu 6vKP3s6Lh4cI4761UN/D+ea+z/RA9Gf3S9UMdGeuN/zZcKRMRjTv0SCYuj9gjH9W0Apg sh9Q== X-Gm-Message-State: ALyK8tKuJuf8v/+9C37hdqeDwe69CI6Kuwdq6XbUE7Qz30G/E1e2wwcVfJ7eIxQrWRiRjTVU X-Received: by 10.66.101.105 with SMTP id ff9mr17630457pab.84.1467258869006; Wed, 29 Jun 2016 20:54:29 -0700 (PDT) Received: from apronin0.mtv.corp.google.com ([172.22.64.136]) by smtp.gmail.com with ESMTPSA id 64sm1184045pfj.96.2016.06.29.20.54.28 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 29 Jun 2016 20:54:28 -0700 (PDT) From: apronin@chromium.org To: Mark Brown Cc: linux-kernel@vger.kernel.org, linux-spi@vger.kernel.org, Andrey Pronin Subject: [PATCH 1/4] spi: Add option to wake a device by toggling CS Date: Wed, 29 Jun 2016 20:54:24 -0700 Message-Id: <1467258867-117727-1-git-send-email-apronin@chromium.org> X-Mailer: git-send-email 2.8.0.rc3.226.g39d4020 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 From: Andrey Pronin Some SPI devices may go to sleep after a period of inactivity on SPI. For such devices, if enough time has passed since the last SPI transaction, toggle CS and wait for the device to start before communicating with it. Signed-off-by: Andrey Pronin --- drivers/spi/spi.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/spi/spi.h | 17 +++++++++++++ 2 files changed, 82 insertions(+) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 77e6e45..c51c864 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -700,6 +700,20 @@ static void spi_set_cs(struct spi_device *spi, bool enable) spi->master->set_cs(spi, !enable); } +static void spi_cs_wake_timer_func(unsigned long arg) +{ + struct spi_device *spi = (struct spi_device *)arg; + + spi->cs_wake_needed = true; +} + +static void spi_reset_cs_wake_timer(struct spi_device *spi) +{ + spi->cs_wake_needed = false; + mod_timer(&spi->cs_wake_timer, + jiffies + spi->cs_sleep_jiffies); +} + #ifdef CONFIG_HAS_DMA static int spi_map_buf(struct spi_master *master, struct device *dev, struct sg_table *sgt, void *buf, size_t len, @@ -948,6 +962,15 @@ static int spi_transfer_one_message(struct spi_master *master, struct spi_statistics *statm = &master->statistics; struct spi_statistics *stats = &msg->spi->statistics; + if (msg->spi->cs_wake_after_sleep && msg->spi->cs_wake_needed) { + dev_info(&msg->spi->dev, "waking after possible sleep\n"); + spi_set_cs(msg->spi, true); + mdelay(1); + spi_set_cs(msg->spi, false); + msleep(msg->spi->cs_wake_duration); + spi_reset_cs_wake_timer(msg->spi); + } + spi_set_cs(msg->spi, true); SPI_STATISTICS_INCREMENT_FIELD(statm, messages); @@ -1024,6 +1047,9 @@ out: if (ret != 0 || !keep_cs) spi_set_cs(msg->spi, false); + if (msg->spi->cs_wake_after_sleep && !ret) + spi_reset_cs_wake_timer(msg->spi); + if (msg->status == -EINPROGRESS) msg->status = ret; @@ -1551,6 +1577,45 @@ of_register_spi_device(struct spi_master *master, struct device_node *nc) } spi->max_speed_hz = value; + /* Do we need to assert CS to wake the device after sleep */ + spi->cs_wake_after_sleep = + of_property_read_bool(nc, "cs-wake-after-sleep"); + if (spi->cs_wake_after_sleep) { + dev_info(&master->dev, "cs-wake-after-sleep enabled\n"); + + /* After what delay device goes to sleep */ + rc = of_property_read_u32(nc, "cs-sleep-delay", &value); + if (rc) { + dev_err(&master->dev, + "%s has no valid 'cs-sleep-delay' property (%d)\n", + nc->full_name, rc); + goto err_out; + } + spi->cs_sleep_jiffies = value * HZ / 1000; /* jiffies */ + + /* How long to wait after waking */ + rc = of_property_read_u32(nc, "cs-wake-duration", &value); + if (rc) { + dev_err(&master->dev, + "%s has no valid 'cs-wake-duration' property (%d)\n", + nc->full_name, rc); + goto err_out; + } + spi->cs_wake_duration = value; /* msec */ + + /* Wake before accessing for the 1st time */ + spi->cs_wake_needed = true; + init_timer(&spi->cs_wake_timer); + spi->cs_wake_timer.data = (unsigned long)spi; + spi->cs_wake_timer.function = spi_cs_wake_timer_func; + } + + /* Should there be a delay before each transfer */ + spi->xfer_delay = 0; + of_property_read_u32(nc, "xfer-delay", &spi->xfer_delay); + if (spi->xfer_delay) + dev_info(&master->dev, "xfer-delay = %u\n", spi->xfer_delay); + /* Store a pointer to the node in the device structure */ of_node_get(nc); spi->dev.of_node = nc; diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 1f03483..4b06ba6 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -126,6 +126,17 @@ void spi_statistics_add_transfer_stats(struct spi_statistics *stats, * @cs_gpio: gpio number of the chipselect line (optional, -ENOENT when * when not using a GPIO line) * + * @cs_wake_after_sleep: Briefly toggle CS before talking to a device + * if it could go to sleep. + * @cs_sleep_jiffies: Delay after which a device may go to sleep if there + * was no SPI activity for it (jiffies). + * @cs_wake_duration: Delay after waking the device by toggling CS before + * it is ready (msec). + * @cs_wake_needed: Is the wake needed (cs_sleep_jiffies passed since + * the last SPI transaction). + * @cs_wake_timer: Timer measuring the delay before the device goes to + * sleep after the last SPI transaction. + * * @statistics: statistics for the spi_device * * A @spi_device is used to interchange data between an SPI slave @@ -166,6 +177,12 @@ struct spi_device { char modalias[SPI_NAME_SIZE]; int cs_gpio; /* chip select gpio */ + bool cs_wake_after_sleep; + unsigned long cs_sleep_jiffies; + unsigned long cs_wake_duration; + bool cs_wake_needed; + struct timer_list cs_wake_timer; + /* the statistics */ struct spi_statistics statistics;