kernel: Use NULL for DRIVER_MODULE()'s evh & arg (which are pointers).
[dragonfly.git] / sys / platform / pc32 / i386 / k6_mem.c
1 /*-
2  * Copyright (c) 1999 Brian Fundakowski Feldman
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD: src/sys/i386/i386/k6_mem.c,v 1.16 2009/03/17 00:48:11 jkim
27  */
28
29 #include <sys/param.h>
30 #include <sys/kernel.h>
31 #include <sys/systm.h>
32 #include <sys/lock.h>
33 #include <sys/malloc.h>
34 #include <sys/memrange.h>
35
36 #include <machine/cputypes.h>
37 #include <machine/md_var.h>
38 #include <machine/specialreg.h>
39
40 /*
41  * A K6-2 MTRR is defined as the highest 15 bits having the address, the next
42  * 15 having the mask, the 1st bit being "write-combining" and the 0th bit
43  * being "uncacheable".
44  *
45  *      Address             Mask        WC  UC
46  * | XXXXXXXXXXXXXXX | XXXXXXXXXXXXXXX | X | X |
47  *
48  * There are two of these in the 64-bit UWCCR.
49  */
50
51 #define UWCCR 0xc0000085
52
53 #define K6_REG_GET(reg, addr, mask, wc, uc) do {                        \
54                 addr = (reg) & 0xfffe0000;                              \
55                 mask = ((reg) & 0x1fffc) >> 2;                          \
56                 wc = ((reg) & 0x2) >> 1;                                \
57                 uc = (reg) & 0x1;                                       \
58         } while (0)
59
60 #define K6_REG_MAKE(addr, mask, wc, uc)                                 \
61         ((addr) | ((mask) << 2) | ((wc) << 1) | uc)
62
63 static void     k6_mrinit(struct mem_range_softc *sc);
64 static int      k6_mrset(struct mem_range_softc *, struct mem_range_desc *,
65                     int *);
66 static __inline int k6_mrmake(struct mem_range_desc *, u_int32_t *);
67 static void     k6_mem_drvinit(void *);
68
69 static struct mem_range_ops k6_mrops =
70 {
71         k6_mrinit,
72         k6_mrset,
73         NULL,
74         NULL
75 };
76
77 static __inline int
78 k6_mrmake(struct mem_range_desc *desc, u_int32_t *mtrr)
79 {
80         u_int32_t len = 0, wc, uc;
81         register int bit;
82
83         if (desc->mr_base &~ 0xfffe0000)
84                 return (EINVAL);
85         if (desc->mr_len < 131072 || !powerof2(desc->mr_len))
86                 return (EINVAL);
87         if (desc->mr_flags &~ (MDF_WRITECOMBINE|MDF_UNCACHEABLE|MDF_FORCE))
88                 return (EOPNOTSUPP);
89
90         for (bit = ffs(desc->mr_len >> 17) - 1; bit < 15; bit++)
91                 len |= 1 << bit; 
92         wc = (desc->mr_flags & MDF_WRITECOMBINE) ? 1 : 0;
93         uc = (desc->mr_flags & MDF_UNCACHEABLE) ? 1 : 0;
94
95         *mtrr = K6_REG_MAKE(desc->mr_base, len, wc, uc);
96         return (0);
97 }
98
99 static void
100 k6_mrinit(struct mem_range_softc *sc)
101 {
102         u_int64_t reg;
103         u_int32_t addr, mask, wc, uc;
104         int d;
105
106         sc->mr_cap = 0;
107         sc->mr_ndesc = 2; /* XXX (BFF) For now, we only have one msr for this */
108         sc->mr_desc = kmalloc(sc->mr_ndesc * sizeof(struct mem_range_desc),
109             M_MEMDESC, M_NOWAIT | M_ZERO);
110         if (sc->mr_desc == NULL)
111                 panic("k6_mrinit: malloc returns NULL");
112
113         reg = rdmsr(UWCCR);
114         for (d = 0; d < sc->mr_ndesc; d++) {
115                 u_int32_t one = (reg & (0xffffffff << (32 * d))) >> (32 * d);
116
117                 K6_REG_GET(one, addr, mask, wc, uc);
118                 sc->mr_desc[d].mr_base = addr;
119                 sc->mr_desc[d].mr_len = ffs(mask) << 17;
120                 if (wc)
121                         sc->mr_desc[d].mr_flags |= MDF_WRITECOMBINE;
122                 if (uc)
123                         sc->mr_desc[d].mr_flags |= MDF_UNCACHEABLE;
124         }
125
126         kprintf("K6-family MTRR support enabled (%d registers)\n", sc->mr_ndesc);
127 }
128
129 static int
130 k6_mrset(struct mem_range_softc *sc, struct mem_range_desc *desc, int *arg)
131 {
132         u_int64_t reg;
133         u_int32_t mtrr;
134         int error, d;
135
136         switch (*arg) {
137         case MEMRANGE_SET_UPDATE:
138                 error = k6_mrmake(desc, &mtrr);
139                 if (error)
140                         return (error);
141                 for (d = 0; d < sc->mr_ndesc; d++) {
142                         if (!sc->mr_desc[d].mr_len) {
143                                 sc->mr_desc[d] = *desc;
144                                 goto out;
145                         }
146                         if (sc->mr_desc[d].mr_base == desc->mr_base &&
147                             sc->mr_desc[d].mr_len == desc->mr_len)
148                                 return (EEXIST);
149                 }
150                 return (ENOSPC);
151         case MEMRANGE_SET_REMOVE:
152                 mtrr = 0;
153                 for (d = 0; d < sc->mr_ndesc; d++)
154                         if (sc->mr_desc[d].mr_base == desc->mr_base &&
155                             sc->mr_desc[d].mr_len == desc->mr_len) {
156                                 bzero(&sc->mr_desc[d], sizeof(sc->mr_desc[d]));
157                                 goto out;
158                         }
159                 return (ENOENT);
160         default:
161                 return (EOPNOTSUPP);
162         }
163 out:
164         mpintr_lock();
165         wbinvd();
166         reg = rdmsr(UWCCR);
167         reg &= ~(0xffffffff << (32 * d));
168         reg |= mtrr << (32 * d);
169         wrmsr(UWCCR, reg);
170         wbinvd();
171         mpintr_unlock();
172
173         return (0);
174 }
175
176 static void
177 k6_mem_drvinit(void *unused)
178 {
179
180         if (cpu_vendor_id != CPU_VENDOR_AMD)
181                 return;
182         if ((cpu_id & 0xf00) != 0x500)
183                 return;
184         if ((cpu_id & 0xf0) < 0x80 ||
185             ((cpu_id & 0xf0) == 0x80 && (cpu_id & 0xf) <= 0x7))
186                 return;
187         mem_range_softc.mr_op = &k6_mrops;
188 }
189 SYSINIT(k6memdev, SI_SUB_DRIVERS, SI_ORDER_FIRST, k6_mem_drvinit, NULL);