summaryrefslogtreecommitdiffstats
path: root/sys/dev/pci/amdpcib.c
blob: d960472ff15f366610097f9cb3e4f1455c05f888 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
/*      $OpenBSD: amdpcib.c,v 1.3 2015/03/14 03:38:48 jsg Exp $	*/

/*
 * Copyright (c) 2007 Michael Shalayeff
 * All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN
 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * AMD8111 series LPC bridge also containing HPET and watchdog
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/timetc.h>

#include <machine/bus.h>

#include <dev/pci/pcivar.h>
#include <dev/pci/pcidevs.h>

#define	AMD8111_HPET	0xa0	/* PCI config space */
#define	AMD8111_HPET_ENA	0x00000001
#define	AMD8111_HPET_BASE	0xfffffc00
#define	AMD8111_HPET_SIZE	0x00000400

#define	AMD8111_HPET_ID		0x000
#define	AMD8111_HPET_WIDTH	0x00002000
#define	AMD8111_HPET_REV	0x000000ff
#define	AMD8111_HPET_PERIOD	0x004
#define	AMD8111_HPET_CFG	0x010
#define	AMD8111_HPET_CFG_GIEN	0x00000001
#define	AMD8111_HPET_ISTAT	0x020
#define	AMD8111_HPET_MAIN	0x0f0
#define	AMD8111_HPET_T0CFG	0x100
#define	AMD8111_HPET_T0CMP	0x108
#define	AMD8111_HPET_T1CFG	0x120
#define	AMD8111_HPET_T1CMP	0x128
#define	AMD8111_HPET_T2CFG	0x140
#define	AMD8111_HPET_T2CMP	0x148

#define	AMD8111_WDOG	0xa8	/* PCI config space */
#define	AMD8111_WDOG_ENA	0x00000001
#define	AMD8111_WDOG_HALT	0x00000002
#define	AMD8111_WDOG_SILENT	0x00000004
#define	AMD8111_WDOG_BASE	0xffffffe0

#define	AMD8111_WDOG_CTRL	0x00
#define	AMD8111_WDOG_RSTOP	0x0001
#define	AMD8111_WDOG_WFIR	0x0002
#define	AMD8111_WDOG_WACT	0x0004
#define	AMD8111_WDOG_WDALIAS	0x0008
#define	AMD8111_WDOG_WTRIG	0x0080
#define	AMD8111_WDOG_COUNT	0x08
#define	AMD8111_WDOG_MASK	0xffff

struct amdpcib_softc {
	struct device sc_dev;

	bus_space_tag_t sc_hpet_iot;
	bus_space_handle_t sc_hpet_ioh;
	struct timecounter sc_hpet_timecounter;
};

struct cfdriver amdpcib_cd = {
	NULL, "amdpcib", DV_DULL
};

int	amdpcib_match(struct device *, void *, void *);
void	amdpcib_attach(struct device *, struct device *, void *);

struct cfattach amdpcib_ca = {
	sizeof(struct amdpcib_softc), amdpcib_match, amdpcib_attach
};

/* from arch/<*>/pci/pcib.c */
void	pcibattach(struct device *parent, struct device *self, void *aux);

u_int	amdpcib_get_timecount(struct timecounter *tc);

const struct pci_matchid amdpcib_devices[] = {
	{ PCI_VENDOR_AMD,	PCI_PRODUCT_AMD_PBC8111_LPC }
	/* also available on 590 and 690 chipsets */
};

int
amdpcib_match(struct device *parent, void *match, void *aux)
{ 
	if (pci_matchbyid((struct pci_attach_args *)aux, amdpcib_devices,
	    sizeof(amdpcib_devices) / sizeof(amdpcib_devices[0])))
		return 2;

	return 0;
}

void
amdpcib_attach(struct device *parent, struct device *self, void *aux)
{
        struct amdpcib_softc *sc = (struct amdpcib_softc *)self;
	struct pci_attach_args *pa = aux;
	struct timecounter *tc = &sc->sc_hpet_timecounter;
	pcireg_t reg;
	u_int32_t v;


	sc->sc_hpet_iot = pa->pa_memt;
	reg = pci_conf_read(pa->pa_pc, pa->pa_tag, AMD8111_HPET);
	if (reg & AMD8111_HPET_ENA &&
	    bus_space_map(sc->sc_hpet_iot, reg & AMD8111_HPET_BASE,
	     AMD8111_HPET_SIZE, 0, &sc->sc_hpet_ioh) == 0) {

		tc->tc_get_timecount = amdpcib_get_timecount;
		/* XXX 64-bit counter is not supported! */
		tc->tc_counter_mask = 0xffffffff;

		v = bus_space_read_4(sc->sc_hpet_iot, sc->sc_hpet_ioh,
		    AMD8111_HPET_PERIOD);
		/* femtosecs -> Hz */
		tc->tc_frequency = 1000000000000000ULL / v;

		tc->tc_name = "AMD8111";
		tc->tc_quality = 2000;
		tc->tc_priv = sc;
		tc_init(tc);

		/* enable counting */
		bus_space_write_4(sc->sc_hpet_iot, sc->sc_hpet_ioh,
		    AMD8111_HPET_CFG, AMD8111_HPET_CFG_GIEN);

		v = bus_space_read_4(sc->sc_hpet_iot, sc->sc_hpet_ioh,
		    AMD8111_HPET_ID);
		printf(": %d-bit %lluHz timer rev %d",
		    (v & AMD8111_HPET_WIDTH? 64 : 32), tc->tc_frequency,
		    v & AMD8111_HPET_REV);
	}

	pcibattach(parent, self, aux);
}

u_int
amdpcib_get_timecount(struct timecounter *tc)
{
        struct amdpcib_softc *sc = tc->tc_priv;

	/* XXX 64-bit counter is not supported! */
	return bus_space_read_4(sc->sc_hpet_iot, sc->sc_hpet_ioh,
	    AMD8111_HPET_MAIN);
}