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