diff options
author | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2009-12-26 09:52:54 -0500 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2009-12-26 09:52:54 -0500 |
commit | 7f50548abb5454bd82c25aae15f0a3bf6a530f46 (patch) | |
tree | 175b5d695437151f0f9f778ad8eb7f274468842f /drivers/misc | |
parent | b3172f222ab5afdc91ea058bd11c42cf169728f3 (diff) | |
parent | 6b7b284958d47b77d06745b36bc7f36dab769d9b (diff) |
Merge commit 'v2.6.33-rc2' into for-2.6.33
Diffstat (limited to 'drivers/misc')
44 files changed, 4807 insertions, 572 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index df1f86b5c83e..e3551d20464f 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig | |||
@@ -13,6 +13,20 @@ menuconfig MISC_DEVICES | |||
13 | 13 | ||
14 | if MISC_DEVICES | 14 | if MISC_DEVICES |
15 | 15 | ||
16 | config AD525X_DPOT | ||
17 | tristate "Analog Devices AD525x Digital Potentiometers" | ||
18 | depends on I2C && SYSFS | ||
19 | help | ||
20 | If you say yes here, you get support for the Analog Devices | ||
21 | AD5258, AD5259, AD5251, AD5252, AD5253, AD5254 and AD5255 | ||
22 | digital potentiometer chips. | ||
23 | |||
24 | See Documentation/misc-devices/ad525x_dpot.txt for the | ||
25 | userspace interface. | ||
26 | |||
27 | This driver can also be built as a module. If so, the module | ||
28 | will be called ad525x_dpot. | ||
29 | |||
16 | config ATMEL_PWM | 30 | config ATMEL_PWM |
17 | tristate "Atmel AT32/AT91 PWM support" | 31 | tristate "Atmel AT32/AT91 PWM support" |
18 | depends on AVR32 || ARCH_AT91SAM9263 || ARCH_AT91SAM9RL || ARCH_AT91CAP9 | 32 | depends on AVR32 || ARCH_AT91SAM9263 || ARCH_AT91SAM9RL || ARCH_AT91CAP9 |
@@ -173,6 +187,31 @@ config SGI_XP | |||
173 | this feature will allow for direct communication between SSIs | 187 | this feature will allow for direct communication between SSIs |
174 | based on a network adapter and DMA messaging. | 188 | based on a network adapter and DMA messaging. |
175 | 189 | ||
190 | config CS5535_MFGPT | ||
191 | tristate "CS5535/CS5536 Geode Multi-Function General Purpose Timer (MFGPT) support" | ||
192 | depends on PCI | ||
193 | depends on X86 | ||
194 | default n | ||
195 | help | ||
196 | This driver provides access to MFGPT functionality for other | ||
197 | drivers that need timers. MFGPTs are available in the CS5535 and | ||
198 | CS5536 companion chips that are found in AMD Geode and several | ||
199 | other platforms. They have a better resolution and max interval | ||
200 | than the generic PIT, and are suitable for use as high-res timers. | ||
201 | You probably don't want to enable this manually; other drivers that | ||
202 | make use of it should enable it. | ||
203 | |||
204 | config CS5535_MFGPT_DEFAULT_IRQ | ||
205 | int | ||
206 | depends on CS5535_MFGPT | ||
207 | default 7 | ||
208 | help | ||
209 | MFGPTs on the CS5535 require an interrupt. The selected IRQ | ||
210 | can be overridden as a module option as well as by driver that | ||
211 | use the cs5535_mfgpt_ API; however, different architectures might | ||
212 | want to use a different IRQ by default. This is here for | ||
213 | architectures to set as necessary. | ||
214 | |||
176 | config HP_ILO | 215 | config HP_ILO |
177 | tristate "Channel interface driver for HP iLO/iLO2 processor" | 216 | tristate "Channel interface driver for HP iLO/iLO2 processor" |
178 | depends on PCI | 217 | depends on PCI |
@@ -210,19 +249,6 @@ config SGI_GRU_DEBUG | |||
210 | This option enables addition debugging code for the SGI GRU driver. If | 249 | This option enables addition debugging code for the SGI GRU driver. If |
211 | you are unsure, say N. | 250 | you are unsure, say N. |
212 | 251 | ||
213 | config DELL_LAPTOP | ||
214 | tristate "Dell Laptop Extras (EXPERIMENTAL)" | ||
215 | depends on X86 | ||
216 | depends on DCDBAS | ||
217 | depends on EXPERIMENTAL | ||
218 | depends on BACKLIGHT_CLASS_DEVICE | ||
219 | depends on RFKILL | ||
220 | depends on POWER_SUPPLY | ||
221 | default n | ||
222 | ---help--- | ||
223 | This driver adds support for rfkill and backlight control to Dell | ||
224 | laptops. | ||
225 | |||
226 | config ISL29003 | 252 | config ISL29003 |
227 | tristate "Intersil ISL29003 ambient light sensor" | 253 | tristate "Intersil ISL29003 ambient light sensor" |
228 | depends on I2C && SYSFS | 254 | depends on I2C && SYSFS |
@@ -246,8 +272,29 @@ config EP93XX_PWM | |||
246 | To compile this driver as a module, choose M here: the module will | 272 | To compile this driver as a module, choose M here: the module will |
247 | be called ep93xx_pwm. | 273 | be called ep93xx_pwm. |
248 | 274 | ||
275 | config DS1682 | ||
276 | tristate "Dallas DS1682 Total Elapsed Time Recorder with Alarm" | ||
277 | depends on I2C && EXPERIMENTAL | ||
278 | help | ||
279 | If you say yes here you get support for Dallas Semiconductor | ||
280 | DS1682 Total Elapsed Time Recorder. | ||
281 | |||
282 | This driver can also be built as a module. If so, the module | ||
283 | will be called ds1682. | ||
284 | |||
285 | config TI_DAC7512 | ||
286 | tristate "Texas Instruments DAC7512" | ||
287 | depends on SPI && SYSFS | ||
288 | help | ||
289 | If you say yes here you get support for the Texas Instruments | ||
290 | DAC7512 16-bit digital-to-analog converter. | ||
291 | |||
292 | This driver can also be built as a module. If so, the module | ||
293 | will be calles ti_dac7512. | ||
294 | |||
249 | source "drivers/misc/c2port/Kconfig" | 295 | source "drivers/misc/c2port/Kconfig" |
250 | source "drivers/misc/eeprom/Kconfig" | 296 | source "drivers/misc/eeprom/Kconfig" |
251 | source "drivers/misc/cb710/Kconfig" | 297 | source "drivers/misc/cb710/Kconfig" |
298 | source "drivers/misc/iwmc3200top/Kconfig" | ||
252 | 299 | ||
253 | endif # MISC_DEVICES | 300 | endif # MISC_DEVICES |
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index f982d2ecfde7..049ff2482f30 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile | |||
@@ -4,6 +4,7 @@ | |||
4 | 4 | ||
5 | obj-$(CONFIG_IBM_ASM) += ibmasm/ | 5 | obj-$(CONFIG_IBM_ASM) += ibmasm/ |
6 | obj-$(CONFIG_HDPU_FEATURES) += hdpuftrs/ | 6 | obj-$(CONFIG_HDPU_FEATURES) += hdpuftrs/ |
7 | obj-$(CONFIG_AD525X_DPOT) += ad525x_dpot.o | ||
7 | obj-$(CONFIG_ATMEL_PWM) += atmel_pwm.o | 8 | obj-$(CONFIG_ATMEL_PWM) += atmel_pwm.o |
8 | obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o | 9 | obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o |
9 | obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o | 10 | obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o |
@@ -17,9 +18,13 @@ obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o | |||
17 | obj-$(CONFIG_KGDB_TESTS) += kgdbts.o | 18 | obj-$(CONFIG_KGDB_TESTS) += kgdbts.o |
18 | obj-$(CONFIG_SGI_XP) += sgi-xp/ | 19 | obj-$(CONFIG_SGI_XP) += sgi-xp/ |
19 | obj-$(CONFIG_SGI_GRU) += sgi-gru/ | 20 | obj-$(CONFIG_SGI_GRU) += sgi-gru/ |
21 | obj-$(CONFIG_CS5535_MFGPT) += cs5535-mfgpt.o | ||
20 | obj-$(CONFIG_HP_ILO) += hpilo.o | 22 | obj-$(CONFIG_HP_ILO) += hpilo.o |
21 | obj-$(CONFIG_ISL29003) += isl29003.o | 23 | obj-$(CONFIG_ISL29003) += isl29003.o |
22 | obj-$(CONFIG_EP93XX_PWM) += ep93xx_pwm.o | 24 | obj-$(CONFIG_EP93XX_PWM) += ep93xx_pwm.o |
25 | obj-$(CONFIG_DS1682) += ds1682.o | ||
26 | obj-$(CONFIG_TI_DAC7512) += ti_dac7512.o | ||
23 | obj-$(CONFIG_C2PORT) += c2port/ | 27 | obj-$(CONFIG_C2PORT) += c2port/ |
28 | obj-$(CONFIG_IWMC3200TOP) += iwmc3200top/ | ||
24 | obj-y += eeprom/ | 29 | obj-y += eeprom/ |
25 | obj-y += cb710/ | 30 | obj-y += cb710/ |
diff --git a/drivers/misc/ad525x_dpot.c b/drivers/misc/ad525x_dpot.c new file mode 100644 index 000000000000..30a59f2bacd2 --- /dev/null +++ b/drivers/misc/ad525x_dpot.c | |||
@@ -0,0 +1,666 @@ | |||
1 | /* | ||
2 | * ad525x_dpot: Driver for the Analog Devices AD525x digital potentiometers | ||
3 | * Copyright (c) 2009 Analog Devices, Inc. | ||
4 | * Author: Michael Hennerich <hennerich@blackfin.uclinux.org> | ||
5 | * | ||
6 | * DEVID #Wipers #Positions Resistor Options (kOhm) | ||
7 | * AD5258 1 64 1, 10, 50, 100 | ||
8 | * AD5259 1 256 5, 10, 50, 100 | ||
9 | * AD5251 2 64 1, 10, 50, 100 | ||
10 | * AD5252 2 256 1, 10, 50, 100 | ||
11 | * AD5255 3 512 25, 250 | ||
12 | * AD5253 4 64 1, 10, 50, 100 | ||
13 | * AD5254 4 256 1, 10, 50, 100 | ||
14 | * | ||
15 | * See Documentation/misc-devices/ad525x_dpot.txt for more info. | ||
16 | * | ||
17 | * derived from ad5258.c | ||
18 | * Copyright (c) 2009 Cyber Switching, Inc. | ||
19 | * Author: Chris Verges <chrisv@cyberswitching.com> | ||
20 | * | ||
21 | * derived from ad5252.c | ||
22 | * Copyright (c) 2006 Michael Hennerich <hennerich@blackfin.uclinux.org> | ||
23 | * | ||
24 | * Licensed under the GPL-2 or later. | ||
25 | */ | ||
26 | |||
27 | #include <linux/module.h> | ||
28 | #include <linux/device.h> | ||
29 | #include <linux/kernel.h> | ||
30 | #include <linux/init.h> | ||
31 | #include <linux/slab.h> | ||
32 | #include <linux/i2c.h> | ||
33 | #include <linux/delay.h> | ||
34 | |||
35 | #define DRIVER_NAME "ad525x_dpot" | ||
36 | #define DRIVER_VERSION "0.1" | ||
37 | |||
38 | enum dpot_devid { | ||
39 | AD5258_ID, | ||
40 | AD5259_ID, | ||
41 | AD5251_ID, | ||
42 | AD5252_ID, | ||
43 | AD5253_ID, | ||
44 | AD5254_ID, | ||
45 | AD5255_ID, | ||
46 | }; | ||
47 | |||
48 | #define AD5258_MAX_POSITION 64 | ||
49 | #define AD5259_MAX_POSITION 256 | ||
50 | #define AD5251_MAX_POSITION 64 | ||
51 | #define AD5252_MAX_POSITION 256 | ||
52 | #define AD5253_MAX_POSITION 64 | ||
53 | #define AD5254_MAX_POSITION 256 | ||
54 | #define AD5255_MAX_POSITION 512 | ||
55 | |||
56 | #define AD525X_RDAC0 0 | ||
57 | #define AD525X_RDAC1 1 | ||
58 | #define AD525X_RDAC2 2 | ||
59 | #define AD525X_RDAC3 3 | ||
60 | |||
61 | #define AD525X_REG_TOL 0x18 | ||
62 | #define AD525X_TOL_RDAC0 (AD525X_REG_TOL | AD525X_RDAC0) | ||
63 | #define AD525X_TOL_RDAC1 (AD525X_REG_TOL | AD525X_RDAC1) | ||
64 | #define AD525X_TOL_RDAC2 (AD525X_REG_TOL | AD525X_RDAC2) | ||
65 | #define AD525X_TOL_RDAC3 (AD525X_REG_TOL | AD525X_RDAC3) | ||
66 | |||
67 | /* RDAC-to-EEPROM Interface Commands */ | ||
68 | #define AD525X_I2C_RDAC (0x00 << 5) | ||
69 | #define AD525X_I2C_EEPROM (0x01 << 5) | ||
70 | #define AD525X_I2C_CMD (0x80) | ||
71 | |||
72 | #define AD525X_DEC_ALL_6DB (AD525X_I2C_CMD | (0x4 << 3)) | ||
73 | #define AD525X_INC_ALL_6DB (AD525X_I2C_CMD | (0x9 << 3)) | ||
74 | #define AD525X_DEC_ALL (AD525X_I2C_CMD | (0x6 << 3)) | ||
75 | #define AD525X_INC_ALL (AD525X_I2C_CMD | (0xB << 3)) | ||
76 | |||
77 | static s32 ad525x_read(struct i2c_client *client, u8 reg); | ||
78 | static s32 ad525x_write(struct i2c_client *client, u8 reg, u8 value); | ||
79 | |||
80 | /* | ||
81 | * Client data (each client gets its own) | ||
82 | */ | ||
83 | |||
84 | struct dpot_data { | ||
85 | struct mutex update_lock; | ||
86 | unsigned rdac_mask; | ||
87 | unsigned max_pos; | ||
88 | unsigned devid; | ||
89 | }; | ||
90 | |||
91 | /* sysfs functions */ | ||
92 | |||
93 | static ssize_t sysfs_show_reg(struct device *dev, | ||
94 | struct device_attribute *attr, char *buf, u32 reg) | ||
95 | { | ||
96 | struct i2c_client *client = to_i2c_client(dev); | ||
97 | struct dpot_data *data = i2c_get_clientdata(client); | ||
98 | s32 value; | ||
99 | |||
100 | mutex_lock(&data->update_lock); | ||
101 | value = ad525x_read(client, reg); | ||
102 | mutex_unlock(&data->update_lock); | ||
103 | |||
104 | if (value < 0) | ||
105 | return -EINVAL; | ||
106 | /* | ||
107 | * Let someone else deal with converting this ... | ||
108 | * the tolerance is a two-byte value where the MSB | ||
109 | * is a sign + integer value, and the LSB is a | ||
110 | * decimal value. See page 18 of the AD5258 | ||
111 | * datasheet (Rev. A) for more details. | ||
112 | */ | ||
113 | |||
114 | if (reg & AD525X_REG_TOL) | ||
115 | return sprintf(buf, "0x%04x\n", value & 0xFFFF); | ||
116 | else | ||
117 | return sprintf(buf, "%u\n", value & data->rdac_mask); | ||
118 | } | ||
119 | |||
120 | static ssize_t sysfs_set_reg(struct device *dev, | ||
121 | struct device_attribute *attr, | ||
122 | const char *buf, size_t count, u32 reg) | ||
123 | { | ||
124 | struct i2c_client *client = to_i2c_client(dev); | ||
125 | struct dpot_data *data = i2c_get_clientdata(client); | ||
126 | unsigned long value; | ||
127 | int err; | ||
128 | |||
129 | err = strict_strtoul(buf, 10, &value); | ||
130 | if (err) | ||
131 | return err; | ||
132 | |||
133 | if (value > data->rdac_mask) | ||
134 | value = data->rdac_mask; | ||
135 | |||
136 | mutex_lock(&data->update_lock); | ||
137 | ad525x_write(client, reg, value); | ||
138 | if (reg & AD525X_I2C_EEPROM) | ||
139 | msleep(26); /* Sleep while the EEPROM updates */ | ||
140 | mutex_unlock(&data->update_lock); | ||
141 | |||
142 | return count; | ||
143 | } | ||
144 | |||
145 | static ssize_t sysfs_do_cmd(struct device *dev, | ||
146 | struct device_attribute *attr, | ||
147 | const char *buf, size_t count, u32 reg) | ||
148 | { | ||
149 | struct i2c_client *client = to_i2c_client(dev); | ||
150 | struct dpot_data *data = i2c_get_clientdata(client); | ||
151 | |||
152 | mutex_lock(&data->update_lock); | ||
153 | ad525x_write(client, reg, 0); | ||
154 | mutex_unlock(&data->update_lock); | ||
155 | |||
156 | return count; | ||
157 | } | ||
158 | |||
159 | /* ------------------------------------------------------------------------- */ | ||
160 | |||
161 | static ssize_t show_rdac0(struct device *dev, | ||
162 | struct device_attribute *attr, char *buf) | ||
163 | { | ||
164 | return sysfs_show_reg(dev, attr, buf, AD525X_I2C_RDAC | AD525X_RDAC0); | ||
165 | } | ||
166 | |||
167 | static ssize_t set_rdac0(struct device *dev, | ||
168 | struct device_attribute *attr, | ||
169 | const char *buf, size_t count) | ||
170 | { | ||
171 | return sysfs_set_reg(dev, attr, buf, count, | ||
172 | AD525X_I2C_RDAC | AD525X_RDAC0); | ||
173 | } | ||
174 | |||
175 | static DEVICE_ATTR(rdac0, S_IWUSR | S_IRUGO, show_rdac0, set_rdac0); | ||
176 | |||
177 | static ssize_t show_eeprom0(struct device *dev, | ||
178 | struct device_attribute *attr, char *buf) | ||
179 | { | ||
180 | return sysfs_show_reg(dev, attr, buf, AD525X_I2C_EEPROM | AD525X_RDAC0); | ||
181 | } | ||
182 | |||
183 | static ssize_t set_eeprom0(struct device *dev, | ||
184 | struct device_attribute *attr, | ||
185 | const char *buf, size_t count) | ||
186 | { | ||
187 | return sysfs_set_reg(dev, attr, buf, count, | ||
188 | AD525X_I2C_EEPROM | AD525X_RDAC0); | ||
189 | } | ||
190 | |||
191 | static DEVICE_ATTR(eeprom0, S_IWUSR | S_IRUGO, show_eeprom0, set_eeprom0); | ||
192 | |||
193 | static ssize_t show_tolerance0(struct device *dev, | ||
194 | struct device_attribute *attr, char *buf) | ||
195 | { | ||
196 | return sysfs_show_reg(dev, attr, buf, | ||
197 | AD525X_I2C_EEPROM | AD525X_TOL_RDAC0); | ||
198 | } | ||
199 | |||
200 | static DEVICE_ATTR(tolerance0, S_IRUGO, show_tolerance0, NULL); | ||
201 | |||
202 | /* ------------------------------------------------------------------------- */ | ||
203 | |||
204 | static ssize_t show_rdac1(struct device *dev, | ||
205 | struct device_attribute *attr, char *buf) | ||
206 | { | ||
207 | return sysfs_show_reg(dev, attr, buf, AD525X_I2C_RDAC | AD525X_RDAC1); | ||
208 | } | ||
209 | |||
210 | static ssize_t set_rdac1(struct device *dev, | ||
211 | struct device_attribute *attr, | ||
212 | const char *buf, size_t count) | ||
213 | { | ||
214 | return sysfs_set_reg(dev, attr, buf, count, | ||
215 | AD525X_I2C_RDAC | AD525X_RDAC1); | ||
216 | } | ||
217 | |||
218 | static DEVICE_ATTR(rdac1, S_IWUSR | S_IRUGO, show_rdac1, set_rdac1); | ||
219 | |||
220 | static ssize_t show_eeprom1(struct device *dev, | ||
221 | struct device_attribute *attr, char *buf) | ||
222 | { | ||
223 | return sysfs_show_reg(dev, attr, buf, AD525X_I2C_EEPROM | AD525X_RDAC1); | ||
224 | } | ||
225 | |||
226 | static ssize_t set_eeprom1(struct device *dev, | ||
227 | struct device_attribute *attr, | ||
228 | const char *buf, size_t count) | ||
229 | { | ||
230 | return sysfs_set_reg(dev, attr, buf, count, | ||
231 | AD525X_I2C_EEPROM | AD525X_RDAC1); | ||
232 | } | ||
233 | |||
234 | static DEVICE_ATTR(eeprom1, S_IWUSR | S_IRUGO, show_eeprom1, set_eeprom1); | ||
235 | |||
236 | static ssize_t show_tolerance1(struct device *dev, | ||
237 | struct device_attribute *attr, char *buf) | ||
238 | { | ||
239 | return sysfs_show_reg(dev, attr, buf, | ||
240 | AD525X_I2C_EEPROM | AD525X_TOL_RDAC1); | ||
241 | } | ||
242 | |||
243 | static DEVICE_ATTR(tolerance1, S_IRUGO, show_tolerance1, NULL); | ||
244 | |||
245 | /* ------------------------------------------------------------------------- */ | ||
246 | |||
247 | static ssize_t show_rdac2(struct device *dev, | ||
248 | struct device_attribute *attr, char *buf) | ||
249 | { | ||
250 | return sysfs_show_reg(dev, attr, buf, AD525X_I2C_RDAC | AD525X_RDAC2); | ||
251 | } | ||
252 | |||
253 | static ssize_t set_rdac2(struct device *dev, | ||
254 | struct device_attribute *attr, | ||
255 | const char *buf, size_t count) | ||
256 | { | ||
257 | return sysfs_set_reg(dev, attr, buf, count, | ||
258 | AD525X_I2C_RDAC | AD525X_RDAC2); | ||
259 | } | ||
260 | |||
261 | static DEVICE_ATTR(rdac2, S_IWUSR | S_IRUGO, show_rdac2, set_rdac2); | ||
262 | |||
263 | static ssize_t show_eeprom2(struct device *dev, | ||
264 | struct device_attribute *attr, char *buf) | ||
265 | { | ||
266 | return sysfs_show_reg(dev, attr, buf, AD525X_I2C_EEPROM | AD525X_RDAC2); | ||
267 | } | ||
268 | |||
269 | static ssize_t set_eeprom2(struct device *dev, | ||
270 | struct device_attribute *attr, | ||
271 | const char *buf, size_t count) | ||
272 | { | ||
273 | return sysfs_set_reg(dev, attr, buf, count, | ||
274 | AD525X_I2C_EEPROM | AD525X_RDAC2); | ||
275 | } | ||
276 | |||
277 | static DEVICE_ATTR(eeprom2, S_IWUSR | S_IRUGO, show_eeprom2, set_eeprom2); | ||
278 | |||
279 | static ssize_t show_tolerance2(struct device *dev, | ||
280 | struct device_attribute *attr, char *buf) | ||
281 | { | ||
282 | return sysfs_show_reg(dev, attr, buf, | ||
283 | AD525X_I2C_EEPROM | AD525X_TOL_RDAC2); | ||
284 | } | ||
285 | |||
286 | static DEVICE_ATTR(tolerance2, S_IRUGO, show_tolerance2, NULL); | ||
287 | |||
288 | /* ------------------------------------------------------------------------- */ | ||
289 | |||
290 | static ssize_t show_rdac3(struct device *dev, | ||
291 | struct device_attribute *attr, char *buf) | ||
292 | { | ||
293 | return sysfs_show_reg(dev, attr, buf, AD525X_I2C_RDAC | AD525X_RDAC3); | ||
294 | } | ||
295 | |||
296 | static ssize_t set_rdac3(struct device *dev, | ||
297 | struct device_attribute *attr, | ||
298 | const char *buf, size_t count) | ||
299 | { | ||
300 | return sysfs_set_reg(dev, attr, buf, count, | ||
301 | AD525X_I2C_RDAC | AD525X_RDAC3); | ||
302 | } | ||
303 | |||
304 | static DEVICE_ATTR(rdac3, S_IWUSR | S_IRUGO, show_rdac3, set_rdac3); | ||
305 | |||
306 | static ssize_t show_eeprom3(struct device *dev, | ||
307 | struct device_attribute *attr, char *buf) | ||
308 | { | ||
309 | return sysfs_show_reg(dev, attr, buf, AD525X_I2C_EEPROM | AD525X_RDAC3); | ||
310 | } | ||
311 | |||
312 | static ssize_t set_eeprom3(struct device *dev, | ||
313 | struct device_attribute *attr, | ||
314 | const char *buf, size_t count) | ||
315 | { | ||
316 | return sysfs_set_reg(dev, attr, buf, count, | ||
317 | AD525X_I2C_EEPROM | AD525X_RDAC3); | ||
318 | } | ||
319 | |||
320 | static DEVICE_ATTR(eeprom3, S_IWUSR | S_IRUGO, show_eeprom3, set_eeprom3); | ||
321 | |||
322 | static ssize_t show_tolerance3(struct device *dev, | ||
323 | struct device_attribute *attr, char *buf) | ||
324 | { | ||
325 | return sysfs_show_reg(dev, attr, buf, | ||
326 | AD525X_I2C_EEPROM | AD525X_TOL_RDAC3); | ||
327 | } | ||
328 | |||
329 | static DEVICE_ATTR(tolerance3, S_IRUGO, show_tolerance3, NULL); | ||
330 | |||
331 | static struct attribute *ad525x_attributes_wipers[4][4] = { | ||
332 | { | ||
333 | &dev_attr_rdac0.attr, | ||
334 | &dev_attr_eeprom0.attr, | ||
335 | &dev_attr_tolerance0.attr, | ||
336 | NULL | ||
337 | }, { | ||
338 | &dev_attr_rdac1.attr, | ||
339 | &dev_attr_eeprom1.attr, | ||
340 | &dev_attr_tolerance1.attr, | ||
341 | NULL | ||
342 | }, { | ||
343 | &dev_attr_rdac2.attr, | ||
344 | &dev_attr_eeprom2.attr, | ||
345 | &dev_attr_tolerance2.attr, | ||
346 | NULL | ||
347 | }, { | ||
348 | &dev_attr_rdac3.attr, | ||
349 | &dev_attr_eeprom3.attr, | ||
350 | &dev_attr_tolerance3.attr, | ||
351 | NULL | ||
352 | } | ||
353 | }; | ||
354 | |||
355 | static const struct attribute_group ad525x_group_wipers[] = { | ||
356 | {.attrs = ad525x_attributes_wipers[AD525X_RDAC0]}, | ||
357 | {.attrs = ad525x_attributes_wipers[AD525X_RDAC1]}, | ||
358 | {.attrs = ad525x_attributes_wipers[AD525X_RDAC2]}, | ||
359 | {.attrs = ad525x_attributes_wipers[AD525X_RDAC3]}, | ||
360 | }; | ||
361 | |||
362 | /* ------------------------------------------------------------------------- */ | ||
363 | |||
364 | static ssize_t set_inc_all(struct device *dev, | ||
365 | struct device_attribute *attr, | ||
366 | const char *buf, size_t count) | ||
367 | { | ||
368 | return sysfs_do_cmd(dev, attr, buf, count, AD525X_INC_ALL); | ||
369 | } | ||
370 | |||
371 | static DEVICE_ATTR(inc_all, S_IWUSR, NULL, set_inc_all); | ||
372 | |||
373 | static ssize_t set_dec_all(struct device *dev, | ||
374 | struct device_attribute *attr, | ||
375 | const char *buf, size_t count) | ||
376 | { | ||
377 | return sysfs_do_cmd(dev, attr, buf, count, AD525X_DEC_ALL); | ||
378 | } | ||
379 | |||
380 | static DEVICE_ATTR(dec_all, S_IWUSR, NULL, set_dec_all); | ||
381 | |||
382 | static ssize_t set_inc_all_6db(struct device *dev, | ||
383 | struct device_attribute *attr, | ||
384 | const char *buf, size_t count) | ||
385 | { | ||
386 | return sysfs_do_cmd(dev, attr, buf, count, AD525X_INC_ALL_6DB); | ||
387 | } | ||
388 | |||
389 | static DEVICE_ATTR(inc_all_6db, S_IWUSR, NULL, set_inc_all_6db); | ||
390 | |||
391 | static ssize_t set_dec_all_6db(struct device *dev, | ||
392 | struct device_attribute *attr, | ||
393 | const char *buf, size_t count) | ||
394 | { | ||
395 | return sysfs_do_cmd(dev, attr, buf, count, AD525X_DEC_ALL_6DB); | ||
396 | } | ||
397 | |||
398 | static DEVICE_ATTR(dec_all_6db, S_IWUSR, NULL, set_dec_all_6db); | ||
399 | |||
400 | static struct attribute *ad525x_attributes_commands[] = { | ||
401 | &dev_attr_inc_all.attr, | ||
402 | &dev_attr_dec_all.attr, | ||
403 | &dev_attr_inc_all_6db.attr, | ||
404 | &dev_attr_dec_all_6db.attr, | ||
405 | NULL | ||
406 | }; | ||
407 | |||
408 | static const struct attribute_group ad525x_group_commands = { | ||
409 | .attrs = ad525x_attributes_commands, | ||
410 | }; | ||
411 | |||
412 | /* ------------------------------------------------------------------------- */ | ||
413 | |||
414 | /* i2c device functions */ | ||
415 | |||
416 | /** | ||
417 | * ad525x_read - return the value contained in the specified register | ||
418 | * on the AD5258 device. | ||
419 | * @client: value returned from i2c_new_device() | ||
420 | * @reg: the register to read | ||
421 | * | ||
422 | * If the tolerance register is specified, 2 bytes are returned. | ||
423 | * Otherwise, 1 byte is returned. A negative value indicates an error | ||
424 | * occurred while reading the register. | ||
425 | */ | ||
426 | static s32 ad525x_read(struct i2c_client *client, u8 reg) | ||
427 | { | ||
428 | struct dpot_data *data = i2c_get_clientdata(client); | ||
429 | |||
430 | if ((reg & AD525X_REG_TOL) || (data->max_pos > 256)) | ||
431 | return i2c_smbus_read_word_data(client, (reg & 0xF8) | | ||
432 | ((reg & 0x7) << 1)); | ||
433 | else | ||
434 | return i2c_smbus_read_byte_data(client, reg); | ||
435 | } | ||
436 | |||
437 | /** | ||
438 | * ad525x_write - store the given value in the specified register on | ||
439 | * the AD5258 device. | ||
440 | * @client: value returned from i2c_new_device() | ||
441 | * @reg: the register to write | ||
442 | * @value: the byte to store in the register | ||
443 | * | ||
444 | * For certain instructions that do not require a data byte, "NULL" | ||
445 | * should be specified for the "value" parameter. These instructions | ||
446 | * include NOP, RESTORE_FROM_EEPROM, and STORE_TO_EEPROM. | ||
447 | * | ||
448 | * A negative return value indicates an error occurred while reading | ||
449 | * the register. | ||
450 | */ | ||
451 | static s32 ad525x_write(struct i2c_client *client, u8 reg, u8 value) | ||
452 | { | ||
453 | struct dpot_data *data = i2c_get_clientdata(client); | ||
454 | |||
455 | /* Only write the instruction byte for certain commands */ | ||
456 | if (reg & AD525X_I2C_CMD) | ||
457 | return i2c_smbus_write_byte(client, reg); | ||
458 | |||
459 | if (data->max_pos > 256) | ||
460 | return i2c_smbus_write_word_data(client, (reg & 0xF8) | | ||
461 | ((reg & 0x7) << 1), value); | ||
462 | else | ||
463 | /* All other registers require instruction + data bytes */ | ||
464 | return i2c_smbus_write_byte_data(client, reg, value); | ||
465 | } | ||
466 | |||
467 | static int ad525x_probe(struct i2c_client *client, | ||
468 | const struct i2c_device_id *id) | ||
469 | { | ||
470 | struct device *dev = &client->dev; | ||
471 | struct dpot_data *data; | ||
472 | int err = 0; | ||
473 | |||
474 | dev_dbg(dev, "%s\n", __func__); | ||
475 | |||
476 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)) { | ||
477 | dev_err(dev, "missing I2C functionality for this driver\n"); | ||
478 | goto exit; | ||
479 | } | ||
480 | |||
481 | data = kzalloc(sizeof(struct dpot_data), GFP_KERNEL); | ||
482 | if (!data) { | ||
483 | err = -ENOMEM; | ||
484 | goto exit; | ||
485 | } | ||
486 | |||
487 | i2c_set_clientdata(client, data); | ||
488 | mutex_init(&data->update_lock); | ||
489 | |||
490 | switch (id->driver_data) { | ||
491 | case AD5258_ID: | ||
492 | data->max_pos = AD5258_MAX_POSITION; | ||
493 | err = sysfs_create_group(&dev->kobj, | ||
494 | &ad525x_group_wipers[AD525X_RDAC0]); | ||
495 | break; | ||
496 | case AD5259_ID: | ||
497 | data->max_pos = AD5259_MAX_POSITION; | ||
498 | err = sysfs_create_group(&dev->kobj, | ||
499 | &ad525x_group_wipers[AD525X_RDAC0]); | ||
500 | break; | ||
501 | case AD5251_ID: | ||
502 | data->max_pos = AD5251_MAX_POSITION; | ||
503 | err = sysfs_create_group(&dev->kobj, | ||
504 | &ad525x_group_wipers[AD525X_RDAC1]); | ||
505 | err |= sysfs_create_group(&dev->kobj, | ||
506 | &ad525x_group_wipers[AD525X_RDAC3]); | ||
507 | err |= sysfs_create_group(&dev->kobj, &ad525x_group_commands); | ||
508 | break; | ||
509 | case AD5252_ID: | ||
510 | data->max_pos = AD5252_MAX_POSITION; | ||
511 | err = sysfs_create_group(&dev->kobj, | ||
512 | &ad525x_group_wipers[AD525X_RDAC1]); | ||
513 | err |= sysfs_create_group(&dev->kobj, | ||
514 | &ad525x_group_wipers[AD525X_RDAC3]); | ||
515 | err |= sysfs_create_group(&dev->kobj, &ad525x_group_commands); | ||
516 | break; | ||
517 | case AD5253_ID: | ||
518 | data->max_pos = AD5253_MAX_POSITION; | ||
519 | err = sysfs_create_group(&dev->kobj, | ||
520 | &ad525x_group_wipers[AD525X_RDAC0]); | ||
521 | err |= sysfs_create_group(&dev->kobj, | ||
522 | &ad525x_group_wipers[AD525X_RDAC1]); | ||
523 | err |= sysfs_create_group(&dev->kobj, | ||
524 | &ad525x_group_wipers[AD525X_RDAC2]); | ||
525 | err |= sysfs_create_group(&dev->kobj, | ||
526 | &ad525x_group_wipers[AD525X_RDAC3]); | ||
527 | err |= sysfs_create_group(&dev->kobj, &ad525x_group_commands); | ||
528 | break; | ||
529 | case AD5254_ID: | ||
530 | data->max_pos = AD5254_MAX_POSITION; | ||
531 | err = sysfs_create_group(&dev->kobj, | ||
532 | &ad525x_group_wipers[AD525X_RDAC0]); | ||
533 | err |= sysfs_create_group(&dev->kobj, | ||
534 | &ad525x_group_wipers[AD525X_RDAC1]); | ||
535 | err |= sysfs_create_group(&dev->kobj, | ||
536 | &ad525x_group_wipers[AD525X_RDAC2]); | ||
537 | err |= sysfs_create_group(&dev->kobj, | ||
538 | &ad525x_group_wipers[AD525X_RDAC3]); | ||
539 | err |= sysfs_create_group(&dev->kobj, &ad525x_group_commands); | ||
540 | break; | ||
541 | case AD5255_ID: | ||
542 | data->max_pos = AD5255_MAX_POSITION; | ||
543 | err = sysfs_create_group(&dev->kobj, | ||
544 | &ad525x_group_wipers[AD525X_RDAC0]); | ||
545 | err |= sysfs_create_group(&dev->kobj, | ||
546 | &ad525x_group_wipers[AD525X_RDAC1]); | ||
547 | err |= sysfs_create_group(&dev->kobj, | ||
548 | &ad525x_group_wipers[AD525X_RDAC2]); | ||
549 | err |= sysfs_create_group(&dev->kobj, &ad525x_group_commands); | ||
550 | break; | ||
551 | default: | ||
552 | err = -ENODEV; | ||
553 | goto exit_free; | ||
554 | } | ||
555 | |||
556 | if (err) { | ||
557 | dev_err(dev, "failed to register sysfs hooks\n"); | ||
558 | goto exit_free; | ||
559 | } | ||
560 | |||
561 | data->devid = id->driver_data; | ||
562 | data->rdac_mask = data->max_pos - 1; | ||
563 | |||
564 | dev_info(dev, "%s %d-Position Digital Potentiometer registered\n", | ||
565 | id->name, data->max_pos); | ||
566 | |||
567 | return 0; | ||
568 | |||
569 | exit_free: | ||
570 | kfree(data); | ||
571 | i2c_set_clientdata(client, NULL); | ||
572 | exit: | ||
573 | dev_err(dev, "failed to create client\n"); | ||
574 | return err; | ||
575 | } | ||
576 | |||
577 | static int __devexit ad525x_remove(struct i2c_client *client) | ||
578 | { | ||
579 | struct dpot_data *data = i2c_get_clientdata(client); | ||
580 | struct device *dev = &client->dev; | ||
581 | |||
582 | switch (data->devid) { | ||
583 | case AD5258_ID: | ||
584 | case AD5259_ID: | ||
585 | sysfs_remove_group(&dev->kobj, | ||
586 | &ad525x_group_wipers[AD525X_RDAC0]); | ||
587 | break; | ||
588 | case AD5251_ID: | ||
589 | case AD5252_ID: | ||
590 | sysfs_remove_group(&dev->kobj, | ||
591 | &ad525x_group_wipers[AD525X_RDAC1]); | ||
592 | sysfs_remove_group(&dev->kobj, | ||
593 | &ad525x_group_wipers[AD525X_RDAC3]); | ||
594 | sysfs_remove_group(&dev->kobj, &ad525x_group_commands); | ||
595 | break; | ||
596 | case AD5253_ID: | ||
597 | case AD5254_ID: | ||
598 | sysfs_remove_group(&dev->kobj, | ||
599 | &ad525x_group_wipers[AD525X_RDAC0]); | ||
600 | sysfs_remove_group(&dev->kobj, | ||
601 | &ad525x_group_wipers[AD525X_RDAC1]); | ||
602 | sysfs_remove_group(&dev->kobj, | ||
603 | &ad525x_group_wipers[AD525X_RDAC2]); | ||
604 | sysfs_remove_group(&dev->kobj, | ||
605 | &ad525x_group_wipers[AD525X_RDAC3]); | ||
606 | sysfs_remove_group(&dev->kobj, &ad525x_group_commands); | ||
607 | break; | ||
608 | case AD5255_ID: | ||
609 | sysfs_remove_group(&dev->kobj, | ||
610 | &ad525x_group_wipers[AD525X_RDAC0]); | ||
611 | sysfs_remove_group(&dev->kobj, | ||
612 | &ad525x_group_wipers[AD525X_RDAC1]); | ||
613 | sysfs_remove_group(&dev->kobj, | ||
614 | &ad525x_group_wipers[AD525X_RDAC2]); | ||
615 | sysfs_remove_group(&dev->kobj, &ad525x_group_commands); | ||
616 | break; | ||
617 | } | ||
618 | |||
619 | i2c_set_clientdata(client, NULL); | ||
620 | kfree(data); | ||
621 | |||
622 | return 0; | ||
623 | } | ||
624 | |||
625 | static const struct i2c_device_id ad525x_idtable[] = { | ||
626 | {"ad5258", AD5258_ID}, | ||
627 | {"ad5259", AD5259_ID}, | ||
628 | {"ad5251", AD5251_ID}, | ||
629 | {"ad5252", AD5252_ID}, | ||
630 | {"ad5253", AD5253_ID}, | ||
631 | {"ad5254", AD5254_ID}, | ||
632 | {"ad5255", AD5255_ID}, | ||
633 | {} | ||
634 | }; | ||
635 | |||
636 | MODULE_DEVICE_TABLE(i2c, ad525x_idtable); | ||
637 | |||
638 | static struct i2c_driver ad525x_driver = { | ||
639 | .driver = { | ||
640 | .owner = THIS_MODULE, | ||
641 | .name = DRIVER_NAME, | ||
642 | }, | ||
643 | .id_table = ad525x_idtable, | ||
644 | .probe = ad525x_probe, | ||
645 | .remove = __devexit_p(ad525x_remove), | ||
646 | }; | ||
647 | |||
648 | static int __init ad525x_init(void) | ||
649 | { | ||
650 | return i2c_add_driver(&ad525x_driver); | ||
651 | } | ||
652 | |||
653 | module_init(ad525x_init); | ||
654 | |||
655 | static void __exit ad525x_exit(void) | ||
656 | { | ||
657 | i2c_del_driver(&ad525x_driver); | ||
658 | } | ||
659 | |||
660 | module_exit(ad525x_exit); | ||
661 | |||
662 | MODULE_AUTHOR("Chris Verges <chrisv@cyberswitching.com>, " | ||
663 | "Michael Hennerich <hennerich@blackfin.uclinux.org>, "); | ||
664 | MODULE_DESCRIPTION("AD5258/9 digital potentiometer driver"); | ||
665 | MODULE_LICENSE("GPL"); | ||
666 | MODULE_VERSION(DRIVER_VERSION); | ||
diff --git a/drivers/misc/cs5535-mfgpt.c b/drivers/misc/cs5535-mfgpt.c new file mode 100644 index 000000000000..8110460558ff --- /dev/null +++ b/drivers/misc/cs5535-mfgpt.c | |||
@@ -0,0 +1,370 @@ | |||
1 | /* | ||
2 | * Driver for the CS5535/CS5536 Multi-Function General Purpose Timers (MFGPT) | ||
3 | * | ||
4 | * Copyright (C) 2006, Advanced Micro Devices, Inc. | ||
5 | * Copyright (C) 2007 Andres Salomon <dilinger@debian.org> | ||
6 | * Copyright (C) 2009 Andres Salomon <dilinger@collabora.co.uk> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of version 2 of the GNU General Public License | ||
10 | * as published by the Free Software Foundation. | ||
11 | * | ||
12 | * The MFGPTs are documented in AMD Geode CS5536 Companion Device Data Book. | ||
13 | */ | ||
14 | |||
15 | #include <linux/kernel.h> | ||
16 | #include <linux/spinlock.h> | ||
17 | #include <linux/interrupt.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/pci.h> | ||
20 | #include <linux/cs5535.h> | ||
21 | |||
22 | #define DRV_NAME "cs5535-mfgpt" | ||
23 | #define MFGPT_BAR 2 | ||
24 | |||
25 | static int mfgpt_reset_timers; | ||
26 | module_param_named(mfgptfix, mfgpt_reset_timers, int, 0644); | ||
27 | MODULE_PARM_DESC(mfgptfix, "Reset the MFGPT timers during init; " | ||
28 | "required by some broken BIOSes (ie, TinyBIOS < 0.99)."); | ||
29 | |||
30 | struct cs5535_mfgpt_timer { | ||
31 | struct cs5535_mfgpt_chip *chip; | ||
32 | int nr; | ||
33 | }; | ||
34 | |||
35 | static struct cs5535_mfgpt_chip { | ||
36 | DECLARE_BITMAP(avail, MFGPT_MAX_TIMERS); | ||
37 | resource_size_t base; | ||
38 | |||
39 | struct pci_dev *pdev; | ||
40 | spinlock_t lock; | ||
41 | int initialized; | ||
42 | } cs5535_mfgpt_chip; | ||
43 | |||
44 | int cs5535_mfgpt_toggle_event(struct cs5535_mfgpt_timer *timer, int cmp, | ||
45 | int event, int enable) | ||
46 | { | ||
47 | uint32_t msr, mask, value, dummy; | ||
48 | int shift = (cmp == MFGPT_CMP1) ? 0 : 8; | ||
49 | |||
50 | if (!timer) { | ||
51 | WARN_ON(1); | ||
52 | return -EIO; | ||
53 | } | ||
54 | |||
55 | /* | ||
56 | * The register maps for these are described in sections 6.17.1.x of | ||
57 | * the AMD Geode CS5536 Companion Device Data Book. | ||
58 | */ | ||
59 | switch (event) { | ||
60 | case MFGPT_EVENT_RESET: | ||
61 | /* | ||
62 | * XXX: According to the docs, we cannot reset timers above | ||
63 | * 6; that is, resets for 7 and 8 will be ignored. Is this | ||
64 | * a problem? -dilinger | ||
65 | */ | ||
66 | msr = MSR_MFGPT_NR; | ||
67 | mask = 1 << (timer->nr + 24); | ||
68 | break; | ||
69 | |||
70 | case MFGPT_EVENT_NMI: | ||
71 | msr = MSR_MFGPT_NR; | ||
72 | mask = 1 << (timer->nr + shift); | ||
73 | break; | ||
74 | |||
75 | case MFGPT_EVENT_IRQ: | ||
76 | msr = MSR_MFGPT_IRQ; | ||
77 | mask = 1 << (timer->nr + shift); | ||
78 | break; | ||
79 | |||
80 | default: | ||
81 | return -EIO; | ||
82 | } | ||
83 | |||
84 | rdmsr(msr, value, dummy); | ||
85 | |||
86 | if (enable) | ||
87 | value |= mask; | ||
88 | else | ||
89 | value &= ~mask; | ||
90 | |||
91 | wrmsr(msr, value, dummy); | ||
92 | return 0; | ||
93 | } | ||
94 | EXPORT_SYMBOL_GPL(cs5535_mfgpt_toggle_event); | ||
95 | |||
96 | int cs5535_mfgpt_set_irq(struct cs5535_mfgpt_timer *timer, int cmp, int *irq, | ||
97 | int enable) | ||
98 | { | ||
99 | uint32_t zsel, lpc, dummy; | ||
100 | int shift; | ||
101 | |||
102 | if (!timer) { | ||
103 | WARN_ON(1); | ||
104 | return -EIO; | ||
105 | } | ||
106 | |||
107 | /* | ||
108 | * Unfortunately, MFGPTs come in pairs sharing their IRQ lines. If VSA | ||
109 | * is using the same CMP of the timer's Siamese twin, the IRQ is set to | ||
110 | * 2, and we mustn't use nor change it. | ||
111 | * XXX: Likewise, 2 Linux drivers might clash if the 2nd overwrites the | ||
112 | * IRQ of the 1st. This can only happen if forcing an IRQ, calling this | ||
113 | * with *irq==0 is safe. Currently there _are_ no 2 drivers. | ||
114 | */ | ||
115 | rdmsr(MSR_PIC_ZSEL_LOW, zsel, dummy); | ||
116 | shift = ((cmp == MFGPT_CMP1 ? 0 : 4) + timer->nr % 4) * 4; | ||
117 | if (((zsel >> shift) & 0xF) == 2) | ||
118 | return -EIO; | ||
119 | |||
120 | /* Choose IRQ: if none supplied, keep IRQ already set or use default */ | ||
121 | if (!*irq) | ||
122 | *irq = (zsel >> shift) & 0xF; | ||
123 | if (!*irq) | ||
124 | *irq = CONFIG_CS5535_MFGPT_DEFAULT_IRQ; | ||
125 | |||
126 | /* Can't use IRQ if it's 0 (=disabled), 2, or routed to LPC */ | ||
127 | if (*irq < 1 || *irq == 2 || *irq > 15) | ||
128 | return -EIO; | ||
129 | rdmsr(MSR_PIC_IRQM_LPC, lpc, dummy); | ||
130 | if (lpc & (1 << *irq)) | ||
131 | return -EIO; | ||
132 | |||
133 | /* All chosen and checked - go for it */ | ||
134 | if (cs5535_mfgpt_toggle_event(timer, cmp, MFGPT_EVENT_IRQ, enable)) | ||
135 | return -EIO; | ||
136 | if (enable) { | ||
137 | zsel = (zsel & ~(0xF << shift)) | (*irq << shift); | ||
138 | wrmsr(MSR_PIC_ZSEL_LOW, zsel, dummy); | ||
139 | } | ||
140 | |||
141 | return 0; | ||
142 | } | ||
143 | EXPORT_SYMBOL_GPL(cs5535_mfgpt_set_irq); | ||
144 | |||
145 | struct cs5535_mfgpt_timer *cs5535_mfgpt_alloc_timer(int timer_nr, int domain) | ||
146 | { | ||
147 | struct cs5535_mfgpt_chip *mfgpt = &cs5535_mfgpt_chip; | ||
148 | struct cs5535_mfgpt_timer *timer = NULL; | ||
149 | unsigned long flags; | ||
150 | int max; | ||
151 | |||
152 | if (!mfgpt->initialized) | ||
153 | goto done; | ||
154 | |||
155 | /* only allocate timers from the working domain if requested */ | ||
156 | if (domain == MFGPT_DOMAIN_WORKING) | ||
157 | max = 6; | ||
158 | else | ||
159 | max = MFGPT_MAX_TIMERS; | ||
160 | |||
161 | if (timer_nr >= max) { | ||
162 | /* programmer error. silly programmers! */ | ||
163 | WARN_ON(1); | ||
164 | goto done; | ||
165 | } | ||
166 | |||
167 | spin_lock_irqsave(&mfgpt->lock, flags); | ||
168 | if (timer_nr < 0) { | ||
169 | unsigned long t; | ||
170 | |||
171 | /* try to find any available timer */ | ||
172 | t = find_first_bit(mfgpt->avail, max); | ||
173 | /* set timer_nr to -1 if no timers available */ | ||
174 | timer_nr = t < max ? (int) t : -1; | ||
175 | } else { | ||
176 | /* check if the requested timer's available */ | ||
177 | if (test_bit(timer_nr, mfgpt->avail)) | ||
178 | timer_nr = -1; | ||
179 | } | ||
180 | |||
181 | if (timer_nr >= 0) | ||
182 | /* if timer_nr is not -1, it's an available timer */ | ||
183 | __clear_bit(timer_nr, mfgpt->avail); | ||
184 | spin_unlock_irqrestore(&mfgpt->lock, flags); | ||
185 | |||
186 | if (timer_nr < 0) | ||
187 | goto done; | ||
188 | |||
189 | timer = kmalloc(sizeof(*timer), GFP_KERNEL); | ||
190 | if (!timer) { | ||
191 | /* aw hell */ | ||
192 | spin_lock_irqsave(&mfgpt->lock, flags); | ||
193 | __set_bit(timer_nr, mfgpt->avail); | ||
194 | spin_unlock_irqrestore(&mfgpt->lock, flags); | ||
195 | goto done; | ||
196 | } | ||
197 | timer->chip = mfgpt; | ||
198 | timer->nr = timer_nr; | ||
199 | dev_info(&mfgpt->pdev->dev, "registered timer %d\n", timer_nr); | ||
200 | |||
201 | done: | ||
202 | return timer; | ||
203 | } | ||
204 | EXPORT_SYMBOL_GPL(cs5535_mfgpt_alloc_timer); | ||
205 | |||
206 | /* | ||
207 | * XXX: This frees the timer memory, but never resets the actual hardware | ||
208 | * timer. The old geode_mfgpt code did this; it would be good to figure | ||
209 | * out a way to actually release the hardware timer. See comments below. | ||
210 | */ | ||
211 | void cs5535_mfgpt_free_timer(struct cs5535_mfgpt_timer *timer) | ||
212 | { | ||
213 | kfree(timer); | ||
214 | } | ||
215 | EXPORT_SYMBOL_GPL(cs5535_mfgpt_free_timer); | ||
216 | |||
217 | uint16_t cs5535_mfgpt_read(struct cs5535_mfgpt_timer *timer, uint16_t reg) | ||
218 | { | ||
219 | return inw(timer->chip->base + reg + (timer->nr * 8)); | ||
220 | } | ||
221 | EXPORT_SYMBOL_GPL(cs5535_mfgpt_read); | ||
222 | |||
223 | void cs5535_mfgpt_write(struct cs5535_mfgpt_timer *timer, uint16_t reg, | ||
224 | uint16_t value) | ||
225 | { | ||
226 | outw(value, timer->chip->base + reg + (timer->nr * 8)); | ||
227 | } | ||
228 | EXPORT_SYMBOL_GPL(cs5535_mfgpt_write); | ||
229 | |||
230 | /* | ||
231 | * This is a sledgehammer that resets all MFGPT timers. This is required by | ||
232 | * some broken BIOSes which leave the system in an unstable state | ||
233 | * (TinyBIOS 0.98, for example; fixed in 0.99). It's uncertain as to | ||
234 | * whether or not this secret MSR can be used to release individual timers. | ||
235 | * Jordan tells me that he and Mitch once played w/ it, but it's unclear | ||
236 | * what the results of that were (and they experienced some instability). | ||
237 | */ | ||
238 | static void __init reset_all_timers(void) | ||
239 | { | ||
240 | uint32_t val, dummy; | ||
241 | |||
242 | /* The following undocumented bit resets the MFGPT timers */ | ||
243 | val = 0xFF; dummy = 0; | ||
244 | wrmsr(MSR_MFGPT_SETUP, val, dummy); | ||
245 | } | ||
246 | |||
247 | /* | ||
248 | * Check whether any MFGPTs are available for the kernel to use. In most | ||
249 | * cases, firmware that uses AMD's VSA code will claim all timers during | ||
250 | * bootup; we certainly don't want to take them if they're already in use. | ||
251 | * In other cases (such as with VSAless OpenFirmware), the system firmware | ||
252 | * leaves timers available for us to use. | ||
253 | */ | ||
254 | static int __init scan_timers(struct cs5535_mfgpt_chip *mfgpt) | ||
255 | { | ||
256 | struct cs5535_mfgpt_timer timer = { .chip = mfgpt }; | ||
257 | unsigned long flags; | ||
258 | int timers = 0; | ||
259 | uint16_t val; | ||
260 | int i; | ||
261 | |||
262 | /* bios workaround */ | ||
263 | if (mfgpt_reset_timers) | ||
264 | reset_all_timers(); | ||
265 | |||
266 | /* just to be safe, protect this section w/ lock */ | ||
267 | spin_lock_irqsave(&mfgpt->lock, flags); | ||
268 | for (i = 0; i < MFGPT_MAX_TIMERS; i++) { | ||
269 | timer.nr = i; | ||
270 | val = cs5535_mfgpt_read(&timer, MFGPT_REG_SETUP); | ||
271 | if (!(val & MFGPT_SETUP_SETUP)) { | ||
272 | __set_bit(i, mfgpt->avail); | ||
273 | timers++; | ||
274 | } | ||
275 | } | ||
276 | spin_unlock_irqrestore(&mfgpt->lock, flags); | ||
277 | |||
278 | return timers; | ||
279 | } | ||
280 | |||
281 | static int __init cs5535_mfgpt_probe(struct pci_dev *pdev, | ||
282 | const struct pci_device_id *pci_id) | ||
283 | { | ||
284 | int err, t; | ||
285 | |||
286 | /* There are two ways to get the MFGPT base address; one is by | ||
287 | * fetching it from MSR_LBAR_MFGPT, the other is by reading the | ||
288 | * PCI BAR info. The latter method is easier (especially across | ||
289 | * different architectures), so we'll stick with that for now. If | ||
290 | * it turns out to be unreliable in the face of crappy BIOSes, we | ||
291 | * can always go back to using MSRs.. */ | ||
292 | |||
293 | err = pci_enable_device_io(pdev); | ||
294 | if (err) { | ||
295 | dev_err(&pdev->dev, "can't enable device IO\n"); | ||
296 | goto done; | ||
297 | } | ||
298 | |||
299 | err = pci_request_region(pdev, MFGPT_BAR, DRV_NAME); | ||
300 | if (err) { | ||
301 | dev_err(&pdev->dev, "can't alloc PCI BAR #%d\n", MFGPT_BAR); | ||
302 | goto done; | ||
303 | } | ||
304 | |||
305 | /* set up the driver-specific struct */ | ||
306 | cs5535_mfgpt_chip.base = pci_resource_start(pdev, MFGPT_BAR); | ||
307 | cs5535_mfgpt_chip.pdev = pdev; | ||
308 | spin_lock_init(&cs5535_mfgpt_chip.lock); | ||
309 | |||
310 | dev_info(&pdev->dev, "allocated PCI BAR #%d: base 0x%llx\n", MFGPT_BAR, | ||
311 | (unsigned long long) cs5535_mfgpt_chip.base); | ||
312 | |||
313 | /* detect the available timers */ | ||
314 | t = scan_timers(&cs5535_mfgpt_chip); | ||
315 | dev_info(&pdev->dev, DRV_NAME ": %d MFGPT timers available\n", t); | ||
316 | cs5535_mfgpt_chip.initialized = 1; | ||
317 | return 0; | ||
318 | |||
319 | done: | ||
320 | return err; | ||
321 | } | ||
322 | |||
323 | static struct pci_device_id cs5535_mfgpt_pci_tbl[] = { | ||
324 | { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_ISA) }, | ||
325 | { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA) }, | ||
326 | { 0, }, | ||
327 | }; | ||
328 | MODULE_DEVICE_TABLE(pci, cs5535_mfgpt_pci_tbl); | ||
329 | |||
330 | /* | ||
331 | * Just like with the cs5535-gpio driver, we can't use the standard PCI driver | ||
332 | * registration stuff. It only allows only one driver to bind to each PCI | ||
333 | * device, and we want the GPIO and MFGPT drivers to be able to share a PCI | ||
334 | * device. Instead, we manually scan for the PCI device, request a single | ||
335 | * region, and keep track of the devices that we're using. | ||
336 | */ | ||
337 | |||
338 | static int __init cs5535_mfgpt_scan_pci(void) | ||
339 | { | ||
340 | struct pci_dev *pdev; | ||
341 | int err = -ENODEV; | ||
342 | int i; | ||
343 | |||
344 | for (i = 0; i < ARRAY_SIZE(cs5535_mfgpt_pci_tbl); i++) { | ||
345 | pdev = pci_get_device(cs5535_mfgpt_pci_tbl[i].vendor, | ||
346 | cs5535_mfgpt_pci_tbl[i].device, NULL); | ||
347 | if (pdev) { | ||
348 | err = cs5535_mfgpt_probe(pdev, | ||
349 | &cs5535_mfgpt_pci_tbl[i]); | ||
350 | if (err) | ||
351 | pci_dev_put(pdev); | ||
352 | |||
353 | /* we only support a single CS5535/6 southbridge */ | ||
354 | break; | ||
355 | } | ||
356 | } | ||
357 | |||
358 | return err; | ||
359 | } | ||
360 | |||
361 | static int __init cs5535_mfgpt_init(void) | ||
362 | { | ||
363 | return cs5535_mfgpt_scan_pci(); | ||
364 | } | ||
365 | |||
366 | module_init(cs5535_mfgpt_init); | ||
367 | |||
368 | MODULE_AUTHOR("Andres Salomon <dilinger@collabora.co.uk>"); | ||
369 | MODULE_DESCRIPTION("CS5535/CS5536 MFGPT timer driver"); | ||
370 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/misc/ds1682.c b/drivers/misc/ds1682.c new file mode 100644 index 000000000000..f3ee4a1abb77 --- /dev/null +++ b/drivers/misc/ds1682.c | |||
@@ -0,0 +1,267 @@ | |||
1 | /* | ||
2 | * Dallas Semiconductor DS1682 Elapsed Time Recorder device driver | ||
3 | * | ||
4 | * Written by: Grant Likely <grant.likely@secretlab.ca> | ||
5 | * | ||
6 | * Copyright (C) 2007 Secret Lab Technologies Ltd. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | /* | ||
14 | * The DS1682 elapsed timer recorder is a simple device that implements | ||
15 | * one elapsed time counter, one event counter, an alarm signal and 10 | ||
16 | * bytes of general purpose EEPROM. | ||
17 | * | ||
18 | * This driver provides access to the DS1682 counters and user data via | ||
19 | * the sysfs. The following attributes are added to the device node: | ||
20 | * elapsed_time (u32): Total elapsed event time in ms resolution | ||
21 | * alarm_time (u32): When elapsed time exceeds the value in alarm_time, | ||
22 | * then the alarm pin is asserted. | ||
23 | * event_count (u16): number of times the event pin has gone low. | ||
24 | * eeprom (u8[10]): general purpose EEPROM | ||
25 | * | ||
26 | * Counter registers and user data are both read/write unless the device | ||
27 | * has been write protected. This driver does not support turning off write | ||
28 | * protection. Once write protection is turned on, it is impossible to | ||
29 | * turn it off again, so I have left the feature out of this driver to avoid | ||
30 | * accidental enabling, but it is trivial to add write protect support. | ||
31 | * | ||
32 | */ | ||
33 | |||
34 | #include <linux/module.h> | ||
35 | #include <linux/init.h> | ||
36 | #include <linux/slab.h> | ||
37 | #include <linux/i2c.h> | ||
38 | #include <linux/string.h> | ||
39 | #include <linux/list.h> | ||
40 | #include <linux/sysfs.h> | ||
41 | #include <linux/ctype.h> | ||
42 | #include <linux/hwmon-sysfs.h> | ||
43 | |||
44 | /* Device registers */ | ||
45 | #define DS1682_REG_CONFIG 0x00 | ||
46 | #define DS1682_REG_ALARM 0x01 | ||
47 | #define DS1682_REG_ELAPSED 0x05 | ||
48 | #define DS1682_REG_EVT_CNTR 0x09 | ||
49 | #define DS1682_REG_EEPROM 0x0b | ||
50 | #define DS1682_REG_RESET 0x1d | ||
51 | #define DS1682_REG_WRITE_DISABLE 0x1e | ||
52 | #define DS1682_REG_WRITE_MEM_DISABLE 0x1f | ||
53 | |||
54 | #define DS1682_EEPROM_SIZE 10 | ||
55 | |||
56 | /* | ||
57 | * Generic counter attributes | ||
58 | */ | ||
59 | static ssize_t ds1682_show(struct device *dev, struct device_attribute *attr, | ||
60 | char *buf) | ||
61 | { | ||
62 | struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); | ||
63 | struct i2c_client *client = to_i2c_client(dev); | ||
64 | __le32 val = 0; | ||
65 | int rc; | ||
66 | |||
67 | dev_dbg(dev, "ds1682_show() called on %s\n", attr->attr.name); | ||
68 | |||
69 | /* Read the register */ | ||
70 | rc = i2c_smbus_read_i2c_block_data(client, sattr->index, sattr->nr, | ||
71 | (u8 *) & val); | ||
72 | if (rc < 0) | ||
73 | return -EIO; | ||
74 | |||
75 | /* Special case: the 32 bit regs are time values with 1/4s | ||
76 | * resolution, scale them up to milliseconds */ | ||
77 | if (sattr->nr == 4) | ||
78 | return sprintf(buf, "%llu\n", | ||
79 | ((unsigned long long)le32_to_cpu(val)) * 250); | ||
80 | |||
81 | /* Format the output string and return # of bytes */ | ||
82 | return sprintf(buf, "%li\n", (long)le32_to_cpu(val)); | ||
83 | } | ||
84 | |||
85 | static ssize_t ds1682_store(struct device *dev, struct device_attribute *attr, | ||
86 | const char *buf, size_t count) | ||
87 | { | ||
88 | struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); | ||
89 | struct i2c_client *client = to_i2c_client(dev); | ||
90 | char *endp; | ||
91 | u64 val; | ||
92 | __le32 val_le; | ||
93 | int rc; | ||
94 | |||
95 | dev_dbg(dev, "ds1682_store() called on %s\n", attr->attr.name); | ||
96 | |||
97 | /* Decode input */ | ||
98 | val = simple_strtoull(buf, &endp, 0); | ||
99 | if (buf == endp) { | ||
100 | dev_dbg(dev, "input string not a number\n"); | ||
101 | return -EINVAL; | ||
102 | } | ||
103 | |||
104 | /* Special case: the 32 bit regs are time values with 1/4s | ||
105 | * resolution, scale input down to quarter-seconds */ | ||
106 | if (sattr->nr == 4) | ||
107 | do_div(val, 250); | ||
108 | |||
109 | /* write out the value */ | ||
110 | val_le = cpu_to_le32(val); | ||
111 | rc = i2c_smbus_write_i2c_block_data(client, sattr->index, sattr->nr, | ||
112 | (u8 *) & val_le); | ||
113 | if (rc < 0) { | ||
114 | dev_err(dev, "register write failed; reg=0x%x, size=%i\n", | ||
115 | sattr->index, sattr->nr); | ||
116 | return -EIO; | ||
117 | } | ||
118 | |||
119 | return count; | ||
120 | } | ||
121 | |||
122 | /* | ||
123 | * Simple register attributes | ||
124 | */ | ||
125 | static SENSOR_DEVICE_ATTR_2(elapsed_time, S_IRUGO | S_IWUSR, ds1682_show, | ||
126 | ds1682_store, 4, DS1682_REG_ELAPSED); | ||
127 | static SENSOR_DEVICE_ATTR_2(alarm_time, S_IRUGO | S_IWUSR, ds1682_show, | ||
128 | ds1682_store, 4, DS1682_REG_ALARM); | ||
129 | static SENSOR_DEVICE_ATTR_2(event_count, S_IRUGO | S_IWUSR, ds1682_show, | ||
130 | ds1682_store, 2, DS1682_REG_EVT_CNTR); | ||
131 | |||
132 | static const struct attribute_group ds1682_group = { | ||
133 | .attrs = (struct attribute *[]) { | ||
134 | &sensor_dev_attr_elapsed_time.dev_attr.attr, | ||
135 | &sensor_dev_attr_alarm_time.dev_attr.attr, | ||
136 | &sensor_dev_attr_event_count.dev_attr.attr, | ||
137 | NULL, | ||
138 | }, | ||
139 | }; | ||
140 | |||
141 | /* | ||
142 | * User data attribute | ||
143 | */ | ||
144 | static ssize_t ds1682_eeprom_read(struct kobject *kobj, struct bin_attribute *attr, | ||
145 | char *buf, loff_t off, size_t count) | ||
146 | { | ||
147 | struct i2c_client *client = kobj_to_i2c_client(kobj); | ||
148 | int rc; | ||
149 | |||
150 | dev_dbg(&client->dev, "ds1682_eeprom_read(p=%p, off=%lli, c=%zi)\n", | ||
151 | buf, off, count); | ||
152 | |||
153 | if (off >= DS1682_EEPROM_SIZE) | ||
154 | return 0; | ||
155 | |||
156 | if (off + count > DS1682_EEPROM_SIZE) | ||
157 | count = DS1682_EEPROM_SIZE - off; | ||
158 | |||
159 | rc = i2c_smbus_read_i2c_block_data(client, DS1682_REG_EEPROM + off, | ||
160 | count, buf); | ||
161 | if (rc < 0) | ||
162 | return -EIO; | ||
163 | |||
164 | return count; | ||
165 | } | ||
166 | |||
167 | static ssize_t ds1682_eeprom_write(struct kobject *kobj, struct bin_attribute *attr, | ||
168 | char *buf, loff_t off, size_t count) | ||
169 | { | ||
170 | struct i2c_client *client = kobj_to_i2c_client(kobj); | ||
171 | |||
172 | dev_dbg(&client->dev, "ds1682_eeprom_write(p=%p, off=%lli, c=%zi)\n", | ||
173 | buf, off, count); | ||
174 | |||
175 | if (off >= DS1682_EEPROM_SIZE) | ||
176 | return -ENOSPC; | ||
177 | |||
178 | if (off + count > DS1682_EEPROM_SIZE) | ||
179 | count = DS1682_EEPROM_SIZE - off; | ||
180 | |||
181 | /* Write out to the device */ | ||
182 | if (i2c_smbus_write_i2c_block_data(client, DS1682_REG_EEPROM + off, | ||
183 | count, buf) < 0) | ||
184 | return -EIO; | ||
185 | |||
186 | return count; | ||
187 | } | ||
188 | |||
189 | static struct bin_attribute ds1682_eeprom_attr = { | ||
190 | .attr = { | ||
191 | .name = "eeprom", | ||
192 | .mode = S_IRUGO | S_IWUSR, | ||
193 | }, | ||
194 | .size = DS1682_EEPROM_SIZE, | ||
195 | .read = ds1682_eeprom_read, | ||
196 | .write = ds1682_eeprom_write, | ||
197 | }; | ||
198 | |||
199 | /* | ||
200 | * Called when a ds1682 device is matched with this driver | ||
201 | */ | ||
202 | static int ds1682_probe(struct i2c_client *client, | ||
203 | const struct i2c_device_id *id) | ||
204 | { | ||
205 | int rc; | ||
206 | |||
207 | if (!i2c_check_functionality(client->adapter, | ||
208 | I2C_FUNC_SMBUS_I2C_BLOCK)) { | ||
209 | dev_err(&client->dev, "i2c bus does not support the ds1682\n"); | ||
210 | rc = -ENODEV; | ||
211 | goto exit; | ||
212 | } | ||
213 | |||
214 | rc = sysfs_create_group(&client->dev.kobj, &ds1682_group); | ||
215 | if (rc) | ||
216 | goto exit; | ||
217 | |||
218 | rc = sysfs_create_bin_file(&client->dev.kobj, &ds1682_eeprom_attr); | ||
219 | if (rc) | ||
220 | goto exit_bin_attr; | ||
221 | |||
222 | return 0; | ||
223 | |||
224 | exit_bin_attr: | ||
225 | sysfs_remove_group(&client->dev.kobj, &ds1682_group); | ||
226 | exit: | ||
227 | return rc; | ||
228 | } | ||
229 | |||
230 | static int ds1682_remove(struct i2c_client *client) | ||
231 | { | ||
232 | sysfs_remove_bin_file(&client->dev.kobj, &ds1682_eeprom_attr); | ||
233 | sysfs_remove_group(&client->dev.kobj, &ds1682_group); | ||
234 | return 0; | ||
235 | } | ||
236 | |||
237 | static const struct i2c_device_id ds1682_id[] = { | ||
238 | { "ds1682", 0 }, | ||
239 | { } | ||
240 | }; | ||
241 | MODULE_DEVICE_TABLE(i2c, ds1682_id); | ||
242 | |||
243 | static struct i2c_driver ds1682_driver = { | ||
244 | .driver = { | ||
245 | .name = "ds1682", | ||
246 | }, | ||
247 | .probe = ds1682_probe, | ||
248 | .remove = ds1682_remove, | ||
249 | .id_table = ds1682_id, | ||
250 | }; | ||
251 | |||
252 | static int __init ds1682_init(void) | ||
253 | { | ||
254 | return i2c_add_driver(&ds1682_driver); | ||
255 | } | ||
256 | |||
257 | static void __exit ds1682_exit(void) | ||
258 | { | ||
259 | i2c_del_driver(&ds1682_driver); | ||
260 | } | ||
261 | |||
262 | MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>"); | ||
263 | MODULE_DESCRIPTION("DS1682 Elapsed Time Indicator driver"); | ||
264 | MODULE_LICENSE("GPL"); | ||
265 | |||
266 | module_init(ds1682_init); | ||
267 | module_exit(ds1682_exit); | ||
diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index db39f4a52f53..2cb2736d65aa 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c | |||
@@ -158,6 +158,7 @@ static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf, | |||
158 | struct i2c_msg msg[2]; | 158 | struct i2c_msg msg[2]; |
159 | u8 msgbuf[2]; | 159 | u8 msgbuf[2]; |
160 | struct i2c_client *client; | 160 | struct i2c_client *client; |
161 | unsigned long timeout, read_time; | ||
161 | int status, i; | 162 | int status, i; |
162 | 163 | ||
163 | memset(msg, 0, sizeof(msg)); | 164 | memset(msg, 0, sizeof(msg)); |
@@ -183,47 +184,60 @@ static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf, | |||
183 | if (count > io_limit) | 184 | if (count > io_limit) |
184 | count = io_limit; | 185 | count = io_limit; |
185 | 186 | ||
186 | /* Smaller eeproms can work given some SMBus extension calls */ | ||
187 | if (at24->use_smbus) { | 187 | if (at24->use_smbus) { |
188 | /* Smaller eeproms can work given some SMBus extension calls */ | ||
188 | if (count > I2C_SMBUS_BLOCK_MAX) | 189 | if (count > I2C_SMBUS_BLOCK_MAX) |
189 | count = I2C_SMBUS_BLOCK_MAX; | 190 | count = I2C_SMBUS_BLOCK_MAX; |
190 | status = i2c_smbus_read_i2c_block_data(client, offset, | 191 | } else { |
191 | count, buf); | 192 | /* |
192 | dev_dbg(&client->dev, "smbus read %zu@%d --> %d\n", | 193 | * When we have a better choice than SMBus calls, use a |
193 | count, offset, status); | 194 | * combined I2C message. Write address; then read up to |
194 | return (status < 0) ? -EIO : status; | 195 | * io_limit data bytes. Note that read page rollover helps us |
196 | * here (unlike writes). msgbuf is u8 and will cast to our | ||
197 | * needs. | ||
198 | */ | ||
199 | i = 0; | ||
200 | if (at24->chip.flags & AT24_FLAG_ADDR16) | ||
201 | msgbuf[i++] = offset >> 8; | ||
202 | msgbuf[i++] = offset; | ||
203 | |||
204 | msg[0].addr = client->addr; | ||
205 | msg[0].buf = msgbuf; | ||
206 | msg[0].len = i; | ||
207 | |||
208 | msg[1].addr = client->addr; | ||
209 | msg[1].flags = I2C_M_RD; | ||
210 | msg[1].buf = buf; | ||
211 | msg[1].len = count; | ||
195 | } | 212 | } |
196 | 213 | ||
197 | /* | 214 | /* |
198 | * When we have a better choice than SMBus calls, use a combined | 215 | * Reads fail if the previous write didn't complete yet. We may |
199 | * I2C message. Write address; then read up to io_limit data bytes. | 216 | * loop a few times until this one succeeds, waiting at least |
200 | * Note that read page rollover helps us here (unlike writes). | 217 | * long enough for one entire page write to work. |
201 | * msgbuf is u8 and will cast to our needs. | ||
202 | */ | 218 | */ |
203 | i = 0; | 219 | timeout = jiffies + msecs_to_jiffies(write_timeout); |
204 | if (at24->chip.flags & AT24_FLAG_ADDR16) | 220 | do { |
205 | msgbuf[i++] = offset >> 8; | 221 | read_time = jiffies; |
206 | msgbuf[i++] = offset; | 222 | if (at24->use_smbus) { |
207 | 223 | status = i2c_smbus_read_i2c_block_data(client, offset, | |
208 | msg[0].addr = client->addr; | 224 | count, buf); |
209 | msg[0].buf = msgbuf; | 225 | } else { |
210 | msg[0].len = i; | 226 | status = i2c_transfer(client->adapter, msg, 2); |
227 | if (status == 2) | ||
228 | status = count; | ||
229 | } | ||
230 | dev_dbg(&client->dev, "read %zu@%d --> %d (%ld)\n", | ||
231 | count, offset, status, jiffies); | ||
211 | 232 | ||
212 | msg[1].addr = client->addr; | 233 | if (status == count) |
213 | msg[1].flags = I2C_M_RD; | 234 | return count; |
214 | msg[1].buf = buf; | ||
215 | msg[1].len = count; | ||
216 | 235 | ||
217 | status = i2c_transfer(client->adapter, msg, 2); | 236 | /* REVISIT: at HZ=100, this is sloooow */ |
218 | dev_dbg(&client->dev, "i2c read %zu@%d --> %d\n", | 237 | msleep(1); |
219 | count, offset, status); | 238 | } while (time_before(read_time, timeout)); |
220 | 239 | ||
221 | if (status == 2) | 240 | return -ETIMEDOUT; |
222 | return count; | ||
223 | else if (status >= 0) | ||
224 | return -EIO; | ||
225 | else | ||
226 | return status; | ||
227 | } | 241 | } |
228 | 242 | ||
229 | static ssize_t at24_read(struct at24_data *at24, | 243 | static ssize_t at24_read(struct at24_data *at24, |
diff --git a/drivers/misc/eeprom/eeprom.c b/drivers/misc/eeprom/eeprom.c index 2c27193aeaa0..f939ebc2507c 100644 --- a/drivers/misc/eeprom/eeprom.c +++ b/drivers/misc/eeprom/eeprom.c | |||
@@ -32,9 +32,6 @@ | |||
32 | static const unsigned short normal_i2c[] = { 0x50, 0x51, 0x52, 0x53, 0x54, | 32 | static const unsigned short normal_i2c[] = { 0x50, 0x51, 0x52, 0x53, 0x54, |
33 | 0x55, 0x56, 0x57, I2C_CLIENT_END }; | 33 | 0x55, 0x56, 0x57, I2C_CLIENT_END }; |
34 | 34 | ||
35 | /* Insmod parameters */ | ||
36 | I2C_CLIENT_INSMOD_1(eeprom); | ||
37 | |||
38 | 35 | ||
39 | /* Size of EEPROM in bytes */ | 36 | /* Size of EEPROM in bytes */ |
40 | #define EEPROM_SIZE 256 | 37 | #define EEPROM_SIZE 256 |
@@ -135,8 +132,7 @@ static struct bin_attribute eeprom_attr = { | |||
135 | }; | 132 | }; |
136 | 133 | ||
137 | /* Return 0 if detection is successful, -ENODEV otherwise */ | 134 | /* Return 0 if detection is successful, -ENODEV otherwise */ |
138 | static int eeprom_detect(struct i2c_client *client, int kind, | 135 | static int eeprom_detect(struct i2c_client *client, struct i2c_board_info *info) |
139 | struct i2c_board_info *info) | ||
140 | { | 136 | { |
141 | struct i2c_adapter *adapter = client->adapter; | 137 | struct i2c_adapter *adapter = client->adapter; |
142 | 138 | ||
@@ -233,7 +229,7 @@ static struct i2c_driver eeprom_driver = { | |||
233 | 229 | ||
234 | .class = I2C_CLASS_DDC | I2C_CLASS_SPD, | 230 | .class = I2C_CLASS_DDC | I2C_CLASS_SPD, |
235 | .detect = eeprom_detect, | 231 | .detect = eeprom_detect, |
236 | .address_data = &addr_data, | 232 | .address_list = normal_i2c, |
237 | }; | 233 | }; |
238 | 234 | ||
239 | static int __init eeprom_init(void) | 235 | static int __init eeprom_init(void) |
diff --git a/drivers/misc/enclosure.c b/drivers/misc/enclosure.c index e9eae4a78402..1eac626e710a 100644 --- a/drivers/misc/enclosure.c +++ b/drivers/misc/enclosure.c | |||
@@ -391,6 +391,7 @@ static const char *const enclosure_status [] = { | |||
391 | [ENCLOSURE_STATUS_NOT_INSTALLED] = "not installed", | 391 | [ENCLOSURE_STATUS_NOT_INSTALLED] = "not installed", |
392 | [ENCLOSURE_STATUS_UNKNOWN] = "unknown", | 392 | [ENCLOSURE_STATUS_UNKNOWN] = "unknown", |
393 | [ENCLOSURE_STATUS_UNAVAILABLE] = "unavailable", | 393 | [ENCLOSURE_STATUS_UNAVAILABLE] = "unavailable", |
394 | [ENCLOSURE_STATUS_MAX] = NULL, | ||
394 | }; | 395 | }; |
395 | 396 | ||
396 | static const char *const enclosure_type [] = { | 397 | static const char *const enclosure_type [] = { |
diff --git a/drivers/misc/hpilo.h b/drivers/misc/hpilo.h index 38576050776a..247eb386a973 100644 --- a/drivers/misc/hpilo.h +++ b/drivers/misc/hpilo.h | |||
@@ -44,9 +44,20 @@ struct ilo_hwinfo { | |||
44 | 44 | ||
45 | struct pci_dev *ilo_dev; | 45 | struct pci_dev *ilo_dev; |
46 | 46 | ||
47 | /* | ||
48 | * open_lock serializes ccb_cnt during open and close | ||
49 | * [ irq disabled ] | ||
50 | * -> alloc_lock used when adding/removing/searching ccb_alloc, | ||
51 | * which represents all ccbs open on the device | ||
52 | * --> fifo_lock controls access to fifo queues shared with hw | ||
53 | * | ||
54 | * Locks must be taken in this order, but open_lock and alloc_lock | ||
55 | * are optional, they do not need to be held in order to take a | ||
56 | * lower level lock. | ||
57 | */ | ||
58 | spinlock_t open_lock; | ||
47 | spinlock_t alloc_lock; | 59 | spinlock_t alloc_lock; |
48 | spinlock_t fifo_lock; | 60 | spinlock_t fifo_lock; |
49 | spinlock_t open_lock; | ||
50 | 61 | ||
51 | struct cdev cdev; | 62 | struct cdev cdev; |
52 | }; | 63 | }; |
diff --git a/drivers/misc/ics932s401.c b/drivers/misc/ics932s401.c index 6e43ab4231ae..395a4ea64e9c 100644 --- a/drivers/misc/ics932s401.c +++ b/drivers/misc/ics932s401.c | |||
@@ -30,9 +30,6 @@ | |||
30 | /* Addresses to scan */ | 30 | /* Addresses to scan */ |
31 | static const unsigned short normal_i2c[] = { 0x69, I2C_CLIENT_END }; | 31 | static const unsigned short normal_i2c[] = { 0x69, I2C_CLIENT_END }; |
32 | 32 | ||
33 | /* Insmod parameters */ | ||
34 | I2C_CLIENT_INSMOD_1(ics932s401); | ||
35 | |||
36 | /* ICS932S401 registers */ | 33 | /* ICS932S401 registers */ |
37 | #define ICS932S401_REG_CFG2 0x01 | 34 | #define ICS932S401_REG_CFG2 0x01 |
38 | #define ICS932S401_CFG1_SPREAD 0x01 | 35 | #define ICS932S401_CFG1_SPREAD 0x01 |
@@ -106,12 +103,12 @@ struct ics932s401_data { | |||
106 | 103 | ||
107 | static int ics932s401_probe(struct i2c_client *client, | 104 | static int ics932s401_probe(struct i2c_client *client, |
108 | const struct i2c_device_id *id); | 105 | const struct i2c_device_id *id); |
109 | static int ics932s401_detect(struct i2c_client *client, int kind, | 106 | static int ics932s401_detect(struct i2c_client *client, |
110 | struct i2c_board_info *info); | 107 | struct i2c_board_info *info); |
111 | static int ics932s401_remove(struct i2c_client *client); | 108 | static int ics932s401_remove(struct i2c_client *client); |
112 | 109 | ||
113 | static const struct i2c_device_id ics932s401_id[] = { | 110 | static const struct i2c_device_id ics932s401_id[] = { |
114 | { "ics932s401", ics932s401 }, | 111 | { "ics932s401", 0 }, |
115 | { } | 112 | { } |
116 | }; | 113 | }; |
117 | MODULE_DEVICE_TABLE(i2c, ics932s401_id); | 114 | MODULE_DEVICE_TABLE(i2c, ics932s401_id); |
@@ -125,7 +122,7 @@ static struct i2c_driver ics932s401_driver = { | |||
125 | .remove = ics932s401_remove, | 122 | .remove = ics932s401_remove, |
126 | .id_table = ics932s401_id, | 123 | .id_table = ics932s401_id, |
127 | .detect = ics932s401_detect, | 124 | .detect = ics932s401_detect, |
128 | .address_data = &addr_data, | 125 | .address_list = normal_i2c, |
129 | }; | 126 | }; |
130 | 127 | ||
131 | static struct ics932s401_data *ics932s401_update_device(struct device *dev) | 128 | static struct ics932s401_data *ics932s401_update_device(struct device *dev) |
@@ -413,36 +410,29 @@ static ssize_t show_spread(struct device *dev, | |||
413 | } | 410 | } |
414 | 411 | ||
415 | /* Return 0 if detection is successful, -ENODEV otherwise */ | 412 | /* Return 0 if detection is successful, -ENODEV otherwise */ |
416 | static int ics932s401_detect(struct i2c_client *client, int kind, | 413 | static int ics932s401_detect(struct i2c_client *client, |
417 | struct i2c_board_info *info) | 414 | struct i2c_board_info *info) |
418 | { | 415 | { |
419 | struct i2c_adapter *adapter = client->adapter; | 416 | struct i2c_adapter *adapter = client->adapter; |
417 | int vendor, device, revision; | ||
420 | 418 | ||
421 | if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) | 419 | if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) |
422 | return -ENODEV; | 420 | return -ENODEV; |
423 | 421 | ||
424 | if (kind <= 0) { | 422 | vendor = i2c_smbus_read_word_data(client, ICS932S401_REG_VENDOR_REV); |
425 | int vendor, device, revision; | 423 | vendor >>= 8; |
426 | 424 | revision = vendor >> ICS932S401_REV_SHIFT; | |
427 | vendor = i2c_smbus_read_word_data(client, | 425 | vendor &= ICS932S401_VENDOR_MASK; |
428 | ICS932S401_REG_VENDOR_REV); | 426 | if (vendor != ICS932S401_VENDOR) |
429 | vendor >>= 8; | 427 | return -ENODEV; |
430 | revision = vendor >> ICS932S401_REV_SHIFT; | 428 | |
431 | vendor &= ICS932S401_VENDOR_MASK; | 429 | device = i2c_smbus_read_word_data(client, ICS932S401_REG_DEVICE); |
432 | if (vendor != ICS932S401_VENDOR) | 430 | device >>= 8; |
433 | return -ENODEV; | 431 | if (device != ICS932S401_DEVICE) |
434 | 432 | return -ENODEV; | |
435 | device = i2c_smbus_read_word_data(client, | 433 | |
436 | ICS932S401_REG_DEVICE); | 434 | if (revision != ICS932S401_REV) |
437 | device >>= 8; | 435 | dev_info(&adapter->dev, "Unknown revision %d\n", revision); |
438 | if (device != ICS932S401_DEVICE) | ||
439 | return -ENODEV; | ||
440 | |||
441 | if (revision != ICS932S401_REV) | ||
442 | dev_info(&adapter->dev, "Unknown revision %d\n", | ||
443 | revision); | ||
444 | } else | ||
445 | dev_dbg(&adapter->dev, "detection forced\n"); | ||
446 | 436 | ||
447 | strlcpy(info->type, "ics932s401", I2C_NAME_SIZE); | 437 | strlcpy(info->type, "ics932s401", I2C_NAME_SIZE); |
448 | 438 | ||
diff --git a/drivers/misc/ioc4.c b/drivers/misc/ioc4.c index 60b0b1a4fb3a..09dcb699e667 100644 --- a/drivers/misc/ioc4.c +++ b/drivers/misc/ioc4.c | |||
@@ -138,7 +138,7 @@ ioc4_unregister_submodule(struct ioc4_submodule *is) | |||
138 | * even though the following code utilizes external interrupt registers | 138 | * even though the following code utilizes external interrupt registers |
139 | * to perform the speed calculation. | 139 | * to perform the speed calculation. |
140 | */ | 140 | */ |
141 | static void | 141 | static void __devinit |
142 | ioc4_clock_calibrate(struct ioc4_driver_data *idd) | 142 | ioc4_clock_calibrate(struct ioc4_driver_data *idd) |
143 | { | 143 | { |
144 | union ioc4_int_out int_out; | 144 | union ioc4_int_out int_out; |
@@ -230,7 +230,7 @@ ioc4_clock_calibrate(struct ioc4_driver_data *idd) | |||
230 | * on the same PCI bus at slot number 3 to differentiate IO9 from IO10. | 230 | * on the same PCI bus at slot number 3 to differentiate IO9 from IO10. |
231 | * If neither is present, it's a PCI-RT. | 231 | * If neither is present, it's a PCI-RT. |
232 | */ | 232 | */ |
233 | static unsigned int | 233 | static unsigned int __devinit |
234 | ioc4_variant(struct ioc4_driver_data *idd) | 234 | ioc4_variant(struct ioc4_driver_data *idd) |
235 | { | 235 | { |
236 | struct pci_dev *pdev = NULL; | 236 | struct pci_dev *pdev = NULL; |
@@ -269,7 +269,7 @@ ioc4_variant(struct ioc4_driver_data *idd) | |||
269 | return IOC4_VARIANT_PCI_RT; | 269 | return IOC4_VARIANT_PCI_RT; |
270 | } | 270 | } |
271 | 271 | ||
272 | static void | 272 | static void __devinit |
273 | ioc4_load_modules(struct work_struct *work) | 273 | ioc4_load_modules(struct work_struct *work) |
274 | { | 274 | { |
275 | /* arg just has to be freed */ | 275 | /* arg just has to be freed */ |
@@ -280,7 +280,7 @@ ioc4_load_modules(struct work_struct *work) | |||
280 | } | 280 | } |
281 | 281 | ||
282 | /* Adds a new instance of an IOC4 card */ | 282 | /* Adds a new instance of an IOC4 card */ |
283 | static int | 283 | static int __devinit |
284 | ioc4_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) | 284 | ioc4_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) |
285 | { | 285 | { |
286 | struct ioc4_driver_data *idd; | 286 | struct ioc4_driver_data *idd; |
@@ -425,7 +425,7 @@ out: | |||
425 | } | 425 | } |
426 | 426 | ||
427 | /* Removes a particular instance of an IOC4 card. */ | 427 | /* Removes a particular instance of an IOC4 card. */ |
428 | static void | 428 | static void __devexit |
429 | ioc4_remove(struct pci_dev *pdev) | 429 | ioc4_remove(struct pci_dev *pdev) |
430 | { | 430 | { |
431 | struct ioc4_submodule *is; | 431 | struct ioc4_submodule *is; |
@@ -476,7 +476,7 @@ static struct pci_driver ioc4_driver = { | |||
476 | .name = "IOC4", | 476 | .name = "IOC4", |
477 | .id_table = ioc4_id_table, | 477 | .id_table = ioc4_id_table, |
478 | .probe = ioc4_probe, | 478 | .probe = ioc4_probe, |
479 | .remove = ioc4_remove, | 479 | .remove = __devexit_p(ioc4_remove), |
480 | }; | 480 | }; |
481 | 481 | ||
482 | MODULE_DEVICE_TABLE(pci, ioc4_id_table); | 482 | MODULE_DEVICE_TABLE(pci, ioc4_id_table); |
@@ -486,14 +486,14 @@ MODULE_DEVICE_TABLE(pci, ioc4_id_table); | |||
486 | *********************/ | 486 | *********************/ |
487 | 487 | ||
488 | /* Module load */ | 488 | /* Module load */ |
489 | static int __devinit | 489 | static int __init |
490 | ioc4_init(void) | 490 | ioc4_init(void) |
491 | { | 491 | { |
492 | return pci_register_driver(&ioc4_driver); | 492 | return pci_register_driver(&ioc4_driver); |
493 | } | 493 | } |
494 | 494 | ||
495 | /* Module unload */ | 495 | /* Module unload */ |
496 | static void __devexit | 496 | static void __exit |
497 | ioc4_exit(void) | 497 | ioc4_exit(void) |
498 | { | 498 | { |
499 | /* Ensure ioc4_load_modules() has completed before exiting */ | 499 | /* Ensure ioc4_load_modules() has completed before exiting */ |
diff --git a/drivers/misc/iwmc3200top/Kconfig b/drivers/misc/iwmc3200top/Kconfig new file mode 100644 index 000000000000..9e4b88fb57f1 --- /dev/null +++ b/drivers/misc/iwmc3200top/Kconfig | |||
@@ -0,0 +1,20 @@ | |||
1 | config IWMC3200TOP | ||
2 | tristate "Intel Wireless MultiCom Top Driver" | ||
3 | depends on MMC && EXPERIMENTAL | ||
4 | select FW_LOADER | ||
5 | ---help--- | ||
6 | Intel Wireless MultiCom 3200 Top driver is responsible for | ||
7 | for firmware load and enabled coms enumeration | ||
8 | |||
9 | config IWMC3200TOP_DEBUG | ||
10 | bool "Enable full debug output of iwmc3200top Driver" | ||
11 | depends on IWMC3200TOP | ||
12 | ---help--- | ||
13 | Enable full debug output of iwmc3200top Driver | ||
14 | |||
15 | config IWMC3200TOP_DEBUGFS | ||
16 | bool "Enable Debugfs debugging interface for iwmc3200top" | ||
17 | depends on IWMC3200TOP | ||
18 | ---help--- | ||
19 | Enable creation of debugfs files for iwmc3200top | ||
20 | |||
diff --git a/drivers/misc/iwmc3200top/Makefile b/drivers/misc/iwmc3200top/Makefile new file mode 100644 index 000000000000..fbf53fb4634e --- /dev/null +++ b/drivers/misc/iwmc3200top/Makefile | |||
@@ -0,0 +1,29 @@ | |||
1 | # iwmc3200top - Intel Wireless MultiCom 3200 Top Driver | ||
2 | # drivers/misc/iwmc3200top/Makefile | ||
3 | # | ||
4 | # Copyright (C) 2009 Intel Corporation. All rights reserved. | ||
5 | # | ||
6 | # This program is free software; you can redistribute it and/or | ||
7 | # modify it under the terms of the GNU General Public License version | ||
8 | # 2 as published by the Free Software Foundation. | ||
9 | # | ||
10 | # This program is distributed in the hope that it will be useful, | ||
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | # GNU General Public License for more details. | ||
14 | # | ||
15 | # You should have received a copy of the GNU General Public License | ||
16 | # along with this program; if not, write to the Free Software | ||
17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
18 | # 02110-1301, USA. | ||
19 | # | ||
20 | # | ||
21 | # Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com> | ||
22 | # - | ||
23 | # | ||
24 | # | ||
25 | |||
26 | obj-$(CONFIG_IWMC3200TOP) += iwmc3200top.o | ||
27 | iwmc3200top-objs := main.o fw-download.o | ||
28 | iwmc3200top-$(CONFIG_IWMC3200TOP_DEBUG) += log.o | ||
29 | iwmc3200top-$(CONFIG_IWMC3200TOP_DEBUGFS) += debugfs.o | ||
diff --git a/drivers/misc/iwmc3200top/debugfs.c b/drivers/misc/iwmc3200top/debugfs.c new file mode 100644 index 000000000000..0c8ea0a1c8a3 --- /dev/null +++ b/drivers/misc/iwmc3200top/debugfs.c | |||
@@ -0,0 +1,133 @@ | |||
1 | /* | ||
2 | * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver | ||
3 | * drivers/misc/iwmc3200top/debufs.c | ||
4 | * | ||
5 | * Copyright (C) 2009 Intel Corporation. All rights reserved. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License version | ||
9 | * 2 as published by the Free Software Foundation. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
19 | * 02110-1301, USA. | ||
20 | * | ||
21 | * | ||
22 | * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com> | ||
23 | * - | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #include <linux/kernel.h> | ||
28 | #include <linux/string.h> | ||
29 | #include <linux/ctype.h> | ||
30 | #include <linux/mmc/sdio_func.h> | ||
31 | #include <linux/mmc/sdio.h> | ||
32 | #include <linux/debugfs.h> | ||
33 | |||
34 | #include "iwmc3200top.h" | ||
35 | #include "fw-msg.h" | ||
36 | #include "log.h" | ||
37 | #include "debugfs.h" | ||
38 | |||
39 | |||
40 | |||
41 | /* Constants definition */ | ||
42 | #define HEXADECIMAL_RADIX 16 | ||
43 | |||
44 | /* Functions definition */ | ||
45 | |||
46 | |||
47 | #define DEBUGFS_ADD(name, parent) do { \ | ||
48 | dbgfs->dbgfs_##parent##_files.file_##name = \ | ||
49 | debugfs_create_file(#name, 0644, dbgfs->dir_##parent, priv, \ | ||
50 | &iwmct_dbgfs_##name##_ops); \ | ||
51 | } while (0) | ||
52 | |||
53 | #define DEBUGFS_RM(name) do { \ | ||
54 | debugfs_remove(name); \ | ||
55 | name = NULL; \ | ||
56 | } while (0) | ||
57 | |||
58 | #define DEBUGFS_READ_FUNC(name) \ | ||
59 | ssize_t iwmct_dbgfs_##name##_read(struct file *file, \ | ||
60 | char __user *user_buf, \ | ||
61 | size_t count, loff_t *ppos); | ||
62 | |||
63 | #define DEBUGFS_WRITE_FUNC(name) \ | ||
64 | ssize_t iwmct_dbgfs_##name##_write(struct file *file, \ | ||
65 | const char __user *user_buf, \ | ||
66 | size_t count, loff_t *ppos); | ||
67 | |||
68 | #define DEBUGFS_READ_FILE_OPS(name) \ | ||
69 | DEBUGFS_READ_FUNC(name) \ | ||
70 | static const struct file_operations iwmct_dbgfs_##name##_ops = { \ | ||
71 | .read = iwmct_dbgfs_##name##_read, \ | ||
72 | .open = iwmct_dbgfs_open_file_generic, \ | ||
73 | }; | ||
74 | |||
75 | #define DEBUGFS_WRITE_FILE_OPS(name) \ | ||
76 | DEBUGFS_WRITE_FUNC(name) \ | ||
77 | static const struct file_operations iwmct_dbgfs_##name##_ops = { \ | ||
78 | .write = iwmct_dbgfs_##name##_write, \ | ||
79 | .open = iwmct_dbgfs_open_file_generic, \ | ||
80 | }; | ||
81 | |||
82 | #define DEBUGFS_READ_WRITE_FILE_OPS(name) \ | ||
83 | DEBUGFS_READ_FUNC(name) \ | ||
84 | DEBUGFS_WRITE_FUNC(name) \ | ||
85 | static const struct file_operations iwmct_dbgfs_##name##_ops = {\ | ||
86 | .write = iwmct_dbgfs_##name##_write, \ | ||
87 | .read = iwmct_dbgfs_##name##_read, \ | ||
88 | .open = iwmct_dbgfs_open_file_generic, \ | ||
89 | }; | ||
90 | |||
91 | |||
92 | /* Debugfs file ops definitions */ | ||
93 | |||
94 | /* | ||
95 | * Create the debugfs files and directories | ||
96 | * | ||
97 | */ | ||
98 | void iwmct_dbgfs_register(struct iwmct_priv *priv, const char *name) | ||
99 | { | ||
100 | struct iwmct_debugfs *dbgfs; | ||
101 | |||
102 | dbgfs = kzalloc(sizeof(struct iwmct_debugfs), GFP_KERNEL); | ||
103 | if (!dbgfs) { | ||
104 | LOG_ERROR(priv, DEBUGFS, "failed to allocate %zd bytes\n", | ||
105 | sizeof(struct iwmct_debugfs)); | ||
106 | return; | ||
107 | } | ||
108 | |||
109 | priv->dbgfs = dbgfs; | ||
110 | dbgfs->name = name; | ||
111 | dbgfs->dir_drv = debugfs_create_dir(name, NULL); | ||
112 | if (!dbgfs->dir_drv) { | ||
113 | LOG_ERROR(priv, DEBUGFS, "failed to create debugfs dir\n"); | ||
114 | return; | ||
115 | } | ||
116 | |||
117 | return; | ||
118 | } | ||
119 | |||
120 | /** | ||
121 | * Remove the debugfs files and directories | ||
122 | * | ||
123 | */ | ||
124 | void iwmct_dbgfs_unregister(struct iwmct_debugfs *dbgfs) | ||
125 | { | ||
126 | if (!dbgfs) | ||
127 | return; | ||
128 | |||
129 | DEBUGFS_RM(dbgfs->dir_drv); | ||
130 | kfree(dbgfs); | ||
131 | dbgfs = NULL; | ||
132 | } | ||
133 | |||
diff --git a/drivers/misc/iwmc3200top/debugfs.h b/drivers/misc/iwmc3200top/debugfs.h new file mode 100644 index 000000000000..71d45759b40f --- /dev/null +++ b/drivers/misc/iwmc3200top/debugfs.h | |||
@@ -0,0 +1,58 @@ | |||
1 | /* | ||
2 | * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver | ||
3 | * drivers/misc/iwmc3200top/debufs.h | ||
4 | * | ||
5 | * Copyright (C) 2009 Intel Corporation. All rights reserved. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License version | ||
9 | * 2 as published by the Free Software Foundation. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
19 | * 02110-1301, USA. | ||
20 | * | ||
21 | * | ||
22 | * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com> | ||
23 | * - | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #ifndef __DEBUGFS_H__ | ||
28 | #define __DEBUGFS_H__ | ||
29 | |||
30 | |||
31 | #ifdef CONFIG_IWMC3200TOP_DEBUGFS | ||
32 | |||
33 | struct iwmct_debugfs { | ||
34 | const char *name; | ||
35 | struct dentry *dir_drv; | ||
36 | struct dir_drv_files { | ||
37 | } dbgfs_drv_files; | ||
38 | }; | ||
39 | |||
40 | void iwmct_dbgfs_register(struct iwmct_priv *priv, const char *name); | ||
41 | void iwmct_dbgfs_unregister(struct iwmct_debugfs *dbgfs); | ||
42 | |||
43 | #else /* CONFIG_IWMC3200TOP_DEBUGFS */ | ||
44 | |||
45 | struct iwmct_debugfs; | ||
46 | |||
47 | static inline void | ||
48 | iwmct_dbgfs_register(struct iwmct_priv *priv, const char *name) | ||
49 | {} | ||
50 | |||
51 | static inline void | ||
52 | iwmct_dbgfs_unregister(struct iwmct_debugfs *dbgfs) | ||
53 | {} | ||
54 | |||
55 | #endif /* CONFIG_IWMC3200TOP_DEBUGFS */ | ||
56 | |||
57 | #endif /* __DEBUGFS_H__ */ | ||
58 | |||
diff --git a/drivers/misc/iwmc3200top/fw-download.c b/drivers/misc/iwmc3200top/fw-download.c new file mode 100644 index 000000000000..50d431e469f5 --- /dev/null +++ b/drivers/misc/iwmc3200top/fw-download.c | |||
@@ -0,0 +1,355 @@ | |||
1 | /* | ||
2 | * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver | ||
3 | * drivers/misc/iwmc3200top/fw-download.c | ||
4 | * | ||
5 | * Copyright (C) 2009 Intel Corporation. All rights reserved. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License version | ||
9 | * 2 as published by the Free Software Foundation. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
19 | * 02110-1301, USA. | ||
20 | * | ||
21 | * | ||
22 | * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com> | ||
23 | * - | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #include <linux/firmware.h> | ||
28 | #include <linux/mmc/sdio_func.h> | ||
29 | #include <asm/unaligned.h> | ||
30 | |||
31 | #include "iwmc3200top.h" | ||
32 | #include "log.h" | ||
33 | #include "fw-msg.h" | ||
34 | |||
35 | #define CHECKSUM_BYTES_NUM sizeof(u32) | ||
36 | |||
37 | /** | ||
38 | init parser struct with file | ||
39 | */ | ||
40 | static int iwmct_fw_parser_init(struct iwmct_priv *priv, const u8 *file, | ||
41 | size_t file_size, size_t block_size) | ||
42 | { | ||
43 | struct iwmct_parser *parser = &priv->parser; | ||
44 | struct iwmct_fw_hdr *fw_hdr = &parser->versions; | ||
45 | |||
46 | LOG_INFOEX(priv, INIT, "-->\n"); | ||
47 | |||
48 | LOG_INFO(priv, FW_DOWNLOAD, "file_size=%zd\n", file_size); | ||
49 | |||
50 | parser->file = file; | ||
51 | parser->file_size = file_size; | ||
52 | parser->cur_pos = 0; | ||
53 | parser->buf = NULL; | ||
54 | |||
55 | parser->buf = kzalloc(block_size, GFP_KERNEL); | ||
56 | if (!parser->buf) { | ||
57 | LOG_ERROR(priv, FW_DOWNLOAD, "kzalloc error\n"); | ||
58 | return -ENOMEM; | ||
59 | } | ||
60 | parser->buf_size = block_size; | ||
61 | |||
62 | /* extract fw versions */ | ||
63 | memcpy(fw_hdr, parser->file, sizeof(struct iwmct_fw_hdr)); | ||
64 | LOG_INFO(priv, FW_DOWNLOAD, "fw versions are:\n" | ||
65 | "top %u.%u.%u gps %u.%u.%u bt %u.%u.%u tic %s\n", | ||
66 | fw_hdr->top_major, fw_hdr->top_minor, fw_hdr->top_revision, | ||
67 | fw_hdr->gps_major, fw_hdr->gps_minor, fw_hdr->gps_revision, | ||
68 | fw_hdr->bt_major, fw_hdr->bt_minor, fw_hdr->bt_revision, | ||
69 | fw_hdr->tic_name); | ||
70 | |||
71 | parser->cur_pos += sizeof(struct iwmct_fw_hdr); | ||
72 | |||
73 | LOG_INFOEX(priv, INIT, "<--\n"); | ||
74 | return 0; | ||
75 | } | ||
76 | |||
77 | static bool iwmct_checksum(struct iwmct_priv *priv) | ||
78 | { | ||
79 | struct iwmct_parser *parser = &priv->parser; | ||
80 | __le32 *file = (__le32 *)parser->file; | ||
81 | int i, pad, steps; | ||
82 | u32 accum = 0; | ||
83 | u32 checksum; | ||
84 | u32 mask = 0xffffffff; | ||
85 | |||
86 | pad = (parser->file_size - CHECKSUM_BYTES_NUM) % 4; | ||
87 | steps = (parser->file_size - CHECKSUM_BYTES_NUM) / 4; | ||
88 | |||
89 | LOG_INFO(priv, FW_DOWNLOAD, "pad=%d steps=%d\n", pad, steps); | ||
90 | |||
91 | for (i = 0; i < steps; i++) | ||
92 | accum += le32_to_cpu(file[i]); | ||
93 | |||
94 | if (pad) { | ||
95 | mask <<= 8 * (4 - pad); | ||
96 | accum += le32_to_cpu(file[steps]) & mask; | ||
97 | } | ||
98 | |||
99 | checksum = get_unaligned_le32((__le32 *)(parser->file + | ||
100 | parser->file_size - CHECKSUM_BYTES_NUM)); | ||
101 | |||
102 | LOG_INFO(priv, FW_DOWNLOAD, | ||
103 | "compare checksum accum=0x%x to checksum=0x%x\n", | ||
104 | accum, checksum); | ||
105 | |||
106 | return checksum == accum; | ||
107 | } | ||
108 | |||
109 | static int iwmct_parse_next_section(struct iwmct_priv *priv, const u8 **p_sec, | ||
110 | size_t *sec_size, __le32 *sec_addr) | ||
111 | { | ||
112 | struct iwmct_parser *parser = &priv->parser; | ||
113 | struct iwmct_dbg *dbg = &priv->dbg; | ||
114 | struct iwmct_fw_sec_hdr *sec_hdr; | ||
115 | |||
116 | LOG_INFOEX(priv, INIT, "-->\n"); | ||
117 | |||
118 | while (parser->cur_pos + sizeof(struct iwmct_fw_sec_hdr) | ||
119 | <= parser->file_size) { | ||
120 | |||
121 | sec_hdr = (struct iwmct_fw_sec_hdr *) | ||
122 | (parser->file + parser->cur_pos); | ||
123 | parser->cur_pos += sizeof(struct iwmct_fw_sec_hdr); | ||
124 | |||
125 | LOG_INFO(priv, FW_DOWNLOAD, | ||
126 | "sec hdr: type=%s addr=0x%x size=%d\n", | ||
127 | sec_hdr->type, sec_hdr->target_addr, | ||
128 | sec_hdr->data_size); | ||
129 | |||
130 | if (strcmp(sec_hdr->type, "ENT") == 0) | ||
131 | parser->entry_point = le32_to_cpu(sec_hdr->target_addr); | ||
132 | else if (strcmp(sec_hdr->type, "LBL") == 0) | ||
133 | strcpy(dbg->label_fw, parser->file + parser->cur_pos); | ||
134 | else if (((strcmp(sec_hdr->type, "TOP") == 0) && | ||
135 | (priv->barker & BARKER_DNLOAD_TOP_MSK)) || | ||
136 | ((strcmp(sec_hdr->type, "GPS") == 0) && | ||
137 | (priv->barker & BARKER_DNLOAD_GPS_MSK)) || | ||
138 | ((strcmp(sec_hdr->type, "BTH") == 0) && | ||
139 | (priv->barker & BARKER_DNLOAD_BT_MSK))) { | ||
140 | *sec_addr = sec_hdr->target_addr; | ||
141 | *sec_size = le32_to_cpu(sec_hdr->data_size); | ||
142 | *p_sec = parser->file + parser->cur_pos; | ||
143 | parser->cur_pos += le32_to_cpu(sec_hdr->data_size); | ||
144 | return 1; | ||
145 | } else if (strcmp(sec_hdr->type, "LOG") != 0) | ||
146 | LOG_WARNING(priv, FW_DOWNLOAD, | ||
147 | "skipping section type %s\n", | ||
148 | sec_hdr->type); | ||
149 | |||
150 | parser->cur_pos += le32_to_cpu(sec_hdr->data_size); | ||
151 | LOG_INFO(priv, FW_DOWNLOAD, | ||
152 | "finished with section cur_pos=%zd\n", parser->cur_pos); | ||
153 | } | ||
154 | |||
155 | LOG_INFOEX(priv, INIT, "<--\n"); | ||
156 | return 0; | ||
157 | } | ||
158 | |||
159 | static int iwmct_download_section(struct iwmct_priv *priv, const u8 *p_sec, | ||
160 | size_t sec_size, __le32 addr) | ||
161 | { | ||
162 | struct iwmct_parser *parser = &priv->parser; | ||
163 | struct iwmct_fw_load_hdr *hdr = (struct iwmct_fw_load_hdr *)parser->buf; | ||
164 | const u8 *cur_block = p_sec; | ||
165 | size_t sent = 0; | ||
166 | int cnt = 0; | ||
167 | int ret = 0; | ||
168 | u32 cmd = 0; | ||
169 | |||
170 | LOG_INFOEX(priv, INIT, "-->\n"); | ||
171 | LOG_INFO(priv, FW_DOWNLOAD, "Download address 0x%x size 0x%zx\n", | ||
172 | addr, sec_size); | ||
173 | |||
174 | while (sent < sec_size) { | ||
175 | int i; | ||
176 | u32 chksm = 0; | ||
177 | u32 reset = atomic_read(&priv->reset); | ||
178 | /* actual FW data */ | ||
179 | u32 data_size = min(parser->buf_size - sizeof(*hdr), | ||
180 | sec_size - sent); | ||
181 | /* Pad to block size */ | ||
182 | u32 trans_size = (data_size + sizeof(*hdr) + | ||
183 | IWMC_SDIO_BLK_SIZE - 1) & | ||
184 | ~(IWMC_SDIO_BLK_SIZE - 1); | ||
185 | ++cnt; | ||
186 | |||
187 | /* in case of reset, interrupt FW DOWNLAOD */ | ||
188 | if (reset) { | ||
189 | LOG_INFO(priv, FW_DOWNLOAD, | ||
190 | "Reset detected. Abort FW download!!!"); | ||
191 | ret = -ECANCELED; | ||
192 | goto exit; | ||
193 | } | ||
194 | |||
195 | memset(parser->buf, 0, parser->buf_size); | ||
196 | cmd |= IWMC_OPCODE_WRITE << CMD_HDR_OPCODE_POS; | ||
197 | cmd |= IWMC_CMD_SIGNATURE << CMD_HDR_SIGNATURE_POS; | ||
198 | cmd |= (priv->dbg.direct ? 1 : 0) << CMD_HDR_DIRECT_ACCESS_POS; | ||
199 | cmd |= (priv->dbg.checksum ? 1 : 0) << CMD_HDR_USE_CHECKSUM_POS; | ||
200 | hdr->data_size = cpu_to_le32(data_size); | ||
201 | hdr->target_addr = addr; | ||
202 | |||
203 | /* checksum is allowed for sizes divisible by 4 */ | ||
204 | if (data_size & 0x3) | ||
205 | cmd &= ~CMD_HDR_USE_CHECKSUM_MSK; | ||
206 | |||
207 | memcpy(hdr->data, cur_block, data_size); | ||
208 | |||
209 | |||
210 | if (cmd & CMD_HDR_USE_CHECKSUM_MSK) { | ||
211 | |||
212 | chksm = data_size + le32_to_cpu(addr) + cmd; | ||
213 | for (i = 0; i < data_size >> 2; i++) | ||
214 | chksm += ((u32 *)cur_block)[i]; | ||
215 | |||
216 | hdr->block_chksm = cpu_to_le32(chksm); | ||
217 | LOG_INFO(priv, FW_DOWNLOAD, "Checksum = 0x%X\n", | ||
218 | hdr->block_chksm); | ||
219 | } | ||
220 | |||
221 | LOG_INFO(priv, FW_DOWNLOAD, "trans#%d, len=%d, sent=%zd, " | ||
222 | "sec_size=%zd, startAddress 0x%X\n", | ||
223 | cnt, trans_size, sent, sec_size, addr); | ||
224 | |||
225 | if (priv->dbg.dump) | ||
226 | LOG_HEXDUMP(FW_DOWNLOAD, parser->buf, trans_size); | ||
227 | |||
228 | |||
229 | hdr->cmd = cpu_to_le32(cmd); | ||
230 | /* send it down */ | ||
231 | /* TODO: add more proper sending and error checking */ | ||
232 | ret = iwmct_tx(priv, 0, parser->buf, trans_size); | ||
233 | if (ret != 0) { | ||
234 | LOG_INFO(priv, FW_DOWNLOAD, | ||
235 | "iwmct_tx returned %d\n", ret); | ||
236 | goto exit; | ||
237 | } | ||
238 | |||
239 | addr = cpu_to_le32(le32_to_cpu(addr) + data_size); | ||
240 | sent += data_size; | ||
241 | cur_block = p_sec + sent; | ||
242 | |||
243 | if (priv->dbg.blocks && (cnt + 1) >= priv->dbg.blocks) { | ||
244 | LOG_INFO(priv, FW_DOWNLOAD, | ||
245 | "Block number limit is reached [%d]\n", | ||
246 | priv->dbg.blocks); | ||
247 | break; | ||
248 | } | ||
249 | } | ||
250 | |||
251 | if (sent < sec_size) | ||
252 | ret = -EINVAL; | ||
253 | exit: | ||
254 | LOG_INFOEX(priv, INIT, "<--\n"); | ||
255 | return ret; | ||
256 | } | ||
257 | |||
258 | static int iwmct_kick_fw(struct iwmct_priv *priv, bool jump) | ||
259 | { | ||
260 | struct iwmct_parser *parser = &priv->parser; | ||
261 | struct iwmct_fw_load_hdr *hdr = (struct iwmct_fw_load_hdr *)parser->buf; | ||
262 | int ret; | ||
263 | u32 cmd; | ||
264 | |||
265 | LOG_INFOEX(priv, INIT, "-->\n"); | ||
266 | |||
267 | memset(parser->buf, 0, parser->buf_size); | ||
268 | cmd = IWMC_CMD_SIGNATURE << CMD_HDR_SIGNATURE_POS; | ||
269 | if (jump) { | ||
270 | cmd |= IWMC_OPCODE_JUMP << CMD_HDR_OPCODE_POS; | ||
271 | hdr->target_addr = cpu_to_le32(parser->entry_point); | ||
272 | LOG_INFO(priv, FW_DOWNLOAD, "jump address 0x%x\n", | ||
273 | parser->entry_point); | ||
274 | } else { | ||
275 | cmd |= IWMC_OPCODE_LAST_COMMAND << CMD_HDR_OPCODE_POS; | ||
276 | LOG_INFO(priv, FW_DOWNLOAD, "last command\n"); | ||
277 | } | ||
278 | |||
279 | hdr->cmd = cpu_to_le32(cmd); | ||
280 | |||
281 | LOG_HEXDUMP(FW_DOWNLOAD, parser->buf, sizeof(*hdr)); | ||
282 | /* send it down */ | ||
283 | /* TODO: add more proper sending and error checking */ | ||
284 | ret = iwmct_tx(priv, 0, parser->buf, IWMC_SDIO_BLK_SIZE); | ||
285 | if (ret) | ||
286 | LOG_INFO(priv, FW_DOWNLOAD, "iwmct_tx returned %d", ret); | ||
287 | |||
288 | LOG_INFOEX(priv, INIT, "<--\n"); | ||
289 | return 0; | ||
290 | } | ||
291 | |||
292 | int iwmct_fw_load(struct iwmct_priv *priv) | ||
293 | { | ||
294 | const u8 *fw_name = FW_NAME(FW_API_VER); | ||
295 | const struct firmware *raw; | ||
296 | const u8 *pdata; | ||
297 | size_t len; | ||
298 | __le32 addr; | ||
299 | int ret; | ||
300 | |||
301 | /* clear parser struct */ | ||
302 | memset(&priv->parser, 0, sizeof(struct iwmct_parser)); | ||
303 | |||
304 | /* get the firmware */ | ||
305 | ret = request_firmware(&raw, fw_name, &priv->func->dev); | ||
306 | if (ret < 0) { | ||
307 | LOG_ERROR(priv, FW_DOWNLOAD, "%s request_firmware failed %d\n", | ||
308 | fw_name, ret); | ||
309 | goto exit; | ||
310 | } | ||
311 | |||
312 | if (raw->size < sizeof(struct iwmct_fw_sec_hdr)) { | ||
313 | LOG_ERROR(priv, FW_DOWNLOAD, "%s smaller then (%zd) (%zd)\n", | ||
314 | fw_name, sizeof(struct iwmct_fw_sec_hdr), raw->size); | ||
315 | goto exit; | ||
316 | } | ||
317 | |||
318 | LOG_INFO(priv, FW_DOWNLOAD, "Read firmware '%s'\n", fw_name); | ||
319 | |||
320 | ret = iwmct_fw_parser_init(priv, raw->data, raw->size, priv->trans_len); | ||
321 | if (ret < 0) { | ||
322 | LOG_ERROR(priv, FW_DOWNLOAD, | ||
323 | "iwmct_parser_init failed: Reason %d\n", ret); | ||
324 | goto exit; | ||
325 | } | ||
326 | |||
327 | /* checksum */ | ||
328 | if (!iwmct_checksum(priv)) { | ||
329 | LOG_ERROR(priv, FW_DOWNLOAD, "checksum error\n"); | ||
330 | ret = -EINVAL; | ||
331 | goto exit; | ||
332 | } | ||
333 | |||
334 | /* download firmware to device */ | ||
335 | while (iwmct_parse_next_section(priv, &pdata, &len, &addr)) { | ||
336 | if (iwmct_download_section(priv, pdata, len, addr)) { | ||
337 | LOG_ERROR(priv, FW_DOWNLOAD, | ||
338 | "%s download section failed\n", fw_name); | ||
339 | ret = -EIO; | ||
340 | goto exit; | ||
341 | } | ||
342 | } | ||
343 | |||
344 | iwmct_kick_fw(priv, !!(priv->barker & BARKER_DNLOAD_JUMP_MSK)); | ||
345 | |||
346 | exit: | ||
347 | kfree(priv->parser.buf); | ||
348 | |||
349 | if (raw) | ||
350 | release_firmware(raw); | ||
351 | |||
352 | raw = NULL; | ||
353 | |||
354 | return ret; | ||
355 | } | ||
diff --git a/drivers/misc/iwmc3200top/fw-msg.h b/drivers/misc/iwmc3200top/fw-msg.h new file mode 100644 index 000000000000..9e26b75bd482 --- /dev/null +++ b/drivers/misc/iwmc3200top/fw-msg.h | |||
@@ -0,0 +1,113 @@ | |||
1 | /* | ||
2 | * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver | ||
3 | * drivers/misc/iwmc3200top/fw-msg.h | ||
4 | * | ||
5 | * Copyright (C) 2009 Intel Corporation. All rights reserved. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License version | ||
9 | * 2 as published by the Free Software Foundation. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
19 | * 02110-1301, USA. | ||
20 | * | ||
21 | * | ||
22 | * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com> | ||
23 | * - | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #ifndef __FWMSG_H__ | ||
28 | #define __FWMSG_H__ | ||
29 | |||
30 | #define COMM_TYPE_D2H 0xFF | ||
31 | #define COMM_TYPE_H2D 0xEE | ||
32 | |||
33 | #define COMM_CATEGORY_OPERATIONAL 0x00 | ||
34 | #define COMM_CATEGORY_DEBUG 0x01 | ||
35 | #define COMM_CATEGORY_TESTABILITY 0x02 | ||
36 | #define COMM_CATEGORY_DIAGNOSTICS 0x03 | ||
37 | |||
38 | #define OP_DBG_ZSTR_MSG cpu_to_le16(0x1A) | ||
39 | |||
40 | #define FW_LOG_SRC_MAX 32 | ||
41 | #define FW_LOG_SRC_ALL 255 | ||
42 | |||
43 | #define FW_STRING_TABLE_ADDR cpu_to_le32(0x0C000000) | ||
44 | |||
45 | #define CMD_DBG_LOG_LEVEL cpu_to_le16(0x0001) | ||
46 | #define CMD_TST_DEV_RESET cpu_to_le16(0x0060) | ||
47 | #define CMD_TST_FUNC_RESET cpu_to_le16(0x0062) | ||
48 | #define CMD_TST_IFACE_RESET cpu_to_le16(0x0064) | ||
49 | #define CMD_TST_CPU_UTILIZATION cpu_to_le16(0x0065) | ||
50 | #define CMD_TST_TOP_DEEP_SLEEP cpu_to_le16(0x0080) | ||
51 | #define CMD_TST_WAKEUP cpu_to_le16(0x0081) | ||
52 | #define CMD_TST_FUNC_WAKEUP cpu_to_le16(0x0082) | ||
53 | #define CMD_TST_FUNC_DEEP_SLEEP_REQUEST cpu_to_le16(0x0083) | ||
54 | #define CMD_TST_GET_MEM_DUMP cpu_to_le16(0x0096) | ||
55 | |||
56 | #define OP_OPR_ALIVE cpu_to_le16(0x0010) | ||
57 | #define OP_OPR_CMD_ACK cpu_to_le16(0x001F) | ||
58 | #define OP_OPR_CMD_NACK cpu_to_le16(0x0020) | ||
59 | #define OP_TST_MEM_DUMP cpu_to_le16(0x0043) | ||
60 | |||
61 | #define CMD_FLAG_PADDING_256 0x80 | ||
62 | |||
63 | #define FW_HCMD_BLOCK_SIZE 256 | ||
64 | |||
65 | struct msg_hdr { | ||
66 | u8 type; | ||
67 | u8 category; | ||
68 | __le16 opcode; | ||
69 | u8 seqnum; | ||
70 | u8 flags; | ||
71 | __le16 length; | ||
72 | } __attribute__((__packed__)); | ||
73 | |||
74 | struct log_hdr { | ||
75 | __le32 timestamp; | ||
76 | u8 severity; | ||
77 | u8 logsource; | ||
78 | __le16 reserved; | ||
79 | } __attribute__((__packed__)); | ||
80 | |||
81 | struct mdump_hdr { | ||
82 | u8 dmpid; | ||
83 | u8 frag; | ||
84 | __le16 size; | ||
85 | __le32 addr; | ||
86 | } __attribute__((__packed__)); | ||
87 | |||
88 | struct top_msg { | ||
89 | struct msg_hdr hdr; | ||
90 | union { | ||
91 | /* D2H messages */ | ||
92 | struct { | ||
93 | struct log_hdr log_hdr; | ||
94 | u8 data[1]; | ||
95 | } __attribute__((__packed__)) log; | ||
96 | |||
97 | struct { | ||
98 | struct log_hdr log_hdr; | ||
99 | struct mdump_hdr md_hdr; | ||
100 | u8 data[1]; | ||
101 | } __attribute__((__packed__)) mdump; | ||
102 | |||
103 | /* H2D messages */ | ||
104 | struct { | ||
105 | u8 logsource; | ||
106 | u8 sevmask; | ||
107 | } __attribute__((__packed__)) logdefs[FW_LOG_SRC_MAX]; | ||
108 | struct mdump_hdr mdump_req; | ||
109 | } u; | ||
110 | } __attribute__((__packed__)); | ||
111 | |||
112 | |||
113 | #endif /* __FWMSG_H__ */ | ||
diff --git a/drivers/misc/iwmc3200top/iwmc3200top.h b/drivers/misc/iwmc3200top/iwmc3200top.h new file mode 100644 index 000000000000..43bd510e1872 --- /dev/null +++ b/drivers/misc/iwmc3200top/iwmc3200top.h | |||
@@ -0,0 +1,209 @@ | |||
1 | /* | ||
2 | * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver | ||
3 | * drivers/misc/iwmc3200top/iwmc3200top.h | ||
4 | * | ||
5 | * Copyright (C) 2009 Intel Corporation. All rights reserved. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License version | ||
9 | * 2 as published by the Free Software Foundation. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
19 | * 02110-1301, USA. | ||
20 | * | ||
21 | * | ||
22 | * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com> | ||
23 | * - | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #ifndef __IWMC3200TOP_H__ | ||
28 | #define __IWMC3200TOP_H__ | ||
29 | |||
30 | #include <linux/workqueue.h> | ||
31 | |||
32 | #define DRV_NAME "iwmc3200top" | ||
33 | #define FW_API_VER 1 | ||
34 | #define _FW_NAME(api) DRV_NAME "." #api ".fw" | ||
35 | #define FW_NAME(api) _FW_NAME(api) | ||
36 | |||
37 | #define IWMC_SDIO_BLK_SIZE 256 | ||
38 | #define IWMC_DEFAULT_TR_BLK 64 | ||
39 | #define IWMC_SDIO_DATA_ADDR 0x0 | ||
40 | #define IWMC_SDIO_INTR_ENABLE_ADDR 0x14 | ||
41 | #define IWMC_SDIO_INTR_STATUS_ADDR 0x13 | ||
42 | #define IWMC_SDIO_INTR_CLEAR_ADDR 0x13 | ||
43 | #define IWMC_SDIO_INTR_GET_SIZE_ADDR 0x2C | ||
44 | |||
45 | #define COMM_HUB_HEADER_LENGTH 16 | ||
46 | #define LOGGER_HEADER_LENGTH 10 | ||
47 | |||
48 | |||
49 | #define BARKER_DNLOAD_BT_POS 0 | ||
50 | #define BARKER_DNLOAD_BT_MSK BIT(BARKER_DNLOAD_BT_POS) | ||
51 | #define BARKER_DNLOAD_GPS_POS 1 | ||
52 | #define BARKER_DNLOAD_GPS_MSK BIT(BARKER_DNLOAD_GPS_POS) | ||
53 | #define BARKER_DNLOAD_TOP_POS 2 | ||
54 | #define BARKER_DNLOAD_TOP_MSK BIT(BARKER_DNLOAD_TOP_POS) | ||
55 | #define BARKER_DNLOAD_RESERVED1_POS 3 | ||
56 | #define BARKER_DNLOAD_RESERVED1_MSK BIT(BARKER_DNLOAD_RESERVED1_POS) | ||
57 | #define BARKER_DNLOAD_JUMP_POS 4 | ||
58 | #define BARKER_DNLOAD_JUMP_MSK BIT(BARKER_DNLOAD_JUMP_POS) | ||
59 | #define BARKER_DNLOAD_SYNC_POS 5 | ||
60 | #define BARKER_DNLOAD_SYNC_MSK BIT(BARKER_DNLOAD_SYNC_POS) | ||
61 | #define BARKER_DNLOAD_RESERVED2_POS 6 | ||
62 | #define BARKER_DNLOAD_RESERVED2_MSK (0x3 << BARKER_DNLOAD_RESERVED2_POS) | ||
63 | #define BARKER_DNLOAD_BARKER_POS 8 | ||
64 | #define BARKER_DNLOAD_BARKER_MSK (0xffffff << BARKER_DNLOAD_BARKER_POS) | ||
65 | |||
66 | #define IWMC_BARKER_REBOOT (0xdeadbe << BARKER_DNLOAD_BARKER_POS) | ||
67 | /* whole field barker */ | ||
68 | #define IWMC_BARKER_ACK 0xfeedbabe | ||
69 | |||
70 | #define IWMC_CMD_SIGNATURE 0xcbbc | ||
71 | |||
72 | #define CMD_HDR_OPCODE_POS 0 | ||
73 | #define CMD_HDR_OPCODE_MSK_MSK (0xf << CMD_HDR_OPCODE_MSK_POS) | ||
74 | #define CMD_HDR_RESPONSE_CODE_POS 4 | ||
75 | #define CMD_HDR_RESPONSE_CODE_MSK (0xf << CMD_HDR_RESPONSE_CODE_POS) | ||
76 | #define CMD_HDR_USE_CHECKSUM_POS 8 | ||
77 | #define CMD_HDR_USE_CHECKSUM_MSK BIT(CMD_HDR_USE_CHECKSUM_POS) | ||
78 | #define CMD_HDR_RESPONSE_REQUIRED_POS 9 | ||
79 | #define CMD_HDR_RESPONSE_REQUIRED_MSK BIT(CMD_HDR_RESPONSE_REQUIRED_POS) | ||
80 | #define CMD_HDR_DIRECT_ACCESS_POS 10 | ||
81 | #define CMD_HDR_DIRECT_ACCESS_MSK BIT(CMD_HDR_DIRECT_ACCESS_POS) | ||
82 | #define CMD_HDR_RESERVED_POS 11 | ||
83 | #define CMD_HDR_RESERVED_MSK BIT(0x1f << CMD_HDR_RESERVED_POS) | ||
84 | #define CMD_HDR_SIGNATURE_POS 16 | ||
85 | #define CMD_HDR_SIGNATURE_MSK BIT(0xffff << CMD_HDR_SIGNATURE_POS) | ||
86 | |||
87 | enum { | ||
88 | IWMC_OPCODE_PING = 0, | ||
89 | IWMC_OPCODE_READ = 1, | ||
90 | IWMC_OPCODE_WRITE = 2, | ||
91 | IWMC_OPCODE_JUMP = 3, | ||
92 | IWMC_OPCODE_REBOOT = 4, | ||
93 | IWMC_OPCODE_PERSISTENT_WRITE = 5, | ||
94 | IWMC_OPCODE_PERSISTENT_READ = 6, | ||
95 | IWMC_OPCODE_READ_MODIFY_WRITE = 7, | ||
96 | IWMC_OPCODE_LAST_COMMAND = 15 | ||
97 | }; | ||
98 | |||
99 | struct iwmct_fw_load_hdr { | ||
100 | __le32 cmd; | ||
101 | __le32 target_addr; | ||
102 | __le32 data_size; | ||
103 | __le32 block_chksm; | ||
104 | u8 data[0]; | ||
105 | }; | ||
106 | |||
107 | /** | ||
108 | * struct iwmct_fw_hdr | ||
109 | * holds all sw components versions | ||
110 | */ | ||
111 | struct iwmct_fw_hdr { | ||
112 | u8 top_major; | ||
113 | u8 top_minor; | ||
114 | u8 top_revision; | ||
115 | u8 gps_major; | ||
116 | u8 gps_minor; | ||
117 | u8 gps_revision; | ||
118 | u8 bt_major; | ||
119 | u8 bt_minor; | ||
120 | u8 bt_revision; | ||
121 | u8 tic_name[31]; | ||
122 | }; | ||
123 | |||
124 | /** | ||
125 | * struct iwmct_fw_sec_hdr | ||
126 | * @type: function type | ||
127 | * @data_size: section's data size | ||
128 | * @target_addr: download address | ||
129 | */ | ||
130 | struct iwmct_fw_sec_hdr { | ||
131 | u8 type[4]; | ||
132 | __le32 data_size; | ||
133 | __le32 target_addr; | ||
134 | }; | ||
135 | |||
136 | /** | ||
137 | * struct iwmct_parser | ||
138 | * @file: fw image | ||
139 | * @file_size: fw size | ||
140 | * @cur_pos: position in file | ||
141 | * @buf: temp buf for download | ||
142 | * @buf_size: size of buf | ||
143 | * @entry_point: address to jump in fw kick-off | ||
144 | */ | ||
145 | struct iwmct_parser { | ||
146 | const u8 *file; | ||
147 | size_t file_size; | ||
148 | size_t cur_pos; | ||
149 | u8 *buf; | ||
150 | size_t buf_size; | ||
151 | u32 entry_point; | ||
152 | struct iwmct_fw_hdr versions; | ||
153 | }; | ||
154 | |||
155 | |||
156 | struct iwmct_work_struct { | ||
157 | struct list_head list; | ||
158 | ssize_t iosize; | ||
159 | }; | ||
160 | |||
161 | struct iwmct_dbg { | ||
162 | int blocks; | ||
163 | bool dump; | ||
164 | bool jump; | ||
165 | bool direct; | ||
166 | bool checksum; | ||
167 | bool fw_download; | ||
168 | int block_size; | ||
169 | int download_trans_blks; | ||
170 | |||
171 | char label_fw[256]; | ||
172 | }; | ||
173 | |||
174 | struct iwmct_debugfs; | ||
175 | |||
176 | struct iwmct_priv { | ||
177 | struct sdio_func *func; | ||
178 | struct iwmct_debugfs *dbgfs; | ||
179 | struct iwmct_parser parser; | ||
180 | atomic_t reset; | ||
181 | atomic_t dev_sync; | ||
182 | u32 trans_len; | ||
183 | u32 barker; | ||
184 | struct iwmct_dbg dbg; | ||
185 | |||
186 | /* drivers work queue */ | ||
187 | struct workqueue_struct *wq; | ||
188 | struct workqueue_struct *bus_rescan_wq; | ||
189 | struct work_struct bus_rescan_worker; | ||
190 | struct work_struct isr_worker; | ||
191 | |||
192 | /* drivers wait queue */ | ||
193 | wait_queue_head_t wait_q; | ||
194 | |||
195 | /* rx request list */ | ||
196 | struct list_head read_req_list; | ||
197 | }; | ||
198 | |||
199 | extern int iwmct_tx(struct iwmct_priv *priv, unsigned int addr, | ||
200 | void *src, int count); | ||
201 | |||
202 | extern int iwmct_fw_load(struct iwmct_priv *priv); | ||
203 | |||
204 | extern void iwmct_dbg_init_params(struct iwmct_priv *drv); | ||
205 | extern void iwmct_dbg_init_drv_attrs(struct device_driver *drv); | ||
206 | extern void iwmct_dbg_remove_drv_attrs(struct device_driver *drv); | ||
207 | extern int iwmct_send_hcmd(struct iwmct_priv *priv, u8 *cmd, u16 len); | ||
208 | |||
209 | #endif /* __IWMC3200TOP_H__ */ | ||
diff --git a/drivers/misc/iwmc3200top/log.c b/drivers/misc/iwmc3200top/log.c new file mode 100644 index 000000000000..d569279698f6 --- /dev/null +++ b/drivers/misc/iwmc3200top/log.c | |||
@@ -0,0 +1,347 @@ | |||
1 | /* | ||
2 | * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver | ||
3 | * drivers/misc/iwmc3200top/log.c | ||
4 | * | ||
5 | * Copyright (C) 2009 Intel Corporation. All rights reserved. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License version | ||
9 | * 2 as published by the Free Software Foundation. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
19 | * 02110-1301, USA. | ||
20 | * | ||
21 | * | ||
22 | * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com> | ||
23 | * - | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #include <linux/kernel.h> | ||
28 | #include <linux/mmc/sdio_func.h> | ||
29 | #include <linux/ctype.h> | ||
30 | #include "fw-msg.h" | ||
31 | #include "iwmc3200top.h" | ||
32 | #include "log.h" | ||
33 | |||
34 | /* Maximal hexadecimal string size of the FW memdump message */ | ||
35 | #define LOG_MSG_SIZE_MAX 12400 | ||
36 | |||
37 | /* iwmct_logdefs is a global used by log macros */ | ||
38 | u8 iwmct_logdefs[LOG_SRC_MAX]; | ||
39 | static u8 iwmct_fw_logdefs[FW_LOG_SRC_MAX]; | ||
40 | |||
41 | |||
42 | static int _log_set_log_filter(u8 *logdefs, int size, u8 src, u8 logmask) | ||
43 | { | ||
44 | int i; | ||
45 | |||
46 | if (src < size) | ||
47 | logdefs[src] = logmask; | ||
48 | else if (src == LOG_SRC_ALL) | ||
49 | for (i = 0; i < size; i++) | ||
50 | logdefs[i] = logmask; | ||
51 | else | ||
52 | return -1; | ||
53 | |||
54 | return 0; | ||
55 | } | ||
56 | |||
57 | |||
58 | int iwmct_log_set_filter(u8 src, u8 logmask) | ||
59 | { | ||
60 | return _log_set_log_filter(iwmct_logdefs, LOG_SRC_MAX, src, logmask); | ||
61 | } | ||
62 | |||
63 | |||
64 | int iwmct_log_set_fw_filter(u8 src, u8 logmask) | ||
65 | { | ||
66 | return _log_set_log_filter(iwmct_fw_logdefs, | ||
67 | FW_LOG_SRC_MAX, src, logmask); | ||
68 | } | ||
69 | |||
70 | |||
71 | static int log_msg_format_hex(char *str, int slen, u8 *ibuf, | ||
72 | int ilen, char *pref) | ||
73 | { | ||
74 | int pos = 0; | ||
75 | int i; | ||
76 | int len; | ||
77 | |||
78 | for (pos = 0, i = 0; pos < slen - 2 && pref[i] != '\0'; i++, pos++) | ||
79 | str[pos] = pref[i]; | ||
80 | |||
81 | for (i = 0; pos < slen - 2 && i < ilen; pos += len, i++) | ||
82 | len = snprintf(&str[pos], slen - pos - 1, " %2.2X", ibuf[i]); | ||
83 | |||
84 | if (i < ilen) | ||
85 | return -1; | ||
86 | |||
87 | return 0; | ||
88 | } | ||
89 | |||
90 | /* NOTE: This function is not thread safe. | ||
91 | Currently it's called only from sdio rx worker - no race there | ||
92 | */ | ||
93 | void iwmct_log_top_message(struct iwmct_priv *priv, u8 *buf, int len) | ||
94 | { | ||
95 | struct top_msg *msg; | ||
96 | static char logbuf[LOG_MSG_SIZE_MAX]; | ||
97 | |||
98 | msg = (struct top_msg *)buf; | ||
99 | |||
100 | if (len < sizeof(msg->hdr) + sizeof(msg->u.log.log_hdr)) { | ||
101 | LOG_ERROR(priv, FW_MSG, "Log message from TOP " | ||
102 | "is too short %d (expected %zd)\n", | ||
103 | len, sizeof(msg->hdr) + sizeof(msg->u.log.log_hdr)); | ||
104 | return; | ||
105 | } | ||
106 | |||
107 | if (!(iwmct_fw_logdefs[msg->u.log.log_hdr.logsource] & | ||
108 | BIT(msg->u.log.log_hdr.severity)) || | ||
109 | !(iwmct_logdefs[LOG_SRC_FW_MSG] & BIT(msg->u.log.log_hdr.severity))) | ||
110 | return; | ||
111 | |||
112 | switch (msg->hdr.category) { | ||
113 | case COMM_CATEGORY_TESTABILITY: | ||
114 | if (!(iwmct_logdefs[LOG_SRC_TST] & | ||
115 | BIT(msg->u.log.log_hdr.severity))) | ||
116 | return; | ||
117 | if (log_msg_format_hex(logbuf, LOG_MSG_SIZE_MAX, buf, | ||
118 | le16_to_cpu(msg->hdr.length) + | ||
119 | sizeof(msg->hdr), "<TST>")) | ||
120 | LOG_WARNING(priv, TST, | ||
121 | "TOP TST message is too long, truncating..."); | ||
122 | LOG_WARNING(priv, TST, "%s\n", logbuf); | ||
123 | break; | ||
124 | case COMM_CATEGORY_DEBUG: | ||
125 | if (msg->hdr.opcode == OP_DBG_ZSTR_MSG) | ||
126 | LOG_INFO(priv, FW_MSG, "%s %s", "<DBG>", | ||
127 | ((u8 *)msg) + sizeof(msg->hdr) | ||
128 | + sizeof(msg->u.log.log_hdr)); | ||
129 | else { | ||
130 | if (log_msg_format_hex(logbuf, LOG_MSG_SIZE_MAX, buf, | ||
131 | le16_to_cpu(msg->hdr.length) | ||
132 | + sizeof(msg->hdr), | ||
133 | "<DBG>")) | ||
134 | LOG_WARNING(priv, FW_MSG, | ||
135 | "TOP DBG message is too long," | ||
136 | "truncating..."); | ||
137 | LOG_WARNING(priv, FW_MSG, "%s\n", logbuf); | ||
138 | } | ||
139 | break; | ||
140 | default: | ||
141 | break; | ||
142 | } | ||
143 | } | ||
144 | |||
145 | static int _log_get_filter_str(u8 *logdefs, int logdefsz, char *buf, int size) | ||
146 | { | ||
147 | int i, pos, len; | ||
148 | for (i = 0, pos = 0; (pos < size-1) && (i < logdefsz); i++) { | ||
149 | len = snprintf(&buf[pos], size - pos - 1, "0x%02X%02X,", | ||
150 | i, logdefs[i]); | ||
151 | pos += len; | ||
152 | } | ||
153 | buf[pos-1] = '\n'; | ||
154 | buf[pos] = '\0'; | ||
155 | |||
156 | if (i < logdefsz) | ||
157 | return -1; | ||
158 | return 0; | ||
159 | } | ||
160 | |||
161 | int log_get_filter_str(char *buf, int size) | ||
162 | { | ||
163 | return _log_get_filter_str(iwmct_logdefs, LOG_SRC_MAX, buf, size); | ||
164 | } | ||
165 | |||
166 | int log_get_fw_filter_str(char *buf, int size) | ||
167 | { | ||
168 | return _log_get_filter_str(iwmct_fw_logdefs, FW_LOG_SRC_MAX, buf, size); | ||
169 | } | ||
170 | |||
171 | #define HEXADECIMAL_RADIX 16 | ||
172 | #define LOG_SRC_FORMAT 7 /* log level is in format of "0xXXXX," */ | ||
173 | |||
174 | ssize_t show_iwmct_log_level(struct device *d, | ||
175 | struct device_attribute *attr, char *buf) | ||
176 | { | ||
177 | struct iwmct_priv *priv = dev_get_drvdata(d); | ||
178 | char *str_buf; | ||
179 | int buf_size; | ||
180 | ssize_t ret; | ||
181 | |||
182 | buf_size = (LOG_SRC_FORMAT * LOG_SRC_MAX) + 1; | ||
183 | str_buf = kzalloc(buf_size, GFP_KERNEL); | ||
184 | if (!str_buf) { | ||
185 | LOG_ERROR(priv, DEBUGFS, | ||
186 | "failed to allocate %d bytes\n", buf_size); | ||
187 | ret = -ENOMEM; | ||
188 | goto exit; | ||
189 | } | ||
190 | |||
191 | if (log_get_filter_str(str_buf, buf_size) < 0) { | ||
192 | ret = -EINVAL; | ||
193 | goto exit; | ||
194 | } | ||
195 | |||
196 | ret = sprintf(buf, "%s", str_buf); | ||
197 | |||
198 | exit: | ||
199 | kfree(str_buf); | ||
200 | return ret; | ||
201 | } | ||
202 | |||
203 | ssize_t store_iwmct_log_level(struct device *d, | ||
204 | struct device_attribute *attr, | ||
205 | const char *buf, size_t count) | ||
206 | { | ||
207 | struct iwmct_priv *priv = dev_get_drvdata(d); | ||
208 | char *token, *str_buf = NULL; | ||
209 | long val; | ||
210 | ssize_t ret = count; | ||
211 | u8 src, mask; | ||
212 | |||
213 | if (!count) | ||
214 | goto exit; | ||
215 | |||
216 | str_buf = kzalloc(count, GFP_KERNEL); | ||
217 | if (!str_buf) { | ||
218 | LOG_ERROR(priv, DEBUGFS, | ||
219 | "failed to allocate %zd bytes\n", count); | ||
220 | ret = -ENOMEM; | ||
221 | goto exit; | ||
222 | } | ||
223 | |||
224 | memcpy(str_buf, buf, count); | ||
225 | |||
226 | while ((token = strsep(&str_buf, ",")) != NULL) { | ||
227 | while (isspace(*token)) | ||
228 | ++token; | ||
229 | if (strict_strtol(token, HEXADECIMAL_RADIX, &val)) { | ||
230 | LOG_ERROR(priv, DEBUGFS, | ||
231 | "failed to convert string to long %s\n", | ||
232 | token); | ||
233 | ret = -EINVAL; | ||
234 | goto exit; | ||
235 | } | ||
236 | |||
237 | mask = val & 0xFF; | ||
238 | src = (val & 0XFF00) >> 8; | ||
239 | iwmct_log_set_filter(src, mask); | ||
240 | } | ||
241 | |||
242 | exit: | ||
243 | kfree(str_buf); | ||
244 | return ret; | ||
245 | } | ||
246 | |||
247 | ssize_t show_iwmct_log_level_fw(struct device *d, | ||
248 | struct device_attribute *attr, char *buf) | ||
249 | { | ||
250 | struct iwmct_priv *priv = dev_get_drvdata(d); | ||
251 | char *str_buf; | ||
252 | int buf_size; | ||
253 | ssize_t ret; | ||
254 | |||
255 | buf_size = (LOG_SRC_FORMAT * FW_LOG_SRC_MAX) + 2; | ||
256 | |||
257 | str_buf = kzalloc(buf_size, GFP_KERNEL); | ||
258 | if (!str_buf) { | ||
259 | LOG_ERROR(priv, DEBUGFS, | ||
260 | "failed to allocate %d bytes\n", buf_size); | ||
261 | ret = -ENOMEM; | ||
262 | goto exit; | ||
263 | } | ||
264 | |||
265 | if (log_get_fw_filter_str(str_buf, buf_size) < 0) { | ||
266 | ret = -EINVAL; | ||
267 | goto exit; | ||
268 | } | ||
269 | |||
270 | ret = sprintf(buf, "%s", str_buf); | ||
271 | |||
272 | exit: | ||
273 | kfree(str_buf); | ||
274 | return ret; | ||
275 | } | ||
276 | |||
277 | ssize_t store_iwmct_log_level_fw(struct device *d, | ||
278 | struct device_attribute *attr, | ||
279 | const char *buf, size_t count) | ||
280 | { | ||
281 | struct iwmct_priv *priv = dev_get_drvdata(d); | ||
282 | struct top_msg cmd; | ||
283 | char *token, *str_buf = NULL; | ||
284 | ssize_t ret = count; | ||
285 | u16 cmdlen = 0; | ||
286 | int i; | ||
287 | long val; | ||
288 | u8 src, mask; | ||
289 | |||
290 | if (!count) | ||
291 | goto exit; | ||
292 | |||
293 | str_buf = kzalloc(count, GFP_KERNEL); | ||
294 | if (!str_buf) { | ||
295 | LOG_ERROR(priv, DEBUGFS, | ||
296 | "failed to allocate %zd bytes\n", count); | ||
297 | ret = -ENOMEM; | ||
298 | goto exit; | ||
299 | } | ||
300 | |||
301 | memcpy(str_buf, buf, count); | ||
302 | |||
303 | cmd.hdr.type = COMM_TYPE_H2D; | ||
304 | cmd.hdr.category = COMM_CATEGORY_DEBUG; | ||
305 | cmd.hdr.opcode = CMD_DBG_LOG_LEVEL; | ||
306 | |||
307 | for (i = 0; ((token = strsep(&str_buf, ",")) != NULL) && | ||
308 | (i < FW_LOG_SRC_MAX); i++) { | ||
309 | |||
310 | while (isspace(*token)) | ||
311 | ++token; | ||
312 | |||
313 | if (strict_strtol(token, HEXADECIMAL_RADIX, &val)) { | ||
314 | LOG_ERROR(priv, DEBUGFS, | ||
315 | "failed to convert string to long %s\n", | ||
316 | token); | ||
317 | ret = -EINVAL; | ||
318 | goto exit; | ||
319 | } | ||
320 | |||
321 | mask = val & 0xFF; /* LSB */ | ||
322 | src = (val & 0XFF00) >> 8; /* 2nd least significant byte. */ | ||
323 | iwmct_log_set_fw_filter(src, mask); | ||
324 | |||
325 | cmd.u.logdefs[i].logsource = src; | ||
326 | cmd.u.logdefs[i].sevmask = mask; | ||
327 | } | ||
328 | |||
329 | cmd.hdr.length = cpu_to_le16(i * sizeof(cmd.u.logdefs[0])); | ||
330 | cmdlen = (i * sizeof(cmd.u.logdefs[0]) + sizeof(cmd.hdr)); | ||
331 | |||
332 | ret = iwmct_send_hcmd(priv, (u8 *)&cmd, cmdlen); | ||
333 | if (ret) { | ||
334 | LOG_ERROR(priv, DEBUGFS, | ||
335 | "Failed to send %d bytes of fwcmd, ret=%zd\n", | ||
336 | cmdlen, ret); | ||
337 | goto exit; | ||
338 | } else | ||
339 | LOG_INFO(priv, DEBUGFS, "fwcmd sent (%d bytes)\n", cmdlen); | ||
340 | |||
341 | ret = count; | ||
342 | |||
343 | exit: | ||
344 | kfree(str_buf); | ||
345 | return ret; | ||
346 | } | ||
347 | |||
diff --git a/drivers/misc/iwmc3200top/log.h b/drivers/misc/iwmc3200top/log.h new file mode 100644 index 000000000000..aba8121f978c --- /dev/null +++ b/drivers/misc/iwmc3200top/log.h | |||
@@ -0,0 +1,158 @@ | |||
1 | /* | ||
2 | * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver | ||
3 | * drivers/misc/iwmc3200top/log.h | ||
4 | * | ||
5 | * Copyright (C) 2009 Intel Corporation. All rights reserved. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License version | ||
9 | * 2 as published by the Free Software Foundation. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
19 | * 02110-1301, USA. | ||
20 | * | ||
21 | * | ||
22 | * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com> | ||
23 | * - | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #ifndef __LOG_H__ | ||
28 | #define __LOG_H__ | ||
29 | |||
30 | |||
31 | /* log severity: | ||
32 | * The log levels here match FW log levels | ||
33 | * so values need to stay as is */ | ||
34 | #define LOG_SEV_CRITICAL 0 | ||
35 | #define LOG_SEV_ERROR 1 | ||
36 | #define LOG_SEV_WARNING 2 | ||
37 | #define LOG_SEV_INFO 3 | ||
38 | #define LOG_SEV_INFOEX 4 | ||
39 | |||
40 | #define LOG_SEV_FILTER_ALL \ | ||
41 | (BIT(LOG_SEV_CRITICAL) | \ | ||
42 | BIT(LOG_SEV_ERROR) | \ | ||
43 | BIT(LOG_SEV_WARNING) | \ | ||
44 | BIT(LOG_SEV_INFO) | \ | ||
45 | BIT(LOG_SEV_INFOEX)) | ||
46 | |||
47 | /* log source */ | ||
48 | #define LOG_SRC_INIT 0 | ||
49 | #define LOG_SRC_DEBUGFS 1 | ||
50 | #define LOG_SRC_FW_DOWNLOAD 2 | ||
51 | #define LOG_SRC_FW_MSG 3 | ||
52 | #define LOG_SRC_TST 4 | ||
53 | #define LOG_SRC_IRQ 5 | ||
54 | |||
55 | #define LOG_SRC_MAX 6 | ||
56 | #define LOG_SRC_ALL 0xFF | ||
57 | |||
58 | /** | ||
59 | * Default intitialization runtime log level | ||
60 | */ | ||
61 | #ifndef LOG_SEV_FILTER_RUNTIME | ||
62 | #define LOG_SEV_FILTER_RUNTIME \ | ||
63 | (BIT(LOG_SEV_CRITICAL) | \ | ||
64 | BIT(LOG_SEV_ERROR) | \ | ||
65 | BIT(LOG_SEV_WARNING)) | ||
66 | #endif | ||
67 | |||
68 | #ifndef FW_LOG_SEV_FILTER_RUNTIME | ||
69 | #define FW_LOG_SEV_FILTER_RUNTIME LOG_SEV_FILTER_ALL | ||
70 | #endif | ||
71 | |||
72 | #ifdef CONFIG_IWMC3200TOP_DEBUG | ||
73 | /** | ||
74 | * Log macros | ||
75 | */ | ||
76 | |||
77 | #define priv2dev(priv) (&(priv->func)->dev) | ||
78 | |||
79 | #define LOG_CRITICAL(priv, src, fmt, args...) \ | ||
80 | do { \ | ||
81 | if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_CRITICAL)) \ | ||
82 | dev_crit(priv2dev(priv), "%s %d: " fmt, \ | ||
83 | __func__, __LINE__, ##args); \ | ||
84 | } while (0) | ||
85 | |||
86 | #define LOG_ERROR(priv, src, fmt, args...) \ | ||
87 | do { \ | ||
88 | if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_ERROR)) \ | ||
89 | dev_err(priv2dev(priv), "%s %d: " fmt, \ | ||
90 | __func__, __LINE__, ##args); \ | ||
91 | } while (0) | ||
92 | |||
93 | #define LOG_WARNING(priv, src, fmt, args...) \ | ||
94 | do { \ | ||
95 | if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_WARNING)) \ | ||
96 | dev_warn(priv2dev(priv), "%s %d: " fmt, \ | ||
97 | __func__, __LINE__, ##args); \ | ||
98 | } while (0) | ||
99 | |||
100 | #define LOG_INFO(priv, src, fmt, args...) \ | ||
101 | do { \ | ||
102 | if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_INFO)) \ | ||
103 | dev_info(priv2dev(priv), "%s %d: " fmt, \ | ||
104 | __func__, __LINE__, ##args); \ | ||
105 | } while (0) | ||
106 | |||
107 | #define LOG_INFOEX(priv, src, fmt, args...) \ | ||
108 | do { \ | ||
109 | if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_INFOEX)) \ | ||
110 | dev_dbg(priv2dev(priv), "%s %d: " fmt, \ | ||
111 | __func__, __LINE__, ##args); \ | ||
112 | } while (0) | ||
113 | |||
114 | #define LOG_HEXDUMP(src, ptr, len) \ | ||
115 | do { \ | ||
116 | if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_INFOEX)) \ | ||
117 | print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_NONE, \ | ||
118 | 16, 1, ptr, len, false); \ | ||
119 | } while (0) | ||
120 | |||
121 | void iwmct_log_top_message(struct iwmct_priv *priv, u8 *buf, int len); | ||
122 | |||
123 | extern u8 iwmct_logdefs[]; | ||
124 | |||
125 | int iwmct_log_set_filter(u8 src, u8 logmask); | ||
126 | int iwmct_log_set_fw_filter(u8 src, u8 logmask); | ||
127 | |||
128 | ssize_t show_iwmct_log_level(struct device *d, | ||
129 | struct device_attribute *attr, char *buf); | ||
130 | ssize_t store_iwmct_log_level(struct device *d, | ||
131 | struct device_attribute *attr, | ||
132 | const char *buf, size_t count); | ||
133 | ssize_t show_iwmct_log_level_fw(struct device *d, | ||
134 | struct device_attribute *attr, char *buf); | ||
135 | ssize_t store_iwmct_log_level_fw(struct device *d, | ||
136 | struct device_attribute *attr, | ||
137 | const char *buf, size_t count); | ||
138 | |||
139 | #else | ||
140 | |||
141 | #define LOG_CRITICAL(priv, src, fmt, args...) | ||
142 | #define LOG_ERROR(priv, src, fmt, args...) | ||
143 | #define LOG_WARNING(priv, src, fmt, args...) | ||
144 | #define LOG_INFO(priv, src, fmt, args...) | ||
145 | #define LOG_INFOEX(priv, src, fmt, args...) | ||
146 | #define LOG_HEXDUMP(src, ptr, len) | ||
147 | |||
148 | static inline void iwmct_log_top_message(struct iwmct_priv *priv, | ||
149 | u8 *buf, int len) {} | ||
150 | static inline int iwmct_log_set_filter(u8 src, u8 logmask) { return 0; } | ||
151 | static inline int iwmct_log_set_fw_filter(u8 src, u8 logmask) { return 0; } | ||
152 | |||
153 | #endif /* CONFIG_IWMC3200TOP_DEBUG */ | ||
154 | |||
155 | int log_get_filter_str(char *buf, int size); | ||
156 | int log_get_fw_filter_str(char *buf, int size); | ||
157 | |||
158 | #endif /* __LOG_H__ */ | ||
diff --git a/drivers/misc/iwmc3200top/main.c b/drivers/misc/iwmc3200top/main.c new file mode 100644 index 000000000000..fafcaa481d74 --- /dev/null +++ b/drivers/misc/iwmc3200top/main.c | |||
@@ -0,0 +1,678 @@ | |||
1 | /* | ||
2 | * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver | ||
3 | * drivers/misc/iwmc3200top/main.c | ||
4 | * | ||
5 | * Copyright (C) 2009 Intel Corporation. All rights reserved. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License version | ||
9 | * 2 as published by the Free Software Foundation. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
19 | * 02110-1301, USA. | ||
20 | * | ||
21 | * | ||
22 | * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com> | ||
23 | * - | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #include <linux/module.h> | ||
28 | #include <linux/init.h> | ||
29 | #include <linux/kernel.h> | ||
30 | #include <linux/debugfs.h> | ||
31 | #include <linux/mmc/sdio_ids.h> | ||
32 | #include <linux/mmc/sdio_func.h> | ||
33 | #include <linux/mmc/sdio.h> | ||
34 | |||
35 | #include "iwmc3200top.h" | ||
36 | #include "log.h" | ||
37 | #include "fw-msg.h" | ||
38 | #include "debugfs.h" | ||
39 | |||
40 | |||
41 | #define DRIVER_DESCRIPTION "Intel(R) IWMC 3200 Top Driver" | ||
42 | #define DRIVER_COPYRIGHT "Copyright (c) 2008 Intel Corporation." | ||
43 | |||
44 | #define DRIVER_VERSION "0.1.62" | ||
45 | |||
46 | MODULE_DESCRIPTION(DRIVER_DESCRIPTION); | ||
47 | MODULE_VERSION(DRIVER_VERSION); | ||
48 | MODULE_LICENSE("GPL"); | ||
49 | MODULE_AUTHOR(DRIVER_COPYRIGHT); | ||
50 | MODULE_FIRMWARE(FW_NAME(FW_API_VER)); | ||
51 | |||
52 | /* | ||
53 | * This workers main task is to wait for OP_OPR_ALIVE | ||
54 | * from TOP FW until ALIVE_MSG_TIMOUT timeout is elapsed. | ||
55 | * When OP_OPR_ALIVE received it will issue | ||
56 | * a call to "bus_rescan_devices". | ||
57 | */ | ||
58 | static void iwmct_rescan_worker(struct work_struct *ws) | ||
59 | { | ||
60 | struct iwmct_priv *priv; | ||
61 | int ret; | ||
62 | |||
63 | priv = container_of(ws, struct iwmct_priv, bus_rescan_worker); | ||
64 | |||
65 | LOG_INFO(priv, FW_MSG, "Calling bus_rescan\n"); | ||
66 | |||
67 | ret = bus_rescan_devices(priv->func->dev.bus); | ||
68 | if (ret < 0) | ||
69 | LOG_INFO(priv, FW_DOWNLOAD, "bus_rescan_devices FAILED!!!\n"); | ||
70 | } | ||
71 | |||
72 | static void op_top_message(struct iwmct_priv *priv, struct top_msg *msg) | ||
73 | { | ||
74 | switch (msg->hdr.opcode) { | ||
75 | case OP_OPR_ALIVE: | ||
76 | LOG_INFO(priv, FW_MSG, "Got ALIVE from device, wake rescan\n"); | ||
77 | queue_work(priv->bus_rescan_wq, &priv->bus_rescan_worker); | ||
78 | break; | ||
79 | default: | ||
80 | LOG_INFO(priv, FW_MSG, "Received msg opcode 0x%X\n", | ||
81 | msg->hdr.opcode); | ||
82 | break; | ||
83 | } | ||
84 | } | ||
85 | |||
86 | |||
87 | static void handle_top_message(struct iwmct_priv *priv, u8 *buf, int len) | ||
88 | { | ||
89 | struct top_msg *msg; | ||
90 | |||
91 | msg = (struct top_msg *)buf; | ||
92 | |||
93 | if (msg->hdr.type != COMM_TYPE_D2H) { | ||
94 | LOG_ERROR(priv, FW_MSG, | ||
95 | "Message from TOP with invalid message type 0x%X\n", | ||
96 | msg->hdr.type); | ||
97 | return; | ||
98 | } | ||
99 | |||
100 | if (len < sizeof(msg->hdr)) { | ||
101 | LOG_ERROR(priv, FW_MSG, | ||
102 | "Message from TOP is too short for message header " | ||
103 | "received %d bytes, expected at least %zd bytes\n", | ||
104 | len, sizeof(msg->hdr)); | ||
105 | return; | ||
106 | } | ||
107 | |||
108 | if (len < le16_to_cpu(msg->hdr.length) + sizeof(msg->hdr)) { | ||
109 | LOG_ERROR(priv, FW_MSG, | ||
110 | "Message length (%d bytes) is shorter than " | ||
111 | "in header (%d bytes)\n", | ||
112 | len, le16_to_cpu(msg->hdr.length)); | ||
113 | return; | ||
114 | } | ||
115 | |||
116 | switch (msg->hdr.category) { | ||
117 | case COMM_CATEGORY_OPERATIONAL: | ||
118 | op_top_message(priv, (struct top_msg *)buf); | ||
119 | break; | ||
120 | |||
121 | case COMM_CATEGORY_DEBUG: | ||
122 | case COMM_CATEGORY_TESTABILITY: | ||
123 | case COMM_CATEGORY_DIAGNOSTICS: | ||
124 | iwmct_log_top_message(priv, buf, len); | ||
125 | break; | ||
126 | |||
127 | default: | ||
128 | LOG_ERROR(priv, FW_MSG, | ||
129 | "Message from TOP with unknown category 0x%X\n", | ||
130 | msg->hdr.category); | ||
131 | break; | ||
132 | } | ||
133 | } | ||
134 | |||
135 | int iwmct_send_hcmd(struct iwmct_priv *priv, u8 *cmd, u16 len) | ||
136 | { | ||
137 | int ret; | ||
138 | u8 *buf; | ||
139 | |||
140 | LOG_INFOEX(priv, FW_MSG, "Sending hcmd:\n"); | ||
141 | |||
142 | /* add padding to 256 for IWMC */ | ||
143 | ((struct top_msg *)cmd)->hdr.flags |= CMD_FLAG_PADDING_256; | ||
144 | |||
145 | LOG_HEXDUMP(FW_MSG, cmd, len); | ||
146 | |||
147 | if (len > FW_HCMD_BLOCK_SIZE) { | ||
148 | LOG_ERROR(priv, FW_MSG, "size %d exceeded hcmd max size %d\n", | ||
149 | len, FW_HCMD_BLOCK_SIZE); | ||
150 | return -1; | ||
151 | } | ||
152 | |||
153 | buf = kzalloc(FW_HCMD_BLOCK_SIZE, GFP_KERNEL); | ||
154 | if (!buf) { | ||
155 | LOG_ERROR(priv, FW_MSG, "kzalloc error, buf size %d\n", | ||
156 | FW_HCMD_BLOCK_SIZE); | ||
157 | return -1; | ||
158 | } | ||
159 | |||
160 | memcpy(buf, cmd, len); | ||
161 | |||
162 | sdio_claim_host(priv->func); | ||
163 | ret = sdio_memcpy_toio(priv->func, IWMC_SDIO_DATA_ADDR, buf, | ||
164 | FW_HCMD_BLOCK_SIZE); | ||
165 | sdio_release_host(priv->func); | ||
166 | |||
167 | kfree(buf); | ||
168 | return ret; | ||
169 | } | ||
170 | |||
171 | int iwmct_tx(struct iwmct_priv *priv, unsigned int addr, | ||
172 | void *src, int count) | ||
173 | { | ||
174 | int ret; | ||
175 | |||
176 | sdio_claim_host(priv->func); | ||
177 | ret = sdio_memcpy_toio(priv->func, addr, src, count); | ||
178 | sdio_release_host(priv->func); | ||
179 | |||
180 | return ret; | ||
181 | } | ||
182 | |||
183 | static void iwmct_irq_read_worker(struct work_struct *ws) | ||
184 | { | ||
185 | struct iwmct_priv *priv; | ||
186 | struct iwmct_work_struct *read_req; | ||
187 | __le32 *buf = NULL; | ||
188 | int ret; | ||
189 | int iosize; | ||
190 | u32 barker; | ||
191 | bool is_barker; | ||
192 | |||
193 | priv = container_of(ws, struct iwmct_priv, isr_worker); | ||
194 | |||
195 | LOG_INFO(priv, IRQ, "enter iwmct_irq_read_worker %p\n", ws); | ||
196 | |||
197 | /* --------------------- Handshake with device -------------------- */ | ||
198 | sdio_claim_host(priv->func); | ||
199 | |||
200 | /* all list manipulations have to be protected by | ||
201 | * sdio_claim_host/sdio_release_host */ | ||
202 | if (list_empty(&priv->read_req_list)) { | ||
203 | LOG_ERROR(priv, IRQ, "read_req_list empty in read worker\n"); | ||
204 | goto exit_release; | ||
205 | } | ||
206 | |||
207 | read_req = list_entry(priv->read_req_list.next, | ||
208 | struct iwmct_work_struct, list); | ||
209 | |||
210 | list_del(&read_req->list); | ||
211 | iosize = read_req->iosize; | ||
212 | kfree(read_req); | ||
213 | |||
214 | buf = kzalloc(iosize, GFP_KERNEL); | ||
215 | if (!buf) { | ||
216 | LOG_ERROR(priv, IRQ, "kzalloc error, buf size %d\n", iosize); | ||
217 | goto exit_release; | ||
218 | } | ||
219 | |||
220 | LOG_INFO(priv, IRQ, "iosize=%d, buf=%p, func=%d\n", | ||
221 | iosize, buf, priv->func->num); | ||
222 | |||
223 | /* read from device */ | ||
224 | ret = sdio_memcpy_fromio(priv->func, buf, IWMC_SDIO_DATA_ADDR, iosize); | ||
225 | if (ret) { | ||
226 | LOG_ERROR(priv, IRQ, "error %d reading buffer\n", ret); | ||
227 | goto exit_release; | ||
228 | } | ||
229 | |||
230 | LOG_HEXDUMP(IRQ, (u8 *)buf, iosize); | ||
231 | |||
232 | barker = le32_to_cpu(buf[0]); | ||
233 | |||
234 | /* Verify whether it's a barker and if not - treat as regular Rx */ | ||
235 | if (barker == IWMC_BARKER_ACK || | ||
236 | (barker & BARKER_DNLOAD_BARKER_MSK) == IWMC_BARKER_REBOOT) { | ||
237 | |||
238 | /* Valid Barker is equal on first 4 dwords */ | ||
239 | is_barker = (buf[1] == buf[0]) && | ||
240 | (buf[2] == buf[0]) && | ||
241 | (buf[3] == buf[0]); | ||
242 | |||
243 | if (!is_barker) { | ||
244 | LOG_WARNING(priv, IRQ, | ||
245 | "Potentially inconsistent barker " | ||
246 | "%08X_%08X_%08X_%08X\n", | ||
247 | le32_to_cpu(buf[0]), le32_to_cpu(buf[1]), | ||
248 | le32_to_cpu(buf[2]), le32_to_cpu(buf[3])); | ||
249 | } | ||
250 | } else { | ||
251 | is_barker = false; | ||
252 | } | ||
253 | |||
254 | /* Handle Top CommHub message */ | ||
255 | if (!is_barker) { | ||
256 | sdio_release_host(priv->func); | ||
257 | handle_top_message(priv, (u8 *)buf, iosize); | ||
258 | goto exit; | ||
259 | } else if (barker == IWMC_BARKER_ACK) { /* Handle barkers */ | ||
260 | if (atomic_read(&priv->dev_sync) == 0) { | ||
261 | LOG_ERROR(priv, IRQ, | ||
262 | "ACK barker arrived out-of-sync\n"); | ||
263 | goto exit_release; | ||
264 | } | ||
265 | |||
266 | /* Continuing to FW download (after Sync is completed)*/ | ||
267 | atomic_set(&priv->dev_sync, 0); | ||
268 | LOG_INFO(priv, IRQ, "ACK barker arrived " | ||
269 | "- starting FW download\n"); | ||
270 | } else { /* REBOOT barker */ | ||
271 | LOG_INFO(priv, IRQ, "Recieved reboot barker: %x\n", barker); | ||
272 | priv->barker = barker; | ||
273 | |||
274 | if (barker & BARKER_DNLOAD_SYNC_MSK) { | ||
275 | /* Send the same barker back */ | ||
276 | ret = sdio_memcpy_toio(priv->func, IWMC_SDIO_DATA_ADDR, | ||
277 | buf, iosize); | ||
278 | if (ret) { | ||
279 | LOG_ERROR(priv, IRQ, | ||
280 | "error %d echoing barker\n", ret); | ||
281 | goto exit_release; | ||
282 | } | ||
283 | LOG_INFO(priv, IRQ, "Echoing barker to device\n"); | ||
284 | atomic_set(&priv->dev_sync, 1); | ||
285 | goto exit_release; | ||
286 | } | ||
287 | |||
288 | /* Continuing to FW download (without Sync) */ | ||
289 | LOG_INFO(priv, IRQ, "No sync requested " | ||
290 | "- starting FW download\n"); | ||
291 | } | ||
292 | |||
293 | sdio_release_host(priv->func); | ||
294 | |||
295 | |||
296 | LOG_INFO(priv, IRQ, "barker download request 0x%x is:\n", priv->barker); | ||
297 | LOG_INFO(priv, IRQ, "******* Top FW %s requested ********\n", | ||
298 | (priv->barker & BARKER_DNLOAD_TOP_MSK) ? "was" : "not"); | ||
299 | LOG_INFO(priv, IRQ, "******* GPS FW %s requested ********\n", | ||
300 | (priv->barker & BARKER_DNLOAD_GPS_MSK) ? "was" : "not"); | ||
301 | LOG_INFO(priv, IRQ, "******* BT FW %s requested ********\n", | ||
302 | (priv->barker & BARKER_DNLOAD_BT_MSK) ? "was" : "not"); | ||
303 | |||
304 | if (priv->dbg.fw_download) | ||
305 | iwmct_fw_load(priv); | ||
306 | else | ||
307 | LOG_ERROR(priv, IRQ, "FW download not allowed\n"); | ||
308 | |||
309 | goto exit; | ||
310 | |||
311 | exit_release: | ||
312 | sdio_release_host(priv->func); | ||
313 | exit: | ||
314 | kfree(buf); | ||
315 | LOG_INFO(priv, IRQ, "exit iwmct_irq_read_worker\n"); | ||
316 | } | ||
317 | |||
318 | static void iwmct_irq(struct sdio_func *func) | ||
319 | { | ||
320 | struct iwmct_priv *priv; | ||
321 | int val, ret; | ||
322 | int iosize; | ||
323 | int addr = IWMC_SDIO_INTR_GET_SIZE_ADDR; | ||
324 | struct iwmct_work_struct *read_req; | ||
325 | |||
326 | priv = sdio_get_drvdata(func); | ||
327 | |||
328 | LOG_INFO(priv, IRQ, "enter iwmct_irq\n"); | ||
329 | |||
330 | /* read the function's status register */ | ||
331 | val = sdio_readb(func, IWMC_SDIO_INTR_STATUS_ADDR, &ret); | ||
332 | |||
333 | LOG_INFO(priv, IRQ, "iir value = %d, ret=%d\n", val, ret); | ||
334 | |||
335 | if (!val) { | ||
336 | LOG_ERROR(priv, IRQ, "iir = 0, exiting ISR\n"); | ||
337 | goto exit_clear_intr; | ||
338 | } | ||
339 | |||
340 | |||
341 | /* | ||
342 | * read 2 bytes of the transaction size | ||
343 | * IMPORTANT: sdio transaction size has to be read before clearing | ||
344 | * sdio interrupt!!! | ||
345 | */ | ||
346 | val = sdio_readb(priv->func, addr++, &ret); | ||
347 | iosize = val; | ||
348 | val = sdio_readb(priv->func, addr++, &ret); | ||
349 | iosize += val << 8; | ||
350 | |||
351 | LOG_INFO(priv, IRQ, "READ size %d\n", iosize); | ||
352 | |||
353 | if (iosize == 0) { | ||
354 | LOG_ERROR(priv, IRQ, "READ size %d, exiting ISR\n", iosize); | ||
355 | goto exit_clear_intr; | ||
356 | } | ||
357 | |||
358 | /* allocate a work structure to pass iosize to the worker */ | ||
359 | read_req = kzalloc(sizeof(struct iwmct_work_struct), GFP_KERNEL); | ||
360 | if (!read_req) { | ||
361 | LOG_ERROR(priv, IRQ, "failed to allocate read_req, exit ISR\n"); | ||
362 | goto exit_clear_intr; | ||
363 | } | ||
364 | |||
365 | INIT_LIST_HEAD(&read_req->list); | ||
366 | read_req->iosize = iosize; | ||
367 | |||
368 | list_add_tail(&priv->read_req_list, &read_req->list); | ||
369 | |||
370 | /* clear the function's interrupt request bit (write 1 to clear) */ | ||
371 | sdio_writeb(func, 1, IWMC_SDIO_INTR_CLEAR_ADDR, &ret); | ||
372 | |||
373 | queue_work(priv->wq, &priv->isr_worker); | ||
374 | |||
375 | LOG_INFO(priv, IRQ, "exit iwmct_irq\n"); | ||
376 | |||
377 | return; | ||
378 | |||
379 | exit_clear_intr: | ||
380 | /* clear the function's interrupt request bit (write 1 to clear) */ | ||
381 | sdio_writeb(func, 1, IWMC_SDIO_INTR_CLEAR_ADDR, &ret); | ||
382 | } | ||
383 | |||
384 | |||
385 | static int blocks; | ||
386 | module_param(blocks, int, 0604); | ||
387 | MODULE_PARM_DESC(blocks, "max_blocks_to_send"); | ||
388 | |||
389 | static int dump; | ||
390 | module_param(dump, bool, 0604); | ||
391 | MODULE_PARM_DESC(dump, "dump_hex_content"); | ||
392 | |||
393 | static int jump = 1; | ||
394 | module_param(jump, bool, 0604); | ||
395 | |||
396 | static int direct = 1; | ||
397 | module_param(direct, bool, 0604); | ||
398 | |||
399 | static int checksum = 1; | ||
400 | module_param(checksum, bool, 0604); | ||
401 | |||
402 | static int fw_download = 1; | ||
403 | module_param(fw_download, bool, 0604); | ||
404 | |||
405 | static int block_size = IWMC_SDIO_BLK_SIZE; | ||
406 | module_param(block_size, int, 0404); | ||
407 | |||
408 | static int download_trans_blks = IWMC_DEFAULT_TR_BLK; | ||
409 | module_param(download_trans_blks, int, 0604); | ||
410 | |||
411 | static int rubbish_barker; | ||
412 | module_param(rubbish_barker, bool, 0604); | ||
413 | |||
414 | #ifdef CONFIG_IWMC3200TOP_DEBUG | ||
415 | static int log_level[LOG_SRC_MAX]; | ||
416 | static unsigned int log_level_argc; | ||
417 | module_param_array(log_level, int, &log_level_argc, 0604); | ||
418 | MODULE_PARM_DESC(log_level, "log_level"); | ||
419 | |||
420 | static int log_level_fw[FW_LOG_SRC_MAX]; | ||
421 | static unsigned int log_level_fw_argc; | ||
422 | module_param_array(log_level_fw, int, &log_level_fw_argc, 0604); | ||
423 | MODULE_PARM_DESC(log_level_fw, "log_level_fw"); | ||
424 | #endif | ||
425 | |||
426 | void iwmct_dbg_init_params(struct iwmct_priv *priv) | ||
427 | { | ||
428 | #ifdef CONFIG_IWMC3200TOP_DEBUG | ||
429 | int i; | ||
430 | |||
431 | for (i = 0; i < log_level_argc; i++) { | ||
432 | dev_notice(&priv->func->dev, "log_level[%d]=0x%X\n", | ||
433 | i, log_level[i]); | ||
434 | iwmct_log_set_filter((log_level[i] >> 8) & 0xFF, | ||
435 | log_level[i] & 0xFF); | ||
436 | } | ||
437 | for (i = 0; i < log_level_fw_argc; i++) { | ||
438 | dev_notice(&priv->func->dev, "log_level_fw[%d]=0x%X\n", | ||
439 | i, log_level_fw[i]); | ||
440 | iwmct_log_set_fw_filter((log_level_fw[i] >> 8) & 0xFF, | ||
441 | log_level_fw[i] & 0xFF); | ||
442 | } | ||
443 | #endif | ||
444 | |||
445 | priv->dbg.blocks = blocks; | ||
446 | LOG_INFO(priv, INIT, "blocks=%d\n", blocks); | ||
447 | priv->dbg.dump = (bool)dump; | ||
448 | LOG_INFO(priv, INIT, "dump=%d\n", dump); | ||
449 | priv->dbg.jump = (bool)jump; | ||
450 | LOG_INFO(priv, INIT, "jump=%d\n", jump); | ||
451 | priv->dbg.direct = (bool)direct; | ||
452 | LOG_INFO(priv, INIT, "direct=%d\n", direct); | ||
453 | priv->dbg.checksum = (bool)checksum; | ||
454 | LOG_INFO(priv, INIT, "checksum=%d\n", checksum); | ||
455 | priv->dbg.fw_download = (bool)fw_download; | ||
456 | LOG_INFO(priv, INIT, "fw_download=%d\n", fw_download); | ||
457 | priv->dbg.block_size = block_size; | ||
458 | LOG_INFO(priv, INIT, "block_size=%d\n", block_size); | ||
459 | priv->dbg.download_trans_blks = download_trans_blks; | ||
460 | LOG_INFO(priv, INIT, "download_trans_blks=%d\n", download_trans_blks); | ||
461 | } | ||
462 | |||
463 | /***************************************************************************** | ||
464 | * | ||
465 | * sysfs attributes | ||
466 | * | ||
467 | *****************************************************************************/ | ||
468 | static ssize_t show_iwmct_fw_version(struct device *d, | ||
469 | struct device_attribute *attr, char *buf) | ||
470 | { | ||
471 | struct iwmct_priv *priv = dev_get_drvdata(d); | ||
472 | return sprintf(buf, "%s\n", priv->dbg.label_fw); | ||
473 | } | ||
474 | static DEVICE_ATTR(cc_label_fw, S_IRUGO, show_iwmct_fw_version, NULL); | ||
475 | |||
476 | #ifdef CONFIG_IWMC3200TOP_DEBUG | ||
477 | static DEVICE_ATTR(log_level, S_IWUSR | S_IRUGO, | ||
478 | show_iwmct_log_level, store_iwmct_log_level); | ||
479 | static DEVICE_ATTR(log_level_fw, S_IWUSR | S_IRUGO, | ||
480 | show_iwmct_log_level_fw, store_iwmct_log_level_fw); | ||
481 | #endif | ||
482 | |||
483 | static struct attribute *iwmct_sysfs_entries[] = { | ||
484 | &dev_attr_cc_label_fw.attr, | ||
485 | #ifdef CONFIG_IWMC3200TOP_DEBUG | ||
486 | &dev_attr_log_level.attr, | ||
487 | &dev_attr_log_level_fw.attr, | ||
488 | #endif | ||
489 | NULL | ||
490 | }; | ||
491 | |||
492 | static struct attribute_group iwmct_attribute_group = { | ||
493 | .name = NULL, /* put in device directory */ | ||
494 | .attrs = iwmct_sysfs_entries, | ||
495 | }; | ||
496 | |||
497 | |||
498 | static int iwmct_probe(struct sdio_func *func, | ||
499 | const struct sdio_device_id *id) | ||
500 | { | ||
501 | struct iwmct_priv *priv; | ||
502 | int ret; | ||
503 | int val = 1; | ||
504 | int addr = IWMC_SDIO_INTR_ENABLE_ADDR; | ||
505 | |||
506 | dev_dbg(&func->dev, "enter iwmct_probe\n"); | ||
507 | |||
508 | dev_dbg(&func->dev, "IRQ polling period id %u msecs, HZ is %d\n", | ||
509 | jiffies_to_msecs(2147483647), HZ); | ||
510 | |||
511 | priv = kzalloc(sizeof(struct iwmct_priv), GFP_KERNEL); | ||
512 | if (!priv) { | ||
513 | dev_err(&func->dev, "kzalloc error\n"); | ||
514 | return -ENOMEM; | ||
515 | } | ||
516 | priv->func = func; | ||
517 | sdio_set_drvdata(func, priv); | ||
518 | |||
519 | |||
520 | /* create drivers work queue */ | ||
521 | priv->wq = create_workqueue(DRV_NAME "_wq"); | ||
522 | priv->bus_rescan_wq = create_workqueue(DRV_NAME "_rescan_wq"); | ||
523 | INIT_WORK(&priv->bus_rescan_worker, iwmct_rescan_worker); | ||
524 | INIT_WORK(&priv->isr_worker, iwmct_irq_read_worker); | ||
525 | |||
526 | init_waitqueue_head(&priv->wait_q); | ||
527 | |||
528 | sdio_claim_host(func); | ||
529 | /* FIXME: Remove after it is fixed in the Boot ROM upgrade */ | ||
530 | func->enable_timeout = 10; | ||
531 | |||
532 | /* In our HW, setting the block size also wakes up the boot rom. */ | ||
533 | ret = sdio_set_block_size(func, priv->dbg.block_size); | ||
534 | if (ret) { | ||
535 | LOG_ERROR(priv, INIT, | ||
536 | "sdio_set_block_size() failure: %d\n", ret); | ||
537 | goto error_sdio_enable; | ||
538 | } | ||
539 | |||
540 | ret = sdio_enable_func(func); | ||
541 | if (ret) { | ||
542 | LOG_ERROR(priv, INIT, "sdio_enable_func() failure: %d\n", ret); | ||
543 | goto error_sdio_enable; | ||
544 | } | ||
545 | |||
546 | /* init reset and dev_sync states */ | ||
547 | atomic_set(&priv->reset, 0); | ||
548 | atomic_set(&priv->dev_sync, 0); | ||
549 | |||
550 | /* init read req queue */ | ||
551 | INIT_LIST_HEAD(&priv->read_req_list); | ||
552 | |||
553 | /* process configurable parameters */ | ||
554 | iwmct_dbg_init_params(priv); | ||
555 | ret = sysfs_create_group(&func->dev.kobj, &iwmct_attribute_group); | ||
556 | if (ret) { | ||
557 | LOG_ERROR(priv, INIT, "Failed to register attributes and " | ||
558 | "initialize module_params\n"); | ||
559 | goto error_dev_attrs; | ||
560 | } | ||
561 | |||
562 | iwmct_dbgfs_register(priv, DRV_NAME); | ||
563 | |||
564 | if (!priv->dbg.direct && priv->dbg.download_trans_blks > 8) { | ||
565 | LOG_INFO(priv, INIT, | ||
566 | "Reducing transaction to 8 blocks = 2K (from %d)\n", | ||
567 | priv->dbg.download_trans_blks); | ||
568 | priv->dbg.download_trans_blks = 8; | ||
569 | } | ||
570 | priv->trans_len = priv->dbg.download_trans_blks * priv->dbg.block_size; | ||
571 | LOG_INFO(priv, INIT, "Transaction length = %d\n", priv->trans_len); | ||
572 | |||
573 | ret = sdio_claim_irq(func, iwmct_irq); | ||
574 | if (ret) { | ||
575 | LOG_ERROR(priv, INIT, "sdio_claim_irq() failure: %d\n", ret); | ||
576 | goto error_claim_irq; | ||
577 | } | ||
578 | |||
579 | |||
580 | /* Enable function's interrupt */ | ||
581 | sdio_writeb(priv->func, val, addr, &ret); | ||
582 | if (ret) { | ||
583 | LOG_ERROR(priv, INIT, "Failure writing to " | ||
584 | "Interrupt Enable Register (%d): %d\n", addr, ret); | ||
585 | goto error_enable_int; | ||
586 | } | ||
587 | |||
588 | sdio_release_host(func); | ||
589 | |||
590 | LOG_INFO(priv, INIT, "exit iwmct_probe\n"); | ||
591 | |||
592 | return ret; | ||
593 | |||
594 | error_enable_int: | ||
595 | sdio_release_irq(func); | ||
596 | error_claim_irq: | ||
597 | sdio_disable_func(func); | ||
598 | error_dev_attrs: | ||
599 | iwmct_dbgfs_unregister(priv->dbgfs); | ||
600 | sysfs_remove_group(&func->dev.kobj, &iwmct_attribute_group); | ||
601 | error_sdio_enable: | ||
602 | sdio_release_host(func); | ||
603 | return ret; | ||
604 | } | ||
605 | |||
606 | static void iwmct_remove(struct sdio_func *func) | ||
607 | { | ||
608 | struct iwmct_work_struct *read_req; | ||
609 | struct iwmct_priv *priv = sdio_get_drvdata(func); | ||
610 | |||
611 | priv = sdio_get_drvdata(func); | ||
612 | |||
613 | LOG_INFO(priv, INIT, "enter\n"); | ||
614 | |||
615 | sdio_claim_host(func); | ||
616 | sdio_release_irq(func); | ||
617 | sdio_release_host(func); | ||
618 | |||
619 | /* Safely destroy osc workqueue */ | ||
620 | destroy_workqueue(priv->bus_rescan_wq); | ||
621 | destroy_workqueue(priv->wq); | ||
622 | |||
623 | sdio_claim_host(func); | ||
624 | sdio_disable_func(func); | ||
625 | sysfs_remove_group(&func->dev.kobj, &iwmct_attribute_group); | ||
626 | iwmct_dbgfs_unregister(priv->dbgfs); | ||
627 | sdio_release_host(func); | ||
628 | |||
629 | /* free read requests */ | ||
630 | while (!list_empty(&priv->read_req_list)) { | ||
631 | read_req = list_entry(priv->read_req_list.next, | ||
632 | struct iwmct_work_struct, list); | ||
633 | |||
634 | list_del(&read_req->list); | ||
635 | kfree(read_req); | ||
636 | } | ||
637 | |||
638 | kfree(priv); | ||
639 | } | ||
640 | |||
641 | |||
642 | static const struct sdio_device_id iwmct_ids[] = { | ||
643 | /* Intel Wireless MultiCom 3200 Top Driver */ | ||
644 | { SDIO_DEVICE(SDIO_VENDOR_ID_INTEL, 0x1404)}, | ||
645 | { }, /* Terminating entry */ | ||
646 | }; | ||
647 | |||
648 | MODULE_DEVICE_TABLE(sdio, iwmct_ids); | ||
649 | |||
650 | static struct sdio_driver iwmct_driver = { | ||
651 | .probe = iwmct_probe, | ||
652 | .remove = iwmct_remove, | ||
653 | .name = DRV_NAME, | ||
654 | .id_table = iwmct_ids, | ||
655 | }; | ||
656 | |||
657 | static int __init iwmct_init(void) | ||
658 | { | ||
659 | int rc; | ||
660 | |||
661 | /* Default log filter settings */ | ||
662 | iwmct_log_set_filter(LOG_SRC_ALL, LOG_SEV_FILTER_RUNTIME); | ||
663 | iwmct_log_set_filter(LOG_SRC_FW_MSG, LOG_SEV_FILTER_ALL); | ||
664 | iwmct_log_set_fw_filter(LOG_SRC_ALL, FW_LOG_SEV_FILTER_RUNTIME); | ||
665 | |||
666 | rc = sdio_register_driver(&iwmct_driver); | ||
667 | |||
668 | return rc; | ||
669 | } | ||
670 | |||
671 | static void __exit iwmct_exit(void) | ||
672 | { | ||
673 | sdio_unregister_driver(&iwmct_driver); | ||
674 | } | ||
675 | |||
676 | module_init(iwmct_init); | ||
677 | module_exit(iwmct_exit); | ||
678 | |||
diff --git a/drivers/misc/kgdbts.c b/drivers/misc/kgdbts.c index e4ff50b95a5e..fcb6ec1af173 100644 --- a/drivers/misc/kgdbts.c +++ b/drivers/misc/kgdbts.c | |||
@@ -712,6 +712,12 @@ static int run_simple_test(int is_get_char, int chr) | |||
712 | 712 | ||
713 | /* End of packet == #XX so look for the '#' */ | 713 | /* End of packet == #XX so look for the '#' */ |
714 | if (put_buf_cnt > 3 && put_buf[put_buf_cnt - 3] == '#') { | 714 | if (put_buf_cnt > 3 && put_buf[put_buf_cnt - 3] == '#') { |
715 | if (put_buf_cnt >= BUFMAX) { | ||
716 | eprintk("kgdbts: ERROR: put buffer overflow on" | ||
717 | " '%s' line %i\n", ts.name, ts.idx); | ||
718 | put_buf_cnt = 0; | ||
719 | return 0; | ||
720 | } | ||
715 | put_buf[put_buf_cnt] = '\0'; | 721 | put_buf[put_buf_cnt] = '\0'; |
716 | v2printk("put%i: %s\n", ts.idx, put_buf); | 722 | v2printk("put%i: %s\n", ts.idx, put_buf); |
717 | /* Trigger check here */ | 723 | /* Trigger check here */ |
@@ -885,16 +891,16 @@ static void kgdbts_run_tests(void) | |||
885 | int nmi_sleep = 0; | 891 | int nmi_sleep = 0; |
886 | int i; | 892 | int i; |
887 | 893 | ||
888 | ptr = strstr(config, "F"); | 894 | ptr = strchr(config, 'F'); |
889 | if (ptr) | 895 | if (ptr) |
890 | fork_test = simple_strtol(ptr + 1, NULL, 10); | 896 | fork_test = simple_strtol(ptr + 1, NULL, 10); |
891 | ptr = strstr(config, "S"); | 897 | ptr = strchr(config, 'S'); |
892 | if (ptr) | 898 | if (ptr) |
893 | do_sys_open_test = simple_strtol(ptr + 1, NULL, 10); | 899 | do_sys_open_test = simple_strtol(ptr + 1, NULL, 10); |
894 | ptr = strstr(config, "N"); | 900 | ptr = strchr(config, 'N'); |
895 | if (ptr) | 901 | if (ptr) |
896 | nmi_sleep = simple_strtol(ptr+1, NULL, 10); | 902 | nmi_sleep = simple_strtol(ptr+1, NULL, 10); |
897 | ptr = strstr(config, "I"); | 903 | ptr = strchr(config, 'I'); |
898 | if (ptr) | 904 | if (ptr) |
899 | sstep_test = simple_strtol(ptr+1, NULL, 10); | 905 | sstep_test = simple_strtol(ptr+1, NULL, 10); |
900 | 906 | ||
diff --git a/drivers/misc/sgi-gru/gru.h b/drivers/misc/sgi-gru/gru.h index f93f03a9e6e9..3ad76cd18b4b 100644 --- a/drivers/misc/sgi-gru/gru.h +++ b/drivers/misc/sgi-gru/gru.h | |||
@@ -53,6 +53,17 @@ struct gru_chiplet_info { | |||
53 | int free_user_cbr; | 53 | int free_user_cbr; |
54 | }; | 54 | }; |
55 | 55 | ||
56 | /* | ||
57 | * Statictics kept for each context. | ||
58 | */ | ||
59 | struct gru_gseg_statistics { | ||
60 | unsigned long fmm_tlbmiss; | ||
61 | unsigned long upm_tlbmiss; | ||
62 | unsigned long tlbdropin; | ||
63 | unsigned long context_stolen; | ||
64 | unsigned long reserved[10]; | ||
65 | }; | ||
66 | |||
56 | /* Flags for GRU options on the gru_create_context() call */ | 67 | /* Flags for GRU options on the gru_create_context() call */ |
57 | /* Select one of the follow 4 options to specify how TLB misses are handled */ | 68 | /* Select one of the follow 4 options to specify how TLB misses are handled */ |
58 | #define GRU_OPT_MISS_DEFAULT 0x0000 /* Use default mode */ | 69 | #define GRU_OPT_MISS_DEFAULT 0x0000 /* Use default mode */ |
diff --git a/drivers/misc/sgi-gru/gru_instructions.h b/drivers/misc/sgi-gru/gru_instructions.h index 3c9c06618e6a..d95587cc794c 100644 --- a/drivers/misc/sgi-gru/gru_instructions.h +++ b/drivers/misc/sgi-gru/gru_instructions.h | |||
@@ -34,17 +34,17 @@ extern void gru_wait_abort_proc(void *cb); | |||
34 | #include <asm/intrinsics.h> | 34 | #include <asm/intrinsics.h> |
35 | #define __flush_cache(p) ia64_fc((unsigned long)p) | 35 | #define __flush_cache(p) ia64_fc((unsigned long)p) |
36 | /* Use volatile on IA64 to ensure ordering via st4.rel */ | 36 | /* Use volatile on IA64 to ensure ordering via st4.rel */ |
37 | #define gru_ordered_store_int(p, v) \ | 37 | #define gru_ordered_store_ulong(p, v) \ |
38 | do { \ | 38 | do { \ |
39 | barrier(); \ | 39 | barrier(); \ |
40 | *((volatile int *)(p)) = v; /* force st.rel */ \ | 40 | *((volatile unsigned long *)(p)) = v; /* force st.rel */ \ |
41 | } while (0) | 41 | } while (0) |
42 | #elif defined(CONFIG_X86_64) | 42 | #elif defined(CONFIG_X86_64) |
43 | #define __flush_cache(p) clflush(p) | 43 | #define __flush_cache(p) clflush(p) |
44 | #define gru_ordered_store_int(p, v) \ | 44 | #define gru_ordered_store_ulong(p, v) \ |
45 | do { \ | 45 | do { \ |
46 | barrier(); \ | 46 | barrier(); \ |
47 | *(int *)p = v; \ | 47 | *(unsigned long *)p = v; \ |
48 | } while (0) | 48 | } while (0) |
49 | #else | 49 | #else |
50 | #error "Unsupported architecture" | 50 | #error "Unsupported architecture" |
@@ -129,8 +129,13 @@ struct gru_instruction_bits { | |||
129 | */ | 129 | */ |
130 | struct gru_instruction { | 130 | struct gru_instruction { |
131 | /* DW 0 */ | 131 | /* DW 0 */ |
132 | unsigned int op32; /* icmd,xtype,iaa0,ima,opc */ | 132 | union { |
133 | unsigned int tri0; | 133 | unsigned long op64; /* icmd,xtype,iaa0,ima,opc,tri0 */ |
134 | struct { | ||
135 | unsigned int op32; | ||
136 | unsigned int tri0; | ||
137 | }; | ||
138 | }; | ||
134 | unsigned long tri1_bufsize; /* DW 1 */ | 139 | unsigned long tri1_bufsize; /* DW 1 */ |
135 | unsigned long baddr0; /* DW 2 */ | 140 | unsigned long baddr0; /* DW 2 */ |
136 | unsigned long nelem; /* DW 3 */ | 141 | unsigned long nelem; /* DW 3 */ |
@@ -140,7 +145,7 @@ struct gru_instruction { | |||
140 | unsigned long avalue; /* DW 7 */ | 145 | unsigned long avalue; /* DW 7 */ |
141 | }; | 146 | }; |
142 | 147 | ||
143 | /* Some shifts and masks for the low 32 bits of a GRU command */ | 148 | /* Some shifts and masks for the low 64 bits of a GRU command */ |
144 | #define GRU_CB_ICMD_SHFT 0 | 149 | #define GRU_CB_ICMD_SHFT 0 |
145 | #define GRU_CB_ICMD_MASK 0x1 | 150 | #define GRU_CB_ICMD_MASK 0x1 |
146 | #define GRU_CB_XTYPE_SHFT 8 | 151 | #define GRU_CB_XTYPE_SHFT 8 |
@@ -155,6 +160,10 @@ struct gru_instruction { | |||
155 | #define GRU_CB_OPC_MASK 0xff | 160 | #define GRU_CB_OPC_MASK 0xff |
156 | #define GRU_CB_EXOPC_SHFT 24 | 161 | #define GRU_CB_EXOPC_SHFT 24 |
157 | #define GRU_CB_EXOPC_MASK 0xff | 162 | #define GRU_CB_EXOPC_MASK 0xff |
163 | #define GRU_IDEF2_SHFT 32 | ||
164 | #define GRU_IDEF2_MASK 0x3ffff | ||
165 | #define GRU_ISTATUS_SHFT 56 | ||
166 | #define GRU_ISTATUS_MASK 0x3 | ||
158 | 167 | ||
159 | /* GRU instruction opcodes (opc field) */ | 168 | /* GRU instruction opcodes (opc field) */ |
160 | #define OP_NOP 0x00 | 169 | #define OP_NOP 0x00 |
@@ -256,6 +265,7 @@ struct gru_instruction { | |||
256 | #define CBE_CAUSE_PROTOCOL_STATE_DATA_ERROR (1 << 16) | 265 | #define CBE_CAUSE_PROTOCOL_STATE_DATA_ERROR (1 << 16) |
257 | #define CBE_CAUSE_RA_RESPONSE_DATA_ERROR (1 << 17) | 266 | #define CBE_CAUSE_RA_RESPONSE_DATA_ERROR (1 << 17) |
258 | #define CBE_CAUSE_HA_RESPONSE_DATA_ERROR (1 << 18) | 267 | #define CBE_CAUSE_HA_RESPONSE_DATA_ERROR (1 << 18) |
268 | #define CBE_CAUSE_FORCED_ERROR (1 << 19) | ||
259 | 269 | ||
260 | /* CBE cbrexecstatus bits */ | 270 | /* CBE cbrexecstatus bits */ |
261 | #define CBR_EXS_ABORT_OCC_BIT 0 | 271 | #define CBR_EXS_ABORT_OCC_BIT 0 |
@@ -264,13 +274,15 @@ struct gru_instruction { | |||
264 | #define CBR_EXS_QUEUED_BIT 3 | 274 | #define CBR_EXS_QUEUED_BIT 3 |
265 | #define CBR_EXS_TLB_INVAL_BIT 4 | 275 | #define CBR_EXS_TLB_INVAL_BIT 4 |
266 | #define CBR_EXS_EXCEPTION_BIT 5 | 276 | #define CBR_EXS_EXCEPTION_BIT 5 |
277 | #define CBR_EXS_CB_INT_PENDING_BIT 6 | ||
267 | 278 | ||
268 | #define CBR_EXS_ABORT_OCC (1 << CBR_EXS_ABORT_OCC_BIT) | 279 | #define CBR_EXS_ABORT_OCC (1 << CBR_EXS_ABORT_OCC_BIT) |
269 | #define CBR_EXS_INT_OCC (1 << CBR_EXS_INT_OCC_BIT) | 280 | #define CBR_EXS_INT_OCC (1 << CBR_EXS_INT_OCC_BIT) |
270 | #define CBR_EXS_PENDING (1 << CBR_EXS_PENDING_BIT) | 281 | #define CBR_EXS_PENDING (1 << CBR_EXS_PENDING_BIT) |
271 | #define CBR_EXS_QUEUED (1 << CBR_EXS_QUEUED_BIT) | 282 | #define CBR_EXS_QUEUED (1 << CBR_EXS_QUEUED_BIT) |
272 | #define CBR_TLB_INVAL (1 << CBR_EXS_TLB_INVAL_BIT) | 283 | #define CBR_EXS_TLB_INVAL (1 << CBR_EXS_TLB_INVAL_BIT) |
273 | #define CBR_EXS_EXCEPTION (1 << CBR_EXS_EXCEPTION_BIT) | 284 | #define CBR_EXS_EXCEPTION (1 << CBR_EXS_EXCEPTION_BIT) |
285 | #define CBR_EXS_CB_INT_PENDING (1 << CBR_EXS_CB_INT_PENDING_BIT) | ||
274 | 286 | ||
275 | /* | 287 | /* |
276 | * Exceptions are retried for the following cases. If any OTHER bits are set | 288 | * Exceptions are retried for the following cases. If any OTHER bits are set |
@@ -296,12 +308,14 @@ union gru_mesqhead { | |||
296 | 308 | ||
297 | 309 | ||
298 | /* Generate the low word of a GRU instruction */ | 310 | /* Generate the low word of a GRU instruction */ |
299 | static inline unsigned int | 311 | static inline unsigned long |
300 | __opword(unsigned char opcode, unsigned char exopc, unsigned char xtype, | 312 | __opdword(unsigned char opcode, unsigned char exopc, unsigned char xtype, |
301 | unsigned char iaa0, unsigned char iaa1, | 313 | unsigned char iaa0, unsigned char iaa1, |
302 | unsigned char ima) | 314 | unsigned long idef2, unsigned char ima) |
303 | { | 315 | { |
304 | return (1 << GRU_CB_ICMD_SHFT) | | 316 | return (1 << GRU_CB_ICMD_SHFT) | |
317 | ((unsigned long)CBS_ACTIVE << GRU_ISTATUS_SHFT) | | ||
318 | (idef2<< GRU_IDEF2_SHFT) | | ||
305 | (iaa0 << GRU_CB_IAA0_SHFT) | | 319 | (iaa0 << GRU_CB_IAA0_SHFT) | |
306 | (iaa1 << GRU_CB_IAA1_SHFT) | | 320 | (iaa1 << GRU_CB_IAA1_SHFT) | |
307 | (ima << GRU_CB_IMA_SHFT) | | 321 | (ima << GRU_CB_IMA_SHFT) | |
@@ -319,12 +333,13 @@ static inline void gru_flush_cache(void *p) | |||
319 | } | 333 | } |
320 | 334 | ||
321 | /* | 335 | /* |
322 | * Store the lower 32 bits of the command including the "start" bit. Then | 336 | * Store the lower 64 bits of the command including the "start" bit. Then |
323 | * start the instruction executing. | 337 | * start the instruction executing. |
324 | */ | 338 | */ |
325 | static inline void gru_start_instruction(struct gru_instruction *ins, int op32) | 339 | static inline void gru_start_instruction(struct gru_instruction *ins, unsigned long op64) |
326 | { | 340 | { |
327 | gru_ordered_store_int(ins, op32); | 341 | gru_ordered_store_ulong(ins, op64); |
342 | mb(); | ||
328 | gru_flush_cache(ins); | 343 | gru_flush_cache(ins); |
329 | } | 344 | } |
330 | 345 | ||
@@ -340,6 +355,30 @@ static inline void gru_start_instruction(struct gru_instruction *ins, int op32) | |||
340 | * - nelem and stride are in elements | 355 | * - nelem and stride are in elements |
341 | * - tri0/tri1 is in bytes for the beginning of the data segment. | 356 | * - tri0/tri1 is in bytes for the beginning of the data segment. |
342 | */ | 357 | */ |
358 | static inline void gru_vload_phys(void *cb, unsigned long gpa, | ||
359 | unsigned int tri0, int iaa, unsigned long hints) | ||
360 | { | ||
361 | struct gru_instruction *ins = (struct gru_instruction *)cb; | ||
362 | |||
363 | ins->baddr0 = (long)gpa | ((unsigned long)iaa << 62); | ||
364 | ins->nelem = 1; | ||
365 | ins->op1_stride = 1; | ||
366 | gru_start_instruction(ins, __opdword(OP_VLOAD, 0, XTYPE_DW, iaa, 0, | ||
367 | (unsigned long)tri0, CB_IMA(hints))); | ||
368 | } | ||
369 | |||
370 | static inline void gru_vstore_phys(void *cb, unsigned long gpa, | ||
371 | unsigned int tri0, int iaa, unsigned long hints) | ||
372 | { | ||
373 | struct gru_instruction *ins = (struct gru_instruction *)cb; | ||
374 | |||
375 | ins->baddr0 = (long)gpa | ((unsigned long)iaa << 62); | ||
376 | ins->nelem = 1; | ||
377 | ins->op1_stride = 1; | ||
378 | gru_start_instruction(ins, __opdword(OP_VSTORE, 0, XTYPE_DW, iaa, 0, | ||
379 | (unsigned long)tri0, CB_IMA(hints))); | ||
380 | } | ||
381 | |||
343 | static inline void gru_vload(void *cb, unsigned long mem_addr, | 382 | static inline void gru_vload(void *cb, unsigned long mem_addr, |
344 | unsigned int tri0, unsigned char xtype, unsigned long nelem, | 383 | unsigned int tri0, unsigned char xtype, unsigned long nelem, |
345 | unsigned long stride, unsigned long hints) | 384 | unsigned long stride, unsigned long hints) |
@@ -348,10 +387,9 @@ static inline void gru_vload(void *cb, unsigned long mem_addr, | |||
348 | 387 | ||
349 | ins->baddr0 = (long)mem_addr; | 388 | ins->baddr0 = (long)mem_addr; |
350 | ins->nelem = nelem; | 389 | ins->nelem = nelem; |
351 | ins->tri0 = tri0; | ||
352 | ins->op1_stride = stride; | 390 | ins->op1_stride = stride; |
353 | gru_start_instruction(ins, __opword(OP_VLOAD, 0, xtype, IAA_RAM, 0, | 391 | gru_start_instruction(ins, __opdword(OP_VLOAD, 0, xtype, IAA_RAM, 0, |
354 | CB_IMA(hints))); | 392 | (unsigned long)tri0, CB_IMA(hints))); |
355 | } | 393 | } |
356 | 394 | ||
357 | static inline void gru_vstore(void *cb, unsigned long mem_addr, | 395 | static inline void gru_vstore(void *cb, unsigned long mem_addr, |
@@ -362,10 +400,9 @@ static inline void gru_vstore(void *cb, unsigned long mem_addr, | |||
362 | 400 | ||
363 | ins->baddr0 = (long)mem_addr; | 401 | ins->baddr0 = (long)mem_addr; |
364 | ins->nelem = nelem; | 402 | ins->nelem = nelem; |
365 | ins->tri0 = tri0; | ||
366 | ins->op1_stride = stride; | 403 | ins->op1_stride = stride; |
367 | gru_start_instruction(ins, __opword(OP_VSTORE, 0, xtype, IAA_RAM, 0, | 404 | gru_start_instruction(ins, __opdword(OP_VSTORE, 0, xtype, IAA_RAM, 0, |
368 | CB_IMA(hints))); | 405 | tri0, CB_IMA(hints))); |
369 | } | 406 | } |
370 | 407 | ||
371 | static inline void gru_ivload(void *cb, unsigned long mem_addr, | 408 | static inline void gru_ivload(void *cb, unsigned long mem_addr, |
@@ -376,10 +413,9 @@ static inline void gru_ivload(void *cb, unsigned long mem_addr, | |||
376 | 413 | ||
377 | ins->baddr0 = (long)mem_addr; | 414 | ins->baddr0 = (long)mem_addr; |
378 | ins->nelem = nelem; | 415 | ins->nelem = nelem; |
379 | ins->tri0 = tri0; | ||
380 | ins->tri1_bufsize = tri1; | 416 | ins->tri1_bufsize = tri1; |
381 | gru_start_instruction(ins, __opword(OP_IVLOAD, 0, xtype, IAA_RAM, 0, | 417 | gru_start_instruction(ins, __opdword(OP_IVLOAD, 0, xtype, IAA_RAM, 0, |
382 | CB_IMA(hints))); | 418 | tri0, CB_IMA(hints))); |
383 | } | 419 | } |
384 | 420 | ||
385 | static inline void gru_ivstore(void *cb, unsigned long mem_addr, | 421 | static inline void gru_ivstore(void *cb, unsigned long mem_addr, |
@@ -390,10 +426,9 @@ static inline void gru_ivstore(void *cb, unsigned long mem_addr, | |||
390 | 426 | ||
391 | ins->baddr0 = (long)mem_addr; | 427 | ins->baddr0 = (long)mem_addr; |
392 | ins->nelem = nelem; | 428 | ins->nelem = nelem; |
393 | ins->tri0 = tri0; | ||
394 | ins->tri1_bufsize = tri1; | 429 | ins->tri1_bufsize = tri1; |
395 | gru_start_instruction(ins, __opword(OP_IVSTORE, 0, xtype, IAA_RAM, 0, | 430 | gru_start_instruction(ins, __opdword(OP_IVSTORE, 0, xtype, IAA_RAM, 0, |
396 | CB_IMA(hints))); | 431 | tri0, CB_IMA(hints))); |
397 | } | 432 | } |
398 | 433 | ||
399 | static inline void gru_vset(void *cb, unsigned long mem_addr, | 434 | static inline void gru_vset(void *cb, unsigned long mem_addr, |
@@ -406,8 +441,8 @@ static inline void gru_vset(void *cb, unsigned long mem_addr, | |||
406 | ins->op2_value_baddr1 = value; | 441 | ins->op2_value_baddr1 = value; |
407 | ins->nelem = nelem; | 442 | ins->nelem = nelem; |
408 | ins->op1_stride = stride; | 443 | ins->op1_stride = stride; |
409 | gru_start_instruction(ins, __opword(OP_VSET, 0, xtype, IAA_RAM, 0, | 444 | gru_start_instruction(ins, __opdword(OP_VSET, 0, xtype, IAA_RAM, 0, |
410 | CB_IMA(hints))); | 445 | 0, CB_IMA(hints))); |
411 | } | 446 | } |
412 | 447 | ||
413 | static inline void gru_ivset(void *cb, unsigned long mem_addr, | 448 | static inline void gru_ivset(void *cb, unsigned long mem_addr, |
@@ -420,8 +455,8 @@ static inline void gru_ivset(void *cb, unsigned long mem_addr, | |||
420 | ins->op2_value_baddr1 = value; | 455 | ins->op2_value_baddr1 = value; |
421 | ins->nelem = nelem; | 456 | ins->nelem = nelem; |
422 | ins->tri1_bufsize = tri1; | 457 | ins->tri1_bufsize = tri1; |
423 | gru_start_instruction(ins, __opword(OP_IVSET, 0, xtype, IAA_RAM, 0, | 458 | gru_start_instruction(ins, __opdword(OP_IVSET, 0, xtype, IAA_RAM, 0, |
424 | CB_IMA(hints))); | 459 | 0, CB_IMA(hints))); |
425 | } | 460 | } |
426 | 461 | ||
427 | static inline void gru_vflush(void *cb, unsigned long mem_addr, | 462 | static inline void gru_vflush(void *cb, unsigned long mem_addr, |
@@ -433,15 +468,15 @@ static inline void gru_vflush(void *cb, unsigned long mem_addr, | |||
433 | ins->baddr0 = (long)mem_addr; | 468 | ins->baddr0 = (long)mem_addr; |
434 | ins->op1_stride = stride; | 469 | ins->op1_stride = stride; |
435 | ins->nelem = nelem; | 470 | ins->nelem = nelem; |
436 | gru_start_instruction(ins, __opword(OP_VFLUSH, 0, xtype, IAA_RAM, 0, | 471 | gru_start_instruction(ins, __opdword(OP_VFLUSH, 0, xtype, IAA_RAM, 0, |
437 | CB_IMA(hints))); | 472 | 0, CB_IMA(hints))); |
438 | } | 473 | } |
439 | 474 | ||
440 | static inline void gru_nop(void *cb, int hints) | 475 | static inline void gru_nop(void *cb, int hints) |
441 | { | 476 | { |
442 | struct gru_instruction *ins = (void *)cb; | 477 | struct gru_instruction *ins = (void *)cb; |
443 | 478 | ||
444 | gru_start_instruction(ins, __opword(OP_NOP, 0, 0, 0, 0, CB_IMA(hints))); | 479 | gru_start_instruction(ins, __opdword(OP_NOP, 0, 0, 0, 0, 0, CB_IMA(hints))); |
445 | } | 480 | } |
446 | 481 | ||
447 | 482 | ||
@@ -455,10 +490,9 @@ static inline void gru_bcopy(void *cb, const unsigned long src, | |||
455 | ins->baddr0 = (long)src; | 490 | ins->baddr0 = (long)src; |
456 | ins->op2_value_baddr1 = (long)dest; | 491 | ins->op2_value_baddr1 = (long)dest; |
457 | ins->nelem = nelem; | 492 | ins->nelem = nelem; |
458 | ins->tri0 = tri0; | ||
459 | ins->tri1_bufsize = bufsize; | 493 | ins->tri1_bufsize = bufsize; |
460 | gru_start_instruction(ins, __opword(OP_BCOPY, 0, xtype, IAA_RAM, | 494 | gru_start_instruction(ins, __opdword(OP_BCOPY, 0, xtype, IAA_RAM, |
461 | IAA_RAM, CB_IMA(hints))); | 495 | IAA_RAM, tri0, CB_IMA(hints))); |
462 | } | 496 | } |
463 | 497 | ||
464 | static inline void gru_bstore(void *cb, const unsigned long src, | 498 | static inline void gru_bstore(void *cb, const unsigned long src, |
@@ -470,9 +504,8 @@ static inline void gru_bstore(void *cb, const unsigned long src, | |||
470 | ins->baddr0 = (long)src; | 504 | ins->baddr0 = (long)src; |
471 | ins->op2_value_baddr1 = (long)dest; | 505 | ins->op2_value_baddr1 = (long)dest; |
472 | ins->nelem = nelem; | 506 | ins->nelem = nelem; |
473 | ins->tri0 = tri0; | 507 | gru_start_instruction(ins, __opdword(OP_BSTORE, 0, xtype, 0, IAA_RAM, |
474 | gru_start_instruction(ins, __opword(OP_BSTORE, 0, xtype, 0, IAA_RAM, | 508 | tri0, CB_IMA(hints))); |
475 | CB_IMA(hints))); | ||
476 | } | 509 | } |
477 | 510 | ||
478 | static inline void gru_gamir(void *cb, int exopc, unsigned long src, | 511 | static inline void gru_gamir(void *cb, int exopc, unsigned long src, |
@@ -481,8 +514,8 @@ static inline void gru_gamir(void *cb, int exopc, unsigned long src, | |||
481 | struct gru_instruction *ins = (void *)cb; | 514 | struct gru_instruction *ins = (void *)cb; |
482 | 515 | ||
483 | ins->baddr0 = (long)src; | 516 | ins->baddr0 = (long)src; |
484 | gru_start_instruction(ins, __opword(OP_GAMIR, exopc, xtype, IAA_RAM, 0, | 517 | gru_start_instruction(ins, __opdword(OP_GAMIR, exopc, xtype, IAA_RAM, 0, |
485 | CB_IMA(hints))); | 518 | 0, CB_IMA(hints))); |
486 | } | 519 | } |
487 | 520 | ||
488 | static inline void gru_gamirr(void *cb, int exopc, unsigned long src, | 521 | static inline void gru_gamirr(void *cb, int exopc, unsigned long src, |
@@ -491,8 +524,8 @@ static inline void gru_gamirr(void *cb, int exopc, unsigned long src, | |||
491 | struct gru_instruction *ins = (void *)cb; | 524 | struct gru_instruction *ins = (void *)cb; |
492 | 525 | ||
493 | ins->baddr0 = (long)src; | 526 | ins->baddr0 = (long)src; |
494 | gru_start_instruction(ins, __opword(OP_GAMIRR, exopc, xtype, IAA_RAM, 0, | 527 | gru_start_instruction(ins, __opdword(OP_GAMIRR, exopc, xtype, IAA_RAM, 0, |
495 | CB_IMA(hints))); | 528 | 0, CB_IMA(hints))); |
496 | } | 529 | } |
497 | 530 | ||
498 | static inline void gru_gamer(void *cb, int exopc, unsigned long src, | 531 | static inline void gru_gamer(void *cb, int exopc, unsigned long src, |
@@ -505,8 +538,8 @@ static inline void gru_gamer(void *cb, int exopc, unsigned long src, | |||
505 | ins->baddr0 = (long)src; | 538 | ins->baddr0 = (long)src; |
506 | ins->op1_stride = operand1; | 539 | ins->op1_stride = operand1; |
507 | ins->op2_value_baddr1 = operand2; | 540 | ins->op2_value_baddr1 = operand2; |
508 | gru_start_instruction(ins, __opword(OP_GAMER, exopc, xtype, IAA_RAM, 0, | 541 | gru_start_instruction(ins, __opdword(OP_GAMER, exopc, xtype, IAA_RAM, 0, |
509 | CB_IMA(hints))); | 542 | 0, CB_IMA(hints))); |
510 | } | 543 | } |
511 | 544 | ||
512 | static inline void gru_gamerr(void *cb, int exopc, unsigned long src, | 545 | static inline void gru_gamerr(void *cb, int exopc, unsigned long src, |
@@ -518,8 +551,8 @@ static inline void gru_gamerr(void *cb, int exopc, unsigned long src, | |||
518 | ins->baddr0 = (long)src; | 551 | ins->baddr0 = (long)src; |
519 | ins->op1_stride = operand1; | 552 | ins->op1_stride = operand1; |
520 | ins->op2_value_baddr1 = operand2; | 553 | ins->op2_value_baddr1 = operand2; |
521 | gru_start_instruction(ins, __opword(OP_GAMERR, exopc, xtype, IAA_RAM, 0, | 554 | gru_start_instruction(ins, __opdword(OP_GAMERR, exopc, xtype, IAA_RAM, 0, |
522 | CB_IMA(hints))); | 555 | 0, CB_IMA(hints))); |
523 | } | 556 | } |
524 | 557 | ||
525 | static inline void gru_gamxr(void *cb, unsigned long src, | 558 | static inline void gru_gamxr(void *cb, unsigned long src, |
@@ -529,8 +562,8 @@ static inline void gru_gamxr(void *cb, unsigned long src, | |||
529 | 562 | ||
530 | ins->baddr0 = (long)src; | 563 | ins->baddr0 = (long)src; |
531 | ins->nelem = 4; | 564 | ins->nelem = 4; |
532 | gru_start_instruction(ins, __opword(OP_GAMXR, EOP_XR_CSWAP, XTYPE_DW, | 565 | gru_start_instruction(ins, __opdword(OP_GAMXR, EOP_XR_CSWAP, XTYPE_DW, |
533 | IAA_RAM, 0, CB_IMA(hints))); | 566 | IAA_RAM, 0, 0, CB_IMA(hints))); |
534 | } | 567 | } |
535 | 568 | ||
536 | static inline void gru_mesq(void *cb, unsigned long queue, | 569 | static inline void gru_mesq(void *cb, unsigned long queue, |
@@ -541,9 +574,8 @@ static inline void gru_mesq(void *cb, unsigned long queue, | |||
541 | 574 | ||
542 | ins->baddr0 = (long)queue; | 575 | ins->baddr0 = (long)queue; |
543 | ins->nelem = nelem; | 576 | ins->nelem = nelem; |
544 | ins->tri0 = tri0; | 577 | gru_start_instruction(ins, __opdword(OP_MESQ, 0, XTYPE_CL, IAA_RAM, 0, |
545 | gru_start_instruction(ins, __opword(OP_MESQ, 0, XTYPE_CL, IAA_RAM, 0, | 578 | tri0, CB_IMA(hints))); |
546 | CB_IMA(hints))); | ||
547 | } | 579 | } |
548 | 580 | ||
549 | static inline unsigned long gru_get_amo_value(void *cb) | 581 | static inline unsigned long gru_get_amo_value(void *cb) |
@@ -662,6 +694,14 @@ static inline void gru_wait_abort(void *cb) | |||
662 | gru_wait_abort_proc(cb); | 694 | gru_wait_abort_proc(cb); |
663 | } | 695 | } |
664 | 696 | ||
697 | /* | ||
698 | * Get a pointer to the start of a gseg | ||
699 | * p - Any valid pointer within the gseg | ||
700 | */ | ||
701 | static inline void *gru_get_gseg_pointer (void *p) | ||
702 | { | ||
703 | return (void *)((unsigned long)p & ~(GRU_GSEG_PAGESIZE - 1)); | ||
704 | } | ||
665 | 705 | ||
666 | /* | 706 | /* |
667 | * Get a pointer to a control block | 707 | * Get a pointer to a control block |
diff --git a/drivers/misc/sgi-gru/grufault.c b/drivers/misc/sgi-gru/grufault.c index 679e01778286..38657cdaf54d 100644 --- a/drivers/misc/sgi-gru/grufault.c +++ b/drivers/misc/sgi-gru/grufault.c | |||
@@ -40,6 +40,12 @@ | |||
40 | #include "gru_instructions.h" | 40 | #include "gru_instructions.h" |
41 | #include <asm/uv/uv_hub.h> | 41 | #include <asm/uv/uv_hub.h> |
42 | 42 | ||
43 | /* Return codes for vtop functions */ | ||
44 | #define VTOP_SUCCESS 0 | ||
45 | #define VTOP_INVALID -1 | ||
46 | #define VTOP_RETRY -2 | ||
47 | |||
48 | |||
43 | /* | 49 | /* |
44 | * Test if a physical address is a valid GRU GSEG address | 50 | * Test if a physical address is a valid GRU GSEG address |
45 | */ | 51 | */ |
@@ -90,19 +96,22 @@ static struct gru_thread_state *gru_alloc_locked_gts(unsigned long vaddr) | |||
90 | { | 96 | { |
91 | struct mm_struct *mm = current->mm; | 97 | struct mm_struct *mm = current->mm; |
92 | struct vm_area_struct *vma; | 98 | struct vm_area_struct *vma; |
93 | struct gru_thread_state *gts = NULL; | 99 | struct gru_thread_state *gts = ERR_PTR(-EINVAL); |
94 | 100 | ||
95 | down_write(&mm->mmap_sem); | 101 | down_write(&mm->mmap_sem); |
96 | vma = gru_find_vma(vaddr); | 102 | vma = gru_find_vma(vaddr); |
97 | if (vma) | 103 | if (!vma) |
98 | gts = gru_alloc_thread_state(vma, TSID(vaddr, vma)); | 104 | goto err; |
99 | if (gts) { | ||
100 | mutex_lock(>s->ts_ctxlock); | ||
101 | downgrade_write(&mm->mmap_sem); | ||
102 | } else { | ||
103 | up_write(&mm->mmap_sem); | ||
104 | } | ||
105 | 105 | ||
106 | gts = gru_alloc_thread_state(vma, TSID(vaddr, vma)); | ||
107 | if (IS_ERR(gts)) | ||
108 | goto err; | ||
109 | mutex_lock(>s->ts_ctxlock); | ||
110 | downgrade_write(&mm->mmap_sem); | ||
111 | return gts; | ||
112 | |||
113 | err: | ||
114 | up_write(&mm->mmap_sem); | ||
106 | return gts; | 115 | return gts; |
107 | } | 116 | } |
108 | 117 | ||
@@ -122,39 +131,15 @@ static void gru_unlock_gts(struct gru_thread_state *gts) | |||
122 | * is necessary to prevent the user from seeing a stale cb.istatus that will | 131 | * is necessary to prevent the user from seeing a stale cb.istatus that will |
123 | * change as soon as the TFH restart is complete. Races may cause an | 132 | * change as soon as the TFH restart is complete. Races may cause an |
124 | * occasional failure to clear the cb.istatus, but that is ok. | 133 | * occasional failure to clear the cb.istatus, but that is ok. |
125 | * | ||
126 | * If the cb address is not valid (should not happen, but...), nothing | ||
127 | * bad will happen.. The get_user()/put_user() will fail but there | ||
128 | * are no bad side-effects. | ||
129 | */ | 134 | */ |
130 | static void gru_cb_set_istatus_active(unsigned long __user *cb) | 135 | static void gru_cb_set_istatus_active(struct gru_instruction_bits *cbk) |
131 | { | 136 | { |
132 | union { | 137 | if (cbk) { |
133 | struct gru_instruction_bits bits; | 138 | cbk->istatus = CBS_ACTIVE; |
134 | unsigned long dw; | ||
135 | } u; | ||
136 | |||
137 | if (cb) { | ||
138 | get_user(u.dw, cb); | ||
139 | u.bits.istatus = CBS_ACTIVE; | ||
140 | put_user(u.dw, cb); | ||
141 | } | 139 | } |
142 | } | 140 | } |
143 | 141 | ||
144 | /* | 142 | /* |
145 | * Convert a interrupt IRQ to a pointer to the GRU GTS that caused the | ||
146 | * interrupt. Interrupts are always sent to a cpu on the blade that contains the | ||
147 | * GRU (except for headless blades which are not currently supported). A blade | ||
148 | * has N grus; a block of N consecutive IRQs is assigned to the GRUs. The IRQ | ||
149 | * number uniquely identifies the GRU chiplet on the local blade that caused the | ||
150 | * interrupt. Always called in interrupt context. | ||
151 | */ | ||
152 | static inline struct gru_state *irq_to_gru(int irq) | ||
153 | { | ||
154 | return &gru_base[uv_numa_blade_id()]->bs_grus[irq - IRQ_GRU]; | ||
155 | } | ||
156 | |||
157 | /* | ||
158 | * Read & clear a TFM | 143 | * Read & clear a TFM |
159 | * | 144 | * |
160 | * The GRU has an array of fault maps. A map is private to a cpu | 145 | * The GRU has an array of fault maps. A map is private to a cpu |
@@ -207,10 +192,11 @@ static int non_atomic_pte_lookup(struct vm_area_struct *vma, | |||
207 | { | 192 | { |
208 | struct page *page; | 193 | struct page *page; |
209 | 194 | ||
210 | /* ZZZ Need to handle HUGE pages */ | 195 | #ifdef CONFIG_HUGETLB_PAGE |
211 | if (is_vm_hugetlb_page(vma)) | 196 | *pageshift = is_vm_hugetlb_page(vma) ? HPAGE_SHIFT : PAGE_SHIFT; |
212 | return -EFAULT; | 197 | #else |
213 | *pageshift = PAGE_SHIFT; | 198 | *pageshift = PAGE_SHIFT; |
199 | #endif | ||
214 | if (get_user_pages | 200 | if (get_user_pages |
215 | (current, current->mm, vaddr, 1, write, 0, &page, NULL) <= 0) | 201 | (current, current->mm, vaddr, 1, write, 0, &page, NULL) <= 0) |
216 | return -EFAULT; | 202 | return -EFAULT; |
@@ -268,7 +254,6 @@ static int atomic_pte_lookup(struct vm_area_struct *vma, unsigned long vaddr, | |||
268 | return 0; | 254 | return 0; |
269 | 255 | ||
270 | err: | 256 | err: |
271 | local_irq_enable(); | ||
272 | return 1; | 257 | return 1; |
273 | } | 258 | } |
274 | 259 | ||
@@ -301,14 +286,69 @@ static int gru_vtop(struct gru_thread_state *gts, unsigned long vaddr, | |||
301 | paddr = paddr & ~((1UL << ps) - 1); | 286 | paddr = paddr & ~((1UL << ps) - 1); |
302 | *gpa = uv_soc_phys_ram_to_gpa(paddr); | 287 | *gpa = uv_soc_phys_ram_to_gpa(paddr); |
303 | *pageshift = ps; | 288 | *pageshift = ps; |
304 | return 0; | 289 | return VTOP_SUCCESS; |
305 | 290 | ||
306 | inval: | 291 | inval: |
307 | return -1; | 292 | return VTOP_INVALID; |
308 | upm: | 293 | upm: |
309 | return -2; | 294 | return VTOP_RETRY; |
295 | } | ||
296 | |||
297 | |||
298 | /* | ||
299 | * Flush a CBE from cache. The CBE is clean in the cache. Dirty the | ||
300 | * CBE cacheline so that the line will be written back to home agent. | ||
301 | * Otherwise the line may be silently dropped. This has no impact | ||
302 | * except on performance. | ||
303 | */ | ||
304 | static void gru_flush_cache_cbe(struct gru_control_block_extended *cbe) | ||
305 | { | ||
306 | if (unlikely(cbe)) { | ||
307 | cbe->cbrexecstatus = 0; /* make CL dirty */ | ||
308 | gru_flush_cache(cbe); | ||
309 | } | ||
310 | } | 310 | } |
311 | 311 | ||
312 | /* | ||
313 | * Preload the TLB with entries that may be required. Currently, preloading | ||
314 | * is implemented only for BCOPY. Preload <tlb_preload_count> pages OR to | ||
315 | * the end of the bcopy tranfer, whichever is smaller. | ||
316 | */ | ||
317 | static void gru_preload_tlb(struct gru_state *gru, | ||
318 | struct gru_thread_state *gts, int atomic, | ||
319 | unsigned long fault_vaddr, int asid, int write, | ||
320 | unsigned char tlb_preload_count, | ||
321 | struct gru_tlb_fault_handle *tfh, | ||
322 | struct gru_control_block_extended *cbe) | ||
323 | { | ||
324 | unsigned long vaddr = 0, gpa; | ||
325 | int ret, pageshift; | ||
326 | |||
327 | if (cbe->opccpy != OP_BCOPY) | ||
328 | return; | ||
329 | |||
330 | if (fault_vaddr == cbe->cbe_baddr0) | ||
331 | vaddr = fault_vaddr + GRU_CACHE_LINE_BYTES * cbe->cbe_src_cl - 1; | ||
332 | else if (fault_vaddr == cbe->cbe_baddr1) | ||
333 | vaddr = fault_vaddr + (1 << cbe->xtypecpy) * cbe->cbe_nelemcur - 1; | ||
334 | |||
335 | fault_vaddr &= PAGE_MASK; | ||
336 | vaddr &= PAGE_MASK; | ||
337 | vaddr = min(vaddr, fault_vaddr + tlb_preload_count * PAGE_SIZE); | ||
338 | |||
339 | while (vaddr > fault_vaddr) { | ||
340 | ret = gru_vtop(gts, vaddr, write, atomic, &gpa, &pageshift); | ||
341 | if (ret || tfh_write_only(tfh, gpa, GAA_RAM, vaddr, asid, write, | ||
342 | GRU_PAGESIZE(pageshift))) | ||
343 | return; | ||
344 | gru_dbg(grudev, | ||
345 | "%s: gid %d, gts 0x%p, tfh 0x%p, vaddr 0x%lx, asid 0x%x, rw %d, ps %d, gpa 0x%lx\n", | ||
346 | atomic ? "atomic" : "non-atomic", gru->gs_gid, gts, tfh, | ||
347 | vaddr, asid, write, pageshift, gpa); | ||
348 | vaddr -= PAGE_SIZE; | ||
349 | STAT(tlb_preload_page); | ||
350 | } | ||
351 | } | ||
312 | 352 | ||
313 | /* | 353 | /* |
314 | * Drop a TLB entry into the GRU. The fault is described by info in an TFH. | 354 | * Drop a TLB entry into the GRU. The fault is described by info in an TFH. |
@@ -320,11 +360,14 @@ upm: | |||
320 | * < 0 = error code | 360 | * < 0 = error code |
321 | * | 361 | * |
322 | */ | 362 | */ |
323 | static int gru_try_dropin(struct gru_thread_state *gts, | 363 | static int gru_try_dropin(struct gru_state *gru, |
364 | struct gru_thread_state *gts, | ||
324 | struct gru_tlb_fault_handle *tfh, | 365 | struct gru_tlb_fault_handle *tfh, |
325 | unsigned long __user *cb) | 366 | struct gru_instruction_bits *cbk) |
326 | { | 367 | { |
327 | int pageshift = 0, asid, write, ret, atomic = !cb; | 368 | struct gru_control_block_extended *cbe = NULL; |
369 | unsigned char tlb_preload_count = gts->ts_tlb_preload_count; | ||
370 | int pageshift = 0, asid, write, ret, atomic = !cbk, indexway; | ||
328 | unsigned long gpa = 0, vaddr = 0; | 371 | unsigned long gpa = 0, vaddr = 0; |
329 | 372 | ||
330 | /* | 373 | /* |
@@ -335,24 +378,34 @@ static int gru_try_dropin(struct gru_thread_state *gts, | |||
335 | */ | 378 | */ |
336 | 379 | ||
337 | /* | 380 | /* |
381 | * Prefetch the CBE if doing TLB preloading | ||
382 | */ | ||
383 | if (unlikely(tlb_preload_count)) { | ||
384 | cbe = gru_tfh_to_cbe(tfh); | ||
385 | prefetchw(cbe); | ||
386 | } | ||
387 | |||
388 | /* | ||
338 | * Error if TFH state is IDLE or FMM mode & the user issuing a UPM call. | 389 | * Error if TFH state is IDLE or FMM mode & the user issuing a UPM call. |
339 | * Might be a hardware race OR a stupid user. Ignore FMM because FMM | 390 | * Might be a hardware race OR a stupid user. Ignore FMM because FMM |
340 | * is a transient state. | 391 | * is a transient state. |
341 | */ | 392 | */ |
342 | if (tfh->status != TFHSTATUS_EXCEPTION) { | 393 | if (tfh->status != TFHSTATUS_EXCEPTION) { |
343 | gru_flush_cache(tfh); | 394 | gru_flush_cache(tfh); |
395 | sync_core(); | ||
344 | if (tfh->status != TFHSTATUS_EXCEPTION) | 396 | if (tfh->status != TFHSTATUS_EXCEPTION) |
345 | goto failnoexception; | 397 | goto failnoexception; |
346 | STAT(tfh_stale_on_fault); | 398 | STAT(tfh_stale_on_fault); |
347 | } | 399 | } |
348 | if (tfh->state == TFHSTATE_IDLE) | 400 | if (tfh->state == TFHSTATE_IDLE) |
349 | goto failidle; | 401 | goto failidle; |
350 | if (tfh->state == TFHSTATE_MISS_FMM && cb) | 402 | if (tfh->state == TFHSTATE_MISS_FMM && cbk) |
351 | goto failfmm; | 403 | goto failfmm; |
352 | 404 | ||
353 | write = (tfh->cause & TFHCAUSE_TLB_MOD) != 0; | 405 | write = (tfh->cause & TFHCAUSE_TLB_MOD) != 0; |
354 | vaddr = tfh->missvaddr; | 406 | vaddr = tfh->missvaddr; |
355 | asid = tfh->missasid; | 407 | asid = tfh->missasid; |
408 | indexway = tfh->indexway; | ||
356 | if (asid == 0) | 409 | if (asid == 0) |
357 | goto failnoasid; | 410 | goto failnoasid; |
358 | 411 | ||
@@ -366,41 +419,51 @@ static int gru_try_dropin(struct gru_thread_state *gts, | |||
366 | goto failactive; | 419 | goto failactive; |
367 | 420 | ||
368 | ret = gru_vtop(gts, vaddr, write, atomic, &gpa, &pageshift); | 421 | ret = gru_vtop(gts, vaddr, write, atomic, &gpa, &pageshift); |
369 | if (ret == -1) | 422 | if (ret == VTOP_INVALID) |
370 | goto failinval; | 423 | goto failinval; |
371 | if (ret == -2) | 424 | if (ret == VTOP_RETRY) |
372 | goto failupm; | 425 | goto failupm; |
373 | 426 | ||
374 | if (!(gts->ts_sizeavail & GRU_SIZEAVAIL(pageshift))) { | 427 | if (!(gts->ts_sizeavail & GRU_SIZEAVAIL(pageshift))) { |
375 | gts->ts_sizeavail |= GRU_SIZEAVAIL(pageshift); | 428 | gts->ts_sizeavail |= GRU_SIZEAVAIL(pageshift); |
376 | if (atomic || !gru_update_cch(gts, 0)) { | 429 | if (atomic || !gru_update_cch(gts)) { |
377 | gts->ts_force_cch_reload = 1; | 430 | gts->ts_force_cch_reload = 1; |
378 | goto failupm; | 431 | goto failupm; |
379 | } | 432 | } |
380 | } | 433 | } |
381 | gru_cb_set_istatus_active(cb); | 434 | |
435 | if (unlikely(cbe) && pageshift == PAGE_SHIFT) { | ||
436 | gru_preload_tlb(gru, gts, atomic, vaddr, asid, write, tlb_preload_count, tfh, cbe); | ||
437 | gru_flush_cache_cbe(cbe); | ||
438 | } | ||
439 | |||
440 | gru_cb_set_istatus_active(cbk); | ||
441 | gts->ustats.tlbdropin++; | ||
382 | tfh_write_restart(tfh, gpa, GAA_RAM, vaddr, asid, write, | 442 | tfh_write_restart(tfh, gpa, GAA_RAM, vaddr, asid, write, |
383 | GRU_PAGESIZE(pageshift)); | 443 | GRU_PAGESIZE(pageshift)); |
384 | STAT(tlb_dropin); | ||
385 | gru_dbg(grudev, | 444 | gru_dbg(grudev, |
386 | "%s: tfh 0x%p, vaddr 0x%lx, asid 0x%x, ps %d, gpa 0x%lx\n", | 445 | "%s: gid %d, gts 0x%p, tfh 0x%p, vaddr 0x%lx, asid 0x%x, indexway 0x%x," |
387 | ret ? "non-atomic" : "atomic", tfh, vaddr, asid, | 446 | " rw %d, ps %d, gpa 0x%lx\n", |
388 | pageshift, gpa); | 447 | atomic ? "atomic" : "non-atomic", gru->gs_gid, gts, tfh, vaddr, asid, |
448 | indexway, write, pageshift, gpa); | ||
449 | STAT(tlb_dropin); | ||
389 | return 0; | 450 | return 0; |
390 | 451 | ||
391 | failnoasid: | 452 | failnoasid: |
392 | /* No asid (delayed unload). */ | 453 | /* No asid (delayed unload). */ |
393 | STAT(tlb_dropin_fail_no_asid); | 454 | STAT(tlb_dropin_fail_no_asid); |
394 | gru_dbg(grudev, "FAILED no_asid tfh: 0x%p, vaddr 0x%lx\n", tfh, vaddr); | 455 | gru_dbg(grudev, "FAILED no_asid tfh: 0x%p, vaddr 0x%lx\n", tfh, vaddr); |
395 | if (!cb) | 456 | if (!cbk) |
396 | tfh_user_polling_mode(tfh); | 457 | tfh_user_polling_mode(tfh); |
397 | else | 458 | else |
398 | gru_flush_cache(tfh); | 459 | gru_flush_cache(tfh); |
460 | gru_flush_cache_cbe(cbe); | ||
399 | return -EAGAIN; | 461 | return -EAGAIN; |
400 | 462 | ||
401 | failupm: | 463 | failupm: |
402 | /* Atomic failure switch CBR to UPM */ | 464 | /* Atomic failure switch CBR to UPM */ |
403 | tfh_user_polling_mode(tfh); | 465 | tfh_user_polling_mode(tfh); |
466 | gru_flush_cache_cbe(cbe); | ||
404 | STAT(tlb_dropin_fail_upm); | 467 | STAT(tlb_dropin_fail_upm); |
405 | gru_dbg(grudev, "FAILED upm tfh: 0x%p, vaddr 0x%lx\n", tfh, vaddr); | 468 | gru_dbg(grudev, "FAILED upm tfh: 0x%p, vaddr 0x%lx\n", tfh, vaddr); |
406 | return 1; | 469 | return 1; |
@@ -408,6 +471,7 @@ failupm: | |||
408 | failfmm: | 471 | failfmm: |
409 | /* FMM state on UPM call */ | 472 | /* FMM state on UPM call */ |
410 | gru_flush_cache(tfh); | 473 | gru_flush_cache(tfh); |
474 | gru_flush_cache_cbe(cbe); | ||
411 | STAT(tlb_dropin_fail_fmm); | 475 | STAT(tlb_dropin_fail_fmm); |
412 | gru_dbg(grudev, "FAILED fmm tfh: 0x%p, state %d\n", tfh, tfh->state); | 476 | gru_dbg(grudev, "FAILED fmm tfh: 0x%p, state %d\n", tfh, tfh->state); |
413 | return 0; | 477 | return 0; |
@@ -415,17 +479,20 @@ failfmm: | |||
415 | failnoexception: | 479 | failnoexception: |
416 | /* TFH status did not show exception pending */ | 480 | /* TFH status did not show exception pending */ |
417 | gru_flush_cache(tfh); | 481 | gru_flush_cache(tfh); |
418 | if (cb) | 482 | gru_flush_cache_cbe(cbe); |
419 | gru_flush_cache(cb); | 483 | if (cbk) |
484 | gru_flush_cache(cbk); | ||
420 | STAT(tlb_dropin_fail_no_exception); | 485 | STAT(tlb_dropin_fail_no_exception); |
421 | gru_dbg(grudev, "FAILED non-exception tfh: 0x%p, status %d, state %d\n", tfh, tfh->status, tfh->state); | 486 | gru_dbg(grudev, "FAILED non-exception tfh: 0x%p, status %d, state %d\n", |
487 | tfh, tfh->status, tfh->state); | ||
422 | return 0; | 488 | return 0; |
423 | 489 | ||
424 | failidle: | 490 | failidle: |
425 | /* TFH state was idle - no miss pending */ | 491 | /* TFH state was idle - no miss pending */ |
426 | gru_flush_cache(tfh); | 492 | gru_flush_cache(tfh); |
427 | if (cb) | 493 | gru_flush_cache_cbe(cbe); |
428 | gru_flush_cache(cb); | 494 | if (cbk) |
495 | gru_flush_cache(cbk); | ||
429 | STAT(tlb_dropin_fail_idle); | 496 | STAT(tlb_dropin_fail_idle); |
430 | gru_dbg(grudev, "FAILED idle tfh: 0x%p, state %d\n", tfh, tfh->state); | 497 | gru_dbg(grudev, "FAILED idle tfh: 0x%p, state %d\n", tfh, tfh->state); |
431 | return 0; | 498 | return 0; |
@@ -433,16 +500,18 @@ failidle: | |||
433 | failinval: | 500 | failinval: |
434 | /* All errors (atomic & non-atomic) switch CBR to EXCEPTION state */ | 501 | /* All errors (atomic & non-atomic) switch CBR to EXCEPTION state */ |
435 | tfh_exception(tfh); | 502 | tfh_exception(tfh); |
503 | gru_flush_cache_cbe(cbe); | ||
436 | STAT(tlb_dropin_fail_invalid); | 504 | STAT(tlb_dropin_fail_invalid); |
437 | gru_dbg(grudev, "FAILED inval tfh: 0x%p, vaddr 0x%lx\n", tfh, vaddr); | 505 | gru_dbg(grudev, "FAILED inval tfh: 0x%p, vaddr 0x%lx\n", tfh, vaddr); |
438 | return -EFAULT; | 506 | return -EFAULT; |
439 | 507 | ||
440 | failactive: | 508 | failactive: |
441 | /* Range invalidate active. Switch to UPM iff atomic */ | 509 | /* Range invalidate active. Switch to UPM iff atomic */ |
442 | if (!cb) | 510 | if (!cbk) |
443 | tfh_user_polling_mode(tfh); | 511 | tfh_user_polling_mode(tfh); |
444 | else | 512 | else |
445 | gru_flush_cache(tfh); | 513 | gru_flush_cache(tfh); |
514 | gru_flush_cache_cbe(cbe); | ||
446 | STAT(tlb_dropin_fail_range_active); | 515 | STAT(tlb_dropin_fail_range_active); |
447 | gru_dbg(grudev, "FAILED range active: tfh 0x%p, vaddr 0x%lx\n", | 516 | gru_dbg(grudev, "FAILED range active: tfh 0x%p, vaddr 0x%lx\n", |
448 | tfh, vaddr); | 517 | tfh, vaddr); |
@@ -455,31 +524,41 @@ failactive: | |||
455 | * Note that this is the interrupt handler that is registered with linux | 524 | * Note that this is the interrupt handler that is registered with linux |
456 | * interrupt handlers. | 525 | * interrupt handlers. |
457 | */ | 526 | */ |
458 | irqreturn_t gru_intr(int irq, void *dev_id) | 527 | static irqreturn_t gru_intr(int chiplet, int blade) |
459 | { | 528 | { |
460 | struct gru_state *gru; | 529 | struct gru_state *gru; |
461 | struct gru_tlb_fault_map imap, dmap; | 530 | struct gru_tlb_fault_map imap, dmap; |
462 | struct gru_thread_state *gts; | 531 | struct gru_thread_state *gts; |
463 | struct gru_tlb_fault_handle *tfh = NULL; | 532 | struct gru_tlb_fault_handle *tfh = NULL; |
533 | struct completion *cmp; | ||
464 | int cbrnum, ctxnum; | 534 | int cbrnum, ctxnum; |
465 | 535 | ||
466 | STAT(intr); | 536 | STAT(intr); |
467 | 537 | ||
468 | gru = irq_to_gru(irq); | 538 | gru = &gru_base[blade]->bs_grus[chiplet]; |
469 | if (!gru) { | 539 | if (!gru) { |
470 | dev_err(grudev, "GRU: invalid interrupt: cpu %d, irq %d\n", | 540 | dev_err(grudev, "GRU: invalid interrupt: cpu %d, chiplet %d\n", |
471 | raw_smp_processor_id(), irq); | 541 | raw_smp_processor_id(), chiplet); |
472 | return IRQ_NONE; | 542 | return IRQ_NONE; |
473 | } | 543 | } |
474 | get_clear_fault_map(gru, &imap, &dmap); | 544 | get_clear_fault_map(gru, &imap, &dmap); |
545 | gru_dbg(grudev, | ||
546 | "cpu %d, chiplet %d, gid %d, imap %016lx %016lx, dmap %016lx %016lx\n", | ||
547 | smp_processor_id(), chiplet, gru->gs_gid, | ||
548 | imap.fault_bits[0], imap.fault_bits[1], | ||
549 | dmap.fault_bits[0], dmap.fault_bits[1]); | ||
475 | 550 | ||
476 | for_each_cbr_in_tfm(cbrnum, dmap.fault_bits) { | 551 | for_each_cbr_in_tfm(cbrnum, dmap.fault_bits) { |
477 | complete(gru->gs_blade->bs_async_wq); | 552 | STAT(intr_cbr); |
553 | cmp = gru->gs_blade->bs_async_wq; | ||
554 | if (cmp) | ||
555 | complete(cmp); | ||
478 | gru_dbg(grudev, "gid %d, cbr_done %d, done %d\n", | 556 | gru_dbg(grudev, "gid %d, cbr_done %d, done %d\n", |
479 | gru->gs_gid, cbrnum, gru->gs_blade->bs_async_wq->done); | 557 | gru->gs_gid, cbrnum, cmp ? cmp->done : -1); |
480 | } | 558 | } |
481 | 559 | ||
482 | for_each_cbr_in_tfm(cbrnum, imap.fault_bits) { | 560 | for_each_cbr_in_tfm(cbrnum, imap.fault_bits) { |
561 | STAT(intr_tfh); | ||
483 | tfh = get_tfh_by_index(gru, cbrnum); | 562 | tfh = get_tfh_by_index(gru, cbrnum); |
484 | prefetchw(tfh); /* Helps on hdw, required for emulator */ | 563 | prefetchw(tfh); /* Helps on hdw, required for emulator */ |
485 | 564 | ||
@@ -492,14 +571,20 @@ irqreturn_t gru_intr(int irq, void *dev_id) | |||
492 | ctxnum = tfh->ctxnum; | 571 | ctxnum = tfh->ctxnum; |
493 | gts = gru->gs_gts[ctxnum]; | 572 | gts = gru->gs_gts[ctxnum]; |
494 | 573 | ||
574 | /* Spurious interrupts can cause this. Ignore. */ | ||
575 | if (!gts) { | ||
576 | STAT(intr_spurious); | ||
577 | continue; | ||
578 | } | ||
579 | |||
495 | /* | 580 | /* |
496 | * This is running in interrupt context. Trylock the mmap_sem. | 581 | * This is running in interrupt context. Trylock the mmap_sem. |
497 | * If it fails, retry the fault in user context. | 582 | * If it fails, retry the fault in user context. |
498 | */ | 583 | */ |
584 | gts->ustats.fmm_tlbmiss++; | ||
499 | if (!gts->ts_force_cch_reload && | 585 | if (!gts->ts_force_cch_reload && |
500 | down_read_trylock(>s->ts_mm->mmap_sem)) { | 586 | down_read_trylock(>s->ts_mm->mmap_sem)) { |
501 | gts->ustats.fmm_tlbdropin++; | 587 | gru_try_dropin(gru, gts, tfh, NULL); |
502 | gru_try_dropin(gts, tfh, NULL); | ||
503 | up_read(>s->ts_mm->mmap_sem); | 588 | up_read(>s->ts_mm->mmap_sem); |
504 | } else { | 589 | } else { |
505 | tfh_user_polling_mode(tfh); | 590 | tfh_user_polling_mode(tfh); |
@@ -509,20 +594,43 @@ irqreturn_t gru_intr(int irq, void *dev_id) | |||
509 | return IRQ_HANDLED; | 594 | return IRQ_HANDLED; |
510 | } | 595 | } |
511 | 596 | ||
597 | irqreturn_t gru0_intr(int irq, void *dev_id) | ||
598 | { | ||
599 | return gru_intr(0, uv_numa_blade_id()); | ||
600 | } | ||
601 | |||
602 | irqreturn_t gru1_intr(int irq, void *dev_id) | ||
603 | { | ||
604 | return gru_intr(1, uv_numa_blade_id()); | ||
605 | } | ||
606 | |||
607 | irqreturn_t gru_intr_mblade(int irq, void *dev_id) | ||
608 | { | ||
609 | int blade; | ||
610 | |||
611 | for_each_possible_blade(blade) { | ||
612 | if (uv_blade_nr_possible_cpus(blade)) | ||
613 | continue; | ||
614 | gru_intr(0, blade); | ||
615 | gru_intr(1, blade); | ||
616 | } | ||
617 | return IRQ_HANDLED; | ||
618 | } | ||
619 | |||
512 | 620 | ||
513 | static int gru_user_dropin(struct gru_thread_state *gts, | 621 | static int gru_user_dropin(struct gru_thread_state *gts, |
514 | struct gru_tlb_fault_handle *tfh, | 622 | struct gru_tlb_fault_handle *tfh, |
515 | unsigned long __user *cb) | 623 | void *cb) |
516 | { | 624 | { |
517 | struct gru_mm_struct *gms = gts->ts_gms; | 625 | struct gru_mm_struct *gms = gts->ts_gms; |
518 | int ret; | 626 | int ret; |
519 | 627 | ||
520 | gts->ustats.upm_tlbdropin++; | 628 | gts->ustats.upm_tlbmiss++; |
521 | while (1) { | 629 | while (1) { |
522 | wait_event(gms->ms_wait_queue, | 630 | wait_event(gms->ms_wait_queue, |
523 | atomic_read(&gms->ms_range_active) == 0); | 631 | atomic_read(&gms->ms_range_active) == 0); |
524 | prefetchw(tfh); /* Helps on hdw, required for emulator */ | 632 | prefetchw(tfh); /* Helps on hdw, required for emulator */ |
525 | ret = gru_try_dropin(gts, tfh, cb); | 633 | ret = gru_try_dropin(gts->ts_gru, gts, tfh, cb); |
526 | if (ret <= 0) | 634 | if (ret <= 0) |
527 | return ret; | 635 | return ret; |
528 | STAT(call_os_wait_queue); | 636 | STAT(call_os_wait_queue); |
@@ -538,52 +646,41 @@ int gru_handle_user_call_os(unsigned long cb) | |||
538 | { | 646 | { |
539 | struct gru_tlb_fault_handle *tfh; | 647 | struct gru_tlb_fault_handle *tfh; |
540 | struct gru_thread_state *gts; | 648 | struct gru_thread_state *gts; |
541 | unsigned long __user *cbp; | 649 | void *cbk; |
542 | int ucbnum, cbrnum, ret = -EINVAL; | 650 | int ucbnum, cbrnum, ret = -EINVAL; |
543 | 651 | ||
544 | STAT(call_os); | 652 | STAT(call_os); |
545 | gru_dbg(grudev, "address 0x%lx\n", cb); | ||
546 | 653 | ||
547 | /* sanity check the cb pointer */ | 654 | /* sanity check the cb pointer */ |
548 | ucbnum = get_cb_number((void *)cb); | 655 | ucbnum = get_cb_number((void *)cb); |
549 | if ((cb & (GRU_HANDLE_STRIDE - 1)) || ucbnum >= GRU_NUM_CB) | 656 | if ((cb & (GRU_HANDLE_STRIDE - 1)) || ucbnum >= GRU_NUM_CB) |
550 | return -EINVAL; | 657 | return -EINVAL; |
551 | cbp = (unsigned long *)cb; | ||
552 | 658 | ||
553 | gts = gru_find_lock_gts(cb); | 659 | gts = gru_find_lock_gts(cb); |
554 | if (!gts) | 660 | if (!gts) |
555 | return -EINVAL; | 661 | return -EINVAL; |
662 | gru_dbg(grudev, "address 0x%lx, gid %d, gts 0x%p\n", cb, gts->ts_gru ? gts->ts_gru->gs_gid : -1, gts); | ||
556 | 663 | ||
557 | if (ucbnum >= gts->ts_cbr_au_count * GRU_CBR_AU_SIZE) | 664 | if (ucbnum >= gts->ts_cbr_au_count * GRU_CBR_AU_SIZE) |
558 | goto exit; | 665 | goto exit; |
559 | 666 | ||
560 | /* | 667 | gru_check_context_placement(gts); |
561 | * If force_unload is set, the UPM TLB fault is phony. The task | ||
562 | * has migrated to another node and the GSEG must be moved. Just | ||
563 | * unload the context. The task will page fault and assign a new | ||
564 | * context. | ||
565 | */ | ||
566 | if (gts->ts_tgid_owner == current->tgid && gts->ts_blade >= 0 && | ||
567 | gts->ts_blade != uv_numa_blade_id()) { | ||
568 | STAT(call_os_offnode_reference); | ||
569 | gts->ts_force_unload = 1; | ||
570 | } | ||
571 | 668 | ||
572 | /* | 669 | /* |
573 | * CCH may contain stale data if ts_force_cch_reload is set. | 670 | * CCH may contain stale data if ts_force_cch_reload is set. |
574 | */ | 671 | */ |
575 | if (gts->ts_gru && gts->ts_force_cch_reload) { | 672 | if (gts->ts_gru && gts->ts_force_cch_reload) { |
576 | gts->ts_force_cch_reload = 0; | 673 | gts->ts_force_cch_reload = 0; |
577 | gru_update_cch(gts, 0); | 674 | gru_update_cch(gts); |
578 | } | 675 | } |
579 | 676 | ||
580 | ret = -EAGAIN; | 677 | ret = -EAGAIN; |
581 | cbrnum = thread_cbr_number(gts, ucbnum); | 678 | cbrnum = thread_cbr_number(gts, ucbnum); |
582 | if (gts->ts_force_unload) { | 679 | if (gts->ts_gru) { |
583 | gru_unload_context(gts, 1); | ||
584 | } else if (gts->ts_gru) { | ||
585 | tfh = get_tfh_by_index(gts->ts_gru, cbrnum); | 680 | tfh = get_tfh_by_index(gts->ts_gru, cbrnum); |
586 | ret = gru_user_dropin(gts, tfh, cbp); | 681 | cbk = get_gseg_base_address_cb(gts->ts_gru->gs_gru_base_vaddr, |
682 | gts->ts_ctxnum, ucbnum); | ||
683 | ret = gru_user_dropin(gts, tfh, cbk); | ||
587 | } | 684 | } |
588 | exit: | 685 | exit: |
589 | gru_unlock_gts(gts); | 686 | gru_unlock_gts(gts); |
@@ -605,11 +702,11 @@ int gru_get_exception_detail(unsigned long arg) | |||
605 | if (copy_from_user(&excdet, (void __user *)arg, sizeof(excdet))) | 702 | if (copy_from_user(&excdet, (void __user *)arg, sizeof(excdet))) |
606 | return -EFAULT; | 703 | return -EFAULT; |
607 | 704 | ||
608 | gru_dbg(grudev, "address 0x%lx\n", excdet.cb); | ||
609 | gts = gru_find_lock_gts(excdet.cb); | 705 | gts = gru_find_lock_gts(excdet.cb); |
610 | if (!gts) | 706 | if (!gts) |
611 | return -EINVAL; | 707 | return -EINVAL; |
612 | 708 | ||
709 | gru_dbg(grudev, "address 0x%lx, gid %d, gts 0x%p\n", excdet.cb, gts->ts_gru ? gts->ts_gru->gs_gid : -1, gts); | ||
613 | ucbnum = get_cb_number((void *)excdet.cb); | 710 | ucbnum = get_cb_number((void *)excdet.cb); |
614 | if (ucbnum >= gts->ts_cbr_au_count * GRU_CBR_AU_SIZE) { | 711 | if (ucbnum >= gts->ts_cbr_au_count * GRU_CBR_AU_SIZE) { |
615 | ret = -EINVAL; | 712 | ret = -EINVAL; |
@@ -617,6 +714,7 @@ int gru_get_exception_detail(unsigned long arg) | |||
617 | cbrnum = thread_cbr_number(gts, ucbnum); | 714 | cbrnum = thread_cbr_number(gts, ucbnum); |
618 | cbe = get_cbe_by_index(gts->ts_gru, cbrnum); | 715 | cbe = get_cbe_by_index(gts->ts_gru, cbrnum); |
619 | gru_flush_cache(cbe); /* CBE not coherent */ | 716 | gru_flush_cache(cbe); /* CBE not coherent */ |
717 | sync_core(); /* make sure we are have current data */ | ||
620 | excdet.opc = cbe->opccpy; | 718 | excdet.opc = cbe->opccpy; |
621 | excdet.exopc = cbe->exopccpy; | 719 | excdet.exopc = cbe->exopccpy; |
622 | excdet.ecause = cbe->ecause; | 720 | excdet.ecause = cbe->ecause; |
@@ -624,7 +722,7 @@ int gru_get_exception_detail(unsigned long arg) | |||
624 | excdet.exceptdet1 = cbe->idef3upd; | 722 | excdet.exceptdet1 = cbe->idef3upd; |
625 | excdet.cbrstate = cbe->cbrstate; | 723 | excdet.cbrstate = cbe->cbrstate; |
626 | excdet.cbrexecstatus = cbe->cbrexecstatus; | 724 | excdet.cbrexecstatus = cbe->cbrexecstatus; |
627 | gru_flush_cache(cbe); | 725 | gru_flush_cache_cbe(cbe); |
628 | ret = 0; | 726 | ret = 0; |
629 | } else { | 727 | } else { |
630 | ret = -EAGAIN; | 728 | ret = -EAGAIN; |
@@ -733,6 +831,11 @@ long gru_get_gseg_statistics(unsigned long arg) | |||
733 | if (copy_from_user(&req, (void __user *)arg, sizeof(req))) | 831 | if (copy_from_user(&req, (void __user *)arg, sizeof(req))) |
734 | return -EFAULT; | 832 | return -EFAULT; |
735 | 833 | ||
834 | /* | ||
835 | * The library creates arrays of contexts for threaded programs. | ||
836 | * If no gts exists in the array, the context has never been used & all | ||
837 | * statistics are implicitly 0. | ||
838 | */ | ||
736 | gts = gru_find_lock_gts(req.gseg); | 839 | gts = gru_find_lock_gts(req.gseg); |
737 | if (gts) { | 840 | if (gts) { |
738 | memcpy(&req.stats, >s->ustats, sizeof(gts->ustats)); | 841 | memcpy(&req.stats, >s->ustats, sizeof(gts->ustats)); |
@@ -762,11 +865,25 @@ int gru_set_context_option(unsigned long arg) | |||
762 | return -EFAULT; | 865 | return -EFAULT; |
763 | gru_dbg(grudev, "op %d, gseg 0x%lx, value1 0x%lx\n", req.op, req.gseg, req.val1); | 866 | gru_dbg(grudev, "op %d, gseg 0x%lx, value1 0x%lx\n", req.op, req.gseg, req.val1); |
764 | 867 | ||
765 | gts = gru_alloc_locked_gts(req.gseg); | 868 | gts = gru_find_lock_gts(req.gseg); |
766 | if (!gts) | 869 | if (!gts) { |
767 | return -EINVAL; | 870 | gts = gru_alloc_locked_gts(req.gseg); |
871 | if (IS_ERR(gts)) | ||
872 | return PTR_ERR(gts); | ||
873 | } | ||
768 | 874 | ||
769 | switch (req.op) { | 875 | switch (req.op) { |
876 | case sco_blade_chiplet: | ||
877 | /* Select blade/chiplet for GRU context */ | ||
878 | if (req.val1 < -1 || req.val1 >= GRU_MAX_BLADES || !gru_base[req.val1] || | ||
879 | req.val0 < -1 || req.val0 >= GRU_CHIPLETS_PER_HUB) { | ||
880 | ret = -EINVAL; | ||
881 | } else { | ||
882 | gts->ts_user_blade_id = req.val1; | ||
883 | gts->ts_user_chiplet_id = req.val0; | ||
884 | gru_check_context_placement(gts); | ||
885 | } | ||
886 | break; | ||
770 | case sco_gseg_owner: | 887 | case sco_gseg_owner: |
771 | /* Register the current task as the GSEG owner */ | 888 | /* Register the current task as the GSEG owner */ |
772 | gts->ts_tgid_owner = current->tgid; | 889 | gts->ts_tgid_owner = current->tgid; |
diff --git a/drivers/misc/sgi-gru/grufile.c b/drivers/misc/sgi-gru/grufile.c index 41c8fe2a928c..cb3b4d228475 100644 --- a/drivers/misc/sgi-gru/grufile.c +++ b/drivers/misc/sgi-gru/grufile.c | |||
@@ -35,6 +35,9 @@ | |||
35 | #include <linux/interrupt.h> | 35 | #include <linux/interrupt.h> |
36 | #include <linux/proc_fs.h> | 36 | #include <linux/proc_fs.h> |
37 | #include <linux/uaccess.h> | 37 | #include <linux/uaccess.h> |
38 | #ifdef CONFIG_X86_64 | ||
39 | #include <asm/uv/uv_irq.h> | ||
40 | #endif | ||
38 | #include <asm/uv/uv.h> | 41 | #include <asm/uv/uv.h> |
39 | #include "gru.h" | 42 | #include "gru.h" |
40 | #include "grulib.h" | 43 | #include "grulib.h" |
@@ -92,7 +95,7 @@ static void gru_vma_close(struct vm_area_struct *vma) | |||
92 | /* | 95 | /* |
93 | * gru_file_mmap | 96 | * gru_file_mmap |
94 | * | 97 | * |
95 | * Called when mmaping the device. Initializes the vma with a fault handler | 98 | * Called when mmapping the device. Initializes the vma with a fault handler |
96 | * and private data structure necessary to allocate, track, and free the | 99 | * and private data structure necessary to allocate, track, and free the |
97 | * underlying pages. | 100 | * underlying pages. |
98 | */ | 101 | */ |
@@ -130,7 +133,6 @@ static int gru_create_new_context(unsigned long arg) | |||
130 | struct gru_vma_data *vdata; | 133 | struct gru_vma_data *vdata; |
131 | int ret = -EINVAL; | 134 | int ret = -EINVAL; |
132 | 135 | ||
133 | |||
134 | if (copy_from_user(&req, (void __user *)arg, sizeof(req))) | 136 | if (copy_from_user(&req, (void __user *)arg, sizeof(req))) |
135 | return -EFAULT; | 137 | return -EFAULT; |
136 | 138 | ||
@@ -150,6 +152,7 @@ static int gru_create_new_context(unsigned long arg) | |||
150 | vdata->vd_dsr_au_count = | 152 | vdata->vd_dsr_au_count = |
151 | GRU_DS_BYTES_TO_AU(req.data_segment_bytes); | 153 | GRU_DS_BYTES_TO_AU(req.data_segment_bytes); |
152 | vdata->vd_cbr_au_count = GRU_CB_COUNT_TO_AU(req.control_blocks); | 154 | vdata->vd_cbr_au_count = GRU_CB_COUNT_TO_AU(req.control_blocks); |
155 | vdata->vd_tlb_preload_count = req.tlb_preload_count; | ||
153 | ret = 0; | 156 | ret = 0; |
154 | } | 157 | } |
155 | up_write(¤t->mm->mmap_sem); | 158 | up_write(¤t->mm->mmap_sem); |
@@ -190,7 +193,7 @@ static long gru_file_unlocked_ioctl(struct file *file, unsigned int req, | |||
190 | { | 193 | { |
191 | int err = -EBADRQC; | 194 | int err = -EBADRQC; |
192 | 195 | ||
193 | gru_dbg(grudev, "file %p\n", file); | 196 | gru_dbg(grudev, "file %p, req 0x%x, 0x%lx\n", file, req, arg); |
194 | 197 | ||
195 | switch (req) { | 198 | switch (req) { |
196 | case GRU_CREATE_CONTEXT: | 199 | case GRU_CREATE_CONTEXT: |
@@ -232,23 +235,24 @@ static long gru_file_unlocked_ioctl(struct file *file, unsigned int req, | |||
232 | * system. | 235 | * system. |
233 | */ | 236 | */ |
234 | static void gru_init_chiplet(struct gru_state *gru, unsigned long paddr, | 237 | static void gru_init_chiplet(struct gru_state *gru, unsigned long paddr, |
235 | void *vaddr, int nid, int bid, int grunum) | 238 | void *vaddr, int blade_id, int chiplet_id) |
236 | { | 239 | { |
237 | spin_lock_init(&gru->gs_lock); | 240 | spin_lock_init(&gru->gs_lock); |
238 | spin_lock_init(&gru->gs_asid_lock); | 241 | spin_lock_init(&gru->gs_asid_lock); |
239 | gru->gs_gru_base_paddr = paddr; | 242 | gru->gs_gru_base_paddr = paddr; |
240 | gru->gs_gru_base_vaddr = vaddr; | 243 | gru->gs_gru_base_vaddr = vaddr; |
241 | gru->gs_gid = bid * GRU_CHIPLETS_PER_BLADE + grunum; | 244 | gru->gs_gid = blade_id * GRU_CHIPLETS_PER_BLADE + chiplet_id; |
242 | gru->gs_blade = gru_base[bid]; | 245 | gru->gs_blade = gru_base[blade_id]; |
243 | gru->gs_blade_id = bid; | 246 | gru->gs_blade_id = blade_id; |
247 | gru->gs_chiplet_id = chiplet_id; | ||
244 | gru->gs_cbr_map = (GRU_CBR_AU == 64) ? ~0 : (1UL << GRU_CBR_AU) - 1; | 248 | gru->gs_cbr_map = (GRU_CBR_AU == 64) ? ~0 : (1UL << GRU_CBR_AU) - 1; |
245 | gru->gs_dsr_map = (1UL << GRU_DSR_AU) - 1; | 249 | gru->gs_dsr_map = (1UL << GRU_DSR_AU) - 1; |
246 | gru->gs_asid_limit = MAX_ASID; | 250 | gru->gs_asid_limit = MAX_ASID; |
247 | gru_tgh_flush_init(gru); | 251 | gru_tgh_flush_init(gru); |
248 | if (gru->gs_gid >= gru_max_gids) | 252 | if (gru->gs_gid >= gru_max_gids) |
249 | gru_max_gids = gru->gs_gid + 1; | 253 | gru_max_gids = gru->gs_gid + 1; |
250 | gru_dbg(grudev, "bid %d, nid %d, gid %d, vaddr %p (0x%lx)\n", | 254 | gru_dbg(grudev, "bid %d, gid %d, vaddr %p (0x%lx)\n", |
251 | bid, nid, gru->gs_gid, gru->gs_gru_base_vaddr, | 255 | blade_id, gru->gs_gid, gru->gs_gru_base_vaddr, |
252 | gru->gs_gru_base_paddr); | 256 | gru->gs_gru_base_paddr); |
253 | } | 257 | } |
254 | 258 | ||
@@ -264,12 +268,10 @@ static int gru_init_tables(unsigned long gru_base_paddr, void *gru_base_vaddr) | |||
264 | 268 | ||
265 | max_user_cbrs = GRU_NUM_CB; | 269 | max_user_cbrs = GRU_NUM_CB; |
266 | max_user_dsr_bytes = GRU_NUM_DSR_BYTES; | 270 | max_user_dsr_bytes = GRU_NUM_DSR_BYTES; |
267 | for_each_online_node(nid) { | 271 | for_each_possible_blade(bid) { |
268 | bid = uv_node_to_blade_id(nid); | 272 | pnode = uv_blade_to_pnode(bid); |
269 | pnode = uv_node_to_pnode(nid); | 273 | nid = uv_blade_to_memory_nid(bid);/* -1 if no memory on blade */ |
270 | if (bid < 0 || gru_base[bid]) | 274 | page = alloc_pages_node(nid, GFP_KERNEL, order); |
271 | continue; | ||
272 | page = alloc_pages_exact_node(nid, GFP_KERNEL, order); | ||
273 | if (!page) | 275 | if (!page) |
274 | goto fail; | 276 | goto fail; |
275 | gru_base[bid] = page_address(page); | 277 | gru_base[bid] = page_address(page); |
@@ -285,7 +287,7 @@ static int gru_init_tables(unsigned long gru_base_paddr, void *gru_base_vaddr) | |||
285 | chip++, gru++) { | 287 | chip++, gru++) { |
286 | paddr = gru_chiplet_paddr(gru_base_paddr, pnode, chip); | 288 | paddr = gru_chiplet_paddr(gru_base_paddr, pnode, chip); |
287 | vaddr = gru_chiplet_vaddr(gru_base_vaddr, pnode, chip); | 289 | vaddr = gru_chiplet_vaddr(gru_base_vaddr, pnode, chip); |
288 | gru_init_chiplet(gru, paddr, vaddr, nid, bid, chip); | 290 | gru_init_chiplet(gru, paddr, vaddr, bid, chip); |
289 | n = hweight64(gru->gs_cbr_map) * GRU_CBR_AU_SIZE; | 291 | n = hweight64(gru->gs_cbr_map) * GRU_CBR_AU_SIZE; |
290 | cbrs = max(cbrs, n); | 292 | cbrs = max(cbrs, n); |
291 | n = hweight64(gru->gs_dsr_map) * GRU_DSR_AU_BYTES; | 293 | n = hweight64(gru->gs_dsr_map) * GRU_DSR_AU_BYTES; |
@@ -298,39 +300,215 @@ static int gru_init_tables(unsigned long gru_base_paddr, void *gru_base_vaddr) | |||
298 | return 0; | 300 | return 0; |
299 | 301 | ||
300 | fail: | 302 | fail: |
301 | for (nid--; nid >= 0; nid--) | 303 | for (bid--; bid >= 0; bid--) |
302 | free_pages((unsigned long)gru_base[nid], order); | 304 | free_pages((unsigned long)gru_base[bid], order); |
303 | return -ENOMEM; | 305 | return -ENOMEM; |
304 | } | 306 | } |
305 | 307 | ||
306 | #ifdef CONFIG_IA64 | 308 | static void gru_free_tables(void) |
309 | { | ||
310 | int bid; | ||
311 | int order = get_order(sizeof(struct gru_state) * | ||
312 | GRU_CHIPLETS_PER_BLADE); | ||
307 | 313 | ||
308 | static int get_base_irq(void) | 314 | for (bid = 0; bid < GRU_MAX_BLADES; bid++) |
315 | free_pages((unsigned long)gru_base[bid], order); | ||
316 | } | ||
317 | |||
318 | static unsigned long gru_chiplet_cpu_to_mmr(int chiplet, int cpu, int *corep) | ||
309 | { | 319 | { |
310 | return IRQ_GRU; | 320 | unsigned long mmr = 0; |
321 | int core; | ||
322 | |||
323 | /* | ||
324 | * We target the cores of a blade and not the hyperthreads themselves. | ||
325 | * There is a max of 8 cores per socket and 2 sockets per blade, | ||
326 | * making for a max total of 16 cores (i.e., 16 CPUs without | ||
327 | * hyperthreading and 32 CPUs with hyperthreading). | ||
328 | */ | ||
329 | core = uv_cpu_core_number(cpu) + UV_MAX_INT_CORES * uv_cpu_socket_number(cpu); | ||
330 | if (core >= GRU_NUM_TFM || uv_cpu_ht_number(cpu)) | ||
331 | return 0; | ||
332 | |||
333 | if (chiplet == 0) { | ||
334 | mmr = UVH_GR0_TLB_INT0_CONFIG + | ||
335 | core * (UVH_GR0_TLB_INT1_CONFIG - UVH_GR0_TLB_INT0_CONFIG); | ||
336 | } else if (chiplet == 1) { | ||
337 | mmr = UVH_GR1_TLB_INT0_CONFIG + | ||
338 | core * (UVH_GR1_TLB_INT1_CONFIG - UVH_GR1_TLB_INT0_CONFIG); | ||
339 | } else { | ||
340 | BUG(); | ||
341 | } | ||
342 | |||
343 | *corep = core; | ||
344 | return mmr; | ||
311 | } | 345 | } |
312 | 346 | ||
313 | #elif defined CONFIG_X86_64 | 347 | #ifdef CONFIG_IA64 |
314 | 348 | ||
315 | static void noop(unsigned int irq) | 349 | static int gru_irq_count[GRU_CHIPLETS_PER_BLADE]; |
350 | |||
351 | static void gru_noop(unsigned int irq) | ||
316 | { | 352 | { |
317 | } | 353 | } |
318 | 354 | ||
319 | static struct irq_chip gru_chip = { | 355 | static struct irq_chip gru_chip[GRU_CHIPLETS_PER_BLADE] = { |
320 | .name = "gru", | 356 | [0 ... GRU_CHIPLETS_PER_BLADE - 1] { |
321 | .mask = noop, | 357 | .mask = gru_noop, |
322 | .unmask = noop, | 358 | .unmask = gru_noop, |
323 | .ack = noop, | 359 | .ack = gru_noop |
360 | } | ||
324 | }; | 361 | }; |
325 | 362 | ||
326 | static int get_base_irq(void) | 363 | static int gru_chiplet_setup_tlb_irq(int chiplet, char *irq_name, |
364 | irq_handler_t irq_handler, int cpu, int blade) | ||
365 | { | ||
366 | unsigned long mmr; | ||
367 | int irq = IRQ_GRU + chiplet; | ||
368 | int ret, core; | ||
369 | |||
370 | mmr = gru_chiplet_cpu_to_mmr(chiplet, cpu, &core); | ||
371 | if (mmr == 0) | ||
372 | return 0; | ||
373 | |||
374 | if (gru_irq_count[chiplet] == 0) { | ||
375 | gru_chip[chiplet].name = irq_name; | ||
376 | ret = set_irq_chip(irq, &gru_chip[chiplet]); | ||
377 | if (ret) { | ||
378 | printk(KERN_ERR "%s: set_irq_chip failed, errno=%d\n", | ||
379 | GRU_DRIVER_ID_STR, -ret); | ||
380 | return ret; | ||
381 | } | ||
382 | |||
383 | ret = request_irq(irq, irq_handler, 0, irq_name, NULL); | ||
384 | if (ret) { | ||
385 | printk(KERN_ERR "%s: request_irq failed, errno=%d\n", | ||
386 | GRU_DRIVER_ID_STR, -ret); | ||
387 | return ret; | ||
388 | } | ||
389 | } | ||
390 | gru_irq_count[chiplet]++; | ||
391 | |||
392 | return 0; | ||
393 | } | ||
394 | |||
395 | static void gru_chiplet_teardown_tlb_irq(int chiplet, int cpu, int blade) | ||
396 | { | ||
397 | unsigned long mmr; | ||
398 | int core, irq = IRQ_GRU + chiplet; | ||
399 | |||
400 | if (gru_irq_count[chiplet] == 0) | ||
401 | return; | ||
402 | |||
403 | mmr = gru_chiplet_cpu_to_mmr(chiplet, cpu, &core); | ||
404 | if (mmr == 0) | ||
405 | return; | ||
406 | |||
407 | if (--gru_irq_count[chiplet] == 0) | ||
408 | free_irq(irq, NULL); | ||
409 | } | ||
410 | |||
411 | #elif defined CONFIG_X86_64 | ||
412 | |||
413 | static int gru_chiplet_setup_tlb_irq(int chiplet, char *irq_name, | ||
414 | irq_handler_t irq_handler, int cpu, int blade) | ||
415 | { | ||
416 | unsigned long mmr; | ||
417 | int irq, core; | ||
418 | int ret; | ||
419 | |||
420 | mmr = gru_chiplet_cpu_to_mmr(chiplet, cpu, &core); | ||
421 | if (mmr == 0) | ||
422 | return 0; | ||
423 | |||
424 | irq = uv_setup_irq(irq_name, cpu, blade, mmr, UV_AFFINITY_CPU); | ||
425 | if (irq < 0) { | ||
426 | printk(KERN_ERR "%s: uv_setup_irq failed, errno=%d\n", | ||
427 | GRU_DRIVER_ID_STR, -irq); | ||
428 | return irq; | ||
429 | } | ||
430 | |||
431 | ret = request_irq(irq, irq_handler, 0, irq_name, NULL); | ||
432 | if (ret) { | ||
433 | uv_teardown_irq(irq); | ||
434 | printk(KERN_ERR "%s: request_irq failed, errno=%d\n", | ||
435 | GRU_DRIVER_ID_STR, -ret); | ||
436 | return ret; | ||
437 | } | ||
438 | gru_base[blade]->bs_grus[chiplet].gs_irq[core] = irq; | ||
439 | return 0; | ||
440 | } | ||
441 | |||
442 | static void gru_chiplet_teardown_tlb_irq(int chiplet, int cpu, int blade) | ||
327 | { | 443 | { |
328 | set_irq_chip(IRQ_GRU, &gru_chip); | 444 | int irq, core; |
329 | set_irq_chip(IRQ_GRU + 1, &gru_chip); | 445 | unsigned long mmr; |
330 | return IRQ_GRU; | 446 | |
447 | mmr = gru_chiplet_cpu_to_mmr(chiplet, cpu, &core); | ||
448 | if (mmr) { | ||
449 | irq = gru_base[blade]->bs_grus[chiplet].gs_irq[core]; | ||
450 | if (irq) { | ||
451 | free_irq(irq, NULL); | ||
452 | uv_teardown_irq(irq); | ||
453 | } | ||
454 | } | ||
331 | } | 455 | } |
456 | |||
332 | #endif | 457 | #endif |
333 | 458 | ||
459 | static void gru_teardown_tlb_irqs(void) | ||
460 | { | ||
461 | int blade; | ||
462 | int cpu; | ||
463 | |||
464 | for_each_online_cpu(cpu) { | ||
465 | blade = uv_cpu_to_blade_id(cpu); | ||
466 | gru_chiplet_teardown_tlb_irq(0, cpu, blade); | ||
467 | gru_chiplet_teardown_tlb_irq(1, cpu, blade); | ||
468 | } | ||
469 | for_each_possible_blade(blade) { | ||
470 | if (uv_blade_nr_possible_cpus(blade)) | ||
471 | continue; | ||
472 | gru_chiplet_teardown_tlb_irq(0, 0, blade); | ||
473 | gru_chiplet_teardown_tlb_irq(1, 0, blade); | ||
474 | } | ||
475 | } | ||
476 | |||
477 | static int gru_setup_tlb_irqs(void) | ||
478 | { | ||
479 | int blade; | ||
480 | int cpu; | ||
481 | int ret; | ||
482 | |||
483 | for_each_online_cpu(cpu) { | ||
484 | blade = uv_cpu_to_blade_id(cpu); | ||
485 | ret = gru_chiplet_setup_tlb_irq(0, "GRU0_TLB", gru0_intr, cpu, blade); | ||
486 | if (ret != 0) | ||
487 | goto exit1; | ||
488 | |||
489 | ret = gru_chiplet_setup_tlb_irq(1, "GRU1_TLB", gru1_intr, cpu, blade); | ||
490 | if (ret != 0) | ||
491 | goto exit1; | ||
492 | } | ||
493 | for_each_possible_blade(blade) { | ||
494 | if (uv_blade_nr_possible_cpus(blade)) | ||
495 | continue; | ||
496 | ret = gru_chiplet_setup_tlb_irq(0, "GRU0_TLB", gru_intr_mblade, 0, blade); | ||
497 | if (ret != 0) | ||
498 | goto exit1; | ||
499 | |||
500 | ret = gru_chiplet_setup_tlb_irq(1, "GRU1_TLB", gru_intr_mblade, 0, blade); | ||
501 | if (ret != 0) | ||
502 | goto exit1; | ||
503 | } | ||
504 | |||
505 | return 0; | ||
506 | |||
507 | exit1: | ||
508 | gru_teardown_tlb_irqs(); | ||
509 | return ret; | ||
510 | } | ||
511 | |||
334 | /* | 512 | /* |
335 | * gru_init | 513 | * gru_init |
336 | * | 514 | * |
@@ -338,8 +516,7 @@ static int get_base_irq(void) | |||
338 | */ | 516 | */ |
339 | static int __init gru_init(void) | 517 | static int __init gru_init(void) |
340 | { | 518 | { |
341 | int ret, irq, chip; | 519 | int ret; |
342 | char id[10]; | ||
343 | 520 | ||
344 | if (!is_uv_system()) | 521 | if (!is_uv_system()) |
345 | return 0; | 522 | return 0; |
@@ -354,41 +531,29 @@ static int __init gru_init(void) | |||
354 | gru_end_paddr = gru_start_paddr + GRU_MAX_BLADES * GRU_SIZE; | 531 | gru_end_paddr = gru_start_paddr + GRU_MAX_BLADES * GRU_SIZE; |
355 | printk(KERN_INFO "GRU space: 0x%lx - 0x%lx\n", | 532 | printk(KERN_INFO "GRU space: 0x%lx - 0x%lx\n", |
356 | gru_start_paddr, gru_end_paddr); | 533 | gru_start_paddr, gru_end_paddr); |
357 | irq = get_base_irq(); | ||
358 | for (chip = 0; chip < GRU_CHIPLETS_PER_BLADE; chip++) { | ||
359 | ret = request_irq(irq + chip, gru_intr, 0, id, NULL); | ||
360 | /* TODO: fix irq handling on x86. For now ignore failure because | ||
361 | * interrupts are not required & not yet fully supported */ | ||
362 | if (ret) { | ||
363 | printk(KERN_WARNING | ||
364 | "!!!WARNING: GRU ignoring request failure!!!\n"); | ||
365 | ret = 0; | ||
366 | } | ||
367 | if (ret) { | ||
368 | printk(KERN_ERR "%s: request_irq failed\n", | ||
369 | GRU_DRIVER_ID_STR); | ||
370 | goto exit1; | ||
371 | } | ||
372 | } | ||
373 | |||
374 | ret = misc_register(&gru_miscdev); | 534 | ret = misc_register(&gru_miscdev); |
375 | if (ret) { | 535 | if (ret) { |
376 | printk(KERN_ERR "%s: misc_register failed\n", | 536 | printk(KERN_ERR "%s: misc_register failed\n", |
377 | GRU_DRIVER_ID_STR); | 537 | GRU_DRIVER_ID_STR); |
378 | goto exit1; | 538 | goto exit0; |
379 | } | 539 | } |
380 | 540 | ||
381 | ret = gru_proc_init(); | 541 | ret = gru_proc_init(); |
382 | if (ret) { | 542 | if (ret) { |
383 | printk(KERN_ERR "%s: proc init failed\n", GRU_DRIVER_ID_STR); | 543 | printk(KERN_ERR "%s: proc init failed\n", GRU_DRIVER_ID_STR); |
384 | goto exit2; | 544 | goto exit1; |
385 | } | 545 | } |
386 | 546 | ||
387 | ret = gru_init_tables(gru_start_paddr, gru_start_vaddr); | 547 | ret = gru_init_tables(gru_start_paddr, gru_start_vaddr); |
388 | if (ret) { | 548 | if (ret) { |
389 | printk(KERN_ERR "%s: init tables failed\n", GRU_DRIVER_ID_STR); | 549 | printk(KERN_ERR "%s: init tables failed\n", GRU_DRIVER_ID_STR); |
390 | goto exit3; | 550 | goto exit2; |
391 | } | 551 | } |
552 | |||
553 | ret = gru_setup_tlb_irqs(); | ||
554 | if (ret != 0) | ||
555 | goto exit3; | ||
556 | |||
392 | gru_kservices_init(); | 557 | gru_kservices_init(); |
393 | 558 | ||
394 | printk(KERN_INFO "%s: v%s\n", GRU_DRIVER_ID_STR, | 559 | printk(KERN_INFO "%s: v%s\n", GRU_DRIVER_ID_STR, |
@@ -396,31 +561,24 @@ static int __init gru_init(void) | |||
396 | return 0; | 561 | return 0; |
397 | 562 | ||
398 | exit3: | 563 | exit3: |
399 | gru_proc_exit(); | 564 | gru_free_tables(); |
400 | exit2: | 565 | exit2: |
401 | misc_deregister(&gru_miscdev); | 566 | gru_proc_exit(); |
402 | exit1: | 567 | exit1: |
403 | for (--chip; chip >= 0; chip--) | 568 | misc_deregister(&gru_miscdev); |
404 | free_irq(irq + chip, NULL); | 569 | exit0: |
405 | return ret; | 570 | return ret; |
406 | 571 | ||
407 | } | 572 | } |
408 | 573 | ||
409 | static void __exit gru_exit(void) | 574 | static void __exit gru_exit(void) |
410 | { | 575 | { |
411 | int i, bid; | ||
412 | int order = get_order(sizeof(struct gru_state) * | ||
413 | GRU_CHIPLETS_PER_BLADE); | ||
414 | |||
415 | if (!is_uv_system()) | 576 | if (!is_uv_system()) |
416 | return; | 577 | return; |
417 | 578 | ||
418 | for (i = 0; i < GRU_CHIPLETS_PER_BLADE; i++) | 579 | gru_teardown_tlb_irqs(); |
419 | free_irq(IRQ_GRU + i, NULL); | ||
420 | gru_kservices_exit(); | 580 | gru_kservices_exit(); |
421 | for (bid = 0; bid < GRU_MAX_BLADES; bid++) | 581 | gru_free_tables(); |
422 | free_pages((unsigned long)gru_base[bid], order); | ||
423 | |||
424 | misc_deregister(&gru_miscdev); | 582 | misc_deregister(&gru_miscdev); |
425 | gru_proc_exit(); | 583 | gru_proc_exit(); |
426 | } | 584 | } |
diff --git a/drivers/misc/sgi-gru/gruhandles.c b/drivers/misc/sgi-gru/gruhandles.c index 37e7cfc53b9c..2f30badc6ffd 100644 --- a/drivers/misc/sgi-gru/gruhandles.c +++ b/drivers/misc/sgi-gru/gruhandles.c | |||
@@ -27,9 +27,11 @@ | |||
27 | #ifdef CONFIG_IA64 | 27 | #ifdef CONFIG_IA64 |
28 | #include <asm/processor.h> | 28 | #include <asm/processor.h> |
29 | #define GRU_OPERATION_TIMEOUT (((cycles_t) local_cpu_data->itc_freq)*10) | 29 | #define GRU_OPERATION_TIMEOUT (((cycles_t) local_cpu_data->itc_freq)*10) |
30 | #define CLKS2NSEC(c) ((c) *1000000000 / local_cpu_data->itc_freq) | ||
30 | #else | 31 | #else |
31 | #include <asm/tsc.h> | 32 | #include <asm/tsc.h> |
32 | #define GRU_OPERATION_TIMEOUT ((cycles_t) tsc_khz*10*1000) | 33 | #define GRU_OPERATION_TIMEOUT ((cycles_t) tsc_khz*10*1000) |
34 | #define CLKS2NSEC(c) ((c) * 1000000 / tsc_khz) | ||
33 | #endif | 35 | #endif |
34 | 36 | ||
35 | /* Extract the status field from a kernel handle */ | 37 | /* Extract the status field from a kernel handle */ |
@@ -39,21 +41,39 @@ struct mcs_op_statistic mcs_op_statistics[mcsop_last]; | |||
39 | 41 | ||
40 | static void update_mcs_stats(enum mcs_op op, unsigned long clks) | 42 | static void update_mcs_stats(enum mcs_op op, unsigned long clks) |
41 | { | 43 | { |
44 | unsigned long nsec; | ||
45 | |||
46 | nsec = CLKS2NSEC(clks); | ||
42 | atomic_long_inc(&mcs_op_statistics[op].count); | 47 | atomic_long_inc(&mcs_op_statistics[op].count); |
43 | atomic_long_add(clks, &mcs_op_statistics[op].total); | 48 | atomic_long_add(nsec, &mcs_op_statistics[op].total); |
44 | if (mcs_op_statistics[op].max < clks) | 49 | if (mcs_op_statistics[op].max < nsec) |
45 | mcs_op_statistics[op].max = clks; | 50 | mcs_op_statistics[op].max = nsec; |
46 | } | 51 | } |
47 | 52 | ||
48 | static void start_instruction(void *h) | 53 | static void start_instruction(void *h) |
49 | { | 54 | { |
50 | unsigned long *w0 = h; | 55 | unsigned long *w0 = h; |
51 | 56 | ||
52 | wmb(); /* setting CMD bit must be last */ | 57 | wmb(); /* setting CMD/STATUS bits must be last */ |
53 | *w0 = *w0 | 1; | 58 | *w0 = *w0 | 0x20001; |
54 | gru_flush_cache(h); | 59 | gru_flush_cache(h); |
55 | } | 60 | } |
56 | 61 | ||
62 | static void report_instruction_timeout(void *h) | ||
63 | { | ||
64 | unsigned long goff = GSEGPOFF((unsigned long)h); | ||
65 | char *id = "???"; | ||
66 | |||
67 | if (TYPE_IS(CCH, goff)) | ||
68 | id = "CCH"; | ||
69 | else if (TYPE_IS(TGH, goff)) | ||
70 | id = "TGH"; | ||
71 | else if (TYPE_IS(TFH, goff)) | ||
72 | id = "TFH"; | ||
73 | |||
74 | panic(KERN_ALERT "GRU %p (%s) is malfunctioning\n", h, id); | ||
75 | } | ||
76 | |||
57 | static int wait_instruction_complete(void *h, enum mcs_op opc) | 77 | static int wait_instruction_complete(void *h, enum mcs_op opc) |
58 | { | 78 | { |
59 | int status; | 79 | int status; |
@@ -64,9 +84,10 @@ static int wait_instruction_complete(void *h, enum mcs_op opc) | |||
64 | status = GET_MSEG_HANDLE_STATUS(h); | 84 | status = GET_MSEG_HANDLE_STATUS(h); |
65 | if (status != CCHSTATUS_ACTIVE) | 85 | if (status != CCHSTATUS_ACTIVE) |
66 | break; | 86 | break; |
67 | if (GRU_OPERATION_TIMEOUT < (get_cycles() - start_time)) | 87 | if (GRU_OPERATION_TIMEOUT < (get_cycles() - start_time)) { |
68 | panic("GRU %p is malfunctioning: start %ld, end %ld\n", | 88 | report_instruction_timeout(h); |
69 | h, start_time, (unsigned long)get_cycles()); | 89 | start_time = get_cycles(); |
90 | } | ||
70 | } | 91 | } |
71 | if (gru_options & OPT_STATS) | 92 | if (gru_options & OPT_STATS) |
72 | update_mcs_stats(opc, get_cycles() - start_time); | 93 | update_mcs_stats(opc, get_cycles() - start_time); |
@@ -75,9 +96,18 @@ static int wait_instruction_complete(void *h, enum mcs_op opc) | |||
75 | 96 | ||
76 | int cch_allocate(struct gru_context_configuration_handle *cch) | 97 | int cch_allocate(struct gru_context_configuration_handle *cch) |
77 | { | 98 | { |
99 | int ret; | ||
100 | |||
78 | cch->opc = CCHOP_ALLOCATE; | 101 | cch->opc = CCHOP_ALLOCATE; |
79 | start_instruction(cch); | 102 | start_instruction(cch); |
80 | return wait_instruction_complete(cch, cchop_allocate); | 103 | ret = wait_instruction_complete(cch, cchop_allocate); |
104 | |||
105 | /* | ||
106 | * Stop speculation into the GSEG being mapped by the previous ALLOCATE. | ||
107 | * The GSEG memory does not exist until the ALLOCATE completes. | ||
108 | */ | ||
109 | sync_core(); | ||
110 | return ret; | ||
81 | } | 111 | } |
82 | 112 | ||
83 | int cch_start(struct gru_context_configuration_handle *cch) | 113 | int cch_start(struct gru_context_configuration_handle *cch) |
@@ -96,9 +126,18 @@ int cch_interrupt(struct gru_context_configuration_handle *cch) | |||
96 | 126 | ||
97 | int cch_deallocate(struct gru_context_configuration_handle *cch) | 127 | int cch_deallocate(struct gru_context_configuration_handle *cch) |
98 | { | 128 | { |
129 | int ret; | ||
130 | |||
99 | cch->opc = CCHOP_DEALLOCATE; | 131 | cch->opc = CCHOP_DEALLOCATE; |
100 | start_instruction(cch); | 132 | start_instruction(cch); |
101 | return wait_instruction_complete(cch, cchop_deallocate); | 133 | ret = wait_instruction_complete(cch, cchop_deallocate); |
134 | |||
135 | /* | ||
136 | * Stop speculation into the GSEG being unmapped by the previous | ||
137 | * DEALLOCATE. | ||
138 | */ | ||
139 | sync_core(); | ||
140 | return ret; | ||
102 | } | 141 | } |
103 | 142 | ||
104 | int cch_interrupt_sync(struct gru_context_configuration_handle | 143 | int cch_interrupt_sync(struct gru_context_configuration_handle |
@@ -126,17 +165,20 @@ int tgh_invalidate(struct gru_tlb_global_handle *tgh, | |||
126 | return wait_instruction_complete(tgh, tghop_invalidate); | 165 | return wait_instruction_complete(tgh, tghop_invalidate); |
127 | } | 166 | } |
128 | 167 | ||
129 | void tfh_write_only(struct gru_tlb_fault_handle *tfh, | 168 | int tfh_write_only(struct gru_tlb_fault_handle *tfh, |
130 | unsigned long pfn, unsigned long vaddr, | 169 | unsigned long paddr, int gaa, |
131 | int asid, int dirty, int pagesize) | 170 | unsigned long vaddr, int asid, int dirty, |
171 | int pagesize) | ||
132 | { | 172 | { |
133 | tfh->fillasid = asid; | 173 | tfh->fillasid = asid; |
134 | tfh->fillvaddr = vaddr; | 174 | tfh->fillvaddr = vaddr; |
135 | tfh->pfn = pfn; | 175 | tfh->pfn = paddr >> GRU_PADDR_SHIFT; |
176 | tfh->gaa = gaa; | ||
136 | tfh->dirty = dirty; | 177 | tfh->dirty = dirty; |
137 | tfh->pagesize = pagesize; | 178 | tfh->pagesize = pagesize; |
138 | tfh->opc = TFHOP_WRITE_ONLY; | 179 | tfh->opc = TFHOP_WRITE_ONLY; |
139 | start_instruction(tfh); | 180 | start_instruction(tfh); |
181 | return wait_instruction_complete(tfh, tfhop_write_only); | ||
140 | } | 182 | } |
141 | 183 | ||
142 | void tfh_write_restart(struct gru_tlb_fault_handle *tfh, | 184 | void tfh_write_restart(struct gru_tlb_fault_handle *tfh, |
diff --git a/drivers/misc/sgi-gru/gruhandles.h b/drivers/misc/sgi-gru/gruhandles.h index f44112242d00..3f998b924d8f 100644 --- a/drivers/misc/sgi-gru/gruhandles.h +++ b/drivers/misc/sgi-gru/gruhandles.h | |||
@@ -91,6 +91,12 @@ | |||
91 | /* Convert an arbitrary handle address to the beginning of the GRU segment */ | 91 | /* Convert an arbitrary handle address to the beginning of the GRU segment */ |
92 | #define GRUBASE(h) ((void *)((unsigned long)(h) & ~(GRU_SIZE - 1))) | 92 | #define GRUBASE(h) ((void *)((unsigned long)(h) & ~(GRU_SIZE - 1))) |
93 | 93 | ||
94 | /* Test a valid handle address to determine the type */ | ||
95 | #define TYPE_IS(hn, h) ((h) >= GRU_##hn##_BASE && (h) < \ | ||
96 | GRU_##hn##_BASE + GRU_NUM_##hn * GRU_HANDLE_STRIDE && \ | ||
97 | (((h) & (GRU_HANDLE_STRIDE - 1)) == 0)) | ||
98 | |||
99 | |||
94 | /* General addressing macros. */ | 100 | /* General addressing macros. */ |
95 | static inline void *get_gseg_base_address(void *base, int ctxnum) | 101 | static inline void *get_gseg_base_address(void *base, int ctxnum) |
96 | { | 102 | { |
@@ -158,6 +164,16 @@ static inline void *gru_chiplet_vaddr(void *vaddr, int pnode, int chiplet) | |||
158 | return vaddr + GRU_SIZE * (2 * pnode + chiplet); | 164 | return vaddr + GRU_SIZE * (2 * pnode + chiplet); |
159 | } | 165 | } |
160 | 166 | ||
167 | static inline struct gru_control_block_extended *gru_tfh_to_cbe( | ||
168 | struct gru_tlb_fault_handle *tfh) | ||
169 | { | ||
170 | unsigned long cbe; | ||
171 | |||
172 | cbe = (unsigned long)tfh - GRU_TFH_BASE + GRU_CBE_BASE; | ||
173 | return (struct gru_control_block_extended*)cbe; | ||
174 | } | ||
175 | |||
176 | |||
161 | 177 | ||
162 | 178 | ||
163 | /* | 179 | /* |
@@ -236,6 +252,17 @@ enum gru_tgh_state { | |||
236 | TGHSTATE_RESTART_CTX, | 252 | TGHSTATE_RESTART_CTX, |
237 | }; | 253 | }; |
238 | 254 | ||
255 | enum gru_tgh_cause { | ||
256 | TGHCAUSE_RR_ECC, | ||
257 | TGHCAUSE_TLB_ECC, | ||
258 | TGHCAUSE_LRU_ECC, | ||
259 | TGHCAUSE_PS_ECC, | ||
260 | TGHCAUSE_MUL_ERR, | ||
261 | TGHCAUSE_DATA_ERR, | ||
262 | TGHCAUSE_SW_FORCE | ||
263 | }; | ||
264 | |||
265 | |||
239 | /* | 266 | /* |
240 | * TFH - TLB Global Handle | 267 | * TFH - TLB Global Handle |
241 | * Used for TLB dropins into the GRU TLB. | 268 | * Used for TLB dropins into the GRU TLB. |
@@ -440,6 +467,12 @@ struct gru_control_block_extended { | |||
440 | unsigned int cbrexecstatus:8; | 467 | unsigned int cbrexecstatus:8; |
441 | }; | 468 | }; |
442 | 469 | ||
470 | /* CBE fields for active BCOPY instructions */ | ||
471 | #define cbe_baddr0 idef1upd | ||
472 | #define cbe_baddr1 idef3upd | ||
473 | #define cbe_src_cl idef6cpy | ||
474 | #define cbe_nelemcur idef5upd | ||
475 | |||
443 | enum gru_cbr_state { | 476 | enum gru_cbr_state { |
444 | CBRSTATE_INACTIVE, | 477 | CBRSTATE_INACTIVE, |
445 | CBRSTATE_IDLE, | 478 | CBRSTATE_IDLE, |
@@ -487,8 +520,8 @@ int cch_interrupt_sync(struct gru_context_configuration_handle *cch); | |||
487 | int tgh_invalidate(struct gru_tlb_global_handle *tgh, unsigned long vaddr, | 520 | int tgh_invalidate(struct gru_tlb_global_handle *tgh, unsigned long vaddr, |
488 | unsigned long vaddrmask, int asid, int pagesize, int global, int n, | 521 | unsigned long vaddrmask, int asid, int pagesize, int global, int n, |
489 | unsigned short ctxbitmap); | 522 | unsigned short ctxbitmap); |
490 | void tfh_write_only(struct gru_tlb_fault_handle *tfh, unsigned long pfn, | 523 | int tfh_write_only(struct gru_tlb_fault_handle *tfh, unsigned long paddr, |
491 | unsigned long vaddr, int asid, int dirty, int pagesize); | 524 | int gaa, unsigned long vaddr, int asid, int dirty, int pagesize); |
492 | void tfh_write_restart(struct gru_tlb_fault_handle *tfh, unsigned long paddr, | 525 | void tfh_write_restart(struct gru_tlb_fault_handle *tfh, unsigned long paddr, |
493 | int gaa, unsigned long vaddr, int asid, int dirty, int pagesize); | 526 | int gaa, unsigned long vaddr, int asid, int dirty, int pagesize); |
494 | void tfh_restart(struct gru_tlb_fault_handle *tfh); | 527 | void tfh_restart(struct gru_tlb_fault_handle *tfh); |
diff --git a/drivers/misc/sgi-gru/grukdump.c b/drivers/misc/sgi-gru/grukdump.c index 55eabfa85585..9b2062d17327 100644 --- a/drivers/misc/sgi-gru/grukdump.c +++ b/drivers/misc/sgi-gru/grukdump.c | |||
@@ -44,7 +44,8 @@ static int gru_user_copy_handle(void __user **dp, void *s) | |||
44 | 44 | ||
45 | static int gru_dump_context_data(void *grubase, | 45 | static int gru_dump_context_data(void *grubase, |
46 | struct gru_context_configuration_handle *cch, | 46 | struct gru_context_configuration_handle *cch, |
47 | void __user *ubuf, int ctxnum, int dsrcnt) | 47 | void __user *ubuf, int ctxnum, int dsrcnt, |
48 | int flush_cbrs) | ||
48 | { | 49 | { |
49 | void *cb, *cbe, *tfh, *gseg; | 50 | void *cb, *cbe, *tfh, *gseg; |
50 | int i, scr; | 51 | int i, scr; |
@@ -55,6 +56,8 @@ static int gru_dump_context_data(void *grubase, | |||
55 | tfh = grubase + GRU_TFH_BASE; | 56 | tfh = grubase + GRU_TFH_BASE; |
56 | 57 | ||
57 | for_each_cbr_in_allocation_map(i, &cch->cbr_allocation_map, scr) { | 58 | for_each_cbr_in_allocation_map(i, &cch->cbr_allocation_map, scr) { |
59 | if (flush_cbrs) | ||
60 | gru_flush_cache(cb); | ||
58 | if (gru_user_copy_handle(&ubuf, cb)) | 61 | if (gru_user_copy_handle(&ubuf, cb)) |
59 | goto fail; | 62 | goto fail; |
60 | if (gru_user_copy_handle(&ubuf, tfh + i * GRU_HANDLE_STRIDE)) | 63 | if (gru_user_copy_handle(&ubuf, tfh + i * GRU_HANDLE_STRIDE)) |
@@ -115,7 +118,7 @@ fail: | |||
115 | 118 | ||
116 | static int gru_dump_context(struct gru_state *gru, int ctxnum, | 119 | static int gru_dump_context(struct gru_state *gru, int ctxnum, |
117 | void __user *ubuf, void __user *ubufend, char data_opt, | 120 | void __user *ubuf, void __user *ubufend, char data_opt, |
118 | char lock_cch) | 121 | char lock_cch, char flush_cbrs) |
119 | { | 122 | { |
120 | struct gru_dump_context_header hdr; | 123 | struct gru_dump_context_header hdr; |
121 | struct gru_dump_context_header __user *uhdr = ubuf; | 124 | struct gru_dump_context_header __user *uhdr = ubuf; |
@@ -159,8 +162,7 @@ static int gru_dump_context(struct gru_state *gru, int ctxnum, | |||
159 | ret = -EFBIG; | 162 | ret = -EFBIG; |
160 | else | 163 | else |
161 | ret = gru_dump_context_data(grubase, cch, ubuf, ctxnum, | 164 | ret = gru_dump_context_data(grubase, cch, ubuf, ctxnum, |
162 | dsrcnt); | 165 | dsrcnt, flush_cbrs); |
163 | |||
164 | } | 166 | } |
165 | if (cch_locked) | 167 | if (cch_locked) |
166 | unlock_cch_handle(cch); | 168 | unlock_cch_handle(cch); |
@@ -215,7 +217,8 @@ int gru_dump_chiplet_request(unsigned long arg) | |||
215 | for (ctxnum = 0; ctxnum < GRU_NUM_CCH; ctxnum++) { | 217 | for (ctxnum = 0; ctxnum < GRU_NUM_CCH; ctxnum++) { |
216 | if (req.ctxnum == ctxnum || req.ctxnum < 0) { | 218 | if (req.ctxnum == ctxnum || req.ctxnum < 0) { |
217 | ret = gru_dump_context(gru, ctxnum, ubuf, ubufend, | 219 | ret = gru_dump_context(gru, ctxnum, ubuf, ubufend, |
218 | req.data_opt, req.lock_cch); | 220 | req.data_opt, req.lock_cch, |
221 | req.flush_cbrs); | ||
219 | if (ret < 0) | 222 | if (ret < 0) |
220 | goto fail; | 223 | goto fail; |
221 | ubuf += ret; | 224 | ubuf += ret; |
diff --git a/drivers/misc/sgi-gru/grukservices.c b/drivers/misc/sgi-gru/grukservices.c index 766e21e15574..34749ee88dfa 100644 --- a/drivers/misc/sgi-gru/grukservices.c +++ b/drivers/misc/sgi-gru/grukservices.c | |||
@@ -31,6 +31,7 @@ | |||
31 | #include <linux/interrupt.h> | 31 | #include <linux/interrupt.h> |
32 | #include <linux/uaccess.h> | 32 | #include <linux/uaccess.h> |
33 | #include <linux/delay.h> | 33 | #include <linux/delay.h> |
34 | #include <asm/io_apic.h> | ||
34 | #include "gru.h" | 35 | #include "gru.h" |
35 | #include "grulib.h" | 36 | #include "grulib.h" |
36 | #include "grutables.h" | 37 | #include "grutables.h" |
@@ -97,9 +98,6 @@ | |||
97 | #define ASYNC_HAN_TO_BID(h) ((h) - 1) | 98 | #define ASYNC_HAN_TO_BID(h) ((h) - 1) |
98 | #define ASYNC_BID_TO_HAN(b) ((b) + 1) | 99 | #define ASYNC_BID_TO_HAN(b) ((b) + 1) |
99 | #define ASYNC_HAN_TO_BS(h) gru_base[ASYNC_HAN_TO_BID(h)] | 100 | #define ASYNC_HAN_TO_BS(h) gru_base[ASYNC_HAN_TO_BID(h)] |
100 | #define KCB_TO_GID(cb) ((cb - gru_start_vaddr) / \ | ||
101 | (GRU_SIZE * GRU_CHIPLETS_PER_BLADE)) | ||
102 | #define KCB_TO_BS(cb) gru_base[KCB_TO_GID(cb)] | ||
103 | 101 | ||
104 | #define GRU_NUM_KERNEL_CBR 1 | 102 | #define GRU_NUM_KERNEL_CBR 1 |
105 | #define GRU_NUM_KERNEL_DSR_BYTES 256 | 103 | #define GRU_NUM_KERNEL_DSR_BYTES 256 |
@@ -160,8 +158,10 @@ static void gru_load_kernel_context(struct gru_blade_state *bs, int blade_id) | |||
160 | up_read(&bs->bs_kgts_sema); | 158 | up_read(&bs->bs_kgts_sema); |
161 | down_write(&bs->bs_kgts_sema); | 159 | down_write(&bs->bs_kgts_sema); |
162 | 160 | ||
163 | if (!bs->bs_kgts) | 161 | if (!bs->bs_kgts) { |
164 | bs->bs_kgts = gru_alloc_gts(NULL, 0, 0, 0, 0); | 162 | bs->bs_kgts = gru_alloc_gts(NULL, 0, 0, 0, 0, 0); |
163 | bs->bs_kgts->ts_user_blade_id = blade_id; | ||
164 | } | ||
165 | kgts = bs->bs_kgts; | 165 | kgts = bs->bs_kgts; |
166 | 166 | ||
167 | if (!kgts->ts_gru) { | 167 | if (!kgts->ts_gru) { |
@@ -172,9 +172,9 @@ static void gru_load_kernel_context(struct gru_blade_state *bs, int blade_id) | |||
172 | kgts->ts_dsr_au_count = GRU_DS_BYTES_TO_AU( | 172 | kgts->ts_dsr_au_count = GRU_DS_BYTES_TO_AU( |
173 | GRU_NUM_KERNEL_DSR_BYTES * ncpus + | 173 | GRU_NUM_KERNEL_DSR_BYTES * ncpus + |
174 | bs->bs_async_dsr_bytes); | 174 | bs->bs_async_dsr_bytes); |
175 | while (!gru_assign_gru_context(kgts, blade_id)) { | 175 | while (!gru_assign_gru_context(kgts)) { |
176 | msleep(1); | 176 | msleep(1); |
177 | gru_steal_context(kgts, blade_id); | 177 | gru_steal_context(kgts); |
178 | } | 178 | } |
179 | gru_load_context(kgts); | 179 | gru_load_context(kgts); |
180 | gru = bs->bs_kgts->ts_gru; | 180 | gru = bs->bs_kgts->ts_gru; |
@@ -200,13 +200,15 @@ static int gru_free_kernel_contexts(void) | |||
200 | bs = gru_base[bid]; | 200 | bs = gru_base[bid]; |
201 | if (!bs) | 201 | if (!bs) |
202 | continue; | 202 | continue; |
203 | |||
204 | /* Ignore busy contexts. Don't want to block here. */ | ||
203 | if (down_write_trylock(&bs->bs_kgts_sema)) { | 205 | if (down_write_trylock(&bs->bs_kgts_sema)) { |
204 | kgts = bs->bs_kgts; | 206 | kgts = bs->bs_kgts; |
205 | if (kgts && kgts->ts_gru) | 207 | if (kgts && kgts->ts_gru) |
206 | gru_unload_context(kgts, 0); | 208 | gru_unload_context(kgts, 0); |
207 | kfree(kgts); | ||
208 | bs->bs_kgts = NULL; | 209 | bs->bs_kgts = NULL; |
209 | up_write(&bs->bs_kgts_sema); | 210 | up_write(&bs->bs_kgts_sema); |
211 | kfree(kgts); | ||
210 | } else { | 212 | } else { |
211 | ret++; | 213 | ret++; |
212 | } | 214 | } |
@@ -220,13 +222,21 @@ static int gru_free_kernel_contexts(void) | |||
220 | static struct gru_blade_state *gru_lock_kernel_context(int blade_id) | 222 | static struct gru_blade_state *gru_lock_kernel_context(int blade_id) |
221 | { | 223 | { |
222 | struct gru_blade_state *bs; | 224 | struct gru_blade_state *bs; |
225 | int bid; | ||
223 | 226 | ||
224 | STAT(lock_kernel_context); | 227 | STAT(lock_kernel_context); |
225 | bs = gru_base[blade_id]; | 228 | again: |
229 | bid = blade_id < 0 ? uv_numa_blade_id() : blade_id; | ||
230 | bs = gru_base[bid]; | ||
226 | 231 | ||
232 | /* Handle the case where migration occured while waiting for the sema */ | ||
227 | down_read(&bs->bs_kgts_sema); | 233 | down_read(&bs->bs_kgts_sema); |
234 | if (blade_id < 0 && bid != uv_numa_blade_id()) { | ||
235 | up_read(&bs->bs_kgts_sema); | ||
236 | goto again; | ||
237 | } | ||
228 | if (!bs->bs_kgts || !bs->bs_kgts->ts_gru) | 238 | if (!bs->bs_kgts || !bs->bs_kgts->ts_gru) |
229 | gru_load_kernel_context(bs, blade_id); | 239 | gru_load_kernel_context(bs, bid); |
230 | return bs; | 240 | return bs; |
231 | 241 | ||
232 | } | 242 | } |
@@ -255,7 +265,7 @@ static int gru_get_cpu_resources(int dsr_bytes, void **cb, void **dsr) | |||
255 | 265 | ||
256 | BUG_ON(dsr_bytes > GRU_NUM_KERNEL_DSR_BYTES); | 266 | BUG_ON(dsr_bytes > GRU_NUM_KERNEL_DSR_BYTES); |
257 | preempt_disable(); | 267 | preempt_disable(); |
258 | bs = gru_lock_kernel_context(uv_numa_blade_id()); | 268 | bs = gru_lock_kernel_context(-1); |
259 | lcpu = uv_blade_processor_id(); | 269 | lcpu = uv_blade_processor_id(); |
260 | *cb = bs->kernel_cb + lcpu * GRU_HANDLE_STRIDE; | 270 | *cb = bs->kernel_cb + lcpu * GRU_HANDLE_STRIDE; |
261 | *dsr = bs->kernel_dsr + lcpu * GRU_NUM_KERNEL_DSR_BYTES; | 271 | *dsr = bs->kernel_dsr + lcpu * GRU_NUM_KERNEL_DSR_BYTES; |
@@ -384,13 +394,31 @@ int gru_get_cb_exception_detail(void *cb, | |||
384 | struct control_block_extended_exc_detail *excdet) | 394 | struct control_block_extended_exc_detail *excdet) |
385 | { | 395 | { |
386 | struct gru_control_block_extended *cbe; | 396 | struct gru_control_block_extended *cbe; |
387 | struct gru_blade_state *bs; | 397 | struct gru_thread_state *kgts = NULL; |
388 | int cbrnum; | 398 | unsigned long off; |
389 | 399 | int cbrnum, bid; | |
390 | bs = KCB_TO_BS(cb); | 400 | |
391 | cbrnum = thread_cbr_number(bs->bs_kgts, get_cb_number(cb)); | 401 | /* |
402 | * Locate kgts for cb. This algorithm is SLOW but | ||
403 | * this function is rarely called (ie., almost never). | ||
404 | * Performance does not matter. | ||
405 | */ | ||
406 | for_each_possible_blade(bid) { | ||
407 | if (!gru_base[bid]) | ||
408 | break; | ||
409 | kgts = gru_base[bid]->bs_kgts; | ||
410 | if (!kgts || !kgts->ts_gru) | ||
411 | continue; | ||
412 | off = cb - kgts->ts_gru->gs_gru_base_vaddr; | ||
413 | if (off < GRU_SIZE) | ||
414 | break; | ||
415 | kgts = NULL; | ||
416 | } | ||
417 | BUG_ON(!kgts); | ||
418 | cbrnum = thread_cbr_number(kgts, get_cb_number(cb)); | ||
392 | cbe = get_cbe(GRUBASE(cb), cbrnum); | 419 | cbe = get_cbe(GRUBASE(cb), cbrnum); |
393 | gru_flush_cache(cbe); /* CBE not coherent */ | 420 | gru_flush_cache(cbe); /* CBE not coherent */ |
421 | sync_core(); | ||
394 | excdet->opc = cbe->opccpy; | 422 | excdet->opc = cbe->opccpy; |
395 | excdet->exopc = cbe->exopccpy; | 423 | excdet->exopc = cbe->exopccpy; |
396 | excdet->ecause = cbe->ecause; | 424 | excdet->ecause = cbe->ecause; |
@@ -409,8 +437,8 @@ char *gru_get_cb_exception_detail_str(int ret, void *cb, | |||
409 | if (ret > 0 && gen->istatus == CBS_EXCEPTION) { | 437 | if (ret > 0 && gen->istatus == CBS_EXCEPTION) { |
410 | gru_get_cb_exception_detail(cb, &excdet); | 438 | gru_get_cb_exception_detail(cb, &excdet); |
411 | snprintf(buf, size, | 439 | snprintf(buf, size, |
412 | "GRU exception: cb %p, opc %d, exopc %d, ecause 0x%x," | 440 | "GRU:%d exception: cb %p, opc %d, exopc %d, ecause 0x%x," |
413 | "excdet0 0x%lx, excdet1 0x%x", | 441 | "excdet0 0x%lx, excdet1 0x%x", smp_processor_id(), |
414 | gen, excdet.opc, excdet.exopc, excdet.ecause, | 442 | gen, excdet.opc, excdet.exopc, excdet.ecause, |
415 | excdet.exceptdet0, excdet.exceptdet1); | 443 | excdet.exceptdet0, excdet.exceptdet1); |
416 | } else { | 444 | } else { |
@@ -457,9 +485,10 @@ int gru_check_status_proc(void *cb) | |||
457 | int ret; | 485 | int ret; |
458 | 486 | ||
459 | ret = gen->istatus; | 487 | ret = gen->istatus; |
460 | if (ret != CBS_EXCEPTION) | 488 | if (ret == CBS_EXCEPTION) |
461 | return ret; | 489 | ret = gru_retry_exception(cb); |
462 | return gru_retry_exception(cb); | 490 | rmb(); |
491 | return ret; | ||
463 | 492 | ||
464 | } | 493 | } |
465 | 494 | ||
@@ -471,7 +500,7 @@ int gru_wait_proc(void *cb) | |||
471 | ret = gru_wait_idle_or_exception(gen); | 500 | ret = gru_wait_idle_or_exception(gen); |
472 | if (ret == CBS_EXCEPTION) | 501 | if (ret == CBS_EXCEPTION) |
473 | ret = gru_retry_exception(cb); | 502 | ret = gru_retry_exception(cb); |
474 | 503 | rmb(); | |
475 | return ret; | 504 | return ret; |
476 | } | 505 | } |
477 | 506 | ||
@@ -538,7 +567,7 @@ int gru_create_message_queue(struct gru_message_queue_desc *mqd, | |||
538 | mqd->mq = mq; | 567 | mqd->mq = mq; |
539 | mqd->mq_gpa = uv_gpa(mq); | 568 | mqd->mq_gpa = uv_gpa(mq); |
540 | mqd->qlines = qlines; | 569 | mqd->qlines = qlines; |
541 | mqd->interrupt_pnode = UV_NASID_TO_PNODE(nasid); | 570 | mqd->interrupt_pnode = nasid >> 1; |
542 | mqd->interrupt_vector = vector; | 571 | mqd->interrupt_vector = vector; |
543 | mqd->interrupt_apicid = apicid; | 572 | mqd->interrupt_apicid = apicid; |
544 | return 0; | 573 | return 0; |
@@ -598,6 +627,8 @@ static int send_noop_message(void *cb, struct gru_message_queue_desc *mqd, | |||
598 | ret = MQE_UNEXPECTED_CB_ERR; | 627 | ret = MQE_UNEXPECTED_CB_ERR; |
599 | break; | 628 | break; |
600 | case CBSS_PAGE_OVERFLOW: | 629 | case CBSS_PAGE_OVERFLOW: |
630 | STAT(mesq_noop_page_overflow); | ||
631 | /* fallthru */ | ||
601 | default: | 632 | default: |
602 | BUG(); | 633 | BUG(); |
603 | } | 634 | } |
@@ -673,18 +704,6 @@ cberr: | |||
673 | } | 704 | } |
674 | 705 | ||
675 | /* | 706 | /* |
676 | * Send a cross-partition interrupt to the SSI that contains the target | ||
677 | * message queue. Normally, the interrupt is automatically delivered by hardware | ||
678 | * but some error conditions require explicit delivery. | ||
679 | */ | ||
680 | static void send_message_queue_interrupt(struct gru_message_queue_desc *mqd) | ||
681 | { | ||
682 | if (mqd->interrupt_vector) | ||
683 | uv_hub_send_ipi(mqd->interrupt_pnode, mqd->interrupt_apicid, | ||
684 | mqd->interrupt_vector); | ||
685 | } | ||
686 | |||
687 | /* | ||
688 | * Handle a PUT failure. Note: if message was a 2-line message, one of the | 707 | * Handle a PUT failure. Note: if message was a 2-line message, one of the |
689 | * lines might have successfully have been written. Before sending the | 708 | * lines might have successfully have been written. Before sending the |
690 | * message, "present" must be cleared in BOTH lines to prevent the receiver | 709 | * message, "present" must be cleared in BOTH lines to prevent the receiver |
@@ -693,7 +712,8 @@ static void send_message_queue_interrupt(struct gru_message_queue_desc *mqd) | |||
693 | static int send_message_put_nacked(void *cb, struct gru_message_queue_desc *mqd, | 712 | static int send_message_put_nacked(void *cb, struct gru_message_queue_desc *mqd, |
694 | void *mesg, int lines) | 713 | void *mesg, int lines) |
695 | { | 714 | { |
696 | unsigned long m; | 715 | unsigned long m, *val = mesg, gpa, save; |
716 | int ret; | ||
697 | 717 | ||
698 | m = mqd->mq_gpa + (gru_get_amo_value_head(cb) << 6); | 718 | m = mqd->mq_gpa + (gru_get_amo_value_head(cb) << 6); |
699 | if (lines == 2) { | 719 | if (lines == 2) { |
@@ -704,7 +724,26 @@ static int send_message_put_nacked(void *cb, struct gru_message_queue_desc *mqd, | |||
704 | gru_vstore(cb, m, gru_get_tri(mesg), XTYPE_CL, lines, 1, IMA); | 724 | gru_vstore(cb, m, gru_get_tri(mesg), XTYPE_CL, lines, 1, IMA); |
705 | if (gru_wait(cb) != CBS_IDLE) | 725 | if (gru_wait(cb) != CBS_IDLE) |
706 | return MQE_UNEXPECTED_CB_ERR; | 726 | return MQE_UNEXPECTED_CB_ERR; |
707 | send_message_queue_interrupt(mqd); | 727 | |
728 | if (!mqd->interrupt_vector) | ||
729 | return MQE_OK; | ||
730 | |||
731 | /* | ||
732 | * Send a cross-partition interrupt to the SSI that contains the target | ||
733 | * message queue. Normally, the interrupt is automatically delivered by | ||
734 | * hardware but some error conditions require explicit delivery. | ||
735 | * Use the GRU to deliver the interrupt. Otherwise partition failures | ||
736 | * could cause unrecovered errors. | ||
737 | */ | ||
738 | gpa = uv_global_gru_mmr_address(mqd->interrupt_pnode, UVH_IPI_INT); | ||
739 | save = *val; | ||
740 | *val = uv_hub_ipi_value(mqd->interrupt_apicid, mqd->interrupt_vector, | ||
741 | dest_Fixed); | ||
742 | gru_vstore_phys(cb, gpa, gru_get_tri(mesg), IAA_REGISTER, IMA); | ||
743 | ret = gru_wait(cb); | ||
744 | *val = save; | ||
745 | if (ret != CBS_IDLE) | ||
746 | return MQE_UNEXPECTED_CB_ERR; | ||
708 | return MQE_OK; | 747 | return MQE_OK; |
709 | } | 748 | } |
710 | 749 | ||
@@ -739,6 +778,9 @@ static int send_message_failure(void *cb, struct gru_message_queue_desc *mqd, | |||
739 | STAT(mesq_send_put_nacked); | 778 | STAT(mesq_send_put_nacked); |
740 | ret = send_message_put_nacked(cb, mqd, mesg, lines); | 779 | ret = send_message_put_nacked(cb, mqd, mesg, lines); |
741 | break; | 780 | break; |
781 | case CBSS_PAGE_OVERFLOW: | ||
782 | STAT(mesq_page_overflow); | ||
783 | /* fallthru */ | ||
742 | default: | 784 | default: |
743 | BUG(); | 785 | BUG(); |
744 | } | 786 | } |
@@ -831,7 +873,6 @@ void *gru_get_next_message(struct gru_message_queue_desc *mqd) | |||
831 | int present = mhdr->present; | 873 | int present = mhdr->present; |
832 | 874 | ||
833 | /* skip NOOP messages */ | 875 | /* skip NOOP messages */ |
834 | STAT(mesq_receive); | ||
835 | while (present == MQS_NOOP) { | 876 | while (present == MQS_NOOP) { |
836 | gru_free_message(mqd, mhdr); | 877 | gru_free_message(mqd, mhdr); |
837 | mhdr = mq->next; | 878 | mhdr = mq->next; |
@@ -851,6 +892,7 @@ void *gru_get_next_message(struct gru_message_queue_desc *mqd) | |||
851 | if (mhdr->lines == 2) | 892 | if (mhdr->lines == 2) |
852 | restore_present2(mhdr, mhdr->present2); | 893 | restore_present2(mhdr, mhdr->present2); |
853 | 894 | ||
895 | STAT(mesq_receive); | ||
854 | return mhdr; | 896 | return mhdr; |
855 | } | 897 | } |
856 | EXPORT_SYMBOL_GPL(gru_get_next_message); | 898 | EXPORT_SYMBOL_GPL(gru_get_next_message); |
@@ -858,6 +900,29 @@ EXPORT_SYMBOL_GPL(gru_get_next_message); | |||
858 | /* ---------------------- GRU DATA COPY FUNCTIONS ---------------------------*/ | 900 | /* ---------------------- GRU DATA COPY FUNCTIONS ---------------------------*/ |
859 | 901 | ||
860 | /* | 902 | /* |
903 | * Load a DW from a global GPA. The GPA can be a memory or MMR address. | ||
904 | */ | ||
905 | int gru_read_gpa(unsigned long *value, unsigned long gpa) | ||
906 | { | ||
907 | void *cb; | ||
908 | void *dsr; | ||
909 | int ret, iaa; | ||
910 | |||
911 | STAT(read_gpa); | ||
912 | if (gru_get_cpu_resources(GRU_NUM_KERNEL_DSR_BYTES, &cb, &dsr)) | ||
913 | return MQE_BUG_NO_RESOURCES; | ||
914 | iaa = gpa >> 62; | ||
915 | gru_vload_phys(cb, gpa, gru_get_tri(dsr), iaa, IMA); | ||
916 | ret = gru_wait(cb); | ||
917 | if (ret == CBS_IDLE) | ||
918 | *value = *(unsigned long *)dsr; | ||
919 | gru_free_cpu_resources(cb, dsr); | ||
920 | return ret; | ||
921 | } | ||
922 | EXPORT_SYMBOL_GPL(gru_read_gpa); | ||
923 | |||
924 | |||
925 | /* | ||
861 | * Copy a block of data using the GRU resources | 926 | * Copy a block of data using the GRU resources |
862 | */ | 927 | */ |
863 | int gru_copy_gpa(unsigned long dest_gpa, unsigned long src_gpa, | 928 | int gru_copy_gpa(unsigned long dest_gpa, unsigned long src_gpa, |
@@ -898,24 +963,24 @@ static int quicktest0(unsigned long arg) | |||
898 | 963 | ||
899 | gru_vload(cb, uv_gpa(&word0), gru_get_tri(dsr), XTYPE_DW, 1, 1, IMA); | 964 | gru_vload(cb, uv_gpa(&word0), gru_get_tri(dsr), XTYPE_DW, 1, 1, IMA); |
900 | if (gru_wait(cb) != CBS_IDLE) { | 965 | if (gru_wait(cb) != CBS_IDLE) { |
901 | printk(KERN_DEBUG "GRU quicktest0: CBR failure 1\n"); | 966 | printk(KERN_DEBUG "GRU:%d quicktest0: CBR failure 1\n", smp_processor_id()); |
902 | goto done; | 967 | goto done; |
903 | } | 968 | } |
904 | 969 | ||
905 | if (*p != MAGIC) { | 970 | if (*p != MAGIC) { |
906 | printk(KERN_DEBUG "GRU: quicktest0 bad magic 0x%lx\n", *p); | 971 | printk(KERN_DEBUG "GRU:%d quicktest0 bad magic 0x%lx\n", smp_processor_id(), *p); |
907 | goto done; | 972 | goto done; |
908 | } | 973 | } |
909 | gru_vstore(cb, uv_gpa(&word1), gru_get_tri(dsr), XTYPE_DW, 1, 1, IMA); | 974 | gru_vstore(cb, uv_gpa(&word1), gru_get_tri(dsr), XTYPE_DW, 1, 1, IMA); |
910 | if (gru_wait(cb) != CBS_IDLE) { | 975 | if (gru_wait(cb) != CBS_IDLE) { |
911 | printk(KERN_DEBUG "GRU quicktest0: CBR failure 2\n"); | 976 | printk(KERN_DEBUG "GRU:%d quicktest0: CBR failure 2\n", smp_processor_id()); |
912 | goto done; | 977 | goto done; |
913 | } | 978 | } |
914 | 979 | ||
915 | if (word0 != word1 || word1 != MAGIC) { | 980 | if (word0 != word1 || word1 != MAGIC) { |
916 | printk(KERN_DEBUG | 981 | printk(KERN_DEBUG |
917 | "GRU quicktest0 err: found 0x%lx, expected 0x%lx\n", | 982 | "GRU:%d quicktest0 err: found 0x%lx, expected 0x%lx\n", |
918 | word1, MAGIC); | 983 | smp_processor_id(), word1, MAGIC); |
919 | goto done; | 984 | goto done; |
920 | } | 985 | } |
921 | ret = 0; | 986 | ret = 0; |
@@ -952,8 +1017,11 @@ static int quicktest1(unsigned long arg) | |||
952 | if (ret) | 1017 | if (ret) |
953 | break; | 1018 | break; |
954 | } | 1019 | } |
955 | if (ret != MQE_QUEUE_FULL || i != 4) | 1020 | if (ret != MQE_QUEUE_FULL || i != 4) { |
1021 | printk(KERN_DEBUG "GRU:%d quicktest1: unexpect status %d, i %d\n", | ||
1022 | smp_processor_id(), ret, i); | ||
956 | goto done; | 1023 | goto done; |
1024 | } | ||
957 | 1025 | ||
958 | for (i = 0; i < 6; i++) { | 1026 | for (i = 0; i < 6; i++) { |
959 | m = gru_get_next_message(&mqd); | 1027 | m = gru_get_next_message(&mqd); |
@@ -961,7 +1029,12 @@ static int quicktest1(unsigned long arg) | |||
961 | break; | 1029 | break; |
962 | gru_free_message(&mqd, m); | 1030 | gru_free_message(&mqd, m); |
963 | } | 1031 | } |
964 | ret = (i == 4) ? 0 : -EIO; | 1032 | if (i != 4) { |
1033 | printk(KERN_DEBUG "GRU:%d quicktest2: bad message, i %d, m %p, m8 %d\n", | ||
1034 | smp_processor_id(), i, m, m ? m[8] : -1); | ||
1035 | goto done; | ||
1036 | } | ||
1037 | ret = 0; | ||
965 | 1038 | ||
966 | done: | 1039 | done: |
967 | kfree(p); | 1040 | kfree(p); |
@@ -977,6 +1050,7 @@ static int quicktest2(unsigned long arg) | |||
977 | int ret = 0; | 1050 | int ret = 0; |
978 | unsigned long *buf; | 1051 | unsigned long *buf; |
979 | void *cb0, *cb; | 1052 | void *cb0, *cb; |
1053 | struct gru_control_block_status *gen; | ||
980 | int i, k, istatus, bytes; | 1054 | int i, k, istatus, bytes; |
981 | 1055 | ||
982 | bytes = numcb * 4 * 8; | 1056 | bytes = numcb * 4 * 8; |
@@ -996,20 +1070,30 @@ static int quicktest2(unsigned long arg) | |||
996 | XTYPE_DW, 4, 1, IMA_INTERRUPT); | 1070 | XTYPE_DW, 4, 1, IMA_INTERRUPT); |
997 | 1071 | ||
998 | ret = 0; | 1072 | ret = 0; |
999 | for (k = 0; k < numcb; k++) { | 1073 | k = numcb; |
1074 | do { | ||
1000 | gru_wait_async_cbr(han); | 1075 | gru_wait_async_cbr(han); |
1001 | for (i = 0; i < numcb; i++) { | 1076 | for (i = 0; i < numcb; i++) { |
1002 | cb = cb0 + i * GRU_HANDLE_STRIDE; | 1077 | cb = cb0 + i * GRU_HANDLE_STRIDE; |
1003 | istatus = gru_check_status(cb); | 1078 | istatus = gru_check_status(cb); |
1004 | if (istatus == CBS_ACTIVE) | 1079 | if (istatus != CBS_ACTIVE && istatus != CBS_CALL_OS) |
1005 | continue; | 1080 | break; |
1006 | if (istatus == CBS_EXCEPTION) | ||
1007 | ret = -EFAULT; | ||
1008 | else if (buf[i] || buf[i + 1] || buf[i + 2] || | ||
1009 | buf[i + 3]) | ||
1010 | ret = -EIO; | ||
1011 | } | 1081 | } |
1012 | } | 1082 | if (i == numcb) |
1083 | continue; | ||
1084 | if (istatus != CBS_IDLE) { | ||
1085 | printk(KERN_DEBUG "GRU:%d quicktest2: cb %d, exception\n", smp_processor_id(), i); | ||
1086 | ret = -EFAULT; | ||
1087 | } else if (buf[4 * i] || buf[4 * i + 1] || buf[4 * i + 2] || | ||
1088 | buf[4 * i + 3]) { | ||
1089 | printk(KERN_DEBUG "GRU:%d quicktest2:cb %d, buf 0x%lx, 0x%lx, 0x%lx, 0x%lx\n", | ||
1090 | smp_processor_id(), i, buf[4 * i], buf[4 * i + 1], buf[4 * i + 2], buf[4 * i + 3]); | ||
1091 | ret = -EIO; | ||
1092 | } | ||
1093 | k--; | ||
1094 | gen = cb; | ||
1095 | gen->istatus = CBS_CALL_OS; /* don't handle this CBR again */ | ||
1096 | } while (k); | ||
1013 | BUG_ON(cmp.done); | 1097 | BUG_ON(cmp.done); |
1014 | 1098 | ||
1015 | gru_unlock_async_resource(han); | 1099 | gru_unlock_async_resource(han); |
@@ -1019,6 +1103,22 @@ done: | |||
1019 | return ret; | 1103 | return ret; |
1020 | } | 1104 | } |
1021 | 1105 | ||
1106 | #define BUFSIZE 200 | ||
1107 | static int quicktest3(unsigned long arg) | ||
1108 | { | ||
1109 | char buf1[BUFSIZE], buf2[BUFSIZE]; | ||
1110 | int ret = 0; | ||
1111 | |||
1112 | memset(buf2, 0, sizeof(buf2)); | ||
1113 | memset(buf1, get_cycles() & 255, sizeof(buf1)); | ||
1114 | gru_copy_gpa(uv_gpa(buf2), uv_gpa(buf1), BUFSIZE); | ||
1115 | if (memcmp(buf1, buf2, BUFSIZE)) { | ||
1116 | printk(KERN_DEBUG "GRU:%d quicktest3 error\n", smp_processor_id()); | ||
1117 | ret = -EIO; | ||
1118 | } | ||
1119 | return ret; | ||
1120 | } | ||
1121 | |||
1022 | /* | 1122 | /* |
1023 | * Debugging only. User hook for various kernel tests | 1123 | * Debugging only. User hook for various kernel tests |
1024 | * of driver & gru. | 1124 | * of driver & gru. |
@@ -1037,6 +1137,9 @@ int gru_ktest(unsigned long arg) | |||
1037 | case 2: | 1137 | case 2: |
1038 | ret = quicktest2(arg); | 1138 | ret = quicktest2(arg); |
1039 | break; | 1139 | break; |
1140 | case 3: | ||
1141 | ret = quicktest3(arg); | ||
1142 | break; | ||
1040 | case 99: | 1143 | case 99: |
1041 | ret = gru_free_kernel_contexts(); | 1144 | ret = gru_free_kernel_contexts(); |
1042 | break; | 1145 | break; |
diff --git a/drivers/misc/sgi-gru/grukservices.h b/drivers/misc/sgi-gru/grukservices.h index d60d34bca44d..02aa94d8484a 100644 --- a/drivers/misc/sgi-gru/grukservices.h +++ b/drivers/misc/sgi-gru/grukservices.h | |||
@@ -131,6 +131,20 @@ extern void *gru_get_next_message(struct gru_message_queue_desc *mqd); | |||
131 | 131 | ||
132 | 132 | ||
133 | /* | 133 | /* |
134 | * Read a GRU global GPA. Source can be located in a remote partition. | ||
135 | * | ||
136 | * Input: | ||
137 | * value memory address where MMR value is returned | ||
138 | * gpa source numalink physical address of GPA | ||
139 | * | ||
140 | * Output: | ||
141 | * 0 OK | ||
142 | * >0 error | ||
143 | */ | ||
144 | int gru_read_gpa(unsigned long *value, unsigned long gpa); | ||
145 | |||
146 | |||
147 | /* | ||
134 | * Copy data using the GRU. Source or destination can be located in a remote | 148 | * Copy data using the GRU. Source or destination can be located in a remote |
135 | * partition. | 149 | * partition. |
136 | * | 150 | * |
diff --git a/drivers/misc/sgi-gru/grulib.h b/drivers/misc/sgi-gru/grulib.h index 889bc442a3e8..e77d1b1f9d05 100644 --- a/drivers/misc/sgi-gru/grulib.h +++ b/drivers/misc/sgi-gru/grulib.h | |||
@@ -63,18 +63,9 @@ | |||
63 | #define THREAD_POINTER(p, th) (p + GRU_GSEG_PAGESIZE * (th)) | 63 | #define THREAD_POINTER(p, th) (p + GRU_GSEG_PAGESIZE * (th)) |
64 | #define GSEG_START(cb) ((void *)((unsigned long)(cb) & ~(GRU_GSEG_PAGESIZE - 1))) | 64 | #define GSEG_START(cb) ((void *)((unsigned long)(cb) & ~(GRU_GSEG_PAGESIZE - 1))) |
65 | 65 | ||
66 | /* | ||
67 | * Statictics kept on a per-GTS basis. | ||
68 | */ | ||
69 | struct gts_statistics { | ||
70 | unsigned long fmm_tlbdropin; | ||
71 | unsigned long upm_tlbdropin; | ||
72 | unsigned long context_stolen; | ||
73 | }; | ||
74 | |||
75 | struct gru_get_gseg_statistics_req { | 66 | struct gru_get_gseg_statistics_req { |
76 | unsigned long gseg; | 67 | unsigned long gseg; |
77 | struct gts_statistics stats; | 68 | struct gru_gseg_statistics stats; |
78 | }; | 69 | }; |
79 | 70 | ||
80 | /* | 71 | /* |
@@ -86,6 +77,7 @@ struct gru_create_context_req { | |||
86 | unsigned int control_blocks; | 77 | unsigned int control_blocks; |
87 | unsigned int maximum_thread_count; | 78 | unsigned int maximum_thread_count; |
88 | unsigned int options; | 79 | unsigned int options; |
80 | unsigned char tlb_preload_count; | ||
89 | }; | 81 | }; |
90 | 82 | ||
91 | /* | 83 | /* |
@@ -98,11 +90,12 @@ struct gru_unload_context_req { | |||
98 | /* | 90 | /* |
99 | * Structure used to set context options | 91 | * Structure used to set context options |
100 | */ | 92 | */ |
101 | enum {sco_gseg_owner, sco_cch_req_slice}; | 93 | enum {sco_gseg_owner, sco_cch_req_slice, sco_blade_chiplet}; |
102 | struct gru_set_context_option_req { | 94 | struct gru_set_context_option_req { |
103 | unsigned long gseg; | 95 | unsigned long gseg; |
104 | int op; | 96 | int op; |
105 | unsigned long val1; | 97 | int val0; |
98 | long val1; | ||
106 | }; | 99 | }; |
107 | 100 | ||
108 | /* | 101 | /* |
@@ -124,6 +117,8 @@ struct gru_dump_chiplet_state_req { | |||
124 | int ctxnum; | 117 | int ctxnum; |
125 | char data_opt; | 118 | char data_opt; |
126 | char lock_cch; | 119 | char lock_cch; |
120 | char flush_cbrs; | ||
121 | char fill[10]; | ||
127 | pid_t pid; | 122 | pid_t pid; |
128 | void *buf; | 123 | void *buf; |
129 | size_t buflen; | 124 | size_t buflen; |
diff --git a/drivers/misc/sgi-gru/grumain.c b/drivers/misc/sgi-gru/grumain.c index 3bc643dad606..f8538bbd0bfa 100644 --- a/drivers/misc/sgi-gru/grumain.c +++ b/drivers/misc/sgi-gru/grumain.c | |||
@@ -27,6 +27,7 @@ | |||
27 | #include <linux/sched.h> | 27 | #include <linux/sched.h> |
28 | #include <linux/device.h> | 28 | #include <linux/device.h> |
29 | #include <linux/list.h> | 29 | #include <linux/list.h> |
30 | #include <linux/err.h> | ||
30 | #include <asm/uv/uv_hub.h> | 31 | #include <asm/uv/uv_hub.h> |
31 | #include "gru.h" | 32 | #include "gru.h" |
32 | #include "grutables.h" | 33 | #include "grutables.h" |
@@ -48,12 +49,20 @@ struct device *grudev = &gru_device; | |||
48 | /* | 49 | /* |
49 | * Select a gru fault map to be used by the current cpu. Note that | 50 | * Select a gru fault map to be used by the current cpu. Note that |
50 | * multiple cpus may be using the same map. | 51 | * multiple cpus may be using the same map. |
51 | * ZZZ should "shift" be used?? Depends on HT cpu numbering | ||
52 | * ZZZ should be inline but did not work on emulator | 52 | * ZZZ should be inline but did not work on emulator |
53 | */ | 53 | */ |
54 | int gru_cpu_fault_map_id(void) | 54 | int gru_cpu_fault_map_id(void) |
55 | { | 55 | { |
56 | #ifdef CONFIG_IA64 | ||
56 | return uv_blade_processor_id() % GRU_NUM_TFM; | 57 | return uv_blade_processor_id() % GRU_NUM_TFM; |
58 | #else | ||
59 | int cpu = smp_processor_id(); | ||
60 | int id, core; | ||
61 | |||
62 | core = uv_cpu_core_number(cpu); | ||
63 | id = core + UV_MAX_INT_CORES * uv_cpu_socket_number(cpu); | ||
64 | return id; | ||
65 | #endif | ||
57 | } | 66 | } |
58 | 67 | ||
59 | /*--------- ASID Management ------------------------------------------- | 68 | /*--------- ASID Management ------------------------------------------- |
@@ -286,7 +295,8 @@ static void gru_unload_mm_tracker(struct gru_state *gru, | |||
286 | void gts_drop(struct gru_thread_state *gts) | 295 | void gts_drop(struct gru_thread_state *gts) |
287 | { | 296 | { |
288 | if (gts && atomic_dec_return(>s->ts_refcnt) == 0) { | 297 | if (gts && atomic_dec_return(>s->ts_refcnt) == 0) { |
289 | gru_drop_mmu_notifier(gts->ts_gms); | 298 | if (gts->ts_gms) |
299 | gru_drop_mmu_notifier(gts->ts_gms); | ||
290 | kfree(gts); | 300 | kfree(gts); |
291 | STAT(gts_free); | 301 | STAT(gts_free); |
292 | } | 302 | } |
@@ -310,16 +320,18 @@ static struct gru_thread_state *gru_find_current_gts_nolock(struct gru_vma_data | |||
310 | * Allocate a thread state structure. | 320 | * Allocate a thread state structure. |
311 | */ | 321 | */ |
312 | struct gru_thread_state *gru_alloc_gts(struct vm_area_struct *vma, | 322 | struct gru_thread_state *gru_alloc_gts(struct vm_area_struct *vma, |
313 | int cbr_au_count, int dsr_au_count, int options, int tsid) | 323 | int cbr_au_count, int dsr_au_count, |
324 | unsigned char tlb_preload_count, int options, int tsid) | ||
314 | { | 325 | { |
315 | struct gru_thread_state *gts; | 326 | struct gru_thread_state *gts; |
327 | struct gru_mm_struct *gms; | ||
316 | int bytes; | 328 | int bytes; |
317 | 329 | ||
318 | bytes = DSR_BYTES(dsr_au_count) + CBR_BYTES(cbr_au_count); | 330 | bytes = DSR_BYTES(dsr_au_count) + CBR_BYTES(cbr_au_count); |
319 | bytes += sizeof(struct gru_thread_state); | 331 | bytes += sizeof(struct gru_thread_state); |
320 | gts = kmalloc(bytes, GFP_KERNEL); | 332 | gts = kmalloc(bytes, GFP_KERNEL); |
321 | if (!gts) | 333 | if (!gts) |
322 | return NULL; | 334 | return ERR_PTR(-ENOMEM); |
323 | 335 | ||
324 | STAT(gts_alloc); | 336 | STAT(gts_alloc); |
325 | memset(gts, 0, sizeof(struct gru_thread_state)); /* zero out header */ | 337 | memset(gts, 0, sizeof(struct gru_thread_state)); /* zero out header */ |
@@ -327,7 +339,10 @@ struct gru_thread_state *gru_alloc_gts(struct vm_area_struct *vma, | |||
327 | mutex_init(>s->ts_ctxlock); | 339 | mutex_init(>s->ts_ctxlock); |
328 | gts->ts_cbr_au_count = cbr_au_count; | 340 | gts->ts_cbr_au_count = cbr_au_count; |
329 | gts->ts_dsr_au_count = dsr_au_count; | 341 | gts->ts_dsr_au_count = dsr_au_count; |
342 | gts->ts_tlb_preload_count = tlb_preload_count; | ||
330 | gts->ts_user_options = options; | 343 | gts->ts_user_options = options; |
344 | gts->ts_user_blade_id = -1; | ||
345 | gts->ts_user_chiplet_id = -1; | ||
331 | gts->ts_tsid = tsid; | 346 | gts->ts_tsid = tsid; |
332 | gts->ts_ctxnum = NULLCTX; | 347 | gts->ts_ctxnum = NULLCTX; |
333 | gts->ts_tlb_int_select = -1; | 348 | gts->ts_tlb_int_select = -1; |
@@ -336,9 +351,10 @@ struct gru_thread_state *gru_alloc_gts(struct vm_area_struct *vma, | |||
336 | if (vma) { | 351 | if (vma) { |
337 | gts->ts_mm = current->mm; | 352 | gts->ts_mm = current->mm; |
338 | gts->ts_vma = vma; | 353 | gts->ts_vma = vma; |
339 | gts->ts_gms = gru_register_mmu_notifier(); | 354 | gms = gru_register_mmu_notifier(); |
340 | if (!gts->ts_gms) | 355 | if (IS_ERR(gms)) |
341 | goto err; | 356 | goto err; |
357 | gts->ts_gms = gms; | ||
342 | } | 358 | } |
343 | 359 | ||
344 | gru_dbg(grudev, "alloc gts %p\n", gts); | 360 | gru_dbg(grudev, "alloc gts %p\n", gts); |
@@ -346,7 +362,7 @@ struct gru_thread_state *gru_alloc_gts(struct vm_area_struct *vma, | |||
346 | 362 | ||
347 | err: | 363 | err: |
348 | gts_drop(gts); | 364 | gts_drop(gts); |
349 | return NULL; | 365 | return ERR_CAST(gms); |
350 | } | 366 | } |
351 | 367 | ||
352 | /* | 368 | /* |
@@ -360,6 +376,7 @@ struct gru_vma_data *gru_alloc_vma_data(struct vm_area_struct *vma, int tsid) | |||
360 | if (!vdata) | 376 | if (!vdata) |
361 | return NULL; | 377 | return NULL; |
362 | 378 | ||
379 | STAT(vdata_alloc); | ||
363 | INIT_LIST_HEAD(&vdata->vd_head); | 380 | INIT_LIST_HEAD(&vdata->vd_head); |
364 | spin_lock_init(&vdata->vd_lock); | 381 | spin_lock_init(&vdata->vd_lock); |
365 | gru_dbg(grudev, "alloc vdata %p\n", vdata); | 382 | gru_dbg(grudev, "alloc vdata %p\n", vdata); |
@@ -392,10 +409,12 @@ struct gru_thread_state *gru_alloc_thread_state(struct vm_area_struct *vma, | |||
392 | struct gru_vma_data *vdata = vma->vm_private_data; | 409 | struct gru_vma_data *vdata = vma->vm_private_data; |
393 | struct gru_thread_state *gts, *ngts; | 410 | struct gru_thread_state *gts, *ngts; |
394 | 411 | ||
395 | gts = gru_alloc_gts(vma, vdata->vd_cbr_au_count, vdata->vd_dsr_au_count, | 412 | gts = gru_alloc_gts(vma, vdata->vd_cbr_au_count, |
413 | vdata->vd_dsr_au_count, | ||
414 | vdata->vd_tlb_preload_count, | ||
396 | vdata->vd_user_options, tsid); | 415 | vdata->vd_user_options, tsid); |
397 | if (!gts) | 416 | if (IS_ERR(gts)) |
398 | return NULL; | 417 | return gts; |
399 | 418 | ||
400 | spin_lock(&vdata->vd_lock); | 419 | spin_lock(&vdata->vd_lock); |
401 | ngts = gru_find_current_gts_nolock(vdata, tsid); | 420 | ngts = gru_find_current_gts_nolock(vdata, tsid); |
@@ -493,6 +512,9 @@ static void gru_load_context_data(void *save, void *grubase, int ctxnum, | |||
493 | memset(cbe + i * GRU_HANDLE_STRIDE, 0, | 512 | memset(cbe + i * GRU_HANDLE_STRIDE, 0, |
494 | GRU_CACHE_LINE_BYTES); | 513 | GRU_CACHE_LINE_BYTES); |
495 | } | 514 | } |
515 | /* Flush CBE to hide race in context restart */ | ||
516 | mb(); | ||
517 | gru_flush_cache(cbe + i * GRU_HANDLE_STRIDE); | ||
496 | cb += GRU_HANDLE_STRIDE; | 518 | cb += GRU_HANDLE_STRIDE; |
497 | } | 519 | } |
498 | 520 | ||
@@ -513,6 +535,12 @@ static void gru_unload_context_data(void *save, void *grubase, int ctxnum, | |||
513 | cb = gseg + GRU_CB_BASE; | 535 | cb = gseg + GRU_CB_BASE; |
514 | cbe = grubase + GRU_CBE_BASE; | 536 | cbe = grubase + GRU_CBE_BASE; |
515 | length = hweight64(dsrmap) * GRU_DSR_AU_BYTES; | 537 | length = hweight64(dsrmap) * GRU_DSR_AU_BYTES; |
538 | |||
539 | /* CBEs may not be coherent. Flush them from cache */ | ||
540 | for_each_cbr_in_allocation_map(i, &cbrmap, scr) | ||
541 | gru_flush_cache(cbe + i * GRU_HANDLE_STRIDE); | ||
542 | mb(); /* Let the CL flush complete */ | ||
543 | |||
516 | gru_prefetch_context(gseg, cb, cbe, cbrmap, length); | 544 | gru_prefetch_context(gseg, cb, cbe, cbrmap, length); |
517 | 545 | ||
518 | for_each_cbr_in_allocation_map(i, &cbrmap, scr) { | 546 | for_each_cbr_in_allocation_map(i, &cbrmap, scr) { |
@@ -533,7 +561,8 @@ void gru_unload_context(struct gru_thread_state *gts, int savestate) | |||
533 | zap_vma_ptes(gts->ts_vma, UGRUADDR(gts), GRU_GSEG_PAGESIZE); | 561 | zap_vma_ptes(gts->ts_vma, UGRUADDR(gts), GRU_GSEG_PAGESIZE); |
534 | cch = get_cch(gru->gs_gru_base_vaddr, ctxnum); | 562 | cch = get_cch(gru->gs_gru_base_vaddr, ctxnum); |
535 | 563 | ||
536 | gru_dbg(grudev, "gts %p\n", gts); | 564 | gru_dbg(grudev, "gts %p, cbrmap 0x%lx, dsrmap 0x%lx\n", |
565 | gts, gts->ts_cbr_map, gts->ts_dsr_map); | ||
537 | lock_cch_handle(cch); | 566 | lock_cch_handle(cch); |
538 | if (cch_interrupt_sync(cch)) | 567 | if (cch_interrupt_sync(cch)) |
539 | BUG(); | 568 | BUG(); |
@@ -549,7 +578,6 @@ void gru_unload_context(struct gru_thread_state *gts, int savestate) | |||
549 | 578 | ||
550 | if (cch_deallocate(cch)) | 579 | if (cch_deallocate(cch)) |
551 | BUG(); | 580 | BUG(); |
552 | gts->ts_force_unload = 0; /* ts_force_unload locked by CCH lock */ | ||
553 | unlock_cch_handle(cch); | 581 | unlock_cch_handle(cch); |
554 | 582 | ||
555 | gru_free_gru_context(gts); | 583 | gru_free_gru_context(gts); |
@@ -565,9 +593,7 @@ void gru_load_context(struct gru_thread_state *gts) | |||
565 | struct gru_context_configuration_handle *cch; | 593 | struct gru_context_configuration_handle *cch; |
566 | int i, err, asid, ctxnum = gts->ts_ctxnum; | 594 | int i, err, asid, ctxnum = gts->ts_ctxnum; |
567 | 595 | ||
568 | gru_dbg(grudev, "gts %p\n", gts); | ||
569 | cch = get_cch(gru->gs_gru_base_vaddr, ctxnum); | 596 | cch = get_cch(gru->gs_gru_base_vaddr, ctxnum); |
570 | |||
571 | lock_cch_handle(cch); | 597 | lock_cch_handle(cch); |
572 | cch->tfm_fault_bit_enable = | 598 | cch->tfm_fault_bit_enable = |
573 | (gts->ts_user_options == GRU_OPT_MISS_FMM_POLL | 599 | (gts->ts_user_options == GRU_OPT_MISS_FMM_POLL |
@@ -591,6 +617,7 @@ void gru_load_context(struct gru_thread_state *gts) | |||
591 | cch->unmap_enable = 1; | 617 | cch->unmap_enable = 1; |
592 | cch->tfm_done_bit_enable = 1; | 618 | cch->tfm_done_bit_enable = 1; |
593 | cch->cb_int_enable = 1; | 619 | cch->cb_int_enable = 1; |
620 | cch->tlb_int_select = 0; /* For now, ints go to cpu 0 */ | ||
594 | } else { | 621 | } else { |
595 | cch->unmap_enable = 0; | 622 | cch->unmap_enable = 0; |
596 | cch->tfm_done_bit_enable = 0; | 623 | cch->tfm_done_bit_enable = 0; |
@@ -616,17 +643,18 @@ void gru_load_context(struct gru_thread_state *gts) | |||
616 | if (cch_start(cch)) | 643 | if (cch_start(cch)) |
617 | BUG(); | 644 | BUG(); |
618 | unlock_cch_handle(cch); | 645 | unlock_cch_handle(cch); |
646 | |||
647 | gru_dbg(grudev, "gid %d, gts %p, cbrmap 0x%lx, dsrmap 0x%lx, tie %d, tis %d\n", | ||
648 | gts->ts_gru->gs_gid, gts, gts->ts_cbr_map, gts->ts_dsr_map, | ||
649 | (gts->ts_user_options == GRU_OPT_MISS_FMM_INTR), gts->ts_tlb_int_select); | ||
619 | } | 650 | } |
620 | 651 | ||
621 | /* | 652 | /* |
622 | * Update fields in an active CCH: | 653 | * Update fields in an active CCH: |
623 | * - retarget interrupts on local blade | 654 | * - retarget interrupts on local blade |
624 | * - update sizeavail mask | 655 | * - update sizeavail mask |
625 | * - force a delayed context unload by clearing the CCH asids. This | ||
626 | * forces TLB misses for new GRU instructions. The context is unloaded | ||
627 | * when the next TLB miss occurs. | ||
628 | */ | 656 | */ |
629 | int gru_update_cch(struct gru_thread_state *gts, int force_unload) | 657 | int gru_update_cch(struct gru_thread_state *gts) |
630 | { | 658 | { |
631 | struct gru_context_configuration_handle *cch; | 659 | struct gru_context_configuration_handle *cch; |
632 | struct gru_state *gru = gts->ts_gru; | 660 | struct gru_state *gru = gts->ts_gru; |
@@ -640,21 +668,13 @@ int gru_update_cch(struct gru_thread_state *gts, int force_unload) | |||
640 | goto exit; | 668 | goto exit; |
641 | if (cch_interrupt(cch)) | 669 | if (cch_interrupt(cch)) |
642 | BUG(); | 670 | BUG(); |
643 | if (!force_unload) { | 671 | for (i = 0; i < 8; i++) |
644 | for (i = 0; i < 8; i++) | 672 | cch->sizeavail[i] = gts->ts_sizeavail; |
645 | cch->sizeavail[i] = gts->ts_sizeavail; | 673 | gts->ts_tlb_int_select = gru_cpu_fault_map_id(); |
646 | gts->ts_tlb_int_select = gru_cpu_fault_map_id(); | 674 | cch->tlb_int_select = gru_cpu_fault_map_id(); |
647 | cch->tlb_int_select = gru_cpu_fault_map_id(); | 675 | cch->tfm_fault_bit_enable = |
648 | cch->tfm_fault_bit_enable = | 676 | (gts->ts_user_options == GRU_OPT_MISS_FMM_POLL |
649 | (gts->ts_user_options == GRU_OPT_MISS_FMM_POLL | 677 | || gts->ts_user_options == GRU_OPT_MISS_FMM_INTR); |
650 | || gts->ts_user_options == GRU_OPT_MISS_FMM_INTR); | ||
651 | } else { | ||
652 | for (i = 0; i < 8; i++) | ||
653 | cch->asid[i] = 0; | ||
654 | cch->tfm_fault_bit_enable = 0; | ||
655 | cch->tlb_int_enable = 0; | ||
656 | gts->ts_force_unload = 1; | ||
657 | } | ||
658 | if (cch_start(cch)) | 678 | if (cch_start(cch)) |
659 | BUG(); | 679 | BUG(); |
660 | ret = 1; | 680 | ret = 1; |
@@ -679,7 +699,54 @@ static int gru_retarget_intr(struct gru_thread_state *gts) | |||
679 | 699 | ||
680 | gru_dbg(grudev, "retarget from %d to %d\n", gts->ts_tlb_int_select, | 700 | gru_dbg(grudev, "retarget from %d to %d\n", gts->ts_tlb_int_select, |
681 | gru_cpu_fault_map_id()); | 701 | gru_cpu_fault_map_id()); |
682 | return gru_update_cch(gts, 0); | 702 | return gru_update_cch(gts); |
703 | } | ||
704 | |||
705 | /* | ||
706 | * Check if a GRU context is allowed to use a specific chiplet. By default | ||
707 | * a context is assigned to any blade-local chiplet. However, users can | ||
708 | * override this. | ||
709 | * Returns 1 if assignment allowed, 0 otherwise | ||
710 | */ | ||
711 | static int gru_check_chiplet_assignment(struct gru_state *gru, | ||
712 | struct gru_thread_state *gts) | ||
713 | { | ||
714 | int blade_id; | ||
715 | int chiplet_id; | ||
716 | |||
717 | blade_id = gts->ts_user_blade_id; | ||
718 | if (blade_id < 0) | ||
719 | blade_id = uv_numa_blade_id(); | ||
720 | |||
721 | chiplet_id = gts->ts_user_chiplet_id; | ||
722 | return gru->gs_blade_id == blade_id && | ||
723 | (chiplet_id < 0 || chiplet_id == gru->gs_chiplet_id); | ||
724 | } | ||
725 | |||
726 | /* | ||
727 | * Unload the gru context if it is not assigned to the correct blade or | ||
728 | * chiplet. Misassignment can occur if the process migrates to a different | ||
729 | * blade or if the user changes the selected blade/chiplet. | ||
730 | */ | ||
731 | void gru_check_context_placement(struct gru_thread_state *gts) | ||
732 | { | ||
733 | struct gru_state *gru; | ||
734 | |||
735 | /* | ||
736 | * If the current task is the context owner, verify that the | ||
737 | * context is correctly placed. This test is skipped for non-owner | ||
738 | * references. Pthread apps use non-owner references to the CBRs. | ||
739 | */ | ||
740 | gru = gts->ts_gru; | ||
741 | if (!gru || gts->ts_tgid_owner != current->tgid) | ||
742 | return; | ||
743 | |||
744 | if (!gru_check_chiplet_assignment(gru, gts)) { | ||
745 | STAT(check_context_unload); | ||
746 | gru_unload_context(gts, 1); | ||
747 | } else if (gru_retarget_intr(gts)) { | ||
748 | STAT(check_context_retarget_intr); | ||
749 | } | ||
683 | } | 750 | } |
684 | 751 | ||
685 | 752 | ||
@@ -712,13 +779,17 @@ static void gts_stolen(struct gru_thread_state *gts, | |||
712 | } | 779 | } |
713 | } | 780 | } |
714 | 781 | ||
715 | void gru_steal_context(struct gru_thread_state *gts, int blade_id) | 782 | void gru_steal_context(struct gru_thread_state *gts) |
716 | { | 783 | { |
717 | struct gru_blade_state *blade; | 784 | struct gru_blade_state *blade; |
718 | struct gru_state *gru, *gru0; | 785 | struct gru_state *gru, *gru0; |
719 | struct gru_thread_state *ngts = NULL; | 786 | struct gru_thread_state *ngts = NULL; |
720 | int ctxnum, ctxnum0, flag = 0, cbr, dsr; | 787 | int ctxnum, ctxnum0, flag = 0, cbr, dsr; |
788 | int blade_id; | ||
721 | 789 | ||
790 | blade_id = gts->ts_user_blade_id; | ||
791 | if (blade_id < 0) | ||
792 | blade_id = uv_numa_blade_id(); | ||
722 | cbr = gts->ts_cbr_au_count; | 793 | cbr = gts->ts_cbr_au_count; |
723 | dsr = gts->ts_dsr_au_count; | 794 | dsr = gts->ts_dsr_au_count; |
724 | 795 | ||
@@ -729,35 +800,39 @@ void gru_steal_context(struct gru_thread_state *gts, int blade_id) | |||
729 | gru = blade->bs_lru_gru; | 800 | gru = blade->bs_lru_gru; |
730 | if (ctxnum == 0) | 801 | if (ctxnum == 0) |
731 | gru = next_gru(blade, gru); | 802 | gru = next_gru(blade, gru); |
803 | blade->bs_lru_gru = gru; | ||
804 | blade->bs_lru_ctxnum = ctxnum; | ||
732 | ctxnum0 = ctxnum; | 805 | ctxnum0 = ctxnum; |
733 | gru0 = gru; | 806 | gru0 = gru; |
734 | while (1) { | 807 | while (1) { |
735 | if (check_gru_resources(gru, cbr, dsr, GRU_NUM_CCH)) | 808 | if (gru_check_chiplet_assignment(gru, gts)) { |
736 | break; | 809 | if (check_gru_resources(gru, cbr, dsr, GRU_NUM_CCH)) |
737 | spin_lock(&gru->gs_lock); | ||
738 | for (; ctxnum < GRU_NUM_CCH; ctxnum++) { | ||
739 | if (flag && gru == gru0 && ctxnum == ctxnum0) | ||
740 | break; | 810 | break; |
741 | ngts = gru->gs_gts[ctxnum]; | 811 | spin_lock(&gru->gs_lock); |
742 | /* | 812 | for (; ctxnum < GRU_NUM_CCH; ctxnum++) { |
743 | * We are grabbing locks out of order, so trylock is | 813 | if (flag && gru == gru0 && ctxnum == ctxnum0) |
744 | * needed. GTSs are usually not locked, so the odds of | 814 | break; |
745 | * success are high. If trylock fails, try to steal a | 815 | ngts = gru->gs_gts[ctxnum]; |
746 | * different GSEG. | 816 | /* |
747 | */ | 817 | * We are grabbing locks out of order, so trylock is |
748 | if (ngts && is_gts_stealable(ngts, blade)) | 818 | * needed. GTSs are usually not locked, so the odds of |
819 | * success are high. If trylock fails, try to steal a | ||
820 | * different GSEG. | ||
821 | */ | ||
822 | if (ngts && is_gts_stealable(ngts, blade)) | ||
823 | break; | ||
824 | ngts = NULL; | ||
825 | } | ||
826 | spin_unlock(&gru->gs_lock); | ||
827 | if (ngts || (flag && gru == gru0 && ctxnum == ctxnum0)) | ||
749 | break; | 828 | break; |
750 | ngts = NULL; | ||
751 | flag = 1; | ||
752 | } | 829 | } |
753 | spin_unlock(&gru->gs_lock); | 830 | if (flag && gru == gru0) |
754 | if (ngts || (flag && gru == gru0 && ctxnum == ctxnum0)) | ||
755 | break; | 831 | break; |
832 | flag = 1; | ||
756 | ctxnum = 0; | 833 | ctxnum = 0; |
757 | gru = next_gru(blade, gru); | 834 | gru = next_gru(blade, gru); |
758 | } | 835 | } |
759 | blade->bs_lru_gru = gru; | ||
760 | blade->bs_lru_ctxnum = ctxnum; | ||
761 | spin_unlock(&blade->bs_lock); | 836 | spin_unlock(&blade->bs_lock); |
762 | 837 | ||
763 | if (ngts) { | 838 | if (ngts) { |
@@ -776,19 +851,34 @@ void gru_steal_context(struct gru_thread_state *gts, int blade_id) | |||
776 | } | 851 | } |
777 | 852 | ||
778 | /* | 853 | /* |
854 | * Assign a gru context. | ||
855 | */ | ||
856 | static int gru_assign_context_number(struct gru_state *gru) | ||
857 | { | ||
858 | int ctxnum; | ||
859 | |||
860 | ctxnum = find_first_zero_bit(&gru->gs_context_map, GRU_NUM_CCH); | ||
861 | __set_bit(ctxnum, &gru->gs_context_map); | ||
862 | return ctxnum; | ||
863 | } | ||
864 | |||
865 | /* | ||
779 | * Scan the GRUs on the local blade & assign a GRU context. | 866 | * Scan the GRUs on the local blade & assign a GRU context. |
780 | */ | 867 | */ |
781 | struct gru_state *gru_assign_gru_context(struct gru_thread_state *gts, | 868 | struct gru_state *gru_assign_gru_context(struct gru_thread_state *gts) |
782 | int blade) | ||
783 | { | 869 | { |
784 | struct gru_state *gru, *grux; | 870 | struct gru_state *gru, *grux; |
785 | int i, max_active_contexts; | 871 | int i, max_active_contexts; |
872 | int blade_id = gts->ts_user_blade_id; | ||
786 | 873 | ||
787 | 874 | if (blade_id < 0) | |
875 | blade_id = uv_numa_blade_id(); | ||
788 | again: | 876 | again: |
789 | gru = NULL; | 877 | gru = NULL; |
790 | max_active_contexts = GRU_NUM_CCH; | 878 | max_active_contexts = GRU_NUM_CCH; |
791 | for_each_gru_on_blade(grux, blade, i) { | 879 | for_each_gru_on_blade(grux, blade_id, i) { |
880 | if (!gru_check_chiplet_assignment(grux, gts)) | ||
881 | continue; | ||
792 | if (check_gru_resources(grux, gts->ts_cbr_au_count, | 882 | if (check_gru_resources(grux, gts->ts_cbr_au_count, |
793 | gts->ts_dsr_au_count, | 883 | gts->ts_dsr_au_count, |
794 | max_active_contexts)) { | 884 | max_active_contexts)) { |
@@ -809,12 +899,9 @@ again: | |||
809 | reserve_gru_resources(gru, gts); | 899 | reserve_gru_resources(gru, gts); |
810 | gts->ts_gru = gru; | 900 | gts->ts_gru = gru; |
811 | gts->ts_blade = gru->gs_blade_id; | 901 | gts->ts_blade = gru->gs_blade_id; |
812 | gts->ts_ctxnum = | 902 | gts->ts_ctxnum = gru_assign_context_number(gru); |
813 | find_first_zero_bit(&gru->gs_context_map, GRU_NUM_CCH); | ||
814 | BUG_ON(gts->ts_ctxnum == GRU_NUM_CCH); | ||
815 | atomic_inc(>s->ts_refcnt); | 903 | atomic_inc(>s->ts_refcnt); |
816 | gru->gs_gts[gts->ts_ctxnum] = gts; | 904 | gru->gs_gts[gts->ts_ctxnum] = gts; |
817 | __set_bit(gts->ts_ctxnum, &gru->gs_context_map); | ||
818 | spin_unlock(&gru->gs_lock); | 905 | spin_unlock(&gru->gs_lock); |
819 | 906 | ||
820 | STAT(assign_context); | 907 | STAT(assign_context); |
@@ -842,7 +929,6 @@ int gru_fault(struct vm_area_struct *vma, struct vm_fault *vmf) | |||
842 | { | 929 | { |
843 | struct gru_thread_state *gts; | 930 | struct gru_thread_state *gts; |
844 | unsigned long paddr, vaddr; | 931 | unsigned long paddr, vaddr; |
845 | int blade_id; | ||
846 | 932 | ||
847 | vaddr = (unsigned long)vmf->virtual_address; | 933 | vaddr = (unsigned long)vmf->virtual_address; |
848 | gru_dbg(grudev, "vma %p, vaddr 0x%lx (0x%lx)\n", | 934 | gru_dbg(grudev, "vma %p, vaddr 0x%lx (0x%lx)\n", |
@@ -857,28 +943,18 @@ int gru_fault(struct vm_area_struct *vma, struct vm_fault *vmf) | |||
857 | again: | 943 | again: |
858 | mutex_lock(>s->ts_ctxlock); | 944 | mutex_lock(>s->ts_ctxlock); |
859 | preempt_disable(); | 945 | preempt_disable(); |
860 | blade_id = uv_numa_blade_id(); | ||
861 | 946 | ||
862 | if (gts->ts_gru) { | 947 | gru_check_context_placement(gts); |
863 | if (gts->ts_gru->gs_blade_id != blade_id) { | ||
864 | STAT(migrated_nopfn_unload); | ||
865 | gru_unload_context(gts, 1); | ||
866 | } else { | ||
867 | if (gru_retarget_intr(gts)) | ||
868 | STAT(migrated_nopfn_retarget); | ||
869 | } | ||
870 | } | ||
871 | 948 | ||
872 | if (!gts->ts_gru) { | 949 | if (!gts->ts_gru) { |
873 | STAT(load_user_context); | 950 | STAT(load_user_context); |
874 | if (!gru_assign_gru_context(gts, blade_id)) { | 951 | if (!gru_assign_gru_context(gts)) { |
875 | preempt_enable(); | 952 | preempt_enable(); |
876 | mutex_unlock(>s->ts_ctxlock); | 953 | mutex_unlock(>s->ts_ctxlock); |
877 | set_current_state(TASK_INTERRUPTIBLE); | 954 | set_current_state(TASK_INTERRUPTIBLE); |
878 | schedule_timeout(GRU_ASSIGN_DELAY); /* true hack ZZZ */ | 955 | schedule_timeout(GRU_ASSIGN_DELAY); /* true hack ZZZ */ |
879 | blade_id = uv_numa_blade_id(); | ||
880 | if (gts->ts_steal_jiffies + GRU_STEAL_DELAY < jiffies) | 956 | if (gts->ts_steal_jiffies + GRU_STEAL_DELAY < jiffies) |
881 | gru_steal_context(gts, blade_id); | 957 | gru_steal_context(gts); |
882 | goto again; | 958 | goto again; |
883 | } | 959 | } |
884 | gru_load_context(gts); | 960 | gru_load_context(gts); |
diff --git a/drivers/misc/sgi-gru/gruprocfs.c b/drivers/misc/sgi-gru/gruprocfs.c index ccd4408a26c7..7768b87d995b 100644 --- a/drivers/misc/sgi-gru/gruprocfs.c +++ b/drivers/misc/sgi-gru/gruprocfs.c | |||
@@ -36,8 +36,7 @@ static void printstat_val(struct seq_file *s, atomic_long_t *v, char *id) | |||
36 | { | 36 | { |
37 | unsigned long val = atomic_long_read(v); | 37 | unsigned long val = atomic_long_read(v); |
38 | 38 | ||
39 | if (val) | 39 | seq_printf(s, "%16lu %s\n", val, id); |
40 | seq_printf(s, "%16lu %s\n", val, id); | ||
41 | } | 40 | } |
42 | 41 | ||
43 | static int statistics_show(struct seq_file *s, void *p) | 42 | static int statistics_show(struct seq_file *s, void *p) |
@@ -46,7 +45,8 @@ static int statistics_show(struct seq_file *s, void *p) | |||
46 | printstat(s, vdata_free); | 45 | printstat(s, vdata_free); |
47 | printstat(s, gts_alloc); | 46 | printstat(s, gts_alloc); |
48 | printstat(s, gts_free); | 47 | printstat(s, gts_free); |
49 | printstat(s, vdata_double_alloc); | 48 | printstat(s, gms_alloc); |
49 | printstat(s, gms_free); | ||
50 | printstat(s, gts_double_allocate); | 50 | printstat(s, gts_double_allocate); |
51 | printstat(s, assign_context); | 51 | printstat(s, assign_context); |
52 | printstat(s, assign_context_failed); | 52 | printstat(s, assign_context_failed); |
@@ -59,28 +59,25 @@ static int statistics_show(struct seq_file *s, void *p) | |||
59 | printstat(s, steal_kernel_context); | 59 | printstat(s, steal_kernel_context); |
60 | printstat(s, steal_context_failed); | 60 | printstat(s, steal_context_failed); |
61 | printstat(s, nopfn); | 61 | printstat(s, nopfn); |
62 | printstat(s, break_cow); | ||
63 | printstat(s, asid_new); | 62 | printstat(s, asid_new); |
64 | printstat(s, asid_next); | 63 | printstat(s, asid_next); |
65 | printstat(s, asid_wrap); | 64 | printstat(s, asid_wrap); |
66 | printstat(s, asid_reuse); | 65 | printstat(s, asid_reuse); |
67 | printstat(s, intr); | 66 | printstat(s, intr); |
67 | printstat(s, intr_cbr); | ||
68 | printstat(s, intr_tfh); | ||
69 | printstat(s, intr_spurious); | ||
68 | printstat(s, intr_mm_lock_failed); | 70 | printstat(s, intr_mm_lock_failed); |
69 | printstat(s, call_os); | 71 | printstat(s, call_os); |
70 | printstat(s, call_os_offnode_reference); | ||
71 | printstat(s, call_os_check_for_bug); | ||
72 | printstat(s, call_os_wait_queue); | 72 | printstat(s, call_os_wait_queue); |
73 | printstat(s, user_flush_tlb); | 73 | printstat(s, user_flush_tlb); |
74 | printstat(s, user_unload_context); | 74 | printstat(s, user_unload_context); |
75 | printstat(s, user_exception); | 75 | printstat(s, user_exception); |
76 | printstat(s, set_context_option); | 76 | printstat(s, set_context_option); |
77 | printstat(s, migrate_check); | 77 | printstat(s, check_context_retarget_intr); |
78 | printstat(s, migrated_retarget); | 78 | printstat(s, check_context_unload); |
79 | printstat(s, migrated_unload); | ||
80 | printstat(s, migrated_unload_delay); | ||
81 | printstat(s, migrated_nopfn_retarget); | ||
82 | printstat(s, migrated_nopfn_unload); | ||
83 | printstat(s, tlb_dropin); | 79 | printstat(s, tlb_dropin); |
80 | printstat(s, tlb_preload_page); | ||
84 | printstat(s, tlb_dropin_fail_no_asid); | 81 | printstat(s, tlb_dropin_fail_no_asid); |
85 | printstat(s, tlb_dropin_fail_upm); | 82 | printstat(s, tlb_dropin_fail_upm); |
86 | printstat(s, tlb_dropin_fail_invalid); | 83 | printstat(s, tlb_dropin_fail_invalid); |
@@ -88,16 +85,15 @@ static int statistics_show(struct seq_file *s, void *p) | |||
88 | printstat(s, tlb_dropin_fail_idle); | 85 | printstat(s, tlb_dropin_fail_idle); |
89 | printstat(s, tlb_dropin_fail_fmm); | 86 | printstat(s, tlb_dropin_fail_fmm); |
90 | printstat(s, tlb_dropin_fail_no_exception); | 87 | printstat(s, tlb_dropin_fail_no_exception); |
91 | printstat(s, tlb_dropin_fail_no_exception_war); | ||
92 | printstat(s, tfh_stale_on_fault); | 88 | printstat(s, tfh_stale_on_fault); |
93 | printstat(s, mmu_invalidate_range); | 89 | printstat(s, mmu_invalidate_range); |
94 | printstat(s, mmu_invalidate_page); | 90 | printstat(s, mmu_invalidate_page); |
95 | printstat(s, mmu_clear_flush_young); | ||
96 | printstat(s, flush_tlb); | 91 | printstat(s, flush_tlb); |
97 | printstat(s, flush_tlb_gru); | 92 | printstat(s, flush_tlb_gru); |
98 | printstat(s, flush_tlb_gru_tgh); | 93 | printstat(s, flush_tlb_gru_tgh); |
99 | printstat(s, flush_tlb_gru_zero_asid); | 94 | printstat(s, flush_tlb_gru_zero_asid); |
100 | printstat(s, copy_gpa); | 95 | printstat(s, copy_gpa); |
96 | printstat(s, read_gpa); | ||
101 | printstat(s, mesq_receive); | 97 | printstat(s, mesq_receive); |
102 | printstat(s, mesq_receive_none); | 98 | printstat(s, mesq_receive_none); |
103 | printstat(s, mesq_send); | 99 | printstat(s, mesq_send); |
@@ -108,7 +104,6 @@ static int statistics_show(struct seq_file *s, void *p) | |||
108 | printstat(s, mesq_send_qlimit_reached); | 104 | printstat(s, mesq_send_qlimit_reached); |
109 | printstat(s, mesq_send_amo_nacked); | 105 | printstat(s, mesq_send_amo_nacked); |
110 | printstat(s, mesq_send_put_nacked); | 106 | printstat(s, mesq_send_put_nacked); |
111 | printstat(s, mesq_qf_not_full); | ||
112 | printstat(s, mesq_qf_locked); | 107 | printstat(s, mesq_qf_locked); |
113 | printstat(s, mesq_qf_noop_not_full); | 108 | printstat(s, mesq_qf_noop_not_full); |
114 | printstat(s, mesq_qf_switch_head_failed); | 109 | printstat(s, mesq_qf_switch_head_failed); |
@@ -118,6 +113,7 @@ static int statistics_show(struct seq_file *s, void *p) | |||
118 | printstat(s, mesq_noop_qlimit_reached); | 113 | printstat(s, mesq_noop_qlimit_reached); |
119 | printstat(s, mesq_noop_amo_nacked); | 114 | printstat(s, mesq_noop_amo_nacked); |
120 | printstat(s, mesq_noop_put_nacked); | 115 | printstat(s, mesq_noop_put_nacked); |
116 | printstat(s, mesq_noop_page_overflow); | ||
121 | return 0; | 117 | return 0; |
122 | } | 118 | } |
123 | 119 | ||
@@ -133,8 +129,10 @@ static int mcs_statistics_show(struct seq_file *s, void *p) | |||
133 | int op; | 129 | int op; |
134 | unsigned long total, count, max; | 130 | unsigned long total, count, max; |
135 | static char *id[] = {"cch_allocate", "cch_start", "cch_interrupt", | 131 | static char *id[] = {"cch_allocate", "cch_start", "cch_interrupt", |
136 | "cch_interrupt_sync", "cch_deallocate", "tgh_invalidate"}; | 132 | "cch_interrupt_sync", "cch_deallocate", "tfh_write_only", |
133 | "tfh_write_restart", "tgh_invalidate"}; | ||
137 | 134 | ||
135 | seq_printf(s, "%-20s%12s%12s%12s\n", "#id", "count", "aver-clks", "max-clks"); | ||
138 | for (op = 0; op < mcsop_last; op++) { | 136 | for (op = 0; op < mcsop_last; op++) { |
139 | count = atomic_long_read(&mcs_op_statistics[op].count); | 137 | count = atomic_long_read(&mcs_op_statistics[op].count); |
140 | total = atomic_long_read(&mcs_op_statistics[op].total); | 138 | total = atomic_long_read(&mcs_op_statistics[op].total); |
@@ -154,6 +152,7 @@ static ssize_t mcs_statistics_write(struct file *file, | |||
154 | 152 | ||
155 | static int options_show(struct seq_file *s, void *p) | 153 | static int options_show(struct seq_file *s, void *p) |
156 | { | 154 | { |
155 | seq_printf(s, "#bitmask: 1=trace, 2=statistics\n"); | ||
157 | seq_printf(s, "0x%lx\n", gru_options); | 156 | seq_printf(s, "0x%lx\n", gru_options); |
158 | return 0; | 157 | return 0; |
159 | } | 158 | } |
@@ -161,14 +160,15 @@ static int options_show(struct seq_file *s, void *p) | |||
161 | static ssize_t options_write(struct file *file, const char __user *userbuf, | 160 | static ssize_t options_write(struct file *file, const char __user *userbuf, |
162 | size_t count, loff_t *data) | 161 | size_t count, loff_t *data) |
163 | { | 162 | { |
164 | unsigned long val; | 163 | char buf[20]; |
165 | char buf[80]; | ||
166 | 164 | ||
167 | if (strncpy_from_user(buf, userbuf, sizeof(buf) - 1) < 0) | 165 | if (count >= sizeof(buf)) |
166 | return -EINVAL; | ||
167 | if (copy_from_user(buf, userbuf, count)) | ||
168 | return -EFAULT; | 168 | return -EFAULT; |
169 | buf[count - 1] = '\0'; | 169 | buf[count] = '\0'; |
170 | if (!strict_strtoul(buf, 10, &val)) | 170 | if (strict_strtoul(buf, 0, &gru_options)) |
171 | gru_options = val; | 171 | return -EINVAL; |
172 | 172 | ||
173 | return count; | 173 | return count; |
174 | } | 174 | } |
@@ -182,16 +182,17 @@ static int cch_seq_show(struct seq_file *file, void *data) | |||
182 | const char *mode[] = { "??", "UPM", "INTR", "OS_POLL" }; | 182 | const char *mode[] = { "??", "UPM", "INTR", "OS_POLL" }; |
183 | 183 | ||
184 | if (gid == 0) | 184 | if (gid == 0) |
185 | seq_printf(file, "#%5s%5s%6s%9s%6s%8s%8s\n", "gid", "bid", | 185 | seq_printf(file, "#%5s%5s%6s%7s%9s%6s%8s%8s\n", "gid", "bid", |
186 | "ctx#", "pid", "cbrs", "dsbytes", "mode"); | 186 | "ctx#", "asid", "pid", "cbrs", "dsbytes", "mode"); |
187 | if (gru) | 187 | if (gru) |
188 | for (i = 0; i < GRU_NUM_CCH; i++) { | 188 | for (i = 0; i < GRU_NUM_CCH; i++) { |
189 | ts = gru->gs_gts[i]; | 189 | ts = gru->gs_gts[i]; |
190 | if (!ts) | 190 | if (!ts) |
191 | continue; | 191 | continue; |
192 | seq_printf(file, " %5d%5d%6d%9d%6d%8d%8s\n", | 192 | seq_printf(file, " %5d%5d%6d%7d%9d%6d%8d%8s\n", |
193 | gru->gs_gid, gru->gs_blade_id, i, | 193 | gru->gs_gid, gru->gs_blade_id, i, |
194 | ts->ts_tgid_owner, | 194 | is_kernel_context(ts) ? 0 : ts->ts_gms->ms_asids[gid].mt_asid, |
195 | is_kernel_context(ts) ? 0 : ts->ts_tgid_owner, | ||
195 | ts->ts_cbr_au_count * GRU_CBR_AU_SIZE, | 196 | ts->ts_cbr_au_count * GRU_CBR_AU_SIZE, |
196 | ts->ts_cbr_au_count * GRU_DSR_AU_BYTES, | 197 | ts->ts_cbr_au_count * GRU_DSR_AU_BYTES, |
197 | mode[ts->ts_user_options & | 198 | mode[ts->ts_user_options & |
@@ -354,7 +355,7 @@ static void delete_proc_files(void) | |||
354 | for (p = proc_files; p->name; p++) | 355 | for (p = proc_files; p->name; p++) |
355 | if (p->entry) | 356 | if (p->entry) |
356 | remove_proc_entry(p->name, proc_gru); | 357 | remove_proc_entry(p->name, proc_gru); |
357 | remove_proc_entry("gru", NULL); | 358 | remove_proc_entry("gru", proc_gru->parent); |
358 | } | 359 | } |
359 | } | 360 | } |
360 | 361 | ||
diff --git a/drivers/misc/sgi-gru/grutables.h b/drivers/misc/sgi-gru/grutables.h index 46990bcfa536..02a77b8b8eef 100644 --- a/drivers/misc/sgi-gru/grutables.h +++ b/drivers/misc/sgi-gru/grutables.h | |||
@@ -161,7 +161,7 @@ extern unsigned int gru_max_gids; | |||
161 | #define GRU_MAX_GRUS (GRU_MAX_BLADES * GRU_CHIPLETS_PER_BLADE) | 161 | #define GRU_MAX_GRUS (GRU_MAX_BLADES * GRU_CHIPLETS_PER_BLADE) |
162 | 162 | ||
163 | #define GRU_DRIVER_ID_STR "SGI GRU Device Driver" | 163 | #define GRU_DRIVER_ID_STR "SGI GRU Device Driver" |
164 | #define GRU_DRIVER_VERSION_STR "0.80" | 164 | #define GRU_DRIVER_VERSION_STR "0.85" |
165 | 165 | ||
166 | /* | 166 | /* |
167 | * GRU statistics. | 167 | * GRU statistics. |
@@ -171,7 +171,8 @@ struct gru_stats_s { | |||
171 | atomic_long_t vdata_free; | 171 | atomic_long_t vdata_free; |
172 | atomic_long_t gts_alloc; | 172 | atomic_long_t gts_alloc; |
173 | atomic_long_t gts_free; | 173 | atomic_long_t gts_free; |
174 | atomic_long_t vdata_double_alloc; | 174 | atomic_long_t gms_alloc; |
175 | atomic_long_t gms_free; | ||
175 | atomic_long_t gts_double_allocate; | 176 | atomic_long_t gts_double_allocate; |
176 | atomic_long_t assign_context; | 177 | atomic_long_t assign_context; |
177 | atomic_long_t assign_context_failed; | 178 | atomic_long_t assign_context_failed; |
@@ -184,28 +185,25 @@ struct gru_stats_s { | |||
184 | atomic_long_t steal_kernel_context; | 185 | atomic_long_t steal_kernel_context; |
185 | atomic_long_t steal_context_failed; | 186 | atomic_long_t steal_context_failed; |
186 | atomic_long_t nopfn; | 187 | atomic_long_t nopfn; |
187 | atomic_long_t break_cow; | ||
188 | atomic_long_t asid_new; | 188 | atomic_long_t asid_new; |
189 | atomic_long_t asid_next; | 189 | atomic_long_t asid_next; |
190 | atomic_long_t asid_wrap; | 190 | atomic_long_t asid_wrap; |
191 | atomic_long_t asid_reuse; | 191 | atomic_long_t asid_reuse; |
192 | atomic_long_t intr; | 192 | atomic_long_t intr; |
193 | atomic_long_t intr_cbr; | ||
194 | atomic_long_t intr_tfh; | ||
195 | atomic_long_t intr_spurious; | ||
193 | atomic_long_t intr_mm_lock_failed; | 196 | atomic_long_t intr_mm_lock_failed; |
194 | atomic_long_t call_os; | 197 | atomic_long_t call_os; |
195 | atomic_long_t call_os_offnode_reference; | ||
196 | atomic_long_t call_os_check_for_bug; | ||
197 | atomic_long_t call_os_wait_queue; | 198 | atomic_long_t call_os_wait_queue; |
198 | atomic_long_t user_flush_tlb; | 199 | atomic_long_t user_flush_tlb; |
199 | atomic_long_t user_unload_context; | 200 | atomic_long_t user_unload_context; |
200 | atomic_long_t user_exception; | 201 | atomic_long_t user_exception; |
201 | atomic_long_t set_context_option; | 202 | atomic_long_t set_context_option; |
202 | atomic_long_t migrate_check; | 203 | atomic_long_t check_context_retarget_intr; |
203 | atomic_long_t migrated_retarget; | 204 | atomic_long_t check_context_unload; |
204 | atomic_long_t migrated_unload; | ||
205 | atomic_long_t migrated_unload_delay; | ||
206 | atomic_long_t migrated_nopfn_retarget; | ||
207 | atomic_long_t migrated_nopfn_unload; | ||
208 | atomic_long_t tlb_dropin; | 205 | atomic_long_t tlb_dropin; |
206 | atomic_long_t tlb_preload_page; | ||
209 | atomic_long_t tlb_dropin_fail_no_asid; | 207 | atomic_long_t tlb_dropin_fail_no_asid; |
210 | atomic_long_t tlb_dropin_fail_upm; | 208 | atomic_long_t tlb_dropin_fail_upm; |
211 | atomic_long_t tlb_dropin_fail_invalid; | 209 | atomic_long_t tlb_dropin_fail_invalid; |
@@ -213,17 +211,16 @@ struct gru_stats_s { | |||
213 | atomic_long_t tlb_dropin_fail_idle; | 211 | atomic_long_t tlb_dropin_fail_idle; |
214 | atomic_long_t tlb_dropin_fail_fmm; | 212 | atomic_long_t tlb_dropin_fail_fmm; |
215 | atomic_long_t tlb_dropin_fail_no_exception; | 213 | atomic_long_t tlb_dropin_fail_no_exception; |
216 | atomic_long_t tlb_dropin_fail_no_exception_war; | ||
217 | atomic_long_t tfh_stale_on_fault; | 214 | atomic_long_t tfh_stale_on_fault; |
218 | atomic_long_t mmu_invalidate_range; | 215 | atomic_long_t mmu_invalidate_range; |
219 | atomic_long_t mmu_invalidate_page; | 216 | atomic_long_t mmu_invalidate_page; |
220 | atomic_long_t mmu_clear_flush_young; | ||
221 | atomic_long_t flush_tlb; | 217 | atomic_long_t flush_tlb; |
222 | atomic_long_t flush_tlb_gru; | 218 | atomic_long_t flush_tlb_gru; |
223 | atomic_long_t flush_tlb_gru_tgh; | 219 | atomic_long_t flush_tlb_gru_tgh; |
224 | atomic_long_t flush_tlb_gru_zero_asid; | 220 | atomic_long_t flush_tlb_gru_zero_asid; |
225 | 221 | ||
226 | atomic_long_t copy_gpa; | 222 | atomic_long_t copy_gpa; |
223 | atomic_long_t read_gpa; | ||
227 | 224 | ||
228 | atomic_long_t mesq_receive; | 225 | atomic_long_t mesq_receive; |
229 | atomic_long_t mesq_receive_none; | 226 | atomic_long_t mesq_receive_none; |
@@ -235,7 +232,7 @@ struct gru_stats_s { | |||
235 | atomic_long_t mesq_send_qlimit_reached; | 232 | atomic_long_t mesq_send_qlimit_reached; |
236 | atomic_long_t mesq_send_amo_nacked; | 233 | atomic_long_t mesq_send_amo_nacked; |
237 | atomic_long_t mesq_send_put_nacked; | 234 | atomic_long_t mesq_send_put_nacked; |
238 | atomic_long_t mesq_qf_not_full; | 235 | atomic_long_t mesq_page_overflow; |
239 | atomic_long_t mesq_qf_locked; | 236 | atomic_long_t mesq_qf_locked; |
240 | atomic_long_t mesq_qf_noop_not_full; | 237 | atomic_long_t mesq_qf_noop_not_full; |
241 | atomic_long_t mesq_qf_switch_head_failed; | 238 | atomic_long_t mesq_qf_switch_head_failed; |
@@ -245,11 +242,13 @@ struct gru_stats_s { | |||
245 | atomic_long_t mesq_noop_qlimit_reached; | 242 | atomic_long_t mesq_noop_qlimit_reached; |
246 | atomic_long_t mesq_noop_amo_nacked; | 243 | atomic_long_t mesq_noop_amo_nacked; |
247 | atomic_long_t mesq_noop_put_nacked; | 244 | atomic_long_t mesq_noop_put_nacked; |
245 | atomic_long_t mesq_noop_page_overflow; | ||
248 | 246 | ||
249 | }; | 247 | }; |
250 | 248 | ||
251 | enum mcs_op {cchop_allocate, cchop_start, cchop_interrupt, cchop_interrupt_sync, | 249 | enum mcs_op {cchop_allocate, cchop_start, cchop_interrupt, cchop_interrupt_sync, |
252 | cchop_deallocate, tghop_invalidate, mcsop_last}; | 250 | cchop_deallocate, tfhop_write_only, tfhop_write_restart, |
251 | tghop_invalidate, mcsop_last}; | ||
253 | 252 | ||
254 | struct mcs_op_statistic { | 253 | struct mcs_op_statistic { |
255 | atomic_long_t count; | 254 | atomic_long_t count; |
@@ -259,8 +258,8 @@ struct mcs_op_statistic { | |||
259 | 258 | ||
260 | extern struct mcs_op_statistic mcs_op_statistics[mcsop_last]; | 259 | extern struct mcs_op_statistic mcs_op_statistics[mcsop_last]; |
261 | 260 | ||
262 | #define OPT_DPRINT 1 | 261 | #define OPT_DPRINT 1 |
263 | #define OPT_STATS 2 | 262 | #define OPT_STATS 2 |
264 | 263 | ||
265 | 264 | ||
266 | #define IRQ_GRU 110 /* Starting IRQ number for interrupts */ | 265 | #define IRQ_GRU 110 /* Starting IRQ number for interrupts */ |
@@ -283,7 +282,7 @@ extern struct mcs_op_statistic mcs_op_statistics[mcsop_last]; | |||
283 | #define gru_dbg(dev, fmt, x...) \ | 282 | #define gru_dbg(dev, fmt, x...) \ |
284 | do { \ | 283 | do { \ |
285 | if (gru_options & OPT_DPRINT) \ | 284 | if (gru_options & OPT_DPRINT) \ |
286 | dev_dbg(dev, "%s: " fmt, __func__, x); \ | 285 | printk(KERN_DEBUG "GRU:%d %s: " fmt, smp_processor_id(), __func__, x);\ |
287 | } while (0) | 286 | } while (0) |
288 | #else | 287 | #else |
289 | #define gru_dbg(x...) | 288 | #define gru_dbg(x...) |
@@ -297,13 +296,7 @@ extern struct mcs_op_statistic mcs_op_statistics[mcsop_last]; | |||
297 | #define ASID_INC 8 /* number of regions */ | 296 | #define ASID_INC 8 /* number of regions */ |
298 | 297 | ||
299 | /* Generate a GRU asid value from a GRU base asid & a virtual address. */ | 298 | /* Generate a GRU asid value from a GRU base asid & a virtual address. */ |
300 | #if defined CONFIG_IA64 | ||
301 | #define VADDR_HI_BIT 64 | 299 | #define VADDR_HI_BIT 64 |
302 | #elif defined CONFIG_X86_64 | ||
303 | #define VADDR_HI_BIT 48 | ||
304 | #else | ||
305 | #error "Unsupported architecture" | ||
306 | #endif | ||
307 | #define GRUREGION(addr) ((addr) >> (VADDR_HI_BIT - 3) & 3) | 300 | #define GRUREGION(addr) ((addr) >> (VADDR_HI_BIT - 3) & 3) |
308 | #define GRUASID(asid, addr) ((asid) + GRUREGION(addr)) | 301 | #define GRUASID(asid, addr) ((asid) + GRUREGION(addr)) |
309 | 302 | ||
@@ -345,6 +338,7 @@ struct gru_vma_data { | |||
345 | long vd_user_options;/* misc user option flags */ | 338 | long vd_user_options;/* misc user option flags */ |
346 | int vd_cbr_au_count; | 339 | int vd_cbr_au_count; |
347 | int vd_dsr_au_count; | 340 | int vd_dsr_au_count; |
341 | unsigned char vd_tlb_preload_count; | ||
348 | }; | 342 | }; |
349 | 343 | ||
350 | /* | 344 | /* |
@@ -360,6 +354,7 @@ struct gru_thread_state { | |||
360 | struct gru_state *ts_gru; /* GRU where the context is | 354 | struct gru_state *ts_gru; /* GRU where the context is |
361 | loaded */ | 355 | loaded */ |
362 | struct gru_mm_struct *ts_gms; /* asid & ioproc struct */ | 356 | struct gru_mm_struct *ts_gms; /* asid & ioproc struct */ |
357 | unsigned char ts_tlb_preload_count; /* TLB preload pages */ | ||
363 | unsigned long ts_cbr_map; /* map of allocated CBRs */ | 358 | unsigned long ts_cbr_map; /* map of allocated CBRs */ |
364 | unsigned long ts_dsr_map; /* map of allocated DATA | 359 | unsigned long ts_dsr_map; /* map of allocated DATA |
365 | resources */ | 360 | resources */ |
@@ -368,6 +363,8 @@ struct gru_thread_state { | |||
368 | long ts_user_options;/* misc user option flags */ | 363 | long ts_user_options;/* misc user option flags */ |
369 | pid_t ts_tgid_owner; /* task that is using the | 364 | pid_t ts_tgid_owner; /* task that is using the |
370 | context - for migration */ | 365 | context - for migration */ |
366 | short ts_user_blade_id;/* user selected blade */ | ||
367 | char ts_user_chiplet_id;/* user selected chiplet */ | ||
371 | unsigned short ts_sizeavail; /* Pagesizes in use */ | 368 | unsigned short ts_sizeavail; /* Pagesizes in use */ |
372 | int ts_tsid; /* thread that owns the | 369 | int ts_tsid; /* thread that owns the |
373 | structure */ | 370 | structure */ |
@@ -384,13 +381,11 @@ struct gru_thread_state { | |||
384 | char ts_blade; /* If >= 0, migrate context if | 381 | char ts_blade; /* If >= 0, migrate context if |
385 | ref from diferent blade */ | 382 | ref from diferent blade */ |
386 | char ts_force_cch_reload; | 383 | char ts_force_cch_reload; |
387 | char ts_force_unload;/* force context to be unloaded | ||
388 | after migration */ | ||
389 | char ts_cbr_idx[GRU_CBR_AU];/* CBR numbers of each | 384 | char ts_cbr_idx[GRU_CBR_AU];/* CBR numbers of each |
390 | allocated CB */ | 385 | allocated CB */ |
391 | int ts_data_valid; /* Indicates if ts_gdata has | 386 | int ts_data_valid; /* Indicates if ts_gdata has |
392 | valid data */ | 387 | valid data */ |
393 | struct gts_statistics ustats; /* User statistics */ | 388 | struct gru_gseg_statistics ustats; /* User statistics */ |
394 | unsigned long ts_gdata[0]; /* save area for GRU data (CB, | 389 | unsigned long ts_gdata[0]; /* save area for GRU data (CB, |
395 | DS, CBE) */ | 390 | DS, CBE) */ |
396 | }; | 391 | }; |
@@ -422,6 +417,7 @@ struct gru_state { | |||
422 | gru segments (64) */ | 417 | gru segments (64) */ |
423 | unsigned short gs_gid; /* unique GRU number */ | 418 | unsigned short gs_gid; /* unique GRU number */ |
424 | unsigned short gs_blade_id; /* blade of GRU */ | 419 | unsigned short gs_blade_id; /* blade of GRU */ |
420 | unsigned char gs_chiplet_id; /* blade chiplet of GRU */ | ||
425 | unsigned char gs_tgh_local_shift; /* used to pick TGH for | 421 | unsigned char gs_tgh_local_shift; /* used to pick TGH for |
426 | local flush */ | 422 | local flush */ |
427 | unsigned char gs_tgh_first_remote; /* starting TGH# for | 423 | unsigned char gs_tgh_first_remote; /* starting TGH# for |
@@ -453,6 +449,7 @@ struct gru_state { | |||
453 | in use */ | 449 | in use */ |
454 | struct gru_thread_state *gs_gts[GRU_NUM_CCH]; /* GTS currently using | 450 | struct gru_thread_state *gs_gts[GRU_NUM_CCH]; /* GTS currently using |
455 | the context */ | 451 | the context */ |
452 | int gs_irq[GRU_NUM_TFM]; /* Interrupt irqs */ | ||
456 | }; | 453 | }; |
457 | 454 | ||
458 | /* | 455 | /* |
@@ -619,6 +616,15 @@ static inline int is_kernel_context(struct gru_thread_state *gts) | |||
619 | return !gts->ts_mm; | 616 | return !gts->ts_mm; |
620 | } | 617 | } |
621 | 618 | ||
619 | /* | ||
620 | * The following are for Nehelem-EX. A more general scheme is needed for | ||
621 | * future processors. | ||
622 | */ | ||
623 | #define UV_MAX_INT_CORES 8 | ||
624 | #define uv_cpu_socket_number(p) ((cpu_physical_id(p) >> 5) & 1) | ||
625 | #define uv_cpu_ht_number(p) (cpu_physical_id(p) & 1) | ||
626 | #define uv_cpu_core_number(p) (((cpu_physical_id(p) >> 2) & 4) | \ | ||
627 | ((cpu_physical_id(p) >> 1) & 3)) | ||
622 | /*----------------------------------------------------------------------------- | 628 | /*----------------------------------------------------------------------------- |
623 | * Function prototypes & externs | 629 | * Function prototypes & externs |
624 | */ | 630 | */ |
@@ -633,24 +639,26 @@ extern struct gru_thread_state *gru_find_thread_state(struct vm_area_struct | |||
633 | *vma, int tsid); | 639 | *vma, int tsid); |
634 | extern struct gru_thread_state *gru_alloc_thread_state(struct vm_area_struct | 640 | extern struct gru_thread_state *gru_alloc_thread_state(struct vm_area_struct |
635 | *vma, int tsid); | 641 | *vma, int tsid); |
636 | extern struct gru_state *gru_assign_gru_context(struct gru_thread_state *gts, | 642 | extern struct gru_state *gru_assign_gru_context(struct gru_thread_state *gts); |
637 | int blade); | ||
638 | extern void gru_load_context(struct gru_thread_state *gts); | 643 | extern void gru_load_context(struct gru_thread_state *gts); |
639 | extern void gru_steal_context(struct gru_thread_state *gts, int blade_id); | 644 | extern void gru_steal_context(struct gru_thread_state *gts); |
640 | extern void gru_unload_context(struct gru_thread_state *gts, int savestate); | 645 | extern void gru_unload_context(struct gru_thread_state *gts, int savestate); |
641 | extern int gru_update_cch(struct gru_thread_state *gts, int force_unload); | 646 | extern int gru_update_cch(struct gru_thread_state *gts); |
642 | extern void gts_drop(struct gru_thread_state *gts); | 647 | extern void gts_drop(struct gru_thread_state *gts); |
643 | extern void gru_tgh_flush_init(struct gru_state *gru); | 648 | extern void gru_tgh_flush_init(struct gru_state *gru); |
644 | extern int gru_kservices_init(void); | 649 | extern int gru_kservices_init(void); |
645 | extern void gru_kservices_exit(void); | 650 | extern void gru_kservices_exit(void); |
651 | extern irqreturn_t gru0_intr(int irq, void *dev_id); | ||
652 | extern irqreturn_t gru1_intr(int irq, void *dev_id); | ||
653 | extern irqreturn_t gru_intr_mblade(int irq, void *dev_id); | ||
646 | extern int gru_dump_chiplet_request(unsigned long arg); | 654 | extern int gru_dump_chiplet_request(unsigned long arg); |
647 | extern long gru_get_gseg_statistics(unsigned long arg); | 655 | extern long gru_get_gseg_statistics(unsigned long arg); |
648 | extern irqreturn_t gru_intr(int irq, void *dev_id); | ||
649 | extern int gru_handle_user_call_os(unsigned long address); | 656 | extern int gru_handle_user_call_os(unsigned long address); |
650 | extern int gru_user_flush_tlb(unsigned long arg); | 657 | extern int gru_user_flush_tlb(unsigned long arg); |
651 | extern int gru_user_unload_context(unsigned long arg); | 658 | extern int gru_user_unload_context(unsigned long arg); |
652 | extern int gru_get_exception_detail(unsigned long arg); | 659 | extern int gru_get_exception_detail(unsigned long arg); |
653 | extern int gru_set_context_option(unsigned long address); | 660 | extern int gru_set_context_option(unsigned long address); |
661 | extern void gru_check_context_placement(struct gru_thread_state *gts); | ||
654 | extern int gru_cpu_fault_map_id(void); | 662 | extern int gru_cpu_fault_map_id(void); |
655 | extern struct vm_area_struct *gru_find_vma(unsigned long vaddr); | 663 | extern struct vm_area_struct *gru_find_vma(unsigned long vaddr); |
656 | extern void gru_flush_all_tlb(struct gru_state *gru); | 664 | extern void gru_flush_all_tlb(struct gru_state *gru); |
@@ -658,7 +666,8 @@ extern int gru_proc_init(void); | |||
658 | extern void gru_proc_exit(void); | 666 | extern void gru_proc_exit(void); |
659 | 667 | ||
660 | extern struct gru_thread_state *gru_alloc_gts(struct vm_area_struct *vma, | 668 | extern struct gru_thread_state *gru_alloc_gts(struct vm_area_struct *vma, |
661 | int cbr_au_count, int dsr_au_count, int options, int tsid); | 669 | int cbr_au_count, int dsr_au_count, |
670 | unsigned char tlb_preload_count, int options, int tsid); | ||
662 | extern unsigned long gru_reserve_cb_resources(struct gru_state *gru, | 671 | extern unsigned long gru_reserve_cb_resources(struct gru_state *gru, |
663 | int cbr_au_count, char *cbmap); | 672 | int cbr_au_count, char *cbmap); |
664 | extern unsigned long gru_reserve_ds_resources(struct gru_state *gru, | 673 | extern unsigned long gru_reserve_ds_resources(struct gru_state *gru, |
diff --git a/drivers/misc/sgi-gru/grutlbpurge.c b/drivers/misc/sgi-gru/grutlbpurge.c index 1d125091f5e7..240a6d361665 100644 --- a/drivers/misc/sgi-gru/grutlbpurge.c +++ b/drivers/misc/sgi-gru/grutlbpurge.c | |||
@@ -184,8 +184,8 @@ void gru_flush_tlb_range(struct gru_mm_struct *gms, unsigned long start, | |||
184 | STAT(flush_tlb_gru_tgh); | 184 | STAT(flush_tlb_gru_tgh); |
185 | asid = GRUASID(asid, start); | 185 | asid = GRUASID(asid, start); |
186 | gru_dbg(grudev, | 186 | gru_dbg(grudev, |
187 | " FLUSH gruid %d, asid 0x%x, num %ld, cbmap 0x%x\n", | 187 | " FLUSH gruid %d, asid 0x%x, vaddr 0x%lx, vamask 0x%x, num %ld, cbmap 0x%x\n", |
188 | gid, asid, num, asids->mt_ctxbitmap); | 188 | gid, asid, start, grupagesize, num, asids->mt_ctxbitmap); |
189 | tgh = get_lock_tgh_handle(gru); | 189 | tgh = get_lock_tgh_handle(gru); |
190 | tgh_invalidate(tgh, start, ~0, asid, grupagesize, 0, | 190 | tgh_invalidate(tgh, start, ~0, asid, grupagesize, 0, |
191 | num - 1, asids->mt_ctxbitmap); | 191 | num - 1, asids->mt_ctxbitmap); |
@@ -299,6 +299,7 @@ struct gru_mm_struct *gru_register_mmu_notifier(void) | |||
299 | { | 299 | { |
300 | struct gru_mm_struct *gms; | 300 | struct gru_mm_struct *gms; |
301 | struct mmu_notifier *mn; | 301 | struct mmu_notifier *mn; |
302 | int err; | ||
302 | 303 | ||
303 | mn = mmu_find_ops(current->mm, &gru_mmuops); | 304 | mn = mmu_find_ops(current->mm, &gru_mmuops); |
304 | if (mn) { | 305 | if (mn) { |
@@ -307,16 +308,22 @@ struct gru_mm_struct *gru_register_mmu_notifier(void) | |||
307 | } else { | 308 | } else { |
308 | gms = kzalloc(sizeof(*gms), GFP_KERNEL); | 309 | gms = kzalloc(sizeof(*gms), GFP_KERNEL); |
309 | if (gms) { | 310 | if (gms) { |
311 | STAT(gms_alloc); | ||
310 | spin_lock_init(&gms->ms_asid_lock); | 312 | spin_lock_init(&gms->ms_asid_lock); |
311 | gms->ms_notifier.ops = &gru_mmuops; | 313 | gms->ms_notifier.ops = &gru_mmuops; |
312 | atomic_set(&gms->ms_refcnt, 1); | 314 | atomic_set(&gms->ms_refcnt, 1); |
313 | init_waitqueue_head(&gms->ms_wait_queue); | 315 | init_waitqueue_head(&gms->ms_wait_queue); |
314 | __mmu_notifier_register(&gms->ms_notifier, current->mm); | 316 | err = __mmu_notifier_register(&gms->ms_notifier, current->mm); |
317 | if (err) | ||
318 | goto error; | ||
315 | } | 319 | } |
316 | } | 320 | } |
317 | gru_dbg(grudev, "gms %p, refcnt %d\n", gms, | 321 | gru_dbg(grudev, "gms %p, refcnt %d\n", gms, |
318 | atomic_read(&gms->ms_refcnt)); | 322 | atomic_read(&gms->ms_refcnt)); |
319 | return gms; | 323 | return gms; |
324 | error: | ||
325 | kfree(gms); | ||
326 | return ERR_PTR(err); | ||
320 | } | 327 | } |
321 | 328 | ||
322 | void gru_drop_mmu_notifier(struct gru_mm_struct *gms) | 329 | void gru_drop_mmu_notifier(struct gru_mm_struct *gms) |
@@ -327,6 +334,7 @@ void gru_drop_mmu_notifier(struct gru_mm_struct *gms) | |||
327 | if (!gms->ms_released) | 334 | if (!gms->ms_released) |
328 | mmu_notifier_unregister(&gms->ms_notifier, current->mm); | 335 | mmu_notifier_unregister(&gms->ms_notifier, current->mm); |
329 | kfree(gms); | 336 | kfree(gms); |
337 | STAT(gms_free); | ||
330 | } | 338 | } |
331 | } | 339 | } |
332 | 340 | ||
diff --git a/drivers/misc/sgi-xp/xp.h b/drivers/misc/sgi-xp/xp.h index 2275126cb334..851b2f25ce0e 100644 --- a/drivers/misc/sgi-xp/xp.h +++ b/drivers/misc/sgi-xp/xp.h | |||
@@ -339,6 +339,7 @@ extern short xp_partition_id; | |||
339 | extern u8 xp_region_size; | 339 | extern u8 xp_region_size; |
340 | 340 | ||
341 | extern unsigned long (*xp_pa) (void *); | 341 | extern unsigned long (*xp_pa) (void *); |
342 | extern unsigned long (*xp_socket_pa) (unsigned long); | ||
342 | extern enum xp_retval (*xp_remote_memcpy) (unsigned long, const unsigned long, | 343 | extern enum xp_retval (*xp_remote_memcpy) (unsigned long, const unsigned long, |
343 | size_t); | 344 | size_t); |
344 | extern int (*xp_cpu_to_nasid) (int); | 345 | extern int (*xp_cpu_to_nasid) (int); |
diff --git a/drivers/misc/sgi-xp/xp_main.c b/drivers/misc/sgi-xp/xp_main.c index 7896849b16dc..01be66d02ca8 100644 --- a/drivers/misc/sgi-xp/xp_main.c +++ b/drivers/misc/sgi-xp/xp_main.c | |||
@@ -44,6 +44,9 @@ EXPORT_SYMBOL_GPL(xp_region_size); | |||
44 | unsigned long (*xp_pa) (void *addr); | 44 | unsigned long (*xp_pa) (void *addr); |
45 | EXPORT_SYMBOL_GPL(xp_pa); | 45 | EXPORT_SYMBOL_GPL(xp_pa); |
46 | 46 | ||
47 | unsigned long (*xp_socket_pa) (unsigned long gpa); | ||
48 | EXPORT_SYMBOL_GPL(xp_socket_pa); | ||
49 | |||
47 | enum xp_retval (*xp_remote_memcpy) (unsigned long dst_gpa, | 50 | enum xp_retval (*xp_remote_memcpy) (unsigned long dst_gpa, |
48 | const unsigned long src_gpa, size_t len); | 51 | const unsigned long src_gpa, size_t len); |
49 | EXPORT_SYMBOL_GPL(xp_remote_memcpy); | 52 | EXPORT_SYMBOL_GPL(xp_remote_memcpy); |
diff --git a/drivers/misc/sgi-xp/xp_sn2.c b/drivers/misc/sgi-xp/xp_sn2.c index fb3ec9d735a9..d8e463f87241 100644 --- a/drivers/misc/sgi-xp/xp_sn2.c +++ b/drivers/misc/sgi-xp/xp_sn2.c | |||
@@ -84,6 +84,15 @@ xp_pa_sn2(void *addr) | |||
84 | } | 84 | } |
85 | 85 | ||
86 | /* | 86 | /* |
87 | * Convert a global physical to a socket physical address. | ||
88 | */ | ||
89 | static unsigned long | ||
90 | xp_socket_pa_sn2(unsigned long gpa) | ||
91 | { | ||
92 | return gpa; | ||
93 | } | ||
94 | |||
95 | /* | ||
87 | * Wrapper for bte_copy(). | 96 | * Wrapper for bte_copy(). |
88 | * | 97 | * |
89 | * dst_pa - physical address of the destination of the transfer. | 98 | * dst_pa - physical address of the destination of the transfer. |
@@ -162,6 +171,7 @@ xp_init_sn2(void) | |||
162 | xp_region_size = sn_region_size; | 171 | xp_region_size = sn_region_size; |
163 | 172 | ||
164 | xp_pa = xp_pa_sn2; | 173 | xp_pa = xp_pa_sn2; |
174 | xp_socket_pa = xp_socket_pa_sn2; | ||
165 | xp_remote_memcpy = xp_remote_memcpy_sn2; | 175 | xp_remote_memcpy = xp_remote_memcpy_sn2; |
166 | xp_cpu_to_nasid = xp_cpu_to_nasid_sn2; | 176 | xp_cpu_to_nasid = xp_cpu_to_nasid_sn2; |
167 | xp_expand_memprotect = xp_expand_memprotect_sn2; | 177 | xp_expand_memprotect = xp_expand_memprotect_sn2; |
diff --git a/drivers/misc/sgi-xp/xp_uv.c b/drivers/misc/sgi-xp/xp_uv.c index d238576b26fa..a0d093274dc0 100644 --- a/drivers/misc/sgi-xp/xp_uv.c +++ b/drivers/misc/sgi-xp/xp_uv.c | |||
@@ -32,12 +32,44 @@ xp_pa_uv(void *addr) | |||
32 | return uv_gpa(addr); | 32 | return uv_gpa(addr); |
33 | } | 33 | } |
34 | 34 | ||
35 | /* | ||
36 | * Convert a global physical to socket physical address. | ||
37 | */ | ||
38 | static unsigned long | ||
39 | xp_socket_pa_uv(unsigned long gpa) | ||
40 | { | ||
41 | return uv_gpa_to_soc_phys_ram(gpa); | ||
42 | } | ||
43 | |||
44 | static enum xp_retval | ||
45 | xp_remote_mmr_read(unsigned long dst_gpa, const unsigned long src_gpa, | ||
46 | size_t len) | ||
47 | { | ||
48 | int ret; | ||
49 | unsigned long *dst_va = __va(uv_gpa_to_soc_phys_ram(dst_gpa)); | ||
50 | |||
51 | BUG_ON(!uv_gpa_in_mmr_space(src_gpa)); | ||
52 | BUG_ON(len != 8); | ||
53 | |||
54 | ret = gru_read_gpa(dst_va, src_gpa); | ||
55 | if (ret == 0) | ||
56 | return xpSuccess; | ||
57 | |||
58 | dev_err(xp, "gru_read_gpa() failed, dst_gpa=0x%016lx src_gpa=0x%016lx " | ||
59 | "len=%ld\n", dst_gpa, src_gpa, len); | ||
60 | return xpGruCopyError; | ||
61 | } | ||
62 | |||
63 | |||
35 | static enum xp_retval | 64 | static enum xp_retval |
36 | xp_remote_memcpy_uv(unsigned long dst_gpa, const unsigned long src_gpa, | 65 | xp_remote_memcpy_uv(unsigned long dst_gpa, const unsigned long src_gpa, |
37 | size_t len) | 66 | size_t len) |
38 | { | 67 | { |
39 | int ret; | 68 | int ret; |
40 | 69 | ||
70 | if (uv_gpa_in_mmr_space(src_gpa)) | ||
71 | return xp_remote_mmr_read(dst_gpa, src_gpa, len); | ||
72 | |||
41 | ret = gru_copy_gpa(dst_gpa, src_gpa, len); | 73 | ret = gru_copy_gpa(dst_gpa, src_gpa, len); |
42 | if (ret == 0) | 74 | if (ret == 0) |
43 | return xpSuccess; | 75 | return xpSuccess; |
@@ -123,6 +155,7 @@ xp_init_uv(void) | |||
123 | xp_region_size = sn_region_size; | 155 | xp_region_size = sn_region_size; |
124 | 156 | ||
125 | xp_pa = xp_pa_uv; | 157 | xp_pa = xp_pa_uv; |
158 | xp_socket_pa = xp_socket_pa_uv; | ||
126 | xp_remote_memcpy = xp_remote_memcpy_uv; | 159 | xp_remote_memcpy = xp_remote_memcpy_uv; |
127 | xp_cpu_to_nasid = xp_cpu_to_nasid_uv; | 160 | xp_cpu_to_nasid = xp_cpu_to_nasid_uv; |
128 | xp_expand_memprotect = xp_expand_memprotect_uv; | 161 | xp_expand_memprotect = xp_expand_memprotect_uv; |
diff --git a/drivers/misc/sgi-xp/xpc_main.c b/drivers/misc/sgi-xp/xpc_main.c index fd3688a3e23f..832ed4c88cf7 100644 --- a/drivers/misc/sgi-xp/xpc_main.c +++ b/drivers/misc/sgi-xp/xpc_main.c | |||
@@ -89,48 +89,40 @@ static int xpc_disengage_max_timelimit = 120; | |||
89 | 89 | ||
90 | static ctl_table xpc_sys_xpc_hb_dir[] = { | 90 | static ctl_table xpc_sys_xpc_hb_dir[] = { |
91 | { | 91 | { |
92 | .ctl_name = CTL_UNNUMBERED, | ||
93 | .procname = "hb_interval", | 92 | .procname = "hb_interval", |
94 | .data = &xpc_hb_interval, | 93 | .data = &xpc_hb_interval, |
95 | .maxlen = sizeof(int), | 94 | .maxlen = sizeof(int), |
96 | .mode = 0644, | 95 | .mode = 0644, |
97 | .proc_handler = &proc_dointvec_minmax, | 96 | .proc_handler = proc_dointvec_minmax, |
98 | .strategy = &sysctl_intvec, | ||
99 | .extra1 = &xpc_hb_min_interval, | 97 | .extra1 = &xpc_hb_min_interval, |
100 | .extra2 = &xpc_hb_max_interval}, | 98 | .extra2 = &xpc_hb_max_interval}, |
101 | { | 99 | { |
102 | .ctl_name = CTL_UNNUMBERED, | ||
103 | .procname = "hb_check_interval", | 100 | .procname = "hb_check_interval", |
104 | .data = &xpc_hb_check_interval, | 101 | .data = &xpc_hb_check_interval, |
105 | .maxlen = sizeof(int), | 102 | .maxlen = sizeof(int), |
106 | .mode = 0644, | 103 | .mode = 0644, |
107 | .proc_handler = &proc_dointvec_minmax, | 104 | .proc_handler = proc_dointvec_minmax, |
108 | .strategy = &sysctl_intvec, | ||
109 | .extra1 = &xpc_hb_check_min_interval, | 105 | .extra1 = &xpc_hb_check_min_interval, |
110 | .extra2 = &xpc_hb_check_max_interval}, | 106 | .extra2 = &xpc_hb_check_max_interval}, |
111 | {} | 107 | {} |
112 | }; | 108 | }; |
113 | static ctl_table xpc_sys_xpc_dir[] = { | 109 | static ctl_table xpc_sys_xpc_dir[] = { |
114 | { | 110 | { |
115 | .ctl_name = CTL_UNNUMBERED, | ||
116 | .procname = "hb", | 111 | .procname = "hb", |
117 | .mode = 0555, | 112 | .mode = 0555, |
118 | .child = xpc_sys_xpc_hb_dir}, | 113 | .child = xpc_sys_xpc_hb_dir}, |
119 | { | 114 | { |
120 | .ctl_name = CTL_UNNUMBERED, | ||
121 | .procname = "disengage_timelimit", | 115 | .procname = "disengage_timelimit", |
122 | .data = &xpc_disengage_timelimit, | 116 | .data = &xpc_disengage_timelimit, |
123 | .maxlen = sizeof(int), | 117 | .maxlen = sizeof(int), |
124 | .mode = 0644, | 118 | .mode = 0644, |
125 | .proc_handler = &proc_dointvec_minmax, | 119 | .proc_handler = proc_dointvec_minmax, |
126 | .strategy = &sysctl_intvec, | ||
127 | .extra1 = &xpc_disengage_min_timelimit, | 120 | .extra1 = &xpc_disengage_min_timelimit, |
128 | .extra2 = &xpc_disengage_max_timelimit}, | 121 | .extra2 = &xpc_disengage_max_timelimit}, |
129 | {} | 122 | {} |
130 | }; | 123 | }; |
131 | static ctl_table xpc_sys_dir[] = { | 124 | static ctl_table xpc_sys_dir[] = { |
132 | { | 125 | { |
133 | .ctl_name = CTL_UNNUMBERED, | ||
134 | .procname = "xpc", | 126 | .procname = "xpc", |
135 | .mode = 0555, | 127 | .mode = 0555, |
136 | .child = xpc_sys_xpc_dir}, | 128 | .child = xpc_sys_xpc_dir}, |
diff --git a/drivers/misc/sgi-xp/xpc_partition.c b/drivers/misc/sgi-xp/xpc_partition.c index 65877bc5edaa..9a6268c89fdd 100644 --- a/drivers/misc/sgi-xp/xpc_partition.c +++ b/drivers/misc/sgi-xp/xpc_partition.c | |||
@@ -18,6 +18,7 @@ | |||
18 | #include <linux/device.h> | 18 | #include <linux/device.h> |
19 | #include <linux/hardirq.h> | 19 | #include <linux/hardirq.h> |
20 | #include "xpc.h" | 20 | #include "xpc.h" |
21 | #include <asm/uv/uv_hub.h> | ||
21 | 22 | ||
22 | /* XPC is exiting flag */ | 23 | /* XPC is exiting flag */ |
23 | int xpc_exiting; | 24 | int xpc_exiting; |
@@ -92,8 +93,12 @@ xpc_get_rsvd_page_pa(int nasid) | |||
92 | break; | 93 | break; |
93 | 94 | ||
94 | /* !!! L1_CACHE_ALIGN() is only a sn2-bte_copy requirement */ | 95 | /* !!! L1_CACHE_ALIGN() is only a sn2-bte_copy requirement */ |
95 | if (L1_CACHE_ALIGN(len) > buf_len) { | 96 | if (is_shub()) |
96 | kfree(buf_base); | 97 | len = L1_CACHE_ALIGN(len); |
98 | |||
99 | if (len > buf_len) { | ||
100 | if (buf_base != NULL) | ||
101 | kfree(buf_base); | ||
97 | buf_len = L1_CACHE_ALIGN(len); | 102 | buf_len = L1_CACHE_ALIGN(len); |
98 | buf = xpc_kmalloc_cacheline_aligned(buf_len, GFP_KERNEL, | 103 | buf = xpc_kmalloc_cacheline_aligned(buf_len, GFP_KERNEL, |
99 | &buf_base); | 104 | &buf_base); |
@@ -105,7 +110,7 @@ xpc_get_rsvd_page_pa(int nasid) | |||
105 | } | 110 | } |
106 | } | 111 | } |
107 | 112 | ||
108 | ret = xp_remote_memcpy(xp_pa(buf), rp_pa, buf_len); | 113 | ret = xp_remote_memcpy(xp_pa(buf), rp_pa, len); |
109 | if (ret != xpSuccess) { | 114 | if (ret != xpSuccess) { |
110 | dev_dbg(xpc_part, "xp_remote_memcpy failed %d\n", ret); | 115 | dev_dbg(xpc_part, "xp_remote_memcpy failed %d\n", ret); |
111 | break; | 116 | break; |
@@ -143,7 +148,7 @@ xpc_setup_rsvd_page(void) | |||
143 | dev_err(xpc_part, "SAL failed to locate the reserved page\n"); | 148 | dev_err(xpc_part, "SAL failed to locate the reserved page\n"); |
144 | return -ESRCH; | 149 | return -ESRCH; |
145 | } | 150 | } |
146 | rp = (struct xpc_rsvd_page *)__va(rp_pa); | 151 | rp = (struct xpc_rsvd_page *)__va(xp_socket_pa(rp_pa)); |
147 | 152 | ||
148 | if (rp->SAL_version < 3) { | 153 | if (rp->SAL_version < 3) { |
149 | /* SAL_versions < 3 had a SAL_partid defined as a u8 */ | 154 | /* SAL_versions < 3 had a SAL_partid defined as a u8 */ |
diff --git a/drivers/misc/sgi-xp/xpc_uv.c b/drivers/misc/sgi-xp/xpc_uv.c index c76677afda1b..8725d5e8ab0c 100644 --- a/drivers/misc/sgi-xp/xpc_uv.c +++ b/drivers/misc/sgi-xp/xpc_uv.c | |||
@@ -106,7 +106,8 @@ xpc_get_gru_mq_irq_uv(struct xpc_gru_mq_uv *mq, int cpu, char *irq_name) | |||
106 | int mmr_pnode = uv_blade_to_pnode(mq->mmr_blade); | 106 | int mmr_pnode = uv_blade_to_pnode(mq->mmr_blade); |
107 | 107 | ||
108 | #if defined CONFIG_X86_64 | 108 | #if defined CONFIG_X86_64 |
109 | mq->irq = uv_setup_irq(irq_name, cpu, mq->mmr_blade, mq->mmr_offset); | 109 | mq->irq = uv_setup_irq(irq_name, cpu, mq->mmr_blade, mq->mmr_offset, |
110 | UV_AFFINITY_CPU); | ||
110 | if (mq->irq < 0) { | 111 | if (mq->irq < 0) { |
111 | dev_err(xpc_part, "uv_setup_irq() returned error=%d\n", | 112 | dev_err(xpc_part, "uv_setup_irq() returned error=%d\n", |
112 | -mq->irq); | 113 | -mq->irq); |
@@ -136,7 +137,7 @@ static void | |||
136 | xpc_release_gru_mq_irq_uv(struct xpc_gru_mq_uv *mq) | 137 | xpc_release_gru_mq_irq_uv(struct xpc_gru_mq_uv *mq) |
137 | { | 138 | { |
138 | #if defined CONFIG_X86_64 | 139 | #if defined CONFIG_X86_64 |
139 | uv_teardown_irq(mq->irq, mq->mmr_blade, mq->mmr_offset); | 140 | uv_teardown_irq(mq->irq); |
140 | 141 | ||
141 | #elif defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV | 142 | #elif defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV |
142 | int mmr_pnode; | 143 | int mmr_pnode; |
@@ -156,22 +157,24 @@ xpc_gru_mq_watchlist_alloc_uv(struct xpc_gru_mq_uv *mq) | |||
156 | { | 157 | { |
157 | int ret; | 158 | int ret; |
158 | 159 | ||
159 | #if defined CONFIG_X86_64 | 160 | #if defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV |
160 | ret = uv_bios_mq_watchlist_alloc(mq->mmr_blade, uv_gpa(mq->address), | 161 | int mmr_pnode = uv_blade_to_pnode(mq->mmr_blade); |
161 | mq->order, &mq->mmr_offset); | 162 | |
162 | if (ret < 0) { | 163 | ret = sn_mq_watchlist_alloc(mmr_pnode, (void *)uv_gpa(mq->address), |
163 | dev_err(xpc_part, "uv_bios_mq_watchlist_alloc() failed, " | ||
164 | "ret=%d\n", ret); | ||
165 | return ret; | ||
166 | } | ||
167 | #elif defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV | ||
168 | ret = sn_mq_watchlist_alloc(mq->mmr_blade, (void *)uv_gpa(mq->address), | ||
169 | mq->order, &mq->mmr_offset); | 164 | mq->order, &mq->mmr_offset); |
170 | if (ret < 0) { | 165 | if (ret < 0) { |
171 | dev_err(xpc_part, "sn_mq_watchlist_alloc() failed, ret=%d\n", | 166 | dev_err(xpc_part, "sn_mq_watchlist_alloc() failed, ret=%d\n", |
172 | ret); | 167 | ret); |
173 | return -EBUSY; | 168 | return -EBUSY; |
174 | } | 169 | } |
170 | #elif defined CONFIG_X86_64 | ||
171 | ret = uv_bios_mq_watchlist_alloc(uv_gpa(mq->address), | ||
172 | mq->order, &mq->mmr_offset); | ||
173 | if (ret < 0) { | ||
174 | dev_err(xpc_part, "uv_bios_mq_watchlist_alloc() failed, " | ||
175 | "ret=%d\n", ret); | ||
176 | return ret; | ||
177 | } | ||
175 | #else | 178 | #else |
176 | #error not a supported configuration | 179 | #error not a supported configuration |
177 | #endif | 180 | #endif |
@@ -184,12 +187,13 @@ static void | |||
184 | xpc_gru_mq_watchlist_free_uv(struct xpc_gru_mq_uv *mq) | 187 | xpc_gru_mq_watchlist_free_uv(struct xpc_gru_mq_uv *mq) |
185 | { | 188 | { |
186 | int ret; | 189 | int ret; |
190 | int mmr_pnode = uv_blade_to_pnode(mq->mmr_blade); | ||
187 | 191 | ||
188 | #if defined CONFIG_X86_64 | 192 | #if defined CONFIG_X86_64 |
189 | ret = uv_bios_mq_watchlist_free(mq->mmr_blade, mq->watchlist_num); | 193 | ret = uv_bios_mq_watchlist_free(mmr_pnode, mq->watchlist_num); |
190 | BUG_ON(ret != BIOS_STATUS_SUCCESS); | 194 | BUG_ON(ret != BIOS_STATUS_SUCCESS); |
191 | #elif defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV | 195 | #elif defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV |
192 | ret = sn_mq_watchlist_free(mq->mmr_blade, mq->watchlist_num); | 196 | ret = sn_mq_watchlist_free(mmr_pnode, mq->watchlist_num); |
193 | BUG_ON(ret != SALRET_OK); | 197 | BUG_ON(ret != SALRET_OK); |
194 | #else | 198 | #else |
195 | #error not a supported configuration | 199 | #error not a supported configuration |
@@ -203,6 +207,7 @@ xpc_create_gru_mq_uv(unsigned int mq_size, int cpu, char *irq_name, | |||
203 | enum xp_retval xp_ret; | 207 | enum xp_retval xp_ret; |
204 | int ret; | 208 | int ret; |
205 | int nid; | 209 | int nid; |
210 | int nasid; | ||
206 | int pg_order; | 211 | int pg_order; |
207 | struct page *page; | 212 | struct page *page; |
208 | struct xpc_gru_mq_uv *mq; | 213 | struct xpc_gru_mq_uv *mq; |
@@ -258,9 +263,11 @@ xpc_create_gru_mq_uv(unsigned int mq_size, int cpu, char *irq_name, | |||
258 | goto out_5; | 263 | goto out_5; |
259 | } | 264 | } |
260 | 265 | ||
266 | nasid = UV_PNODE_TO_NASID(uv_cpu_to_pnode(cpu)); | ||
267 | |||
261 | mmr_value = (struct uv_IO_APIC_route_entry *)&mq->mmr_value; | 268 | mmr_value = (struct uv_IO_APIC_route_entry *)&mq->mmr_value; |
262 | ret = gru_create_message_queue(mq->gru_mq_desc, mq->address, mq_size, | 269 | ret = gru_create_message_queue(mq->gru_mq_desc, mq->address, mq_size, |
263 | nid, mmr_value->vector, mmr_value->dest); | 270 | nasid, mmr_value->vector, mmr_value->dest); |
264 | if (ret != 0) { | 271 | if (ret != 0) { |
265 | dev_err(xpc_part, "gru_create_message_queue() returned " | 272 | dev_err(xpc_part, "gru_create_message_queue() returned " |
266 | "error=%d\n", ret); | 273 | "error=%d\n", ret); |
@@ -945,11 +952,13 @@ xpc_get_fifo_entry_uv(struct xpc_fifo_head_uv *head) | |||
945 | head->first = first->next; | 952 | head->first = first->next; |
946 | if (head->first == NULL) | 953 | if (head->first == NULL) |
947 | head->last = NULL; | 954 | head->last = NULL; |
955 | |||
956 | head->n_entries--; | ||
957 | BUG_ON(head->n_entries < 0); | ||
958 | |||
959 | first->next = NULL; | ||
948 | } | 960 | } |
949 | head->n_entries--; | ||
950 | BUG_ON(head->n_entries < 0); | ||
951 | spin_unlock_irqrestore(&head->lock, irq_flags); | 961 | spin_unlock_irqrestore(&head->lock, irq_flags); |
952 | first->next = NULL; | ||
953 | return first; | 962 | return first; |
954 | } | 963 | } |
955 | 964 | ||
@@ -1018,7 +1027,8 @@ xpc_make_first_contact_uv(struct xpc_partition *part) | |||
1018 | xpc_send_activate_IRQ_part_uv(part, &msg, sizeof(msg), | 1027 | xpc_send_activate_IRQ_part_uv(part, &msg, sizeof(msg), |
1019 | XPC_ACTIVATE_MQ_MSG_SYNC_ACT_STATE_UV); | 1028 | XPC_ACTIVATE_MQ_MSG_SYNC_ACT_STATE_UV); |
1020 | 1029 | ||
1021 | while (part->sn.uv.remote_act_state != XPC_P_AS_ACTIVATING) { | 1030 | while (!((part->sn.uv.remote_act_state == XPC_P_AS_ACTIVATING) || |
1031 | (part->sn.uv.remote_act_state == XPC_P_AS_ACTIVE))) { | ||
1022 | 1032 | ||
1023 | dev_dbg(xpc_part, "waiting to make first contact with " | 1033 | dev_dbg(xpc_part, "waiting to make first contact with " |
1024 | "partition %d\n", XPC_PARTID(part)); | 1034 | "partition %d\n", XPC_PARTID(part)); |
@@ -1421,7 +1431,6 @@ xpc_handle_notify_mq_msg_uv(struct xpc_partition *part, | |||
1421 | msg_slot = ch_uv->recv_msg_slots + | 1431 | msg_slot = ch_uv->recv_msg_slots + |
1422 | (msg->hdr.msg_slot_number % ch->remote_nentries) * ch->entry_size; | 1432 | (msg->hdr.msg_slot_number % ch->remote_nentries) * ch->entry_size; |
1423 | 1433 | ||
1424 | BUG_ON(msg->hdr.msg_slot_number != msg_slot->hdr.msg_slot_number); | ||
1425 | BUG_ON(msg_slot->hdr.size != 0); | 1434 | BUG_ON(msg_slot->hdr.size != 0); |
1426 | 1435 | ||
1427 | memcpy(msg_slot, msg, msg->hdr.size); | 1436 | memcpy(msg_slot, msg, msg->hdr.size); |
@@ -1645,8 +1654,6 @@ xpc_received_payload_uv(struct xpc_channel *ch, void *payload) | |||
1645 | sizeof(struct xpc_notify_mq_msghdr_uv)); | 1654 | sizeof(struct xpc_notify_mq_msghdr_uv)); |
1646 | if (ret != xpSuccess) | 1655 | if (ret != xpSuccess) |
1647 | XPC_DEACTIVATE_PARTITION(&xpc_partitions[ch->partid], ret); | 1656 | XPC_DEACTIVATE_PARTITION(&xpc_partitions[ch->partid], ret); |
1648 | |||
1649 | msg->hdr.msg_slot_number += ch->remote_nentries; | ||
1650 | } | 1657 | } |
1651 | 1658 | ||
1652 | static struct xpc_arch_operations xpc_arch_ops_uv = { | 1659 | static struct xpc_arch_operations xpc_arch_ops_uv = { |
diff --git a/drivers/misc/ti_dac7512.c b/drivers/misc/ti_dac7512.c new file mode 100644 index 000000000000..d3f229a3a77e --- /dev/null +++ b/drivers/misc/ti_dac7512.c | |||
@@ -0,0 +1,101 @@ | |||
1 | /* | ||
2 | * dac7512.c - Linux kernel module for | ||
3 | * Texas Instruments DAC7512 | ||
4 | * | ||
5 | * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
20 | */ | ||
21 | |||
22 | #include <linux/module.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/spi/spi.h> | ||
25 | |||
26 | #define DAC7512_DRV_NAME "dac7512" | ||
27 | #define DRIVER_VERSION "1.0" | ||
28 | |||
29 | static ssize_t dac7512_store_val(struct device *dev, | ||
30 | struct device_attribute *attr, | ||
31 | const char *buf, size_t count) | ||
32 | { | ||
33 | struct spi_device *spi = to_spi_device(dev); | ||
34 | unsigned char tmp[2]; | ||
35 | unsigned long val; | ||
36 | |||
37 | if (strict_strtoul(buf, 10, &val) < 0) | ||
38 | return -EINVAL; | ||
39 | |||
40 | tmp[0] = val >> 8; | ||
41 | tmp[1] = val & 0xff; | ||
42 | spi_write(spi, tmp, sizeof(tmp)); | ||
43 | return count; | ||
44 | } | ||
45 | |||
46 | static DEVICE_ATTR(value, S_IWUSR, NULL, dac7512_store_val); | ||
47 | |||
48 | static struct attribute *dac7512_attributes[] = { | ||
49 | &dev_attr_value.attr, | ||
50 | NULL | ||
51 | }; | ||
52 | |||
53 | static const struct attribute_group dac7512_attr_group = { | ||
54 | .attrs = dac7512_attributes, | ||
55 | }; | ||
56 | |||
57 | static int __devinit dac7512_probe(struct spi_device *spi) | ||
58 | { | ||
59 | int ret; | ||
60 | |||
61 | spi->bits_per_word = 8; | ||
62 | spi->mode = SPI_MODE_0; | ||
63 | ret = spi_setup(spi); | ||
64 | if (ret < 0) | ||
65 | return ret; | ||
66 | |||
67 | return sysfs_create_group(&spi->dev.kobj, &dac7512_attr_group); | ||
68 | } | ||
69 | |||
70 | static int __devexit dac7512_remove(struct spi_device *spi) | ||
71 | { | ||
72 | sysfs_remove_group(&spi->dev.kobj, &dac7512_attr_group); | ||
73 | return 0; | ||
74 | } | ||
75 | |||
76 | static struct spi_driver dac7512_driver = { | ||
77 | .driver = { | ||
78 | .name = DAC7512_DRV_NAME, | ||
79 | .owner = THIS_MODULE, | ||
80 | }, | ||
81 | .probe = dac7512_probe, | ||
82 | .remove = __devexit_p(dac7512_remove), | ||
83 | }; | ||
84 | |||
85 | static int __init dac7512_init(void) | ||
86 | { | ||
87 | return spi_register_driver(&dac7512_driver); | ||
88 | } | ||
89 | |||
90 | static void __exit dac7512_exit(void) | ||
91 | { | ||
92 | spi_unregister_driver(&dac7512_driver); | ||
93 | } | ||
94 | |||
95 | MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>"); | ||
96 | MODULE_DESCRIPTION("DAC7512 16-bit DAC"); | ||
97 | MODULE_LICENSE("GPL v2"); | ||
98 | MODULE_VERSION(DRIVER_VERSION); | ||
99 | |||
100 | module_init(dac7512_init); | ||
101 | module_exit(dac7512_exit); | ||