Home | History | Annotate | Line # | Download | only in s3c2xx0
      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