xref: /netbsd-src/sys/arch/x86/pci/rdcpcib.c (revision c2ac6d470a5024e58aa2482dcb6ff645aeb883bb)
1*c2ac6d47Schristos /*	$NetBSD: rdcpcib.c,v 1.3 2020/04/07 12:42:11 christos Exp $	*/
268c8192bSbouyer 
368c8192bSbouyer /*
468c8192bSbouyer  * Copyright (c) 2011 Manuel Bouyer.
568c8192bSbouyer  *
668c8192bSbouyer  * Redistribution and use in source and binary forms, with or without
768c8192bSbouyer  * modification, are permitted provided that the following conditions
868c8192bSbouyer  * are met:
968c8192bSbouyer  * 1. Redistributions of source code must retain the above copyright
1068c8192bSbouyer  *    notice, this list of conditions and the following disclaimer.
1168c8192bSbouyer  * 2. Redistributions in binary form must reproduce the above copyright
1268c8192bSbouyer  *    notice, this list of conditions and the following disclaimer in the
1368c8192bSbouyer  *    documentation and/or other materials provided with the distribution.
1468c8192bSbouyer  *
1568c8192bSbouyer  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1668c8192bSbouyer  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1768c8192bSbouyer  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1868c8192bSbouyer  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1968c8192bSbouyer  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2068c8192bSbouyer  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2168c8192bSbouyer  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2268c8192bSbouyer  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2368c8192bSbouyer  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2468c8192bSbouyer  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2568c8192bSbouyer  */
2668c8192bSbouyer 
2768c8192bSbouyer /*
2868c8192bSbouyer  * driver for the RDC vortex86/PMX-1000 SoC PCI-ISA bridge, which also drives
2968c8192bSbouyer  * the watchdog timer
3068c8192bSbouyer  */
3168c8192bSbouyer 
3268c8192bSbouyer 
3368c8192bSbouyer #include <sys/cdefs.h>
34*c2ac6d47Schristos __KERNEL_RCSID(0, "$NetBSD: rdcpcib.c,v 1.3 2020/04/07 12:42:11 christos Exp $");
3568c8192bSbouyer 
3668c8192bSbouyer #include <sys/types.h>
3768c8192bSbouyer #include <sys/param.h>
3868c8192bSbouyer #include <sys/systm.h>
3968c8192bSbouyer #include <sys/device.h>
4068c8192bSbouyer #include <sys/sysctl.h>
4168c8192bSbouyer #include <sys/timetc.h>
4268c8192bSbouyer #include <sys/gpio.h>
43391925c7Sdyoung #include <sys/bus.h>
4468c8192bSbouyer 
4568c8192bSbouyer #include <dev/pci/pcivar.h>
4668c8192bSbouyer #include <dev/pci/pcireg.h>
4768c8192bSbouyer #include <dev/pci/pcidevs.h>
4868c8192bSbouyer 
4968c8192bSbouyer #include <dev/gpio/gpiovar.h>
5068c8192bSbouyer #include <dev/sysmon/sysmonvar.h>
5168c8192bSbouyer 
5268c8192bSbouyer #include "pcibvar.h"
5368c8192bSbouyer 
5468c8192bSbouyer /*
5568c8192bSbouyer  * special registers: iospace-registers for indirect access to timer and GPIO
5668c8192bSbouyer  */
5768c8192bSbouyer #define RDC_IND_BASE 0x22
5868c8192bSbouyer #define RDC_IND_SIZE 0x2
5968c8192bSbouyer #define RDC_IND_ADDR 0
6068c8192bSbouyer #define RDC_IND_DATA 1
6168c8192bSbouyer 
6268c8192bSbouyer struct rdcpcib_softc {
6368c8192bSbouyer 	struct pcib_softc	rdc_pcib;
6468c8192bSbouyer 
6568c8192bSbouyer 	/* indirect registers mapping */
6668c8192bSbouyer 	bus_space_tag_t		rdc_iot;
6768c8192bSbouyer 	bus_space_handle_t	rdc_ioh;
6868c8192bSbouyer 
6968c8192bSbouyer 	/* Watchdog suppoprt */
7068c8192bSbouyer 	struct sysmon_wdog	rdc_smw;
7168c8192bSbouyer };
7268c8192bSbouyer 
7368c8192bSbouyer static int rdcpcibmatch(device_t, cfdata_t, void *);
7468c8192bSbouyer static void rdcpcibattach(device_t, device_t, void *);
7568c8192bSbouyer static int rdcpcibdetach(device_t, int);
7668c8192bSbouyer 
7768c8192bSbouyer static uint8_t rdc_ind_read(struct rdcpcib_softc *, uint8_t);
7868c8192bSbouyer static void rdc_ind_write(struct rdcpcib_softc *, uint8_t, uint8_t);
7968c8192bSbouyer 
8068c8192bSbouyer static void rdc_wdtimer_configure(device_t);
8168c8192bSbouyer static int rdc_wdtimer_unconfigure(device_t, int);
8268c8192bSbouyer static int rdc_wdtimer_setmode(struct sysmon_wdog *);
8368c8192bSbouyer static int rdc_wdtimer_tickle(struct sysmon_wdog *);
8468c8192bSbouyer static void rdc_wdtimer_stop(struct rdcpcib_softc *);
8568c8192bSbouyer static void rdc_wdtimer_start(struct rdcpcib_softc *);
8668c8192bSbouyer 
8768c8192bSbouyer CFATTACH_DECL2_NEW(rdcpcib, sizeof(struct rdcpcib_softc),
8868c8192bSbouyer     rdcpcibmatch, rdcpcibattach, rdcpcibdetach, NULL,
8968c8192bSbouyer     pcibrescan, pcibchilddet);
9068c8192bSbouyer 
91*c2ac6d47Schristos 
92*c2ac6d47Schristos static const struct rdcpcib_device {
93*c2ac6d47Schristos 	pcireg_t vendor, product;
94*c2ac6d47Schristos } rdcpcib_devices[] = {
95*c2ac6d47Schristos 	{ PCI_VENDOR_RDC, PCI_PRODUCT_RDC_R6011_PCIB},
96*c2ac6d47Schristos 	{ PCI_VENDOR_RDC, PCI_PRODUCT_RDC_R6013_PCIB},
97*c2ac6d47Schristos 	{ PCI_VENDOR_RDC, PCI_PRODUCT_RDC_R6031_PCIB},
98*c2ac6d47Schristos 	{ PCI_VENDOR_RDC, PCI_PRODUCT_RDC_R6035_PCIB},
99*c2ac6d47Schristos 	{ PCI_VENDOR_RDC, PCI_PRODUCT_RDC_R6036_PCIB},
100*c2ac6d47Schristos };
101*c2ac6d47Schristos 
10268c8192bSbouyer static int
rdcpcibmatch(device_t parent,cfdata_t match,void * aux)10368c8192bSbouyer rdcpcibmatch(device_t parent, cfdata_t match, void *aux)
10468c8192bSbouyer {
10568c8192bSbouyer 	struct pci_attach_args *pa = aux;
10668c8192bSbouyer 
10768c8192bSbouyer 	if (PCI_CLASS(pa->pa_class) != PCI_CLASS_BRIDGE ||
10868c8192bSbouyer 	    PCI_SUBCLASS(pa->pa_class) != PCI_SUBCLASS_BRIDGE_ISA)
10968c8192bSbouyer 		return 0;
11068c8192bSbouyer 
111*c2ac6d47Schristos 	for (size_t i = 0; i < __arraycount(rdcpcib_devices); i++) {
112*c2ac6d47Schristos 		if (PCI_VENDOR(pa->pa_id) == rdcpcib_devices[i].vendor &&
113*c2ac6d47Schristos 		    PCI_PRODUCT(pa->pa_id) == rdcpcib_devices[i].product)
11468c8192bSbouyer 			return 10;
115*c2ac6d47Schristos 	}
11668c8192bSbouyer 
11768c8192bSbouyer 	return 0;
11868c8192bSbouyer }
11968c8192bSbouyer 
12068c8192bSbouyer static void
rdcpcibattach(device_t parent,device_t self,void * aux)12168c8192bSbouyer rdcpcibattach(device_t parent, device_t self, void *aux)
12268c8192bSbouyer {
12368c8192bSbouyer 	struct rdcpcib_softc *sc = device_private(self);
12468c8192bSbouyer 
12568c8192bSbouyer 	/* generic PCI/ISA bridge */
12668c8192bSbouyer 	pcibattach(parent, self, aux);
12768c8192bSbouyer 
12868c8192bSbouyer 	/* map indirect registers */
12968c8192bSbouyer 	sc->rdc_iot = x86_bus_space_io;
13068c8192bSbouyer 	if (bus_space_map(sc->rdc_iot, RDC_IND_BASE, RDC_IND_SIZE, 0,
13168c8192bSbouyer 	    &sc->rdc_ioh) != 0) {
13268c8192bSbouyer 		aprint_error_dev(self, "couldn't map indirect registers\n");
13368c8192bSbouyer 		return;
13468c8192bSbouyer 	}
13568c8192bSbouyer 
13668c8192bSbouyer 	/* Set up the watchdog. */
13768c8192bSbouyer 	rdc_wdtimer_configure(self);
13868c8192bSbouyer 
13968c8192bSbouyer 	/* Install power handler XXX */
14068c8192bSbouyer 	if (!pmf_device_register(self, NULL, NULL))
14168c8192bSbouyer 		aprint_error_dev(self, "couldn't establish power handler\n");
14268c8192bSbouyer }
14368c8192bSbouyer 
14468c8192bSbouyer static int
rdcpcibdetach(device_t self,int flags)14568c8192bSbouyer rdcpcibdetach(device_t self, int flags)
14668c8192bSbouyer {
14768c8192bSbouyer 	struct rdcpcib_softc *sc = device_private(self);
14868c8192bSbouyer 	int rc;
14968c8192bSbouyer 
15068c8192bSbouyer 	pmf_device_deregister(self);
15168c8192bSbouyer 
15268c8192bSbouyer 	if ((rc = rdc_wdtimer_unconfigure(self, flags)) != 0)
15368c8192bSbouyer 		return rc;
15468c8192bSbouyer 
15568c8192bSbouyer 	bus_space_unmap(sc->rdc_iot, sc->rdc_ioh, RDC_IND_SIZE);
15668c8192bSbouyer 	return pcibdetach(self, flags);
15768c8192bSbouyer }
15868c8192bSbouyer 
15968c8192bSbouyer /* indirect registers read/write */
16068c8192bSbouyer static uint8_t
rdc_ind_read(struct rdcpcib_softc * sc,uint8_t addr)16168c8192bSbouyer rdc_ind_read(struct rdcpcib_softc *sc, uint8_t addr)
16268c8192bSbouyer {
16368c8192bSbouyer 	bus_space_write_1(sc->rdc_iot, sc->rdc_ioh, RDC_IND_ADDR, addr);
16468c8192bSbouyer 	return bus_space_read_1(sc->rdc_iot, sc->rdc_ioh, RDC_IND_DATA);
16568c8192bSbouyer }
16668c8192bSbouyer 
16768c8192bSbouyer static void
rdc_ind_write(struct rdcpcib_softc * sc,uint8_t addr,uint8_t data)16868c8192bSbouyer rdc_ind_write(struct rdcpcib_softc *sc, uint8_t addr, uint8_t data)
16968c8192bSbouyer {
17068c8192bSbouyer 	bus_space_write_1(sc->rdc_iot, sc->rdc_ioh, RDC_IND_ADDR, addr);
17168c8192bSbouyer 	bus_space_write_1(sc->rdc_iot, sc->rdc_ioh, RDC_IND_DATA, data);
17268c8192bSbouyer }
17368c8192bSbouyer 
17468c8192bSbouyer /*
17568c8192bSbouyer  * watchdog timer registers
17668c8192bSbouyer  */
17768c8192bSbouyer 
17868c8192bSbouyer /* control */
17968c8192bSbouyer #define RDC_WDT0_CTRL	0x37
18068c8192bSbouyer #define RDC_WDT0_CTRL_EN	0x40
18168c8192bSbouyer 
18268c8192bSbouyer /* signal select */
18368c8192bSbouyer #define RDC_WDT0_SSEL	0x38
18468c8192bSbouyer #define RDC_WDT0_SSEL_MSK	0xf0
18568c8192bSbouyer #define RDC_WDT0_SSEL_NMI	0xc0
18668c8192bSbouyer #define RDC_WDT0_SSEL_RST	0xd0
18768c8192bSbouyer 
18868c8192bSbouyer /* counter */
18968c8192bSbouyer #define RDC_WDT0_CNTL	0x39
19068c8192bSbouyer #define RDC_WDT0_CNTH	0x3A
19168c8192bSbouyer #define RDC_WDT0_CNTU	0x3B
19268c8192bSbouyer #define RDC_WDT0_FREQ		32768 /* Hz */
19368c8192bSbouyer #define RDC_WDT0_PERIOD_MAX	(1 << 24)
19468c8192bSbouyer 
19568c8192bSbouyer /* clear counter */
19668c8192bSbouyer #define RDC_WDT0_CTRL1	0x3c
19768c8192bSbouyer #define RDC_WDT0_CTRL1_RELOAD	0x40
19868c8192bSbouyer #define RDC_WDT0_CRTL1_FIRE	0x80
19968c8192bSbouyer 
20068c8192bSbouyer 
20168c8192bSbouyer /*
20268c8192bSbouyer  * Initialize the watchdog timer.
20368c8192bSbouyer  */
20468c8192bSbouyer static void
rdc_wdtimer_configure(device_t self)20568c8192bSbouyer rdc_wdtimer_configure(device_t self)
20668c8192bSbouyer {
20768c8192bSbouyer 	struct rdcpcib_softc *sc = device_private(self);
20868c8192bSbouyer 	uint8_t reg;
20968c8192bSbouyer 
21068c8192bSbouyer 	/* Explicitly stop the timer. */
21168c8192bSbouyer 	rdc_wdtimer_stop(sc);
21268c8192bSbouyer 
21368c8192bSbouyer 	/*
21468c8192bSbouyer 	 * Register the driver with the sysmon watchdog framework.
21568c8192bSbouyer 	 */
21668c8192bSbouyer 	sc->rdc_smw.smw_name = device_xname(self);
21768c8192bSbouyer 	sc->rdc_smw.smw_cookie = sc;
21868c8192bSbouyer 	sc->rdc_smw.smw_setmode = rdc_wdtimer_setmode;
21968c8192bSbouyer 	sc->rdc_smw.smw_tickle = rdc_wdtimer_tickle;
22068c8192bSbouyer 	sc->rdc_smw.smw_period = RDC_WDT0_PERIOD_MAX / RDC_WDT0_FREQ;
22168c8192bSbouyer 
22268c8192bSbouyer 	if (sysmon_wdog_register(&sc->rdc_smw)) {
22368c8192bSbouyer 		aprint_error_dev(self, "unable to register wdt"
22468c8192bSbouyer 		       "as a sysmon watchdog device.\n");
22568c8192bSbouyer 		return;
22668c8192bSbouyer 	}
22768c8192bSbouyer 
22868c8192bSbouyer 	aprint_verbose_dev(self, "watchdog timer configured.\n");
22968c8192bSbouyer 	reg = rdc_ind_read(sc, RDC_WDT0_CTRL1);
23068c8192bSbouyer 	if (reg & RDC_WDT0_CRTL1_FIRE) {
23168c8192bSbouyer 		aprint_error_dev(self, "watchdog fired bit set, clearing\n");
23268c8192bSbouyer 		rdc_ind_write(sc, RDC_WDT0_CTRL1, reg & ~RDC_WDT0_CRTL1_FIRE);
23368c8192bSbouyer 	}
23468c8192bSbouyer }
23568c8192bSbouyer 
23668c8192bSbouyer static int
rdc_wdtimer_unconfigure(device_t self,int flags)23768c8192bSbouyer rdc_wdtimer_unconfigure(device_t self, int flags)
23868c8192bSbouyer {
23968c8192bSbouyer 	struct rdcpcib_softc *sc = device_private(self);
24068c8192bSbouyer 	int rc;
24168c8192bSbouyer 
24268c8192bSbouyer 	if ((rc = sysmon_wdog_unregister(&sc->rdc_smw)) != 0) {
24368c8192bSbouyer 		if (rc == ERESTART)
24468c8192bSbouyer 			rc = EINTR;
24568c8192bSbouyer 		return rc;
24668c8192bSbouyer 	}
24768c8192bSbouyer 
24868c8192bSbouyer 	/* Explicitly stop the timer. */
24968c8192bSbouyer 	rdc_wdtimer_stop(sc);
25068c8192bSbouyer 
25168c8192bSbouyer 	return 0;
25268c8192bSbouyer }
25368c8192bSbouyer 
25468c8192bSbouyer 
25568c8192bSbouyer /*
25668c8192bSbouyer  * Sysmon watchdog callbacks.
25768c8192bSbouyer  */
25868c8192bSbouyer static int
rdc_wdtimer_setmode(struct sysmon_wdog * smw)25968c8192bSbouyer rdc_wdtimer_setmode(struct sysmon_wdog *smw)
26068c8192bSbouyer {
26168c8192bSbouyer 	struct rdcpcib_softc *sc = smw->smw_cookie;
26268c8192bSbouyer 	unsigned int period;
26368c8192bSbouyer 
26468c8192bSbouyer 	if ((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED) {
26568c8192bSbouyer 		/* Stop the timer. */
26668c8192bSbouyer 		rdc_wdtimer_stop(sc);
26768c8192bSbouyer 	} else {
26868c8192bSbouyer 		period = smw->smw_period * RDC_WDT0_FREQ;
26968c8192bSbouyer 		if (period < 1 ||
27068c8192bSbouyer 		    period > RDC_WDT0_PERIOD_MAX)
27168c8192bSbouyer 			return EINVAL;
27268c8192bSbouyer 		period = period - 1;
27368c8192bSbouyer 
27468c8192bSbouyer 		/* Stop the timer, */
27568c8192bSbouyer 		rdc_wdtimer_stop(sc);
27668c8192bSbouyer 
27768c8192bSbouyer 		/* set the timeout, */
27868c8192bSbouyer 		rdc_ind_write(sc, RDC_WDT0_CNTL, (period >>  0) & 0xff);
27968c8192bSbouyer 		rdc_ind_write(sc, RDC_WDT0_CNTH, (period >>  8) & 0xff);
28068c8192bSbouyer 		rdc_ind_write(sc, RDC_WDT0_CNTU, (period >> 16) & 0xff);
28168c8192bSbouyer 
28268c8192bSbouyer 		/* and start the timer again */
28368c8192bSbouyer 		rdc_wdtimer_start(sc);
28468c8192bSbouyer 	}
28568c8192bSbouyer 	return 0;
28668c8192bSbouyer }
28768c8192bSbouyer 
28868c8192bSbouyer static int
rdc_wdtimer_tickle(struct sysmon_wdog * smw)28968c8192bSbouyer rdc_wdtimer_tickle(struct sysmon_wdog *smw)
29068c8192bSbouyer {
29168c8192bSbouyer 	struct rdcpcib_softc *sc = smw->smw_cookie;
29268c8192bSbouyer 	uint8_t reg;
29368c8192bSbouyer 
29468c8192bSbouyer 	reg = rdc_ind_read(sc, RDC_WDT0_CTRL1);
29568c8192bSbouyer 	rdc_ind_write(sc, RDC_WDT0_CTRL1, reg | RDC_WDT0_CTRL1_RELOAD);
29668c8192bSbouyer 	return 0;
29768c8192bSbouyer }
29868c8192bSbouyer 
29968c8192bSbouyer static void
rdc_wdtimer_stop(struct rdcpcib_softc * sc)30068c8192bSbouyer rdc_wdtimer_stop(struct rdcpcib_softc *sc)
30168c8192bSbouyer {
30268c8192bSbouyer 	uint8_t reg;
30368c8192bSbouyer 	reg = rdc_ind_read(sc, RDC_WDT0_CTRL);
30468c8192bSbouyer 	rdc_ind_write(sc, RDC_WDT0_CTRL, reg & ~RDC_WDT0_CTRL_EN);
30568c8192bSbouyer }
30668c8192bSbouyer 
30768c8192bSbouyer static void
rdc_wdtimer_start(struct rdcpcib_softc * sc)30868c8192bSbouyer rdc_wdtimer_start(struct rdcpcib_softc *sc)
30968c8192bSbouyer {
31068c8192bSbouyer 	uint8_t reg;
31168c8192bSbouyer 	rdc_ind_write(sc, RDC_WDT0_SSEL, RDC_WDT0_SSEL_RST);
31268c8192bSbouyer 	reg = rdc_ind_read(sc, RDC_WDT0_CTRL);
31368c8192bSbouyer 	rdc_ind_write(sc, RDC_WDT0_CTRL, reg | RDC_WDT0_CTRL_EN);
31468c8192bSbouyer }
315