xref: /netbsd-src/sys/arch/mips/alchemy/dev/aupsc.c (revision c7fb772b85b2b5d4cfb282f868f454b4701534fd)
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