ee48cd1eb34e7badde6dda986c38af83c7862da3
[linux-flexiantxendom0-3.2.10.git] / arch / ia64 / sn / io / sn2 / shub.c
1 /* $Id$
2  *
3  * This file is subject to the terms and conditions of the GNU General Public
4  * License.  See the file "COPYING" in the main directory of this archive
5  * for more details.
6  *
7  * Copyright (C) 1992-1997, 2000-2003 Silicon Graphics, Inc.  All Rights Reserved.
8  */
9
10 #ident  "$Revision: 1.167 $"
11
12 #include <linux/types.h>
13 #include <linux/slab.h>
14 #include <linux/interrupt.h>
15 #include <asm/smp.h>
16 #include <asm/irq.h>
17 #include <asm/hw_irq.h>
18 #include <asm/sn/sgi.h>
19 #include <asm/sn/iograph.h>
20 #include <asm/sn/invent.h>
21 #include <asm/sn/hcl.h>
22 #include <asm/sn/labelcl.h>
23 #include <asm/sn/io.h>
24 #include <asm/sn/sn_private.h>
25 #include <asm/sn/klconfig.h>
26 #include <asm/sn/sn_cpuid.h>
27 #include <asm/sn/pci/pciio.h>
28 #include <asm/sn/pci/pcibr.h>
29 #include <asm/sn/xtalk/xtalk.h>
30 #include <asm/sn/pci/pcibr_private.h>
31 #include <asm/sn/intr.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
37 /*
38  * Shub WAR for Xbridge Little Endian problem:
39  *      Xbridge has to run in BIG ENDIAN even with Shub.
40  */
41
42
43 /*
44  * io_sh_swapper: Turn on Shub byte swapping.
45  *      All data destined to and from Shub to XIO are byte-swapped.
46  */
47 void
48 io_sh_swapper(nasid_t nasid, int onoff)
49 {
50     ii_iwc_u_t      ii_iwc;
51
52     ii_iwc.ii_iwc_regval = REMOTE_HUB_L(nasid, IIO_IWC);
53
54     ii_iwc.ii_iwc_fld_s.i_dma_byte_swap = onoff;
55     REMOTE_HUB_S(nasid, IIO_IWC, ii_iwc.ii_iwc_regval);
56     ii_iwc.ii_iwc_regval = REMOTE_HUB_L(nasid, IIO_IWC);
57
58 }
59
60 /*
61  * io_get_sh_swapper: Return current Swap mode.
62  *      1 = Swap on, 0 = Swap off.
63  */
64 int
65 io_get_sh_swapper(nasid_t nasid)
66 {
67     ii_iwc_u_t      ii_iwc;
68
69     ii_iwc.ii_iwc_regval = REMOTE_HUB_L(nasid, IIO_IWC);
70     return(ii_iwc.ii_iwc_fld_s.i_dma_byte_swap);
71
72 }
73
74 #define SHUB_NUM_ECF_REGISTERS 8
75
76 static uint32_t shub_perf_counts[SHUB_NUM_ECF_REGISTERS];
77
78 static shubreg_t shub_perf_counts_regs[SHUB_NUM_ECF_REGISTERS] = {
79         SH_PERFORMANCE_COUNTER0,
80         SH_PERFORMANCE_COUNTER1,
81         SH_PERFORMANCE_COUNTER2,
82         SH_PERFORMANCE_COUNTER3,
83         SH_PERFORMANCE_COUNTER4,
84         SH_PERFORMANCE_COUNTER5,
85         SH_PERFORMANCE_COUNTER6,
86         SH_PERFORMANCE_COUNTER7
87 };
88
89 static inline void
90 shub_mmr_write(cnodeid_t cnode, shubreg_t reg, uint64_t val)
91 {
92         int                nasid = cnodeid_to_nasid(cnode);
93         volatile uint64_t *addr = (uint64_t *)(GLOBAL_MMR_ADDR(nasid, reg));
94
95         *addr = val;
96         __ia64_mf_a();
97 }
98
99 static inline void
100 shub_mmr_write32(cnodeid_t cnode, shubreg_t reg, uint32_t val)
101 {
102         int                nasid = cnodeid_to_nasid(cnode);
103         volatile uint32_t *addr = (uint32_t *)(GLOBAL_MMR_ADDR(nasid, reg));
104
105         *addr = val;
106         __ia64_mf_a();
107 }
108
109 static inline uint64_t
110 shub_mmr_read(cnodeid_t cnode, shubreg_t reg)
111 {
112         int               nasid = cnodeid_to_nasid(cnode);
113         volatile uint64_t val;
114
115         val = *(uint64_t *)(GLOBAL_MMR_ADDR(nasid, reg));
116         __ia64_mf_a();
117
118         return val;
119 }
120
121 static inline uint32_t
122 shub_mmr_read32(cnodeid_t cnode, shubreg_t reg)
123 {
124         int               nasid = cnodeid_to_nasid(cnode);
125         volatile uint32_t val;
126
127         val = *(uint32_t *)(GLOBAL_MMR_ADDR(nasid, reg));
128         __ia64_mf_a();
129
130         return val;
131 }
132
133 static int
134 reset_shub_stats(cnodeid_t cnode)
135 {
136         int i;
137
138         for (i=0; i < SHUB_NUM_ECF_REGISTERS; i++) {
139                 shub_perf_counts[i] = 0;
140                 shub_mmr_write32(cnode, shub_perf_counts_regs[i], 0);
141         }
142         return 0;
143 }
144
145 static int
146 configure_shub_stats(cnodeid_t cnode, unsigned long arg)
147 {
148         uint64_t        *p = (uint64_t *)arg;
149         uint64_t        i;
150         uint64_t        regcnt;
151         uint64_t        regval[2];
152
153         if (copy_from_user((void *)&regcnt, p, sizeof(regcnt)))
154             return -EFAULT;
155
156         for (p++, i=0; i < regcnt; i++, p += 2) {
157                 if (copy_from_user((void *)regval, (void *)p, sizeof(regval)))
158                     return -EFAULT;
159                 if (regval[0] & 0x7) {
160                     printk("Error: configure_shub_stats: unaligned address 0x%016lx\n", regval[0]);
161                     return -EINVAL;
162                 }
163                 shub_mmr_write(cnode, (shubreg_t)regval[0], regval[1]);
164         }
165         return 0;
166 }
167
168 static int
169 capture_shub_stats(cnodeid_t cnode, uint32_t *counts)
170 {
171         int             i;
172
173         for (i=0; i < SHUB_NUM_ECF_REGISTERS; i++) {
174                 counts[i] = shub_mmr_read32(cnode, shub_perf_counts_regs[i]);
175         }
176         return 0;
177 }
178
179 static int
180 shubstats_ioctl(struct inode *inode, struct file *file,
181         unsigned int cmd, unsigned long arg)
182 {
183         cnodeid_t       cnode;
184         uint64_t        longarg;
185         devfs_handle_t  d;
186         int             nasid;
187
188         if ((d = devfs_get_handle_from_inode(inode)) == NULL)
189                 return -ENODEV;
190         cnode = (cnodeid_t)hwgraph_fastinfo_get(d);
191
192         switch (cmd) {
193         case SNDRV_SHUB_CONFIGURE:
194                 return configure_shub_stats(cnode, arg);
195                 break;
196
197         case SNDRV_SHUB_RESETSTATS:
198                 reset_shub_stats(cnode);
199                 break;
200
201         case SNDRV_SHUB_INFOSIZE:
202                 longarg = sizeof(shub_perf_counts);
203                 if (copy_to_user((void *)arg, &longarg, sizeof(longarg))) {
204                     return -EFAULT;
205                 }
206                 break;
207
208         case SNDRV_SHUB_GETSTATS:
209                 capture_shub_stats(cnode, shub_perf_counts);
210                 if (copy_to_user((void *)arg, shub_perf_counts,
211                                         sizeof(shub_perf_counts))) {
212                     return -EFAULT;
213                 }
214                 break;
215
216         case SNDRV_SHUB_GETNASID:
217                 nasid = cnodeid_to_nasid(cnode);
218                 if (copy_to_user((void *)arg, &nasid,
219                                         sizeof(nasid))) {
220                     return -EFAULT;
221                 }
222                 break;
223
224         default:
225                 return -EINVAL;
226         }
227
228         return 0;
229 }
230
231 struct file_operations shub_mon_fops = {
232                 ioctl:          shubstats_ioctl,
233 };