1119452Sobrien/*-
245720Speter * Copyright 1998 Massachusetts Institute of Technology
345720Speter *
445720Speter * Permission to use, copy, modify, and distribute this software and
545720Speter * its documentation for any purpose and without fee is hereby
645720Speter * granted, provided that both the above copyright notice and this
745720Speter * permission notice appear in all copies, that both the above
845720Speter * copyright notice and this permission notice appear in all
945720Speter * supporting documentation, and that the name of M.I.T. not be used
1045720Speter * in advertising or publicity pertaining to distribution of the
1145720Speter * software without specific, written prior permission.  M.I.T. makes
1245720Speter * no representations about the suitability of this software for any
1345720Speter * purpose.  It is provided "as is" without express or implied
1445720Speter * warranty.
1545720Speter *
1645720Speter * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
1745720Speter * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
1845720Speter * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
1945720Speter * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
2045720Speter * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2145720Speter * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2245720Speter * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
2345720Speter * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
2445720Speter * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2545720Speter * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
2645720Speter * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2745720Speter * SUCH DAMAGE.
2845720Speter */
2945720Speter
30233707Sjhb#ifdef __i386__
31233707Sjhb#include "opt_eisa.h"
32233707Sjhb#include "opt_mca.h"
33233707Sjhb#endif
34115683Sobrien#include <sys/cdefs.h>
35115683Sobrien__FBSDID("$FreeBSD: releng/10.3/sys/x86/x86/legacy.c 233707 2012-03-30 19:10:14Z jhb $");
36115683Sobrien
3745720Speter/*
38103862Sjhb * This code implements a system driver for legacy systems that do not
39103862Sjhb * support ACPI or when ACPI support is not present in the kernel.
4045720Speter */
4145720Speter
4245720Speter#include <sys/param.h>
4345720Speter#include <sys/systm.h>
4445720Speter#include <sys/bus.h>
45141238Snjl#include <sys/cpu.h>
4645720Speter#include <sys/kernel.h>
4766400Smsmith#include <sys/malloc.h>
48129876Sphk#include <sys/module.h>
4945720Speter#include <machine/bus.h>
50129012Snjl#include <sys/pcpu.h>
5145720Speter#include <sys/rman.h>
52129012Snjl#include <sys/smp.h>
5345720Speter
54116277Smdodd#ifdef DEV_MCA
55116277Smdodd#include <i386/bios/mca_machdep.h>
56116277Smdodd#endif
57116277Smdodd
58216443Sjkim#include <machine/clock.h>
5945720Speter#include <machine/resource.h>
60233707Sjhb#include <x86/legacyvar.h>
6145720Speter
62103862Sjhbstatic MALLOC_DEFINE(M_LEGACYDEV, "legacydrv", "legacy system device");
63103862Sjhbstruct legacy_device {
64233676Sjhb	int	lg_pcibus;
65233676Sjhb	int	lg_pcislot;
66233676Sjhb	int	lg_pcifunc;
6766400Smsmith};
6866400Smsmith
69103862Sjhb#define DEVTOAT(dev)	((struct legacy_device *)device_get_ivars(dev))
7066400Smsmith
71103862Sjhbstatic	int legacy_probe(device_t);
72103862Sjhbstatic	int legacy_attach(device_t);
73103862Sjhbstatic	int legacy_print_child(device_t, device_t);
74212413Savgstatic device_t legacy_add_child(device_t bus, u_int order, const char *name,
7548832Smsmith				int unit);
76103862Sjhbstatic	int legacy_read_ivar(device_t, device_t, int, uintptr_t *);
77103862Sjhbstatic	int legacy_write_ivar(device_t, device_t, int, uintptr_t);
7845720Speter
79103862Sjhbstatic device_method_t legacy_methods[] = {
8045720Speter	/* Device interface */
81103862Sjhb	DEVMETHOD(device_probe,		legacy_probe),
82103862Sjhb	DEVMETHOD(device_attach,	legacy_attach),
8345720Speter	DEVMETHOD(device_detach,	bus_generic_detach),
8445720Speter	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
8546917Sdfr	DEVMETHOD(device_suspend,	bus_generic_suspend),
8646917Sdfr	DEVMETHOD(device_resume,	bus_generic_resume),
8745720Speter
8845720Speter	/* Bus interface */
89103862Sjhb	DEVMETHOD(bus_print_child,	legacy_print_child),
90103862Sjhb	DEVMETHOD(bus_add_child,	legacy_add_child),
91103862Sjhb	DEVMETHOD(bus_read_ivar,	legacy_read_ivar),
92103862Sjhb	DEVMETHOD(bus_write_ivar,	legacy_write_ivar),
93133886Sgibbs	DEVMETHOD(bus_alloc_resource,	bus_generic_alloc_resource),
94222929Sjhb	DEVMETHOD(bus_adjust_resource,	bus_generic_adjust_resource),
95133886Sgibbs	DEVMETHOD(bus_release_resource,	bus_generic_release_resource),
96103862Sjhb	DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
97103862Sjhb	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
98103862Sjhb	DEVMETHOD(bus_setup_intr,	bus_generic_setup_intr),
99103862Sjhb	DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
10045720Speter
10145720Speter	{ 0, 0 }
10245720Speter};
10345720Speter
104103862Sjhbstatic driver_t legacy_driver = {
105103862Sjhb	"legacy",
106103862Sjhb	legacy_methods,
10745720Speter	1,			/* no softc */
10845720Speter};
109103862Sjhbstatic devclass_t legacy_devclass;
11045720Speter
111103862SjhbDRIVER_MODULE(legacy, nexus, legacy_driver, legacy_devclass, 0, 0);
11245720Speter
11345720Speterstatic int
114103862Sjhblegacy_probe(device_t dev)
11545720Speter{
11645720Speter
117103862Sjhb	device_set_desc(dev, "legacy system");
118103862Sjhb	device_quiet(dev);
119103862Sjhb	return (0);
12050183Speter}
12145808Speter
12250183Speterstatic int
123103862Sjhblegacy_attach(device_t dev)
12450183Speter{
125129012Snjl	device_t child;
12645808Speter
12750183Speter	/*
128177041Sjhb	 * Let our child drivers identify any child devices that they
129177041Sjhb	 * can find.  Once that is done attach any devices that we
130103862Sjhb	 * found.
13150183Speter	 */
132103862Sjhb	bus_generic_probe(dev);
13350252Speter	bus_generic_attach(dev);
134103862Sjhb
13550183Speter	/*
136103862Sjhb	 * If we didn't see EISA or ISA on a pci bridge, create some
13750183Speter	 * connection points now so they show up "on motherboard".
13850183Speter	 */
139233707Sjhb#ifdef DEV_EISA
14050183Speter	if (!devclass_get_device(devclass_find("eisa"), 0)) {
14166503Speter		child = BUS_ADD_CHILD(dev, 0, "eisa", 0);
14250183Speter		if (child == NULL)
143103862Sjhb			panic("legacy_attach eisa");
14450252Speter		device_probe_and_attach(child);
14550183Speter	}
146138520Simp#endif
147116277Smdodd#ifdef DEV_MCA
148116277Smdodd	if (MCA_system && !devclass_get_device(devclass_find("mca"), 0)) {
14966503Speter        	child = BUS_ADD_CHILD(dev, 0, "mca", 0);
15050823Smdodd        	if (child == 0)
151103862Sjhb                	panic("legacy_probe mca");
15250823Smdodd		device_probe_and_attach(child);
15350823Smdodd	}
154116277Smdodd#endif
15550183Speter	if (!devclass_get_device(devclass_find("isa"), 0)) {
15666503Speter		child = BUS_ADD_CHILD(dev, 0, "isa", 0);
15750183Speter		if (child == NULL)
158103862Sjhb			panic("legacy_attach isa");
15950252Speter		device_probe_and_attach(child);
16050183Speter	}
16154073Smdodd
16245720Speter	return 0;
16345720Speter}
16445720Speter
16549195Smdoddstatic int
166103862Sjhblegacy_print_child(device_t bus, device_t child)
16745720Speter{
168103862Sjhb	struct legacy_device *atdev = DEVTOAT(child);
16949195Smdodd	int retval = 0;
17049195Smdodd
17149195Smdodd	retval += bus_print_child_header(bus, child);
172103862Sjhb	if (atdev->lg_pcibus != -1)
173103862Sjhb		retval += printf(" pcibus %d", atdev->lg_pcibus);
17466400Smsmith	retval += printf(" on motherboard\n");	/* XXX "motherboard", ick */
17549195Smdodd
17649195Smdodd	return (retval);
17745720Speter}
17845720Speter
17948832Smsmithstatic device_t
180212413Savglegacy_add_child(device_t bus, u_int order, const char *name, int unit)
18148832Smsmith{
182103862Sjhb	device_t child;
183103862Sjhb	struct legacy_device *atdev;
18466400Smsmith
185103862Sjhb	atdev = malloc(sizeof(struct legacy_device), M_LEGACYDEV,
186103862Sjhb	    M_NOWAIT | M_ZERO);
187129961Sjhb	if (atdev == NULL)
188129961Sjhb		return(NULL);
189103862Sjhb	atdev->lg_pcibus = -1;
190233676Sjhb	atdev->lg_pcislot = -1;
191233676Sjhb	atdev->lg_pcifunc = -1;
19266400Smsmith
193129961Sjhb	child = device_add_child_ordered(bus, order, name, unit);
194129961Sjhb	if (child == NULL)
195129961Sjhb		free(atdev, M_LEGACYDEV);
196129961Sjhb	else
197129961Sjhb		/* should we free this in legacy_child_detached? */
198129961Sjhb		device_set_ivars(child, atdev);
19966400Smsmith
200129961Sjhb	return (child);
20148832Smsmith}
20248832Smsmith
20366416Speterstatic int
204103862Sjhblegacy_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
20566416Speter{
206103862Sjhb	struct legacy_device *atdev = DEVTOAT(child);
20766416Speter
20866416Speter	switch (which) {
209172394Smarius	case LEGACY_IVAR_PCIDOMAIN:
210172394Smarius		*result = 0;
211172394Smarius		break;
212103862Sjhb	case LEGACY_IVAR_PCIBUS:
213103862Sjhb		*result = atdev->lg_pcibus;
21466416Speter		break;
215233676Sjhb	case LEGACY_IVAR_PCISLOT:
216233676Sjhb		*result = atdev->lg_pcislot;
217233676Sjhb		break;
218233676Sjhb	case LEGACY_IVAR_PCIFUNC:
219233676Sjhb		*result = atdev->lg_pcifunc;
220233676Sjhb		break;
22166416Speter	default:
22266416Speter		return ENOENT;
22366416Speter	}
22466416Speter	return 0;
22566416Speter}
22666416Speter
22766416Speter
22866416Speterstatic int
229103862Sjhblegacy_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
23066416Speter{
231103862Sjhb	struct legacy_device *atdev = DEVTOAT(child);
23266416Speter
23366416Speter	switch (which) {
234172394Smarius	case LEGACY_IVAR_PCIDOMAIN:
235172394Smarius		return EINVAL;
236103862Sjhb	case LEGACY_IVAR_PCIBUS:
237103862Sjhb		atdev->lg_pcibus = value;
23866416Speter		break;
239233676Sjhb	case LEGACY_IVAR_PCISLOT:
240233676Sjhb		atdev->lg_pcislot = value;
241233676Sjhb		break;
242233676Sjhb	case LEGACY_IVAR_PCIFUNC:
243233676Sjhb		atdev->lg_pcifunc = value;
244233676Sjhb		break;
24566416Speter	default:
24666416Speter		return ENOENT;
24766416Speter	}
24866416Speter	return 0;
24966416Speter}
25066416Speter
251129012Snjl/*
252129012Snjl * Legacy CPU attachment when ACPI is not available.  Drivers like
253129012Snjl * cpufreq(4) hang off this.
254129012Snjl */
255177041Sjhbstatic void	cpu_identify(driver_t *driver, device_t parent);
256129012Snjlstatic int	cpu_read_ivar(device_t dev, device_t child, int index,
257129012Snjl		    uintptr_t *result);
258212413Savgstatic device_t cpu_add_child(device_t bus, u_int order, const char *name,
259141238Snjl		    int unit);
260141238Snjlstatic struct resource_list *cpu_get_rlist(device_t dev, device_t child);
261129012Snjl
262141238Snjlstruct cpu_device {
263141238Snjl	struct resource_list cd_rl;
264141238Snjl	struct pcpu *cd_pcpu;
265141238Snjl};
266141238Snjl
267129012Snjlstatic device_method_t cpu_methods[] = {
268129012Snjl	/* Device interface */
269177041Sjhb	DEVMETHOD(device_identify,	cpu_identify),
270129012Snjl	DEVMETHOD(device_probe,		bus_generic_probe),
271129012Snjl	DEVMETHOD(device_attach,	bus_generic_attach),
272129012Snjl	DEVMETHOD(device_detach,	bus_generic_detach),
273129012Snjl	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
274129012Snjl	DEVMETHOD(device_suspend,	bus_generic_suspend),
275129012Snjl	DEVMETHOD(device_resume,	bus_generic_resume),
276129012Snjl
277129012Snjl	/* Bus interface */
278141238Snjl	DEVMETHOD(bus_add_child,	cpu_add_child),
279129012Snjl	DEVMETHOD(bus_read_ivar,	cpu_read_ivar),
280141238Snjl	DEVMETHOD(bus_get_resource_list, cpu_get_rlist),
281141238Snjl	DEVMETHOD(bus_get_resource,	bus_generic_rl_get_resource),
282141238Snjl	DEVMETHOD(bus_set_resource,	bus_generic_rl_set_resource),
283141238Snjl	DEVMETHOD(bus_alloc_resource,	bus_generic_rl_alloc_resource),
284141238Snjl	DEVMETHOD(bus_release_resource,	bus_generic_rl_release_resource),
285129012Snjl	DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
286129012Snjl	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
287129012Snjl	DEVMETHOD(bus_setup_intr,	bus_generic_setup_intr),
288129012Snjl	DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
289129012Snjl
290227843Smarius	DEVMETHOD_END
291129012Snjl};
292129012Snjl
293129012Snjlstatic driver_t cpu_driver = {
294129012Snjl	"cpu",
295129012Snjl	cpu_methods,
296129012Snjl	1,		/* no softc */
297129012Snjl};
298141380Snjlstatic devclass_t cpu_devclass;
299129012SnjlDRIVER_MODULE(cpu, legacy, cpu_driver, cpu_devclass, 0, 0);
300129012Snjl
301177041Sjhbstatic void
302177041Sjhbcpu_identify(driver_t *driver, device_t parent)
303177041Sjhb{
304177041Sjhb	device_t child;
305177041Sjhb	int i;
306177041Sjhb
307177041Sjhb	/*
308177041Sjhb	 * Attach a cpuX device for each CPU.  We use an order of 150
309177041Sjhb	 * so that these devices are attached after the Host-PCI
310177041Sjhb	 * bridges (which are added at order 100).
311177041Sjhb	 */
312209059Sjhb	CPU_FOREACH(i) {
313209059Sjhb		child = BUS_ADD_CHILD(parent, 150, "cpu", i);
314209059Sjhb		if (child == NULL)
315209059Sjhb			panic("legacy_attach cpu");
316209059Sjhb	}
317177041Sjhb}
318177041Sjhb
319141238Snjlstatic device_t
320212413Savgcpu_add_child(device_t bus, u_int order, const char *name, int unit)
321141238Snjl{
322141238Snjl	struct cpu_device *cd;
323141238Snjl	device_t child;
324141238Snjl	struct pcpu *pc;
325141238Snjl
326141238Snjl	if ((cd = malloc(sizeof(*cd), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL)
327141238Snjl		return (NULL);
328141238Snjl
329141238Snjl	resource_list_init(&cd->cd_rl);
330141942Snjl	pc = pcpu_find(device_get_unit(bus));
331141238Snjl	cd->cd_pcpu = pc;
332141238Snjl
333141238Snjl	child = device_add_child_ordered(bus, order, name, unit);
334141238Snjl	if (child != NULL) {
335141238Snjl		pc->pc_device = child;
336141238Snjl		device_set_ivars(child, cd);
337141238Snjl	} else
338141238Snjl		free(cd, M_DEVBUF);
339141238Snjl	return (child);
340141238Snjl}
341141238Snjl
342141238Snjlstatic struct resource_list *
343141238Snjlcpu_get_rlist(device_t dev, device_t child)
344141238Snjl{
345141238Snjl	struct cpu_device *cpdev;
346141238Snjl
347141238Snjl	cpdev = device_get_ivars(child);
348141238Snjl	return (&cpdev->cd_rl);
349141238Snjl}
350141238Snjl
351129012Snjlstatic int
352129012Snjlcpu_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
353129012Snjl{
354141238Snjl	struct cpu_device *cpdev;
355129012Snjl
356216443Sjkim	switch (index) {
357216443Sjkim	case CPU_IVAR_PCPU:
358216443Sjkim		cpdev = device_get_ivars(child);
359216443Sjkim		*result = (uintptr_t)cpdev->cd_pcpu;
360216443Sjkim		break;
361216443Sjkim	case CPU_IVAR_NOMINAL_MHZ:
362216443Sjkim		if (tsc_is_invariant) {
363220433Sjkim			*result = (uintptr_t)(atomic_load_acq_64(&tsc_freq) /
364220433Sjkim			    1000000);
365216443Sjkim			break;
366216443Sjkim		}
367216443Sjkim		/* FALLTHROUGH */
368216443Sjkim	default:
369129012Snjl		return (ENOENT);
370216443Sjkim	}
371129012Snjl	return (0);
372129012Snjl}
373