From patchwork Mon Mar 17 16:22:04 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Daniel Sobe X-Patchwork-Id: 3845091 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.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 6A558BF540 for ; Mon, 17 Mar 2014 16:38:07 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 41BFD201B4 for ; Mon, 17 Mar 2014 16:38:06 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id CA330201EF for ; Mon, 17 Mar 2014 16:38:04 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754532AbaCQQiE (ORCPT ); Mon, 17 Mar 2014 12:38:04 -0400 Received: from [213.199.154.78] ([213.199.154.78]:25612 "EHLO emea01-db3-obe.outbound.protection.outlook.com" rhost-flags-FAIL-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S1753572AbaCQQiD convert rfc822-to-8bit (ORCPT ); Mon, 17 Mar 2014 12:38:03 -0400 X-Greylist: delayed 955 seconds by postgrey-1.27 at vger.kernel.org; Mon, 17 Mar 2014 12:38:02 EDT Received: from DB4PR04MB393.eurprd04.prod.outlook.com (10.141.238.11) by DB4PR04MB396.eurprd04.prod.outlook.com (10.141.238.18) with Microsoft SMTP Server (TLS) id 15.0.898.11; Mon, 17 Mar 2014 16:22:04 +0000 Received: from DB4PR04MB393.eurprd04.prod.outlook.com ([10.141.238.11]) by DB4PR04MB393.eurprd04.prod.outlook.com ([10.141.238.11]) with mapi id 15.00.0898.005; Mon, 17 Mar 2014 16:22:04 +0000 From: Daniel Sobe To: "linux-spi@vger.kernel.org" Subject: [PATCH] spi-imx: improve timing of bursts Thread-Topic: [PATCH] spi-imx: improve timing of bursts Thread-Index: AQHPQfxWyuqR3pKS006nWcG0hrmQKw== Date: Mon, 17 Mar 2014 16:22:04 +0000 Message-ID: <8c8893eae0014e59ac6942062590d36e@DB4PR04MB393.eurprd04.prod.outlook.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-originating-ip: [92.121.32.13] x-forefront-prvs: 0153A8321A x-forefront-antispam-report: SFV:NSPM; SFS:(10019001)(6009001)(428001)(377424004)(199002)(189002)(80022001)(63696002)(79102001)(66066001)(65816001)(59766001)(77982001)(20776003)(93516002)(86362001)(94946001)(94316002)(95416001)(76176001)(4396001)(47446002)(74502001)(31966008)(74662001)(56816005)(90146001)(83072002)(95666003)(93136001)(97336001)(97186001)(92566001)(85852003)(56776001)(54316002)(74366001)(74316001)(74706001)(53806001)(74876001)(33646001)(76796001)(76786001)(76576001)(54356001)(87936001)(69226001)(81542001)(87266001)(47976001)(50986001)(47736001)(49866001)(81342001)(46102001)(83322001)(81816001)(85306002)(76482001)(2656002)(81686001)(19580405001)(19580395003)(80976001)(24736002); DIR:OUT; SFP:1102; SCL:1; SRVR:DB4PR04MB396; H:DB4PR04MB393.eurprd04.prod.outlook.com; FPR:A0FBF04E.A0D697C6.F1E0B9B6.84C5A972.2064E; MLV:sfv; PTR:InfoNoRecords; A:1; MX:1; LANG:en; received-spf: None (: nxp.com does not designate permitted sender hosts) MIME-Version: 1.0 X-OriginatorOrg: nxp.com 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, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=ham 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 Hi, The patch below addresses 2 improvements: 1) Burstsize of a transfer adjusted up to the maximum burstsize of the hardware --> no unwanted CS toggling between data words 2) Variable trigger level allows to increase priority for data transfer, enables continuous transfers up to the burstsize. --> remove gaps every 64 words The original behaviour of the driver is untouched. The additional features can be enabled via sysfs. With file "bigburst" the driver is instructed to use the maximum possible burst length, depending on the length of the transfer it received. Due to the hardware of the iMX6 this only makes sense if a transfer has its bits per word set to 32. With file "txthres" the transmit FIFO level is set (in the range of 1 to 64), all other values disable the functionality. An interrupt will be triggered if the specified FIFO level (or lower) is reached, re-filling the FIFO up to the max resp. the remaining data of the transfer. If disabled, the original behaviour (refilling when FIFO is empty) is active. The functionality creates a higher interrupt load but allows continuous transfers up to the selected burstsize. I'd like to receive feedback on the patch, improving it and making it suitable for a possible inclusion into the kernel. Unfortunately, I could only test the patch on a very old kernel. The patch itself applies against a recent kernel (3.14-rc6) and I checked that it compiles without warnings. Signed-off-by: Daniel Sobe --- To unsubscribe from this list: send the line "unsubscribe linux-spi" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html --- linux-original/drivers/spi/spi-imx.c    2014-03-17 17:12:35.691791218 +0100 +++ linux-stable/drivers/spi/spi-imx.c    2014-03-17 17:09:44.451795597 +0100 @@ -51,12 +51,14 @@  /* generic defines to abstract from the different register layouts */  #define MXC_INT_RR    (1 << 0) /* Receive data ready interrupt */  #define MXC_INT_TE    (1 << 1) /* Transmit FIFO empty interrupt */ +#define MXC_INT_THE     (1 << 2) /* Transmit FIFO threshold interrupt */    struct spi_imx_config {      unsigned int speed_hz;      unsigned int bpw;      unsigned int mode;      u8 cs; +    unsigned long burstlen;  };    enum spi_imx_devtype { @@ -154,6 +156,10 @@  static int mxc_clkdivs[] = {0, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192,      256, 384, 512, 768, 1024};   +// (sysfs-)tunable attributes +static int allow_big_bursts = 0; // if set to 1 this enables 32-bit big bursts +static int tx_fifo_thres = -1;   // if positive, use TX data request interrupt when applicable +  /* MX21, MX27 */  static unsigned int spi_imx_clkdiv_1(unsigned int fin,          unsigned int fspi, unsigned int max) @@ -200,8 +206,13 @@    #define MX51_ECSPI_INT        0x10  #define MX51_ECSPI_INT_TEEN        (1 <<  0) +#define MX51_ECSPI_INT_TDREN        (1 <<  1)  #define MX51_ECSPI_INT_RREN        (1 <<  3)   +#define MX51_ECSPI_DMA      0x14 +#define MX51_ECSPI_DMA_TX_THRES_OFFSET  0 +#define MX51_ECSPI_DMA_TX_THRES_MASK    (0x3F) +  #define MX51_ECSPI_STAT        0x18  #define MX51_ECSPI_STAT_RR        (1 <<  3)   @@ -250,6 +261,12 @@      if (enable & MXC_INT_TE)          val |= MX51_ECSPI_INT_TEEN;   +    // enable Tx threshold interrupt for faster reaction when transmitting long messages +    if (enable & MXC_INT_THE) { +        val |= MX51_ECSPI_INT_TDREN; +        writel((unsigned int) tx_fifo_thres, spi_imx->base + MX51_ECSPI_DMA); +    } +      if (enable & MXC_INT_RR)          val |= MX51_ECSPI_INT_RREN;   @@ -270,6 +287,7 @@  {      u32 ctrl = MX51_ECSPI_CTRL_ENABLE, cfg = 0;      u32 clk = config->speed_hz, delay; +    unsigned long burstlen;        /*       * The hardware seems to have a race condition when changing modes. The @@ -286,6 +304,27 @@      /* set chip select to use */      ctrl |= MX51_ECSPI_CTRL_CS(config->cs);   +    // if possible and requested, increase burstlength +    // over whole message in 32 bit mode +    // (which is the only mode where HW supports this) +    // all other modes still use burstlength == bpw +    if ((config->bpw == 32) && (allow_big_bursts == 1)) { +        if (config->burstlen <= 4096) { +            burstlen = config->burstlen; +        } else { +            // burst longer than burst register width -- need to scale appropriately +            // so that the hardware will always terminate the burst appropriately +            burstlen = 16; +            do { +                burstlen = burstlen << 1; +            } while ((config->burstlen % (burstlen << 1)) == 0); +        } +    } else { +      burstlen = config->bpw; +    } + +    ctrl |= (burstlen - 1) << MX51_ECSPI_CTRL_BL_OFFSET; +      ctrl |= (config->bpw - 1) << MX51_ECSPI_CTRL_BL_OFFSET;        cfg |= MX51_ECSPI_CONFIG_SBBCTRL(config->cs); @@ -666,6 +705,10 @@          spi_imx->txfifo++;      }   +    // always reconfigure IRQ source (for the TX data request IRQ to work properly) +    spi_imx->devtype_data->intctrl(spi_imx, +        ((spi_imx->txfifo == spi_imx_get_fifosize(spi_imx)) && (tx_fifo_thres != -1)) ? MXC_INT_THE : MXC_INT_TE); +      spi_imx->devtype_data->trigger(spi_imx);  }   @@ -708,6 +751,7 @@      config.speed_hz  = t ? t->speed_hz : spi->max_speed_hz;      config.mode = spi->mode;      config.cs = spi->chip_select; +    config.burstlen = t ? (t->len * 8) : config.bpw;        if (!config.speed_hz)          config.speed_hz = spi->max_speed_hz; @@ -745,8 +789,6 @@        spi_imx_push(spi_imx);   -    spi_imx->devtype_data->intctrl(spi_imx, MXC_INT_TE); -      wait_for_completion(&spi_imx->xfer_done);        return transfer->len; @@ -801,6 +843,63 @@      return 0;  }   +static ssize_t spidrv_setbigburst(struct device *dev, +                                  struct device_attribute *attr, +                                  const char *buf, size_t size) +{ +    unsigned long tmp; +    tmp = simple_strtoul(buf, NULL, 10); +    if (tmp == 1) { +        allow_big_bursts = 1; +        } else { +        allow_big_bursts = 0; +    } +    return size; +} + +static ssize_t spidrv_getbigburst(struct device *dev, +                                  struct device_attribute *attr, +                                  char *buf) +{ +    sprintf (buf, "%d\n", allow_big_bursts); +    return (ssize_t)(strlen (buf) + 1); +} + +static ssize_t spidrv_settxthres(struct device *dev, +                                 struct device_attribute *attr, +                                 const char *buf, size_t size) +{ +    unsigned long tmp; +    tmp = simple_strtoul(buf, NULL, 10); +    if ((tmp > 0) && (tmp < 65)) { +        tx_fifo_thres = tmp; +    } else { +        tx_fifo_thres = -1; +    } +    return size; +} + +static ssize_t spidrv_gettxthres(struct device *dev, +                                 struct device_attribute *attr, +                                 char *buf) +{ +    sprintf (buf, "%d\n", tx_fifo_thres); +    return (ssize_t)(strlen (buf) + 1); +} + +static DEVICE_ATTR(bigburst, 0666, spidrv_getbigburst, spidrv_setbigburst); +static DEVICE_ATTR(txthres,  0666, spidrv_gettxthres , spidrv_settxthres); + +static struct attribute *spidrv_attributes[] = { +    &dev_attr_bigburst.attr, +    &dev_attr_txthres.attr, +    NULL +}; + +static const struct attribute_group spidrv_group = { +    .attrs = spidrv_attributes, +}; +  static int spi_imx_probe(struct platform_device *pdev)  {      struct device_node *np = pdev->dev.of_node; @@ -840,6 +939,10 @@      spi_imx = spi_master_get_devdata(master);      spi_imx->bitbang.master = master;   +    if (sysfs_create_group(&pdev->dev.kobj, &spidrv_group)) { +        return -ENOMEM; +    } +      for (i = 0; i < master->num_chipselect; i++) {          int cs_gpio = of_get_named_gpio(np, "cs-gpios", i);          if (!gpio_is_valid(cs_gpio) && mxc_platform_info)