xref: /netbsd-src/sys/arch/sh3/dev/adc.c (revision e5fbc36ada28f9b9a5836ecffaf4a06aa1ebb687)
1*e5fbc36aSthorpej /*	$NetBSD: adc.c,v 1.16 2023/12/20 15:34:45 thorpej Exp $ */
2721e6868Suwe 
3721e6868Suwe /*
4721e6868Suwe  * Copyright (c) 2003 Valeriy E. Ushakov
5721e6868Suwe  * All rights reserved.
6721e6868Suwe  *
7721e6868Suwe  * Redistribution and use in source and binary forms, with or without
8721e6868Suwe  * modification, are permitted provided that the following conditions
9721e6868Suwe  * are met:
10721e6868Suwe  * 1. Redistributions of source code must retain the above copyright
11721e6868Suwe  *    notice, this list of conditions and the following disclaimer.
12721e6868Suwe  * 2. Redistributions in binary form must reproduce the above copyright
13721e6868Suwe  *    notice, this list of conditions and the following disclaimer in the
14721e6868Suwe  *    documentation and/or other materials provided with the distribution.
15721e6868Suwe  * 3. The name of the author may not be used to endorse or promote products
16721e6868Suwe  *    derived from this software without specific prior written permission
17721e6868Suwe  *
18721e6868Suwe  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19721e6868Suwe  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20721e6868Suwe  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21721e6868Suwe  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22721e6868Suwe  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23721e6868Suwe  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24721e6868Suwe  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25721e6868Suwe  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26721e6868Suwe  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27721e6868Suwe  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28721e6868Suwe  */
29721e6868Suwe 
30437e63c1Suwe #include <sys/cdefs.h>
31*e5fbc36aSthorpej __KERNEL_RCSID(0, "$NetBSD: adc.c,v 1.16 2023/12/20 15:34:45 thorpej Exp $");
32437e63c1Suwe 
33721e6868Suwe #include <sys/param.h>
34721e6868Suwe #include <sys/kernel.h>
35721e6868Suwe #include <sys/device.h>
36721e6868Suwe #include <sys/systm.h>
37721e6868Suwe 
38721e6868Suwe #include <sh3/adcreg.h>
3949fd54d3Suwe #include <sh3/dev/adcvar.h>
40721e6868Suwe 
41721e6868Suwe #define ADC_(x)    (*((volatile uint8_t *)SH7709_AD ## x))
42721e6868Suwe 
43721e6868Suwe 
44178f553dSuwe static int	adc_match(device_t, cfdata_t, void *);
459c577c1eSuwe static void	adc_attach(device_t, device_t, void *);
46721e6868Suwe 
479c577c1eSuwe CFATTACH_DECL_NEW(adc, 0,
48721e6868Suwe     adc_match, adc_attach, NULL, NULL);
49721e6868Suwe 
50178f553dSuwe static int	adc_search(device_t, cfdata_t, const int *, void *);
51721e6868Suwe static int	adc_print(void *, const char *);
52721e6868Suwe 
53721e6868Suwe 
54721e6868Suwe static int
adc_match(device_t parent,cfdata_t cf,void * aux)55178f553dSuwe adc_match(device_t parent, cfdata_t cf, void *aux)
56721e6868Suwe {
57721e6868Suwe 
58721e6868Suwe 	/* REMINDER: also in 7727 and 7729 */
59721e6868Suwe 	if ((cpu_product != CPU_PRODUCT_7709)
608ca2e6bcSnonaka 	    && (cpu_product != CPU_PRODUCT_7709A)
618ca2e6bcSnonaka 	    && (cpu_product != CPU_PRODUCT_7706))
62721e6868Suwe 		return (0);
63721e6868Suwe 
64178f553dSuwe 	if (strcmp(cf->cf_name, "adc") != 0)
65721e6868Suwe 		return (0);
66721e6868Suwe 
67721e6868Suwe 	return (1);
68721e6868Suwe }
69721e6868Suwe 
70721e6868Suwe 
71721e6868Suwe static void
adc_attach(device_t parent,device_t self,void * aux)729c577c1eSuwe adc_attach(device_t parent, device_t self, void *aux)
73721e6868Suwe {
74721e6868Suwe 
75721e6868Suwe 	ADC_(CSR) = 0;
76721e6868Suwe 	ADC_(CR) = 0;
77721e6868Suwe 
78178f553dSuwe 	aprint_naive("\n");
79178f553dSuwe 	aprint_normal("\n");
80178f553dSuwe 
812685996bSthorpej 	config_search(self, NULL,
82c7fb772bSthorpej 	    CFARGS(.search = adc_search));
83375ebd8cSuwe 
84375ebd8cSuwe 	/*
85375ebd8cSuwe 	 * XXX: TODO: provide hooks to manage power.  For now register
86375ebd8cSuwe 	 * null hooks which is no worse than before.
87375ebd8cSuwe 	 *
88375ebd8cSuwe 	 * NB: ADC registers are reset by standby!
89375ebd8cSuwe 	 */
90375ebd8cSuwe 	if (!pmf_device_register(self, NULL, NULL))
91375ebd8cSuwe 		aprint_error_dev(self, "unable to establish power handler\n");
92721e6868Suwe }
93721e6868Suwe 
94721e6868Suwe 
95721e6868Suwe static int
adc_search(device_t parent,cfdata_t cf,const int * ldesc,void * aux)96178f553dSuwe adc_search(device_t parent, cfdata_t cf, const int *ldesc, void *aux)
97721e6868Suwe {
98721e6868Suwe 
992685996bSthorpej 	if (config_probe(parent, cf, NULL))
100c7fb772bSthorpej 		config_attach(parent, cf, NULL, adc_print, CFARGS_NONE);
101721e6868Suwe 
102721e6868Suwe 	return (0);
103721e6868Suwe }
104721e6868Suwe 
105721e6868Suwe 
106721e6868Suwe static int
adc_print(void * aux,const char * pnp)107721e6868Suwe adc_print(void *aux, const char *pnp)
108721e6868Suwe {
109721e6868Suwe 
110721e6868Suwe 	return (pnp ? QUIET : UNCONF);
111721e6868Suwe }
112721e6868Suwe 
113721e6868Suwe 
114721e6868Suwe /*
115721e6868Suwe  * Sample specified ADC channel.
116721e6868Suwe  * Must be called at spltty().
117721e6868Suwe  */
118721e6868Suwe int
adc_sample_channel(int chan)119721e6868Suwe adc_sample_channel(int chan)
120721e6868Suwe {
121721e6868Suwe 	volatile uint8_t *hireg;
122721e6868Suwe 	volatile uint8_t *loreg;
123721e6868Suwe 	int regoff;
124721e6868Suwe 	uint8_t csr;
125721e6868Suwe 	int timo;
126721e6868Suwe #ifdef DIAGNOSTIC
127721e6868Suwe 	uint8_t cr;
128721e6868Suwe 	char bits[128];
129721e6868Suwe #endif
130721e6868Suwe 	if ((chan < 0) || (chan >= 8))
131721e6868Suwe 		return (-1);
132721e6868Suwe 
133721e6868Suwe 	regoff = (chan & 0x03) << 2;
134721e6868Suwe 	hireg = (volatile uint8_t *)(SH7709_ADDRAH + regoff);
135721e6868Suwe 	loreg = (volatile uint8_t *)(SH7709_ADDRAL + regoff);
136721e6868Suwe 
137721e6868Suwe 	/* the loop below typically takes 27 iterations on j680 */
138721e6868Suwe 	timo = 300;
139721e6868Suwe 
140721e6868Suwe #ifdef DIAGNOSTIC
141721e6868Suwe 	csr = ADC_(CSR);
142721e6868Suwe 	if ((csr & SH7709_ADCSR_ADST) != 0) {
143721e6868Suwe 		/* another conversion is in progress?! */
1440570b110Schs 	        snprintb(bits, sizeof(bits), SH7709_ADCSR_BITS, csr);
1459a5d3f28Schristos 		printf("adc_sample_channel(%d): CSR=%s", chan, bits);
146721e6868Suwe 		cr = ADC_(CR);
147721e6868Suwe 		cr &= ~0x07;	/* three lower bits always read as 1s */
1480570b110Schs 	        snprintb(bits, sizeof(bits), SH7709_ADCR_BITS, cr);
1499a5d3f28Schristos 		printf(", CR=%s\n", bits);
150721e6868Suwe 		return (-1);
151721e6868Suwe 	}
152721e6868Suwe #endif
153721e6868Suwe 
154721e6868Suwe 	/* start scanning */
155721e6868Suwe 	ADC_(CSR) = chan | SH7709_ADCSR_ADST | SH7709_ADCSR_CKS;
156721e6868Suwe 
157721e6868Suwe 	do {
158721e6868Suwe 		csr = ADC_(CSR);
159721e6868Suwe 		if (timo-- == 0)
160721e6868Suwe 			break;
161721e6868Suwe 	} while ((csr & SH7709_ADCSR_ADF) == 0);
162721e6868Suwe 
163721e6868Suwe 	/* stop scanning */
164721e6868Suwe 	csr &= ~(SH7709_ADCSR_ADF | SH7709_ADCSR_ADST);
165721e6868Suwe 	ADC_(CSR) = csr;
166721e6868Suwe 
167721e6868Suwe 	if (timo <= 0) {
168721e6868Suwe 		printf("adc_sample_channel(%d): timed out\n", chan);
169721e6868Suwe 		return (-1);
170721e6868Suwe 	}
171721e6868Suwe 
172721e6868Suwe 	/*
173721e6868Suwe 	 * 10 bit of data: bits [9..2] in the high register, bits
174721e6868Suwe 	 * [1..0] in the bits [7..6] of the low register.
175721e6868Suwe 	 */
176721e6868Suwe 	return (((*hireg << 8) | *loreg) >> 6);
177721e6868Suwe }
178