ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / arch / ia64 / sn / io / sn2 / shub.c
1 /*
2  * This file is subject to the terms and conditions of the GNU General Public
3  * License.  See the file "COPYING" in the main directory of this archive
4  * for more details.
5  *
6  * Copyright (C) 1992-1997, 2000-2003 Silicon Graphics, Inc.  All Rights Reserved.
7  */
8
9 #include <linux/types.h>
10 #include <linux/slab.h>
11 #include <linux/interrupt.h>
12 #include <linux/seq_file.h>
13 #include <linux/sched.h>
14 #include <asm/smp.h>
15 #include <asm/irq.h>
16 #include <asm/hw_irq.h>
17 #include <asm/system.h>
18 #include <asm/sn/sgi.h>
19 #include <asm/uaccess.h>
20 #include <asm/sn/hcl.h>
21 #include <asm/sn/labelcl.h>
22 #include <asm/sn/io.h>
23 #include <asm/sn/sn_private.h>
24 #include <asm/sn/klconfig.h>
25 #include <asm/sn/sn_cpuid.h>
26 #include <asm/sn/pci/pciio.h>
27 #include <asm/sn/pci/pcibr.h>
28 #include <asm/sn/xtalk/xtalk.h>
29 #include <asm/sn/pci/pcibr_private.h>
30 #include <asm/sn/intr.h>
31 #include <asm/sn/sn2/shub_mmr.h>
32 #include <asm/sn/sn2/shub_mmr_t.h>
33 #include <asm/sal.h>
34 #include <asm/sn/sn_sal.h>
35 #include <asm/sn/sndrv.h>
36 #include <asm/sn/sn2/shubio.h>
37
38 #define SHUB_NUM_ECF_REGISTERS 8
39
40 static uint32_t shub_perf_counts[SHUB_NUM_ECF_REGISTERS];
41
42 static shubreg_t shub_perf_counts_regs[SHUB_NUM_ECF_REGISTERS] = {
43         SH_PERFORMANCE_COUNTER0,
44         SH_PERFORMANCE_COUNTER1,
45         SH_PERFORMANCE_COUNTER2,
46         SH_PERFORMANCE_COUNTER3,
47         SH_PERFORMANCE_COUNTER4,
48         SH_PERFORMANCE_COUNTER5,
49         SH_PERFORMANCE_COUNTER6,
50         SH_PERFORMANCE_COUNTER7
51 };
52
53 static inline void
54 shub_mmr_write(cnodeid_t cnode, shubreg_t reg, uint64_t val)
55 {
56         int                nasid = cnodeid_to_nasid(cnode);
57         volatile uint64_t *addr = (uint64_t *)(GLOBAL_MMR_ADDR(nasid, reg));
58
59         *addr = val;
60         __ia64_mf_a();
61 }
62
63 static inline void
64 shub_mmr_write_iospace(cnodeid_t cnode, shubreg_t reg, uint64_t val)
65 {
66         int                nasid = cnodeid_to_nasid(cnode);
67
68         REMOTE_HUB_S(nasid, reg, val);
69 }
70
71 static inline void
72 shub_mmr_write32(cnodeid_t cnode, shubreg_t reg, uint32_t val)
73 {
74         int                nasid = cnodeid_to_nasid(cnode);
75         volatile uint32_t *addr = (uint32_t *)(GLOBAL_MMR_ADDR(nasid, reg));
76
77         *addr = val;
78         __ia64_mf_a();
79 }
80
81 static inline uint64_t
82 shub_mmr_read(cnodeid_t cnode, shubreg_t reg)
83 {
84         int               nasid = cnodeid_to_nasid(cnode);
85         volatile uint64_t val;
86
87         val = *(uint64_t *)(GLOBAL_MMR_ADDR(nasid, reg));
88         __ia64_mf_a();
89
90         return val;
91 }
92
93 static inline uint64_t
94 shub_mmr_read_iospace(cnodeid_t cnode, shubreg_t reg)
95 {
96         int               nasid = cnodeid_to_nasid(cnode);
97
98         return REMOTE_HUB_L(nasid, reg);
99 }
100
101 static inline uint32_t
102 shub_mmr_read32(cnodeid_t cnode, shubreg_t reg)
103 {
104         int               nasid = cnodeid_to_nasid(cnode);
105         volatile uint32_t val;
106
107         val = *(uint32_t *)(GLOBAL_MMR_ADDR(nasid, reg));
108         __ia64_mf_a();
109
110         return val;
111 }
112
113 static int
114 reset_shub_stats(cnodeid_t cnode)
115 {
116         int i;
117
118         for (i=0; i < SHUB_NUM_ECF_REGISTERS; i++) {
119                 shub_perf_counts[i] = 0;
120                 shub_mmr_write32(cnode, shub_perf_counts_regs[i], 0);
121         }
122         return 0;
123 }
124
125 static int
126 configure_shub_stats(cnodeid_t cnode, unsigned long arg)
127 {
128         uint64_t        *p = (uint64_t *)arg;
129         uint64_t        i;
130         uint64_t        regcnt;
131         uint64_t        regval[2];
132
133         if (copy_from_user((void *)&regcnt, p, sizeof(regcnt)))
134             return -EFAULT;
135
136         for (p++, i=0; i < regcnt; i++, p += 2) {
137                 if (copy_from_user((void *)regval, (void *)p, sizeof(regval)))
138                     return -EFAULT;
139                 if (regval[0] & 0x7) {
140                     printk("Error: configure_shub_stats: unaligned address 0x%016lx\n", regval[0]);
141                     return -EINVAL;
142                 }
143                 shub_mmr_write(cnode, (shubreg_t)regval[0], regval[1]);
144         }
145         return 0;
146 }
147
148 static int
149 capture_shub_stats(cnodeid_t cnode, uint32_t *counts)
150 {
151         int             i;
152
153         for (i=0; i < SHUB_NUM_ECF_REGISTERS; i++) {
154                 counts[i] = shub_mmr_read32(cnode, shub_perf_counts_regs[i]);
155         }
156         return 0;
157 }
158
159 static int
160 shubstats_ioctl(struct inode *inode, struct file *file,
161         unsigned int cmd, unsigned long arg)
162 {
163         cnodeid_t       cnode;
164         uint64_t        longarg;
165         uint64_t        intarg;
166         uint64_t        regval[2];
167         int             nasid;
168
169         cnode = (cnodeid_t)(u64)file->f_dentry->d_fsdata;
170         if (cnode < 0 || cnode >= numnodes)
171                 return -ENODEV;
172
173         switch (cmd) {
174         case SNDRV_SHUB_CONFIGURE:
175                 return configure_shub_stats(cnode, arg);
176                 break;
177
178         case SNDRV_SHUB_RESETSTATS:
179                 reset_shub_stats(cnode);
180                 break;
181
182         case SNDRV_SHUB_INFOSIZE:
183                 longarg = sizeof(shub_perf_counts);
184                 if (copy_to_user((void *)arg, &longarg, sizeof(longarg))) {
185                     return -EFAULT;
186                 }
187                 break;
188
189         case SNDRV_SHUB_GETSTATS:
190                 capture_shub_stats(cnode, shub_perf_counts);
191                 if (copy_to_user((void *)arg, shub_perf_counts,
192                                         sizeof(shub_perf_counts))) {
193                     return -EFAULT;
194                 }
195                 break;
196
197         case SNDRV_SHUB_GETNASID:
198                 nasid = cnodeid_to_nasid(cnode);
199                 if (copy_to_user((void *)arg, &nasid,
200                                         sizeof(nasid))) {
201                     return -EFAULT;
202                 }
203                 break;
204
205         case SNDRV_SHUB_GETMMR32:
206                 intarg = shub_mmr_read32(cnode, arg);
207                 if (copy_to_user((void *)arg, &intarg,
208                                         sizeof(intarg))) {
209                     return -EFAULT;
210                 }
211                 break;
212  
213         case SNDRV_SHUB_GETMMR64:
214         case SNDRV_SHUB_GETMMR64_IO:
215                 if (cmd == SNDRV_SHUB_GETMMR64)
216                     longarg = shub_mmr_read(cnode, arg);
217                 else
218                     longarg = shub_mmr_read_iospace(cnode, arg);
219                 if (copy_to_user((void *)arg, &longarg, sizeof(longarg)))
220                     return -EFAULT;
221                 break;
222  
223         case SNDRV_SHUB_PUTMMR64:
224         case SNDRV_SHUB_PUTMMR64_IO:
225                 if (copy_from_user((void *)regval, (void *)arg, sizeof(regval)))
226                     return -EFAULT;
227                 if (regval[0] & 0x7) {
228                     printk("Error: configure_shub_stats: unaligned address 0x%016lx\n", regval[0]);
229                     return -EINVAL;
230                 }
231                 if (cmd == SNDRV_SHUB_PUTMMR64)
232                     shub_mmr_write(cnode, (shubreg_t)regval[0], regval[1]);
233                 else
234                     shub_mmr_write_iospace(cnode, (shubreg_t)regval[0], regval[1]);
235                 break;
236  
237         default:
238                 return -EINVAL;
239         }
240
241         return 0;
242 }
243
244 struct file_operations shub_mon_fops = {
245                 .ioctl          = shubstats_ioctl,
246 };