1239278Sgonzo/*- 2239278Sgonzo * Copyright (c) 2011 Jakub Wojciech Klama <jceel@FreeBSD.org> 3239278Sgonzo * All rights reserved. 4239278Sgonzo * 5239278Sgonzo * Redistribution and use in source and binary forms, with or without 6239278Sgonzo * modification, are permitted provided that the following conditions 7239278Sgonzo * are met: 8239278Sgonzo * 1. Redistributions of source code must retain the above copyright 9239278Sgonzo * notice, this list of conditions and the following disclaimer. 10239278Sgonzo * 2. Redistributions in binary form must reproduce the above copyright 11239278Sgonzo * notice, this list of conditions and the following disclaimer in the 12239278Sgonzo * documentation and/or other materials provided with the distribution. 13239278Sgonzo * 14239278Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15239278Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16239278Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17239278Sgonzo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18239278Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19239278Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20239278Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21239278Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22239278Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23239278Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24239278Sgonzo * SUCH DAMAGE. 25239278Sgonzo * 26239278Sgonzo */ 27239278Sgonzo#include <sys/cdefs.h> 28239278Sgonzo__FBSDID("$FreeBSD: releng/11.0/sys/arm/lpc/lpc_spi.c 261410 2014-02-02 19:17:28Z ian $"); 29239278Sgonzo 30239278Sgonzo#include <sys/param.h> 31239278Sgonzo#include <sys/systm.h> 32239278Sgonzo#include <sys/bio.h> 33239278Sgonzo#include <sys/bus.h> 34239278Sgonzo#include <sys/conf.h> 35239278Sgonzo#include <sys/endian.h> 36239278Sgonzo#include <sys/kernel.h> 37239278Sgonzo#include <sys/kthread.h> 38239278Sgonzo#include <sys/lock.h> 39239278Sgonzo#include <sys/malloc.h> 40239278Sgonzo#include <sys/module.h> 41239278Sgonzo#include <sys/mutex.h> 42239278Sgonzo#include <sys/queue.h> 43239278Sgonzo#include <sys/resource.h> 44239278Sgonzo#include <sys/rman.h> 45239278Sgonzo#include <sys/time.h> 46239278Sgonzo#include <sys/timetc.h> 47239278Sgonzo#include <sys/watchdog.h> 48239278Sgonzo 49239278Sgonzo#include <machine/bus.h> 50239278Sgonzo#include <machine/cpu.h> 51239278Sgonzo#include <machine/cpufunc.h> 52239278Sgonzo#include <machine/resource.h> 53239278Sgonzo#include <machine/intr.h> 54239278Sgonzo 55239278Sgonzo#include <dev/spibus/spi.h> 56239278Sgonzo#include <dev/spibus/spibusvar.h> 57239278Sgonzo 58239278Sgonzo#include <dev/ofw/ofw_bus.h> 59239278Sgonzo#include <dev/ofw/ofw_bus_subr.h> 60239278Sgonzo 61239278Sgonzo#include <arm/lpc/lpcreg.h> 62239278Sgonzo#include <arm/lpc/lpcvar.h> 63239278Sgonzo 64239278Sgonzo#include "spibus_if.h" 65239278Sgonzo 66239278Sgonzostruct lpc_spi_softc 67239278Sgonzo{ 68239278Sgonzo device_t ls_dev; 69239278Sgonzo struct resource * ls_mem_res; 70239278Sgonzo struct resource * ls_irq_res; 71239278Sgonzo bus_space_tag_t ls_bst; 72239278Sgonzo bus_space_handle_t ls_bsh; 73239278Sgonzo}; 74239278Sgonzo 75239278Sgonzostatic int lpc_spi_probe(device_t); 76239278Sgonzostatic int lpc_spi_attach(device_t); 77239278Sgonzostatic int lpc_spi_detach(device_t); 78239278Sgonzostatic int lpc_spi_transfer(device_t, device_t, struct spi_command *); 79239278Sgonzo 80239278Sgonzo#define lpc_spi_read_4(_sc, _reg) \ 81239278Sgonzo bus_space_read_4(_sc->ls_bst, _sc->ls_bsh, _reg) 82239278Sgonzo#define lpc_spi_write_4(_sc, _reg, _val) \ 83239278Sgonzo bus_space_write_4(_sc->ls_bst, _sc->ls_bsh, _reg, _val) 84239278Sgonzo 85239278Sgonzostatic int 86239278Sgonzolpc_spi_probe(device_t dev) 87239278Sgonzo{ 88261410Sian 89261410Sian if (!ofw_bus_status_okay(dev)) 90261410Sian return (ENXIO); 91261410Sian 92239278Sgonzo if (!ofw_bus_is_compatible(dev, "lpc,spi")) 93239278Sgonzo return (ENXIO); 94239278Sgonzo 95239278Sgonzo device_set_desc(dev, "LPC32x0 PL022 SPI/SSP controller"); 96239278Sgonzo return (BUS_PROBE_DEFAULT); 97239278Sgonzo} 98239278Sgonzo 99239278Sgonzostatic int 100239278Sgonzolpc_spi_attach(device_t dev) 101239278Sgonzo{ 102239278Sgonzo struct lpc_spi_softc *sc = device_get_softc(dev); 103239278Sgonzo int rid; 104239278Sgonzo 105239278Sgonzo sc->ls_dev = dev; 106239278Sgonzo 107239278Sgonzo rid = 0; 108239278Sgonzo sc->ls_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 109239278Sgonzo RF_ACTIVE); 110239278Sgonzo if (!sc->ls_mem_res) { 111239278Sgonzo device_printf(dev, "cannot allocate memory window\n"); 112239278Sgonzo return (ENXIO); 113239278Sgonzo } 114239278Sgonzo 115239278Sgonzo sc->ls_bst = rman_get_bustag(sc->ls_mem_res); 116239278Sgonzo sc->ls_bsh = rman_get_bushandle(sc->ls_mem_res); 117239278Sgonzo 118239278Sgonzo rid = 0; 119239278Sgonzo sc->ls_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 120239278Sgonzo RF_ACTIVE); 121239278Sgonzo if (!sc->ls_irq_res) { 122239278Sgonzo device_printf(dev, "cannot allocate interrupt\n"); 123239278Sgonzo return (ENXIO); 124239278Sgonzo } 125239278Sgonzo 126239278Sgonzo bus_space_write_4(sc->ls_bst, 0xd0028100, 0, (1<<12)|(1<<10)|(1<<9)|(1<<8)|(1<<6)|(1<<5)); 127239278Sgonzo lpc_pwr_write(dev, LPC_CLKPWR_SSP_CTRL, LPC_CLKPWR_SSP_CTRL_SSP0EN); 128239278Sgonzo lpc_spi_write_4(sc, LPC_SSP_CR0, LPC_SSP_CR0_DSS(8)); 129239278Sgonzo lpc_spi_write_4(sc, LPC_SSP_CR1, LPC_SSP_CR1_SSE); 130239278Sgonzo lpc_spi_write_4(sc, LPC_SSP_CPSR, 128); 131239278Sgonzo 132239278Sgonzo device_add_child(dev, "spibus", 0); 133239278Sgonzo return (bus_generic_attach(dev)); 134239278Sgonzo} 135239278Sgonzo 136239278Sgonzostatic int 137239278Sgonzolpc_spi_detach(device_t dev) 138239278Sgonzo{ 139239278Sgonzo return (EBUSY); 140239278Sgonzo} 141239278Sgonzo 142239278Sgonzostatic int 143239278Sgonzolpc_spi_transfer(device_t dev, device_t child, struct spi_command *cmd) 144239278Sgonzo{ 145239278Sgonzo struct lpc_spi_softc *sc = device_get_softc(dev); 146239278Sgonzo struct spibus_ivar *devi = SPIBUS_IVAR(child); 147239278Sgonzo uint8_t *in_buf, *out_buf; 148239278Sgonzo int i; 149239278Sgonzo 150239278Sgonzo /* Set CS active */ 151239278Sgonzo lpc_gpio_set_state(child, devi->cs, 0); 152239278Sgonzo 153239278Sgonzo /* Wait for FIFO to be ready */ 154239278Sgonzo while ((lpc_spi_read_4(sc, LPC_SSP_SR) & LPC_SSP_SR_TNF) == 0); 155239278Sgonzo 156239278Sgonzo /* Command */ 157239278Sgonzo in_buf = cmd->rx_cmd; 158239278Sgonzo out_buf = cmd->tx_cmd; 159239278Sgonzo for (i = 0; i < cmd->tx_cmd_sz; i++) { 160239278Sgonzo lpc_spi_write_4(sc, LPC_SSP_DR, out_buf[i]); 161239278Sgonzo in_buf[i] = lpc_spi_read_4(sc, LPC_SSP_DR); 162239278Sgonzo } 163239278Sgonzo 164239278Sgonzo /* Data */ 165239278Sgonzo in_buf = cmd->rx_data; 166239278Sgonzo out_buf = cmd->tx_data; 167239278Sgonzo for (i = 0; i < cmd->tx_data_sz; i++) { 168239278Sgonzo lpc_spi_write_4(sc, LPC_SSP_DR, out_buf[i]); 169239278Sgonzo in_buf[i] = lpc_spi_read_4(sc, LPC_SSP_DR); 170239278Sgonzo } 171239278Sgonzo 172239278Sgonzo /* Set CS inactive */ 173239278Sgonzo lpc_gpio_set_state(child, devi->cs, 1); 174239278Sgonzo 175239278Sgonzo return (0); 176239278Sgonzo} 177239278Sgonzo 178239278Sgonzostatic device_method_t lpc_spi_methods[] = { 179239278Sgonzo /* Device interface */ 180239278Sgonzo DEVMETHOD(device_probe, lpc_spi_probe), 181239278Sgonzo DEVMETHOD(device_attach, lpc_spi_attach), 182239278Sgonzo DEVMETHOD(device_detach, lpc_spi_detach), 183239278Sgonzo 184239278Sgonzo /* SPI interface */ 185239278Sgonzo DEVMETHOD(spibus_transfer, lpc_spi_transfer), 186239278Sgonzo 187239278Sgonzo { 0, 0 } 188239278Sgonzo}; 189239278Sgonzo 190239278Sgonzostatic devclass_t lpc_spi_devclass; 191239278Sgonzo 192239278Sgonzostatic driver_t lpc_spi_driver = { 193239278Sgonzo "spi", 194239278Sgonzo lpc_spi_methods, 195239278Sgonzo sizeof(struct lpc_spi_softc), 196239278Sgonzo}; 197239278Sgonzo 198239278SgonzoDRIVER_MODULE(lpcspi, simplebus, lpc_spi_driver, lpc_spi_devclass, 0, 0); 199