]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - sys/powerpc/mpc85xx/lbc.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / sys / powerpc / mpc85xx / lbc.c
1 /*-
2  * Copyright (c) 2006-2008, Juniper Networks, Inc.
3  * Copyright (c) 2008 Semihalf, Rafal Czubak
4  * All rights reserved.
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  * 3. The name of the author may not be used to endorse or promote products
15  *    derived from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/ktr.h>
36 #include <sys/kernel.h>
37 #include <sys/malloc.h>
38 #include <sys/module.h>
39 #include <sys/bus.h>
40 #include <sys/rman.h>
41 #include <machine/bus.h>
42 #include <machine/ocpbus.h>
43
44 #include <vm/vm.h>
45 #include <vm/pmap.h>
46
47 #include <powerpc/mpc85xx/lbc.h>
48 #include <powerpc/mpc85xx/mpc85xx.h>
49 #include <powerpc/mpc85xx/ocpbus.h>
50
51 struct lbc_softc {
52         device_t                sc_dev;
53
54         struct resource         *sc_res;
55         bus_space_handle_t      sc_bsh;
56         bus_space_tag_t         sc_bst;
57         int                     sc_rid;
58
59         struct rman             sc_rman;
60         vm_offset_t             sc_kva[LBC_DEV_MAX];
61 };
62
63 struct lbc_devinfo {
64         int             lbc_devtype;
65         /* LBC child unit. It also represents resource table entry number */
66         int             lbc_unit;
67 };
68
69 /* Resources for MPC8555CDS system */
70 const struct lbc_resource mpc85xx_lbc_resources[] = {
71         /* Boot flash bank */
72         {
73                 LBC_DEVTYPE_CFI, 0, 0xff800000, 0x00800000, 16,
74                 LBCRES_MSEL_GPCM, LBCRES_DECC_DISABLED,
75                 LBCRES_ATOM_DISABLED, 0
76         },
77
78         /* Second flash bank */
79         {
80                 LBC_DEVTYPE_CFI, 1, 0xff000000, 0x00800000, 16,
81                 LBCRES_MSEL_GPCM, LBCRES_DECC_DISABLED,
82                 LBCRES_ATOM_DISABLED, 0
83         },
84
85         /* DS1553 RTC/NVRAM */
86         {
87                 LBC_DEVTYPE_RTC, 2, 0xf8000000, 0x8000, 8,
88                 LBCRES_MSEL_GPCM, LBCRES_DECC_DISABLED,
89                 LBCRES_ATOM_DISABLED, 0
90         },
91
92         {0}
93 };
94
95 static int lbc_probe(device_t);
96 static int lbc_attach(device_t);
97 static int lbc_shutdown(device_t);
98 static int lbc_get_resource(device_t, device_t, int, int, u_long *,
99     u_long *);
100 static struct resource *lbc_alloc_resource(device_t, device_t, int, int *,
101     u_long, u_long, u_long, u_int);
102 static int lbc_print_child(device_t, device_t);
103 static int lbc_release_resource(device_t, device_t, int, int,
104     struct resource *);
105 static int lbc_read_ivar(device_t, device_t, int, uintptr_t *);
106
107 /*
108  * Bus interface definition
109  */
110 static device_method_t lbc_methods[] = {
111         /* Device interface */
112         DEVMETHOD(device_probe,         lbc_probe),
113         DEVMETHOD(device_attach,        lbc_attach),
114         DEVMETHOD(device_shutdown,      lbc_shutdown),
115
116         /* Bus interface */
117         DEVMETHOD(bus_print_child,      lbc_print_child),
118         DEVMETHOD(bus_read_ivar,        lbc_read_ivar),
119         DEVMETHOD(bus_setup_intr,       bus_generic_setup_intr),
120         DEVMETHOD(bus_teardown_intr,    NULL),
121
122         DEVMETHOD(bus_get_resource,     NULL),
123         DEVMETHOD(bus_alloc_resource,   lbc_alloc_resource),
124         DEVMETHOD(bus_release_resource, lbc_release_resource),
125         DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
126         DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
127
128         { 0, 0 }
129 };
130
131 static driver_t lbc_driver = {
132         "lbc",
133         lbc_methods,
134         sizeof(struct lbc_softc)
135 };
136 devclass_t lbc_devclass;
137 DRIVER_MODULE(lbc, ocpbus, lbc_driver, lbc_devclass, 0, 0);
138
139 static __inline void
140 lbc_write_reg(struct lbc_softc *sc, bus_size_t off, uint32_t val)
141 {
142
143         bus_space_write_4(sc->sc_bst, sc->sc_bsh, off, val);
144 }
145
146 static __inline uint32_t
147 lbc_read_reg(struct lbc_softc *sc, bus_size_t off)
148 {
149
150         return (bus_space_read_4(sc->sc_bst, sc->sc_bsh, off));
151 }
152
153 /*
154  * Calculate address mask used by OR(n) registers. Use memory region size to
155  * determine mask value. The size must be a power of two and within the range
156  * of 32KB - 4GB. Otherwise error code is returned. Value representing
157  * 4GB size can be passed as 0xffffffff.
158  */
159 static uint32_t
160 lbc_address_mask(uint32_t size)
161 {
162         int n = 15;
163
164         if (size == ~0UL)
165                 return (0);
166
167         while (n < 32) {
168                 if (size == (1UL << n))
169                         break;
170                 n++;
171         }
172
173         if (n == 32)
174                 return (EINVAL);
175
176         return (0xffff8000 << (n - 15));
177 }
178
179 static device_t
180 lbc_mk_child(device_t dev, const struct lbc_resource *lbcres)
181 {
182         struct lbc_devinfo *dinfo;
183         device_t child;
184
185         if (lbcres->lbr_unit > LBC_DEV_MAX - 1)
186                 return (NULL);
187
188         child = device_add_child(dev, NULL, -1);
189         if (child == NULL) {
190                 device_printf(dev, "could not add LBC child device\n");
191                 return (NULL);
192         }
193         dinfo = malloc(sizeof(struct lbc_devinfo), M_DEVBUF, M_WAITOK | M_ZERO);
194         dinfo->lbc_devtype = lbcres->lbr_devtype;
195         dinfo->lbc_unit = lbcres->lbr_unit;
196         device_set_ivars(child, dinfo);
197         return (child);
198 }
199
200 static int
201 lbc_init_child(device_t dev, device_t child)
202 {
203         struct lbc_softc *sc;
204         struct lbc_devinfo *dinfo;
205         const struct lbc_resource *res;
206         u_long start, size;
207         uint32_t regbuff;
208         int error, unit;
209
210         sc = device_get_softc(dev);
211         dinfo = device_get_ivars(child);
212
213         res = mpc85xx_lbc_resources;
214
215         regbuff = 0;
216         unit = -1;
217         for (; res->lbr_devtype; res++) {
218                 if (res->lbr_unit != dinfo->lbc_unit)
219                         continue;
220
221                 start = res->lbr_base_addr;
222                 size = res->lbr_size;
223                 unit = res->lbr_unit;
224
225                 /*
226                  * Configure LAW for this LBC device and map its physical
227                  * memory region into KVA
228                  */
229                 error = law_enable(OCP85XX_TGTIF_LBC, start, size);
230                 if (error)
231                         return (error);
232
233                 sc->sc_kva[unit] = (vm_offset_t)pmap_mapdev(start, size);
234                 if (sc->sc_kva[unit] == 0) {
235                         law_disable(OCP85XX_TGTIF_LBC, start, size);
236                         return (ENOSPC);
237                 }
238
239                 /*
240                  * Compute and program BR value
241                  */
242                 regbuff |= start;
243
244                 switch (res->lbr_port_size) {
245                 case 8:
246                         regbuff |= (1 << 11);
247                         break;
248                 case 16:
249                         regbuff |= (2 << 11);
250                         break;
251                 case 32:
252                         regbuff |= (3 << 11);
253                         break;
254                 default:
255                         error = EINVAL;
256                         goto fail;
257                 }
258                 regbuff |= (res->lbr_decc << 9);
259                 regbuff |= (res->lbr_wp << 8);
260                 regbuff |= (res->lbr_msel << 5);
261                 regbuff |= (res->lbr_atom << 2);
262                 regbuff |= 1;
263
264                 lbc_write_reg(sc, LBC85XX_BR(unit), regbuff);
265
266                 /*
267                  * Compute and program OR value
268                  */
269                 regbuff = 0;
270                 regbuff |= lbc_address_mask(size);
271
272                 switch (res->lbr_msel) {
273                 case LBCRES_MSEL_GPCM:
274                         /* TODO Add flag support for option registers */
275                         regbuff |= 0x00000ff7;
276                         break;
277                 case LBCRES_MSEL_FCM:
278                         printf("FCM mode not supported yet!");
279                         error = ENOSYS;
280                         goto fail;
281                 case LBCRES_MSEL_UPMA:
282                 case LBCRES_MSEL_UPMB:
283                 case LBCRES_MSEL_UPMC:
284                         printf("UPM mode not supported yet!");
285                         error = ENOSYS;
286                         goto fail;
287                 }
288
289                 lbc_write_reg(sc, LBC85XX_OR(unit), regbuff);
290
291                 return (0);
292         }
293 fail:
294         if (unit != -1) {
295                 law_disable(OCP85XX_TGTIF_LBC, start, size);
296                 pmap_unmapdev(sc->sc_kva[unit], size);
297                 return (error);
298         } else
299                 return (ENOENT);
300 }
301
302 static int
303 lbc_probe(device_t dev)
304 {
305         device_t parent;
306         uintptr_t devtype;
307         int error;
308
309         parent = device_get_parent(dev);
310         error = BUS_READ_IVAR(parent, dev, OCPBUS_IVAR_DEVTYPE, &devtype);
311         if (error)
312                 return (error);
313         if (devtype != OCPBUS_DEVTYPE_LBC)
314                 return (ENXIO);
315
316         device_set_desc(dev, "Freescale MPC85xx Local Bus Controller");
317         return (BUS_PROBE_DEFAULT);
318 }
319
320 static int
321 lbc_attach(device_t dev)
322 {
323         struct lbc_softc *sc;
324         struct rman *rm;
325         const struct lbc_resource *lbcres;
326         int error;
327
328         sc = device_get_softc(dev);
329         sc->sc_dev = dev;
330
331         sc->sc_rid = 0;
332         sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid,
333             RF_ACTIVE);
334         if (sc->sc_res == NULL)
335                 return (ENXIO);
336
337         sc->sc_bst = rman_get_bustag(sc->sc_res);
338         sc->sc_bsh = rman_get_bushandle(sc->sc_res);
339
340         rm = &sc->sc_rman;
341         rm->rm_type = RMAN_ARRAY;
342         rm->rm_descr = "MPC85XX Local Bus Space";
343         rm->rm_start = 0UL;
344         rm->rm_end = ~0UL;
345         error = rman_init(rm);
346         if (error)
347                 goto fail;
348
349         error = rman_manage_region(rm, rm->rm_start, rm->rm_end);
350         if (error) {
351                 rman_fini(rm);
352                 goto fail;
353         }
354
355         /*
356          * Initialize configuration register:
357          * - enable Local Bus
358          * - set data buffer control signal function
359          * - disable parity byte select
360          * - set ECC parity type
361          * - set bus monitor timing and timer prescale
362          */
363         lbc_write_reg(sc, LBC85XX_LBCR, 0x00000000);
364
365         /*
366          * Initialize clock ratio register:
367          * - disable PLL bypass mode
368          * - configure LCLK delay cycles for the assertion of LALE
369          * - set system clock divider
370          */
371         lbc_write_reg(sc, LBC85XX_LCRR, 0x00030008);
372
373         lbcres = mpc85xx_lbc_resources;
374
375         for (; lbcres->lbr_devtype; lbcres++)
376                 if (!lbc_mk_child(dev, lbcres)) {
377                         error = ENXIO;
378                         goto fail;
379                 }
380
381         return (bus_generic_attach(dev));
382
383 fail:
384         bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rid, sc->sc_res);
385         return (error);
386 }
387
388 static int
389 lbc_shutdown(device_t dev)
390 {
391
392         /* TODO */
393         return(0);
394 }
395
396 static struct resource *
397 lbc_alloc_resource(device_t dev, device_t child, int type, int *rid,
398     u_long start, u_long end, u_long count, u_int flags)
399 {
400         struct lbc_softc *sc;
401         struct lbc_devinfo *dinfo;
402         struct resource *rv;
403         struct rman *rm;
404         int error;
405
406         sc = device_get_softc(dev);
407         dinfo = device_get_ivars(child);
408
409         if (type != SYS_RES_MEMORY && type != SYS_RES_IRQ)
410                 return (NULL);
411
412         /* We only support default allocations. */
413         if (start != 0ul || end != ~0ul)
414                 return (NULL);
415
416         if (type == SYS_RES_IRQ)
417                 return (bus_alloc_resource(dev, type, rid, start, end, count,
418                     flags));
419
420         if (!sc->sc_kva[dinfo->lbc_unit]) {
421                 error = lbc_init_child(dev, child);
422                 if (error)
423                         return (NULL);
424         }
425
426         error = lbc_get_resource(dev, child, type, *rid, &start, &count);
427         if (error)
428                 return (NULL);
429
430         rm = &sc->sc_rman;
431         end = start + count - 1;
432         rv = rman_reserve_resource(rm, start, end, count, flags, child);
433         if (rv != NULL) {
434                 rman_set_bustag(rv, &bs_be_tag);
435                 rman_set_bushandle(rv, rman_get_start(rv));
436         }
437         return (rv);
438 }
439
440 static int
441 lbc_print_child(device_t dev, device_t child)
442 {
443         u_long size, start;
444         int error, retval, rid;
445
446         retval = bus_print_child_header(dev, child);
447
448         rid = 0;
449         while (1) {
450                 error = lbc_get_resource(dev, child, SYS_RES_MEMORY, rid,
451                     &start, &size);
452                 if (error)
453                         break;
454                 retval += (rid == 0) ? printf(" iomem ") : printf(",");
455                 retval += printf("%#lx", start);
456                 if (size > 1)
457                         retval += printf("-%#lx", start + size - 1);
458                 rid++;
459         }
460
461         retval += bus_print_child_footer(dev, child);
462         return (retval);
463 }
464
465 static int
466 lbc_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
467 {
468         struct lbc_devinfo *dinfo;
469
470         if (device_get_parent(child) != dev)
471                 return (EINVAL);
472
473         dinfo = device_get_ivars(child);
474
475         switch (index) {
476         case LBC_IVAR_DEVTYPE:
477                 *result = dinfo->lbc_devtype;
478                 return (0);
479         default:
480                 break;
481         }
482         return (EINVAL);
483 }
484
485 static int
486 lbc_release_resource(device_t dev, device_t child, int type, int rid,
487     struct resource *res)
488 {
489
490         return (rman_release_resource(res));
491 }
492
493 static int
494 lbc_get_resource(device_t dev, device_t child, int type, int rid,
495     u_long *startp, u_long *countp)
496 {
497         struct lbc_softc *sc;
498         struct lbc_devinfo *dinfo;
499         const struct lbc_resource *lbcres;
500
501         if (type != SYS_RES_MEMORY)
502                 return (ENOENT);
503
504         /* Currently all LBC devices have a single RID per type. */
505         if (rid != 0)
506                 return (ENOENT);
507
508         sc = device_get_softc(dev);
509         dinfo = device_get_ivars(child);
510
511         if ((dinfo->lbc_unit < 0) || (dinfo->lbc_unit > (LBC_DEV_MAX - 1)))
512                 return (EINVAL);
513
514         lbcres = mpc85xx_lbc_resources;
515
516         switch (dinfo->lbc_devtype) {
517         case LBC_DEVTYPE_CFI:
518         case LBC_DEVTYPE_RTC:
519                 for (; lbcres->lbr_devtype; lbcres++) {
520                         if (dinfo->lbc_unit == lbcres->lbr_unit) {
521                                 *startp = sc->sc_kva[lbcres->lbr_unit];
522                                 *countp = lbcres->lbr_size;
523                                 return (0);
524                         }
525                 }
526         default:
527                 return (EDOOFUS);
528         }
529         return (0);
530 }