1192357Sgonzo/*- 2192357Sgonzo * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org> 3192357Sgonzo * All rights reserved. 4192357Sgonzo * 5192357Sgonzo * Redistribution and use in source and binary forms, with or without 6192357Sgonzo * modification, are permitted provided that the following conditions 7192357Sgonzo * are met: 8192357Sgonzo * 1. Redistributions of source code must retain the above copyright 9192357Sgonzo * notice unmodified, this list of conditions, and the following 10192357Sgonzo * disclaimer. 11192357Sgonzo * 2. Redistributions in binary form must reproduce the above copyright 12192357Sgonzo * notice, this list of conditions and the following disclaimer in the 13192357Sgonzo * documentation and/or other materials provided with the distribution. 14192357Sgonzo * 15192357Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16192357Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17192357Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18192357Sgonzo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19192357Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20192357Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21192357Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22192357Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23192357Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24192357Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25192357Sgonzo * SUCH DAMAGE. 26192357Sgonzo */ 27192357Sgonzo 28192357Sgonzo#include <sys/cdefs.h> 29192357Sgonzo__FBSDID("$FreeBSD$"); 30192357Sgonzo 31192357Sgonzo#include <sys/param.h> 32192357Sgonzo#include <sys/systm.h> 33192357Sgonzo 34192357Sgonzo#include <sys/bus.h> 35192357Sgonzo#include <sys/interrupt.h> 36192357Sgonzo#include <sys/malloc.h> 37192357Sgonzo#include <sys/kernel.h> 38192357Sgonzo#include <sys/module.h> 39192357Sgonzo#include <sys/rman.h> 40192357Sgonzo 41192357Sgonzo#include <vm/vm.h> 42192357Sgonzo#include <vm/pmap.h> 43192357Sgonzo#include <vm/vm_extern.h> 44192357Sgonzo 45192357Sgonzo#include <machine/bus.h> 46192357Sgonzo#include <machine/cpu.h> 47192357Sgonzo#include <machine/pmap.h> 48192357Sgonzo 49192357Sgonzo#include <dev/spibus/spi.h> 50192357Sgonzo#include <dev/spibus/spibusvar.h> 51192357Sgonzo#include "spibus_if.h" 52192357Sgonzo 53192357Sgonzo#include <mips/atheros/ar71xxreg.h> 54192357Sgonzo 55192357Sgonzo#undef AR71XX_SPI_DEBUG 56192357Sgonzo#ifdef AR71XX_SPI_DEBUG 57192357Sgonzo#define dprintf printf 58192357Sgonzo#else 59192357Sgonzo#define dprintf(x, arg...) 60192357Sgonzo#endif 61192357Sgonzo 62192357Sgonzo/* 63192357Sgonzo * register space access macros 64192357Sgonzo */ 65192357Sgonzo#define SPI_WRITE(sc, reg, val) do { \ 66192357Sgonzo bus_write_4(sc->sc_mem_res, (reg), (val)); \ 67192357Sgonzo } while (0) 68192357Sgonzo 69192357Sgonzo#define SPI_READ(sc, reg) bus_read_4(sc->sc_mem_res, (reg)) 70192357Sgonzo 71192357Sgonzo#define SPI_SET_BITS(sc, reg, bits) \ 72192357Sgonzo SPI_WRITE(sc, reg, SPI_READ(sc, (reg)) | (bits)) 73192357Sgonzo 74192357Sgonzo#define SPI_CLEAR_BITS(sc, reg, bits) \ 75192357Sgonzo SPI_WRITE(sc, reg, SPI_READ(sc, (reg)) & ~(bits)) 76192357Sgonzo 77192357Sgonzostruct ar71xx_spi_softc { 78192357Sgonzo device_t sc_dev; 79192357Sgonzo struct resource *sc_mem_res; 80202723Sgonzo uint32_t sc_reg_ctrl; 81192357Sgonzo}; 82192357Sgonzo 83192357Sgonzostatic int 84192357Sgonzoar71xx_spi_probe(device_t dev) 85192357Sgonzo{ 86192357Sgonzo device_set_desc(dev, "AR71XX SPI"); 87192357Sgonzo return (0); 88192357Sgonzo} 89192357Sgonzo 90192357Sgonzostatic int 91192357Sgonzoar71xx_spi_attach(device_t dev) 92192357Sgonzo{ 93192357Sgonzo struct ar71xx_spi_softc *sc = device_get_softc(dev); 94192357Sgonzo int rid; 95192357Sgonzo 96192357Sgonzo sc->sc_dev = dev; 97192357Sgonzo rid = 0; 98192357Sgonzo sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 99192357Sgonzo RF_ACTIVE); 100192357Sgonzo if (!sc->sc_mem_res) { 101192357Sgonzo device_printf(dev, "Could not map memory\n"); 102192357Sgonzo return (ENXIO); 103192357Sgonzo } 104192357Sgonzo 105192357Sgonzo 106202723Sgonzo SPI_WRITE(sc, AR71XX_SPI_FS, 1); 107202723Sgonzo sc->sc_reg_ctrl = SPI_READ(sc, AR71XX_SPI_CTRL); 108202723Sgonzo SPI_WRITE(sc, AR71XX_SPI_CTRL, 0x43); 109202723Sgonzo SPI_WRITE(sc, AR71XX_SPI_IO_CTRL, SPI_IO_CTRL_CSMASK); 110192357Sgonzo 111192357Sgonzo device_add_child(dev, "spibus", 0); 112192357Sgonzo return (bus_generic_attach(dev)); 113192357Sgonzo} 114192357Sgonzo 115192357Sgonzostatic void 116192357Sgonzoar71xx_spi_chip_activate(struct ar71xx_spi_softc *sc, int cs) 117192357Sgonzo{ 118202723Sgonzo uint32_t ioctrl = SPI_IO_CTRL_CSMASK; 119192357Sgonzo /* 120192357Sgonzo * Put respective CSx to low 121192357Sgonzo */ 122192357Sgonzo ioctrl &= ~(SPI_IO_CTRL_CS0 << cs); 123192357Sgonzo 124192357Sgonzo SPI_WRITE(sc, AR71XX_SPI_IO_CTRL, ioctrl); 125192357Sgonzo} 126192357Sgonzo 127192357Sgonzostatic void 128192357Sgonzoar71xx_spi_chip_deactivate(struct ar71xx_spi_softc *sc, int cs) 129192357Sgonzo{ 130192357Sgonzo /* 131192357Sgonzo * Put all CSx to high 132192357Sgonzo */ 133202723Sgonzo SPI_WRITE(sc, AR71XX_SPI_IO_CTRL, SPI_IO_CTRL_CSMASK); 134192357Sgonzo} 135192357Sgonzo 136192357Sgonzostatic uint8_t 137202723Sgonzoar71xx_spi_txrx(struct ar71xx_spi_softc *sc, int cs, uint8_t data) 138192357Sgonzo{ 139192357Sgonzo int bit; 140192357Sgonzo /* CS0 */ 141202723Sgonzo uint32_t ioctrl = SPI_IO_CTRL_CSMASK; 142202723Sgonzo /* 143202723Sgonzo * low-level for selected CS 144202723Sgonzo */ 145202723Sgonzo ioctrl &= ~(SPI_IO_CTRL_CS0 << cs); 146192357Sgonzo 147192357Sgonzo uint32_t iod, rds; 148192357Sgonzo for (bit = 7; bit >=0; bit--) { 149192357Sgonzo if (data & (1 << bit)) 150192357Sgonzo iod = ioctrl | SPI_IO_CTRL_DO; 151192357Sgonzo else 152192357Sgonzo iod = ioctrl & ~SPI_IO_CTRL_DO; 153192357Sgonzo SPI_WRITE(sc, AR71XX_SPI_IO_CTRL, iod); 154192357Sgonzo SPI_WRITE(sc, AR71XX_SPI_IO_CTRL, iod | SPI_IO_CTRL_CLK); 155192357Sgonzo } 156192357Sgonzo 157202723Sgonzo /* 158202723Sgonzo * Provide falling edge for connected device by clear clock bit. 159202723Sgonzo */ 160202723Sgonzo SPI_WRITE(sc, AR71XX_SPI_IO_CTRL, iod); 161192357Sgonzo rds = SPI_READ(sc, AR71XX_SPI_RDS); 162192357Sgonzo 163192357Sgonzo return (rds & 0xff); 164192357Sgonzo} 165192357Sgonzo 166192357Sgonzostatic int 167192357Sgonzoar71xx_spi_transfer(device_t dev, device_t child, struct spi_command *cmd) 168192357Sgonzo{ 169192357Sgonzo struct ar71xx_spi_softc *sc; 170192357Sgonzo uint8_t *buf_in, *buf_out; 171192357Sgonzo struct spibus_ivar *devi = SPIBUS_IVAR(child); 172192357Sgonzo int i; 173192357Sgonzo 174192357Sgonzo sc = device_get_softc(dev); 175192357Sgonzo 176192357Sgonzo ar71xx_spi_chip_activate(sc, devi->cs); 177192357Sgonzo 178192357Sgonzo KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz, 179192357Sgonzo ("TX/RX command sizes should be equal")); 180192357Sgonzo KASSERT(cmd->tx_data_sz == cmd->rx_data_sz, 181192357Sgonzo ("TX/RX data sizes should be equal")); 182192357Sgonzo 183192357Sgonzo /* 184192357Sgonzo * Transfer command 185192357Sgonzo */ 186192357Sgonzo buf_out = (uint8_t *)cmd->tx_cmd; 187192357Sgonzo buf_in = (uint8_t *)cmd->rx_cmd; 188192357Sgonzo for (i = 0; i < cmd->tx_cmd_sz; i++) 189202723Sgonzo buf_in[i] = ar71xx_spi_txrx(sc, devi->cs, buf_out[i]); 190192357Sgonzo 191192357Sgonzo /* 192192357Sgonzo * Receive/transmit data (depends on command) 193192357Sgonzo */ 194192357Sgonzo buf_out = (uint8_t *)cmd->tx_data; 195192357Sgonzo buf_in = (uint8_t *)cmd->rx_data; 196192357Sgonzo for (i = 0; i < cmd->tx_data_sz; i++) 197202723Sgonzo buf_in[i] = ar71xx_spi_txrx(sc, devi->cs, buf_out[i]); 198192357Sgonzo 199192357Sgonzo ar71xx_spi_chip_deactivate(sc, devi->cs); 200192357Sgonzo 201192357Sgonzo return (0); 202192357Sgonzo} 203192357Sgonzo 204192357Sgonzostatic int 205192357Sgonzoar71xx_spi_detach(device_t dev) 206192357Sgonzo{ 207202723Sgonzo struct ar71xx_spi_softc *sc = device_get_softc(dev); 208192357Sgonzo 209202723Sgonzo SPI_WRITE(sc, AR71XX_SPI_CTRL, sc->sc_reg_ctrl); 210202723Sgonzo SPI_WRITE(sc, AR71XX_SPI_FS, 0); 211202723Sgonzo 212202723Sgonzo if (sc->sc_mem_res) 213202723Sgonzo bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); 214202723Sgonzo 215202723Sgonzo return (0); 216192357Sgonzo} 217192357Sgonzo 218192357Sgonzostatic device_method_t ar71xx_spi_methods[] = { 219192357Sgonzo /* Device interface */ 220192357Sgonzo DEVMETHOD(device_probe, ar71xx_spi_probe), 221192357Sgonzo DEVMETHOD(device_attach, ar71xx_spi_attach), 222192357Sgonzo DEVMETHOD(device_detach, ar71xx_spi_detach), 223192357Sgonzo 224192357Sgonzo DEVMETHOD(spibus_transfer, ar71xx_spi_transfer), 225192357Sgonzo 226192357Sgonzo {0, 0} 227192357Sgonzo}; 228192357Sgonzo 229192357Sgonzostatic driver_t ar71xx_spi_driver = { 230192357Sgonzo "spi", 231192357Sgonzo ar71xx_spi_methods, 232192357Sgonzo sizeof(struct ar71xx_spi_softc), 233192357Sgonzo}; 234192357Sgonzo 235192357Sgonzostatic devclass_t ar71xx_spi_devclass; 236192357Sgonzo 237192357SgonzoDRIVER_MODULE(ar71xx_spi, nexus, ar71xx_spi_driver, ar71xx_spi_devclass, 0, 0); 238