~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/sparc64/kernel/chmc.c

Version: ~ [ linux-5.15-rc7 ] ~ [ linux-5.14.14 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.75 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.155 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.213 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.252 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.287 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.289 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.18.140 ] ~ [ linux-3.16.85 ] ~ [ linux-3.14.79 ] ~ [ linux-3.12.74 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /* $Id: chmc.c,v 1.3 2001/04/03 12:49:47 davem Exp $
  2  * memctrlr.c: Driver for UltraSPARC-III memory controller.
  3  *
  4  * Copyright (C) 2001 David S. Miller (davem@redhat.com)
  5  */
  6 
  7 #include <linux/module.h>
  8 #include <linux/kernel.h>
  9 #include <linux/types.h>
 10 #include <linux/slab.h>
 11 #include <linux/list.h>
 12 #include <linux/init.h>
 13 #include <asm/spitfire.h>
 14 #include <asm/chmctrl.h>
 15 #include <asm/oplib.h>
 16 #include <asm/io.h>
 17 
 18 #define CHMCTRL_NDGRPS  2
 19 #define CHMCTRL_NDIMMS  4
 20 
 21 #define DIMMS_PER_MC    (CHMCTRL_NDGRPS * CHMCTRL_NDIMMS)
 22 
 23 /* OBP memory-layout property format. */
 24 struct obp_map {
 25         unsigned char   dimm_map[144];
 26         unsigned char   pin_map[576];
 27 };
 28 
 29 #define DIMM_LABEL_SZ   8
 30 
 31 struct obp_mem_layout {
 32         /* One max 8-byte string label per DIMM.  Usually
 33          * this matches the label on the motherboard where
 34          * that DIMM resides.
 35          */
 36         char            dimm_labels[DIMMS_PER_MC][DIMM_LABEL_SZ];
 37 
 38         /* If symmetric use map[0], else it is
 39          * asymmetric and map[1] should be used.
 40          */
 41         char            symmetric;
 42 
 43         struct obp_map  map[2];
 44 };
 45 
 46 #define CHMCTRL_NBANKS  4
 47 
 48 struct bank_info {
 49         struct mctrl_info       *mp;
 50         int                     bank_id;
 51 
 52         u64                     raw_reg;
 53         int                     valid;
 54         int                     uk;
 55         int                     um;
 56         int                     lk;
 57         int                     lm;
 58         int                     interleave;
 59         unsigned long           base;
 60         unsigned long           size;
 61 };
 62 
 63 struct mctrl_info {
 64         struct list_head        list;
 65         int                     portid;
 66         int                     index;
 67 
 68         struct obp_mem_layout   layout_prop;
 69         int                     layout_size;
 70 
 71         void                    *regs;
 72 
 73         u64                     timing_control1;
 74         u64                     timing_control2;
 75         u64                     timing_control3;
 76         u64                     timing_control4;
 77         u64                     memaddr_control;
 78 
 79         struct bank_info        logical_banks[CHMCTRL_NBANKS];
 80 };
 81 
 82 static LIST_HEAD(mctrl_list);
 83 
 84 /* Does BANK decode PHYS_ADDR? */
 85 static int bank_match(struct bank_info *bp, unsigned long phys_addr)
 86 {
 87         unsigned long upper_bits = (phys_addr & PA_UPPER_BITS) >> PA_UPPER_BITS_SHIFT;
 88         unsigned long lower_bits = (phys_addr & PA_LOWER_BITS) >> PA_LOWER_BITS_SHIFT;
 89 
 90         /* Bank must be enabled to match. */
 91         if (bp->valid == 0)
 92                 return 0;
 93 
 94         /* Would BANK match upper bits? */
 95         upper_bits ^= bp->um;           /* What bits are different? */
 96         upper_bits  = ~upper_bits;      /* Invert. */
 97         upper_bits |= bp->uk;           /* What bits don't matter for matching? */
 98         upper_bits  = ~upper_bits;      /* Invert. */
 99 
100         if (upper_bits)
101                 return 0;
102 
103         /* Would BANK match lower bits? */
104         lower_bits ^= bp->lm;           /* What bits are different? */
105         lower_bits  = ~lower_bits;      /* Invert. */
106         lower_bits |= bp->lk;           /* What bits don't matter for matching? */
107         lower_bits  = ~lower_bits;      /* Invert. */
108 
109         if (lower_bits)
110                 return 0;
111 
112         /* I always knew you'd be the one. */
113         return 1;
114 }
115 
116 /* Given PHYS_ADDR, search memory controller banks for a match. */
117 static struct bank_info *find_bank(unsigned long phys_addr)
118 {
119         struct list_head *mctrl_head = &mctrl_list;
120         struct list_head *mctrl_entry = mctrl_head->next;
121 
122         for (;;) {
123                 struct mctrl_info *mp =
124                         list_entry(mctrl_entry, struct mctrl_info, list);
125                 int bank_no;
126 
127                 if (mctrl_entry == mctrl_head)
128                         break;
129                 mctrl_entry = mctrl_entry->next;
130 
131                 for (bank_no = 0; bank_no < CHMCTRL_NBANKS; bank_no++) {
132                         struct bank_info *bp;
133 
134                         bp = &mp->logical_banks[bank_no];
135                         if (bank_match(bp, phys_addr))
136                                 return bp;
137                 }
138         }
139 
140         return NULL;
141 }
142 
143 /* This is the main purpose of this driver. */
144 #define SYNDROME_MIN    -1
145 #define SYNDROME_MAX    144
146 int chmc_getunumber(int syndrome_code,
147                     unsigned long phys_addr,
148                     char *buf, int buflen)
149 {
150         struct bank_info *bp;
151         struct obp_mem_layout *prop;
152         int bank_in_controller, first_dimm;
153 
154         bp = find_bank(phys_addr);
155         if (bp == NULL ||
156             syndrome_code < SYNDROME_MIN ||
157             syndrome_code > SYNDROME_MAX) {
158                 buf[0] = '?';
159                 buf[1] = '?';
160                 buf[2] = '?';
161                 buf[3] = '\0';
162                 return 0;
163         }
164 
165         prop = &bp->mp->layout_prop;
166         bank_in_controller = bp->bank_id & (CHMCTRL_NBANKS - 1);
167         first_dimm  = (bank_in_controller & (CHMCTRL_NDGRPS - 1));
168         first_dimm *= CHMCTRL_NDIMMS;
169 
170         if (syndrome_code != SYNDROME_MIN) {
171                 struct obp_map *map;
172                 int qword, where_in_line, where, map_index, map_offset;
173                 unsigned int map_val;
174 
175                 /* Yaay, single bit error so we can figure out
176                  * the exact dimm.
177                  */
178                 if (prop->symmetric)
179                         map = &prop->map[0];
180                 else
181                         map = &prop->map[1];
182 
183                 /* Covert syndrome code into the way the bits are
184                  * positioned on the bus.
185                  */
186                 if (syndrome_code < 144 - 16)
187                         syndrome_code += 16;
188                 else if (syndrome_code < 144)
189                         syndrome_code -= (144 - 7);
190                 else if (syndrome_code < (144 + 3))
191                         syndrome_code -= (144 + 3 - 4);
192                 else
193                         syndrome_code -= 144 + 3;
194 
195                 /* All this magic has to do with how a cache line
196                  * comes over the wire on Safari.  A 64-bit line
197                  * comes over in 4 quadword cycles, each of which
198                  * transmit ECC/MTAG info as well as the actual
199                  * data.  144 bits per quadword, 576 total.
200                  */
201 #define LINE_SIZE       64
202 #define LINE_ADDR_MSK   (LINE_SIZE - 1)
203 #define QW_PER_LINE     4
204 #define QW_BYTES        (LINE_SIZE / QW_PER_LINE)
205 #define QW_BITS         144
206 #define LAST_BIT        (576 - 1)
207 
208                 qword = (phys_addr & LINE_ADDR_MSK) / QW_BYTES;
209                 where_in_line = ((3 - qword) * QW_BITS) + syndrome_code;
210                 where = (LAST_BIT - where_in_line);
211                 map_index = where >> 2;
212                 map_offset = where & 0x3;
213                 map_val = map->dimm_map[map_index];
214                 map_val = ((map_val >> ((3 - map_offset) << 1)) & (2 - 1));
215 
216                 sprintf(buf, "%s, pin %3d",
217                         prop->dimm_labels[first_dimm + map_val],
218                         map->pin_map[where_in_line]);
219         } else {
220                 int dimm;
221 
222                 /* Multi-bit error, we just dump out all the
223                  * dimm labels assosciated with this bank.
224                  */
225                 for (dimm = 0; dimm < CHMCTRL_NDIMMS; dimm++) {
226                         sprintf(buf, "%s ",
227                                 prop->dimm_labels[first_dimm + dimm]);
228                         buf += strlen(buf);
229                 }
230         }
231         return 0;
232 }
233 
234 /* Accessing the registers is slightly complicated.  If you want
235  * to get at the memory controller which is on the same processor
236  * the code is executing, you must use special ASI load/store else
237  * you go through the global mapping.
238  */
239 static u64 read_mcreg(struct mctrl_info *mp, unsigned long offset)
240 {
241         unsigned long ret;
242 
243         if (mp->portid == smp_processor_id()) {
244                 __asm__ __volatile__("ldxa      [%1] %2, %0"
245                                      : "=r" (ret)
246                                      : "r" (offset), "i" (ASI_MCU_CTRL_REG));
247         } else {
248                 __asm__ __volatile__("ldxa      [%1] %2, %0"
249                                      : "=r" (ret)
250                                      : "r" (mp->regs + offset),
251                                        "i" (ASI_PHYS_BYPASS_EC_E));
252         }
253         return ret;
254 }
255 
256 #if 0 /* currently unused */
257 static void write_mcreg(struct mctrl_info *mp, unsigned long offset, u64 val)
258 {
259         if (mp->portid == smp_processor_id()) {
260                 __asm__ __volatile__("stxa      %0, [%1] %2"
261                                      : : "r" (val),
262                                          "r" (offset), "i" (ASI_MCU_CTRL_REG));
263         } else {
264                 __asm__ __volatile__("ldxa      %0, [%1] %2"
265                                      : : "r" (val),
266                                          "r" (mp->regs + offset),
267                                          "i" (ASI_PHYS_BYPASS_EC_E));
268         }
269 }
270 #endif
271 
272 static void interpret_one_decode_reg(struct mctrl_info *mp, int which_bank, u64 val)
273 {
274         struct bank_info *p = &mp->logical_banks[which_bank];
275 
276         p->mp = mp;
277         p->bank_id = (CHMCTRL_NBANKS * mp->portid) + which_bank;
278         p->raw_reg = val;
279         p->valid = (val & MEM_DECODE_VALID) >> MEM_DECODE_VALID_SHIFT;
280         p->uk = (val & MEM_DECODE_UK) >> MEM_DECODE_UK_SHIFT;
281         p->um = (val & MEM_DECODE_UM) >> MEM_DECODE_UM_SHIFT;
282         p->lk = (val & MEM_DECODE_LK) >> MEM_DECODE_LK_SHIFT;
283         p->lm = (val & MEM_DECODE_LM) >> MEM_DECODE_LM_SHIFT;
284 
285         p->base  =  (p->um);
286         p->base &= ~(p->uk);
287         p->base <<= PA_UPPER_BITS_SHIFT;
288 
289         switch(p->lk) {
290         case 0xf:
291         default:
292                 p->interleave = 1;
293                 break;
294 
295         case 0xe:
296                 p->interleave = 2;
297                 break;
298 
299         case 0xc:
300                 p->interleave = 4;
301                 break;
302 
303         case 0x8:
304                 p->interleave = 8;
305                 break;
306 
307         case 0x0:
308                 p->interleave = 16;
309                 break;
310         };
311 
312         /* UK[10] is reserved, and UK[11] is not set for the SDRAM
313          * bank size definition.
314          */
315         p->size = (((unsigned long)p->uk &
316                     ((1UL << 10UL) - 1UL)) + 1UL) << PA_UPPER_BITS_SHIFT;
317         p->size /= p->interleave;
318 }
319 
320 static void fetch_decode_regs(struct mctrl_info *mp)
321 {
322         if (mp->layout_size == 0)
323                 return;
324 
325         interpret_one_decode_reg(mp, 0,
326                                  read_mcreg(mp, CHMCTRL_DECODE1));
327         interpret_one_decode_reg(mp, 1,
328                                  read_mcreg(mp, CHMCTRL_DECODE2));
329         interpret_one_decode_reg(mp, 2,
330                                  read_mcreg(mp, CHMCTRL_DECODE3));
331         interpret_one_decode_reg(mp, 3,
332                                  read_mcreg(mp, CHMCTRL_DECODE4));
333 }
334 
335 static int init_one_mctrl(int node, int index)
336 {
337         struct mctrl_info *mp = kmalloc(sizeof(*mp), GFP_KERNEL);
338         int portid = prom_getintdefault(node, "portid", -1);
339         struct linux_prom64_registers p_reg_prop;
340         int t;
341 
342         if (!mp)
343                 return -1;
344         memset(mp, 0, sizeof(*mp));
345         if (portid == -1)
346                 goto fail;
347 
348         mp->portid = portid;
349         mp->layout_size = prom_getproplen(node, "memory-layout");
350         if (mp->layout_size < 0)
351                 mp->layout_size = 0;
352         if (mp->layout_size > sizeof(mp->layout_prop))
353                 goto fail;
354 
355         if (mp->layout_size > 0)
356                 prom_getproperty(node, "memory-layout",
357                                  (char *) &mp->layout_prop,
358                                  mp->layout_size);
359 
360         t = prom_getproperty(node, "reg",
361                              (char *) &p_reg_prop,
362                              sizeof(p_reg_prop));
363         if (t < 0 || p_reg_prop.reg_size != 0x48)
364                 goto fail;
365 
366         mp->regs = ioremap(p_reg_prop.phys_addr, p_reg_prop.reg_size);
367         if (mp->regs == NULL)
368                 goto fail;
369 
370         if (mp->layout_size != 0UL) {
371                 mp->timing_control1 = read_mcreg(mp, CHMCTRL_TCTRL1);
372                 mp->timing_control2 = read_mcreg(mp, CHMCTRL_TCTRL2);
373                 mp->timing_control3 = read_mcreg(mp, CHMCTRL_TCTRL3);
374                 mp->timing_control4 = read_mcreg(mp, CHMCTRL_TCTRL4);
375                 mp->memaddr_control = read_mcreg(mp, CHMCTRL_MACTRL);
376         }
377 
378         fetch_decode_regs(mp);
379 
380         mp->index = index;
381 
382         list_add(&mp->list, &mctrl_list);
383 
384         /* Report the device. */
385         printk(KERN_INFO "chmc%d: US3 memory controller at %p [%s]\n",
386                mp->index,
387                mp->regs, (mp->layout_size ? "ACTIVE" : "INACTIVE"));
388 
389         return 0;
390 
391 fail:
392         if (mp) {
393                 if (mp->regs != NULL)
394                         iounmap(mp->regs);
395                 kfree(mp);
396         }
397         return -1;
398 }
399 
400 static int __init probe_for_string(char *name, int index)
401 {
402         int node = prom_getchild(prom_root_node);
403 
404         while ((node = prom_searchsiblings(node, name)) != 0) {
405                 int ret = init_one_mctrl(node, index);
406 
407                 if (!ret)
408                         index++;
409 
410                 node = prom_getsibling(node);
411                 if (!node)
412                         break;
413         }
414 
415         return index;
416 }
417 
418 static int __init chmc_init(void)
419 {
420         int index;
421 
422         /* This driver is only for cheetah platforms. */
423         if (tlb_type != cheetah && tlb_type != cheetah_plus)
424                 return -ENODEV;
425 
426         index = probe_for_string("memory-controller", 0);
427         index = probe_for_string("mc-us3", index);
428 
429         return 0;
430 }
431 
432 static void __exit chmc_cleanup(void)
433 {
434         struct list_head *head = &mctrl_list;
435         struct list_head *tmp = head->next;
436 
437         for (;;) {
438                 struct mctrl_info *p =
439                         list_entry(tmp, struct mctrl_info, list);
440                 if (tmp == head)
441                         break;
442                 tmp = tmp->next;
443 
444                 list_del(&p->list);
445                 iounmap(p->regs);
446                 kfree(p);
447         }
448 }
449 
450 module_init(chmc_init);
451 module_exit(chmc_cleanup);
452 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp