1/*- 2 * Copyright (c) 2012 The NetBSD Foundation, Inc. 3 * All rights reserved. 4 * 5 * This code is derived from software contributed to The NetBSD Foundation 6 * by Paul Fleischer <paul@xpg.dk> 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS 18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 * POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30/* Derived from s3c2410_spi.c */ 31 32/* 33 * Copyright (c) 2004 Genetec Corporation. All rights reserved. 34 * Written by Hiroyuki Bessho for Genetec Corporation. 35 * 36 * Redistribution and use in source and binary forms, with or without 37 * modification, are permitted provided that the following conditions 38 * are met: 39 * 1. Redistributions of source code must retain the above copyright 40 * notice, this list of conditions and the following disclaimer. 41 * 2. Redistributions in binary form must reproduce the above copyright 42 * notice, this list of conditions and the following disclaimer in the 43 * documentation and/or other materials provided with the distribution. 44 * 3. The name of Genetec Corporation may not be used to endorse or 45 * promote products derived from this software without specific prior 46 * written permission. 47 * 48 * THIS SOFTWARE IS PROVIDED BY GENETEC CORPORATION ``AS IS'' AND 49 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 50 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 51 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GENETEC CORPORATION 52 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 53 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 54 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 55 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 56 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 57 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 58 * POSSIBILITY OF SUCH DAMAGE. 59 */ 60 61/* 62 * Support S3C2440's SPI dirver. 63 * Real works are done by drivers attached to SPI ports. 64 */ 65 66#include <sys/cdefs.h> 67__KERNEL_RCSID(0, "$NetBSD: s3c2410_spi.c,v 1.5 2009/03/14 15:36:02 dsl Exp $"); 68 69#include <sys/param.h> 70#include <sys/systm.h> 71#include <sys/conf.h> 72 73#include <sys/mutex.h> 74#include <sys/condvar.h> 75 76#include <sys/bus.h> 77#include <machine/cpu.h> 78 79#include <arm/s3c2xx0/s3c24x0var.h> 80#include <arm/s3c2xx0/s3c24x0reg.h> 81#include <arm/s3c2xx0/s3c2440reg.h> 82 83#include <arm/s3c2xx0/s3c24x0_spi.h> 84 85#include "locators.h" 86 87struct ssspi_softc { 88 struct device dev; 89 90 bus_space_tag_t iot; 91 bus_space_handle_t ioh; 92 short index; 93 94 void *sc_ih; 95 struct kmutex sc_intr_mtx; 96 struct kcondvar sc_intr_cv; 97 uint8_t sc_rxbyte; 98 bool sc_received; 99}; 100 101 102/* prototypes */ 103static int ssspi_match(struct device *, struct cfdata *, void *); 104static void ssspi_attach(struct device *, struct device *, void *); 105static int ssspi_search(struct device *, struct cfdata *, 106 const int *, void *); 107static int ssspi_print(void *, const char *); 108 int ssspi_intr(void *arg); 109 110/* attach structures */ 111CFATTACH_DECL(ssspi, sizeof(struct ssspi_softc), ssspi_match, ssspi_attach, 112 NULL, NULL); 113 114 115static int 116ssspi_print(void *aux, const char *name) 117{ 118 struct ssspi_attach_args *spia = aux; 119 120 if (spia->spia_aux_intr != SSSPICF_INTR_DEFAULT) 121 printf(" intr %d", spia->spia_aux_intr); 122 return (UNCONF); 123} 124 125int 126ssspi_match(struct device *parent, struct cfdata *match, void *aux) 127{ 128 struct s3c2xx0_attach_args *sa = aux; 129 130 /* S3C2440 have only two SPIs */ 131 switch (sa->sa_index) { 132 case 0: 133 case 1: 134 break; 135 default: 136 return 0; 137 } 138 139 return 1; 140} 141 142void 143ssspi_attach(struct device *parent, struct device *self, void *aux) 144{ 145 struct ssspi_softc *sc = (struct ssspi_softc*)self; 146 struct s3c2xx0_attach_args *sa = (struct s3c2xx0_attach_args *)aux; 147 bus_space_tag_t iot = sa->sa_iot; 148 149 static bus_space_handle_t spi_ioh = 0; 150 151 /* we map all registers for SPI0 and SPI1 at once, then 152 use subregions */ 153 if (spi_ioh == 0) { 154 if (bus_space_map(iot, S3C2440_SPI0_BASE, 155 2 * S3C24X0_SPI_SIZE, 156 0, &spi_ioh)) { 157 aprint_error(": can't map registers\n"); 158 return; 159 } 160 } 161 162 aprint_normal("\n"); 163 164 sc->index = sa->sa_index; 165 sc->iot = iot; 166 167 bus_space_subregion(iot, spi_ioh, sc->index == 0 ? 0 : S3C24X0_SPI_SIZE, 168 S3C24X0_SPI_SIZE, &sc->ioh); 169 170 mutex_init(&sc->sc_intr_mtx, MUTEX_DEFAULT, IPL_BIO); 171 cv_init(&sc->sc_intr_cv, "S3C2440_spiintr"); 172 173 /* 174 * Attach child devices 175 */ 176 config_search_ia(ssspi_search, self, "ssspi", NULL); 177} 178 179int 180ssspi_search(struct device *parent, struct cfdata *cf, const int *ldesc, void *aux) 181{ 182 struct ssspi_softc *sc = (struct ssspi_softc *)parent; 183 struct ssspi_attach_args spia; 184 static const unsigned char intr[] = { S3C24X0_INT_SPI0, 185 S3C2440_INT_SPI1 }; 186 187 KASSERT(sc->index == 0 || sc->index == 1); 188 189 spia.spia_iot = sc->iot; 190 spia.spia_ioh = sc->ioh; 191 spia.spia_gpioh = s3c2xx0_softc->sc_gpio_ioh; 192 spia.spia_index = sc->index; 193 spia.spia_intr = intr[sc->index]; 194 spia.spia_aux_intr = cf->cf_loc[SSSPICF_INTR]; 195 spia.spia_dmat = s3c2xx0_softc->sc_dmat; 196 197 if (config_match(parent, cf, &spia)) 198 config_attach(parent, cf, &spia, ssspi_print); 199 200 return 0; 201} 202 203/* 204 * Intiialze SPI port. called by child devices. 205 */ 206int 207s3c24x0_spi_setup(struct ssspi_softc *sc, uint32_t mode, int bps, int use_ss) 208{ 209 int pclk = s3c2xx0_softc->sc_pclk; 210 int prescaler; 211 uint32_t pgcon, pecon, peup; 212 bus_space_handle_t gpioh = s3c2xx0_softc->sc_gpio_ioh; 213 bus_space_tag_t iot = sc->iot; 214 215 if (bps > 1) { 216 prescaler = pclk / 2 / bps - 1; 217 218 if (prescaler <= 0 || 0xff < prescaler) 219 return -1; 220 bus_space_write_1(sc->iot, sc->ioh, SPI_SPPRE, prescaler); 221 } 222 223 if (sc->index == 0) { 224 pecon = bus_space_read_4(iot, gpioh, GPIO_PECON); 225 226 if (use_ss) { 227 pgcon = bus_space_read_4(iot, gpioh, GPIO_PGCON); 228 pgcon = GPIO_SET_FUNC(pgcon, 2, PCON_ALTFUN2); 229 bus_space_write_4(iot, gpioh, GPIO_PGCON, pgcon); 230 } 231 232 pecon = GPIO_SET_FUNC(pecon, 11, PCON_ALTFUN); /* SPIMISO0 */ 233 pecon = GPIO_SET_FUNC(pecon, 12, PCON_ALTFUN); /* SPIMOSI0 */ 234 pecon = GPIO_SET_FUNC(pecon, 13, PCON_ALTFUN); /* SPICL0 */ 235 236 bus_space_write_4(iot, gpioh, GPIO_PECON, pecon); 237 238 /* Enable pull-up for pin 11, 12, and 13*/ 239 peup = bus_space_read_4(iot, gpioh, GPIO_PEUP); 240 peup &= ~(1<<11); 241 peup &= ~(1<<12); 242 peup &= ~(1<<13); 243 bus_space_write_4(iot, gpioh, GPIO_PEUP, peup); 244 245 sc->sc_ih = s3c24x0_intr_establish(S3C24X0_INT_SPI0, IPL_BIO, 246 IST_EDGE_RISING, ssspi_intr, 247 sc); 248 printf("ih: %p\n", sc->sc_ih); 249 } else { 250 pgcon = bus_space_read_4(iot, gpioh, GPIO_PGCON); 251 252 if (use_ss) 253 pgcon = GPIO_SET_FUNC(pgcon, 3, PCON_ALTFUN2); 254 255 pgcon = GPIO_SET_FUNC(pgcon, 5, PCON_ALTFUN2); /* SPIMISO1 */ 256 pgcon = GPIO_SET_FUNC(pgcon, 6, PCON_ALTFUN2); /* SPIMOSI1 */ 257 pgcon = GPIO_SET_FUNC(pgcon, 7, PCON_ALTFUN2); /* SPICLK1 */ 258 259 bus_space_write_4(iot, gpioh, GPIO_PGCON, pgcon); 260 261 /* Enable pull-up for pin 5, 6, and 7*/ 262 peup = bus_space_read_4(iot, gpioh, GPIO_PGUP); 263 peup &= ~(1<<5); 264 peup &= ~(1<<6); 265 peup &= ~(1<<7); 266 bus_space_write_4(iot, gpioh, GPIO_PGUP, peup); 267 268 } 269 270 bus_space_write_4(iot, sc->ioh, SPI_SPCON, mode); 271 272 return 0; 273} 274 275int 276s3c24x0_spi_master_send(struct ssspi_softc *sc, uint8_t value) 277{ 278 sc->sc_received = FALSE; 279 bus_space_write_1(sc->iot, sc->ioh, SPI_SPTDAT, value); 280 281 return 0; 282} 283 284void 285s3c24x0_spi_spin_wait(struct ssspi_softc *sc) 286{ 287 uint32_t reg; 288 do { 289 reg = bus_space_read_4(sc->iot, sc->ioh, SPI_SPSTA); 290 } while(! (reg & SPSTA_REDY)); 291} 292 293int 294s3c24x0_spi_wait(struct ssspi_softc *sc, uint8_t *valPtr) 295{ 296#if 0 297 uint32_t reg; 298 do { 299 reg = bus_space_read_4(sc->iot, sc->ioh, SPI_SPSTA); 300 } while(!(reg & SPSTA_REDY)); 301 302#else 303 mutex_enter(&sc->sc_intr_mtx); 304 while( sc->sc_received == FALSE) { 305 cv_wait(&sc->sc_intr_cv, &sc->sc_intr_mtx); 306 } 307 mutex_exit(&sc->sc_intr_mtx); 308#endif 309 310 if (valPtr != NULL) { 311 // *valPtr = bus_space_read_1(sc->iot, sc->ioh, SPI_SPRDAT); 312 *valPtr = sc->sc_rxbyte; 313 } 314 315 return 0; 316} 317 318int 319s3c24x0_spi_bps(struct ssspi_softc *sc, int bps) 320{ 321 int pclk = s3c2xx0_softc->sc_pclk; 322 int prescaler; 323 324 if (bps > 1) { 325 prescaler = pclk / 2 / bps - 1; 326 327 if (prescaler <= 0 || 0xff < prescaler) 328 return -1; 329 bus_space_write_1(sc->iot, sc->ioh, SPI_SPPRE, prescaler); 330 } 331 332 return 0; 333} 334 335int 336ssspi_intr(void *arg) 337{ 338#if 1 339 uint32_t reg; 340 struct ssspi_softc *sc; 341 342 sc = (struct ssspi_softc*)arg; 343 344 reg = bus_space_read_4(sc->iot, sc->ioh, SPI_SPSTA); 345 if (reg & SPSTA_REDY) { 346 sc->sc_rxbyte = bus_space_read_1(sc->iot, sc->ioh, SPI_SPRDAT); 347 348 mutex_enter(&sc->sc_intr_mtx); 349 sc->sc_received = TRUE; 350 cv_broadcast(&sc->sc_intr_cv); 351 mutex_exit(&sc->sc_intr_mtx); 352 } 353#endif 354 return 1; 355} 356