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 (at) 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 driver. 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.5 2025/02/21 20:31:16 andvar 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(self, NULL, 174 CFARGS(.search = ssspi_search)); 175 } 176 177 int 178 ssspi_search(device_t parent, cfdata_t cf, const int *ldesc, void *aux) 179 { 180 struct ssspi_softc *sc = device_private(parent); 181 struct ssspi_attach_args spia; 182 static const unsigned char intr[] = { S3C24X0_INT_SPI0, 183 S3C2440_INT_SPI1 }; 184 185 KASSERT(sc->index == 0 || sc->index == 1); 186 187 spia.spia_iot = sc->iot; 188 spia.spia_ioh = sc->ioh; 189 spia.spia_gpioh = s3c2xx0_softc->sc_gpio_ioh; 190 spia.spia_index = sc->index; 191 spia.spia_intr = intr[sc->index]; 192 spia.spia_aux_intr = cf->cf_loc[SSSPICF_INTR]; 193 spia.spia_dmat = s3c2xx0_softc->sc_dmat; 194 195 if (config_probe(parent, cf, &spia)) 196 config_attach(parent, cf, &spia, ssspi_print, CFARGS_NONE); 197 198 return 0; 199 } 200 201 /* 202 * Intiialze SPI port. called by child devices. 203 */ 204 int 205 s3c24x0_spi_setup(struct ssspi_softc *sc, uint32_t mode, int bps, int use_ss) 206 { 207 int pclk = s3c2xx0_softc->sc_pclk; 208 int prescaler; 209 uint32_t pgcon, pecon, peup; 210 bus_space_handle_t gpioh = s3c2xx0_softc->sc_gpio_ioh; 211 bus_space_tag_t iot = sc->iot; 212 213 if (bps > 1) { 214 prescaler = pclk / 2 / bps - 1; 215 216 if (prescaler <= 0 || 0xff < prescaler) 217 return -1; 218 bus_space_write_1(sc->iot, sc->ioh, SPI_SPPRE, prescaler); 219 } 220 221 if (sc->index == 0) { 222 pecon = bus_space_read_4(iot, gpioh, GPIO_PECON); 223 224 if (use_ss) { 225 pgcon = bus_space_read_4(iot, gpioh, GPIO_PGCON); 226 pgcon = GPIO_SET_FUNC(pgcon, 2, PCON_ALTFUN2); 227 bus_space_write_4(iot, gpioh, GPIO_PGCON, pgcon); 228 } 229 230 pecon = GPIO_SET_FUNC(pecon, 11, PCON_ALTFUN); /* SPIMISO0 */ 231 pecon = GPIO_SET_FUNC(pecon, 12, PCON_ALTFUN); /* SPIMOSI0 */ 232 pecon = GPIO_SET_FUNC(pecon, 13, PCON_ALTFUN); /* SPICL0 */ 233 234 bus_space_write_4(iot, gpioh, GPIO_PECON, pecon); 235 236 /* Enable pull-up for pin 11, 12, and 13*/ 237 peup = bus_space_read_4(iot, gpioh, GPIO_PEUP); 238 peup &= ~(1<<11); 239 peup &= ~(1<<12); 240 peup &= ~(1<<13); 241 bus_space_write_4(iot, gpioh, GPIO_PEUP, peup); 242 243 sc->sc_ih = s3c24x0_intr_establish(S3C24X0_INT_SPI0, IPL_BIO, 244 IST_EDGE_RISING, ssspi_intr, 245 sc); 246 printf("ih: %p\n", sc->sc_ih); 247 } else { 248 pgcon = bus_space_read_4(iot, gpioh, GPIO_PGCON); 249 250 if (use_ss) 251 pgcon = GPIO_SET_FUNC(pgcon, 3, PCON_ALTFUN2); 252 253 pgcon = GPIO_SET_FUNC(pgcon, 5, PCON_ALTFUN2); /* SPIMISO1 */ 254 pgcon = GPIO_SET_FUNC(pgcon, 6, PCON_ALTFUN2); /* SPIMOSI1 */ 255 pgcon = GPIO_SET_FUNC(pgcon, 7, PCON_ALTFUN2); /* SPICLK1 */ 256 257 bus_space_write_4(iot, gpioh, GPIO_PGCON, pgcon); 258 259 /* Enable pull-up for pin 5, 6, and 7*/ 260 peup = bus_space_read_4(iot, gpioh, GPIO_PGUP); 261 peup &= ~(1<<5); 262 peup &= ~(1<<6); 263 peup &= ~(1<<7); 264 bus_space_write_4(iot, gpioh, GPIO_PGUP, peup); 265 266 } 267 268 bus_space_write_4(iot, sc->ioh, SPI_SPCON, mode); 269 270 return 0; 271 } 272 273 int 274 s3c24x0_spi_master_send(struct ssspi_softc *sc, uint8_t value) 275 { 276 sc->sc_received = FALSE; 277 bus_space_write_1(sc->iot, sc->ioh, SPI_SPTDAT, value); 278 279 return 0; 280 } 281 282 void 283 s3c24x0_spi_spin_wait(struct ssspi_softc *sc) 284 { 285 uint32_t reg; 286 do { 287 reg = bus_space_read_4(sc->iot, sc->ioh, SPI_SPSTA); 288 } while(! (reg & SPSTA_REDY)); 289 } 290 291 int 292 s3c24x0_spi_wait(struct ssspi_softc *sc, uint8_t *valPtr) 293 { 294 #if 0 295 uint32_t reg; 296 do { 297 reg = bus_space_read_4(sc->iot, sc->ioh, SPI_SPSTA); 298 } while(!(reg & SPSTA_REDY)); 299 300 #else 301 mutex_enter(&sc->sc_intr_mtx); 302 while( sc->sc_received == FALSE) { 303 cv_wait(&sc->sc_intr_cv, &sc->sc_intr_mtx); 304 } 305 mutex_exit(&sc->sc_intr_mtx); 306 #endif 307 308 if (valPtr != NULL) { 309 // *valPtr = bus_space_read_1(sc->iot, sc->ioh, SPI_SPRDAT); 310 *valPtr = sc->sc_rxbyte; 311 } 312 313 return 0; 314 } 315 316 int 317 s3c24x0_spi_bps(struct ssspi_softc *sc, int bps) 318 { 319 int pclk = s3c2xx0_softc->sc_pclk; 320 int prescaler; 321 322 if (bps > 1) { 323 prescaler = pclk / 2 / bps - 1; 324 325 if (prescaler <= 0 || 0xff < prescaler) 326 return -1; 327 bus_space_write_1(sc->iot, sc->ioh, SPI_SPPRE, prescaler); 328 } 329 330 return 0; 331 } 332 333 int 334 ssspi_intr(void *arg) 335 { 336 #if 1 337 uint32_t reg; 338 struct ssspi_softc *sc; 339 340 sc = (struct ssspi_softc*)arg; 341 342 reg = bus_space_read_4(sc->iot, sc->ioh, SPI_SPSTA); 343 if (reg & SPSTA_REDY) { 344 sc->sc_rxbyte = bus_space_read_1(sc->iot, sc->ioh, SPI_SPRDAT); 345 346 mutex_enter(&sc->sc_intr_mtx); 347 sc->sc_received = TRUE; 348 cv_broadcast(&sc->sc_intr_cv); 349 mutex_exit(&sc->sc_intr_mtx); 350 } 351 #endif 352 return 1; 353 } 354