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: s3c2440_spi.c,v 1.2 2012/10/27 17:17:40 chs 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 87 struct ssspi_softc { 88 bus_space_tag_t iot; 89 bus_space_handle_t ioh; 90 short index; 91 92 void *sc_ih; 93 struct kmutex sc_intr_mtx; 94 struct kcondvar sc_intr_cv; 95 uint8_t sc_rxbyte; 96 bool sc_received; 97 }; 98 99 100 /* prototypes */ 101 static int ssspi_match(device_t, cfdata_t, void *); 102 static void ssspi_attach(device_t, device_t, void *); 103 static int ssspi_search(device_t, cfdata_t, const int *, void *); 104 static int ssspi_print(void *, const char *); 105 int ssspi_intr(void *arg); 106 107 /* attach structures */ 108 CFATTACH_DECL_NEW(ssspi, sizeof(struct ssspi_softc), ssspi_match, ssspi_attach, 109 NULL, NULL); 110 111 112 static int 113 ssspi_print(void *aux, const char *name) 114 { 115 struct ssspi_attach_args *spia = aux; 116 117 if (spia->spia_aux_intr != SSSPICF_INTR_DEFAULT) 118 printf(" intr %d", spia->spia_aux_intr); 119 return (UNCONF); 120 } 121 122 int 123 ssspi_match(device_t parent, cfdata_t match, void *aux) 124 { 125 struct s3c2xx0_attach_args *sa = aux; 126 127 /* S3C2440 have only two SPIs */ 128 switch (sa->sa_index) { 129 case 0: 130 case 1: 131 break; 132 default: 133 return 0; 134 } 135 136 return 1; 137 } 138 139 void 140 ssspi_attach(device_t parent, device_t self, void *aux) 141 { 142 struct ssspi_softc *sc = device_private(self); 143 struct s3c2xx0_attach_args *sa = aux; 144 bus_space_tag_t iot = sa->sa_iot; 145 146 static bus_space_handle_t spi_ioh = 0; 147 148 /* we map all registers for SPI0 and SPI1 at once, then 149 use subregions */ 150 if (spi_ioh == 0) { 151 if (bus_space_map(iot, S3C2440_SPI0_BASE, 152 2 * S3C24X0_SPI_SIZE, 153 0, &spi_ioh)) { 154 aprint_error(": can't map registers\n"); 155 return; 156 } 157 } 158 159 aprint_normal("\n"); 160 161 sc->index = sa->sa_index; 162 sc->iot = iot; 163 164 bus_space_subregion(iot, spi_ioh, sc->index == 0 ? 0 : S3C24X0_SPI_SIZE, 165 S3C24X0_SPI_SIZE, &sc->ioh); 166 167 mutex_init(&sc->sc_intr_mtx, MUTEX_DEFAULT, IPL_BIO); 168 cv_init(&sc->sc_intr_cv, "S3C2440_spiintr"); 169 170 /* 171 * Attach child devices 172 */ 173 config_search_ia(ssspi_search, self, "ssspi", NULL); 174 } 175 176 int 177 ssspi_search(device_t parent, cfdata_t cf, const int *ldesc, void *aux) 178 { 179 struct ssspi_softc *sc = device_private(parent); 180 struct ssspi_attach_args spia; 181 static const unsigned char intr[] = { S3C24X0_INT_SPI0, 182 S3C2440_INT_SPI1 }; 183 184 KASSERT(sc->index == 0 || sc->index == 1); 185 186 spia.spia_iot = sc->iot; 187 spia.spia_ioh = sc->ioh; 188 spia.spia_gpioh = s3c2xx0_softc->sc_gpio_ioh; 189 spia.spia_index = sc->index; 190 spia.spia_intr = intr[sc->index]; 191 spia.spia_aux_intr = cf->cf_loc[SSSPICF_INTR]; 192 spia.spia_dmat = s3c2xx0_softc->sc_dmat; 193 194 if (config_match(parent, cf, &spia)) 195 config_attach(parent, cf, &spia, ssspi_print); 196 197 return 0; 198 } 199 200 /* 201 * Intiialze SPI port. called by child devices. 202 */ 203 int 204 s3c24x0_spi_setup(struct ssspi_softc *sc, uint32_t mode, int bps, int use_ss) 205 { 206 int pclk = s3c2xx0_softc->sc_pclk; 207 int prescaler; 208 uint32_t pgcon, pecon, peup; 209 bus_space_handle_t gpioh = s3c2xx0_softc->sc_gpio_ioh; 210 bus_space_tag_t iot = sc->iot; 211 212 if (bps > 1) { 213 prescaler = pclk / 2 / bps - 1; 214 215 if (prescaler <= 0 || 0xff < prescaler) 216 return -1; 217 bus_space_write_1(sc->iot, sc->ioh, SPI_SPPRE, prescaler); 218 } 219 220 if (sc->index == 0) { 221 pecon = bus_space_read_4(iot, gpioh, GPIO_PECON); 222 223 if (use_ss) { 224 pgcon = bus_space_read_4(iot, gpioh, GPIO_PGCON); 225 pgcon = GPIO_SET_FUNC(pgcon, 2, PCON_ALTFUN2); 226 bus_space_write_4(iot, gpioh, GPIO_PGCON, pgcon); 227 } 228 229 pecon = GPIO_SET_FUNC(pecon, 11, PCON_ALTFUN); /* SPIMISO0 */ 230 pecon = GPIO_SET_FUNC(pecon, 12, PCON_ALTFUN); /* SPIMOSI0 */ 231 pecon = GPIO_SET_FUNC(pecon, 13, PCON_ALTFUN); /* SPICL0 */ 232 233 bus_space_write_4(iot, gpioh, GPIO_PECON, pecon); 234 235 /* Enable pull-up for pin 11, 12, and 13*/ 236 peup = bus_space_read_4(iot, gpioh, GPIO_PEUP); 237 peup &= ~(1<<11); 238 peup &= ~(1<<12); 239 peup &= ~(1<<13); 240 bus_space_write_4(iot, gpioh, GPIO_PEUP, peup); 241 242 sc->sc_ih = s3c24x0_intr_establish(S3C24X0_INT_SPI0, IPL_BIO, 243 IST_EDGE_RISING, ssspi_intr, 244 sc); 245 printf("ih: %p\n", sc->sc_ih); 246 } else { 247 pgcon = bus_space_read_4(iot, gpioh, GPIO_PGCON); 248 249 if (use_ss) 250 pgcon = GPIO_SET_FUNC(pgcon, 3, PCON_ALTFUN2); 251 252 pgcon = GPIO_SET_FUNC(pgcon, 5, PCON_ALTFUN2); /* SPIMISO1 */ 253 pgcon = GPIO_SET_FUNC(pgcon, 6, PCON_ALTFUN2); /* SPIMOSI1 */ 254 pgcon = GPIO_SET_FUNC(pgcon, 7, PCON_ALTFUN2); /* SPICLK1 */ 255 256 bus_space_write_4(iot, gpioh, GPIO_PGCON, pgcon); 257 258 /* Enable pull-up for pin 5, 6, and 7*/ 259 peup = bus_space_read_4(iot, gpioh, GPIO_PGUP); 260 peup &= ~(1<<5); 261 peup &= ~(1<<6); 262 peup &= ~(1<<7); 263 bus_space_write_4(iot, gpioh, GPIO_PGUP, peup); 264 265 } 266 267 bus_space_write_4(iot, sc->ioh, SPI_SPCON, mode); 268 269 return 0; 270 } 271 272 int 273 s3c24x0_spi_master_send(struct ssspi_softc *sc, uint8_t value) 274 { 275 sc->sc_received = FALSE; 276 bus_space_write_1(sc->iot, sc->ioh, SPI_SPTDAT, value); 277 278 return 0; 279 } 280 281 void 282 s3c24x0_spi_spin_wait(struct ssspi_softc *sc) 283 { 284 uint32_t reg; 285 do { 286 reg = bus_space_read_4(sc->iot, sc->ioh, SPI_SPSTA); 287 } while(! (reg & SPSTA_REDY)); 288 } 289 290 int 291 s3c24x0_spi_wait(struct ssspi_softc *sc, uint8_t *valPtr) 292 { 293 #if 0 294 uint32_t reg; 295 do { 296 reg = bus_space_read_4(sc->iot, sc->ioh, SPI_SPSTA); 297 } while(!(reg & SPSTA_REDY)); 298 299 #else 300 mutex_enter(&sc->sc_intr_mtx); 301 while( sc->sc_received == FALSE) { 302 cv_wait(&sc->sc_intr_cv, &sc->sc_intr_mtx); 303 } 304 mutex_exit(&sc->sc_intr_mtx); 305 #endif 306 307 if (valPtr != NULL) { 308 // *valPtr = bus_space_read_1(sc->iot, sc->ioh, SPI_SPRDAT); 309 *valPtr = sc->sc_rxbyte; 310 } 311 312 return 0; 313 } 314 315 int 316 s3c24x0_spi_bps(struct ssspi_softc *sc, int bps) 317 { 318 int pclk = s3c2xx0_softc->sc_pclk; 319 int prescaler; 320 321 if (bps > 1) { 322 prescaler = pclk / 2 / bps - 1; 323 324 if (prescaler <= 0 || 0xff < prescaler) 325 return -1; 326 bus_space_write_1(sc->iot, sc->ioh, SPI_SPPRE, prescaler); 327 } 328 329 return 0; 330 } 331 332 int 333 ssspi_intr(void *arg) 334 { 335 #if 1 336 uint32_t reg; 337 struct ssspi_softc *sc; 338 339 sc = (struct ssspi_softc*)arg; 340 341 reg = bus_space_read_4(sc->iot, sc->ioh, SPI_SPSTA); 342 if (reg & SPSTA_REDY) { 343 sc->sc_rxbyte = bus_space_read_1(sc->iot, sc->ioh, SPI_SPRDAT); 344 345 mutex_enter(&sc->sc_intr_mtx); 346 sc->sc_received = TRUE; 347 cv_broadcast(&sc->sc_intr_cv); 348 mutex_exit(&sc->sc_intr_mtx); 349 } 350 #endif 351 return 1; 352 } 353