1 | /* $NetBSD: slhci_pcmcia.c,v 1.10 2016/04/23 10:15:31 skrll Exp $ */ |
2 | /* |
3 | * Not (c) 2007 Matthew Orgass |
4 | * This file is public domain, meaning anyone can make any use of part or all |
5 | * of this file including copying into other works without credit. Any use, |
6 | * modified or not, is solely the responsibility of the user. If this file is |
7 | * part of a collection then use in the collection is governed by the terms of |
8 | * the collection. |
9 | */ |
10 | |
11 | /* Glue for RATOC USB HOST CF+ Card (SL811HS chip) */ |
12 | |
13 | #include <sys/cdefs.h> |
14 | __KERNEL_RCSID(0, "$NetBSD: slhci_pcmcia.c,v 1.10 2016/04/23 10:15:31 skrll Exp $" ); |
15 | |
16 | #include <sys/param.h> |
17 | #include <sys/device.h> |
18 | #include <sys/queue.h> |
19 | #include <sys/gcq.h> |
20 | #include <sys/systm.h> |
21 | #include <sys/errno.h> |
22 | |
23 | #include <sys/bus.h> |
24 | #include <dev/pcmcia/pcmciareg.h> |
25 | #include <dev/pcmcia/pcmciavar.h> |
26 | #include <dev/pcmcia/pcmciadevs.h> |
27 | |
28 | #include <dev/usb/usb.h> |
29 | #include <dev/usb/usbdi.h> |
30 | #include <dev/usb/usbdivar.h> |
31 | |
32 | #include <dev/ic/sl811hsvar.h> |
33 | |
34 | struct slhci_pcmcia_softc { |
35 | struct slhci_softc sc_slhci; |
36 | |
37 | struct pcmcia_function *sc_pf; |
38 | void * sc_ih; |
39 | int sc_flags; |
40 | #define PFL_ENABLED (0x1) |
41 | }; |
42 | |
43 | |
44 | int slhci_pcmcia_probe(device_t, cfdata_t, void *); |
45 | void slhci_pcmcia_attach(device_t, device_t, void *); |
46 | int slhci_pcmcia_detach(device_t, int); |
47 | int slhci_pcmcia_validate_config(struct pcmcia_config_entry *); |
48 | int slhci_pcmcia_enable(struct slhci_pcmcia_softc *, int); |
49 | |
50 | CFATTACH_DECL_NEW(slhci_pcmcia, sizeof(struct slhci_pcmcia_softc), |
51 | slhci_pcmcia_probe, slhci_pcmcia_attach, slhci_pcmcia_detach, |
52 | slhci_activate); |
53 | |
54 | /* Ratoc has two PCMCIA products with id 1. |
55 | * Ratoc has a firmware update that modifies the CIS. The new CIS may |
56 | * need a new entry. */ |
57 | static const struct pcmcia_product slhci_pcmcia_products[] = { |
58 | { PCMCIA_VENDOR_RATOC, PCMCIA_PRODUCT_RATOC_REX_CFU1, |
59 | PCMCIA_CIS_RATOC_REX_CFU1 }, |
60 | }; |
61 | static const size_t slhci_pcmcia_nproducts = |
62 | sizeof(slhci_pcmcia_products) / sizeof(slhci_pcmcia_products[0]); |
63 | |
64 | int |
65 | slhci_pcmcia_probe(device_t parent, cfdata_t match, void *aux) |
66 | { |
67 | struct pcmcia_attach_args *pa = aux; |
68 | |
69 | if (pcmcia_product_lookup(pa, slhci_pcmcia_products, |
70 | slhci_pcmcia_nproducts, sizeof(slhci_pcmcia_products[0]), NULL)) |
71 | return 1; |
72 | |
73 | return 0; |
74 | } |
75 | |
76 | int |
77 | slhci_pcmcia_validate_config(struct pcmcia_config_entry *cfe) |
78 | { |
79 | if (cfe->num_iospace != 1 || cfe->num_memspace != 0) |
80 | return EINVAL; |
81 | return 0; |
82 | } |
83 | |
84 | void |
85 | slhci_pcmcia_attach(device_t parent, device_t self, void *aux) |
86 | { |
87 | struct slhci_pcmcia_softc *psc = device_private(self); |
88 | struct pcmcia_attach_args *pa = aux; |
89 | struct pcmcia_function *pf = pa->pf; |
90 | |
91 | psc->sc_slhci.sc_dev = self; |
92 | psc->sc_slhci.sc_bus.ub_hcpriv = &psc->sc_slhci; |
93 | |
94 | psc->sc_pf = pf; |
95 | psc->sc_flags = 0; |
96 | |
97 | slhci_pcmcia_enable(psc, 1); |
98 | |
99 | return; |
100 | } |
101 | |
102 | int |
103 | slhci_pcmcia_detach(device_t self, int flags) |
104 | { |
105 | struct slhci_pcmcia_softc *psc = device_private(self); |
106 | |
107 | slhci_pcmcia_enable(psc, 0); |
108 | |
109 | return slhci_detach(&psc->sc_slhci, flags); |
110 | } |
111 | |
112 | int |
113 | slhci_pcmcia_enable(struct slhci_pcmcia_softc *psc, int enable) |
114 | { |
115 | struct pcmcia_function *pf; |
116 | struct pcmcia_io_handle *pioh; |
117 | struct slhci_softc *sc; |
118 | int error; |
119 | |
120 | pf = psc->sc_pf; |
121 | sc = &psc->sc_slhci; |
122 | |
123 | if (enable) { |
124 | if (psc->sc_flags & PFL_ENABLED) |
125 | return 0; |
126 | |
127 | error = pcmcia_function_configure(pf, |
128 | slhci_pcmcia_validate_config); |
129 | if (error) { |
130 | printf("%s: configure failed, error=%d\n" , |
131 | SC_NAME(sc), error); |
132 | return 1; |
133 | } |
134 | |
135 | pioh = &pf->cfe->iospace[0].handle; |
136 | |
137 | /* The data port is repeated three times; using a stride of |
138 | * 2 prevents read/write errors on a Clio C-1000 hpcmips |
139 | * system. |
140 | */ |
141 | slhci_preinit(sc, NULL, pioh->iot, pioh->ioh, 100, 2); |
142 | |
143 | psc->sc_ih = pcmcia_intr_establish(pf, IPL_USB, |
144 | slhci_intr, sc); |
145 | |
146 | if (psc->sc_ih == NULL) { |
147 | printf("%s: unable to establish interrupt\n" , |
148 | SC_NAME(sc)); |
149 | goto fail1; |
150 | } |
151 | |
152 | if (pcmcia_function_enable(pf)) { |
153 | printf("%s: function enable failed\n" , SC_NAME(sc)); |
154 | goto fail2; |
155 | } |
156 | |
157 | if (slhci_attach(sc)) { |
158 | printf("%s: slhci_attach failed\n" , SC_NAME(sc)); |
159 | goto fail3; |
160 | } |
161 | |
162 | psc->sc_flags |= PFL_ENABLED; |
163 | return 0; |
164 | } else { |
165 | if (!(psc->sc_flags & PFL_ENABLED)) |
166 | return 1; |
167 | psc->sc_flags &= ~PFL_ENABLED; |
168 | fail3: |
169 | pcmcia_function_disable(pf); |
170 | fail2: |
171 | pcmcia_intr_disestablish(psc->sc_pf, psc->sc_ih); |
172 | fail1: |
173 | pcmcia_function_unconfigure(pf); |
174 | |
175 | return 1; |
176 | } |
177 | } |
178 | |
179 | |
180 | |