The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/mips/mediatek/mtk_spi_v1.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org>
    3  * Copyright (c) 2011, Aleksandr Rybalko <ray@FreeBSD.org>
    4  * Copyright (c) 2013, Alexander A. Mityaev <sansan@adm.ua>
    5  * All rights reserved.
    6  *
    7  * Redistribution and use in source and binary forms, with or without
    8  * modification, are permitted provided that the following conditions
    9  * are met:
   10  * 1. Redistributions of source code must retain the above copyright
   11  *    notice unmodified, this list of conditions, and the following
   12  *    disclaimer.
   13  * 2. Redistributions in binary form must reproduce the above copyright
   14  *    notice, this list of conditions and the following disclaimer in the
   15  *    documentation and/or other materials provided with the distribution.
   16  *
   17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   27  * SUCH DAMAGE.
   28  */
   29 
   30 #include <sys/cdefs.h>
   31 __FBSDID("$FreeBSD$");
   32 
   33 #include <sys/param.h>
   34 #include <sys/systm.h>
   35 #include <sys/bus.h>
   36 
   37 #include <sys/kernel.h>
   38 #include <sys/module.h>
   39 #include <sys/rman.h>
   40 #include <sys/lock.h>
   41 #include <sys/mutex.h>
   42 
   43 #include <machine/bus.h>
   44 #include <machine/cpu.h>
   45 
   46 #include <sys/gpio.h>
   47 #include "gpiobus_if.h"
   48 
   49 #include <dev/gpio/gpiobusvar.h>
   50 
   51 #include <dev/spibus/spi.h>
   52 #include <dev/spibus/spibusvar.h>
   53 #include "spibus_if.h"
   54 
   55 #include "opt_platform.h"
   56 
   57 #include <dev/ofw/openfirm.h>
   58 #include <dev/ofw/ofw_bus.h>
   59 #include <dev/ofw/ofw_bus_subr.h>
   60 
   61 #include <mips/mediatek/mtk_soc.h>
   62 #include <mips/mediatek/mtk_spi_v1.h>
   63 #include <dev/flash/mx25lreg.h>
   64 
   65 #undef MTK_SPI_DEBUG
   66 #ifdef MTK_SPI_DEBUG
   67 #define dprintf printf
   68 #else
   69 #define dprintf(x, arg...)
   70 #endif
   71 
   72 /*
   73  * register space access macros
   74  */
   75 #define SPI_WRITE(sc, reg, val) do {    \
   76                 bus_write_4(sc->sc_mem_res, (reg), (val)); \
   77         } while (0)
   78 
   79 #define SPI_READ(sc, reg)        bus_read_4(sc->sc_mem_res, (reg))
   80 
   81 #define SPI_SET_BITS(sc, reg, bits)     \
   82         SPI_WRITE(sc, reg, SPI_READ(sc, (reg)) | (bits))
   83 
   84 #define SPI_CLEAR_BITS(sc, reg, bits)   \
   85         SPI_WRITE(sc, reg, SPI_READ(sc, (reg)) & ~(bits))
   86 
   87 struct mtk_spi_softc {
   88         device_t                sc_dev;
   89         struct resource         *sc_mem_res;
   90         struct gpiobus_pin      *gpio_cs;
   91         int                     nonflash;
   92 };
   93 
   94 static int      mtk_spi_probe(device_t);
   95 static int      mtk_spi_attach(device_t);
   96 static int      mtk_spi_detach(device_t);
   97 static int      mtk_spi_wait(struct mtk_spi_softc *);
   98 static void     mtk_spi_chip_activate(struct mtk_spi_softc *);
   99 static void     mtk_spi_chip_deactivate(struct mtk_spi_softc *);
  100 static uint8_t  mtk_spi_txrx(struct mtk_spi_softc *, uint8_t *, int);
  101 static int      mtk_spi_transfer(device_t, device_t, struct spi_command *);
  102 static phandle_t mtk_spi_get_node(device_t, device_t);
  103 
  104 static struct ofw_compat_data compat_data[] = {
  105         { "ralink,rt2880-spi",  1 },
  106         { "ralink,rt3050-spi",  1 },
  107         { "ralink,rt3352-spi",  1 },
  108         { "ralink,rt3883-spi",  1 },
  109         { "ralink,rt5350-spi",  1 },
  110         { "ralink,mt7620a-spi", 1 },
  111         { NULL,                 0 }
  112 };
  113 
  114 static int
  115 mtk_spi_probe(device_t dev)
  116 {
  117 
  118         if (!ofw_bus_status_okay(dev))
  119                 return (ENXIO);
  120 
  121         if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
  122                 return(ENXIO);
  123 
  124         device_set_desc(dev, "MTK SPI Controller (v1)");
  125 
  126         return (0);
  127 }
  128 
  129 static int
  130 mtk_spi_attach(device_t dev)
  131 {
  132         struct mtk_spi_softc *sc = device_get_softc(dev);
  133         int rid;
  134 
  135         sc->sc_dev = dev;
  136         rid = 0;
  137         sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
  138             RF_SHAREABLE | RF_ACTIVE);
  139         if (!sc->sc_mem_res) {
  140                 device_printf(dev, "Could not map memory\n");
  141                 return (ENXIO);
  142         }
  143 
  144         if (mtk_spi_wait(sc)) {
  145                 bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
  146                 return (EBUSY);
  147         }
  148 
  149         if (ofw_bus_has_prop(dev, "non-flash"))
  150                 sc->nonflash = 1;
  151         else
  152                 sc->nonflash = 0;
  153 
  154         ofw_gpiobus_parse_gpios(dev, "cs-gpios", &sc->gpio_cs);
  155 
  156         if (sc->gpio_cs != NULL) {
  157                 GPIO_PIN_SETFLAGS(sc->gpio_cs->dev, sc->gpio_cs->pin,
  158                     GPIO_PIN_OUTPUT);
  159                 GPIO_PIN_SET(sc->gpio_cs->dev, sc->gpio_cs->pin, 1);
  160         }
  161 
  162         device_add_child(dev, "spibus", -1);
  163         return (bus_generic_attach(dev));
  164 }
  165 
  166 static int
  167 mtk_spi_detach(device_t dev)
  168 {
  169         struct mtk_spi_softc *sc = device_get_softc(dev);
  170 
  171         SPI_SET_BITS(sc, MTK_SPICTL, HIZSMOSI | CS_HIGH);
  172 
  173         if (sc->sc_mem_res)
  174                 bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
  175 
  176         return (0);
  177 }
  178 
  179 static void
  180 mtk_spi_chip_activate(struct mtk_spi_softc *sc)
  181 {
  182         mtk_spi_wait(sc);
  183         /*
  184          * Put all CSx to low
  185          */
  186         if (sc->gpio_cs != NULL) {
  187                 GPIO_PIN_SET(sc->gpio_cs->dev, sc->gpio_cs->pin, 0);
  188                 SPI_CLEAR_BITS(sc, MTK_SPICTL, HIZSMOSI);
  189         } else {
  190                 SPI_CLEAR_BITS(sc, MTK_SPICTL, CS_HIGH | HIZSMOSI);
  191         }
  192 }
  193 
  194 static void
  195 mtk_spi_chip_deactivate(struct mtk_spi_softc *sc)
  196 {
  197         mtk_spi_wait(sc);
  198         /*
  199          * Put all CSx to high
  200          */
  201         if (sc->gpio_cs != NULL) {
  202                 GPIO_PIN_SET(sc->gpio_cs->dev, sc->gpio_cs->pin, 1);
  203                 SPI_SET_BITS(sc, MTK_SPICTL, HIZSMOSI);
  204         } else {
  205                 SPI_SET_BITS(sc, MTK_SPICTL, CS_HIGH | HIZSMOSI);
  206         }
  207 }
  208 
  209 static int
  210 mtk_spi_wait(struct mtk_spi_softc *sc)
  211 {
  212         int i = 1000;
  213 
  214         while (i--) {
  215                 if (!SPI_READ(sc, MTK_SPIBUSY))
  216                         break;
  217         }
  218         if (i == 0) {
  219                 printf("busy\n");
  220                 return (1);
  221         }
  222 
  223         return (0);
  224 }
  225 
  226 static uint8_t
  227 mtk_spi_txrx(struct mtk_spi_softc *sc, uint8_t *data, int write)
  228 {
  229 
  230         if (mtk_spi_wait(sc))
  231                 return (EBUSY);
  232 
  233         if (write == MTK_SPI_WRITE) {
  234                 SPI_WRITE(sc, MTK_SPIDATA, *data);
  235                 SPI_SET_BITS(sc, MTK_SPICTL, START_WRITE);
  236         } else {/* MTK_SPI_READ */
  237                 SPI_SET_BITS(sc, MTK_SPICTL, START_READ);
  238                 if (mtk_spi_wait(sc))
  239                         return (EBUSY);
  240 
  241                 *data = SPI_READ(sc, MTK_SPIDATA) & 0xff;
  242         }
  243         return (0);
  244 }
  245 
  246 static int
  247 mtk_spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
  248 {
  249         struct mtk_spi_softc *sc;
  250         uint8_t *buf, byte, *tx_buf;
  251         uint32_t cs, clock, mode;
  252         int i, sz, error = 0, write = 0;
  253         int div, clk, cfgreg;
  254 
  255         sc = device_get_softc(dev);
  256 
  257         spibus_get_cs(child, &cs);
  258         spibus_get_clock(child, &clock);
  259         spibus_get_mode(child, &mode);
  260 
  261         cs &= ~SPIBUS_CS_HIGH;
  262 
  263         if (cs != 0)
  264                 /* Only 1 CS */
  265                 return (ENXIO);
  266 
  267         cfgreg = MSBFIRST;
  268         switch(mode) {
  269                 case 0: /* This is workadound because of
  270                            mode 0 not work this soc. */
  271                 case 3:
  272                         cfgreg |= SPICLKPOL | TX_ON_CLK_FALL;
  273                         break;
  274                 case 1:
  275                         cfgreg |= TX_ON_CLK_FALL;
  276                         break;
  277                 case 2:
  278                         cfgreg |= CAPT_ON_CLK_FALL;
  279                         break;
  280         }
  281 
  282         /*
  283          * W25Q64CV max 104MHz, bus 120-192 MHz, so divide by 2.
  284          * Update: divide by 4, DEV2 to fast for flash.
  285          */
  286         if (clock != 0) {
  287                 div = (mtk_soc_get_cpuclk() + (clock - 1)) / clock;
  288                 clk = fls(div) - 2;
  289                 if (clk < 0)
  290                         clk = 0;
  291                 else if (clk > 6)
  292                         clk = 6;
  293         } else {
  294                 clk = 6;
  295         }
  296 
  297         SPI_WRITE(sc, MTK_SPICFG, cfgreg | clk);
  298 
  299         if (sc->nonflash == 0) {
  300                 /* There is always a command to transfer. */
  301                 tx_buf = (uint8_t *)(cmd->tx_cmd);
  302 
  303                 /* Perform some fixup because MTK dont support duplex SPI */
  304                 switch(tx_buf[0]) {
  305                         case CMD_READ_IDENT:
  306                                 cmd->tx_cmd_sz = 1;
  307                                 cmd->rx_cmd_sz = 3;
  308                                 break;
  309                         case CMD_ENTER_4B_MODE:
  310                         case CMD_EXIT_4B_MODE:
  311                         case CMD_WRITE_ENABLE:
  312                         case CMD_WRITE_DISABLE:
  313                                 cmd->tx_cmd_sz = 1;
  314                                 cmd->rx_cmd_sz = 0;
  315                                 break;
  316                         case CMD_READ_STATUS:
  317                                 cmd->tx_cmd_sz = 1;
  318                                 cmd->rx_cmd_sz = 1;
  319                                 break;
  320                         case CMD_READ:
  321                         case CMD_FAST_READ:
  322                                 cmd->rx_cmd_sz = cmd->tx_data_sz = 0;
  323                                 break;
  324                         case CMD_SECTOR_ERASE:
  325                                 cmd->rx_cmd_sz = 0;
  326                                 break;
  327                         case CMD_PAGE_PROGRAM:
  328                                 cmd->rx_cmd_sz = cmd->rx_data_sz = 0;
  329                                 break;
  330                 }
  331         }
  332         
  333         mtk_spi_chip_activate(sc);
  334 
  335         if (cmd->tx_cmd_sz + cmd->rx_cmd_sz) {
  336                 buf = (uint8_t *)(cmd->rx_cmd);
  337                 tx_buf = (uint8_t *)(cmd->tx_cmd);
  338                 sz = cmd->tx_cmd_sz;
  339                 if (sc->nonflash == 0)
  340                         sz += cmd->rx_cmd_sz;
  341 
  342                 for (i = 0; i < sz; i++) {
  343                         if(i < cmd->tx_cmd_sz) {
  344                                 byte = tx_buf[i];
  345                                 error = mtk_spi_txrx(sc, &byte,
  346                                     MTK_SPI_WRITE);
  347                                 if (error)
  348                                         goto mtk_spi_transfer_fail;
  349                                 continue;
  350                         }
  351                         error = mtk_spi_txrx(sc, &byte,
  352                             MTK_SPI_READ);
  353                         if (error)
  354                                 goto mtk_spi_transfer_fail;
  355                         buf[i] = byte;
  356                 }
  357         }
  358 
  359         /*
  360          * Transfer/Receive data
  361          */
  362 
  363         if (cmd->tx_data_sz + cmd->rx_data_sz) {
  364                 write = (cmd->tx_data_sz > 0)?1:0;
  365                 buf = (uint8_t *)(write ? cmd->tx_data : cmd->rx_data);
  366                 sz = write ? cmd->tx_data_sz : cmd->rx_data_sz;
  367 
  368                 for (i = 0; i < sz; i++) {
  369                         byte = buf[i];
  370                         error = mtk_spi_txrx(sc, &byte,
  371                             write ? MTK_SPI_WRITE : MTK_SPI_READ);
  372                         if (error)
  373                                 goto mtk_spi_transfer_fail;
  374                         buf[i] = byte;
  375                 }
  376         }
  377 mtk_spi_transfer_fail:
  378         mtk_spi_chip_deactivate(sc);
  379 
  380         return (error);
  381 }
  382 
  383 static phandle_t
  384 mtk_spi_get_node(device_t bus, device_t dev)
  385 {
  386 
  387         /* We only have one child, the SPI bus, which needs our own node. */
  388         return (ofw_bus_get_node(bus));
  389 }
  390 
  391 static device_method_t mtk_spi_methods[] = {
  392         /* Device interface */
  393         DEVMETHOD(device_probe,         mtk_spi_probe),
  394         DEVMETHOD(device_attach,        mtk_spi_attach),
  395         DEVMETHOD(device_detach,        mtk_spi_detach),
  396 
  397         DEVMETHOD(spibus_transfer,      mtk_spi_transfer),
  398 
  399         /* ofw_bus interface */
  400         DEVMETHOD(ofw_bus_get_node,     mtk_spi_get_node),
  401 
  402         DEVMETHOD_END
  403 };
  404 
  405 static driver_t mtk_spi_driver = {
  406         .name = "spi",
  407         .methods = mtk_spi_methods,
  408         .size = sizeof(struct mtk_spi_softc),
  409 };
  410 
  411 static devclass_t mtk_spi_devclass;
  412 
  413 DRIVER_MODULE(mtk_spi_v1, simplebus, mtk_spi_driver, mtk_spi_devclass, 0, 0);

Cache object: 0dda66a94d01556a7ffe01f8f70f37f5


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.