1*c7fb772bSthorpej /* $NetBSD: aupsc.c,v 1.9 2021/08/07 16:18:58 thorpej Exp $ */
2ac76a40eSshige
3ac76a40eSshige /*-
4ac76a40eSshige * Copyright (c) 2006 Shigeyuki Fukushima.
5ac76a40eSshige * All rights reserved.
6ac76a40eSshige *
7ac76a40eSshige * Written by Shigeyuki Fukushima.
8ac76a40eSshige *
9ac76a40eSshige * Redistribution and use in source and binary forms, with or without
10ac76a40eSshige * modification, are permitted provided that the following conditions
11ac76a40eSshige * are met:
12ac76a40eSshige * 1. Redistributions of source code must retain the above copyright
13ac76a40eSshige * notice, this list of conditions and the following disclaimer.
14ac76a40eSshige * 2. Redistributions in binary form must reproduce the above
15ac76a40eSshige * copyright notice, this list of conditions and the following
16ac76a40eSshige * disclaimer in the documentation and/or other materials provided
17ac76a40eSshige * with the distribution.
18ac76a40eSshige * 3. The name of the author may not be used to endorse or promote
19ac76a40eSshige * products derived from this software without specific prior
20ac76a40eSshige * written permission.
21ac76a40eSshige *
22ac76a40eSshige * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
23ac76a40eSshige * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24ac76a40eSshige * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25ac76a40eSshige * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
26ac76a40eSshige * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27ac76a40eSshige * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
28ac76a40eSshige * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29ac76a40eSshige * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
30ac76a40eSshige * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31ac76a40eSshige * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32ac76a40eSshige * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33ac76a40eSshige */
34ac76a40eSshige
35ac76a40eSshige #include <sys/cdefs.h>
36*c7fb772bSthorpej __KERNEL_RCSID(0, "$NetBSD: aupsc.c,v 1.9 2021/08/07 16:18:58 thorpej Exp $");
37ac76a40eSshige
38ac76a40eSshige #include "locators.h"
39ac76a40eSshige
40ac76a40eSshige #include <sys/param.h>
41ac76a40eSshige #include <sys/systm.h>
42ac76a40eSshige #include <sys/device.h>
43ac76a40eSshige #include <sys/errno.h>
44ac76a40eSshige
45e265f67bSdyoung #include <sys/bus.h>
46ac76a40eSshige #include <machine/cpu.h>
47ac76a40eSshige
48ac76a40eSshige #include <mips/alchemy/include/aubusvar.h>
49ac76a40eSshige #include <mips/alchemy/include/aureg.h>
50ac76a40eSshige #include <mips/alchemy/dev/aupscreg.h>
51ac76a40eSshige #include <mips/alchemy/dev/aupscvar.h>
5297e07f10Sshige #include <mips/alchemy/dev/ausmbus_pscreg.h>
53ac76a40eSshige
54ac76a40eSshige struct aupsc_softc {
55f58fcf6aSkiyohara device_t sc_dev;
56ac76a40eSshige bus_space_tag_t sc_bust;
57ac76a40eSshige bus_space_handle_t sc_bush;
58ac76a40eSshige int sc_pscsel;
59ac76a40eSshige };
60ac76a40eSshige
61ac76a40eSshige const struct aupsc_proto {
62ac76a40eSshige const char *name;
6301a719a4Sshige int protocol;
64ac76a40eSshige } aupsc_protos [] = {
653bd9bd2fSgdamore { "ausmbus", AUPSC_SEL_SMBUS },
663bd9bd2fSgdamore { "auspi", AUPSC_SEL_SPI },
67ac76a40eSshige #if 0
68ac76a40eSshige { "auaudio" },
69ac76a40eSshige { "aui2s" },
70ac76a40eSshige #endif
713bd9bd2fSgdamore { NULL, AUPSC_SEL_DISABLE }
72ac76a40eSshige };
73ac76a40eSshige
74f58fcf6aSkiyohara static int aupsc_match(device_t, struct cfdata *, void *);
75f58fcf6aSkiyohara static void aupsc_attach(device_t, device_t, void *);
76f58fcf6aSkiyohara static int aupsc_submatch(device_t, struct cfdata *, const int *, void *);
7701a719a4Sshige static int aupsc_print(void *, const char *);
7801a719a4Sshige
7901a719a4Sshige static void aupsc_enable(void *, int);
8001a719a4Sshige static void aupsc_disable(void *);
8101a719a4Sshige static void aupsc_suspend(void *);
8201a719a4Sshige
83ac76a40eSshige
84f58fcf6aSkiyohara CFATTACH_DECL_NEW(aupsc, sizeof(struct aupsc_softc),
85ac76a40eSshige aupsc_match, aupsc_attach, NULL, NULL);
86ac76a40eSshige
87ac76a40eSshige static int
aupsc_match(device_t parent,struct cfdata * cf,void * aux)88f58fcf6aSkiyohara aupsc_match(device_t parent, struct cfdata *cf, void *aux)
89ac76a40eSshige {
90ac76a40eSshige struct aubus_attach_args *aa = (struct aubus_attach_args *)aux;
91ac76a40eSshige
92ac76a40eSshige if (strcmp(aa->aa_name, cf->cf_name) != 0)
93ac76a40eSshige return 0;
94ac76a40eSshige
95ac76a40eSshige return 1;
96ac76a40eSshige }
97ac76a40eSshige
98ac76a40eSshige static void
aupsc_attach(device_t parent,device_t self,void * aux)99f58fcf6aSkiyohara aupsc_attach(device_t parent, device_t self, void *aux)
100ac76a40eSshige {
101ac76a40eSshige int i;
102ac76a40eSshige uint32_t rv;
103f58fcf6aSkiyohara struct aupsc_softc *sc = device_private(self);
104ac76a40eSshige struct aubus_attach_args *aa = (struct aubus_attach_args *)aux;
105ac76a40eSshige struct aupsc_attach_args pa;
10601a719a4Sshige struct aupsc_controller ctrl;
107ac76a40eSshige
108f58fcf6aSkiyohara sc->sc_dev = self;
109ac76a40eSshige sc->sc_bust = aa->aa_st;
110ac76a40eSshige if (bus_space_map(sc->sc_bust, aa->aa_addr,
111ac76a40eSshige AUPSC_SIZE, 0, &sc->sc_bush) != 0) {
112f58fcf6aSkiyohara aprint_error(": unable to map device registers\n");
113ac76a40eSshige return;
114ac76a40eSshige }
115ac76a40eSshige
116ac76a40eSshige /* Initialize PSC_SEL register */
117ac76a40eSshige sc->sc_pscsel = AUPSC_SEL_DISABLE;
118ac76a40eSshige rv = bus_space_read_4(sc->sc_bust, sc->sc_bush, AUPSC_SEL);
119ac76a40eSshige bus_space_write_4(sc->sc_bust, sc->sc_bush,
120ac76a40eSshige AUPSC_SEL, (rv & AUPSC_SEL_PS(AUPSC_SEL_DISABLE)));
12101a719a4Sshige bus_space_write_4(sc->sc_bust, sc->sc_bush,
12201a719a4Sshige AUPSC_CTRL, AUPSC_CTRL_ENA(AUPSC_CTRL_DISABLE));
123ac76a40eSshige
124ac76a40eSshige aprint_normal(": Alchemy PSC\n");
125f58fcf6aSkiyohara aprint_naive("\n");
126ac76a40eSshige
12701a719a4Sshige ctrl.psc_bust = sc->sc_bust;
12801a719a4Sshige ctrl.psc_bush = sc->sc_bush;
12901a719a4Sshige ctrl.psc_sel = &(sc->sc_pscsel);
13001a719a4Sshige ctrl.psc_enable = aupsc_enable;
13101a719a4Sshige ctrl.psc_disable = aupsc_disable;
13201a719a4Sshige ctrl.psc_suspend = aupsc_suspend;
13301a719a4Sshige pa.aupsc_ctrl = ctrl;
1343bd9bd2fSgdamore pa.aupsc_addr = aa->aa_addr;
1353bd9bd2fSgdamore pa.aupsc_irq = aa->aa_irq[0];
136ac76a40eSshige
13701a719a4Sshige for (i = 0 ; aupsc_protos[i].name != NULL ; i++) {
13801a719a4Sshige struct aupsc_protocol_device p;
13901a719a4Sshige uint32_t s;
14001a719a4Sshige
14101a719a4Sshige pa.aupsc_name = aupsc_protos[i].name;
14201a719a4Sshige
14301a719a4Sshige p.sc_dev = sc->sc_dev;
14401a719a4Sshige p.sc_ctrl = ctrl;
14501a719a4Sshige
14601a719a4Sshige aupsc_enable(&p, aupsc_protos[i].protocol);
1473bd9bd2fSgdamore s = bus_space_read_4(sc->sc_bust, sc->sc_bush, AUPSC_STAT);
14801a719a4Sshige aupsc_disable(&p);
14901a719a4Sshige
1503bd9bd2fSgdamore if (s & AUPSC_STAT_SR) {
1512685996bSthorpej config_found(self, &pa, aupsc_print,
152*c7fb772bSthorpej CFARGS(.submatch = aupsc_submatch));
153ac76a40eSshige }
154ac76a40eSshige }
15501a719a4Sshige }
156ac76a40eSshige
157ac76a40eSshige static int
aupsc_submatch(device_t parent,struct cfdata * cf,const int * ldesc,void * aux)158f58fcf6aSkiyohara aupsc_submatch(device_t parent, struct cfdata *cf, const int *ldesc, void *aux)
159ac76a40eSshige {
160ac76a40eSshige
161ac76a40eSshige return config_match(parent, cf, aux);
162ac76a40eSshige }
163ac76a40eSshige
164ac76a40eSshige static int
aupsc_print(void * aux,const char * pnp)165ac76a40eSshige aupsc_print(void *aux, const char *pnp)
166ac76a40eSshige {
1673bd9bd2fSgdamore /*
1683bd9bd2fSgdamore * By default we don't want to print anything, because
1693bd9bd2fSgdamore * otherwise we see complaints about protocols that aren't
1703bd9bd2fSgdamore * configured on every port. (E.g. each PSC can support 4
1713bd9bd2fSgdamore * protocols, but on a typical design, only one protocol can
1723bd9bd2fSgdamore * be configured per board.)
1733bd9bd2fSgdamore *
1743bd9bd2fSgdamore * Basically, this whole thing should be replaced with an
1753bd9bd2fSgdamore * indirect configuration mechanism. Direct configuration
1763bd9bd2fSgdamore * doesn't make sense when we absolutely require kernel
1773bd9bd2fSgdamore * configuration to operate.
1783bd9bd2fSgdamore *
1793bd9bd2fSgdamore * Alternatively, a board-specific configuration mechanism
1803bd9bd2fSgdamore * could determine this, and provide direct configuration as
1813bd9bd2fSgdamore * we do for PCMCIA.
1823bd9bd2fSgdamore */
183ac76a40eSshige
1843bd9bd2fSgdamore return QUIET;
185ac76a40eSshige }
18601a719a4Sshige
18701a719a4Sshige static void
aupsc_enable(void * arg,int proto)18801a719a4Sshige aupsc_enable(void *arg, int proto)
18901a719a4Sshige {
19001a719a4Sshige struct aupsc_protocol_device *sc = arg;
1913bd9bd2fSgdamore int i;
19201a719a4Sshige
19301a719a4Sshige /* XXX: (TODO) setting clock AUPSC_SEL_CLK */
19401a719a4Sshige switch (proto) {
19501a719a4Sshige case AUPSC_SEL_SPI:
19601a719a4Sshige case AUPSC_SEL_I2S:
19701a719a4Sshige case AUPSC_SEL_AC97:
19801a719a4Sshige case AUPSC_SEL_SMBUS:
19901a719a4Sshige break;
20001a719a4Sshige case AUPSC_SEL_DISABLE:
20101a719a4Sshige aupsc_disable(arg);
20201a719a4Sshige break;
20301a719a4Sshige default:
20401a719a4Sshige printf("%s: aupsc_enable: unsupported protocol.\n",
205f58fcf6aSkiyohara device_xname(sc->sc_dev));
20601a719a4Sshige return;
20701a719a4Sshige }
20801a719a4Sshige
20901a719a4Sshige if (*(sc->sc_ctrl.psc_sel) != AUPSC_SEL_DISABLE) {
21001a719a4Sshige printf("%s: aupsc_enable: please disable first.\n",
211f58fcf6aSkiyohara device_xname(sc->sc_dev));
21201a719a4Sshige return;
21301a719a4Sshige }
21401a719a4Sshige
21501a719a4Sshige bus_space_write_4(sc->sc_ctrl.psc_bust, sc->sc_ctrl.psc_bush,
21601a719a4Sshige AUPSC_SEL, AUPSC_SEL_PS(proto));
21701a719a4Sshige bus_space_write_4(sc->sc_ctrl.psc_bust, sc->sc_ctrl.psc_bush,
21801a719a4Sshige AUPSC_CTRL, AUPSC_CTRL_ENA(AUPSC_CTRL_ENABLE));
2193bd9bd2fSgdamore
2203bd9bd2fSgdamore /* wait up to a whole second, but test every 10us */
2213bd9bd2fSgdamore for (i = 1000000; i; i -= 10) {
2223bd9bd2fSgdamore if (bus_space_read_4(sc->sc_ctrl.psc_bust,
2233bd9bd2fSgdamore sc->sc_ctrl.psc_bush, AUPSC_STAT) & AUPSC_STAT_SR)
2243bd9bd2fSgdamore return;
2253bd9bd2fSgdamore delay(10);
2263bd9bd2fSgdamore }
22701a719a4Sshige }
22801a719a4Sshige
22901a719a4Sshige static void
aupsc_disable(void * arg)23001a719a4Sshige aupsc_disable(void *arg)
23101a719a4Sshige {
23201a719a4Sshige struct aupsc_protocol_device *sc = arg;
23301a719a4Sshige
23401a719a4Sshige bus_space_write_4(sc->sc_ctrl.psc_bust, sc->sc_ctrl.psc_bush,
23501a719a4Sshige AUPSC_SEL, AUPSC_SEL_PS(AUPSC_SEL_DISABLE));
23601a719a4Sshige bus_space_write_4(sc->sc_ctrl.psc_bust, sc->sc_ctrl.psc_bush,
23701a719a4Sshige AUPSC_CTRL, AUPSC_CTRL_ENA(AUPSC_CTRL_DISABLE));
23801a719a4Sshige delay(1);
23901a719a4Sshige }
24001a719a4Sshige
24101a719a4Sshige static void
aupsc_suspend(void * arg)24201a719a4Sshige aupsc_suspend(void *arg)
24301a719a4Sshige {
24401a719a4Sshige struct aupsc_protocol_device *sc = arg;
24501a719a4Sshige
24601a719a4Sshige bus_space_write_4(sc->sc_ctrl.psc_bust, sc->sc_ctrl.psc_bush,
24701a719a4Sshige AUPSC_CTRL, AUPSC_CTRL_ENA(AUPSC_CTRL_SUSPEND));
24801a719a4Sshige delay(1);
24901a719a4Sshige }
250