Author: gonzo
Date: Fri Oct 25 21:38:38 2019
New Revision: 354103
URL: https://svnweb.freebsd.org/changeset/base/354103

Log:
  arm64: rk3399: add SPI driver and include it in GENERIC config
  
  SPI driver for Rockchip's RK3399 SoC. Implements PIO mode, CS selection,
  SPI mode and frequency configuration.
  
  Reviewed by:  manu
  MFC after:    1 month
  Differential Revision:        https://reviews.freebsd.org/D22148

Added:
  head/sys/arm64/rockchip/rk_spi.c   (contents, props changed)
Modified:
  head/sys/arm64/conf/GENERIC
  head/sys/conf/files.arm64

Modified: head/sys/arm64/conf/GENERIC
==============================================================================
--- head/sys/arm64/conf/GENERIC Fri Oct 25 21:32:28 2019        (r354102)
+++ head/sys/arm64/conf/GENERIC Fri Oct 25 21:38:38 2019        (r354103)
@@ -286,6 +286,7 @@ device              mv_thermal      # Marvell Thermal 
Sensor Controller
 # SPI
 device         spibus
 device         bcm2835_spi     # Broadcom BCM283x SPI bus
+device         rk_spi          # RockChip SPI controller
 
 # PWM
 device         pwm

Added: head/sys/arm64/rockchip/rk_spi.c
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/sys/arm64/rockchip/rk_spi.c    Fri Oct 25 21:38:38 2019        
(r354103)
@@ -0,0 +1,483 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2019 Oleksandr Tymoshenko <go...@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <sys/resource.h>
+#include <machine/bus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/spibus/spi.h>
+#include <dev/spibus/spibusvar.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+
+#include "spibus_if.h"
+
+#define        RK_SPI_CTRLR0           0x0000
+#define                CTRLR0_OPM_MASTER       (0 << 20)
+#define                CTRLR0_XFM_TR           (0 << 18)
+#define                CTRLR0_FRF_MOTO         (0 << 16)
+#define                CTRLR0_BHT_8BIT         (1 << 13)
+#define                CTRLR0_EM_BIG           (1 << 11)
+#define                CTRLR0_SSD_ONE          (1 << 10)
+#define                CTRLR0_SCPOL            (1 <<  7)
+#define                CTRLR0_SCPH             (1 <<  6)
+#define                CTRLR0_DFS_8BIT         (1 <<  0)
+#define        RK_SPI_CTRLR1           0x0004
+#define        RK_SPI_ENR              0x0008
+#define        RK_SPI_SER              0x000c
+#define        RK_SPI_BAUDR            0x0010
+#define        RK_SPI_TXFTLR           0x0014
+#define        RK_SPI_RXFTLR           0x0018
+#define        RK_SPI_TXFLR            0x001c
+#define        RK_SPI_RXFLR            0x0020
+#define        RK_SPI_SR               0x0024
+#define                SR_BUSY                 (1 <<  0)
+#define        RK_SPI_IPR              0x0028
+#define        RK_SPI_IMR              0x002c
+#define                IMR_RFFIM               (1 <<  4)
+#define                IMR_TFEIM               (1 <<  0)
+#define        RK_SPI_ISR              0x0030
+#define                ISR_RFFIS               (1 <<  4)
+#define                ISR_TFEIS               (1 <<  0)
+#define        RK_SPI_RISR             0x0034
+#define        RK_SPI_ICR              0x0038
+#define        RK_SPI_DMACR            0x003c
+#define        RK_SPI_DMATDLR          0x0040
+#define        RK_SPI_DMARDLR          0x0044
+#define        RK_SPI_TXDR             0x0400
+#define        RK_SPI_RXDR             0x0800
+
+#define        CS_MAX                  1
+
+static struct ofw_compat_data compat_data[] = {
+       { "rockchip,rk3399-spi",                1 },
+       { NULL,                                 0 }
+};
+
+static struct resource_spec rk_spi_spec[] = {
+       { SYS_RES_MEMORY,       0,      RF_ACTIVE },
+       { SYS_RES_IRQ,          0,      RF_ACTIVE | RF_SHAREABLE },
+       { -1, 0 }
+};
+
+struct rk_spi_softc {
+       device_t        dev;
+       device_t        spibus;
+       struct resource *res[2];
+       struct mtx      mtx;
+       clk_t           clk_apb;
+       clk_t           clk_spi;
+       void *          intrhand;
+       int             transfer;
+       uint32_t        fifo_size;
+       uint64_t        max_freq;
+
+       uint32_t        intreg;
+       uint8_t         *rxbuf;
+       uint32_t        rxidx;
+       uint8_t         *txbuf;
+       uint32_t        txidx;
+       uint32_t        txlen;
+       uint32_t        rxlen;
+};
+
+#define        RK_SPI_LOCK(sc)                 mtx_lock(&(sc)->mtx)
+#define        RK_SPI_UNLOCK(sc)               mtx_unlock(&(sc)->mtx)
+#define        RK_SPI_READ_4(sc, reg)          bus_read_4((sc)->res[0], (reg))
+#define        RK_SPI_WRITE_4(sc, reg, val)    bus_write_4((sc)->res[0], 
(reg), (val))
+
+static int rk_spi_probe(device_t dev);
+static int rk_spi_attach(device_t dev);
+static int rk_spi_detach(device_t dev);
+static void rk_spi_intr(void *arg);
+
+static void
+rk_spi_enable_chip(struct rk_spi_softc *sc, int enable)
+{
+
+       RK_SPI_WRITE_4(sc, RK_SPI_ENR, enable ? 1 : 0);
+}
+
+static int
+rk_spi_set_cs(struct rk_spi_softc *sc, uint32_t cs, bool active)
+{
+       uint32_t reg;
+
+       if (cs & SPIBUS_CS_HIGH) {
+               device_printf(sc->dev, "SPIBUS_CS_HIGH is not supported\n");
+               return (EINVAL);
+       }
+
+       if (cs > CS_MAX)
+               return (EINVAL);
+
+       reg = RK_SPI_READ_4(sc, RK_SPI_SER);
+       if (active)
+               reg |= (1 << cs);
+       else
+               reg &= ~(1 << cs);
+       RK_SPI_WRITE_4(sc, RK_SPI_SER, reg);
+
+       return (0);
+}
+
+static void
+rk_spi_hw_setup(struct rk_spi_softc *sc, uint32_t mode, uint32_t freq)
+{
+       uint32_t cr0;
+       uint32_t div;
+
+       cr0 =  CTRLR0_OPM_MASTER | CTRLR0_XFM_TR | CTRLR0_FRF_MOTO |
+           CTRLR0_BHT_8BIT | CTRLR0_EM_BIG | CTRLR0_SSD_ONE |
+           CTRLR0_DFS_8BIT;
+
+       if (mode & SPIBUS_MODE_CPHA)
+               cr0 |= CTRLR0_SCPH;
+       if (mode & SPIBUS_MODE_CPOL)
+               cr0 |= CTRLR0_SCPOL;
+
+       /* minimum divider is 2 */
+       if (sc->max_freq < freq*2) {
+               clk_set_freq(sc->clk_spi, 2 * freq, CLK_SET_ROUND_DOWN);
+               clk_get_freq(sc->clk_spi, &sc->max_freq);
+       }
+
+       div = ((sc->max_freq + freq - 1) / freq);
+       div = (div + 1) & 0xfffe;
+       RK_SPI_WRITE_4(sc, RK_SPI_BAUDR, div);
+
+       RK_SPI_WRITE_4(sc, RK_SPI_CTRLR0, cr0);
+}
+
+static uint32_t
+rk_spi_fifo_size(struct rk_spi_softc *sc)
+{
+       uint32_t txftlr, reg;
+
+       for (txftlr = 2; txftlr < 32; txftlr++) {
+               RK_SPI_WRITE_4(sc, RK_SPI_TXFTLR, txftlr);
+               reg = RK_SPI_READ_4(sc, RK_SPI_TXFTLR);
+               if (reg != txftlr)
+                       break;
+       }
+       RK_SPI_WRITE_4(sc, RK_SPI_TXFTLR, 0);
+
+       if (txftlr == 31)
+               return 0;
+
+       return txftlr;
+}
+
+static void
+rk_spi_empty_rxfifo(struct rk_spi_softc *sc)
+{
+       uint32_t rxlevel;
+       rxlevel = RK_SPI_READ_4(sc, RK_SPI_RXFLR);
+       while (sc->rxidx < sc->rxlen &&
+           (rxlevel-- > 0)) {
+               sc->rxbuf[sc->rxidx++] = (uint8_t)RK_SPI_READ_4(sc, 
RK_SPI_RXDR);
+       }
+}
+
+static void
+rk_spi_fill_txfifo(struct rk_spi_softc *sc)
+{
+       uint32_t txlevel;
+       txlevel = RK_SPI_READ_4(sc, RK_SPI_TXFLR);
+       int cnt = 0;
+
+       while (sc->txidx < sc->txlen && txlevel < sc->fifo_size) {
+               RK_SPI_WRITE_4(sc, RK_SPI_TXDR, sc->txbuf[sc->txidx++]);
+               txlevel++;
+               cnt++;
+       }
+
+       if (sc->txidx != sc->txlen)
+               sc->intreg |= (IMR_TFEIM  | IMR_RFFIM);
+}
+
+static int
+rk_spi_xfer_buf(struct rk_spi_softc *sc, void *rxbuf, void *txbuf, uint32_t 
len)
+{
+       int err;
+
+       if (len == 0)
+               return (0);
+
+       sc->rxbuf = rxbuf;
+       sc->rxlen = len;
+       sc->rxidx = 0;
+       sc->txbuf = txbuf;
+       sc->txlen = len;
+       sc->txidx = 0;
+       sc->intreg = 0;
+       rk_spi_fill_txfifo(sc);
+
+       RK_SPI_WRITE_4(sc, RK_SPI_IMR, sc->intreg);
+
+       err = 0;
+       while (err == 0 && sc->intreg != 0)
+               err = msleep(sc, &sc->mtx, 0, "rk_spi", 10 * hz);
+
+       while (err == 0 && sc->rxidx != sc->txidx) {
+               /* read residual data from RX fifo */
+               rk_spi_empty_rxfifo(sc);
+       }
+
+       if (sc->rxidx != sc->rxlen || sc->txidx != sc->txlen)
+               err = EIO;
+
+       return (err);
+}
+
+static int
+rk_spi_probe(device_t dev)
+{
+       if (!ofw_bus_status_okay(dev))
+               return (ENXIO);
+
+       if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
+               return (ENXIO);
+
+       device_set_desc(dev, "Rockchip SPI");
+       return (BUS_PROBE_DEFAULT);
+}
+
+static int
+rk_spi_attach(device_t dev)
+{
+       struct rk_spi_softc *sc;
+       int error;
+
+       sc = device_get_softc(dev);
+       sc->dev = dev;
+
+       mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+       if (bus_alloc_resources(dev, rk_spi_spec, sc->res) != 0) {
+               device_printf(dev, "cannot allocate resources for device\n");
+               error = ENXIO;
+               goto fail;
+       }
+
+       if (bus_setup_intr(dev, sc->res[1],
+           INTR_TYPE_MISC | INTR_MPSAFE, NULL, rk_spi_intr, sc,
+           &sc->intrhand)) {
+               bus_release_resources(dev, rk_spi_spec, sc->res);
+               device_printf(dev, "cannot setup interrupt handler\n");
+               return (ENXIO);
+       }
+
+       /* Activate the module clock. */
+       error = clk_get_by_ofw_name(dev, 0, "apb_pclk", &sc->clk_apb);
+       if (error != 0) {
+               device_printf(dev, "cannot get apb_pclk clock\n");
+               goto fail;
+       }
+       error = clk_get_by_ofw_name(dev, 0, "spiclk", &sc->clk_spi);
+       if (error != 0) {
+               device_printf(dev, "cannot get spiclk clock\n");
+               goto fail;
+       }
+       error = clk_enable(sc->clk_apb);
+       if (error != 0) {
+               device_printf(dev, "cannot enable ahb clock\n");
+               goto fail;
+       }
+       error = clk_enable(sc->clk_spi);
+       if (error != 0) {
+               device_printf(dev, "cannot enable spiclk clock\n");
+               goto fail;
+       }
+       clk_get_freq(sc->clk_spi, &sc->max_freq);
+
+       sc->fifo_size = rk_spi_fifo_size(sc);
+       if (sc->fifo_size == 0) {
+               device_printf(dev, "failed to get fifo size\n");
+               goto fail;
+       }
+
+       sc->spibus = device_add_child(dev, "spibus", -1);
+
+       RK_SPI_WRITE_4(sc, RK_SPI_IMR, 0);
+       RK_SPI_WRITE_4(sc, RK_SPI_TXFTLR, sc->fifo_size/2 - 1);
+       RK_SPI_WRITE_4(sc, RK_SPI_RXFTLR, sc->fifo_size/2 - 1);
+
+       return (bus_generic_attach(dev));
+
+fail:
+       rk_spi_detach(dev);
+       return (error);
+}
+
+static int
+rk_spi_detach(device_t dev)
+{
+       struct rk_spi_softc *sc;
+
+       sc = device_get_softc(dev);
+
+       bus_generic_detach(sc->dev);
+       if (sc->spibus != NULL)
+               device_delete_child(dev, sc->spibus);
+
+       if (sc->clk_spi != NULL)
+               clk_release(sc->clk_spi);
+       if (sc->clk_apb)
+               clk_release(sc->clk_apb);
+
+       if (sc->intrhand != NULL)
+               bus_teardown_intr(sc->dev, sc->res[1], sc->intrhand);
+
+       bus_release_resources(dev, rk_spi_spec, sc->res);
+       mtx_destroy(&sc->mtx);
+
+       return (0);
+}
+
+static void
+rk_spi_intr(void *arg)
+{
+       struct rk_spi_softc *sc;
+       uint32_t intreg, isr;
+
+       sc = arg;
+
+       RK_SPI_LOCK(sc);
+       intreg = RK_SPI_READ_4(sc, RK_SPI_IMR);
+       isr = RK_SPI_READ_4(sc, RK_SPI_ISR);
+       RK_SPI_WRITE_4(sc, RK_SPI_ICR, isr);
+
+       if (isr & ISR_RFFIS)
+               rk_spi_empty_rxfifo(sc);
+
+       if (isr & ISR_TFEIS)
+               rk_spi_fill_txfifo(sc);
+
+       /* no bytes left, disable interrupt */
+       if (sc->txidx == sc->txlen) {
+               sc->intreg = 0;
+               wakeup(sc);
+       }
+
+       if (sc->intreg != intreg) {
+               (void)RK_SPI_WRITE_4(sc, RK_SPI_IMR, sc->intreg);
+               (void)RK_SPI_READ_4(sc, RK_SPI_IMR);
+       }
+
+       RK_SPI_UNLOCK(sc);
+}
+
+static phandle_t
+rk_spi_get_node(device_t bus, device_t dev)
+{
+
+       return ofw_bus_get_node(bus);
+}
+
+static int
+rk_spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
+{
+       struct rk_spi_softc *sc;
+       uint32_t cs, mode, clock;
+       int err = 0;
+
+       sc = device_get_softc(dev);
+
+       spibus_get_cs(child, &cs);
+       spibus_get_clock(child, &clock);
+       spibus_get_mode(child, &mode);
+
+       RK_SPI_LOCK(sc);
+       rk_spi_hw_setup(sc, mode, clock);
+       rk_spi_enable_chip(sc, 1);
+       err = rk_spi_set_cs(sc, cs, true);
+       if (err != 0) {
+               rk_spi_enable_chip(sc, 0);
+               RK_SPI_UNLOCK(sc);
+               return (err);
+       }
+
+       /* Transfer command then data bytes. */
+       err = 0;
+       if (cmd->tx_cmd_sz > 0)
+               err = rk_spi_xfer_buf(sc, cmd->rx_cmd, cmd->tx_cmd,
+                   cmd->tx_cmd_sz);
+       if (cmd->tx_data_sz > 0 && err == 0)
+               err = rk_spi_xfer_buf(sc, cmd->rx_data, cmd->tx_data,
+                   cmd->tx_data_sz);
+
+       rk_spi_set_cs(sc, cs, false);
+       rk_spi_enable_chip(sc, 0);
+       RK_SPI_UNLOCK(sc);
+
+       return (err);
+}
+
+static device_method_t rk_spi_methods[] = {
+       /* Device interface */
+       DEVMETHOD(device_probe,         rk_spi_probe),
+       DEVMETHOD(device_attach,        rk_spi_attach),
+       DEVMETHOD(device_detach,        rk_spi_detach),
+
+        /* spibus_if  */
+       DEVMETHOD(spibus_transfer,      rk_spi_transfer),
+
+        /* ofw_bus_if */
+       DEVMETHOD(ofw_bus_get_node,     rk_spi_get_node),
+
+       DEVMETHOD_END
+};
+
+static driver_t rk_spi_driver = {
+       "spi",
+       rk_spi_methods,
+       sizeof(struct rk_spi_softc),
+};
+
+static devclass_t rk_spi_devclass;
+
+DRIVER_MODULE(rk_spi, simplebus, rk_spi_driver, rk_spi_devclass, 0, 0);
+DRIVER_MODULE(ofw_spibus, rk_spi, ofw_spibus_driver, ofw_spibus_devclass, 0, 
0);
+MODULE_DEPEND(rk_spi, ofw_spibus, 1, 1, 1);
+SIMPLEBUS_PNP_INFO(compat_data);

Modified: head/sys/conf/files.arm64
==============================================================================
--- head/sys/conf/files.arm64   Fri Oct 25 21:32:28 2019        (r354102)
+++ head/sys/conf/files.arm64   Fri Oct 25 21:38:38 2019        (r354103)
@@ -298,6 +298,7 @@ arm64/rockchip/rk805.c                      optional fdt 
rk805 soc_rockch
 arm64/rockchip/rk_grf.c                        optional fdt 
soc_rockchip_rk3328 | fdt soc_rockchip_rk3399
 arm64/rockchip/rk_pinctrl.c            optional fdt rk_pinctrl 
soc_rockchip_rk3328 | fdt rk_pinctrl soc_rockchip_rk3399
 arm64/rockchip/rk_gpio.c               optional fdt rk_gpio 
soc_rockchip_rk3328 | fdt rk_gpio soc_rockchip_rk3399
+arm64/rockchip/rk_spi.c                        optional fdt rk_spi
 arm64/rockchip/rk_usb2phy.c            optional fdt rk_usb2phy 
soc_rockchip_rk3328 | soc_rockchip_rk3399
 arm64/rockchip/rk_typec_phy.c          optional fdt rk_typec_phy 
soc_rockchip_rk3399
 arm64/rockchip/if_dwc_rk.c             optional fdt dwc_rk soc_rockchip_rk3328 
| fdt dwc_rk soc_rockchip_rk3399
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to