xref: /netbsd-src/sys/arch/arm/s3c2xx0/s3c2410_spi.c (revision aaf4ece63a859a04e37cf3a7229b5fab0157cc06)
1 /* $NetBSD: s3c2410_spi.c,v 1.4 2005/12/11 12:16:51 christos Exp $ */
2 
3 /*
4  * Copyright (c) 2004  Genetec Corporation.  All rights reserved.
5  * Written by Hiroyuki Bessho for Genetec Corporation.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. The name of Genetec Corporation may not be used to endorse or
16  *    promote products derived from this software without specific prior
17  *    written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY GENETEC CORPORATION ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL GENETEC CORPORATION
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 /*
33  * Support S3C2410's SPI dirver.
34  * Real works are done by drivers attached to SPI ports.
35  */
36 
37 #include <sys/cdefs.h>
38 __KERNEL_RCSID(0, "$NetBSD: s3c2410_spi.c,v 1.4 2005/12/11 12:16:51 christos Exp $");
39 
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/conf.h>
43 
44 #include <machine/bus.h>
45 #include <machine/cpu.h>
46 
47 #include <arm/s3c2xx0/s3c24x0var.h>
48 #include <arm/s3c2xx0/s3c24x0reg.h>
49 #include <arm/s3c2xx0/s3c2410reg.h>
50 
51 #include <arm/s3c2xx0/s3c24x0_spi.h>
52 
53 #include "locators.h"
54 
55 struct ssspi_softc {
56 	struct device dev;
57 
58 	bus_space_tag_t    iot;
59 	bus_space_handle_t ioh;
60 	short	index;
61 };
62 
63 
64 /* prototypes */
65 static int	ssspi_match(struct device *, struct cfdata *, void *);
66 static void	ssspi_attach(struct device *, struct device *, void *);
67 static int 	ssspi_search(struct device *, struct cfdata *,
68 			     const int *, void *);
69 static int	ssspi_print(void *, const char *);
70 
71 /* attach structures */
72 CFATTACH_DECL(ssspi, sizeof(struct ssspi_softc), ssspi_match, ssspi_attach,
73     NULL, NULL);
74 
75 
76 static int
77 ssspi_print(void *aux, const char *name)
78 {
79 	struct ssspi_attach_args *spia = aux;
80 
81 	if (spia->spia_aux_intr != SSSPICF_INTR_DEFAULT)
82 		printf(" intr %d", spia->spia_aux_intr);
83         return (UNCONF);
84 }
85 
86 int
87 ssspi_match(struct device *parent, struct cfdata *match, void *aux)
88 {
89 	struct s3c2xx0_attach_args *sa = aux;
90 
91 	/* S3C2410 have only two SPIs */
92 	switch (sa->sa_index) {
93 	case 0:
94 	case 1:
95 		break;
96 	default:
97 		return 0;
98 	}
99 
100 	return 1;
101 }
102 
103 void
104 ssspi_attach(struct device *parent, struct device *self, void *aux)
105 {
106 	struct ssspi_softc *sc = (struct ssspi_softc*)self;
107 	struct s3c2xx0_attach_args *sa = (struct s3c2xx0_attach_args *)aux;
108 	bus_space_tag_t iot = sa->sa_iot;
109 
110 	static bus_space_handle_t spi_ioh = 0;
111 
112 	/* we map all registers for SPI0 and SPI1 at once, then
113 	   use subregions */
114 	if (spi_ioh == 0) {
115 		if (bus_space_map(iot, S3C2410_SPI0_BASE,
116 				  2 * S3C24X0_SPI_SIZE,
117 				  0, &spi_ioh)) {
118 			aprint_error(": can't map registers\n");
119 			return;
120 		}
121 	}
122 
123 	aprint_normal("\n");
124 
125 	sc->index = sa->sa_index;
126 	sc->iot = iot;
127 
128 	bus_space_subregion(iot, spi_ioh, sc->index == 0 ? 0 : S3C24X0_SPI_SIZE,
129 	    S3C24X0_SPI_SIZE, &sc->ioh);
130 
131 	/*
132 	 *  Attach child devices
133 	 */
134 	config_search_ia(ssspi_search, self, "ssspi", NULL);
135 }
136 
137 int
138 ssspi_search(parent, cf, ldesc, aux)
139 	struct device *parent;
140 	struct cfdata *cf;
141 	const int *ldesc;
142 	void *aux;
143 {
144 	struct ssspi_softc *sc = (struct ssspi_softc *)parent;
145 	struct ssspi_attach_args spia;
146 	static const unsigned char intr[] = { S3C24X0_INT_SPI0,
147 					      S3C2410_INT_SPI1 };
148 
149 	KASSERT(sc->index == 0 || sc->index == 1);
150 
151 	spia.spia_iot = sc->iot;
152 	spia.spia_ioh = sc->ioh;
153 	spia.spia_gpioh = s3c2xx0_softc->sc_gpio_ioh;
154 	spia.spia_index = sc->index;
155 	spia.spia_intr = intr[sc->index];
156 	spia.spia_aux_intr = cf->cf_loc[SSSPICF_INTR];
157 	spia.spia_dmat = s3c2xx0_softc->sc_dmat;
158 
159         if (config_match(parent, cf, &spia))
160                 config_attach(parent, cf, &spia, ssspi_print);
161 
162         return 0;
163 }
164 
165 /*
166  * Intiialze SPI port. called by child devices.
167  */
168 int
169 s3c24x0_spi_setup(struct ssspi_softc *sc, uint32_t mode, int bps, int use_ss)
170 {
171 	int pclk = s3c2xx0_softc->sc_pclk;
172 	int prescaler;
173 	uint32_t pgcon, pecon;
174 	bus_space_handle_t gpioh = s3c2xx0_softc->sc_gpio_ioh;
175 	bus_space_tag_t iot = sc->iot;
176 
177 	if (bps > 1) {
178 		prescaler = pclk / 2 / bps - 1;
179 
180 		if (prescaler <= 0 || 0xff < prescaler)
181 			return -1;
182 		bus_space_write_1(sc->iot, sc->ioh, SPI_SPPRE, prescaler);
183 	}
184 
185 
186 	if (sc->index == 0) {
187 		pecon = bus_space_read_4(iot, gpioh, GPIO_PECON);
188 
189 		if (use_ss) {
190 			pgcon = bus_space_read_4(iot, gpioh, GPIO_PGCON);
191 			pgcon = GPIO_SET_FUNC(pgcon, 2, PCON_ALTFUN2);
192 			bus_space_write_4(iot, gpioh, GPIO_PGCON, pgcon);
193 		}
194 
195 		pecon = GPIO_SET_FUNC(pecon, 11, PCON_ALTFUN2); /* SPIMISO0 */
196 		pecon = GPIO_SET_FUNC(pecon, 12, PCON_ALTFUN2); /* SPIMOSI0 */
197 		pecon = GPIO_SET_FUNC(pecon, 13, PCON_ALTFUN2); /* SPICL0 */
198 
199 		bus_space_write_4(iot, gpioh, GPIO_PECON, pecon);
200 	}
201 	else {
202 		pgcon = bus_space_read_4(iot, gpioh, GPIO_PGCON);
203 
204 		if (use_ss)
205 			pgcon = GPIO_SET_FUNC(pgcon, 3, PCON_ALTFUN2);
206 
207 		pgcon = GPIO_SET_FUNC(pgcon, 5, PCON_ALTFUN2); /* SPIMISO1 */
208 		pgcon = GPIO_SET_FUNC(pgcon, 6, PCON_ALTFUN2); /* SPIMOSI1 */
209 		pgcon = GPIO_SET_FUNC(pgcon, 7, PCON_ALTFUN2); /* SPICLK1 */
210 
211 		bus_space_write_4(iot, gpioh, GPIO_PGCON, pgcon);
212 	}
213 
214 	bus_space_write_4(iot, sc->ioh, SPI_SPCON, mode);
215 
216 	return 0;
217 }
218