]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/pci/pci_dw_mv.c
Update Subversion to 1.14.0 LTS. See contrib/subversion/CHANGES for a
[FreeBSD/FreeBSD.git] / sys / dev / pci / pci_dw_mv.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2019 Michal Meloun <mmel@FreeBSD.org>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  */
28
29 /* Armada 8k DesignWare PCIe driver */
30
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33
34
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/bus.h>
38 #include <sys/devmap.h>
39 #include <sys/proc.h>
40 #include <sys/kernel.h>
41 #include <sys/malloc.h>
42 #include <sys/module.h>
43 #include <sys/mutex.h>
44 #include <sys/rman.h>
45 #include <sys/sysctl.h>
46
47 #include <machine/bus.h>
48 #include <machine/intr.h>
49 #include <machine/resource.h>
50
51 #include <dev/extres/clk/clk.h>
52 #include <dev/extres/phy/phy.h>
53 #include <dev/ofw/ofw_bus.h>
54 #include <dev/ofw/ofw_bus_subr.h>
55 #include <dev/ofw/ofw_pci.h>
56 #include <dev/ofw/ofwpci.h>
57 #include <dev/pci/pcivar.h>
58 #include <dev/pci/pcireg.h>
59 #include <dev/pci/pcib_private.h>
60 #include <dev/pci/pci_dw.h>
61
62 #include "pcib_if.h"
63 #include "pci_dw_if.h"
64
65 #define MV_GLOBAL_CONTROL_REG           0x8000
66 #define PCIE_APP_LTSSM_EN               (1 << 2)
67 //#define PCIE_DEVICE_TYPE_SHIFT                4
68 //#define PCIE_DEVICE_TYPE_MASK         0xF
69 //#define PCIE_DEVICE_TYPE_RC           0x4/
70
71 #define MV_GLOBAL_STATUS_REG            0x8008
72 #define  MV_STATUS_RDLH_LINK_UP                 (1 << 1)
73 #define  MV_STATUS_PHY_LINK_UP                  (1 << 9)
74
75
76 #define MV_INT_CAUSE1                   0x801C
77 #define MV_INT_MASK1                    0x8020
78 #define  INT_A_ASSERT_MASK                      (1 <<  9)
79 #define  INT_B_ASSERT_MASK                      (1 << 10)
80 #define  INT_C_ASSERT_MASK                      (1 << 11)
81 #define  INT_D_ASSERT_MASK                      (1 << 12)
82
83 #define MV_INT_CAUSE2                   0x8024
84 #define MV_INT_MASK2                    0x8028
85 #define MV_ERR_INT_CAUSE                0x802C
86 #define MV_ERR_INT_MASK                 0x8030
87
88 #define MV_ARCACHE_TRC_REG              0x8050
89 #define MV_AWCACHE_TRC_REG              0x8054
90 #define MV_ARUSER_REG                   0x805C
91 #define MV_AWUSER_REG                   0x8060
92
93
94
95 #define MV_MAX_LANES    8
96
97
98 struct pci_mv_softc {
99         struct pci_dw_softc     dw_sc;
100         device_t                dev;
101         phandle_t               node;
102         struct resource         *irq_res;
103         void                    *intr_cookie;
104         phy_t                   phy[MV_MAX_LANES];
105         clk_t                   clk_core;
106         clk_t                   clk_reg;
107 };
108
109 /* Compatible devices. */
110 static struct ofw_compat_data compat_data[] = {
111         {"marvell,armada8k-pcie", 1},
112         {NULL,                    0},
113 };
114
115
116 static int
117 pci_mv_phy_init(struct pci_mv_softc *sc)
118 {
119         int i, rv;
120
121         for (i = 0; i < MV_MAX_LANES; i++) {
122                 rv =  phy_get_by_ofw_idx(sc->dev, sc->node, i, &(sc->phy[i]));
123                 if (rv != 0 && rv != ENOENT) {
124                         device_printf(sc->dev, "Cannot get phy[%d]\n", i);
125                         goto fail;
126                 }
127                 if (sc->phy[i] == NULL)
128                         continue;
129                 rv = phy_enable(sc->phy[i]);
130                 if (rv != 0) {
131                         device_printf(sc->dev, "Cannot enable phy[%d]\n", i);
132                         goto fail;
133                 }
134           }
135           return (0);
136
137 fail:
138         for (i = 0; i < MV_MAX_LANES; i++) {
139                 if (sc->phy[i] == NULL)
140                         continue;
141                 phy_release(sc->phy[i]);
142           }
143
144         return (rv);
145 }
146
147 static void
148 pci_mv_init(struct pci_mv_softc *sc)
149 {
150         uint32_t reg;
151
152
153         /* Set device configuration to RC */
154         reg = pci_dw_dbi_rd4(sc->dev, MV_GLOBAL_CONTROL_REG);
155         reg &= ~0x000000F0;
156         reg |= 0x000000040;
157         pci_dw_dbi_wr4(sc->dev, MV_GLOBAL_CONTROL_REG, reg);
158
159         /* AxCache master transaction attribures */
160         pci_dw_dbi_wr4(sc->dev, MV_ARCACHE_TRC_REG, 0x3511);
161         pci_dw_dbi_wr4(sc->dev, MV_AWCACHE_TRC_REG, 0x5311);
162
163         /* AxDomain master transaction attribures */
164         pci_dw_dbi_wr4(sc->dev, MV_ARUSER_REG, 0x0002);
165         pci_dw_dbi_wr4(sc->dev, MV_AWUSER_REG, 0x0002);
166
167         /* Enable all INTx interrupt (virtuual) pins */
168         reg = pci_dw_dbi_rd4(sc->dev, MV_INT_MASK1);
169         reg |= INT_A_ASSERT_MASK | INT_B_ASSERT_MASK |
170                INT_C_ASSERT_MASK | INT_D_ASSERT_MASK;
171         pci_dw_dbi_wr4(sc->dev, MV_INT_MASK1, reg);
172
173         /* Enable local interrupts */
174         pci_dw_dbi_wr4(sc->dev, DW_MSI_INTR0_MASK, 0xFFFFFFFF);
175         pci_dw_dbi_wr4(sc->dev, MV_INT_MASK1, 0xFFFFFFFF);
176         pci_dw_dbi_wr4(sc->dev, MV_INT_MASK2, 0xFFFFFFFF);
177         pci_dw_dbi_wr4(sc->dev, MV_INT_CAUSE1, 0xFFFFFFFF);
178         pci_dw_dbi_wr4(sc->dev, MV_INT_CAUSE2, 0xFFFFFFFF);
179
180         /* Errors have own interrupt, not yet populated in DTt */
181         pci_dw_dbi_wr4(sc->dev, MV_ERR_INT_MASK, 0);
182 }
183 static int pci_mv_intr(void *arg)
184 {
185         struct pci_mv_softc *sc = arg;
186         uint32_t cause1, cause2;
187
188         /* Ack all interrups */
189         cause1 = pci_dw_dbi_rd4(sc->dev, MV_INT_CAUSE1);
190         cause2 = pci_dw_dbi_rd4(sc->dev, MV_INT_CAUSE2);
191         if (cause1 == 0 || cause2 == 0)
192                 return(FILTER_STRAY);
193
194         pci_dw_dbi_wr4(sc->dev, MV_INT_CAUSE1, cause1);
195         pci_dw_dbi_wr4(sc->dev, MV_INT_CAUSE2, cause2);
196         return (FILTER_HANDLED);
197 }
198
199 static int
200 pci_mv_get_link(device_t dev, bool *status)
201 {
202         uint32_t reg;
203
204         reg = pci_dw_dbi_rd4(dev, MV_GLOBAL_STATUS_REG);
205         if ((reg & (MV_STATUS_RDLH_LINK_UP | MV_STATUS_PHY_LINK_UP)) ==
206             (MV_STATUS_RDLH_LINK_UP | MV_STATUS_PHY_LINK_UP))
207                 *status = true;
208         else
209                 *status = false;
210
211         return (0);
212 }
213
214 static int
215 pci_mv_probe(device_t dev)
216 {
217
218         if (!ofw_bus_status_okay(dev))
219                 return (ENXIO);
220
221         if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
222                 return (ENXIO);
223
224         device_set_desc(dev, "Marvell Armada8K PCI-E Controller");
225         return (BUS_PROBE_DEFAULT);
226 }
227
228 static int
229 pci_mv_attach(device_t dev)
230 {
231         struct pci_mv_softc *sc;
232         phandle_t node;
233         int rv;
234         int rid;
235
236         sc = device_get_softc(dev);
237         node = ofw_bus_get_node(dev);
238         sc->dev = dev;
239         sc->node = node;
240         
241         rid = 0;
242         sc->dw_sc.dbi_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
243             RF_ACTIVE);
244         if (sc->dw_sc.dbi_res == NULL) {
245                 device_printf(dev, "Cannot allocate DBI memory\n");
246                 rv = ENXIO;
247                 goto out;
248         }
249
250         /* PCI interrupt */
251         rid = 0;
252         sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
253             RF_ACTIVE | RF_SHAREABLE);
254         if (sc->irq_res == NULL) {
255                 device_printf(dev, "Cannot allocate IRQ resources\n");
256                 rv = ENXIO;
257                 goto out;
258         }
259
260         /* Clocks */
261         rv = clk_get_by_ofw_name(sc->dev, 0, "core", &sc->clk_core);
262         if (rv != 0) {
263                 device_printf(sc->dev, "Cannot get 'core' clock\n");
264                 rv = ENXIO;
265                 goto out;
266         }
267
268         rv = clk_get_by_ofw_name(sc->dev, 0, "reg", &sc->clk_reg);
269         if (rv != 0) {
270                 device_printf(sc->dev, "Cannot get 'reg' clock\n");
271                 rv = ENXIO;
272                 goto out;
273         }
274
275
276         rv = clk_enable(sc->clk_core);
277         if (rv != 0) {
278                 device_printf(sc->dev, "Cannot enable 'core' clock\n");
279                 rv = ENXIO;
280                 goto out;
281         }
282
283         rv = clk_enable(sc->clk_reg);
284         if (rv != 0) {
285                 device_printf(sc->dev, "Cannot enable 'reg' clock\n");
286                 rv = ENXIO;
287                 goto out;
288         }
289
290         rv = pci_mv_phy_init(sc);
291         if (rv)
292                 goto out;
293
294         rv = pci_dw_init(dev);
295         if (rv != 0)
296                 goto out;
297
298         pci_mv_init(sc);
299
300         /* Setup interrupt  */
301         if (bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
302                     pci_mv_intr, NULL, sc, &sc->intr_cookie)) {
303                 device_printf(dev, "cannot setup interrupt handler\n");
304                 rv = ENXIO;
305                 goto out;
306         }
307
308         return (bus_generic_attach(dev));
309 out:
310         /* XXX Cleanup */
311         return (rv);
312 }
313
314 static device_method_t pci_mv_methods[] = {
315         /* Device interface */
316         DEVMETHOD(device_probe,                 pci_mv_probe),
317         DEVMETHOD(device_attach,                pci_mv_attach),
318
319         DEVMETHOD(pci_dw_get_link,              pci_mv_get_link),
320
321         DEVMETHOD_END
322 };
323
324 DEFINE_CLASS_1(pcib, pci_mv_driver, pci_mv_methods,
325     sizeof(struct pci_mv_softc), pci_dw_driver);
326 static devclass_t pci_mv_devclass;
327 DRIVER_MODULE( pci_mv, simplebus, pci_mv_driver, pci_mv_devclass,
328     NULL, NULL);