1/*	$OpenBSD: puc_cardbus.c,v 1.10 2024/05/24 06:26:47 jsg Exp $	*/
2
3/*
4 * Copyright (c) 2006 Michael Shalayeff
5 * All rights reserved.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN
16 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
17 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include <sys/param.h>
21#include <sys/systm.h>
22#include <sys/device.h>
23
24#include <dev/pci/pcireg.h>
25#include <dev/pci/pcivar.h>
26#include <dev/pci/pcidevs.h>
27#include <dev/cardbus/cardbusvar.h>
28
29#include <dev/pci/pucvar.h>
30
31struct puc_cardbus_softc {
32	struct puc_softc sc_psc;
33
34	struct cardbus_devfunc *ct;
35	int intrline;
36};
37
38int	puc_cardbus_match(struct device *, void *, void *);
39void	puc_cardbus_attach(struct device *, struct device *, void *);
40int	puc_cardbus_detach(struct device *, int);
41
42const char *puc_cardbus_intr_string(struct puc_attach_args *);
43void *puc_cardbus_intr_establish(struct puc_attach_args *, int,
44    int (*)(void *), void *, char *);
45
46const struct cfattach puc_cardbus_ca = {
47	sizeof(struct puc_cardbus_softc), puc_cardbus_match,
48	puc_cardbus_attach, puc_cardbus_detach
49};
50
51int
52puc_cardbus_match(struct device *parent, void *match, void *aux)
53{
54	struct cardbus_attach_args *ca = aux;
55	pci_chipset_tag_t pc = ca->ca_pc;
56	pcireg_t bhlc, reg;
57
58	bhlc = pci_conf_read(pc, ca->ca_tag, PCI_BHLC_REG);
59	if (PCI_HDRTYPE_TYPE(bhlc) != 0)
60		return(0);
61
62	/* this one is some sort of a bridge and not a puc */
63	if (PCI_VENDOR(ca->ca_id) == PCI_VENDOR_OXFORD2 &&
64	    PCI_PRODUCT(ca->ca_id) == PCI_PRODUCT_OXFORD2_EXSYS_EX41098)
65		return (0);
66
67	reg = pci_conf_read(pc, ca->ca_tag, PCI_SUBSYS_ID_REG);
68	if (puc_find_description(PCI_VENDOR(ca->ca_id),
69	    PCI_PRODUCT(ca->ca_id), PCI_VENDOR(reg), PCI_PRODUCT(reg)))
70		return (10);
71
72	return (0);
73}
74
75void
76puc_cardbus_attach(struct device *parent, struct device *self, void *aux)
77{
78	struct puc_cardbus_softc *csc = (struct puc_cardbus_softc *)self;
79	struct puc_softc *sc = &csc->sc_psc;
80	struct cardbus_attach_args *ca = aux;
81	struct cardbus_devfunc *ct = ca->ca_ct;
82	cardbus_chipset_tag_t cc = ct->ct_cc;
83	pci_chipset_tag_t pc = ca->ca_pc;
84	cardbus_function_tag_t cf = ct->ct_cf;
85	struct puc_attach_args paa;
86	pcireg_t reg;
87	int i;
88
89	Cardbus_function_enable(ct);
90
91	csc->ct = ct;
92
93	reg = pci_conf_read(pc, ca->ca_tag, PCI_SUBSYS_ID_REG);
94	sc->sc_desc = puc_find_description(PCI_VENDOR(ca->ca_id),
95	    PCI_PRODUCT(ca->ca_id), PCI_VENDOR(reg), PCI_PRODUCT(reg));
96
97	puc_print_ports(sc->sc_desc);
98
99	/* the fifth one is some memory we dunno */
100	for (i = 0; i < PUC_NBARS; i++) {
101		pcireg_t type;
102		int bar;
103
104		sc->sc_bar_mappings[i].mapped = 0;
105		bar = PCI_MAPREG_START + 4 * i;
106		if (!pci_mapreg_probe(pc, ca->ca_tag, bar, &type))
107			continue;
108
109		if (!(sc->sc_bar_mappings[i].mapped = !Cardbus_mapreg_map(ct,
110		    bar, type, 0,
111		    &sc->sc_bar_mappings[i].t, &sc->sc_bar_mappings[i].h,
112		    &sc->sc_bar_mappings[i].a, &sc->sc_bar_mappings[i].s)))
113			printf("%s: couldn't map BAR at offset 0x%lx\n",
114			    sc->sc_dev.dv_xname, (long)bar);
115		sc->sc_bar_mappings[i].type = type;
116	}
117
118	csc->intrline = ca->ca_intrline;
119
120	if (pci_get_capability(pc, ca->ca_tag, PCI_CAP_PWRMGMT, &reg,
121	    0)) {
122		reg = pci_conf_read(pc, ca->ca_tag, reg + 4) & 3;
123		if (reg) {
124			printf("%s: awakening from state D%d\n",
125			    sc->sc_dev.dv_xname, reg);
126			pci_conf_write(pc, ca->ca_tag, reg + 4, 0);
127		}
128	}
129
130	(*cf->cardbus_ctrl)(cc, CARDBUS_MEM_ENABLE);
131	(*cf->cardbus_ctrl)(cc, CARDBUS_IO_ENABLE);
132	(*cf->cardbus_ctrl)(cc, CARDBUS_BM_ENABLE);
133
134	paa.puc = sc;
135	paa.intr_string = &puc_cardbus_intr_string;
136	paa.intr_establish = &puc_cardbus_intr_establish;
137
138	puc_common_attach(sc, &paa);
139}
140
141const char *
142puc_cardbus_intr_string(struct puc_attach_args *paa)
143{
144	struct puc_cardbus_softc *sc = paa->puc;
145	static char str[16];
146
147	snprintf(str, sizeof str, "irq %d", sc->intrline);
148	return (str);
149}
150
151void *
152puc_cardbus_intr_establish(struct puc_attach_args *paa, int type,
153    int (*func)(void *), void *arg, char *name)
154{
155	struct puc_cardbus_softc *sc = paa->puc;
156	struct puc_softc *psc = &sc->sc_psc;
157	struct cardbus_devfunc *ct = sc->ct;
158
159	psc->sc_ports[paa->port].intrhand =
160	    cardbus_intr_establish(ct->ct_cc, ct->ct_cf, sc->intrline,
161		type, func, arg, name);
162
163	return (psc->sc_ports[paa->port].intrhand);
164}
165
166int
167puc_cardbus_detach(struct device *self, int flags)
168{
169	struct puc_cardbus_softc *sc = (struct puc_cardbus_softc *)self;
170	struct puc_softc *psc = &sc->sc_psc;
171	struct cardbus_devfunc *ct = sc->ct;
172	int i, rv;
173
174	for (i = PUC_MAX_PORTS; i--; ) {
175		if (psc->sc_ports[i].intrhand)
176			cardbus_intr_disestablish(ct->ct_cc, ct->ct_cf,
177			    psc->sc_ports[i].intrhand);
178		if (psc->sc_ports[i].dev)
179			if ((rv = config_detach(psc->sc_ports[i].dev, flags)))
180				return (rv);
181	}
182
183	for (i = PUC_NBARS; i--; )
184		if (psc->sc_bar_mappings[i].mapped)
185			Cardbus_mapreg_unmap(ct, psc->sc_bar_mappings[i].type,
186			    psc->sc_bar_mappings[i].t,
187			    psc->sc_bar_mappings[i].h,
188			    psc->sc_bar_mappings[i].s);
189
190	return (0);
191}
192