xref: /openbsd/sys/dev/cardbus/if_re_cardbus.c (revision 891d7ab6)
1 /*	$OpenBSD: if_re_cardbus.c,v 1.22 2011/04/02 17:47:04 jasper Exp $	*/
2 
3 /*
4  * Copyright (c) 2005 Peter Valchev <pvalchev@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 /*
20  * Cardbus front-end for the Realtek 8169
21  */
22 
23 #include <sys/param.h>
24 #include <sys/endian.h>
25 #include <sys/systm.h>
26 #include <sys/sockio.h>
27 #include <sys/mbuf.h>
28 #include <sys/malloc.h>
29 #include <sys/kernel.h>
30 #include <sys/device.h>
31 #include <sys/timeout.h>
32 #include <sys/socket.h>
33 
34 #include <net/if.h>
35 #include <net/if_dl.h>
36 #include <net/if_media.h>
37 
38 #ifdef INET
39 #include <netinet/in.h>
40 #include <netinet/in_systm.h>
41 #include <netinet/in_var.h>
42 #include <netinet/ip.h>
43 #include <netinet/if_ether.h>
44 #endif
45 
46 #include <dev/mii/mii.h>
47 #include <dev/mii/miivar.h>
48 
49 #include <dev/pci/pcidevs.h>
50 
51 #include <dev/cardbus/cardbusvar.h>
52 
53 #include <dev/ic/rtl81x9reg.h>
54 #include <dev/ic/revar.h>
55 
56 struct re_cardbus_softc {
57 	/* General */
58 	struct rl_softc sc_rl;
59 
60 	/* Cardbus-specific data */
61 	void *sc_ih;
62 	cardbus_devfunc_t ct;
63 	pcitag_t sc_tag;
64 	pci_chipset_tag_t sc_pc;
65 	int sc_csr;
66 	int sc_cben;
67 	int sc_bar_reg;
68 	pcireg_t sc_bar_val;
69 	int sc_intrline;
70 
71 	bus_size_t sc_mapsize;
72 };
73 
74 int	re_cardbus_probe(struct device *, void *, void *);
75 void	re_cardbus_attach(struct device *, struct device *, void *);
76 int	re_cardbus_detach(struct device *, int);
77 void	re_cardbus_setup(struct rl_softc *);
78 
79 /*
80  * Cardbus autoconfig definitions
81  */
82 struct cfattach re_cardbus_ca = {
83 	sizeof(struct re_cardbus_softc),
84 	re_cardbus_probe,
85 	re_cardbus_attach,
86 	re_cardbus_detach
87 };
88 
89 const struct pci_matchid re_cardbus_devices[] = {
90 	{ PCI_VENDOR_REALTEK, PCI_PRODUCT_REALTEK_RT8169 },
91 };
92 
93 /*
94  * Probe for a RealTek 8169/8110 chip. Check the PCI vendor and device
95  * IDs against our list and return a device name if we find a match.
96  */
97 int
98 re_cardbus_probe(struct device *parent, void *match, void *aux)
99 {
100 	return (cardbus_matchbyid((struct cardbus_attach_args *)aux,
101 	    re_cardbus_devices, nitems(re_cardbus_devices)));
102 }
103 
104 /*
105  * Attach the interface. Allocate softc structures, do ifmedia
106  * setup and ethernet/BPF attach.
107  */
108 void
109 re_cardbus_attach(struct device *parent, struct device *self, void *aux)
110 {
111 	struct re_cardbus_softc	*csc = (struct re_cardbus_softc *)self;
112 	struct rl_softc		*sc = &csc->sc_rl;
113 	struct cardbus_attach_args *ca = aux;
114 	struct cardbus_softc *psc =
115 	    (struct cardbus_softc *)sc->sc_dev.dv_parent;
116 	cardbus_chipset_tag_t cc = psc->sc_cc;
117 	cardbus_function_tag_t cf = psc->sc_cf;
118 	cardbus_devfunc_t ct = ca->ca_ct;
119 	bus_addr_t adr;
120 	char intrstr[16];
121 
122 	sc->sc_dmat = ca->ca_dmat;
123 	csc->ct = ct;
124 	csc->sc_tag = ca->ca_tag;
125 	csc->sc_pc = ca->ca_pc;
126 	csc->sc_intrline = ca->ca_intrline;
127 
128 	/*
129 	 * Map control/status registers.
130 	 */
131 	if (Cardbus_mapreg_map(ct, RL_PCI_LOMEM, PCI_MAPREG_TYPE_MEM, 0,
132 	    &sc->rl_btag, &sc->rl_bhandle, &adr, &csc->sc_mapsize) == 0) {
133 		csc->sc_cben = CARDBUS_MEM_ENABLE;
134 		csc->sc_csr |= PCI_COMMAND_MEM_ENABLE;
135 		csc->sc_bar_reg = RL_PCI_LOMEM;
136 		csc->sc_bar_val = adr | PCI_MAPREG_TYPE_MEM;
137 	} else {
138 		printf(": can't map mem space\n");
139 		return;
140 	}
141 
142 	/* Enable power */
143 	Cardbus_function_enable(ct);
144 
145 	/* Get chip out of powersave mode (if applicable), initialize
146 	 * config registers */
147 	re_cardbus_setup(sc);
148 
149 	/* Allocate interrupt */
150 	csc->sc_ih = cardbus_intr_establish(cc, cf, csc->sc_intrline,
151 	    IPL_NET, re_intr, sc, sc->sc_dev.dv_xname);
152 	if (csc->sc_ih == NULL) {
153 		printf(": couldn't establish interrupt at %d",
154 		    ca->ca_intrline);
155 		Cardbus_function_disable(csc->ct);
156 		return;
157 	}
158 	snprintf(intrstr, sizeof(intrstr), "irq %d", ca->ca_intrline);
159 
160 	/* Call bus-independent (common) attach routine */
161 	if (re_attach(sc, intrstr)) {
162 		cardbus_intr_disestablish(ct->ct_cc, ct->ct_cf, csc->sc_ih);
163 		Cardbus_mapreg_unmap(ct, csc->sc_bar_reg, sc->rl_btag,
164 		    sc->rl_bhandle, csc->sc_mapsize);
165 	}
166 }
167 
168 /*
169  * Get chip out of power-saving mode, init registers
170  */
171 void
172 re_cardbus_setup(struct rl_softc *sc)
173 {
174 	struct re_cardbus_softc *csc = (struct re_cardbus_softc *)sc;
175 	cardbus_devfunc_t ct = csc->ct;
176 	cardbus_chipset_tag_t cc = ct->ct_cc;
177 	pci_chipset_tag_t pc = csc->sc_pc;
178 	pcireg_t reg, command;
179 	int pmreg;
180 
181 	/* Handle power management nonsense */
182 	if (pci_get_capability(pc, csc->sc_tag,
183 	    PCI_CAP_PWRMGMT, &pmreg, 0)) {
184 		command = pci_conf_read(pc, csc->sc_tag,
185 		    pmreg + PCI_PMCSR);
186 
187 		if (command & RL_PSTATE_MASK) {
188 			pcireg_t iobase, membase, irq;
189 
190 			/* Save important PCI config data */
191 			iobase = pci_conf_read(pc, csc->sc_tag, RL_PCI_LOIO);
192 			membase = pci_conf_read(pc, csc->sc_tag, RL_PCI_LOMEM);
193 			irq = pci_conf_read(pc, csc->sc_tag, RL_PCI_INTLINE);
194 
195 			/* Reset the power state */
196 			printf("%s: chip is in D%d power mode "
197 			    "-- setting to D0\n", sc->sc_dev.dv_xname,
198 			    command & RL_PSTATE_MASK);
199 			command &= RL_PSTATE_MASK;
200 			pci_conf_write(pc, csc->sc_tag, pmreg + PCI_PMCSR,
201 			    command);
202 
203 			/* Restore PCI config data */
204 			pci_conf_write(pc, csc->sc_tag, RL_PCI_LOIO, iobase);
205 			pci_conf_write(pc, csc->sc_tag, RL_PCI_LOMEM, membase);
206 			pci_conf_write(pc, csc->sc_tag, RL_PCI_INTLINE, irq);
207 		}
208 	}
209 
210 	/* Make sure the right access type is on the Cardbus bridge */
211 	(*ct->ct_cf->cardbus_ctrl)(cc, csc->sc_cben);
212 	(*ct->ct_cf->cardbus_ctrl)(cc, CARDBUS_BM_ENABLE);
213 
214 	/* Program the BAR */
215 	pci_conf_write(pc, csc->sc_tag, csc->sc_bar_reg, csc->sc_bar_val);
216 
217 	/* Enable proper bits in CARDBUS CSR */
218 	reg = pci_conf_read(pc, csc->sc_tag, PCI_COMMAND_STATUS_REG);
219 	reg &= ~(PCI_COMMAND_IO_ENABLE|PCI_COMMAND_MEM_ENABLE);
220 	reg |= csc->sc_csr;
221 	pci_conf_write(pc, csc->sc_tag, PCI_COMMAND_STATUS_REG, reg);
222 
223 	/* Make sure the latency timer is set to some reasonable value */
224 	reg = pci_conf_read(pc, csc->sc_tag, PCI_BHLC_REG);
225 	if (PCI_LATTIMER(reg) < 0x20) {
226 		reg &= ~(PCI_LATTIMER_MASK << PCI_LATTIMER_SHIFT);
227 		reg |= (0x20 << PCI_LATTIMER_SHIFT);
228 		pci_conf_write(pc, csc->sc_tag, PCI_BHLC_REG, reg);
229 	}
230 }
231 
232 /*
233  * Cardbus detach function: deallocate all resources
234  */
235 int
236 re_cardbus_detach(struct device *self, int flags)
237 {
238 	struct re_cardbus_softc *csc = (void *)self;
239 	struct rl_softc *sc = &csc->sc_rl;
240 	struct cardbus_devfunc *ct = csc->ct;
241 	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
242 
243 	/* Remove timeout handler */
244 	timeout_del(&sc->timer_handle);
245 
246 	/* Detach PHY */
247 	if (LIST_FIRST(&sc->sc_mii.mii_phys) != NULL)
248 		mii_detach(&sc->sc_mii, MII_PHY_ANY, MII_OFFSET_ANY);
249 
250 	/* Delete media stuff */
251 	ifmedia_delete_instance(&sc->sc_mii.mii_media, IFM_INST_ANY);
252 	ether_ifdetach(ifp);
253 	if_detach(ifp);
254 
255 	/* Disable interrupts */
256 	if (csc->sc_ih != NULL)
257 		cardbus_intr_disestablish(ct->ct_cc, ct->ct_cf, csc->sc_ih);
258 
259 	/* Free cardbus resources */
260 	Cardbus_mapreg_unmap(ct, csc->sc_bar_reg, sc->rl_btag, sc->rl_bhandle,
261 	    csc->sc_mapsize);
262 
263 	return (0);
264 }
265