Merge branch 'linus' into release
This commit is contained in:
@@ -498,6 +498,18 @@ config SGI_GRU_DEBUG
|
||||
This option enables addition debugging code for the SGI GRU driver. If
|
||||
you are unsure, say N.
|
||||
|
||||
config DELL_LAPTOP
|
||||
tristate "Dell Laptop Extras (EXPERIMENTAL)"
|
||||
depends on X86
|
||||
depends on DCDBAS
|
||||
depends on EXPERIMENTAL
|
||||
depends on BACKLIGHT_CLASS_DEVICE
|
||||
depends on RFKILL
|
||||
default n
|
||||
---help---
|
||||
This driver adds support for rfkill and backlight control to Dell
|
||||
laptops.
|
||||
|
||||
source "drivers/misc/c2port/Kconfig"
|
||||
|
||||
endif # MISC_DEVICES
|
||||
|
@@ -18,6 +18,7 @@ obj-$(CONFIG_ICS932S401) += ics932s401.o
|
||||
obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o
|
||||
obj-$(CONFIG_LKDTM) += lkdtm.o
|
||||
obj-$(CONFIG_TIFM_CORE) += tifm_core.o
|
||||
obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o
|
||||
obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o
|
||||
obj-$(CONFIG_PHANTOM) += phantom.o
|
||||
obj-$(CONFIG_SGI_IOC4) += ioc4.o
|
||||
|
436
drivers/misc/dell-laptop.c
Normal file
436
drivers/misc/dell-laptop.c
Normal file
@@ -0,0 +1,436 @@
|
||||
/*
|
||||
* Driver for Dell laptop extras
|
||||
*
|
||||
* Copyright (c) Red Hat <mjg@redhat.com>
|
||||
*
|
||||
* Based on documentation in the libsmbios package, Copyright (C) 2005 Dell
|
||||
* Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/backlight.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/rfkill.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/acpi.h>
|
||||
#include "../firmware/dcdbas.h"
|
||||
|
||||
#define BRIGHTNESS_TOKEN 0x7d
|
||||
|
||||
/* This structure will be modified by the firmware when we enter
|
||||
* system management mode, hence the volatiles */
|
||||
|
||||
struct calling_interface_buffer {
|
||||
u16 class;
|
||||
u16 select;
|
||||
volatile u32 input[4];
|
||||
volatile u32 output[4];
|
||||
} __packed;
|
||||
|
||||
struct calling_interface_token {
|
||||
u16 tokenID;
|
||||
u16 location;
|
||||
union {
|
||||
u16 value;
|
||||
u16 stringlength;
|
||||
};
|
||||
};
|
||||
|
||||
struct calling_interface_structure {
|
||||
struct dmi_header header;
|
||||
u16 cmdIOAddress;
|
||||
u8 cmdIOCode;
|
||||
u32 supportedCmds;
|
||||
struct calling_interface_token tokens[];
|
||||
} __packed;
|
||||
|
||||
static int da_command_address;
|
||||
static int da_command_code;
|
||||
static int da_num_tokens;
|
||||
static struct calling_interface_token *da_tokens;
|
||||
|
||||
static struct backlight_device *dell_backlight_device;
|
||||
static struct rfkill *wifi_rfkill;
|
||||
static struct rfkill *bluetooth_rfkill;
|
||||
static struct rfkill *wwan_rfkill;
|
||||
|
||||
static const struct dmi_system_id __initdata dell_device_table[] = {
|
||||
{
|
||||
.ident = "Dell laptop",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
||||
DMI_MATCH(DMI_CHASSIS_TYPE, "8"),
|
||||
},
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
static void parse_da_table(const struct dmi_header *dm)
|
||||
{
|
||||
/* Final token is a terminator, so we don't want to copy it */
|
||||
int tokens = (dm->length-11)/sizeof(struct calling_interface_token)-1;
|
||||
struct calling_interface_structure *table =
|
||||
container_of(dm, struct calling_interface_structure, header);
|
||||
|
||||
/* 4 bytes of table header, plus 7 bytes of Dell header, plus at least
|
||||
6 bytes of entry */
|
||||
|
||||
if (dm->length < 17)
|
||||
return;
|
||||
|
||||
da_command_address = table->cmdIOAddress;
|
||||
da_command_code = table->cmdIOCode;
|
||||
|
||||
da_tokens = krealloc(da_tokens, (da_num_tokens + tokens) *
|
||||
sizeof(struct calling_interface_token),
|
||||
GFP_KERNEL);
|
||||
|
||||
if (!da_tokens)
|
||||
return;
|
||||
|
||||
memcpy(da_tokens+da_num_tokens, table->tokens,
|
||||
sizeof(struct calling_interface_token) * tokens);
|
||||
|
||||
da_num_tokens += tokens;
|
||||
}
|
||||
|
||||
static void find_tokens(const struct dmi_header *dm)
|
||||
{
|
||||
switch (dm->type) {
|
||||
case 0xd4: /* Indexed IO */
|
||||
break;
|
||||
case 0xd5: /* Protected Area Type 1 */
|
||||
break;
|
||||
case 0xd6: /* Protected Area Type 2 */
|
||||
break;
|
||||
case 0xda: /* Calling interface */
|
||||
parse_da_table(dm);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int find_token_location(int tokenid)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < da_num_tokens; i++) {
|
||||
if (da_tokens[i].tokenID == tokenid)
|
||||
return da_tokens[i].location;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static struct calling_interface_buffer *
|
||||
dell_send_request(struct calling_interface_buffer *buffer, int class,
|
||||
int select)
|
||||
{
|
||||
struct smi_cmd command;
|
||||
|
||||
command.magic = SMI_CMD_MAGIC;
|
||||
command.command_address = da_command_address;
|
||||
command.command_code = da_command_code;
|
||||
command.ebx = virt_to_phys(buffer);
|
||||
command.ecx = 0x42534931;
|
||||
|
||||
buffer->class = class;
|
||||
buffer->select = select;
|
||||
|
||||
dcdbas_smi_request(&command);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/* Derived from information in DellWirelessCtl.cpp:
|
||||
Class 17, select 11 is radio control. It returns an array of 32-bit values.
|
||||
|
||||
result[0]: return code
|
||||
result[1]:
|
||||
Bit 0: Hardware switch supported
|
||||
Bit 1: Wifi locator supported
|
||||
Bit 2: Wifi is supported
|
||||
Bit 3: Bluetooth is supported
|
||||
Bit 4: WWAN is supported
|
||||
Bit 5: Wireless keyboard supported
|
||||
Bits 6-7: Reserved
|
||||
Bit 8: Wifi is installed
|
||||
Bit 9: Bluetooth is installed
|
||||
Bit 10: WWAN is installed
|
||||
Bits 11-15: Reserved
|
||||
Bit 16: Hardware switch is on
|
||||
Bit 17: Wifi is blocked
|
||||
Bit 18: Bluetooth is blocked
|
||||
Bit 19: WWAN is blocked
|
||||
Bits 20-31: Reserved
|
||||
result[2]: NVRAM size in bytes
|
||||
result[3]: NVRAM format version number
|
||||
*/
|
||||
|
||||
static int dell_rfkill_set(int radio, enum rfkill_state state)
|
||||
{
|
||||
struct calling_interface_buffer buffer;
|
||||
int disable = (state == RFKILL_STATE_UNBLOCKED) ? 0 : 1;
|
||||
|
||||
memset(&buffer, 0, sizeof(struct calling_interface_buffer));
|
||||
buffer.input[0] = (1 | (radio<<8) | (disable << 16));
|
||||
dell_send_request(&buffer, 17, 11);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dell_wifi_set(void *data, enum rfkill_state state)
|
||||
{
|
||||
return dell_rfkill_set(1, state);
|
||||
}
|
||||
|
||||
static int dell_bluetooth_set(void *data, enum rfkill_state state)
|
||||
{
|
||||
return dell_rfkill_set(2, state);
|
||||
}
|
||||
|
||||
static int dell_wwan_set(void *data, enum rfkill_state state)
|
||||
{
|
||||
return dell_rfkill_set(3, state);
|
||||
}
|
||||
|
||||
static int dell_rfkill_get(int bit, enum rfkill_state *state)
|
||||
{
|
||||
struct calling_interface_buffer buffer;
|
||||
int status;
|
||||
int new_state = RFKILL_STATE_HARD_BLOCKED;
|
||||
|
||||
memset(&buffer, 0, sizeof(struct calling_interface_buffer));
|
||||
dell_send_request(&buffer, 17, 11);
|
||||
status = buffer.output[1];
|
||||
|
||||
if (status & (1<<16))
|
||||
new_state = RFKILL_STATE_SOFT_BLOCKED;
|
||||
|
||||
if (status & (1<<bit))
|
||||
*state = new_state;
|
||||
else
|
||||
*state = RFKILL_STATE_UNBLOCKED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dell_wifi_get(void *data, enum rfkill_state *state)
|
||||
{
|
||||
return dell_rfkill_get(17, state);
|
||||
}
|
||||
|
||||
static int dell_bluetooth_get(void *data, enum rfkill_state *state)
|
||||
{
|
||||
return dell_rfkill_get(18, state);
|
||||
}
|
||||
|
||||
static int dell_wwan_get(void *data, enum rfkill_state *state)
|
||||
{
|
||||
return dell_rfkill_get(19, state);
|
||||
}
|
||||
|
||||
static int dell_setup_rfkill(void)
|
||||
{
|
||||
struct calling_interface_buffer buffer;
|
||||
int status;
|
||||
int ret;
|
||||
|
||||
memset(&buffer, 0, sizeof(struct calling_interface_buffer));
|
||||
dell_send_request(&buffer, 17, 11);
|
||||
status = buffer.output[1];
|
||||
|
||||
if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) {
|
||||
wifi_rfkill = rfkill_allocate(NULL, RFKILL_TYPE_WLAN);
|
||||
if (!wifi_rfkill)
|
||||
goto err_wifi;
|
||||
wifi_rfkill->name = "dell-wifi";
|
||||
wifi_rfkill->toggle_radio = dell_wifi_set;
|
||||
wifi_rfkill->get_state = dell_wifi_get;
|
||||
ret = rfkill_register(wifi_rfkill);
|
||||
if (ret)
|
||||
goto err_wifi;
|
||||
}
|
||||
|
||||
if ((status & (1<<3|1<<9)) == (1<<3|1<<9)) {
|
||||
bluetooth_rfkill = rfkill_allocate(NULL, RFKILL_TYPE_BLUETOOTH);
|
||||
if (!bluetooth_rfkill)
|
||||
goto err_bluetooth;
|
||||
bluetooth_rfkill->name = "dell-bluetooth";
|
||||
bluetooth_rfkill->toggle_radio = dell_bluetooth_set;
|
||||
bluetooth_rfkill->get_state = dell_bluetooth_get;
|
||||
ret = rfkill_register(bluetooth_rfkill);
|
||||
if (ret)
|
||||
goto err_bluetooth;
|
||||
}
|
||||
|
||||
if ((status & (1<<4|1<<10)) == (1<<4|1<<10)) {
|
||||
wwan_rfkill = rfkill_allocate(NULL, RFKILL_TYPE_WWAN);
|
||||
if (!wwan_rfkill)
|
||||
goto err_wwan;
|
||||
wwan_rfkill->name = "dell-wwan";
|
||||
wwan_rfkill->toggle_radio = dell_wwan_set;
|
||||
wwan_rfkill->get_state = dell_wwan_get;
|
||||
ret = rfkill_register(wwan_rfkill);
|
||||
if (ret)
|
||||
goto err_wwan;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err_wwan:
|
||||
if (wwan_rfkill)
|
||||
rfkill_free(wwan_rfkill);
|
||||
if (bluetooth_rfkill) {
|
||||
rfkill_unregister(bluetooth_rfkill);
|
||||
bluetooth_rfkill = NULL;
|
||||
}
|
||||
err_bluetooth:
|
||||
if (bluetooth_rfkill)
|
||||
rfkill_free(bluetooth_rfkill);
|
||||
if (wifi_rfkill) {
|
||||
rfkill_unregister(wifi_rfkill);
|
||||
wifi_rfkill = NULL;
|
||||
}
|
||||
err_wifi:
|
||||
if (wifi_rfkill)
|
||||
rfkill_free(wifi_rfkill);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dell_send_intensity(struct backlight_device *bd)
|
||||
{
|
||||
struct calling_interface_buffer buffer;
|
||||
|
||||
memset(&buffer, 0, sizeof(struct calling_interface_buffer));
|
||||
buffer.input[0] = find_token_location(BRIGHTNESS_TOKEN);
|
||||
buffer.input[1] = bd->props.brightness;
|
||||
|
||||
if (buffer.input[0] == -1)
|
||||
return -ENODEV;
|
||||
|
||||
if (power_supply_is_system_supplied() > 0)
|
||||
dell_send_request(&buffer, 1, 2);
|
||||
else
|
||||
dell_send_request(&buffer, 1, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dell_get_intensity(struct backlight_device *bd)
|
||||
{
|
||||
struct calling_interface_buffer buffer;
|
||||
|
||||
memset(&buffer, 0, sizeof(struct calling_interface_buffer));
|
||||
buffer.input[0] = find_token_location(BRIGHTNESS_TOKEN);
|
||||
|
||||
if (buffer.input[0] == -1)
|
||||
return -ENODEV;
|
||||
|
||||
if (power_supply_is_system_supplied() > 0)
|
||||
dell_send_request(&buffer, 0, 2);
|
||||
else
|
||||
dell_send_request(&buffer, 0, 1);
|
||||
|
||||
return buffer.output[1];
|
||||
}
|
||||
|
||||
static struct backlight_ops dell_ops = {
|
||||
.get_brightness = dell_get_intensity,
|
||||
.update_status = dell_send_intensity,
|
||||
};
|
||||
|
||||
static int __init dell_init(void)
|
||||
{
|
||||
struct calling_interface_buffer buffer;
|
||||
int max_intensity = 0;
|
||||
int ret;
|
||||
|
||||
if (!dmi_check_system(dell_device_table))
|
||||
return -ENODEV;
|
||||
|
||||
dmi_walk(find_tokens);
|
||||
|
||||
if (!da_tokens) {
|
||||
printk(KERN_INFO "dell-laptop: Unable to find dmi tokens\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = dell_setup_rfkill();
|
||||
|
||||
if (ret) {
|
||||
printk(KERN_WARNING "dell-laptop: Unable to setup rfkill\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
/* In the event of an ACPI backlight being available, don't
|
||||
* register the platform controller.
|
||||
*/
|
||||
if (acpi_video_backlight_support())
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
memset(&buffer, 0, sizeof(struct calling_interface_buffer));
|
||||
buffer.input[0] = find_token_location(BRIGHTNESS_TOKEN);
|
||||
|
||||
if (buffer.input[0] != -1) {
|
||||
dell_send_request(&buffer, 0, 2);
|
||||
max_intensity = buffer.output[3];
|
||||
}
|
||||
|
||||
if (max_intensity) {
|
||||
dell_backlight_device = backlight_device_register(
|
||||
"dell_backlight",
|
||||
NULL, NULL,
|
||||
&dell_ops);
|
||||
|
||||
if (IS_ERR(dell_backlight_device)) {
|
||||
ret = PTR_ERR(dell_backlight_device);
|
||||
dell_backlight_device = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
dell_backlight_device->props.max_brightness = max_intensity;
|
||||
dell_backlight_device->props.brightness =
|
||||
dell_get_intensity(dell_backlight_device);
|
||||
backlight_update_status(dell_backlight_device);
|
||||
}
|
||||
|
||||
return 0;
|
||||
out:
|
||||
if (wifi_rfkill)
|
||||
rfkill_unregister(wifi_rfkill);
|
||||
if (bluetooth_rfkill)
|
||||
rfkill_unregister(bluetooth_rfkill);
|
||||
if (wwan_rfkill)
|
||||
rfkill_unregister(wwan_rfkill);
|
||||
kfree(da_tokens);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit dell_exit(void)
|
||||
{
|
||||
backlight_device_unregister(dell_backlight_device);
|
||||
if (wifi_rfkill)
|
||||
rfkill_unregister(wifi_rfkill);
|
||||
if (bluetooth_rfkill)
|
||||
rfkill_unregister(bluetooth_rfkill);
|
||||
if (wwan_rfkill)
|
||||
rfkill_unregister(wwan_rfkill);
|
||||
}
|
||||
|
||||
module_init(dell_init);
|
||||
module_exit(dell_exit);
|
||||
|
||||
MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
|
||||
MODULE_DESCRIPTION("Dell laptop driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("dmi:*svnDellInc.:*:ct8:*");
|
@@ -119,7 +119,7 @@ enclosure_register(struct device *dev, const char *name, int components,
|
||||
edev->edev.class = &enclosure_class;
|
||||
edev->edev.parent = get_device(dev);
|
||||
edev->cb = cb;
|
||||
snprintf(edev->edev.bus_id, BUS_ID_SIZE, "%s", name);
|
||||
dev_set_name(&edev->edev, name);
|
||||
err = device_register(&edev->edev);
|
||||
if (err)
|
||||
goto err;
|
||||
@@ -170,7 +170,7 @@ EXPORT_SYMBOL_GPL(enclosure_unregister);
|
||||
static void enclosure_link_name(struct enclosure_component *cdev, char *name)
|
||||
{
|
||||
strcpy(name, "enclosure_device:");
|
||||
strcat(name, cdev->cdev.bus_id);
|
||||
strcat(name, dev_name(&cdev->cdev));
|
||||
}
|
||||
|
||||
static void enclosure_remove_links(struct enclosure_component *cdev)
|
||||
@@ -256,9 +256,9 @@ enclosure_component_register(struct enclosure_device *edev,
|
||||
cdev = &ecomp->cdev;
|
||||
cdev->parent = get_device(&edev->edev);
|
||||
if (name)
|
||||
snprintf(cdev->bus_id, BUS_ID_SIZE, "%s", name);
|
||||
dev_set_name(cdev, name);
|
||||
else
|
||||
snprintf(cdev->bus_id, BUS_ID_SIZE, "%u", number);
|
||||
dev_set_name(cdev, "%u", number);
|
||||
|
||||
cdev->release = enclosure_component_release;
|
||||
cdev->groups = enclosure_groups;
|
||||
|
@@ -50,7 +50,7 @@ static void wake_up_event_readers(struct service_processor *sp)
|
||||
* Store the event in the circular event buffer, wake up any sleeping
|
||||
* event readers.
|
||||
* There is no reader marker in the buffer, therefore readers are
|
||||
* responsible for keeping up with the writer, or they will loose events.
|
||||
* responsible for keeping up with the writer, or they will lose events.
|
||||
*/
|
||||
void ibmasm_receive_event(struct service_processor *sp, void *data, unsigned int data_size)
|
||||
{
|
||||
|
@@ -146,8 +146,6 @@ static struct inode *ibmasmfs_make_inode(struct super_block *sb, int mode)
|
||||
|
||||
if (ret) {
|
||||
ret->i_mode = mode;
|
||||
ret->i_uid = ret->i_gid = 0;
|
||||
ret->i_blocks = 0;
|
||||
ret->i_atime = ret->i_mtime = ret->i_ctime = CURRENT_TIME;
|
||||
}
|
||||
return ret;
|
||||
|
@@ -104,8 +104,7 @@ static int __devinit ibmasm_init_one(struct pci_dev *pdev, const struct pci_devi
|
||||
}
|
||||
|
||||
sp->irq = pdev->irq;
|
||||
sp->base_address = ioremap(pci_resource_start(pdev, 0),
|
||||
pci_resource_len(pdev, 0));
|
||||
sp->base_address = pci_ioremap_bar(pdev, 0);
|
||||
if (!sp->base_address) {
|
||||
dev_err(sp->dev, "Failed to ioremap pci memory\n");
|
||||
result = -ENODEV;
|
||||
|
@@ -269,6 +269,16 @@ ioc4_variant(struct ioc4_driver_data *idd)
|
||||
return IOC4_VARIANT_PCI_RT;
|
||||
}
|
||||
|
||||
static void
|
||||
ioc4_load_modules(struct work_struct *work)
|
||||
{
|
||||
/* arg just has to be freed */
|
||||
|
||||
request_module("sgiioc4");
|
||||
|
||||
kfree(work);
|
||||
}
|
||||
|
||||
/* Adds a new instance of an IOC4 card */
|
||||
static int
|
||||
ioc4_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id)
|
||||
@@ -378,6 +388,30 @@ ioc4_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id)
|
||||
}
|
||||
mutex_unlock(&ioc4_mutex);
|
||||
|
||||
/* Request sgiioc4 IDE driver on boards that bring that functionality
|
||||
* off of IOC4. The root filesystem may be hosted on a drive connected
|
||||
* to IOC4, so we need to make sure the sgiioc4 driver is loaded as it
|
||||
* won't be picked up by modprobes due to the ioc4 module owning the
|
||||
* PCI device.
|
||||
*/
|
||||
if (idd->idd_variant != IOC4_VARIANT_PCI_RT) {
|
||||
struct work_struct *work;
|
||||
work = kzalloc(sizeof(struct work_struct), GFP_KERNEL);
|
||||
if (!work) {
|
||||
printk(KERN_WARNING
|
||||
"%s: IOC4 unable to allocate memory for "
|
||||
"load of sub-modules.\n", __func__);
|
||||
} else {
|
||||
/* Request the module from a work procedure as the
|
||||
* modprobe goes out to a userland helper and that
|
||||
* will hang if done directly from ioc4_probe().
|
||||
*/
|
||||
printk(KERN_INFO "IOC4 loading sgiioc4 submodule\n");
|
||||
INIT_WORK(work, ioc4_load_modules);
|
||||
schedule_work(work);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_misc_region:
|
||||
@@ -462,6 +496,8 @@ ioc4_init(void)
|
||||
static void __devexit
|
||||
ioc4_exit(void)
|
||||
{
|
||||
/* Ensure ioc4_load_modules() has completed before exiting */
|
||||
flush_scheduled_work();
|
||||
pci_unregister_driver(&ioc4_driver);
|
||||
}
|
||||
|
||||
|
@@ -6,7 +6,7 @@
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* You need an userspace library to cooperate with this driver. It (and other
|
||||
* You need a userspace library to cooperate with this driver. It (and other
|
||||
* info) may be obtained here:
|
||||
* http://www.fi.muni.cz/~xslaby/phantom.html
|
||||
* or alternatively, you might use OpenHaptics provided by Sensable.
|
||||
|
@@ -29,7 +29,7 @@ static struct device_driver gru_driver = {
|
||||
};
|
||||
|
||||
static struct device gru_device = {
|
||||
.bus_id = {0},
|
||||
.init_name = "",
|
||||
.driver = &gru_driver,
|
||||
};
|
||||
|
||||
|
@@ -317,7 +317,6 @@ int gru_proc_init(void)
|
||||
{
|
||||
struct proc_entry *p;
|
||||
|
||||
proc_mkdir("sgi_uv", NULL);
|
||||
proc_gru = proc_mkdir("sgi_uv/gru", NULL);
|
||||
|
||||
for (p = proc_files; p->name; p++)
|
||||
|
@@ -194,9 +194,10 @@ enum xp_retval {
|
||||
xpGruSendMqError, /* 59: gru send message queue related error */
|
||||
|
||||
xpBadChannelNumber, /* 60: invalid channel number */
|
||||
xpBadMsgType, /* 60: invalid message type */
|
||||
xpBadMsgType, /* 61: invalid message type */
|
||||
xpBiosError, /* 62: BIOS error */
|
||||
|
||||
xpUnknownReason /* 61: unknown reason - must be last in enum */
|
||||
xpUnknownReason /* 63: unknown reason - must be last in enum */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -345,6 +346,8 @@ extern unsigned long (*xp_pa) (void *);
|
||||
extern enum xp_retval (*xp_remote_memcpy) (unsigned long, const unsigned long,
|
||||
size_t);
|
||||
extern int (*xp_cpu_to_nasid) (int);
|
||||
extern enum xp_retval (*xp_expand_memprotect) (unsigned long, unsigned long);
|
||||
extern enum xp_retval (*xp_restrict_memprotect) (unsigned long, unsigned long);
|
||||
|
||||
extern u64 xp_nofault_PIOR_target;
|
||||
extern int xp_nofault_PIOR(void *);
|
||||
|
@@ -25,7 +25,7 @@ struct device_driver xp_dbg_name = {
|
||||
};
|
||||
|
||||
struct device xp_dbg_subname = {
|
||||
.bus_id = {0}, /* set to "" */
|
||||
.init_name = "", /* set to "" */
|
||||
.driver = &xp_dbg_name
|
||||
};
|
||||
|
||||
@@ -51,6 +51,13 @@ EXPORT_SYMBOL_GPL(xp_remote_memcpy);
|
||||
int (*xp_cpu_to_nasid) (int cpuid);
|
||||
EXPORT_SYMBOL_GPL(xp_cpu_to_nasid);
|
||||
|
||||
enum xp_retval (*xp_expand_memprotect) (unsigned long phys_addr,
|
||||
unsigned long size);
|
||||
EXPORT_SYMBOL_GPL(xp_expand_memprotect);
|
||||
enum xp_retval (*xp_restrict_memprotect) (unsigned long phys_addr,
|
||||
unsigned long size);
|
||||
EXPORT_SYMBOL_GPL(xp_restrict_memprotect);
|
||||
|
||||
/*
|
||||
* xpc_registrations[] keeps track of xpc_connect()'s done by the kernel-level
|
||||
* users of XPC.
|
||||
|
@@ -120,6 +120,38 @@ xp_cpu_to_nasid_sn2(int cpuid)
|
||||
return cpuid_to_nasid(cpuid);
|
||||
}
|
||||
|
||||
static enum xp_retval
|
||||
xp_expand_memprotect_sn2(unsigned long phys_addr, unsigned long size)
|
||||
{
|
||||
u64 nasid_array = 0;
|
||||
int ret;
|
||||
|
||||
ret = sn_change_memprotect(phys_addr, size, SN_MEMPROT_ACCESS_CLASS_1,
|
||||
&nasid_array);
|
||||
if (ret != 0) {
|
||||
dev_err(xp, "sn_change_memprotect(,, "
|
||||
"SN_MEMPROT_ACCESS_CLASS_1,) failed ret=%d\n", ret);
|
||||
return xpSalError;
|
||||
}
|
||||
return xpSuccess;
|
||||
}
|
||||
|
||||
static enum xp_retval
|
||||
xp_restrict_memprotect_sn2(unsigned long phys_addr, unsigned long size)
|
||||
{
|
||||
u64 nasid_array = 0;
|
||||
int ret;
|
||||
|
||||
ret = sn_change_memprotect(phys_addr, size, SN_MEMPROT_ACCESS_CLASS_0,
|
||||
&nasid_array);
|
||||
if (ret != 0) {
|
||||
dev_err(xp, "sn_change_memprotect(,, "
|
||||
"SN_MEMPROT_ACCESS_CLASS_0,) failed ret=%d\n", ret);
|
||||
return xpSalError;
|
||||
}
|
||||
return xpSuccess;
|
||||
}
|
||||
|
||||
enum xp_retval
|
||||
xp_init_sn2(void)
|
||||
{
|
||||
@@ -132,6 +164,8 @@ xp_init_sn2(void)
|
||||
xp_pa = xp_pa_sn2;
|
||||
xp_remote_memcpy = xp_remote_memcpy_sn2;
|
||||
xp_cpu_to_nasid = xp_cpu_to_nasid_sn2;
|
||||
xp_expand_memprotect = xp_expand_memprotect_sn2;
|
||||
xp_restrict_memprotect = xp_restrict_memprotect_sn2;
|
||||
|
||||
return xp_register_nofault_code_sn2();
|
||||
}
|
||||
|
@@ -15,6 +15,11 @@
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <asm/uv/uv_hub.h>
|
||||
#if defined CONFIG_X86_64
|
||||
#include <asm/uv/bios.h>
|
||||
#elif defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV
|
||||
#include <asm/sn/sn_sal.h>
|
||||
#endif
|
||||
#include "../sgi-gru/grukservices.h"
|
||||
#include "xp.h"
|
||||
|
||||
@@ -49,18 +54,79 @@ xp_cpu_to_nasid_uv(int cpuid)
|
||||
return UV_PNODE_TO_NASID(uv_cpu_to_pnode(cpuid));
|
||||
}
|
||||
|
||||
static enum xp_retval
|
||||
xp_expand_memprotect_uv(unsigned long phys_addr, unsigned long size)
|
||||
{
|
||||
int ret;
|
||||
|
||||
#if defined CONFIG_X86_64
|
||||
ret = uv_bios_change_memprotect(phys_addr, size, UV_MEMPROT_ALLOW_RW);
|
||||
if (ret != BIOS_STATUS_SUCCESS) {
|
||||
dev_err(xp, "uv_bios_change_memprotect(,, "
|
||||
"UV_MEMPROT_ALLOW_RW) failed, ret=%d\n", ret);
|
||||
return xpBiosError;
|
||||
}
|
||||
|
||||
#elif defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV
|
||||
u64 nasid_array;
|
||||
|
||||
ret = sn_change_memprotect(phys_addr, size, SN_MEMPROT_ACCESS_CLASS_1,
|
||||
&nasid_array);
|
||||
if (ret != 0) {
|
||||
dev_err(xp, "sn_change_memprotect(,, "
|
||||
"SN_MEMPROT_ACCESS_CLASS_1,) failed ret=%d\n", ret);
|
||||
return xpSalError;
|
||||
}
|
||||
#else
|
||||
#error not a supported configuration
|
||||
#endif
|
||||
return xpSuccess;
|
||||
}
|
||||
|
||||
static enum xp_retval
|
||||
xp_restrict_memprotect_uv(unsigned long phys_addr, unsigned long size)
|
||||
{
|
||||
int ret;
|
||||
|
||||
#if defined CONFIG_X86_64
|
||||
ret = uv_bios_change_memprotect(phys_addr, size,
|
||||
UV_MEMPROT_RESTRICT_ACCESS);
|
||||
if (ret != BIOS_STATUS_SUCCESS) {
|
||||
dev_err(xp, "uv_bios_change_memprotect(,, "
|
||||
"UV_MEMPROT_RESTRICT_ACCESS) failed, ret=%d\n", ret);
|
||||
return xpBiosError;
|
||||
}
|
||||
|
||||
#elif defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV
|
||||
u64 nasid_array;
|
||||
|
||||
ret = sn_change_memprotect(phys_addr, size, SN_MEMPROT_ACCESS_CLASS_0,
|
||||
&nasid_array);
|
||||
if (ret != 0) {
|
||||
dev_err(xp, "sn_change_memprotect(,, "
|
||||
"SN_MEMPROT_ACCESS_CLASS_0,) failed ret=%d\n", ret);
|
||||
return xpSalError;
|
||||
}
|
||||
#else
|
||||
#error not a supported configuration
|
||||
#endif
|
||||
return xpSuccess;
|
||||
}
|
||||
|
||||
enum xp_retval
|
||||
xp_init_uv(void)
|
||||
{
|
||||
BUG_ON(!is_uv());
|
||||
|
||||
xp_max_npartitions = XP_MAX_NPARTITIONS_UV;
|
||||
xp_partition_id = 0; /* !!! not correct value */
|
||||
xp_region_size = 0; /* !!! not correct value */
|
||||
xp_partition_id = sn_partition_id;
|
||||
xp_region_size = sn_region_size;
|
||||
|
||||
xp_pa = xp_pa_uv;
|
||||
xp_remote_memcpy = xp_remote_memcpy_uv;
|
||||
xp_cpu_to_nasid = xp_cpu_to_nasid_uv;
|
||||
xp_expand_memprotect = xp_expand_memprotect_uv;
|
||||
xp_restrict_memprotect = xp_restrict_memprotect_uv;
|
||||
|
||||
return xpSuccess;
|
||||
}
|
||||
|
@@ -180,6 +180,18 @@ struct xpc_vars_part_sn2 {
|
||||
(XPC_RP_MACH_NASIDS(_rp) + \
|
||||
xpc_nasid_mask_nlongs))
|
||||
|
||||
/*
|
||||
* Info pertinent to a GRU message queue using a watch list for irq generation.
|
||||
*/
|
||||
struct xpc_gru_mq_uv {
|
||||
void *address; /* address of GRU message queue */
|
||||
unsigned int order; /* size of GRU message queue as a power of 2 */
|
||||
int irq; /* irq raised when message is received in mq */
|
||||
int mmr_blade; /* blade where watchlist was allocated from */
|
||||
unsigned long mmr_offset; /* offset of irq mmr located on mmr_blade */
|
||||
int watchlist_num; /* number of watchlist allocatd by BIOS */
|
||||
};
|
||||
|
||||
/*
|
||||
* The activate_mq is used to send/receive GRU messages that affect XPC's
|
||||
* heartbeat, partition active state, and channel state. This is UV only.
|
||||
|
@@ -59,12 +59,12 @@ struct device_driver xpc_dbg_name = {
|
||||
};
|
||||
|
||||
struct device xpc_part_dbg_subname = {
|
||||
.bus_id = {0}, /* set to "part" at xpc_init() time */
|
||||
.init_name = "", /* set to "part" at xpc_init() time */
|
||||
.driver = &xpc_dbg_name
|
||||
};
|
||||
|
||||
struct device xpc_chan_dbg_subname = {
|
||||
.bus_id = {0}, /* set to "chan" at xpc_init() time */
|
||||
.init_name = "", /* set to "chan" at xpc_init() time */
|
||||
.driver = &xpc_dbg_name
|
||||
};
|
||||
|
||||
@@ -1258,8 +1258,8 @@ xpc_init(void)
|
||||
int ret;
|
||||
struct task_struct *kthread;
|
||||
|
||||
snprintf(xpc_part->bus_id, BUS_ID_SIZE, "part");
|
||||
snprintf(xpc_chan->bus_id, BUS_ID_SIZE, "chan");
|
||||
dev_set_name(xpc_part, "part");
|
||||
dev_set_name(xpc_chan, "chan");
|
||||
|
||||
if (is_shub()) {
|
||||
/*
|
||||
|
@@ -553,22 +553,17 @@ static u64 xpc_prot_vec_sn2[MAX_NUMNODES];
|
||||
static enum xp_retval
|
||||
xpc_allow_amo_ops_sn2(struct amo *amos_page)
|
||||
{
|
||||
u64 nasid_array = 0;
|
||||
int ret;
|
||||
enum xp_retval ret = xpSuccess;
|
||||
|
||||
/*
|
||||
* On SHUB 1.1, we cannot call sn_change_memprotect() since the BIST
|
||||
* collides with memory operations. On those systems we call
|
||||
* xpc_allow_amo_ops_shub_wars_1_1_sn2() instead.
|
||||
*/
|
||||
if (!enable_shub_wars_1_1()) {
|
||||
ret = sn_change_memprotect(ia64_tpa((u64)amos_page), PAGE_SIZE,
|
||||
SN_MEMPROT_ACCESS_CLASS_1,
|
||||
&nasid_array);
|
||||
if (ret != 0)
|
||||
return xpSalError;
|
||||
}
|
||||
return xpSuccess;
|
||||
if (!enable_shub_wars_1_1())
|
||||
ret = xp_expand_memprotect(ia64_tpa((u64)amos_page), PAGE_SIZE);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@@ -18,7 +18,15 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <asm/uv/uv_hub.h>
|
||||
#if defined CONFIG_X86_64
|
||||
#include <asm/uv/bios.h>
|
||||
#include <asm/uv/uv_irq.h>
|
||||
#elif defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV
|
||||
#include <asm/sn/intr.h>
|
||||
#include <asm/sn/sn_sal.h>
|
||||
#endif
|
||||
#include "../sgi-gru/gru.h"
|
||||
#include "../sgi-gru/grukservices.h"
|
||||
#include "xpc.h"
|
||||
@@ -27,15 +35,17 @@ static atomic64_t xpc_heartbeat_uv;
|
||||
static DECLARE_BITMAP(xpc_heartbeating_to_mask_uv, XP_MAX_NPARTITIONS_UV);
|
||||
|
||||
#define XPC_ACTIVATE_MSG_SIZE_UV (1 * GRU_CACHE_LINE_BYTES)
|
||||
#define XPC_ACTIVATE_MQ_SIZE_UV (4 * XP_MAX_NPARTITIONS_UV * \
|
||||
XPC_ACTIVATE_MSG_SIZE_UV)
|
||||
#define XPC_ACTIVATE_IRQ_NAME "xpc_activate"
|
||||
|
||||
#define XPC_NOTIFY_MSG_SIZE_UV (2 * GRU_CACHE_LINE_BYTES)
|
||||
#define XPC_NOTIFY_MQ_SIZE_UV (4 * XP_MAX_NPARTITIONS_UV * \
|
||||
XPC_NOTIFY_MSG_SIZE_UV)
|
||||
#define XPC_NOTIFY_IRQ_NAME "xpc_notify"
|
||||
|
||||
#define XPC_ACTIVATE_MQ_SIZE_UV (4 * XP_MAX_NPARTITIONS_UV * \
|
||||
XPC_ACTIVATE_MSG_SIZE_UV)
|
||||
#define XPC_NOTIFY_MQ_SIZE_UV (4 * XP_MAX_NPARTITIONS_UV * \
|
||||
XPC_NOTIFY_MSG_SIZE_UV)
|
||||
|
||||
static void *xpc_activate_mq_uv;
|
||||
static void *xpc_notify_mq_uv;
|
||||
static struct xpc_gru_mq_uv *xpc_activate_mq_uv;
|
||||
static struct xpc_gru_mq_uv *xpc_notify_mq_uv;
|
||||
|
||||
static int
|
||||
xpc_setup_partitions_sn_uv(void)
|
||||
@@ -52,62 +62,209 @@ xpc_setup_partitions_sn_uv(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *
|
||||
xpc_create_gru_mq_uv(unsigned int mq_size, int cpuid, unsigned int irq,
|
||||
irq_handler_t irq_handler)
|
||||
static int
|
||||
xpc_get_gru_mq_irq_uv(struct xpc_gru_mq_uv *mq, int cpu, char *irq_name)
|
||||
{
|
||||
int ret;
|
||||
int nid;
|
||||
int mq_order;
|
||||
struct page *page;
|
||||
void *mq;
|
||||
|
||||
nid = cpu_to_node(cpuid);
|
||||
mq_order = get_order(mq_size);
|
||||
page = alloc_pages_node(nid, GFP_KERNEL | __GFP_ZERO | GFP_THISNODE,
|
||||
mq_order);
|
||||
if (page == NULL) {
|
||||
dev_err(xpc_part, "xpc_create_gru_mq_uv() failed to alloc %d "
|
||||
"bytes of memory on nid=%d for GRU mq\n", mq_size, nid);
|
||||
return NULL;
|
||||
#if defined CONFIG_X86_64
|
||||
mq->irq = uv_setup_irq(irq_name, cpu, mq->mmr_blade, mq->mmr_offset);
|
||||
if (mq->irq < 0) {
|
||||
dev_err(xpc_part, "uv_setup_irq() returned error=%d\n",
|
||||
mq->irq);
|
||||
}
|
||||
|
||||
mq = page_address(page);
|
||||
ret = gru_create_message_queue(mq, mq_size);
|
||||
if (ret != 0) {
|
||||
dev_err(xpc_part, "gru_create_message_queue() returned "
|
||||
"error=%d\n", ret);
|
||||
free_pages((unsigned long)mq, mq_order);
|
||||
return NULL;
|
||||
}
|
||||
#elif defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV
|
||||
int mmr_pnode;
|
||||
unsigned long mmr_value;
|
||||
|
||||
/* !!! Need to do some other things to set up IRQ */
|
||||
if (strcmp(irq_name, XPC_ACTIVATE_IRQ_NAME) == 0)
|
||||
mq->irq = SGI_XPC_ACTIVATE;
|
||||
else if (strcmp(irq_name, XPC_NOTIFY_IRQ_NAME) == 0)
|
||||
mq->irq = SGI_XPC_NOTIFY;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
ret = request_irq(irq, irq_handler, 0, "xpc", NULL);
|
||||
if (ret != 0) {
|
||||
dev_err(xpc_part, "request_irq(irq=%d) returned error=%d\n",
|
||||
irq, ret);
|
||||
free_pages((unsigned long)mq, mq_order);
|
||||
return NULL;
|
||||
}
|
||||
mmr_pnode = uv_blade_to_pnode(mq->mmr_blade);
|
||||
mmr_value = (unsigned long)cpu_physical_id(cpu) << 32 | mq->irq;
|
||||
|
||||
/* !!! enable generation of irq when GRU mq op occurs to this mq */
|
||||
uv_write_global_mmr64(mmr_pnode, mq->mmr_offset, mmr_value);
|
||||
#else
|
||||
#error not a supported configuration
|
||||
#endif
|
||||
|
||||
/* ??? allow other partitions to access GRU mq? */
|
||||
|
||||
return mq;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
xpc_destroy_gru_mq_uv(void *mq, unsigned int mq_size, unsigned int irq)
|
||||
xpc_release_gru_mq_irq_uv(struct xpc_gru_mq_uv *mq)
|
||||
{
|
||||
/* ??? disallow other partitions to access GRU mq? */
|
||||
#if defined CONFIG_X86_64
|
||||
uv_teardown_irq(mq->irq, mq->mmr_blade, mq->mmr_offset);
|
||||
|
||||
/* !!! disable generation of irq when GRU mq op occurs to this mq */
|
||||
#elif defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV
|
||||
int mmr_pnode;
|
||||
unsigned long mmr_value;
|
||||
|
||||
free_irq(irq, NULL);
|
||||
mmr_pnode = uv_blade_to_pnode(mq->mmr_blade);
|
||||
mmr_value = 1UL << 16;
|
||||
|
||||
free_pages((unsigned long)mq, get_order(mq_size));
|
||||
uv_write_global_mmr64(mmr_pnode, mq->mmr_offset, mmr_value);
|
||||
#else
|
||||
#error not a supported configuration
|
||||
#endif
|
||||
}
|
||||
|
||||
static int
|
||||
xpc_gru_mq_watchlist_alloc_uv(struct xpc_gru_mq_uv *mq)
|
||||
{
|
||||
int ret;
|
||||
|
||||
#if defined CONFIG_X86_64
|
||||
ret = uv_bios_mq_watchlist_alloc(mq->mmr_blade, uv_gpa(mq->address),
|
||||
mq->order, &mq->mmr_offset);
|
||||
if (ret < 0) {
|
||||
dev_err(xpc_part, "uv_bios_mq_watchlist_alloc() failed, "
|
||||
"ret=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
#elif defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV
|
||||
ret = sn_mq_watchlist_alloc(mq->mmr_blade, uv_gpa(mq->address),
|
||||
mq->order, &mq->mmr_offset);
|
||||
if (ret < 0) {
|
||||
dev_err(xpc_part, "sn_mq_watchlist_alloc() failed, ret=%d\n",
|
||||
ret);
|
||||
return -EBUSY;
|
||||
}
|
||||
#else
|
||||
#error not a supported configuration
|
||||
#endif
|
||||
|
||||
mq->watchlist_num = ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
xpc_gru_mq_watchlist_free_uv(struct xpc_gru_mq_uv *mq)
|
||||
{
|
||||
int ret;
|
||||
|
||||
#if defined CONFIG_X86_64
|
||||
ret = uv_bios_mq_watchlist_free(mq->mmr_blade, mq->watchlist_num);
|
||||
BUG_ON(ret != BIOS_STATUS_SUCCESS);
|
||||
#elif defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV
|
||||
ret = sn_mq_watchlist_free(mq->mmr_blade, mq->watchlist_num);
|
||||
BUG_ON(ret != SALRET_OK);
|
||||
#else
|
||||
#error not a supported configuration
|
||||
#endif
|
||||
}
|
||||
|
||||
static struct xpc_gru_mq_uv *
|
||||
xpc_create_gru_mq_uv(unsigned int mq_size, int cpu, char *irq_name,
|
||||
irq_handler_t irq_handler)
|
||||
{
|
||||
enum xp_retval xp_ret;
|
||||
int ret;
|
||||
int nid;
|
||||
int pg_order;
|
||||
struct page *page;
|
||||
struct xpc_gru_mq_uv *mq;
|
||||
|
||||
mq = kmalloc(sizeof(struct xpc_gru_mq_uv), GFP_KERNEL);
|
||||
if (mq == NULL) {
|
||||
dev_err(xpc_part, "xpc_create_gru_mq_uv() failed to kmalloc() "
|
||||
"a xpc_gru_mq_uv structure\n");
|
||||
ret = -ENOMEM;
|
||||
goto out_1;
|
||||
}
|
||||
|
||||
pg_order = get_order(mq_size);
|
||||
mq->order = pg_order + PAGE_SHIFT;
|
||||
mq_size = 1UL << mq->order;
|
||||
|
||||
mq->mmr_blade = uv_cpu_to_blade_id(cpu);
|
||||
|
||||
nid = cpu_to_node(cpu);
|
||||
page = alloc_pages_node(nid, GFP_KERNEL | __GFP_ZERO | GFP_THISNODE,
|
||||
pg_order);
|
||||
if (page == NULL) {
|
||||
dev_err(xpc_part, "xpc_create_gru_mq_uv() failed to alloc %d "
|
||||
"bytes of memory on nid=%d for GRU mq\n", mq_size, nid);
|
||||
ret = -ENOMEM;
|
||||
goto out_2;
|
||||
}
|
||||
mq->address = page_address(page);
|
||||
|
||||
ret = gru_create_message_queue(mq->address, mq_size);
|
||||
if (ret != 0) {
|
||||
dev_err(xpc_part, "gru_create_message_queue() returned "
|
||||
"error=%d\n", ret);
|
||||
ret = -EINVAL;
|
||||
goto out_3;
|
||||
}
|
||||
|
||||
/* enable generation of irq when GRU mq operation occurs to this mq */
|
||||
ret = xpc_gru_mq_watchlist_alloc_uv(mq);
|
||||
if (ret != 0)
|
||||
goto out_3;
|
||||
|
||||
ret = xpc_get_gru_mq_irq_uv(mq, cpu, irq_name);
|
||||
if (ret != 0)
|
||||
goto out_4;
|
||||
|
||||
ret = request_irq(mq->irq, irq_handler, 0, irq_name, NULL);
|
||||
if (ret != 0) {
|
||||
dev_err(xpc_part, "request_irq(irq=%d) returned error=%d\n",
|
||||
mq->irq, ret);
|
||||
goto out_5;
|
||||
}
|
||||
|
||||
/* allow other partitions to access this GRU mq */
|
||||
xp_ret = xp_expand_memprotect(xp_pa(mq->address), mq_size);
|
||||
if (xp_ret != xpSuccess) {
|
||||
ret = -EACCES;
|
||||
goto out_6;
|
||||
}
|
||||
|
||||
return mq;
|
||||
|
||||
/* something went wrong */
|
||||
out_6:
|
||||
free_irq(mq->irq, NULL);
|
||||
out_5:
|
||||
xpc_release_gru_mq_irq_uv(mq);
|
||||
out_4:
|
||||
xpc_gru_mq_watchlist_free_uv(mq);
|
||||
out_3:
|
||||
free_pages((unsigned long)mq->address, pg_order);
|
||||
out_2:
|
||||
kfree(mq);
|
||||
out_1:
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static void
|
||||
xpc_destroy_gru_mq_uv(struct xpc_gru_mq_uv *mq)
|
||||
{
|
||||
unsigned int mq_size;
|
||||
int pg_order;
|
||||
int ret;
|
||||
|
||||
/* disallow other partitions to access GRU mq */
|
||||
mq_size = 1UL << mq->order;
|
||||
ret = xp_restrict_memprotect(xp_pa(mq->address), mq_size);
|
||||
BUG_ON(ret != xpSuccess);
|
||||
|
||||
/* unregister irq handler and release mq irq/vector mapping */
|
||||
free_irq(mq->irq, NULL);
|
||||
xpc_release_gru_mq_irq_uv(mq);
|
||||
|
||||
/* disable generation of irq when GRU mq op occurs to this mq */
|
||||
xpc_gru_mq_watchlist_free_uv(mq);
|
||||
|
||||
pg_order = mq->order - PAGE_SHIFT;
|
||||
free_pages((unsigned long)mq->address, pg_order);
|
||||
|
||||
kfree(mq);
|
||||
}
|
||||
|
||||
static enum xp_retval
|
||||
@@ -402,7 +559,10 @@ xpc_handle_activate_IRQ_uv(int irq, void *dev_id)
|
||||
struct xpc_partition *part;
|
||||
int wakeup_hb_checker = 0;
|
||||
|
||||
while ((msg_hdr = gru_get_next_message(xpc_activate_mq_uv)) != NULL) {
|
||||
while (1) {
|
||||
msg_hdr = gru_get_next_message(xpc_activate_mq_uv->address);
|
||||
if (msg_hdr == NULL)
|
||||
break;
|
||||
|
||||
partid = msg_hdr->partid;
|
||||
if (partid < 0 || partid >= XP_MAX_NPARTITIONS_UV) {
|
||||
@@ -418,7 +578,7 @@ xpc_handle_activate_IRQ_uv(int irq, void *dev_id)
|
||||
}
|
||||
}
|
||||
|
||||
gru_free_message(xpc_activate_mq_uv, msg_hdr);
|
||||
gru_free_message(xpc_activate_mq_uv->address, msg_hdr);
|
||||
}
|
||||
|
||||
if (wakeup_hb_checker)
|
||||
@@ -482,7 +642,7 @@ xpc_send_local_activate_IRQ_uv(struct xpc_partition *part, int act_state_req)
|
||||
struct xpc_partition_uv *part_uv = &part->sn.uv;
|
||||
|
||||
/*
|
||||
* !!! Make our side think that the remote parition sent an activate
|
||||
* !!! Make our side think that the remote partition sent an activate
|
||||
* !!! message our way by doing what the activate IRQ handler would
|
||||
* !!! do had one really been sent.
|
||||
*/
|
||||
@@ -500,14 +660,39 @@ static enum xp_retval
|
||||
xpc_get_partition_rsvd_page_pa_uv(void *buf, u64 *cookie, unsigned long *rp_pa,
|
||||
size_t *len)
|
||||
{
|
||||
/* !!! call the UV version of sn_partition_reserved_page_pa() */
|
||||
return xpUnsupported;
|
||||
s64 status;
|
||||
enum xp_retval ret;
|
||||
|
||||
#if defined CONFIG_X86_64
|
||||
status = uv_bios_reserved_page_pa((u64)buf, cookie, (u64 *)rp_pa,
|
||||
(u64 *)len);
|
||||
if (status == BIOS_STATUS_SUCCESS)
|
||||
ret = xpSuccess;
|
||||
else if (status == BIOS_STATUS_MORE_PASSES)
|
||||
ret = xpNeedMoreInfo;
|
||||
else
|
||||
ret = xpBiosError;
|
||||
|
||||
#elif defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV
|
||||
status = sn_partition_reserved_page_pa((u64)buf, cookie, rp_pa, len);
|
||||
if (status == SALRET_OK)
|
||||
ret = xpSuccess;
|
||||
else if (status == SALRET_MORE_PASSES)
|
||||
ret = xpNeedMoreInfo;
|
||||
else
|
||||
ret = xpSalError;
|
||||
|
||||
#else
|
||||
#error not a supported configuration
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
xpc_setup_rsvd_page_sn_uv(struct xpc_rsvd_page *rp)
|
||||
{
|
||||
rp->sn.activate_mq_gpa = uv_gpa(xpc_activate_mq_uv);
|
||||
rp->sn.activate_mq_gpa = uv_gpa(xpc_activate_mq_uv->address);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1411,22 +1596,18 @@ xpc_init_uv(void)
|
||||
return -E2BIG;
|
||||
}
|
||||
|
||||
/* ??? The cpuid argument's value is 0, is that what we want? */
|
||||
/* !!! The irq argument's value isn't correct. */
|
||||
xpc_activate_mq_uv = xpc_create_gru_mq_uv(XPC_ACTIVATE_MQ_SIZE_UV, 0, 0,
|
||||
xpc_activate_mq_uv = xpc_create_gru_mq_uv(XPC_ACTIVATE_MQ_SIZE_UV, 0,
|
||||
XPC_ACTIVATE_IRQ_NAME,
|
||||
xpc_handle_activate_IRQ_uv);
|
||||
if (xpc_activate_mq_uv == NULL)
|
||||
return -ENOMEM;
|
||||
if (IS_ERR(xpc_activate_mq_uv))
|
||||
return PTR_ERR(xpc_activate_mq_uv);
|
||||
|
||||
/* ??? The cpuid argument's value is 0, is that what we want? */
|
||||
/* !!! The irq argument's value isn't correct. */
|
||||
xpc_notify_mq_uv = xpc_create_gru_mq_uv(XPC_NOTIFY_MQ_SIZE_UV, 0, 0,
|
||||
xpc_notify_mq_uv = xpc_create_gru_mq_uv(XPC_NOTIFY_MQ_SIZE_UV, 0,
|
||||
XPC_NOTIFY_IRQ_NAME,
|
||||
xpc_handle_notify_IRQ_uv);
|
||||
if (xpc_notify_mq_uv == NULL) {
|
||||
/* !!! The irq argument's value isn't correct. */
|
||||
xpc_destroy_gru_mq_uv(xpc_activate_mq_uv,
|
||||
XPC_ACTIVATE_MQ_SIZE_UV, 0);
|
||||
return -ENOMEM;
|
||||
if (IS_ERR(xpc_notify_mq_uv)) {
|
||||
xpc_destroy_gru_mq_uv(xpc_activate_mq_uv);
|
||||
return PTR_ERR(xpc_notify_mq_uv);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -1435,9 +1616,6 @@ xpc_init_uv(void)
|
||||
void
|
||||
xpc_exit_uv(void)
|
||||
{
|
||||
/* !!! The irq argument's value isn't correct. */
|
||||
xpc_destroy_gru_mq_uv(xpc_notify_mq_uv, XPC_NOTIFY_MQ_SIZE_UV, 0);
|
||||
|
||||
/* !!! The irq argument's value isn't correct. */
|
||||
xpc_destroy_gru_mq_uv(xpc_activate_mq_uv, XPC_ACTIVATE_MQ_SIZE_UV, 0);
|
||||
xpc_destroy_gru_mq_uv(xpc_notify_mq_uv);
|
||||
xpc_destroy_gru_mq_uv(xpc_activate_mq_uv);
|
||||
}
|
||||
|
@@ -95,11 +95,6 @@ struct xpnet_pending_msg {
|
||||
atomic_t use_count;
|
||||
};
|
||||
|
||||
/* driver specific structure pointed to by the device structure */
|
||||
struct xpnet_dev_private {
|
||||
struct net_device_stats stats;
|
||||
};
|
||||
|
||||
struct net_device *xpnet_device;
|
||||
|
||||
/*
|
||||
@@ -138,7 +133,7 @@ struct device_driver xpnet_dbg_name = {
|
||||
};
|
||||
|
||||
struct device xpnet_dbg_subname = {
|
||||
.bus_id = {0}, /* set to "" */
|
||||
.init_name = "", /* set to "" */
|
||||
.driver = &xpnet_dbg_name
|
||||
};
|
||||
|
||||
@@ -153,8 +148,6 @@ xpnet_receive(short partid, int channel, struct xpnet_message *msg)
|
||||
struct sk_buff *skb;
|
||||
void *dst;
|
||||
enum xp_retval ret;
|
||||
struct xpnet_dev_private *priv =
|
||||
(struct xpnet_dev_private *)xpnet_device->priv;
|
||||
|
||||
if (!XPNET_VALID_MSG(msg)) {
|
||||
/*
|
||||
@@ -162,7 +155,7 @@ xpnet_receive(short partid, int channel, struct xpnet_message *msg)
|
||||
*/
|
||||
xpc_received(partid, channel, (void *)msg);
|
||||
|
||||
priv->stats.rx_errors++;
|
||||
xpnet_device->stats.rx_errors++;
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -177,7 +170,7 @@ xpnet_receive(short partid, int channel, struct xpnet_message *msg)
|
||||
|
||||
xpc_received(partid, channel, (void *)msg);
|
||||
|
||||
priv->stats.rx_errors++;
|
||||
xpnet_device->stats.rx_errors++;
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -227,7 +220,7 @@ xpnet_receive(short partid, int channel, struct xpnet_message *msg)
|
||||
|
||||
xpc_received(partid, channel, (void *)msg);
|
||||
|
||||
priv->stats.rx_errors++;
|
||||
xpnet_device->stats.rx_errors++;
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -248,8 +241,8 @@ xpnet_receive(short partid, int channel, struct xpnet_message *msg)
|
||||
skb_end_pointer(skb), skb->len);
|
||||
|
||||
xpnet_device->last_rx = jiffies;
|
||||
priv->stats.rx_packets++;
|
||||
priv->stats.rx_bytes += skb->len + ETH_HLEN;
|
||||
xpnet_device->stats.rx_packets++;
|
||||
xpnet_device->stats.rx_bytes += skb->len + ETH_HLEN;
|
||||
|
||||
netif_rx_ni(skb);
|
||||
xpc_received(partid, channel, (void *)msg);
|
||||
@@ -353,28 +346,6 @@ xpnet_dev_change_mtu(struct net_device *dev, int new_mtu)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Required for the net_device structure.
|
||||
*/
|
||||
static int
|
||||
xpnet_dev_set_config(struct net_device *dev, struct ifmap *new_map)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return statistics to the caller.
|
||||
*/
|
||||
static struct net_device_stats *
|
||||
xpnet_dev_get_stats(struct net_device *dev)
|
||||
{
|
||||
struct xpnet_dev_private *priv;
|
||||
|
||||
priv = (struct xpnet_dev_private *)dev->priv;
|
||||
|
||||
return &priv->stats;
|
||||
}
|
||||
|
||||
/*
|
||||
* Notification that the other end has received the message and
|
||||
* DMA'd the skb information. At this point, they are done with
|
||||
@@ -456,7 +427,6 @@ xpnet_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
struct xpnet_pending_msg *queued_msg;
|
||||
u64 start_addr, end_addr;
|
||||
short dest_partid;
|
||||
struct xpnet_dev_private *priv = (struct xpnet_dev_private *)dev->priv;
|
||||
u16 embedded_bytes = 0;
|
||||
|
||||
dev_dbg(xpnet, ">skb->head=0x%p skb->data=0x%p skb->tail=0x%p "
|
||||
@@ -479,7 +449,7 @@ xpnet_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
dev_warn(xpnet, "failed to kmalloc %ld bytes; dropping "
|
||||
"packet\n", sizeof(struct xpnet_pending_msg));
|
||||
|
||||
priv->stats.tx_errors++;
|
||||
dev->stats.tx_errors++;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
@@ -529,8 +499,8 @@ xpnet_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
kfree(queued_msg);
|
||||
}
|
||||
|
||||
priv->stats.tx_packets++;
|
||||
priv->stats.tx_bytes += skb->len;
|
||||
dev->stats.tx_packets++;
|
||||
dev->stats.tx_bytes += skb->len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -541,14 +511,19 @@ xpnet_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
static void
|
||||
xpnet_dev_tx_timeout(struct net_device *dev)
|
||||
{
|
||||
struct xpnet_dev_private *priv;
|
||||
|
||||
priv = (struct xpnet_dev_private *)dev->priv;
|
||||
|
||||
priv->stats.tx_errors++;
|
||||
return;
|
||||
dev->stats.tx_errors++;
|
||||
}
|
||||
|
||||
static const struct net_device_ops xpnet_netdev_ops = {
|
||||
.ndo_open = xpnet_dev_open,
|
||||
.ndo_stop = xpnet_dev_stop,
|
||||
.ndo_start_xmit = xpnet_dev_hard_start_xmit,
|
||||
.ndo_change_mtu = xpnet_dev_change_mtu,
|
||||
.ndo_tx_timeout = xpnet_dev_tx_timeout,
|
||||
.ndo_set_mac_address = eth_mac_addr,
|
||||
.ndo_validate_addr = eth_validate_addr,
|
||||
};
|
||||
|
||||
static int __init
|
||||
xpnet_init(void)
|
||||
{
|
||||
@@ -568,8 +543,7 @@ xpnet_init(void)
|
||||
* use ether_setup() to init the majority of our device
|
||||
* structure and then override the necessary pieces.
|
||||
*/
|
||||
xpnet_device = alloc_netdev(sizeof(struct xpnet_dev_private),
|
||||
XPNET_DEVICE_NAME, ether_setup);
|
||||
xpnet_device = alloc_netdev(0, XPNET_DEVICE_NAME, ether_setup);
|
||||
if (xpnet_device == NULL) {
|
||||
kfree(xpnet_broadcast_partitions);
|
||||
return -ENOMEM;
|
||||
@@ -578,13 +552,6 @@ xpnet_init(void)
|
||||
netif_carrier_off(xpnet_device);
|
||||
|
||||
xpnet_device->mtu = XPNET_DEF_MTU;
|
||||
xpnet_device->change_mtu = xpnet_dev_change_mtu;
|
||||
xpnet_device->open = xpnet_dev_open;
|
||||
xpnet_device->get_stats = xpnet_dev_get_stats;
|
||||
xpnet_device->stop = xpnet_dev_stop;
|
||||
xpnet_device->hard_start_xmit = xpnet_dev_hard_start_xmit;
|
||||
xpnet_device->tx_timeout = xpnet_dev_tx_timeout;
|
||||
xpnet_device->set_config = xpnet_dev_set_config;
|
||||
|
||||
/*
|
||||
* Multicast assumes the LSB of the first octet is set for multicast
|
||||
|
@@ -164,7 +164,7 @@ static void tifm_7xx1_switch_media(struct work_struct *work)
|
||||
if (sock) {
|
||||
printk(KERN_INFO
|
||||
"%s : demand removing card from socket %u:%u\n",
|
||||
fm->dev.bus_id, fm->id, cnt);
|
||||
dev_name(&fm->dev), fm->id, cnt);
|
||||
fm->sockets[cnt] = NULL;
|
||||
sock_addr = sock->addr;
|
||||
spin_unlock_irqrestore(&fm->lock, flags);
|
||||
@@ -354,8 +354,7 @@ static int tifm_7xx1_probe(struct pci_dev *dev,
|
||||
fm->has_ms_pif = tifm_7xx1_has_ms_pif;
|
||||
pci_set_drvdata(dev, fm);
|
||||
|
||||
fm->addr = ioremap(pci_resource_start(dev, 0),
|
||||
pci_resource_len(dev, 0));
|
||||
fm->addr = pci_ioremap_bar(dev, 0);
|
||||
if (!fm->addr)
|
||||
goto err_out_free;
|
||||
|
||||
|
@@ -203,7 +203,7 @@ int tifm_add_adapter(struct tifm_adapter *fm)
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
snprintf(fm->dev.bus_id, BUS_ID_SIZE, "tifm%u", fm->id);
|
||||
dev_set_name(&fm->dev, "tifm%u", fm->id);
|
||||
rc = device_add(&fm->dev);
|
||||
if (rc) {
|
||||
spin_lock(&tifm_adapter_lock);
|
||||
@@ -266,9 +266,8 @@ struct tifm_dev *tifm_alloc_device(struct tifm_adapter *fm, unsigned int id,
|
||||
sock->dev.dma_mask = fm->dev.parent->dma_mask;
|
||||
sock->dev.release = tifm_free_device;
|
||||
|
||||
snprintf(sock->dev.bus_id, BUS_ID_SIZE,
|
||||
"tifm_%s%u:%u", tifm_media_type_name(type, 2),
|
||||
fm->id, id);
|
||||
dev_set_name(&sock->dev, "tifm_%s%u:%u",
|
||||
tifm_media_type_name(type, 2), fm->id, id);
|
||||
printk(KERN_INFO DRIVER_NAME
|
||||
": %s card detected in socket %u:%u\n",
|
||||
tifm_media_type_name(type, 0), fm->id, id);
|
||||
|
Reference in New Issue
Block a user