177b78cdcSnisimura /*-
277b78cdcSnisimura * Copyright (c) 2012 The NetBSD Foundation, Inc.
377b78cdcSnisimura * All rights reserved.
477b78cdcSnisimura *
577b78cdcSnisimura * This code is derived from software contributed to The NetBSD Foundation
677b78cdcSnisimura * by Paul Fleischer <paul@xpg.dk>
777b78cdcSnisimura *
877b78cdcSnisimura * Redistribution and use in source and binary forms, with or without
977b78cdcSnisimura * modification, are permitted provided that the following conditions
1077b78cdcSnisimura * are met:
1177b78cdcSnisimura * 1. Redistributions of source code must retain the above copyright
1277b78cdcSnisimura * notice, this list of conditions and the following disclaimer.
1377b78cdcSnisimura * 2. Redistributions in binary form must reproduce the above copyright
1477b78cdcSnisimura * notice, this list of conditions and the following disclaimer in the
1577b78cdcSnisimura * documentation and/or other materials provided with the distribution.
1677b78cdcSnisimura *
1777b78cdcSnisimura * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
1877b78cdcSnisimura * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
1977b78cdcSnisimura * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2077b78cdcSnisimura * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2177b78cdcSnisimura * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2277b78cdcSnisimura * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2377b78cdcSnisimura * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2477b78cdcSnisimura * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2577b78cdcSnisimura * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2677b78cdcSnisimura * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2777b78cdcSnisimura * POSSIBILITY OF SUCH DAMAGE.
2877b78cdcSnisimura */
2977b78cdcSnisimura
3077b78cdcSnisimura /* Derived from s3c2410_spi.c */
3177b78cdcSnisimura
3277b78cdcSnisimura /*
3377b78cdcSnisimura * Copyright (c) 2004 Genetec Corporation. All rights reserved.
3477b78cdcSnisimura * Written by Hiroyuki Bessho for Genetec Corporation.
3577b78cdcSnisimura *
3677b78cdcSnisimura * Redistribution and use in source and binary forms, with or without
3777b78cdcSnisimura * modification, are permitted provided that the following conditions
3877b78cdcSnisimura * are met:
3977b78cdcSnisimura * 1. Redistributions of source code must retain the above copyright
4077b78cdcSnisimura * notice, this list of conditions and the following disclaimer.
4177b78cdcSnisimura * 2. Redistributions in binary form must reproduce the above copyright
4277b78cdcSnisimura * notice, this list of conditions and the following disclaimer in the
4377b78cdcSnisimura * documentation and/or other materials provided with the distribution.
4477b78cdcSnisimura * 3. The name of Genetec Corporation may not be used to endorse or
4577b78cdcSnisimura * promote products derived from this software without specific prior
4677b78cdcSnisimura * written permission.
4777b78cdcSnisimura *
4877b78cdcSnisimura * THIS SOFTWARE IS PROVIDED BY GENETEC CORPORATION ``AS IS'' AND
4977b78cdcSnisimura * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
5077b78cdcSnisimura * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
5177b78cdcSnisimura * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GENETEC CORPORATION
5277b78cdcSnisimura * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
5377b78cdcSnisimura * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
5477b78cdcSnisimura * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
5577b78cdcSnisimura * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
5677b78cdcSnisimura * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
5777b78cdcSnisimura * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
5877b78cdcSnisimura * POSSIBILITY OF SUCH DAMAGE.
5977b78cdcSnisimura */
6077b78cdcSnisimura
6177b78cdcSnisimura /*
6277b78cdcSnisimura * Support S3C2440's SPI dirver.
6377b78cdcSnisimura * Real works are done by drivers attached to SPI ports.
6477b78cdcSnisimura */
6577b78cdcSnisimura
6677b78cdcSnisimura #include <sys/cdefs.h>
67*c7fb772bSthorpej __KERNEL_RCSID(0, "$NetBSD: s3c2440_spi.c,v 1.4 2021/08/07 16:18:45 thorpej Exp $");
6877b78cdcSnisimura
6977b78cdcSnisimura #include <sys/param.h>
7077b78cdcSnisimura #include <sys/systm.h>
7177b78cdcSnisimura #include <sys/conf.h>
7277b78cdcSnisimura
7377b78cdcSnisimura #include <sys/mutex.h>
7477b78cdcSnisimura #include <sys/condvar.h>
7577b78cdcSnisimura
7677b78cdcSnisimura #include <sys/bus.h>
7777b78cdcSnisimura #include <machine/cpu.h>
7877b78cdcSnisimura
7977b78cdcSnisimura #include <arm/s3c2xx0/s3c24x0var.h>
8077b78cdcSnisimura #include <arm/s3c2xx0/s3c24x0reg.h>
8177b78cdcSnisimura #include <arm/s3c2xx0/s3c2440reg.h>
8277b78cdcSnisimura
8377b78cdcSnisimura #include <arm/s3c2xx0/s3c24x0_spi.h>
8477b78cdcSnisimura
8577b78cdcSnisimura #include "locators.h"
8677b78cdcSnisimura
8777b78cdcSnisimura struct ssspi_softc {
8877b78cdcSnisimura bus_space_tag_t iot;
8977b78cdcSnisimura bus_space_handle_t ioh;
9077b78cdcSnisimura short index;
9177b78cdcSnisimura
9277b78cdcSnisimura void *sc_ih;
9377b78cdcSnisimura struct kmutex sc_intr_mtx;
9477b78cdcSnisimura struct kcondvar sc_intr_cv;
9577b78cdcSnisimura uint8_t sc_rxbyte;
9677b78cdcSnisimura bool sc_received;
9777b78cdcSnisimura };
9877b78cdcSnisimura
9977b78cdcSnisimura
10077b78cdcSnisimura /* prototypes */
101cbab9cadSchs static int ssspi_match(device_t, cfdata_t, void *);
102cbab9cadSchs static void ssspi_attach(device_t, device_t, void *);
103cbab9cadSchs static int ssspi_search(device_t, cfdata_t, const int *, void *);
10477b78cdcSnisimura static int ssspi_print(void *, const char *);
10577b78cdcSnisimura int ssspi_intr(void *arg);
10677b78cdcSnisimura
10777b78cdcSnisimura /* attach structures */
108cbab9cadSchs CFATTACH_DECL_NEW(ssspi, sizeof(struct ssspi_softc), ssspi_match, ssspi_attach,
10977b78cdcSnisimura NULL, NULL);
11077b78cdcSnisimura
11177b78cdcSnisimura
11277b78cdcSnisimura static int
ssspi_print(void * aux,const char * name)11377b78cdcSnisimura ssspi_print(void *aux, const char *name)
11477b78cdcSnisimura {
11577b78cdcSnisimura struct ssspi_attach_args *spia = aux;
11677b78cdcSnisimura
11777b78cdcSnisimura if (spia->spia_aux_intr != SSSPICF_INTR_DEFAULT)
11877b78cdcSnisimura printf(" intr %d", spia->spia_aux_intr);
11977b78cdcSnisimura return (UNCONF);
12077b78cdcSnisimura }
12177b78cdcSnisimura
12277b78cdcSnisimura int
ssspi_match(device_t parent,cfdata_t match,void * aux)123cbab9cadSchs ssspi_match(device_t parent, cfdata_t match, void *aux)
12477b78cdcSnisimura {
12577b78cdcSnisimura struct s3c2xx0_attach_args *sa = aux;
12677b78cdcSnisimura
12777b78cdcSnisimura /* S3C2440 have only two SPIs */
12877b78cdcSnisimura switch (sa->sa_index) {
12977b78cdcSnisimura case 0:
13077b78cdcSnisimura case 1:
13177b78cdcSnisimura break;
13277b78cdcSnisimura default:
13377b78cdcSnisimura return 0;
13477b78cdcSnisimura }
13577b78cdcSnisimura
13677b78cdcSnisimura return 1;
13777b78cdcSnisimura }
13877b78cdcSnisimura
13977b78cdcSnisimura void
ssspi_attach(device_t parent,device_t self,void * aux)140cbab9cadSchs ssspi_attach(device_t parent, device_t self, void *aux)
14177b78cdcSnisimura {
142cbab9cadSchs struct ssspi_softc *sc = device_private(self);
143cbab9cadSchs struct s3c2xx0_attach_args *sa = aux;
14477b78cdcSnisimura bus_space_tag_t iot = sa->sa_iot;
14577b78cdcSnisimura
14677b78cdcSnisimura static bus_space_handle_t spi_ioh = 0;
14777b78cdcSnisimura
14877b78cdcSnisimura /* we map all registers for SPI0 and SPI1 at once, then
14977b78cdcSnisimura use subregions */
15077b78cdcSnisimura if (spi_ioh == 0) {
15177b78cdcSnisimura if (bus_space_map(iot, S3C2440_SPI0_BASE,
15277b78cdcSnisimura 2 * S3C24X0_SPI_SIZE,
15377b78cdcSnisimura 0, &spi_ioh)) {
15477b78cdcSnisimura aprint_error(": can't map registers\n");
15577b78cdcSnisimura return;
15677b78cdcSnisimura }
15777b78cdcSnisimura }
15877b78cdcSnisimura
15977b78cdcSnisimura aprint_normal("\n");
16077b78cdcSnisimura
16177b78cdcSnisimura sc->index = sa->sa_index;
16277b78cdcSnisimura sc->iot = iot;
16377b78cdcSnisimura
16477b78cdcSnisimura bus_space_subregion(iot, spi_ioh, sc->index == 0 ? 0 : S3C24X0_SPI_SIZE,
16577b78cdcSnisimura S3C24X0_SPI_SIZE, &sc->ioh);
16677b78cdcSnisimura
16777b78cdcSnisimura mutex_init(&sc->sc_intr_mtx, MUTEX_DEFAULT, IPL_BIO);
16877b78cdcSnisimura cv_init(&sc->sc_intr_cv, "S3C2440_spiintr");
16977b78cdcSnisimura
17077b78cdcSnisimura /*
17177b78cdcSnisimura * Attach child devices
17277b78cdcSnisimura */
1732685996bSthorpej config_search(self, NULL,
174*c7fb772bSthorpej CFARGS(.search = ssspi_search));
17577b78cdcSnisimura }
17677b78cdcSnisimura
17777b78cdcSnisimura int
ssspi_search(device_t parent,cfdata_t cf,const int * ldesc,void * aux)178cbab9cadSchs ssspi_search(device_t parent, cfdata_t cf, const int *ldesc, void *aux)
17977b78cdcSnisimura {
180cbab9cadSchs struct ssspi_softc *sc = device_private(parent);
18177b78cdcSnisimura struct ssspi_attach_args spia;
18277b78cdcSnisimura static const unsigned char intr[] = { S3C24X0_INT_SPI0,
18377b78cdcSnisimura S3C2440_INT_SPI1 };
18477b78cdcSnisimura
18577b78cdcSnisimura KASSERT(sc->index == 0 || sc->index == 1);
18677b78cdcSnisimura
18777b78cdcSnisimura spia.spia_iot = sc->iot;
18877b78cdcSnisimura spia.spia_ioh = sc->ioh;
18977b78cdcSnisimura spia.spia_gpioh = s3c2xx0_softc->sc_gpio_ioh;
19077b78cdcSnisimura spia.spia_index = sc->index;
19177b78cdcSnisimura spia.spia_intr = intr[sc->index];
19277b78cdcSnisimura spia.spia_aux_intr = cf->cf_loc[SSSPICF_INTR];
19377b78cdcSnisimura spia.spia_dmat = s3c2xx0_softc->sc_dmat;
19477b78cdcSnisimura
1952685996bSthorpej if (config_probe(parent, cf, &spia))
196*c7fb772bSthorpej config_attach(parent, cf, &spia, ssspi_print, CFARGS_NONE);
19777b78cdcSnisimura
19877b78cdcSnisimura return 0;
19977b78cdcSnisimura }
20077b78cdcSnisimura
20177b78cdcSnisimura /*
20277b78cdcSnisimura * Intiialze SPI port. called by child devices.
20377b78cdcSnisimura */
20477b78cdcSnisimura int
s3c24x0_spi_setup(struct ssspi_softc * sc,uint32_t mode,int bps,int use_ss)20577b78cdcSnisimura s3c24x0_spi_setup(struct ssspi_softc *sc, uint32_t mode, int bps, int use_ss)
20677b78cdcSnisimura {
20777b78cdcSnisimura int pclk = s3c2xx0_softc->sc_pclk;
20877b78cdcSnisimura int prescaler;
20977b78cdcSnisimura uint32_t pgcon, pecon, peup;
21077b78cdcSnisimura bus_space_handle_t gpioh = s3c2xx0_softc->sc_gpio_ioh;
21177b78cdcSnisimura bus_space_tag_t iot = sc->iot;
21277b78cdcSnisimura
21377b78cdcSnisimura if (bps > 1) {
21477b78cdcSnisimura prescaler = pclk / 2 / bps - 1;
21577b78cdcSnisimura
21677b78cdcSnisimura if (prescaler <= 0 || 0xff < prescaler)
21777b78cdcSnisimura return -1;
21877b78cdcSnisimura bus_space_write_1(sc->iot, sc->ioh, SPI_SPPRE, prescaler);
21977b78cdcSnisimura }
22077b78cdcSnisimura
22177b78cdcSnisimura if (sc->index == 0) {
22277b78cdcSnisimura pecon = bus_space_read_4(iot, gpioh, GPIO_PECON);
22377b78cdcSnisimura
22477b78cdcSnisimura if (use_ss) {
22577b78cdcSnisimura pgcon = bus_space_read_4(iot, gpioh, GPIO_PGCON);
22677b78cdcSnisimura pgcon = GPIO_SET_FUNC(pgcon, 2, PCON_ALTFUN2);
22777b78cdcSnisimura bus_space_write_4(iot, gpioh, GPIO_PGCON, pgcon);
22877b78cdcSnisimura }
22977b78cdcSnisimura
23077b78cdcSnisimura pecon = GPIO_SET_FUNC(pecon, 11, PCON_ALTFUN); /* SPIMISO0 */
23177b78cdcSnisimura pecon = GPIO_SET_FUNC(pecon, 12, PCON_ALTFUN); /* SPIMOSI0 */
23277b78cdcSnisimura pecon = GPIO_SET_FUNC(pecon, 13, PCON_ALTFUN); /* SPICL0 */
23377b78cdcSnisimura
23477b78cdcSnisimura bus_space_write_4(iot, gpioh, GPIO_PECON, pecon);
23577b78cdcSnisimura
23677b78cdcSnisimura /* Enable pull-up for pin 11, 12, and 13*/
23777b78cdcSnisimura peup = bus_space_read_4(iot, gpioh, GPIO_PEUP);
23877b78cdcSnisimura peup &= ~(1<<11);
23977b78cdcSnisimura peup &= ~(1<<12);
24077b78cdcSnisimura peup &= ~(1<<13);
24177b78cdcSnisimura bus_space_write_4(iot, gpioh, GPIO_PEUP, peup);
24277b78cdcSnisimura
24377b78cdcSnisimura sc->sc_ih = s3c24x0_intr_establish(S3C24X0_INT_SPI0, IPL_BIO,
24477b78cdcSnisimura IST_EDGE_RISING, ssspi_intr,
24577b78cdcSnisimura sc);
24677b78cdcSnisimura printf("ih: %p\n", sc->sc_ih);
24777b78cdcSnisimura } else {
24877b78cdcSnisimura pgcon = bus_space_read_4(iot, gpioh, GPIO_PGCON);
24977b78cdcSnisimura
25077b78cdcSnisimura if (use_ss)
25177b78cdcSnisimura pgcon = GPIO_SET_FUNC(pgcon, 3, PCON_ALTFUN2);
25277b78cdcSnisimura
25377b78cdcSnisimura pgcon = GPIO_SET_FUNC(pgcon, 5, PCON_ALTFUN2); /* SPIMISO1 */
25477b78cdcSnisimura pgcon = GPIO_SET_FUNC(pgcon, 6, PCON_ALTFUN2); /* SPIMOSI1 */
25577b78cdcSnisimura pgcon = GPIO_SET_FUNC(pgcon, 7, PCON_ALTFUN2); /* SPICLK1 */
25677b78cdcSnisimura
25777b78cdcSnisimura bus_space_write_4(iot, gpioh, GPIO_PGCON, pgcon);
25877b78cdcSnisimura
25977b78cdcSnisimura /* Enable pull-up for pin 5, 6, and 7*/
26077b78cdcSnisimura peup = bus_space_read_4(iot, gpioh, GPIO_PGUP);
26177b78cdcSnisimura peup &= ~(1<<5);
26277b78cdcSnisimura peup &= ~(1<<6);
26377b78cdcSnisimura peup &= ~(1<<7);
26477b78cdcSnisimura bus_space_write_4(iot, gpioh, GPIO_PGUP, peup);
26577b78cdcSnisimura
26677b78cdcSnisimura }
26777b78cdcSnisimura
26877b78cdcSnisimura bus_space_write_4(iot, sc->ioh, SPI_SPCON, mode);
26977b78cdcSnisimura
27077b78cdcSnisimura return 0;
27177b78cdcSnisimura }
27277b78cdcSnisimura
27377b78cdcSnisimura int
s3c24x0_spi_master_send(struct ssspi_softc * sc,uint8_t value)27477b78cdcSnisimura s3c24x0_spi_master_send(struct ssspi_softc *sc, uint8_t value)
27577b78cdcSnisimura {
27677b78cdcSnisimura sc->sc_received = FALSE;
27777b78cdcSnisimura bus_space_write_1(sc->iot, sc->ioh, SPI_SPTDAT, value);
27877b78cdcSnisimura
27977b78cdcSnisimura return 0;
28077b78cdcSnisimura }
28177b78cdcSnisimura
28277b78cdcSnisimura void
s3c24x0_spi_spin_wait(struct ssspi_softc * sc)28377b78cdcSnisimura s3c24x0_spi_spin_wait(struct ssspi_softc *sc)
28477b78cdcSnisimura {
28577b78cdcSnisimura uint32_t reg;
28677b78cdcSnisimura do {
28777b78cdcSnisimura reg = bus_space_read_4(sc->iot, sc->ioh, SPI_SPSTA);
28877b78cdcSnisimura } while(! (reg & SPSTA_REDY));
28977b78cdcSnisimura }
29077b78cdcSnisimura
29177b78cdcSnisimura int
s3c24x0_spi_wait(struct ssspi_softc * sc,uint8_t * valPtr)29277b78cdcSnisimura s3c24x0_spi_wait(struct ssspi_softc *sc, uint8_t *valPtr)
29377b78cdcSnisimura {
29477b78cdcSnisimura #if 0
29577b78cdcSnisimura uint32_t reg;
29677b78cdcSnisimura do {
29777b78cdcSnisimura reg = bus_space_read_4(sc->iot, sc->ioh, SPI_SPSTA);
29877b78cdcSnisimura } while(!(reg & SPSTA_REDY));
29977b78cdcSnisimura
30077b78cdcSnisimura #else
30177b78cdcSnisimura mutex_enter(&sc->sc_intr_mtx);
30277b78cdcSnisimura while( sc->sc_received == FALSE) {
30377b78cdcSnisimura cv_wait(&sc->sc_intr_cv, &sc->sc_intr_mtx);
30477b78cdcSnisimura }
30577b78cdcSnisimura mutex_exit(&sc->sc_intr_mtx);
30677b78cdcSnisimura #endif
30777b78cdcSnisimura
30877b78cdcSnisimura if (valPtr != NULL) {
30977b78cdcSnisimura // *valPtr = bus_space_read_1(sc->iot, sc->ioh, SPI_SPRDAT);
31077b78cdcSnisimura *valPtr = sc->sc_rxbyte;
31177b78cdcSnisimura }
31277b78cdcSnisimura
31377b78cdcSnisimura return 0;
31477b78cdcSnisimura }
31577b78cdcSnisimura
31677b78cdcSnisimura int
s3c24x0_spi_bps(struct ssspi_softc * sc,int bps)31777b78cdcSnisimura s3c24x0_spi_bps(struct ssspi_softc *sc, int bps)
31877b78cdcSnisimura {
31977b78cdcSnisimura int pclk = s3c2xx0_softc->sc_pclk;
32077b78cdcSnisimura int prescaler;
32177b78cdcSnisimura
32277b78cdcSnisimura if (bps > 1) {
32377b78cdcSnisimura prescaler = pclk / 2 / bps - 1;
32477b78cdcSnisimura
32577b78cdcSnisimura if (prescaler <= 0 || 0xff < prescaler)
32677b78cdcSnisimura return -1;
32777b78cdcSnisimura bus_space_write_1(sc->iot, sc->ioh, SPI_SPPRE, prescaler);
32877b78cdcSnisimura }
32977b78cdcSnisimura
33077b78cdcSnisimura return 0;
33177b78cdcSnisimura }
33277b78cdcSnisimura
33377b78cdcSnisimura int
ssspi_intr(void * arg)33477b78cdcSnisimura ssspi_intr(void *arg)
33577b78cdcSnisimura {
33677b78cdcSnisimura #if 1
33777b78cdcSnisimura uint32_t reg;
33877b78cdcSnisimura struct ssspi_softc *sc;
33977b78cdcSnisimura
34077b78cdcSnisimura sc = (struct ssspi_softc*)arg;
34177b78cdcSnisimura
34277b78cdcSnisimura reg = bus_space_read_4(sc->iot, sc->ioh, SPI_SPSTA);
34377b78cdcSnisimura if (reg & SPSTA_REDY) {
34477b78cdcSnisimura sc->sc_rxbyte = bus_space_read_1(sc->iot, sc->ioh, SPI_SPRDAT);
34577b78cdcSnisimura
34677b78cdcSnisimura mutex_enter(&sc->sc_intr_mtx);
34777b78cdcSnisimura sc->sc_received = TRUE;
34877b78cdcSnisimura cv_broadcast(&sc->sc_intr_cv);
34977b78cdcSnisimura mutex_exit(&sc->sc_intr_mtx);
35077b78cdcSnisimura }
35177b78cdcSnisimura #endif
35277b78cdcSnisimura return 1;
35377b78cdcSnisimura }
354