diff options
Diffstat (limited to 'drivers')
767 files changed, 30847 insertions, 7522 deletions
diff --git a/drivers/Makefile b/drivers/Makefile index fceb71a741c3..e121b66ef082 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -57,6 +57,7 @@ obj-$(CONFIG_ATA_OVER_ETH) += block/aoe/ obj-$(CONFIG_PARIDE) += block/paride/ obj-$(CONFIG_TC) += tc/ obj-$(CONFIG_UWB) += uwb/ +obj-$(CONFIG_USB_OTG_UTILS) += usb/otg/ obj-$(CONFIG_USB) += usb/ obj-$(CONFIG_USB_MUSB_HDRC) += usb/musb/ obj-$(CONFIG_PCI) += usb/ diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index f4f632917509..b0243fd55ac0 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -312,9 +312,13 @@ config ACPI_DEBUG bool "Debug Statements" default n help - The ACPI driver can optionally report errors with a great deal - of verbosity. Saying Y enables these statements. This will increase - your kernel size by around 50K. + The ACPI subsystem can produce debug output. Saying Y enables this + output and increases the kernel size by around 50K. + + Use the acpi.debug_layer and acpi.debug_level kernel command-line + parameters documented in Documentation/acpi/debug.txt and + Documentation/kernel-parameters.txt to control the type and + amount of debug output. config ACPI_DEBUG_FUNC_TRACE bool "Additionally enable ACPI function tracing" @@ -324,14 +328,6 @@ config ACPI_DEBUG_FUNC_TRACE ACPI Debug Statements slow down ACPI processing. Function trace is about half of the penalty and is rarely useful. -config ACPI_EC - bool - default y - help - This driver is required on some systems for the proper operation of - the battery and thermal drivers. If you are compiling for a - mobile system, say Y. - config ACPI_PCI_SLOT tristate "PCI slot detection driver" default n @@ -341,10 +337,6 @@ config ACPI_PCI_SLOT help you correlate PCI bus addresses with the physical geography of your slots. If you are unsure, say N. -config ACPI_POWER - bool - default y - config ACPI_SYSTEM bool default y diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index d91c027ece8f..3c0c93300f12 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -39,19 +39,23 @@ obj-y += sleep/ obj-y += bus.o glue.o obj-y += scan.o # Keep EC driver first. Initialization of others depend on it. -obj-$(CONFIG_ACPI_EC) += ec.o +obj-y += ec.o obj-$(CONFIG_ACPI_AC) += ac.o obj-$(CONFIG_ACPI_BATTERY) += battery.o obj-$(CONFIG_ACPI_BUTTON) += button.o obj-$(CONFIG_ACPI_FAN) += fan.o obj-$(CONFIG_ACPI_DOCK) += dock.o obj-$(CONFIG_ACPI_VIDEO) += video.o +ifdef CONFIG_ACPI_VIDEO +obj-y += video_detect.o +endif + obj-y += pci_root.o pci_link.o pci_irq.o pci_bind.o obj-$(CONFIG_ACPI_PCI_SLOT) += pci_slot.o obj-$(CONFIG_ACPI_PROCESSOR) += processor.o obj-$(CONFIG_ACPI_CONTAINER) += container.o obj-$(CONFIG_ACPI_THERMAL) += thermal.o -obj-$(CONFIG_ACPI_POWER) += power.o +obj-y += power.o obj-$(CONFIG_ACPI_SYSTEM) += system.o event.o obj-$(CONFIG_ACPI_DEBUG) += debug.o obj-$(CONFIG_ACPI_NUMA) += numa.o diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c index d72a1b6c8a94..9b917dac7732 100644 --- a/drivers/acpi/ac.c +++ b/drivers/acpi/ac.c @@ -37,7 +37,6 @@ #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> -#define ACPI_AC_COMPONENT 0x00020000 #define ACPI_AC_CLASS "ac_adapter" #define ACPI_AC_DEVICE_NAME "AC Adapter" #define ACPI_AC_FILE_STATE "state" @@ -242,7 +241,7 @@ static void acpi_ac_notify(acpi_handle handle, u32 event, void *data) acpi_ac_get_state(ac); acpi_bus_generate_proc_event(device, event, (u32) ac->state); acpi_bus_generate_netlink_event(device->pnp.device_class, - device->dev.bus_id, event, + dev_name(&device->dev), event, (u32) ac->state); #ifdef CONFIG_ACPI_SYSFS_POWER kobject_uevent(&ac->charger.dev->kobj, KOBJ_CHANGE); diff --git a/drivers/acpi/acpi_memhotplug.c b/drivers/acpi/acpi_memhotplug.c index 71d21c51c45f..63a17b55b39b 100644 --- a/drivers/acpi/acpi_memhotplug.c +++ b/drivers/acpi/acpi_memhotplug.c @@ -32,7 +32,6 @@ #include <linux/memory_hotplug.h> #include <acpi/acpi_drivers.h> -#define ACPI_MEMORY_DEVICE_COMPONENT 0x08000000UL #define ACPI_MEMORY_DEVICE_CLASS "memory" #define ACPI_MEMORY_DEVICE_HID "PNP0C80" #define ACPI_MEMORY_DEVICE_NAME "Hotplug Mem Device" diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index b2133e89ad9a..a0a178dd189c 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -46,7 +46,6 @@ #define ACPI_BATTERY_VALUE_UNKNOWN 0xFFFFFFFF -#define ACPI_BATTERY_COMPONENT 0x00040000 #define ACPI_BATTERY_CLASS "battery" #define ACPI_BATTERY_DEVICE_NAME "Battery" #define ACPI_BATTERY_NOTIFY_STATUS 0x80 @@ -175,6 +174,15 @@ static int acpi_battery_get_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_CURRENT_NOW: val->intval = battery->current_now * 1000; + /* if power units are mW, convert to mA by + dividing by current voltage (mV/1000) */ + if (!battery->power_unit) { + if (battery->voltage_now) { + val->intval /= battery->voltage_now; + val->intval *= 1000; + } else + val->intval = -1; + } break; case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: @@ -782,7 +790,7 @@ static void acpi_battery_notify(acpi_handle handle, u32 event, void *data) acpi_bus_generate_proc_event(device, event, acpi_battery_present(battery)); acpi_bus_generate_netlink_event(device->pnp.device_class, - device->dev.bus_id, event, + dev_name(&device->dev), event, acpi_battery_present(battery)); #ifdef CONFIG_ACPI_SYSFS_POWER /* acpi_batter_update could remove power_supply object */ diff --git a/drivers/acpi/blacklist.c b/drivers/acpi/blacklist.c index ea92bac42c53..09c69806c1fc 100644 --- a/drivers/acpi/blacklist.c +++ b/drivers/acpi/blacklist.c @@ -176,16 +176,6 @@ static int __init dmi_enable_osi_linux(const struct dmi_system_id *d) acpi_dmi_osi_linux(1, d); /* enable */ return 0; } -static int __init dmi_disable_osi_linux(const struct dmi_system_id *d) -{ - acpi_dmi_osi_linux(0, d); /* disable */ - return 0; -} -static int __init dmi_unknown_osi_linux(const struct dmi_system_id *d) -{ - acpi_dmi_osi_linux(-1, d); /* unknown */ - return 0; -} static int __init dmi_disable_osi_vista(const struct dmi_system_id *d) { printk(KERN_NOTICE PREFIX "DMI detected: %s\n", d->ident); @@ -193,295 +183,21 @@ static int __init dmi_disable_osi_vista(const struct dmi_system_id *d) return 0; } -/* - * Most BIOS that invoke OSI(Linux) do nothing with it. - * But some cause Linux to break. - * Only a couple use it to make Linux run better. - * - * Thus, Linux should continue to disable OSI(Linux) by default, - * should continue to discourage BIOS writers from using it, and - * should whitelist the few existing systems that require it. - * - * If it appears clear a vendor isn't using OSI(Linux) - * for anything constructive, blacklist them by name to disable - * unnecessary dmesg warnings on all of their products. - */ - static struct dmi_system_id acpi_osi_dmi_table[] __initdata = { - /* - * Disable OSI(Linux) warnings on all "Acer, inc." - * - * _OSI(Linux) disables the latest Windows BIOS code: - * DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3100"), - * DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5050"), - * DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5100"), - * DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5580"), - * DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 3010"), - * _OSI(Linux) effect unknown: - * DMI_MATCH(DMI_PRODUCT_NAME, "Ferrari 5000"), - */ - /* - * note that dmi_check_system() uses strstr() - * to match sub-strings rather than !strcmp(), - * so "Acer" below matches "Acer, inc." above. - */ - /* - * Disable OSI(Linux) warnings on all "Acer" - * - * _OSI(Linux) effect unknown: - * DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5610"), - * DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 7720Z"), - * DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 5520"), - * DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 6460"), - * DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 7510"), - * - * _OSI(Linux) is a NOP: - * DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5315"), - * DMI_MATCH(DMI_PRODUCT_NAME, "Extensa 5220"), - */ - { - .callback = dmi_disable_osi_linux, - .ident = "Acer", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - }, - }, - /* - * Disable OSI(Linux) warnings on all "Apple Computer, Inc." - * Disable OSI(Linux) warnings on all "Apple Inc." - * - * _OSI(Linux) confirmed to be a NOP: - * DMI_MATCH(DMI_PRODUCT_NAME, "MacBook1,1"), - * DMI_MATCH(DMI_PRODUCT_NAME, "MacBook2,1"), - * DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro2,2"), - * DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro3,1"), - * _OSI(Linux) effect unknown: - * DMI_MATCH(DMI_PRODUCT_NAME, "MacPro2,1"), - * DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro1,1"), - */ - { - .callback = dmi_disable_osi_linux, - .ident = "Apple", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Apple"), - }, - }, - /* - * Disable OSI(Linux) warnings on all "BenQ" - * - * _OSI(Linux) confirmed to be a NOP: - * DMI_MATCH(DMI_PRODUCT_NAME, "Joybook S31"), - */ - { - .callback = dmi_disable_osi_linux, - .ident = "BenQ", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "BenQ"), - }, - }, - /* - * Disable OSI(Linux) warnings on all "Clevo Co." - * - * _OSI(Linux) confirmed to be a NOP: - * DMI_MATCH(DMI_PRODUCT_NAME, "M570RU"), - */ - { - .callback = dmi_disable_osi_linux, - .ident = "Clevo", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Clevo Co."), - }, - }, - /* - * Disable OSI(Linux) warnings on all "COMPAL" - * - * _OSI(Linux) confirmed to be a NOP: - * DMI_MATCH(DMI_BOARD_NAME, "HEL8X"), - * _OSI(Linux) unknown effect: - * DMI_MATCH(DMI_BOARD_NAME, "IFL91"), - */ - { - .callback = dmi_disable_osi_linux, - .ident = "Compal", - .matches = { - DMI_MATCH(DMI_BIOS_VENDOR, "COMPAL"), - }, - }, - { /* OSI(Linux) touches USB, unknown side-effect */ - .callback = dmi_disable_osi_linux, - .ident = "Dell Dimension 5150", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Dell DM051"), - }, - }, - { /* OSI(Linux) is a NOP */ - .callback = dmi_disable_osi_linux, - .ident = "Dell i1501", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1501"), - }, - }, - { /* OSI(Linux) effect unknown */ - .callback = dmi_unknown_osi_linux, - .ident = "Dell Latitude D830", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Latitude D830"), - }, - }, - { /* OSI(Linux) effect unknown */ - .callback = dmi_unknown_osi_linux, - .ident = "Dell OptiPlex GX620", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex GX620"), - }, - }, - { /* OSI(Linux) causes some USB initialization to not run */ - .callback = dmi_unknown_osi_linux, - .ident = "Dell OptiPlex 755", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 755"), - }, - }, - { /* OSI(Linux) effect unknown */ - .callback = dmi_unknown_osi_linux, - .ident = "Dell PE 1900", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge 1900"), - }, - }, - { /* OSI(Linux) is a NOP */ - .callback = dmi_unknown_osi_linux, - .ident = "Dell PE 1950", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge 1950"), - }, - }, - { /* OSI(Linux) is a NOP */ - .callback = dmi_disable_osi_linux, - .ident = "Dell PE R200", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge R200"), - }, - }, - { /* OSI(Linux) touches USB */ - .callback = dmi_disable_osi_linux, - .ident = "Dell PR 390", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Precision WorkStation 390"), - }, - }, - { /* OSI(Linux) touches USB */ - .callback = dmi_unknown_osi_linux, - .ident = "Dell PR 390", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Precision WorkStation 690"), - }, - }, - { /* OSI(Linux) unknown - ASL looks benign, but may effect dock/SMM */ - .callback = dmi_unknown_osi_linux, - .ident = "Dell PR M4300", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Precision M4300"), - }, - }, - { /* OSI(Linux) is a NOP */ - .callback = dmi_disable_osi_linux, - .ident = "Dell Vostro 1000", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1000"), - }, - }, - { /* OSI(Linux) effect unknown */ - .callback = dmi_unknown_osi_linux, - .ident = "Dell PE SC440", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge SC440"), - }, - }, - { /* OSI(Linux) effect unknown */ - .callback = dmi_unknown_osi_linux, - .ident = "Dialogue Flybook V5", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dialogue Technology Corporation"), - DMI_MATCH(DMI_PRODUCT_NAME, "Flybook V5"), - }, - }, - /* - * Disable OSI(Linux) warnings on all "FUJITSU SIEMENS" - * - * _OSI(Linux) disables latest Windows BIOS code: - * DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pa 2510"), - * _OSI(Linux) confirmed to be a NOP: - * DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pi 1536"), - * DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pi 1556"), - * DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 1546"), - * DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO Mobile V5505"), - * _OSI(Linux) unknown effect: - * DMI_MATCH(DMI_PRODUCT_NAME, "Amilo M1425"), - * DMI_MATCH(DMI_PRODUCT_NAME, "Amilo Si 1520"), - */ - { - .callback = dmi_disable_osi_linux, - .ident = "Fujitsu Siemens", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), - }, - }, { .callback = dmi_disable_osi_vista, .ident = "Fujitsu Siemens", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), - DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO Mobile V5505"), + DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO Mobile V5505"), }, }, + /* - * Disable OSI(Linux) warnings on all "Hewlett-Packard" - * - * _OSI(Linux) confirmed to be a NOP: - * .ident = "HP Pavilion tx 1000" - * DMI_MATCH(DMI_BOARD_NAME, "30BF"), - * .ident = "HP Pavilion dv2000" - * DMI_MATCH(DMI_BOARD_NAME, "30B5"), - * .ident = "HP Pavilion dv5000", - * DMI_MATCH(DMI_BOARD_NAME, "30A7"), - * .ident = "HP Pavilion dv6300 30BC", - * DMI_MATCH(DMI_BOARD_NAME, "30BC"), - * .ident = "HP Pavilion dv6000", - * DMI_MATCH(DMI_BOARD_NAME, "30B7"), - * DMI_MATCH(DMI_BOARD_NAME, "30B8"), - * .ident = "HP Pavilion dv9000", - * DMI_MATCH(DMI_BOARD_NAME, "30B9"), - * .ident = "HP Pavilion dv9500", - * DMI_MATCH(DMI_BOARD_NAME, "30CB"), - * .ident = "HP/Compaq Presario C500", - * DMI_MATCH(DMI_BOARD_NAME, "30C6"), - * .ident = "HP/Compaq Presario F500", - * DMI_MATCH(DMI_BOARD_NAME, "30D3"), - * _OSI(Linux) unknown effect: - * .ident = "HP Pavilion dv6500", - * DMI_MATCH(DMI_BOARD_NAME, "30D0"), + * BIOS invocation of _OSI(Linux) is almost always a BIOS bug. + * Linux ignores it, except for the machines enumerated below. */ - { - .callback = dmi_disable_osi_linux, - .ident = "Hewlett-Packard", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), - }, - }, + /* * Lenovo has a mix of systems OSI(Linux) situations * and thus we can not wildcard the vendor. @@ -519,113 +235,6 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = { DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X61"), }, }, - { - .callback = dmi_disable_osi_linux, - .ident = "Lenovo 3000 V100", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_VERSION, "LENOVO3000 V100"), - }, - }, - { - .callback = dmi_disable_osi_linux, - .ident = "Lenovo 3000 N100", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_VERSION, "3000 N100"), - }, - }, - /* - * Disable OSI(Linux) warnings on all "LG Electronics" - * - * _OSI(Linux) confirmed to be a NOP: - * DMI_MATCH(DMI_PRODUCT_NAME, "P1-J150B"), - * with DMI_MATCH(DMI_BOARD_NAME, "ROCKY"), - * - * unknown: - * DMI_MATCH(DMI_PRODUCT_NAME, "S1-MDGDG"), - * with DMI_MATCH(DMI_BOARD_NAME, "ROCKY"), - */ - { - .callback = dmi_disable_osi_linux, - .ident = "LG", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LG Electronics"), - }, - }, - /* NEC - OSI(Linux) effect unknown */ - { - .callback = dmi_unknown_osi_linux, - .ident = "NEC VERSA M360", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "NEC Computers SAS"), - DMI_MATCH(DMI_PRODUCT_NAME, "NEC VERSA M360"), - }, - }, - /* Panasonic */ - { - .callback = dmi_unknown_osi_linux, - .ident = "Panasonic", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Matsushita"), - /* Toughbook CF-52 */ - DMI_MATCH(DMI_PRODUCT_NAME, "CF-52CCABVBG"), - }, - }, - /* - * Disable OSI(Linux) warnings on all "Samsung Electronics" - * - * OSI(Linux) disables PNP0C32 and other BIOS code for Windows: - * DMI_MATCH(DMI_PRODUCT_NAME, "R40P/R41P"), - * DMI_MATCH(DMI_PRODUCT_NAME, "R59P/R60P/R61P"), - */ - { - .callback = dmi_disable_osi_linux, - .ident = "Samsung", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), - }, - }, - /* - * Disable OSI(Linux) warnings on all "Sony Corporation" - * - * _OSI(Linux) is a NOP: - * DMI_MATCH(DMI_PRODUCT_NAME, "VGN-NR11S_S"), - * DMI_MATCH(DMI_PRODUCT_NAME, "VGN-SZ38GP_C"), - * DMI_MATCH(DMI_PRODUCT_NAME, "VGN-SZ650N"), - * DMI_MATCH(DMI_PRODUCT_NAME, "VGN-TZ21MN_N"), - * _OSI(Linux) unknown effect: - * DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FZ11M"), - */ - { - .callback = dmi_disable_osi_linux, - .ident = "Sony", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), - }, - }, - /* - * Disable OSI(Linux) warnings on all "TOSHIBA" - * - * _OSI(Linux) breaks sound (bugzilla 7787): - * DMI_MATCH(DMI_PRODUCT_NAME, "Satellite P100"), - * DMI_MATCH(DMI_PRODUCT_NAME, "Satellite P105"), - * _OSI(Linux) is a NOP: - * DMI_MATCH(DMI_PRODUCT_NAME, "Satellite A100"), - * DMI_MATCH(DMI_PRODUCT_NAME, "Satellite A210"), - * _OSI(Linux) unknown effect: - * DMI_MATCH(DMI_PRODUCT_NAME, "Satellite A135"), - * DMI_MATCH(DMI_PRODUCT_NAME, "Satellite A200"), - * DMI_MATCH(DMI_PRODUCT_NAME, "Satellite P205"), - * DMI_MATCH(DMI_PRODUCT_NAME, "Satellite U305"), - */ - { - .callback = dmi_disable_osi_linux, - .ident = "Toshiba", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), - }, - }, {} }; diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index c797c6473f31..7edf6d913c13 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -688,6 +688,14 @@ void __init acpi_early_init(void) if (acpi_disabled) return; + /* + * ACPI CA initializes acpi_dbg_level to non-zero, which means + * we get debug output merely by turning on CONFIG_ACPI_DEBUG. + * Turn it off so we don't get output unless the user specifies + * acpi.debug_level. + */ + acpi_dbg_level = 0; + printk(KERN_INFO PREFIX "Core revision %08x\n", ACPI_CA_VERSION); /* enable workarounds, unless strict ACPI spec. compliance */ @@ -774,7 +782,7 @@ static int __init acpi_bus_init(void) "Unable to initialize ACPI OS objects\n"); goto error1; } -#ifdef CONFIG_ACPI_EC + /* * ACPI 2.0 requires the EC driver to be loaded and work before * the EC device is found in the namespace (i.e. before acpi_initialize_objects() @@ -785,7 +793,6 @@ static int __init acpi_bus_init(void) */ status = acpi_ec_ecdt_probe(); /* Ignore result. Not having an ECDT is not fatal. */ -#endif status = acpi_initialize_objects(ACPI_FULL_INITIALIZATION); if (ACPI_FAILURE(status)) { diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index cb046c3fc3f2..171fd914f435 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -33,7 +33,6 @@ #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> -#define ACPI_BUTTON_COMPONENT 0x00080000 #define ACPI_BUTTON_CLASS "button" #define ACPI_BUTTON_FILE_INFO "info" #define ACPI_BUTTON_FILE_STATE "state" @@ -479,7 +478,7 @@ static int acpi_button_add(struct acpi_device *device) device->wakeup.gpe_number, ACPI_GPE_TYPE_WAKE_RUN); acpi_enable_gpe(device->wakeup.gpe_device, - device->wakeup.gpe_number, ACPI_NOT_ISR); + device->wakeup.gpe_number); device->wakeup.state.enabled = 1; } diff --git a/drivers/acpi/cm_sbs.c b/drivers/acpi/cm_sbs.c index 4441e84b28a9..307963bd1043 100644 --- a/drivers/acpi/cm_sbs.c +++ b/drivers/acpi/cm_sbs.c @@ -34,7 +34,6 @@ ACPI_MODULE_NAME("cm_sbs"); #define ACPI_AC_CLASS "ac_adapter" #define ACPI_BATTERY_CLASS "battery" -#define ACPI_SBS_COMPONENT 0x00080000 #define _COMPONENT ACPI_SBS_COMPONENT static struct proc_dir_entry *acpi_ac_dir; static struct proc_dir_entry *acpi_battery_dir; @@ -105,9 +104,3 @@ void acpi_unlock_battery_dir(struct proc_dir_entry *acpi_battery_dir_param) return; } EXPORT_SYMBOL(acpi_unlock_battery_dir); - -static int __init acpi_cm_sbs_init(void) -{ - return 0; -} -subsys_initcall(acpi_cm_sbs_init); diff --git a/drivers/acpi/container.c b/drivers/acpi/container.c index 134818b265a9..17020c12623c 100644 --- a/drivers/acpi/container.c +++ b/drivers/acpi/container.c @@ -41,7 +41,6 @@ #define INSTALL_NOTIFY_HANDLER 1 #define UNINSTALL_NOTIFY_HANDLER 2 -#define ACPI_CONTAINER_COMPONENT 0x01000000 #define _COMPONENT ACPI_CONTAINER_COMPONENT ACPI_MODULE_NAME("container"); diff --git a/drivers/acpi/debug.c b/drivers/acpi/debug.c index abf36b4b1d1d..c48396892008 100644 --- a/drivers/acpi/debug.c +++ b/drivers/acpi/debug.c @@ -44,6 +44,21 @@ static const struct acpi_dlayer acpi_debug_layers[] = { ACPI_DEBUG_INIT(ACPI_CA_DISASSEMBLER), ACPI_DEBUG_INIT(ACPI_COMPILER), ACPI_DEBUG_INIT(ACPI_TOOLS), + + ACPI_DEBUG_INIT(ACPI_BUS_COMPONENT), + ACPI_DEBUG_INIT(ACPI_AC_COMPONENT), + ACPI_DEBUG_INIT(ACPI_BATTERY_COMPONENT), + ACPI_DEBUG_INIT(ACPI_BUTTON_COMPONENT), + ACPI_DEBUG_INIT(ACPI_SBS_COMPONENT), + ACPI_DEBUG_INIT(ACPI_FAN_COMPONENT), + ACPI_DEBUG_INIT(ACPI_PCI_COMPONENT), + ACPI_DEBUG_INIT(ACPI_POWER_COMPONENT), + ACPI_DEBUG_INIT(ACPI_CONTAINER_COMPONENT), + ACPI_DEBUG_INIT(ACPI_SYSTEM_COMPONENT), + ACPI_DEBUG_INIT(ACPI_THERMAL_COMPONENT), + ACPI_DEBUG_INIT(ACPI_MEMORY_DEVICE_COMPONENT), + ACPI_DEBUG_INIT(ACPI_VIDEO_COMPONENT), + ACPI_DEBUG_INIT(ACPI_PROCESSOR_COMPONENT), }; static const struct acpi_dlevel acpi_debug_levels[] = { diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index ef42316f89f5..30f3ef236ecb 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -70,7 +70,7 @@ enum ec_command { #define ACPI_EC_UDELAY_GLK 1000 /* Wait 1ms max. to get global lock */ #define ACPI_EC_UDELAY 100 /* Wait 100us before polling EC again */ -#define ACPI_EC_STORM_THRESHOLD 20 /* number of false interrupts +#define ACPI_EC_STORM_THRESHOLD 8 /* number of false interrupts per one transaction */ enum { @@ -100,8 +100,11 @@ struct transaction { u8 *rdata; unsigned short irq_count; u8 command; + u8 wi; + u8 ri; u8 wlen; u8 rlen; + bool done; }; static struct acpi_ec { @@ -178,34 +181,46 @@ static int ec_transaction_done(struct acpi_ec *ec) unsigned long flags; int ret = 0; spin_lock_irqsave(&ec->curr_lock, flags); - if (!ec->curr || (!ec->curr->wlen && !ec->curr->rlen)) + if (!ec->curr || ec->curr->done) ret = 1; spin_unlock_irqrestore(&ec->curr_lock, flags); return ret; } +static void start_transaction(struct acpi_ec *ec) +{ + ec->curr->irq_count = ec->curr->wi = ec->curr->ri = 0; + ec->curr->done = false; + acpi_ec_write_cmd(ec, ec->curr->command); +} + static void gpe_transaction(struct acpi_ec *ec, u8 status) { unsigned long flags; spin_lock_irqsave(&ec->curr_lock, flags); if (!ec->curr) goto unlock; - if (ec->curr->wlen > 0) { - if ((status & ACPI_EC_FLAG_IBF) == 0) { - acpi_ec_write_data(ec, *(ec->curr->wdata++)); - --ec->curr->wlen; - } else - /* false interrupt, state didn't change */ - ++ec->curr->irq_count; - - } else if (ec->curr->rlen > 0) { + if (ec->curr->wlen > ec->curr->wi) { + if ((status & ACPI_EC_FLAG_IBF) == 0) + acpi_ec_write_data(ec, + ec->curr->wdata[ec->curr->wi++]); + else + goto err; + } else if (ec->curr->rlen > ec->curr->ri) { if ((status & ACPI_EC_FLAG_OBF) == 1) { - *(ec->curr->rdata++) = acpi_ec_read_data(ec); - --ec->curr->rlen; + ec->curr->rdata[ec->curr->ri++] = acpi_ec_read_data(ec); + if (ec->curr->rlen == ec->curr->ri) + ec->curr->done = true; } else - /* false interrupt, state didn't change */ - ++ec->curr->irq_count; - } + goto err; + } else if (ec->curr->wlen == ec->curr->wi && + (status & ACPI_EC_FLAG_IBF) == 0) + ec->curr->done = true; + goto unlock; +err: + /* false interrupt, state didn't change */ + if (in_interrupt()) + ++ec->curr->irq_count; unlock: spin_unlock_irqrestore(&ec->curr_lock, flags); } @@ -215,6 +230,15 @@ static int acpi_ec_wait(struct acpi_ec *ec) if (wait_event_timeout(ec->wait, ec_transaction_done(ec), msecs_to_jiffies(ACPI_EC_DELAY))) return 0; + /* try restart command if we get any false interrupts */ + if (ec->curr->irq_count && + (acpi_ec_read_status(ec) & ACPI_EC_FLAG_IBF) == 0) { + pr_debug(PREFIX "controller reset, restart transaction\n"); + start_transaction(ec); + if (wait_event_timeout(ec->wait, ec_transaction_done(ec), + msecs_to_jiffies(ACPI_EC_DELAY))) + return 0; + } /* missing GPEs, switch back to poll mode */ if (printk_ratelimit()) pr_info(PREFIX "missing confirmations, " @@ -239,10 +263,10 @@ static int ec_check_sci(struct acpi_ec *ec, u8 state) static int ec_poll(struct acpi_ec *ec) { unsigned long delay = jiffies + msecs_to_jiffies(ACPI_EC_DELAY); - msleep(1); + udelay(ACPI_EC_UDELAY); while (time_before(jiffies, delay)) { gpe_transaction(ec, acpi_ec_read_status(ec)); - msleep(1); + udelay(ACPI_EC_UDELAY); if (ec_transaction_done(ec)) return 0; } @@ -259,14 +283,13 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, /* disable GPE during transaction if storm is detected */ if (test_bit(EC_FLAGS_GPE_STORM, &ec->flags)) { clear_bit(EC_FLAGS_GPE_MODE, &ec->flags); - acpi_disable_gpe(NULL, ec->gpe, ACPI_NOT_ISR); + acpi_disable_gpe(NULL, ec->gpe); } /* start transaction */ spin_lock_irqsave(&ec->curr_lock, tmp); /* following two actions should be kept atomic */ - t->irq_count = 0; ec->curr = t; - acpi_ec_write_cmd(ec, ec->curr->command); + start_transaction(ec); if (ec->curr->command == ACPI_EC_COMMAND_QUERY) clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags); spin_unlock_irqrestore(&ec->curr_lock, tmp); @@ -283,10 +306,11 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, /* check if we received SCI during transaction */ ec_check_sci(ec, acpi_ec_read_status(ec)); /* it is safe to enable GPE outside of transaction */ - acpi_enable_gpe(NULL, ec->gpe, ACPI_NOT_ISR); + acpi_enable_gpe(NULL, ec->gpe); } else if (test_bit(EC_FLAGS_GPE_MODE, &ec->flags) && t->irq_count > ACPI_EC_STORM_THRESHOLD) { - pr_debug(PREFIX "GPE storm detected\n"); + pr_info(PREFIX "GPE storm detected, " + "transactions will use polling mode\n"); set_bit(EC_FLAGS_GPE_STORM, &ec->flags); } return ret; @@ -558,17 +582,26 @@ static u32 acpi_ec_gpe_handler(void *data) pr_debug(PREFIX "~~~> interrupt\n"); status = acpi_ec_read_status(ec); - gpe_transaction(ec, status); - if (ec_transaction_done(ec) && (status & ACPI_EC_FLAG_IBF) == 0) - wake_up(&ec->wait); + if (test_bit(EC_FLAGS_GPE_MODE, &ec->flags)) { + gpe_transaction(ec, status); + if (ec_transaction_done(ec) && + (status & ACPI_EC_FLAG_IBF) == 0) + wake_up(&ec->wait); + } ec_check_sci(ec, status); if (!test_bit(EC_FLAGS_GPE_MODE, &ec->flags) && !test_bit(EC_FLAGS_NO_GPE, &ec->flags)) { /* this is non-query, must be confirmation */ - if (printk_ratelimit()) - pr_info(PREFIX "non-query interrupt received," + if (!test_bit(EC_FLAGS_GPE_STORM, &ec->flags)) { + if (printk_ratelimit()) + pr_info(PREFIX "non-query interrupt received," + " switching to interrupt mode\n"); + } else { + /* hush, STORM switches the mode every transaction */ + pr_debug(PREFIX "non-query interrupt received," " switching to interrupt mode\n"); + } set_bit(EC_FLAGS_GPE_MODE, &ec->flags); } return ACPI_INTERRUPT_HANDLED; @@ -736,7 +769,7 @@ static acpi_status ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval) { acpi_status status; - unsigned long long tmp; + unsigned long long tmp = 0; struct acpi_ec *ec = context; status = acpi_walk_resources(handle, METHOD_NAME__CRS, @@ -751,6 +784,7 @@ ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval) return status; ec->gpe = tmp; /* Use the global lock for all EC transactions? */ + tmp = 0; acpi_evaluate_integer(handle, "_GLK", NULL, &tmp); ec->global_lock = tmp; ec->handle = handle; @@ -868,7 +902,7 @@ static int ec_install_handlers(struct acpi_ec *ec) if (ACPI_FAILURE(status)) return -ENODEV; acpi_set_gpe_type(NULL, ec->gpe, ACPI_GPE_TYPE_RUNTIME); - acpi_enable_gpe(NULL, ec->gpe, ACPI_NOT_ISR); + acpi_enable_gpe(NULL, ec->gpe); status = acpi_install_address_space_handler(ec->handle, ACPI_ADR_SPACE_EC, &acpi_ec_space_handler, @@ -1007,7 +1041,7 @@ static int acpi_ec_suspend(struct acpi_device *device, pm_message_t state) /* Stop using GPE */ set_bit(EC_FLAGS_NO_GPE, &ec->flags); clear_bit(EC_FLAGS_GPE_MODE, &ec->flags); - acpi_disable_gpe(NULL, ec->gpe, ACPI_NOT_ISR); + acpi_disable_gpe(NULL, ec->gpe); return 0; } @@ -1016,7 +1050,7 @@ static int acpi_ec_resume(struct acpi_device *device) struct acpi_ec *ec = acpi_driver_data(device); /* Enable use of GPE back */ clear_bit(EC_FLAGS_NO_GPE, &ec->flags); - acpi_enable_gpe(NULL, ec->gpe, ACPI_NOT_ISR); + acpi_enable_gpe(NULL, ec->gpe); return 0; } diff --git a/drivers/acpi/events/evgpe.c b/drivers/acpi/events/evgpe.c index c5e53aae86f7..f45c74fe745e 100644 --- a/drivers/acpi/events/evgpe.c +++ b/drivers/acpi/events/evgpe.c @@ -289,8 +289,6 @@ acpi_status acpi_ev_disable_gpe(struct acpi_gpe_event_info *gpe_event_info) */ status = acpi_hw_low_disable_gpe(gpe_event_info); return_ACPI_STATUS(status); - - return_ACPI_STATUS(AE_OK); } /******************************************************************************* diff --git a/drivers/acpi/events/evxfevnt.c b/drivers/acpi/events/evxfevnt.c index 73bfd6bf962f..41554f736b68 100644 --- a/drivers/acpi/events/evxfevnt.c +++ b/drivers/acpi/events/evxfevnt.c @@ -248,21 +248,15 @@ ACPI_EXPORT_SYMBOL(acpi_set_gpe_type) * DESCRIPTION: Enable an ACPI event (general purpose) * ******************************************************************************/ -acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number, u32 flags) +acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number) { acpi_status status = AE_OK; + acpi_cpu_flags flags; struct acpi_gpe_event_info *gpe_event_info; ACPI_FUNCTION_TRACE(acpi_enable_gpe); - /* Use semaphore lock if not executing at interrupt level */ - - if (flags & ACPI_NOT_ISR) { - status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS); - if (ACPI_FAILURE(status)) { - return_ACPI_STATUS(status); - } - } + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); /* Ensure that we have a valid GPE number */ @@ -277,9 +271,7 @@ acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number, u32 flags) status = acpi_ev_enable_gpe(gpe_event_info, TRUE); unlock_and_exit: - if (flags & ACPI_NOT_ISR) { - (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); - } + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); return_ACPI_STATUS(status); } @@ -299,22 +291,15 @@ ACPI_EXPORT_SYMBOL(acpi_enable_gpe) * DESCRIPTION: Disable an ACPI event (general purpose) * ******************************************************************************/ -acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number, u32 flags) +acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number) { acpi_status status = AE_OK; + acpi_cpu_flags flags; struct acpi_gpe_event_info *gpe_event_info; ACPI_FUNCTION_TRACE(acpi_disable_gpe); - /* Use semaphore lock if not executing at interrupt level */ - - if (flags & ACPI_NOT_ISR) { - status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS); - if (ACPI_FAILURE(status)) { - return_ACPI_STATUS(status); - } - } - + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); /* Ensure that we have a valid GPE number */ gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); @@ -325,10 +310,8 @@ acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number, u32 flags) status = acpi_ev_disable_gpe(gpe_event_info); - unlock_and_exit: - if (flags & ACPI_NOT_ISR) { - (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); - } +unlock_and_exit: + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); return_ACPI_STATUS(status); } @@ -521,6 +504,9 @@ acpi_status acpi_get_event_status(u32 event, acpi_event_status * event_status) if (value) *event_status |= ACPI_EVENT_FLAG_SET; + if (acpi_gbl_fixed_event_handlers[event].handler) + *event_status |= ACPI_EVENT_FLAG_HANDLE; + return_ACPI_STATUS(status); } @@ -571,6 +557,9 @@ acpi_get_gpe_status(acpi_handle gpe_device, status = acpi_hw_get_gpe_status(gpe_event_info, event_status); + if (gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) + *event_status |= ACPI_EVENT_FLAG_HANDLE; + unlock_and_exit: if (flags & ACPI_NOT_ISR) { (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c index 60d54d1f6b19..eaaee1660bdf 100644 --- a/drivers/acpi/fan.c +++ b/drivers/acpi/fan.c @@ -34,7 +34,6 @@ #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> -#define ACPI_FAN_COMPONENT 0x00200000 #define ACPI_FAN_CLASS "fan" #define ACPI_FAN_FILE_STATE "state" diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index 24649ada08df..adec3d15810a 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -140,6 +140,46 @@ struct device *acpi_get_physical_device(acpi_handle handle) EXPORT_SYMBOL(acpi_get_physical_device); +/* ToDo: When a PCI bridge is found, return the PCI device behind the bridge + * This should work in general, but did not on a Lenovo T61 for the + * graphics card. But this must be fixed when the PCI device is + * bound and the kernel device struct is attached to the acpi device + * Note: A success call will increase reference count by one + * Do call put_device(dev) on the returned device then + */ +struct device *acpi_get_physical_pci_device(acpi_handle handle) +{ + struct device *dev; + long long device_id; + acpi_status status; + + status = + acpi_evaluate_integer(handle, "_ADR", NULL, &device_id); + + if (ACPI_FAILURE(status)) + return NULL; + + /* We need to attempt to determine whether the _ADR refers to a + PCI device or not. There's no terribly good way to do this, + so the best we can hope for is to assume that there'll never + be a device in the host bridge */ + if (device_id >= 0x10000) { + /* It looks like a PCI device. Does it exist? */ + dev = acpi_get_physical_device(handle); + } else { + /* It doesn't look like a PCI device. Does its parent + exist? */ + acpi_handle phandle; + if (acpi_get_parent(handle, &phandle)) + return NULL; + dev = acpi_get_physical_device(phandle); + } + if (!dev) + return NULL; + return dev; +} +EXPORT_SYMBOL(acpi_get_physical_pci_device); + static int acpi_bind_one(struct device *dev, acpi_handle handle) { struct acpi_device *acpi_dev; diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 4be252145cb4..c8111424dcb8 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -35,7 +35,6 @@ #include <linux/interrupt.h> #include <linux/kmod.h> #include <linux/delay.h> -#include <linux/dmi.h> #include <linux/workqueue.h> #include <linux/nmi.h> #include <linux/acpi.h> @@ -97,54 +96,44 @@ static DEFINE_SPINLOCK(acpi_res_lock); static char osi_additional_string[OSI_STRING_LENGTH_MAX]; /* - * "Ode to _OSI(Linux)" + * The story of _OSI(Linux) * - * osi_linux -- Control response to BIOS _OSI(Linux) query. + * From pre-history through Linux-2.6.22, + * Linux responded TRUE upon a BIOS OSI(Linux) query. * - * As Linux evolves, the features that it supports change. - * So an OSI string such as "Linux" is not specific enough - * to be useful across multiple versions of Linux. It - * doesn't identify any particular feature, interface, - * or even any particular version of Linux... + * Unfortunately, reference BIOS writers got wind of this + * and put OSI(Linux) in their example code, quickly exposing + * this string as ill-conceived and opening the door to + * an un-bounded number of BIOS incompatibilities. * - * Unfortunately, Linux-2.6.22 and earlier responded "yes" - * to a BIOS _OSI(Linux) query. When - * a reference mobile BIOS started using it, its use - * started to spread to many vendor platforms. - * As it is not supportable, we need to halt that spread. + * For example, OSI(Linux) was used on resume to re-POST a + * video card on one system, because Linux at that time + * could not do a speedy restore in its native driver. + * But then upon gaining quick native restore capability, + * Linux has no way to tell the BIOS to skip the time-consuming + * POST -- putting Linux at a permanent performance disadvantage. + * On another system, the BIOS writer used OSI(Linux) + * to infer native OS support for IPMI! On other systems, + * OSI(Linux) simply got in the way of Linux claiming to + * be compatible with other operating systems, exposing + * BIOS issues such as skipped device initialization. * - * Today, most BIOS references to _OSI(Linux) are noise -- - * they have no functional effect and are just dead code - * carried over from the reference BIOS. - * - * The next most common case is that _OSI(Linux) harms Linux, - * usually by causing the BIOS to follow paths that are - * not tested during Windows validation. - * - * Finally, there is a short list of platforms - * where OSI(Linux) benefits Linux. - * - * In Linux-2.6.23, OSI(Linux) is first disabled by default. - * DMI is used to disable the dmesg warning about OSI(Linux) - * on platforms where it is known to have no effect. - * But a dmesg warning remains for systems where - * we do not know if OSI(Linux) is good or bad for the system. - * DMI is also used to enable OSI(Linux) for the machines - * that are known to need it. + * So "Linux" turned out to be a really poor chose of + * OSI string, and from Linux-2.6.23 onward we respond FALSE. * * BIOS writers should NOT query _OSI(Linux) on future systems. - * It will be ignored by default, and to get Linux to - * not ignore it will require a kernel source update to - * add a DMI entry, or a boot-time "acpi_osi=Linux" invocation. + * Linux will complain on the console when it sees it, and return FALSE. + * To get Linux to return TRUE for your system will require + * a kernel source update to add a DMI entry, + * or boot with "acpi_osi=Linux" */ -#define OSI_LINUX_ENABLE 0 static struct osi_linux { unsigned int enable:1; unsigned int dmi:1; unsigned int cmdline:1; unsigned int known:1; -} osi_linux = { OSI_LINUX_ENABLE, 0, 0, 0}; +} osi_linux = { 0, 0, 0, 0}; static void __init acpi_request_region (struct acpi_generic_address *addr, unsigned int length, char *desc) @@ -1296,34 +1285,6 @@ acpi_status acpi_os_release_object(acpi_cache_t * cache, void *object) return (AE_OK); } -/** - * acpi_dmi_dump - dump DMI slots needed for blacklist entry - * - * Returns 0 on success - */ -static int acpi_dmi_dump(void) -{ - - if (!dmi_available) - return -1; - - printk(KERN_NOTICE PREFIX "DMI System Vendor: %s\n", - dmi_get_system_info(DMI_SYS_VENDOR)); - printk(KERN_NOTICE PREFIX "DMI Product Name: %s\n", - dmi_get_system_info(DMI_PRODUCT_NAME)); - printk(KERN_NOTICE PREFIX "DMI Product Version: %s\n", - dmi_get_system_info(DMI_PRODUCT_VERSION)); - printk(KERN_NOTICE PREFIX "DMI Board Name: %s\n", - dmi_get_system_info(DMI_BOARD_NAME)); - printk(KERN_NOTICE PREFIX "DMI BIOS Vendor: %s\n", - dmi_get_system_info(DMI_BIOS_VENDOR)); - printk(KERN_NOTICE PREFIX "DMI BIOS Date: %s\n", - dmi_get_system_info(DMI_BIOS_DATE)); - - return 0; -} - - /****************************************************************************** * * FUNCTION: acpi_os_validate_interface @@ -1350,21 +1311,6 @@ acpi_os_validate_interface (char *interface) osi_linux.cmdline ? " via cmdline" : osi_linux.dmi ? " via DMI" : ""); - if (!osi_linux.dmi) { - if (acpi_dmi_dump()) - printk(KERN_NOTICE PREFIX - "[please extract dmidecode output]\n"); - printk(KERN_NOTICE PREFIX - "Please send DMI info above to " - "linux-acpi@vger.kernel.org\n"); - } - if (!osi_linux.known && !osi_linux.cmdline) { - printk(KERN_NOTICE PREFIX - "If \"acpi_osi=%sLinux\" works better, " - "please notify linux-acpi@vger.kernel.org\n", - osi_linux.enable ? "!" : ""); - } - if (osi_linux.enable) return AE_OK; } diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c index 11acaee14d66..bf79d83bdfbb 100644 --- a/drivers/acpi/pci_irq.c +++ b/drivers/acpi/pci_irq.c @@ -384,6 +384,27 @@ acpi_pci_free_irq(struct acpi_prt_entry *entry, return irq; } +#ifdef CONFIG_X86_IO_APIC +extern int noioapicquirk; + +static int bridge_has_boot_interrupt_variant(struct pci_bus *bus) +{ + struct pci_bus *bus_it; + + for (bus_it = bus ; bus_it ; bus_it = bus_it->parent) { + if (!bus_it->self) + return 0; + + printk(KERN_INFO "vendor=%04x device=%04x\n", bus_it->self->vendor, + bus_it->self->device); + + if (bus_it->self->irq_reroute_variant) + return bus_it->self->irq_reroute_variant; + } + return 0; +} +#endif /* CONFIG_X86_IO_APIC */ + /* * acpi_pci_irq_lookup * success: return IRQ >= 0 @@ -413,6 +434,41 @@ acpi_pci_irq_lookup(struct pci_bus *bus, } ret = func(entry, triggering, polarity, link); + +#ifdef CONFIG_X86_IO_APIC + /* + * Some chipsets (e.g. intel 6700PXH) generate a legacy INTx when the + * IRQ entry in the chipset's IO-APIC is masked (as, e.g. the RT kernel + * does during interrupt handling). When this INTx generation cannot be + * disabled, we reroute these interrupts to their legacy equivalent to + * get rid of spurious interrupts. + */ + if (!noioapicquirk) { + switch (bridge_has_boot_interrupt_variant(bus)) { + case 0: + /* no rerouting necessary */ + break; + + case INTEL_IRQ_REROUTE_VARIANT: + /* + * Remap according to INTx routing table in 6700PXH + * specs, intel order number 302628-002, section + * 2.15.2. Other chipsets (80332, ...) have the same + * mapping and are handled here as well. + */ + printk(KERN_INFO "pci irq %d -> rerouted to legacy " + "irq %d\n", ret, (ret % 4) + 16); + ret = (ret % 4) + 16; + break; + + default: + printk(KERN_INFO "not rerouting irq %d to legacy irq: " + "unknown mapping\n", ret); + break; + } + } +#endif /* CONFIG_X86_IO_APIC */ + return ret; } diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c index fcfdef7b4fdd..e52ad91ce2dc 100644 --- a/drivers/acpi/pci_link.c +++ b/drivers/acpi/pci_link.c @@ -531,7 +531,7 @@ int __init acpi_irq_penalty_init(void) return 0; } -static int acpi_irq_balance; /* 0: static, 1: balance */ +static int acpi_irq_balance = -1; /* 0: static, 1: balance */ static int acpi_pci_link_allocate(struct acpi_pci_link *link) { @@ -950,10 +950,17 @@ device_initcall(irqrouter_init_sysfs); static int __init acpi_pci_link_init(void) { - if (acpi_noirq) return 0; + if (acpi_irq_balance == -1) { + /* no command line switch: enable balancing in IOAPIC mode */ + if (acpi_irq_model == ACPI_IRQ_MODEL_IOAPIC) + acpi_irq_balance = 1; + else + acpi_irq_balance = 0; + } + acpi_link.count = 0; INIT_LIST_HEAD(&acpi_link.entries); diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 1b8f67d21d53..5b38a026d122 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -31,6 +31,7 @@ #include <linux/spinlock.h> #include <linux/pm.h> #include <linux/pci.h> +#include <linux/pci-acpi.h> #include <linux/acpi.h> #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> @@ -193,6 +194,7 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device) unsigned long long value = 0; acpi_handle handle = NULL; struct acpi_device *child; + u32 flags, base_flags; if (!device) @@ -210,6 +212,13 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device) device->ops.bind = acpi_pci_bind; + /* + * All supported architectures that use ACPI have support for + * PCI domains, so we indicate this in _OSC support capabilities. + */ + flags = base_flags = OSC_PCI_SEGMENT_GROUPS_SUPPORT; + pci_acpi_osc_support(device->handle, flags); + /* * Segment * ------- @@ -335,6 +344,17 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device) list_for_each_entry(child, &device->children, node) acpi_pci_bridge_scan(child); + /* Indicate support for various _OSC capabilities. */ + if (pci_ext_cfg_avail(root->bus->self)) + flags |= OSC_EXT_PCI_CONFIG_SUPPORT; + if (pcie_aspm_enabled()) + flags |= OSC_ACTIVE_STATE_PWR_SUPPORT | + OSC_CLOCK_PWR_CAPABILITY_SUPPORT; + if (pci_msi_enabled()) + flags |= OSC_MSI_SUPPORT; + if (flags != base_flags) + pci_acpi_osc_support(device->handle, flags); + end: if (result) { if (!list_empty(&root->node)) @@ -376,15 +396,9 @@ static int acpi_pci_root_remove(struct acpi_device *device, int type) static int __init acpi_pci_root_init(void) { - if (acpi_pci_disabled) return 0; - /* DEBUG: - acpi_dbg_layer = ACPI_PCI_COMPONENT; - acpi_dbg_level = 0xFFFFFFFF; - */ - if (acpi_bus_register_driver(&acpi_pci_root_driver) < 0) return -ENODEV; diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index a1718e56103b..bb7d50dd2818 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -44,9 +44,8 @@ #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> -#define _COMPONENT ACPI_POWER_COMPONENT +#define _COMPONENT ACPI_POWER_COMPONENT ACPI_MODULE_NAME("power"); -#define ACPI_POWER_COMPONENT 0x00800000 #define ACPI_POWER_CLASS "power_resource" #define ACPI_POWER_DEVICE_NAME "Power Resource" #define ACPI_POWER_FILE_INFO "info" @@ -153,7 +152,8 @@ static int acpi_power_get_state(acpi_handle handle, int *state) ACPI_POWER_RESOURCE_STATE_OFF; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] is %s\n", - acpi_ut_get_node_name(handle), state ? "on" : "off")); + acpi_ut_get_node_name(handle), + *state ? "on" : "off")); return 0; } @@ -516,11 +516,6 @@ int acpi_power_transition(struct acpi_device *device, int state) cl = &device->power.states[device->power.state].resources; tl = &device->power.states[state].resources; - if (!cl->count && !tl->count) { - result = -ENODEV; - goto end; - } - /* TBD: Resources must be ordered. */ /* diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index 24a362f8034c..34948362f41d 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -59,7 +59,6 @@ #include <acpi/acpi_drivers.h> #include <acpi/processor.h> -#define ACPI_PROCESSOR_COMPONENT 0x01000000 #define ACPI_PROCESSOR_CLASS "processor" #define ACPI_PROCESSOR_DEVICE_NAME "Processor" #define ACPI_PROCESSOR_FILE_INFO "info" @@ -89,6 +88,7 @@ static int acpi_processor_handle_eject(struct acpi_processor *pr); static const struct acpi_device_id processor_device_ids[] = { + {ACPI_PROCESSOR_OBJECT_HID, 0}, {ACPI_PROCESSOR_HID, 0}, {"", 0}, }; @@ -409,7 +409,7 @@ static int acpi_processor_remove_fs(struct acpi_device *device) /* Use the acpiid in MADT to map cpus in case of SMP */ #ifndef CONFIG_SMP -static int get_cpu_id(acpi_handle handle, u32 acpi_id) {return -1;} +static int get_cpu_id(acpi_handle handle, int type, u32 acpi_id) { return -1; } #else static struct acpi_table_madt *madt; @@ -428,27 +428,35 @@ static int map_lapic_id(struct acpi_subtable_header *entry, } static int map_lsapic_id(struct acpi_subtable_header *entry, - u32 acpi_id, int *apic_id) + int device_declaration, u32 acpi_id, int *apic_id) { struct acpi_madt_local_sapic *lsapic = (struct acpi_madt_local_sapic *)entry; + u32 tmp = (lsapic->id << 8) | lsapic->eid; + /* Only check enabled APICs*/ - if (lsapic->lapic_flags & ACPI_MADT_ENABLED) { - /* First check against id */ - if (lsapic->processor_id == acpi_id) { - *apic_id = (lsapic->id << 8) | lsapic->eid; - return 1; - /* Check against optional uid */ - } else if (entry->length >= 16 && - lsapic->uid == acpi_id) { - *apic_id = lsapic->uid; - return 1; - } - } + if (!(lsapic->lapic_flags & ACPI_MADT_ENABLED)) + return 0; + + /* Device statement declaration type */ + if (device_declaration) { + if (entry->length < 16) + printk(KERN_ERR PREFIX + "Invalid LSAPIC with Device type processor (SAPIC ID %#x)\n", + tmp); + else if (lsapic->uid == acpi_id) + goto found; + /* Processor statement declaration type */ + } else if (lsapic->processor_id == acpi_id) + goto found; + return 0; +found: + *apic_id = tmp; + return 1; } -static int map_madt_entry(u32 acpi_id) +static int map_madt_entry(int type, u32 acpi_id) { unsigned long madt_end, entry; int apic_id = -1; @@ -469,7 +477,7 @@ static int map_madt_entry(u32 acpi_id) if (map_lapic_id(header, acpi_id, &apic_id)) break; } else if (header->type == ACPI_MADT_TYPE_LOCAL_SAPIC) { - if (map_lsapic_id(header, acpi_id, &apic_id)) + if (map_lsapic_id(header, type, acpi_id, &apic_id)) break; } entry += header->length; @@ -477,7 +485,7 @@ static int map_madt_entry(u32 acpi_id) return apic_id; } -static int map_mat_entry(acpi_handle handle, u32 acpi_id) +static int map_mat_entry(acpi_handle handle, int type, u32 acpi_id) { struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; union acpi_object *obj; @@ -500,7 +508,7 @@ static int map_mat_entry(acpi_handle handle, u32 acpi_id) if (header->type == ACPI_MADT_TYPE_LOCAL_APIC) { map_lapic_id(header, acpi_id, &apic_id); } else if (header->type == ACPI_MADT_TYPE_LOCAL_SAPIC) { - map_lsapic_id(header, acpi_id, &apic_id); + map_lsapic_id(header, type, acpi_id, &apic_id); } exit: @@ -509,14 +517,14 @@ exit: return apic_id; } -static int get_cpu_id(acpi_handle handle, u32 acpi_id) +static int get_cpu_id(acpi_handle handle, int type, u32 acpi_id) { int i; int apic_id = -1; - apic_id = map_mat_entry(handle, acpi_id); + apic_id = map_mat_entry(handle, type, acpi_id); if (apic_id == -1) - apic_id = map_madt_entry(acpi_id); + apic_id = map_madt_entry(type, acpi_id); if (apic_id == -1) return apic_id; @@ -532,15 +540,16 @@ static int get_cpu_id(acpi_handle handle, u32 acpi_id) Driver Interface -------------------------------------------------------------------------- */ -static int acpi_processor_get_info(struct acpi_processor *pr, unsigned has_uid) +static int acpi_processor_get_info(struct acpi_device *device) { acpi_status status = 0; union acpi_object object = { 0 }; struct acpi_buffer buffer = { sizeof(union acpi_object), &object }; - int cpu_index; + struct acpi_processor *pr; + int cpu_index, device_declaration = 0; static int cpu0_initialized; - + pr = acpi_driver_data(device); if (!pr) return -EINVAL; @@ -561,22 +570,23 @@ static int acpi_processor_get_info(struct acpi_processor *pr, unsigned has_uid) ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No bus mastering arbitration control\n")); - /* Check if it is a Device with HID and UID */ - if (has_uid) { + if (!strcmp(acpi_device_hid(device), ACPI_PROCESSOR_HID)) { + /* + * Declared with "Device" statement; match _UID. + * Note that we don't handle string _UIDs yet. + */ unsigned long long value; status = acpi_evaluate_integer(pr->handle, METHOD_NAME__UID, NULL, &value); if (ACPI_FAILURE(status)) { - printk(KERN_ERR PREFIX "Evaluating processor _UID\n"); + printk(KERN_ERR PREFIX + "Evaluating processor _UID [%#x]\n", status); return -ENODEV; } + device_declaration = 1; pr->acpi_id = value; } else { - /* - * Evalute the processor object. Note that it is common on SMP to - * have the first (boot) processor with a valid PBLK address while - * all others have a NULL address. - */ + /* Declared with "Processor" statement; match ProcessorID */ status = acpi_evaluate_object(pr->handle, NULL, NULL, &buffer); if (ACPI_FAILURE(status)) { printk(KERN_ERR PREFIX "Evaluating processor object\n"); @@ -584,12 +594,13 @@ static int acpi_processor_get_info(struct acpi_processor *pr, unsigned has_uid) } /* - * TBD: Synch processor ID (via LAPIC/LSAPIC structures) on SMP. - * >>> 'acpi_get_processor_id(acpi_id, &id)' in arch/xxx/acpi.c - */ + * TBD: Synch processor ID (via LAPIC/LSAPIC structures) on SMP. + * >>> 'acpi_get_processor_id(acpi_id, &id)' in + * arch/xxx/acpi.c + */ pr->acpi_id = object.processor.proc_id; } - cpu_index = get_cpu_id(pr->handle, pr->acpi_id); + cpu_index = get_cpu_id(pr->handle, device_declaration, pr->acpi_id); /* Handle UP system running SMP kernel, with no LAPIC in MADT */ if (!cpu0_initialized && (cpu_index == -1) && @@ -661,7 +672,7 @@ static int __cpuinit acpi_processor_start(struct acpi_device *device) pr = acpi_driver_data(device); - result = acpi_processor_get_info(pr, device->flags.unique_id); + result = acpi_processor_get_info(device); if (result) { /* Processor is physically not present */ return 0; @@ -761,20 +772,20 @@ static void acpi_processor_notify(acpi_handle handle, u32 event, void *data) acpi_bus_generate_proc_event(device, event, pr->performance_platform_limit); acpi_bus_generate_netlink_event(device->pnp.device_class, - device->dev.bus_id, event, + dev_name(&device->dev), event, pr->performance_platform_limit); break; case ACPI_PROCESSOR_NOTIFY_POWER: acpi_processor_cst_has_changed(pr); acpi_bus_generate_proc_event(device, event, 0); acpi_bus_generate_netlink_event(device->pnp.device_class, - device->dev.bus_id, event, 0); + dev_name(&device->dev), event, 0); break; case ACPI_PROCESSOR_NOTIFY_THROTTLING: acpi_processor_tstate_has_changed(pr); acpi_bus_generate_proc_event(device, event, 0); acpi_bus_generate_netlink_event(device->pnp.device_class, - device->dev.bus_id, event, 0); + dev_name(&device->dev), event, 0); default: ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Unsupported event [0x%x]\n", event)); diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 81b40ed5379e..5f8d746a9b81 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -59,7 +59,6 @@ #include <acpi/processor.h> #include <asm/processor.h> -#define ACPI_PROCESSOR_COMPONENT 0x01000000 #define ACPI_PROCESSOR_CLASS "processor" #define _COMPONENT ACPI_PROCESSOR_COMPONENT ACPI_MODULE_NAME("processor_idle"); diff --git a/drivers/acpi/processor_perflib.c b/drivers/acpi/processor_perflib.c index dbcf260ea93f..0d7b772bef50 100644 --- a/drivers/acpi/processor_perflib.c +++ b/drivers/acpi/processor_perflib.c @@ -44,9 +44,9 @@ #endif #include <acpi/acpi_bus.h> +#include <acpi/acpi_drivers.h> #include <acpi/processor.h> -#define ACPI_PROCESSOR_COMPONENT 0x01000000 #define ACPI_PROCESSOR_CLASS "processor" #define ACPI_PROCESSOR_FILE_PERFORMANCE "performance" #define _COMPONENT ACPI_PROCESSOR_COMPONENT diff --git a/drivers/acpi/processor_thermal.c b/drivers/acpi/processor_thermal.c index ef34b18f95ca..b1eb376fae45 100644 --- a/drivers/acpi/processor_thermal.c +++ b/drivers/acpi/processor_thermal.c @@ -40,7 +40,6 @@ #include <acpi/processor.h> #include <acpi/acpi_drivers.h> -#define ACPI_PROCESSOR_COMPONENT 0x01000000 #define ACPI_PROCESSOR_CLASS "processor" #define _COMPONENT ACPI_PROCESSOR_COMPONENT ACPI_MODULE_NAME("processor_thermal"); diff --git a/drivers/acpi/processor_throttling.c b/drivers/acpi/processor_throttling.c index 3da2df93d924..a0c38c94a8a0 100644 --- a/drivers/acpi/processor_throttling.c +++ b/drivers/acpi/processor_throttling.c @@ -38,9 +38,9 @@ #include <asm/uaccess.h> #include <acpi/acpi_bus.h> +#include <acpi/acpi_drivers.h> #include <acpi/processor.h> -#define ACPI_PROCESSOR_COMPONENT 0x01000000 #define ACPI_PROCESSOR_CLASS "processor" #define _COMPONENT ACPI_PROCESSOR_COMPONENT ACPI_MODULE_NAME("processor_throttling"); diff --git a/drivers/acpi/reboot.c b/drivers/acpi/reboot.c index 755baf2ca70a..a6b662c00b67 100644 --- a/drivers/acpi/reboot.c +++ b/drivers/acpi/reboot.c @@ -15,28 +15,9 @@ void acpi_reboot(void) rr = &acpi_gbl_FADT.reset_register; - /* - * Is the ACPI reset register supported? - * - * According to ACPI 3.0, FADT.flags.RESET_REG_SUP indicates - * whether the ACPI reset mechanism is supported. - * - * However, some boxes have this bit clear, yet a valid - * ACPI_RESET_REG & RESET_VALUE, and ACPI reboot is the only - * mechanism that works for them after S3. - * - * This suggests that other operating systems may not be checking - * the RESET_REG_SUP bit, and are using other means to decide - * whether to use the ACPI reboot mechanism or not. - * - * So when acpi reboot is requested, - * only the reset_register is checked. If the following - * conditions are met, it indicates that the reset register is supported. - * a. reset_register is not zero - * b. the access width is eight - * c. the bit_offset is zero - */ - if (!(rr->address) || rr->bit_width != 8 || rr->bit_offset != 0) + /* Is the reset register supported? */ + if (!(acpi_gbl_FADT.flags & ACPI_FADT_RESET_REGISTER) || + rr->bit_width != 8 || rr->bit_offset != 0) return; reset_value = acpi_gbl_FADT.reset_value; diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index a9dda8e0f9f9..39b7233c3485 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -109,8 +109,7 @@ static int acpi_bus_hot_remove_device(void *context) return 0; ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Hot-removing device %s...\n", device->dev.bus_id)); - + "Hot-removing device %s...\n", dev_name(&device->dev))); if (acpi_bus_trim(device, 1)) { printk(KERN_ERR PREFIX @@ -460,7 +459,7 @@ static int acpi_device_register(struct acpi_device *device, acpi_device_bus_id->instance_no = 0; list_add_tail(&acpi_device_bus_id->node, &acpi_bus_id_list); } - sprintf(device->dev.bus_id, "%s:%02x", acpi_device_bus_id->bus_id, acpi_device_bus_id->instance_no); + dev_set_name(&device->dev, "%s:%02x", acpi_device_bus_id->bus_id, acpi_device_bus_id->instance_no); if (device->parent) { list_add_tail(&device->node, &device->parent->children); @@ -484,7 +483,8 @@ static int acpi_device_register(struct acpi_device *device, result = acpi_device_setup_files(device); if(result) - printk(KERN_ERR PREFIX "Error creating sysfs interface for device %s\n", device->dev.bus_id); + printk(KERN_ERR PREFIX "Error creating sysfs interface for device %s\n", + dev_name(&device->dev)); device->removal_type = ACPI_BUS_REMOVAL_NORMAL; return 0; @@ -751,16 +751,6 @@ static int acpi_bus_get_wakeup_device_flags(struct acpi_device *device) if (!acpi_match_device_ids(device, button_device_ids)) device->wakeup.flags.run_wake = 1; - /* - * Don't set Power button GPE as run_wake - * if Fixed Power button is used - */ - if (!strcmp(device->pnp.hardware_id, "PNP0C0C") && - !(acpi_gbl_FADT.flags & ACPI_FADT_POWER_BUTTON)) { - device->wakeup.flags.run_wake = 0; - device->wakeup.flags.valid = 0; - } - end: if (ACPI_FAILURE(status)) device->flags.wake_capable = 0; @@ -919,36 +909,6 @@ static void acpi_device_get_busid(struct acpi_device *device, } } -static int -acpi_video_bus_match(struct acpi_device *device) -{ - acpi_handle h_dummy; - - if (!device) - return -EINVAL; - - /* Since there is no HID, CID for ACPI Video drivers, we have - * to check well known required nodes for each feature we support. - */ - - /* Does this device able to support video switching ? */ - if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOD", &h_dummy)) && - ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOS", &h_dummy))) - return 0; - - /* Does this device able to retrieve a video ROM ? */ - if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_ROM", &h_dummy))) - return 0; - - /* Does this device able to configure which video head to be POSTed ? */ - if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_VPO", &h_dummy)) && - ACPI_SUCCESS(acpi_get_handle(device->handle, "_GPD", &h_dummy)) && - ACPI_SUCCESS(acpi_get_handle(device->handle, "_SPD", &h_dummy))) - return 0; - - return -ENODEV; -} - /* * acpi_bay_match - see if a device is an ejectable driver bay * @@ -1031,7 +991,7 @@ static void acpi_device_set_id(struct acpi_device *device, will get autoloaded and the device might still match against another driver. */ - if (ACPI_SUCCESS(acpi_video_bus_match(device))) + if (acpi_is_video_device(device)) cid_add = ACPI_VIDEO_HID; else if (ACPI_SUCCESS(acpi_bay_match(device))) cid_add = ACPI_BAY_HID; @@ -1043,7 +1003,7 @@ static void acpi_device_set_id(struct acpi_device *device, hid = ACPI_POWER_HID; break; case ACPI_BUS_TYPE_PROCESSOR: - hid = ACPI_PROCESSOR_HID; + hid = ACPI_PROCESSOR_OBJECT_HID; break; case ACPI_BUS_TYPE_SYSTEM: hid = ACPI_SYSTEM_HID; diff --git a/drivers/acpi/sleep/main.c b/drivers/acpi/sleep/main.c index 80c0868d0480..28a691cc625e 100644 --- a/drivers/acpi/sleep/main.c +++ b/drivers/acpi/sleep/main.c @@ -90,6 +90,18 @@ void __init acpi_old_suspend_ordering(void) old_suspend_ordering = true; } +/* + * According to the ACPI specification the BIOS should make sure that ACPI is + * enabled and SCI_EN bit is set on wake-up from S1 - S3 sleep states. Still, + * some BIOSes don't do that and therefore we use acpi_enable() to enable ACPI + * on such systems during resume. Unfortunately that doesn't help in + * particularly pathological cases in which SCI_EN has to be set directly on + * resume, although the specification states very clearly that this flag is + * owned by the hardware. The set_sci_en_on_resume variable will be set in such + * cases. + */ +static bool set_sci_en_on_resume; + /** * acpi_pm_disable_gpes - Disable the GPEs. */ @@ -235,7 +247,11 @@ static int acpi_suspend_enter(suspend_state_t pm_state) } /* If ACPI is not enabled by the BIOS, we need to enable it here. */ - acpi_enable(); + if (set_sci_en_on_resume) + acpi_set_register(ACPI_BITREG_SCI_ENABLE, 1); + else + acpi_enable(); + /* Reprogram control registers and execute _BFS */ acpi_leave_sleep_state_prep(acpi_state); @@ -323,6 +339,12 @@ static int __init init_old_suspend_ordering(const struct dmi_system_id *d) return 0; } +static int __init init_set_sci_en_on_resume(const struct dmi_system_id *d) +{ + set_sci_en_on_resume = true; + return 0; +} + static struct dmi_system_id __initdata acpisleep_dmi_table[] = { { .callback = init_old_suspend_ordering, @@ -340,6 +362,22 @@ static struct dmi_system_id __initdata acpisleep_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "HP xw4600 Workstation"), }, }, + { + .callback = init_set_sci_en_on_resume, + .ident = "Apple MacBook 1,1", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Computer, Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBook1,1"), + }, + }, + { + .callback = init_set_sci_en_on_resume, + .ident = "Apple MacMini 1,1", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Computer, Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Macmini1,1"), + }, + }, {}, }; #endif /* CONFIG_SUSPEND */ diff --git a/drivers/acpi/sleep/proc.c b/drivers/acpi/sleep/proc.c index 631ee2ee2ca0..4dbc2271acf5 100644 --- a/drivers/acpi/sleep/proc.c +++ b/drivers/acpi/sleep/proc.c @@ -367,7 +367,7 @@ acpi_system_wakeup_device_seq_show(struct seq_file *seq, void *offset) if (ldev) seq_printf(seq, "%s:%s", ldev->bus ? ldev->bus->name : "no-bus", - ldev->bus_id); + dev_name(ldev)); seq_printf(seq, "\n"); put_device(ldev); diff --git a/drivers/acpi/sleep/wakeup.c b/drivers/acpi/sleep/wakeup.c index 38655eb132dc..dea4c23df764 100644 --- a/drivers/acpi/sleep/wakeup.c +++ b/drivers/acpi/sleep/wakeup.c @@ -88,7 +88,7 @@ void acpi_enable_wakeup_device(u8 sleep_state) spin_unlock(&acpi_device_lock); if (!dev->wakeup.flags.run_wake) acpi_enable_gpe(dev->wakeup.gpe_device, - dev->wakeup.gpe_number, ACPI_ISR); + dev->wakeup.gpe_number); spin_lock(&acpi_device_lock); } spin_unlock(&acpi_device_lock); @@ -122,7 +122,7 @@ void acpi_disable_wakeup_device(u8 sleep_state) ACPI_GPE_TYPE_WAKE_RUN); /* Re-enable it, since set_gpe_type will disable it */ acpi_enable_gpe(dev->wakeup.gpe_device, - dev->wakeup.gpe_number, ACPI_NOT_ISR); + dev->wakeup.gpe_number); spin_lock(&acpi_device_lock); } continue; @@ -133,7 +133,7 @@ void acpi_disable_wakeup_device(u8 sleep_state) /* Never disable run-wake GPE */ if (!dev->wakeup.flags.run_wake) { acpi_disable_gpe(dev->wakeup.gpe_device, - dev->wakeup.gpe_number, ACPI_NOT_ISR); + dev->wakeup.gpe_number); acpi_clear_gpe(dev->wakeup.gpe_device, dev->wakeup.gpe_number, ACPI_NOT_ISR); } @@ -162,7 +162,7 @@ static int __init acpi_wakeup_device_init(void) dev->wakeup.gpe_number, ACPI_GPE_TYPE_WAKE_RUN); acpi_enable_gpe(dev->wakeup.gpe_device, - dev->wakeup.gpe_number, ACPI_NOT_ISR); + dev->wakeup.gpe_number); dev->wakeup.state.enabled = 1; spin_lock(&acpi_device_lock); } diff --git a/drivers/acpi/system.c b/drivers/acpi/system.c index 1d74171b7940..6e4107f82403 100644 --- a/drivers/acpi/system.c +++ b/drivers/acpi/system.c @@ -78,9 +78,15 @@ static ssize_t acpi_table_show(struct kobject *kobj, container_of(bin_attr, struct acpi_table_attr, attr); struct acpi_table_header *table_header = NULL; acpi_status status; + char name[ACPI_NAME_SIZE]; + + if (strncmp(table_attr->name, "NULL", 4)) + memcpy(name, table_attr->name, ACPI_NAME_SIZE); + else + memcpy(name, "\0\0\0\0", 4); status = - acpi_get_table(table_attr->name, table_attr->instance, + acpi_get_table(name, table_attr->instance, &table_header); if (ACPI_FAILURE(status)) return -ENODEV; @@ -95,21 +101,24 @@ static void acpi_table_attr_init(struct acpi_table_attr *table_attr, struct acpi_table_header *header = NULL; struct acpi_table_attr *attr = NULL; - memcpy(table_attr->name, table_header->signature, ACPI_NAME_SIZE); + if (table_header->signature[0] != '\0') + memcpy(table_attr->name, table_header->signature, + ACPI_NAME_SIZE); + else + memcpy(table_attr->name, "NULL", 4); list_for_each_entry(attr, &acpi_table_attr_list, node) { - if (!memcmp(table_header->signature, attr->name, - ACPI_NAME_SIZE)) + if (!memcmp(table_attr->name, attr->name, ACPI_NAME_SIZE)) if (table_attr->instance < attr->instance) table_attr->instance = attr->instance; } table_attr->instance++; if (table_attr->instance > 1 || (table_attr->instance == 1 && - !acpi_get_table(table_header-> - signature, 2, - &header))) - sprintf(table_attr->name + 4, "%d", table_attr->instance); + !acpi_get_table + (table_header->signature, 2, &header))) + sprintf(table_attr->name + ACPI_NAME_SIZE, "%d", + table_attr->instance); table_attr->attr.size = 0; table_attr->attr.read = acpi_table_show; @@ -167,7 +176,6 @@ static int acpi_system_sysfs_init(void) #define COUNT_ERROR 2 /* other */ #define NUM_COUNTERS_EXTRA 3 -#define ACPI_EVENT_VALID 0x01 struct event_counter { u32 count; u32 flags; @@ -312,12 +320,6 @@ static int get_status(u32 index, acpi_event_status *status, acpi_handle *handle) } else if (index < (num_gpes + ACPI_NUM_FIXED_EVENTS)) result = acpi_get_event_status(index - num_gpes, status); - /* - * sleep/power button GPE/Fixed Event is enabled after acpi_system_init, - * check the status at runtime and mark it as valid once it's enabled - */ - if (!result && (*status & ACPI_EVENT_FLAG_ENABLED)) - all_counters[index].flags |= ACPI_EVENT_VALID; end: return result; } @@ -346,12 +348,14 @@ static ssize_t counter_show(struct kobject *kobj, if (result) goto end; - if (!(all_counters[index].flags & ACPI_EVENT_VALID)) - size += sprintf(buf + size, " invalid"); + if (!(status & ACPI_EVENT_FLAG_HANDLE)) + size += sprintf(buf + size, " invalid"); else if (status & ACPI_EVENT_FLAG_ENABLED) - size += sprintf(buf + size, " enable"); + size += sprintf(buf + size, " enabled"); + else if (status & ACPI_EVENT_FLAG_WAKE_ENABLED) + size += sprintf(buf + size, " wake_enabled"); else - size += sprintf(buf + size, " disable"); + size += sprintf(buf + size, " disabled"); end: size += sprintf(buf + size, "\n"); @@ -385,7 +389,7 @@ static ssize_t counter_set(struct kobject *kobj, if (result) goto end; - if (!(all_counters[index].flags & ACPI_EVENT_VALID)) { + if (!(status & ACPI_EVENT_FLAG_HANDLE)) { printk(KERN_WARNING PREFIX "Can not change Invalid GPE/Fixed Event status\n"); return -EINVAL; @@ -394,10 +398,10 @@ static ssize_t counter_set(struct kobject *kobj, if (index < num_gpes) { if (!strcmp(buf, "disable\n") && (status & ACPI_EVENT_FLAG_ENABLED)) - result = acpi_disable_gpe(handle, index, ACPI_NOT_ISR); + result = acpi_disable_gpe(handle, index); else if (!strcmp(buf, "enable\n") && !(status & ACPI_EVENT_FLAG_ENABLED)) - result = acpi_enable_gpe(handle, index, ACPI_NOT_ISR); + result = acpi_enable_gpe(handle, index); else if (!strcmp(buf, "clear\n") && (status & ACPI_EVENT_FLAG_SET)) result = acpi_clear_gpe(handle, index, ACPI_NOT_ISR); diff --git a/drivers/acpi/tables/tbfadt.c b/drivers/acpi/tables/tbfadt.c index 2c7885e7ffba..2817158fb6a1 100644 --- a/drivers/acpi/tables/tbfadt.c +++ b/drivers/acpi/tables/tbfadt.c @@ -304,7 +304,7 @@ static void acpi_tb_convert_fadt(void) * The ACPI 1.0 reserved fields that will be zeroed are the bytes located at * offset 45, 55, 95, and the word located at offset 109, 110. */ - if (acpi_gbl_FADT.header.revision < 3) { + if (acpi_gbl_FADT.header.revision < FADT2_REVISION_ID) { acpi_gbl_FADT.preferred_profile = 0; acpi_gbl_FADT.pstate_control = 0; acpi_gbl_FADT.cst_control = 0; diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index ad6cae938f0b..073ff09218a9 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -47,7 +47,6 @@ #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> -#define ACPI_THERMAL_COMPONENT 0x04000000 #define ACPI_THERMAL_CLASS "thermal_zone" #define ACPI_THERMAL_DEVICE_NAME "Thermal Zone" #define ACPI_THERMAL_FILE_STATE "state" @@ -576,7 +575,7 @@ static int acpi_thermal_critical(struct acpi_thermal *tz) acpi_bus_generate_proc_event(tz->device, ACPI_THERMAL_NOTIFY_CRITICAL, tz->trips.critical.flags.enabled); acpi_bus_generate_netlink_event(tz->device->pnp.device_class, - tz->device->dev.bus_id, + dev_name(&tz->device->dev), ACPI_THERMAL_NOTIFY_CRITICAL, tz->trips.critical.flags.enabled); @@ -605,7 +604,7 @@ static int acpi_thermal_hot(struct acpi_thermal *tz) acpi_bus_generate_proc_event(tz->device, ACPI_THERMAL_NOTIFY_HOT, tz->trips.hot.flags.enabled); acpi_bus_generate_netlink_event(tz->device->pnp.device_class, - tz->device->dev.bus_id, + dev_name(&tz->device->dev), ACPI_THERMAL_NOTIFY_HOT, tz->trips.hot.flags.enabled); @@ -1592,14 +1591,14 @@ static void acpi_thermal_notify(acpi_handle handle, u32 event, void *data) acpi_thermal_check(tz); acpi_bus_generate_proc_event(device, event, 0); acpi_bus_generate_netlink_event(device->pnp.device_class, - device->dev.bus_id, event, 0); + dev_name(&device->dev), event, 0); break; case ACPI_THERMAL_NOTIFY_DEVICES: acpi_thermal_trips_update(tz, ACPI_TRIPS_REFRESH_DEVICES); acpi_thermal_check(tz); acpi_bus_generate_proc_event(device, event, 0); acpi_bus_generate_netlink_event(device->pnp.device_class, - device->dev.bus_id, event, 0); + dev_name(&device->dev), event, 0); break; default: ACPI_DEBUG_PRINT((ACPI_DB_INFO, diff --git a/drivers/acpi/toshiba_acpi.c b/drivers/acpi/toshiba_acpi.c index 66aac06f2ac5..25f531d892de 100644 --- a/drivers/acpi/toshiba_acpi.c +++ b/drivers/acpi/toshiba_acpi.c @@ -848,8 +848,6 @@ static int __init toshiba_acpi_init(void) ret = input_register_polled_device(toshiba_acpi.poll_dev); if (ret) { printk(MY_ERR "unable to register kill-switch input device\n"); - rfkill_free(toshiba_acpi.rfk_dev); - toshiba_acpi.rfk_dev = NULL; toshiba_acpi_exit(); return ret; } diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c index e827be36ee8d..f844941089bb 100644 --- a/drivers/acpi/utils.c +++ b/drivers/acpi/utils.c @@ -259,34 +259,26 @@ acpi_evaluate_integer(acpi_handle handle, struct acpi_object_list *arguments, unsigned long long *data) { acpi_status status = AE_OK; - union acpi_object *element; + union acpi_object element; struct acpi_buffer buffer = { 0, NULL }; - if (!data) return AE_BAD_PARAMETER; - element = kzalloc(sizeof(union acpi_object), irqs_disabled() ? GFP_ATOMIC: GFP_KERNEL); - if (!element) - return AE_NO_MEMORY; - buffer.length = sizeof(union acpi_object); - buffer.pointer = element; + buffer.pointer = &element; status = acpi_evaluate_object(handle, pathname, arguments, &buffer); if (ACPI_FAILURE(status)) { acpi_util_eval_error(handle, pathname, status); - kfree(element); return status; } - if (element->type != ACPI_TYPE_INTEGER) { + if (element.type != ACPI_TYPE_INTEGER) { acpi_util_eval_error(handle, pathname, AE_BAD_DATA); - kfree(element); return AE_BAD_DATA; } - *data = element->integer.value; - kfree(element); + *data = element.integer.value; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Return value [%llu]\n", *data)); diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index a29b0ccac65a..baa441929720 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -41,7 +41,6 @@ #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> -#define ACPI_VIDEO_COMPONENT 0x08000000 #define ACPI_VIDEO_CLASS "video" #define ACPI_VIDEO_BUS_NAME "Video Bus" #define ACPI_VIDEO_DEVICE_NAME "Video Device" @@ -739,7 +738,8 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device) device->cap._DSS = 1; } - max_level = acpi_video_init_brightness(device); + if (acpi_video_backlight_support()) + max_level = acpi_video_init_brightness(device); if (device->cap._BCL && device->cap._BCM && max_level > 0) { int result; @@ -785,18 +785,21 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device) printk(KERN_ERR PREFIX "Create sysfs link\n"); } - if (device->cap._DCS && device->cap._DSS){ - static int count = 0; - char *name; - name = kzalloc(MAX_NAME_LEN, GFP_KERNEL); - if (!name) - return; - sprintf(name, "acpi_video%d", count++); - device->output_dev = video_output_register(name, - NULL, device, &acpi_output_properties); - kfree(name); + + if (acpi_video_display_switch_support()) { + + if (device->cap._DCS && device->cap._DSS) { + static int count; + char *name; + name = kzalloc(MAX_NAME_LEN, GFP_KERNEL); + if (!name) + return; + sprintf(name, "acpi_video%d", count++); + device->output_dev = video_output_register(name, + NULL, device, &acpi_output_properties); + kfree(name); + } } - return; } /* @@ -842,11 +845,16 @@ static void acpi_video_bus_find_cap(struct acpi_video_bus *video) static int acpi_video_bus_check(struct acpi_video_bus *video) { acpi_status status = -ENOENT; - + struct device *dev; if (!video) return -EINVAL; + dev = acpi_get_physical_pci_device(video->device->handle); + if (!dev) + return -ENODEV; + put_device(dev); + /* Since there is no HID, CID and so on for VGA driver, we have * to check well known required nodes. */ @@ -2094,12 +2102,6 @@ static int __init acpi_video_init(void) { int result = 0; - - /* - acpi_dbg_level = 0xFFFFFFFF; - acpi_dbg_layer = 0x08000000; - */ - acpi_video_dir = proc_mkdir(ACPI_VIDEO_CLASS, acpi_root_dir); if (!acpi_video_dir) return -ENODEV; diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c new file mode 100644 index 000000000000..f022eb6f5637 --- /dev/null +++ b/drivers/acpi/video_detect.c @@ -0,0 +1,267 @@ +/* + * Copyright (C) 2008 SuSE Linux Products GmbH + * Thomas Renninger <trenn@suse.de> + * + * May be copied or modified under the terms of the GNU General Public License + * + * video_detect.c: + * Provides acpi_is_video_device() for early scanning of ACPI devices in scan.c + * There a Linux specific (Spec does not provide a HID for video devices) is + * assinged + * + * After PCI devices are glued with ACPI devices + * acpi_get_physical_pci_device() can be called to identify ACPI graphics + * devices for which a real graphics card is plugged in + * + * Now acpi_video_get_capabilities() can be called to check which + * capabilities the graphics cards plugged in support. The check for general + * video capabilities will be triggered by the first caller of + * acpi_video_get_capabilities(NULL); which will happen when the first + * backlight (or display output) switching supporting driver calls: + * acpi_video_backlight_support(); + * + * Depending on whether ACPI graphics extensions (cmp. ACPI spec Appendix B) + * are available, video.ko should be used to handle the device. + * + * Otherwise vendor specific drivers like thinkpad_acpi, asus_acpi, + * sony_acpi,... can take care about backlight brightness and display output + * switching. + * + * If CONFIG_ACPI_VIDEO is neither set as "compiled in" (y) nor as a module (m) + * this file will not be compiled, acpi_video_get_capabilities() and + * acpi_video_backlight_support() will always return 0 and vendor specific + * drivers always can handle backlight. + * + */ + +#include <linux/acpi.h> +#include <linux/dmi.h> + +ACPI_MODULE_NAME("video"); +#define _COMPONENT ACPI_VIDEO_COMPONENT + +static long acpi_video_support; +static bool acpi_video_caps_checked; + +static acpi_status +acpi_backlight_cap_match(acpi_handle handle, u32 level, void *context, + void **retyurn_value) +{ + long *cap = context; + acpi_handle h_dummy; + + if (ACPI_SUCCESS(acpi_get_handle(handle, "_BCM", &h_dummy)) && + ACPI_SUCCESS(acpi_get_handle(handle, "_BCL", &h_dummy))) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found generic backlight " + "support\n")); + *cap |= ACPI_VIDEO_BACKLIGHT; + /* We have backlight support, no need to scan further */ + return AE_CTRL_TERMINATE; + } + return 0; +} + +/* Returns true if the device is a video device which can be handled by + * video.ko. + * The device will get a Linux specific CID added in scan.c to + * identify the device as an ACPI graphics device + * Be aware that the graphics device may not be physically present + * Use acpi_video_get_capabilities() to detect general ACPI video + * capabilities of present cards + */ +long acpi_is_video_device(struct acpi_device *device) +{ + acpi_handle h_dummy; + long video_caps = 0; + + if (!device) + return 0; + + /* Does this device able to support video switching ? */ + if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOD", &h_dummy)) && + ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOS", &h_dummy))) + video_caps |= ACPI_VIDEO_OUTPUT_SWITCHING; + + /* Does this device able to retrieve a video ROM ? */ + if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_ROM", &h_dummy))) + video_caps |= ACPI_VIDEO_ROM_AVAILABLE; + + /* Does this device able to configure which video head to be POSTed ? */ + if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_VPO", &h_dummy)) && + ACPI_SUCCESS(acpi_get_handle(device->handle, "_GPD", &h_dummy)) && + ACPI_SUCCESS(acpi_get_handle(device->handle, "_SPD", &h_dummy))) + video_caps |= ACPI_VIDEO_DEVICE_POSTING; + + /* Only check for backlight functionality if one of the above hit. */ + if (video_caps) + acpi_walk_namespace(ACPI_TYPE_DEVICE, device->handle, + ACPI_UINT32_MAX, acpi_backlight_cap_match, + &video_caps, NULL); + + return video_caps; +} +EXPORT_SYMBOL(acpi_is_video_device); + +static acpi_status +find_video(acpi_handle handle, u32 lvl, void *context, void **rv) +{ + long *cap = context; + struct device *dev; + struct acpi_device *acpi_dev; + + const struct acpi_device_id video_ids[] = { + {ACPI_VIDEO_HID, 0}, + {"", 0}, + }; + if (acpi_bus_get_device(handle, &acpi_dev)) + return AE_OK; + + if (!acpi_match_device_ids(acpi_dev, video_ids)) { + dev = acpi_get_physical_pci_device(handle); + if (!dev) + return AE_OK; + put_device(dev); + *cap |= acpi_is_video_device(acpi_dev); + } + return AE_OK; +} + +/* + * Returns the video capabilities of a specific ACPI graphics device + * + * if NULL is passed as argument all ACPI devices are enumerated and + * all graphics capabilities of physically present devices are + * summerized and returned. This is cached and done only once. + */ +long acpi_video_get_capabilities(acpi_handle graphics_handle) +{ + long caps = 0; + struct acpi_device *tmp_dev; + acpi_status status; + + if (acpi_video_caps_checked && graphics_handle == NULL) + return acpi_video_support; + + if (!graphics_handle) { + /* Only do the global walk through all graphics devices once */ + acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, find_video, + &caps, NULL); + /* There might be boot param flags set already... */ + acpi_video_support |= caps; + acpi_video_caps_checked = 1; + /* Add blacklists here. Be careful to use the right *DMI* bits + * to still be able to override logic via boot params, e.g.: + * + * if (dmi_name_in_vendors("XY")) { + * acpi_video_support |= + * ACPI_VIDEO_OUTPUT_SWITCHING_DMI_VENDOR; + * acpi_video_support |= + * ACPI_VIDEO_BACKLIGHT_DMI_VENDOR; + *} + */ + } else { + status = acpi_bus_get_device(graphics_handle, &tmp_dev); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "Invalid device")); + return 0; + } + acpi_walk_namespace(ACPI_TYPE_DEVICE, graphics_handle, + ACPI_UINT32_MAX, find_video, + &caps, NULL); + } + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "We have 0x%lX video support %s %s\n", + graphics_handle ? caps : acpi_video_support, + graphics_handle ? "on device " : "in general", + graphics_handle ? acpi_device_bid(tmp_dev) : "")); + return caps; +} +EXPORT_SYMBOL(acpi_video_get_capabilities); + +/* Returns true if video.ko can do backlight switching */ +int acpi_video_backlight_support(void) +{ + /* + * We must check whether the ACPI graphics device is physically plugged + * in. Therefore this must be called after binding PCI and ACPI devices + */ + if (!acpi_video_caps_checked) + acpi_video_get_capabilities(NULL); + + /* First check for boot param -> highest prio */ + if (acpi_video_support & ACPI_VIDEO_BACKLIGHT_FORCE_VENDOR) + return 0; + else if (acpi_video_support & ACPI_VIDEO_BACKLIGHT_FORCE_VIDEO) + return 1; + + /* Then check for DMI blacklist -> second highest prio */ + if (acpi_video_support & ACPI_VIDEO_BACKLIGHT_DMI_VENDOR) + return 0; + else if (acpi_video_support & ACPI_VIDEO_BACKLIGHT_DMI_VIDEO) + return 1; + + /* Then go the default way */ + return acpi_video_support & ACPI_VIDEO_BACKLIGHT; +} +EXPORT_SYMBOL(acpi_video_backlight_support); + +/* + * Returns true if video.ko can do display output switching. + * This does not work well/at all with binary graphics drivers + * which disable system io ranges and do it on their own. + */ +int acpi_video_display_switch_support(void) +{ + if (!acpi_video_caps_checked) + acpi_video_get_capabilities(NULL); + + if (acpi_video_support & ACPI_VIDEO_OUTPUT_SWITCHING_FORCE_VENDOR) + return 0; + else if (acpi_video_support & ACPI_VIDEO_OUTPUT_SWITCHING_FORCE_VIDEO) + return 1; + + if (acpi_video_support & ACPI_VIDEO_OUTPUT_SWITCHING_DMI_VENDOR) + return 0; + else if (acpi_video_support & ACPI_VIDEO_OUTPUT_SWITCHING_DMI_VIDEO) + return 1; + + return acpi_video_support & ACPI_VIDEO_OUTPUT_SWITCHING; +} +EXPORT_SYMBOL(acpi_video_display_switch_support); + +/* + * Use acpi_display_output=vendor/video or acpi_backlight=vendor/video + * To force that backlight or display output switching is processed by vendor + * specific acpi drivers or video.ko driver. + */ +int __init acpi_backlight(char *str) +{ + if (str == NULL || *str == '\0') + return 1; + else { + if (!strcmp("vendor", str)) + acpi_video_support |= + ACPI_VIDEO_BACKLIGHT_FORCE_VENDOR; + if (!strcmp("video", str)) + acpi_video_support |= + ACPI_VIDEO_OUTPUT_SWITCHING_FORCE_VIDEO; + } + return 1; +} +__setup("acpi_backlight=", acpi_backlight); + +int __init acpi_display_output(char *str) +{ + if (str == NULL || *str == '\0') + return 1; + else { + if (!strcmp("vendor", str)) + acpi_video_support |= + ACPI_VIDEO_OUTPUT_SWITCHING_FORCE_VENDOR; + if (!strcmp("video", str)) + acpi_video_support |= + ACPI_VIDEO_OUTPUT_SWITCHING_FORCE_VIDEO; + } + return 1; +} +__setup("acpi_display_output=", acpi_display_output); diff --git a/drivers/acpi/wmi.c b/drivers/acpi/wmi.c index 47cd7baf9b1b..8a8b377712c9 100644 --- a/drivers/acpi/wmi.c +++ b/drivers/acpi/wmi.c @@ -660,7 +660,7 @@ static void acpi_wmi_notify(acpi_handle handle, u32 event, void *data) wblock->handler(event, wblock->handler_data); acpi_bus_generate_netlink_event( - device->pnp.device_class, device->dev.bus_id, + device->pnp.device_class, dev_name(&device->dev), event, 0); break; } diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 82af7011f2dd..4214bfb13bbd 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -612,7 +612,7 @@ u64 ata_tf_read_block(struct ata_taskfile *tf, struct ata_device *dev) if (tf->flags & ATA_TFLAG_LBA48) { block |= (u64)tf->hob_lbah << 40; block |= (u64)tf->hob_lbam << 32; - block |= tf->hob_lbal << 24; + block |= (u64)tf->hob_lbal << 24; } else block |= (tf->device & 0xf) << 24; @@ -1712,6 +1712,8 @@ unsigned ata_exec_internal_sg(struct ata_device *dev, else tag = 0; + if (test_and_set_bit(tag, &ap->qc_allocated)) + BUG(); qc = __ata_qc_from_tag(ap, tag); qc->tag = tag; @@ -4024,6 +4026,7 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = { /* Weird ATAPI devices */ { "TORiSAN DVD-ROM DRD-N216", NULL, ATA_HORKAGE_MAX_SEC_128 }, + { "QUANTUM DAT DAT72-000", NULL, ATA_HORKAGE_ATAPI_MOD16_DMA }, /* Devices we expect to fail diagnostics */ @@ -4444,7 +4447,8 @@ int atapi_check_dma(struct ata_queued_cmd *qc) /* Don't allow DMA if it isn't multiple of 16 bytes. Quite a * few ATAPI devices choke on such DMA requests. */ - if (unlikely(qc->nbytes & 15)) + if (!(qc->dev->horkage & ATA_HORKAGE_ATAPI_MOD16_DMA) && + unlikely(qc->nbytes & 15)) return 1; if (ap->ops->check_atapi_dma) @@ -4561,6 +4565,37 @@ void swap_buf_le16(u16 *buf, unsigned int buf_words) } /** + * ata_qc_new - Request an available ATA command, for queueing + * @ap: Port associated with device @dev + * @dev: Device from whom we request an available command structure + * + * LOCKING: + * None. + */ + +static struct ata_queued_cmd *ata_qc_new(struct ata_port *ap) +{ + struct ata_queued_cmd *qc = NULL; + unsigned int i; + + /* no command while frozen */ + if (unlikely(ap->pflags & ATA_PFLAG_FROZEN)) + return NULL; + + /* the last tag is reserved for internal command. */ + for (i = 0; i < ATA_MAX_QUEUE - 1; i++) + if (!test_and_set_bit(i, &ap->qc_allocated)) { + qc = __ata_qc_from_tag(ap, i); + break; + } + + if (qc) + qc->tag = i; + + return qc; +} + +/** * ata_qc_new_init - Request an available ATA command, and initialize it * @dev: Device from whom we request an available command structure * @tag: command tag @@ -4569,20 +4604,16 @@ void swap_buf_le16(u16 *buf, unsigned int buf_words) * None. */ -struct ata_queued_cmd *ata_qc_new_init(struct ata_device *dev, int tag) +struct ata_queued_cmd *ata_qc_new_init(struct ata_device *dev) { struct ata_port *ap = dev->link->ap; struct ata_queued_cmd *qc; - if (unlikely(ap->pflags & ATA_PFLAG_FROZEN)) - return NULL; - - qc = __ata_qc_from_tag(ap, tag); + qc = ata_qc_new(ap); if (qc) { qc->scsicmd = NULL; qc->ap = ap; qc->dev = dev; - qc->tag = tag; ata_qc_reinit(qc); } @@ -4590,6 +4621,31 @@ struct ata_queued_cmd *ata_qc_new_init(struct ata_device *dev, int tag) return qc; } +/** + * ata_qc_free - free unused ata_queued_cmd + * @qc: Command to complete + * + * Designed to free unused ata_queued_cmd object + * in case something prevents using it. + * + * LOCKING: + * spin_lock_irqsave(host lock) + */ +void ata_qc_free(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + unsigned int tag; + + WARN_ON(qc == NULL); /* ata_qc_from_tag _might_ return NULL */ + + qc->flags = 0; + tag = qc->tag; + if (likely(ata_tag_valid(tag))) { + qc->tag = ATA_TAG_POISON; + clear_bit(tag, &ap->qc_allocated); + } +} + void __ata_qc_complete(struct ata_queued_cmd *qc) { struct ata_port *ap = qc->ap; @@ -5934,7 +5990,7 @@ static void ata_port_detach(struct ata_port *ap) * to us. Restore SControl and disable all existing devices. */ __ata_port_for_each_link(link, ap) { - sata_scr_write(link, SCR_CONTROL, link->saved_scontrol); + sata_scr_write(link, SCR_CONTROL, link->saved_scontrol & 0xff0); ata_link_for_each_dev(dev, link) ata_dev_disable(dev); } diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 8077bdf5d30d..32da9a93ce44 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -610,9 +610,6 @@ void ata_scsi_error(struct Scsi_Host *host) if (ata_ncq_enabled(dev)) ehc->saved_ncq_enabled |= 1 << devno; } - - /* set last reset timestamp to some time in the past */ - ehc->last_reset = jiffies - 60 * HZ; } ap->pflags |= ATA_PFLAG_EH_IN_PROGRESS; @@ -2281,17 +2278,21 @@ int ata_eh_reset(struct ata_link *link, int classify, if (link->flags & ATA_LFLAG_NO_SRST) softreset = NULL; - now = jiffies; - deadline = ata_deadline(ehc->last_reset, ATA_EH_RESET_COOL_DOWN); - if (time_before(now, deadline)) - schedule_timeout_uninterruptible(deadline - now); + /* make sure each reset attemp is at least COOL_DOWN apart */ + if (ehc->i.flags & ATA_EHI_DID_RESET) { + now = jiffies; + WARN_ON(time_after(ehc->last_reset, now)); + deadline = ata_deadline(ehc->last_reset, + ATA_EH_RESET_COOL_DOWN); + if (time_before(now, deadline)) + schedule_timeout_uninterruptible(deadline - now); + } spin_lock_irqsave(ap->lock, flags); ap->pflags |= ATA_PFLAG_RESETTING; spin_unlock_irqrestore(ap->lock, flags); ata_eh_about_to_do(link, NULL, ATA_EH_RESET); - ehc->last_reset = jiffies; ata_link_for_each_dev(dev, link) { /* If we issue an SRST then an ATA drive (not ATAPI) @@ -2379,7 +2380,6 @@ int ata_eh_reset(struct ata_link *link, int classify, /* * Perform reset */ - ehc->last_reset = jiffies; if (ata_is_host_link(link)) ata_eh_freeze_port(ap); @@ -2391,6 +2391,7 @@ int ata_eh_reset(struct ata_link *link, int classify, reset == softreset ? "soft" : "hard"); /* mark that this EH session started with reset */ + ehc->last_reset = jiffies; if (reset == hardreset) ehc->i.flags |= ATA_EHI_DID_HARDRESET; else @@ -2535,7 +2536,7 @@ int ata_eh_reset(struct ata_link *link, int classify, ata_eh_done(link, NULL, ATA_EH_RESET); if (slave) ata_eh_done(slave, NULL, ATA_EH_RESET); - ehc->last_reset = jiffies; + ehc->last_reset = jiffies; /* update to completion time */ ehc->i.action |= ATA_EH_REVALIDATE; rc = 0; diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index bbb30d882f05..424494ac7efb 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -190,7 +190,7 @@ static ssize_t ata_scsi_park_show(struct device *device, struct ata_port *ap; struct ata_link *link; struct ata_device *dev; - unsigned long flags; + unsigned long flags, now; unsigned int uninitialized_var(msecs); int rc = 0; @@ -208,10 +208,11 @@ static ssize_t ata_scsi_park_show(struct device *device, } link = dev->link; + now = jiffies; if (ap->pflags & ATA_PFLAG_EH_IN_PROGRESS && link->eh_context.unloaded_mask & (1 << dev->devno) && - time_after(dev->unpark_deadline, jiffies)) - msecs = jiffies_to_msecs(dev->unpark_deadline - jiffies); + time_after(dev->unpark_deadline, now)) + msecs = jiffies_to_msecs(dev->unpark_deadline - now); else msecs = 0; @@ -708,11 +709,7 @@ static struct ata_queued_cmd *ata_scsi_qc_new(struct ata_device *dev, { struct ata_queued_cmd *qc; - if (cmd->request->tag != -1) - qc = ata_qc_new_init(dev, cmd->request->tag); - else - qc = ata_qc_new_init(dev, 0); - + qc = ata_qc_new_init(dev); if (qc) { qc->scsicmd = cmd; qc->scsidone = done; @@ -1107,17 +1104,7 @@ static int ata_scsi_dev_config(struct scsi_device *sdev, depth = min(sdev->host->can_queue, ata_id_queue_depth(dev->id)); depth = min(ATA_MAX_QUEUE - 1, depth); - - /* - * If this device is behind a port multiplier, we have - * to share the tag map between all devices on that PMP. - * Set up the shared tag map here and we get automatic. - */ - if (dev->link->ap->pmp_link) - scsi_init_shared_tag_map(sdev->host, ATA_MAX_QUEUE - 1); - - scsi_set_tag_type(sdev, MSG_SIMPLE_TAG); - scsi_activate_tcq(sdev, depth); + scsi_adjust_queue_depth(sdev, MSG_SIMPLE_TAG, depth); } return 0; @@ -1957,11 +1944,6 @@ static unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf) hdr[1] |= (1 << 7); memcpy(rbuf, hdr, sizeof(hdr)); - - /* if ncq, set tags supported */ - if (ata_id_has_ncq(args->id)) - rbuf[7] |= (1 << 1); - memcpy(&rbuf[8], "ATA ", 8); ata_id_string(args->id, &rbuf[16], ATA_ID_PROD, 16); ata_id_string(args->id, &rbuf[32], ATA_ID_FW_REV, 4); @@ -3387,7 +3369,7 @@ static void ata_scsi_remove_dev(struct ata_device *dev) if (sdev) { ata_dev_printk(dev, KERN_INFO, "detaching (SCSI %s)\n", - sdev->sdev_gendev.bus_id); + dev_name(&sdev->sdev_gendev)); scsi_remove_device(sdev); scsi_device_put(sdev); diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c index 4b4739486327..9033d164c4ec 100644 --- a/drivers/ata/libata-sff.c +++ b/drivers/ata/libata-sff.c @@ -1227,10 +1227,19 @@ fsm_start: /* ATA PIO protocol */ if (unlikely((status & ATA_DRQ) == 0)) { /* handle BSY=0, DRQ=0 as error */ - if (likely(status & (ATA_ERR | ATA_DF))) + if (likely(status & (ATA_ERR | ATA_DF))) { /* device stops HSM for abort/error */ qc->err_mask |= AC_ERR_DEV; - else { + + /* If diagnostic failed and this is + * IDENTIFY, it's likely a phantom + * device. Mark hint. + */ + if (qc->dev->horkage & + ATA_HORKAGE_DIAGNOSTIC) + qc->err_mask |= + AC_ERR_NODEV_HINT; + } else { /* HSM violation. Let EH handle this. * Phantom devices also trigger this * condition. Mark hint. diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index d3831d39bdaa..fe2839e58774 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -74,7 +74,7 @@ extern struct ata_link *ata_dev_phys_link(struct ata_device *dev); extern void ata_force_cbl(struct ata_port *ap); extern u64 ata_tf_to_lba(const struct ata_taskfile *tf); extern u64 ata_tf_to_lba48(const struct ata_taskfile *tf); -extern struct ata_queued_cmd *ata_qc_new_init(struct ata_device *dev, int tag); +extern struct ata_queued_cmd *ata_qc_new_init(struct ata_device *dev); extern int ata_build_rw_tf(struct ata_taskfile *tf, struct ata_device *dev, u64 block, u32 n_block, unsigned int tf_flags, unsigned int tag); @@ -103,6 +103,7 @@ extern int ata_dev_configure(struct ata_device *dev); extern int sata_down_spd_limit(struct ata_link *link); extern int ata_down_xfermask_limit(struct ata_device *dev, unsigned int sel); extern void ata_sg_clean(struct ata_queued_cmd *qc); +extern void ata_qc_free(struct ata_queued_cmd *qc); extern void ata_qc_issue(struct ata_queued_cmd *qc); extern void __ata_qc_complete(struct ata_queued_cmd *qc); extern int atapi_check_dma(struct ata_queued_cmd *qc); @@ -118,22 +119,6 @@ extern struct ata_port *ata_port_alloc(struct ata_host *host); extern void ata_dev_enable_pm(struct ata_device *dev, enum link_pm policy); extern void ata_lpm_schedule(struct ata_port *ap, enum link_pm); -/** - * ata_qc_free - free unused ata_queued_cmd - * @qc: Command to complete - * - * Designed to free unused ata_queued_cmd object - * in case something prevents using it. - * - * LOCKING: - * spin_lock_irqsave(host lock) - */ -static inline void ata_qc_free(struct ata_queued_cmd *qc) -{ - qc->flags = 0; - qc->tag = ATA_TAG_POISON; -} - /* libata-acpi.c */ #ifdef CONFIG_ATA_ACPI extern void ata_acpi_associate_sata_port(struct ata_port *ap); diff --git a/drivers/ata/pata_cs5535.c b/drivers/ata/pata_cs5535.c index 1b2d4a0f5f74..8b236af84c2e 100644 --- a/drivers/ata/pata_cs5535.c +++ b/drivers/ata/pata_cs5535.c @@ -72,7 +72,6 @@ /** * cs5535_cable_detect - detect cable type * @ap: Port to detect on - * @deadline: deadline jiffies for the operation * * Perform cable detection for ATA66 capable cable. Return a libata * cable type. diff --git a/drivers/ata/pata_cs5536.c b/drivers/ata/pata_cs5536.c index 73f8332cb679..afed92976198 100644 --- a/drivers/ata/pata_cs5536.c +++ b/drivers/ata/pata_cs5536.c @@ -110,7 +110,6 @@ static inline int cs5536_write(struct pci_dev *pdev, int reg, int val) /** * cs5536_cable_detect - detect cable type * @ap: Port to detect on - * @deadline: deadline jiffies for the operation * * Perform cable detection for ATA66 capable cable. Return a libata * cable type. diff --git a/drivers/ata/pata_pcmcia.c b/drivers/ata/pata_pcmcia.c index 271cb64d429e..64b2e2281ee7 100644 --- a/drivers/ata/pata_pcmcia.c +++ b/drivers/ata/pata_pcmcia.c @@ -416,6 +416,7 @@ static struct pcmcia_device_id pcmcia_devices[] = { PCMCIA_DEVICE_PROD_ID1("STI Flash", 0xe4a13209), PCMCIA_DEVICE_PROD_ID12("STI", "Flash 5.0", 0xbf2df18d, 0x8cb57a0e), PCMCIA_MFC_DEVICE_PROD_ID12(1, "SanDisk", "ConnectPlus", 0x7a954bd9, 0x74be00c6), + PCMCIA_DEVICE_PROD_ID2("Flash Card", 0x5a362506), PCMCIA_DEVICE_NULL, }; diff --git a/drivers/ata/pata_sch.c b/drivers/ata/pata_sch.c index c8cc027789fe..6aeeeeb34124 100644 --- a/drivers/ata/pata_sch.c +++ b/drivers/ata/pata_sch.c @@ -83,7 +83,7 @@ static struct ata_port_operations sch_pata_ops = { }; static struct ata_port_info sch_port_info = { - .flags = 0, + .flags = ATA_FLAG_SLAVE_POSS, .pio_mask = ATA_PIO4, /* pio0-4 */ .mwdma_mask = ATA_MWDMA2, /* mwdma0-2 */ .udma_mask = ATA_UDMA5, /* udma0-5 */ diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c index fae3841de0d8..6f1460614325 100644 --- a/drivers/ata/sata_nv.c +++ b/drivers/ata/sata_nv.c @@ -307,10 +307,10 @@ static int nv_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val); static void nv_nf2_freeze(struct ata_port *ap); static void nv_nf2_thaw(struct ata_port *ap); +static int nv_nf2_hardreset(struct ata_link *link, unsigned int *class, + unsigned long deadline); static void nv_ck804_freeze(struct ata_port *ap); static void nv_ck804_thaw(struct ata_port *ap); -static int nv_hardreset(struct ata_link *link, unsigned int *class, - unsigned long deadline); static int nv_adma_slave_config(struct scsi_device *sdev); static int nv_adma_check_atapi_dma(struct ata_queued_cmd *qc); static void nv_adma_qc_prep(struct ata_queued_cmd *qc); @@ -405,17 +405,8 @@ static struct scsi_host_template nv_swncq_sht = { .slave_configure = nv_swncq_slave_config, }; -/* OSDL bz3352 reports that some nv controllers can't determine device - * signature reliably and nv_hardreset is implemented to work around - * the problem. This was reported on nf3 and it's unclear whether any - * other controllers are affected. However, the workaround has been - * applied to all variants and there isn't much to gain by trying to - * find out exactly which ones are affected at this point especially - * because NV has moved over to ahci for newer controllers. - */ static struct ata_port_operations nv_common_ops = { .inherits = &ata_bmdma_port_ops, - .hardreset = nv_hardreset, .scr_read = nv_scr_read, .scr_write = nv_scr_write, }; @@ -429,12 +420,22 @@ static struct ata_port_operations nv_generic_ops = { .hardreset = ATA_OP_NULL, }; +/* OSDL bz3352 reports that nf2/3 controllers can't determine device + * signature reliably. Also, the following thread reports detection + * failure on cold boot with the standard debouncing timing. + * + * http://thread.gmane.org/gmane.linux.ide/34098 + * + * Debounce with hotplug timing and request follow-up SRST. + */ static struct ata_port_operations nv_nf2_ops = { .inherits = &nv_common_ops, .freeze = nv_nf2_freeze, .thaw = nv_nf2_thaw, + .hardreset = nv_nf2_hardreset, }; +/* CK804 finally gets hardreset right */ static struct ata_port_operations nv_ck804_ops = { .inherits = &nv_common_ops, .freeze = nv_ck804_freeze, @@ -443,7 +444,7 @@ static struct ata_port_operations nv_ck804_ops = { }; static struct ata_port_operations nv_adma_ops = { - .inherits = &nv_common_ops, + .inherits = &nv_ck804_ops, .check_atapi_dma = nv_adma_check_atapi_dma, .sff_tf_read = nv_adma_tf_read, @@ -467,7 +468,7 @@ static struct ata_port_operations nv_adma_ops = { }; static struct ata_port_operations nv_swncq_ops = { - .inherits = &nv_common_ops, + .inherits = &nv_generic_ops, .qc_defer = ata_std_qc_defer, .qc_prep = nv_swncq_qc_prep, @@ -1553,6 +1554,17 @@ static void nv_nf2_thaw(struct ata_port *ap) iowrite8(mask, scr_addr + NV_INT_ENABLE); } +static int nv_nf2_hardreset(struct ata_link *link, unsigned int *class, + unsigned long deadline) +{ + bool online; + int rc; + + rc = sata_link_hardreset(link, sata_deb_timing_hotplug, deadline, + &online, NULL); + return online ? -EAGAIN : rc; +} + static void nv_ck804_freeze(struct ata_port *ap) { void __iomem *mmio_base = ap->host->iomap[NV_MMIO_BAR]; @@ -1605,21 +1617,6 @@ static void nv_mcp55_thaw(struct ata_port *ap) ata_sff_thaw(ap); } -static int nv_hardreset(struct ata_link *link, unsigned int *class, - unsigned long deadline) -{ - int rc; - - /* SATA hardreset fails to retrieve proper device signature on - * some controllers. Request follow up SRST. For more info, - * see http://bugzilla.kernel.org/show_bug.cgi?id=3352 - */ - rc = sata_sff_hardreset(link, class, deadline); - if (rc) - return rc; - return -EAGAIN; -} - static void nv_adma_error_handler(struct ata_port *ap) { struct nv_adma_port_priv *pp = ap->private_data; diff --git a/drivers/ata/sata_promise.c b/drivers/ata/sata_promise.c index 750d8cdc00cd..ba9a2570a742 100644 --- a/drivers/ata/sata_promise.c +++ b/drivers/ata/sata_promise.c @@ -153,6 +153,10 @@ static void pdc_freeze(struct ata_port *ap); static void pdc_sata_freeze(struct ata_port *ap); static void pdc_thaw(struct ata_port *ap); static void pdc_sata_thaw(struct ata_port *ap); +static int pdc_pata_softreset(struct ata_link *link, unsigned int *class, + unsigned long deadline); +static int pdc_sata_hardreset(struct ata_link *link, unsigned int *class, + unsigned long deadline); static void pdc_error_handler(struct ata_port *ap); static void pdc_post_internal_cmd(struct ata_queued_cmd *qc); static int pdc_pata_cable_detect(struct ata_port *ap); @@ -186,6 +190,7 @@ static struct ata_port_operations pdc_sata_ops = { .scr_read = pdc_sata_scr_read, .scr_write = pdc_sata_scr_write, .port_start = pdc_sata_port_start, + .hardreset = pdc_sata_hardreset, }; /* First-generation chips need a more restrictive ->check_atapi_dma op */ @@ -200,6 +205,7 @@ static struct ata_port_operations pdc_pata_ops = { .freeze = pdc_freeze, .thaw = pdc_thaw, .port_start = pdc_common_port_start, + .softreset = pdc_pata_softreset, }; static const struct ata_port_info pdc_port_info[] = { @@ -693,6 +699,20 @@ static void pdc_sata_thaw(struct ata_port *ap) readl(host_mmio + hotplug_offset); /* flush */ } +static int pdc_pata_softreset(struct ata_link *link, unsigned int *class, + unsigned long deadline) +{ + pdc_reset_port(link->ap); + return ata_sff_softreset(link, class, deadline); +} + +static int pdc_sata_hardreset(struct ata_link *link, unsigned int *class, + unsigned long deadline) +{ + pdc_reset_port(link->ap); + return sata_sff_hardreset(link, class, deadline); +} + static void pdc_error_handler(struct ata_port *ap) { if (!(ap->pflags & ATA_PFLAG_FROZEN)) diff --git a/drivers/ata/sata_via.c b/drivers/ata/sata_via.c index 62367fe4d5dc..c18935f0bda2 100644 --- a/drivers/ata/sata_via.c +++ b/drivers/ata/sata_via.c @@ -602,8 +602,10 @@ static int svia_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) rc = vt8251_prepare_host(pdev, &host); break; default: - return -EINVAL; + rc = -EINVAL; } + if (rc) + return rc; svia_configure(pdev); diff --git a/drivers/base/attribute_container.c b/drivers/base/attribute_container.c index f57652db0a2a..b9cda053d3c0 100644 --- a/drivers/base/attribute_container.c +++ b/drivers/base/attribute_container.c @@ -167,7 +167,7 @@ attribute_container_add_device(struct device *dev, ic->classdev.parent = get_device(dev); ic->classdev.class = cont->class; cont->class->dev_release = attribute_container_release; - strcpy(ic->classdev.bus_id, dev->bus_id); + dev_set_name(&ic->classdev, dev_name(dev)); if (fn) fn(cont, dev, &ic->classdev); else diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 5aee1c0169ea..83f32b891fa9 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -333,7 +333,7 @@ static int match_name(struct device *dev, void *data) { const char *name = data; - return sysfs_streq(name, dev->bus_id); + return sysfs_streq(name, dev_name(dev)); } /** @@ -461,12 +461,12 @@ int bus_add_device(struct device *dev) int error = 0; if (bus) { - pr_debug("bus: '%s': add device %s\n", bus->name, dev->bus_id); + pr_debug("bus: '%s': add device %s\n", bus->name, dev_name(dev)); error = device_add_attrs(bus, dev); if (error) goto out_put; error = sysfs_create_link(&bus->p->devices_kset->kobj, - &dev->kobj, dev->bus_id); + &dev->kobj, dev_name(dev)); if (error) goto out_id; error = sysfs_create_link(&dev->kobj, @@ -482,7 +482,7 @@ int bus_add_device(struct device *dev) out_deprecated: sysfs_remove_link(&dev->kobj, "subsystem"); out_subsys: - sysfs_remove_link(&bus->p->devices_kset->kobj, dev->bus_id); + sysfs_remove_link(&bus->p->devices_kset->kobj, dev_name(dev)); out_id: device_remove_attrs(bus, dev); out_put: @@ -526,13 +526,13 @@ void bus_remove_device(struct device *dev) sysfs_remove_link(&dev->kobj, "subsystem"); remove_deprecated_bus_links(dev); sysfs_remove_link(&dev->bus->p->devices_kset->kobj, - dev->bus_id); + dev_name(dev)); device_remove_attrs(dev->bus, dev); if (klist_node_attached(&dev->knode_bus)) klist_del(&dev->knode_bus); pr_debug("bus: '%s': remove device %s\n", - dev->bus->name, dev->bus_id); + dev->bus->name, dev_name(dev)); device_release_driver(dev); bus_put(dev->bus); } diff --git a/drivers/base/core.c b/drivers/base/core.c index 8c2cc2648f5a..14aa2b6953c9 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -119,7 +119,7 @@ static void device_release(struct kobject *kobj) else WARN(1, KERN_ERR "Device '%s' does not have a release() " "function, it is broken and must be fixed.\n", - dev->bus_id); + dev_name(dev)); } static struct kobj_type device_ktype = { @@ -209,7 +209,7 @@ static int dev_uevent(struct kset *kset, struct kobject *kobj, retval = dev->bus->uevent(dev, env); if (retval) pr_debug("device: '%s': %s: bus uevent() returned %d\n", - dev->bus_id, __func__, retval); + dev_name(dev), __func__, retval); } /* have the class specific function add its stuff */ @@ -217,7 +217,7 @@ static int dev_uevent(struct kset *kset, struct kobject *kobj, retval = dev->class->dev_uevent(dev, env); if (retval) pr_debug("device: '%s': %s: class uevent() " - "returned %d\n", dev->bus_id, + "returned %d\n", dev_name(dev), __func__, retval); } @@ -226,7 +226,7 @@ static int dev_uevent(struct kset *kset, struct kobject *kobj, retval = dev->type->uevent(dev, env); if (retval) pr_debug("device: '%s': %s: dev_type uevent() " - "returned %d\n", dev->bus_id, + "returned %d\n", dev_name(dev), __func__, retval); } @@ -672,7 +672,7 @@ static int device_add_class_symlinks(struct device *dev) if (dev->kobj.parent != &dev->class->p->class_subsys.kobj && device_is_not_partition(dev)) { error = sysfs_create_link(&dev->class->p->class_subsys.kobj, - &dev->kobj, dev->bus_id); + &dev->kobj, dev_name(dev)); if (error) goto out_subsys; } @@ -712,11 +712,11 @@ out_busid: if (dev->kobj.parent != &dev->class->p->class_subsys.kobj && device_is_not_partition(dev)) sysfs_remove_link(&dev->class->p->class_subsys.kobj, - dev->bus_id); + dev_name(dev)); #else /* link in the class directory pointing to the device */ error = sysfs_create_link(&dev->class->p->class_subsys.kobj, - &dev->kobj, dev->bus_id); + &dev->kobj, dev_name(dev)); if (error) goto out_subsys; @@ -729,7 +729,7 @@ out_busid: return 0; out_busid: - sysfs_remove_link(&dev->class->p->class_subsys.kobj, dev->bus_id); + sysfs_remove_link(&dev->class->p->class_subsys.kobj, dev_name(dev)); #endif out_subsys: @@ -758,12 +758,12 @@ static void device_remove_class_symlinks(struct device *dev) if (dev->kobj.parent != &dev->class->p->class_subsys.kobj && device_is_not_partition(dev)) sysfs_remove_link(&dev->class->p->class_subsys.kobj, - dev->bus_id); + dev_name(dev)); #else if (dev->parent && device_is_not_partition(dev)) sysfs_remove_link(&dev->kobj, "device"); - sysfs_remove_link(&dev->class->p->class_subsys.kobj, dev->bus_id); + sysfs_remove_link(&dev->class->p->class_subsys.kobj, dev_name(dev)); #endif sysfs_remove_link(&dev->kobj, "subsystem"); @@ -866,7 +866,7 @@ int device_add(struct device *dev) if (!strlen(dev->bus_id)) goto done; - pr_debug("device: '%s': %s\n", dev->bus_id, __func__); + pr_debug("device: '%s': %s\n", dev_name(dev), __func__); parent = get_device(dev->parent); setup_parent(dev, parent); @@ -876,7 +876,7 @@ int device_add(struct device *dev) set_dev_node(dev, dev_to_node(parent)); /* first, register with generic layer. */ - error = kobject_add(&dev->kobj, dev->kobj.parent, "%s", dev->bus_id); + error = kobject_add(&dev->kobj, dev->kobj.parent, "%s", dev_name(dev)); if (error) goto Error; @@ -1086,7 +1086,7 @@ void device_del(struct device *dev) */ void device_unregister(struct device *dev) { - pr_debug("device: '%s': %s\n", dev->bus_id, __func__); + pr_debug("device: '%s': %s\n", dev_name(dev), __func__); device_del(dev); put_device(dev); } @@ -1199,7 +1199,7 @@ EXPORT_SYMBOL_GPL(device_remove_file); static void device_create_release(struct device *dev) { - pr_debug("device: '%s': %s\n", dev->bus_id, __func__); + pr_debug("device: '%s': %s\n", dev_name(dev), __func__); kfree(dev); } @@ -1344,7 +1344,7 @@ int device_rename(struct device *dev, char *new_name) if (!dev) return -EINVAL; - pr_debug("device: '%s': %s: renaming to '%s'\n", dev->bus_id, + pr_debug("device: '%s': %s: renaming to '%s'\n", dev_name(dev), __func__, new_name); #ifdef CONFIG_SYSFS_DEPRECATED @@ -1381,7 +1381,7 @@ int device_rename(struct device *dev, char *new_name) #else if (dev->class) { error = sysfs_create_link_nowarn(&dev->class->p->class_subsys.kobj, - &dev->kobj, dev->bus_id); + &dev->kobj, dev_name(dev)); if (error) goto out; sysfs_remove_link(&dev->class->p->class_subsys.kobj, @@ -1459,8 +1459,8 @@ int device_move(struct device *dev, struct device *new_parent) new_parent = get_device(new_parent); new_parent_kobj = get_device_parent(dev, new_parent); - pr_debug("device: '%s': %s: moving to '%s'\n", dev->bus_id, - __func__, new_parent ? new_parent->bus_id : "<NULL>"); + pr_debug("device: '%s': %s: moving to '%s'\n", dev_name(dev), + __func__, new_parent ? dev_name(new_parent) : "<NULL>"); error = kobject_move(&dev->kobj, new_parent_kobj); if (error) { cleanup_glue_dir(dev, new_parent_kobj); diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 20febc00a525..17a8e45cf9c6 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -34,7 +34,7 @@ static void driver_bound(struct device *dev) return; } - pr_debug("driver: '%s': %s: bound to device '%s'\n", dev->bus_id, + pr_debug("driver: '%s': %s: bound to device '%s'\n", dev_name(dev), __func__, dev->driver->name); if (dev->bus) @@ -104,13 +104,13 @@ static int really_probe(struct device *dev, struct device_driver *drv) atomic_inc(&probe_count); pr_debug("bus: '%s': %s: probing driver %s with device %s\n", - drv->bus->name, __func__, drv->name, dev->bus_id); + drv->bus->name, __func__, drv->name, dev_name(dev)); WARN_ON(!list_empty(&dev->devres_head)); dev->driver = drv; if (driver_sysfs_add(dev)) { printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n", - __func__, dev->bus_id); + __func__, dev_name(dev)); goto probe_failed; } @@ -127,7 +127,7 @@ static int really_probe(struct device *dev, struct device_driver *drv) driver_bound(dev); ret = 1; pr_debug("bus: '%s': %s: bound device %s to driver %s\n", - drv->bus->name, __func__, dev->bus_id, drv->name); + drv->bus->name, __func__, dev_name(dev), drv->name); goto done; probe_failed: @@ -139,7 +139,7 @@ probe_failed: /* driver matched but the probe failed */ printk(KERN_WARNING "%s: probe of %s failed with error %d\n", - drv->name, dev->bus_id, ret); + drv->name, dev_name(dev), ret); } /* * Ignore errors returned by ->probe so that the next driver can try @@ -194,7 +194,7 @@ int driver_probe_device(struct device_driver *drv, struct device *dev) goto done; pr_debug("bus: '%s': %s: matched device %s with driver %s\n", - drv->bus->name, __func__, dev->bus_id, drv->name); + drv->bus->name, __func__, dev_name(dev), drv->name); ret = really_probe(dev, drv); diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index b7e571031ecd..44699d9dd85c 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -291,12 +291,6 @@ firmware_class_timeout(u_long data) fw_load_abort(fw_priv); } -static inline void fw_setup_device_id(struct device *f_dev, struct device *dev) -{ - /* XXX warning we should watch out for name collisions */ - strlcpy(f_dev->bus_id, dev->bus_id, BUS_ID_SIZE); -} - static int fw_register_device(struct device **dev_p, const char *fw_name, struct device *device) { @@ -321,7 +315,7 @@ static int fw_register_device(struct device **dev_p, const char *fw_name, fw_priv->timeout.data = (u_long) fw_priv; init_timer(&fw_priv->timeout); - fw_setup_device_id(f_dev, device); + dev_set_name(f_dev, dev_name(device)); f_dev->parent = device; f_dev->class = &firmware_class; dev_set_drvdata(f_dev, fw_priv); diff --git a/drivers/base/isa.c b/drivers/base/isa.c index efd577574948..479694b6cbe3 100644 --- a/drivers/base/isa.c +++ b/drivers/base/isa.c @@ -11,7 +11,7 @@ #include <linux/isa.h> static struct device isa_bus = { - .bus_id = "isa" + .init_name = "isa" }; struct isa_dev { @@ -135,9 +135,8 @@ int isa_register_driver(struct isa_driver *isa_driver, unsigned int ndev) isa_dev->dev.parent = &isa_bus; isa_dev->dev.bus = &isa_bus_type; - snprintf(isa_dev->dev.bus_id, BUS_ID_SIZE, "%s.%u", - isa_driver->driver.name, id); - + dev_set_name(&isa_dev->dev, "%s.%u", + isa_driver->driver.name, id); isa_dev->dev.platform_data = isa_driver; isa_dev->dev.release = isa_dev_release; isa_dev->id = id; diff --git a/drivers/base/platform.c b/drivers/base/platform.c index dfcbfe504867..349a1013603f 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -24,7 +24,7 @@ driver)) struct device platform_bus = { - .bus_id = "platform", + .init_name = "platform", }; EXPORT_SYMBOL_GPL(platform_bus); @@ -242,16 +242,15 @@ int platform_device_add(struct platform_device *pdev) pdev->dev.bus = &platform_bus_type; if (pdev->id != -1) - snprintf(pdev->dev.bus_id, BUS_ID_SIZE, "%s.%d", pdev->name, - pdev->id); + dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id); else - strlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE); + dev_set_name(&pdev->dev, pdev->name); for (i = 0; i < pdev->num_resources; i++) { struct resource *p, *r = &pdev->resource[i]; if (r->name == NULL) - r->name = pdev->dev.bus_id; + r->name = dev_name(&pdev->dev); p = r->parent; if (!p) { @@ -264,14 +263,14 @@ int platform_device_add(struct platform_device *pdev) if (p && insert_resource(p, r)) { printk(KERN_ERR "%s: failed to claim resource %d\n", - pdev->dev.bus_id, i); + dev_name(&pdev->dev), i); ret = -EBUSY; goto failed; } } pr_debug("Registering platform device '%s'. Parent at %s\n", - pdev->dev.bus_id, pdev->dev.parent->bus_id); + dev_name(&pdev->dev), dev_name(pdev->dev.parent)); ret = device_add(&pdev->dev); if (ret == 0) @@ -503,8 +502,6 @@ int platform_driver_register(struct platform_driver *drv) drv->driver.suspend = platform_drv_suspend; if (drv->resume) drv->driver.resume = platform_drv_resume; - if (drv->pm) - drv->driver.pm = &drv->pm->base; return driver_register(&drv->driver); } EXPORT_SYMBOL_GPL(platform_driver_register); @@ -609,7 +606,7 @@ static int platform_match(struct device *dev, struct device_driver *drv) struct platform_device *pdev; pdev = container_of(dev, struct platform_device, dev); - return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0); + return (strcmp(pdev->name, drv->name) == 0); } #ifdef CONFIG_PM_SLEEP @@ -686,7 +683,10 @@ static int platform_pm_suspend(struct device *dev) struct device_driver *drv = dev->driver; int ret = 0; - if (drv && drv->pm) { + if (!drv) + return 0; + + if (drv->pm) { if (drv->pm->suspend) ret = drv->pm->suspend(dev); } else { @@ -698,16 +698,15 @@ static int platform_pm_suspend(struct device *dev) static int platform_pm_suspend_noirq(struct device *dev) { - struct platform_driver *pdrv; + struct device_driver *drv = dev->driver; int ret = 0; - if (!dev->driver) + if (!drv) return 0; - pdrv = to_platform_driver(dev->driver); - if (pdrv->pm) { - if (pdrv->pm->suspend_noirq) - ret = pdrv->pm->suspend_noirq(dev); + if (drv->pm) { + if (drv->pm->suspend_noirq) + ret = drv->pm->suspend_noirq(dev); } else { ret = platform_legacy_suspend_late(dev, PMSG_SUSPEND); } @@ -720,7 +719,10 @@ static int platform_pm_resume(struct device *dev) struct device_driver *drv = dev->driver; int ret = 0; - if (drv && drv->pm) { + if (!drv) + return 0; + + if (drv->pm) { if (drv->pm->resume) ret = drv->pm->resume(dev); } else { @@ -732,16 +734,15 @@ static int platform_pm_resume(struct device *dev) static int platform_pm_resume_noirq(struct device *dev) { - struct platform_driver *pdrv; + struct device_driver *drv = dev->driver; int ret = 0; - if (!dev->driver) + if (!drv) return 0; - pdrv = to_platform_driver(dev->driver); - if (pdrv->pm) { - if (pdrv->pm->resume_noirq) - ret = pdrv->pm->resume_noirq(dev); + if (drv->pm) { + if (drv->pm->resume_noirq) + ret = drv->pm->resume_noirq(dev); } else { ret = platform_legacy_resume_early(dev); } @@ -780,16 +781,15 @@ static int platform_pm_freeze(struct device *dev) static int platform_pm_freeze_noirq(struct device *dev) { - struct platform_driver *pdrv; + struct device_driver *drv = dev->driver; int ret = 0; - if (!dev->driver) + if (!drv) return 0; - pdrv = to_platform_driver(dev->driver); - if (pdrv->pm) { - if (pdrv->pm->freeze_noirq) - ret = pdrv->pm->freeze_noirq(dev); + if (drv->pm) { + if (drv->pm->freeze_noirq) + ret = drv->pm->freeze_noirq(dev); } else { ret = platform_legacy_suspend_late(dev, PMSG_FREEZE); } @@ -802,7 +802,10 @@ static int platform_pm_thaw(struct device *dev) struct device_driver *drv = dev->driver; int ret = 0; - if (drv && drv->pm) { + if (!drv) + return 0; + + if (drv->pm) { if (drv->pm->thaw) ret = drv->pm->thaw(dev); } else { @@ -814,16 +817,15 @@ static int platform_pm_thaw(struct device *dev) static int platform_pm_thaw_noirq(struct device *dev) { - struct platform_driver *pdrv; + struct device_driver *drv = dev->driver; int ret = 0; - if (!dev->driver) + if (!drv) return 0; - pdrv = to_platform_driver(dev->driver); - if (pdrv->pm) { - if (pdrv->pm->thaw_noirq) - ret = pdrv->pm->thaw_noirq(dev); + if (drv->pm) { + if (drv->pm->thaw_noirq) + ret = drv->pm->thaw_noirq(dev); } else { ret = platform_legacy_resume_early(dev); } @@ -836,7 +838,10 @@ static int platform_pm_poweroff(struct device *dev) struct device_driver *drv = dev->driver; int ret = 0; - if (drv && drv->pm) { + if (!drv) + return 0; + + if (drv->pm) { if (drv->pm->poweroff) ret = drv->pm->poweroff(dev); } else { @@ -848,16 +853,15 @@ static int platform_pm_poweroff(struct device *dev) static int platform_pm_poweroff_noirq(struct device *dev) { - struct platform_driver *pdrv; + struct device_driver *drv = dev->driver; int ret = 0; - if (!dev->driver) + if (!drv) return 0; - pdrv = to_platform_driver(dev->driver); - if (pdrv->pm) { - if (pdrv->pm->poweroff_noirq) - ret = pdrv->pm->poweroff_noirq(dev); + if (drv->pm) { + if (drv->pm->poweroff_noirq) + ret = drv->pm->poweroff_noirq(dev); } else { ret = platform_legacy_suspend_late(dev, PMSG_HIBERNATE); } @@ -870,7 +874,10 @@ static int platform_pm_restore(struct device *dev) struct device_driver *drv = dev->driver; int ret = 0; - if (drv && drv->pm) { + if (!drv) + return 0; + + if (drv->pm) { if (drv->pm->restore) ret = drv->pm->restore(dev); } else { @@ -882,16 +889,15 @@ static int platform_pm_restore(struct device *dev) static int platform_pm_restore_noirq(struct device *dev) { - struct platform_driver *pdrv; + struct device_driver *drv = dev->driver; int ret = 0; - if (!dev->driver) + if (!drv) return 0; - pdrv = to_platform_driver(dev->driver); - if (pdrv->pm) { - if (pdrv->pm->restore_noirq) - ret = pdrv->pm->restore_noirq(dev); + if (drv->pm) { + if (drv->pm->restore_noirq) + ret = drv->pm->restore_noirq(dev); } else { ret = platform_legacy_resume_early(dev); } @@ -912,17 +918,15 @@ static int platform_pm_restore_noirq(struct device *dev) #endif /* !CONFIG_HIBERNATION */ -static struct pm_ext_ops platform_pm_ops = { - .base = { - .prepare = platform_pm_prepare, - .complete = platform_pm_complete, - .suspend = platform_pm_suspend, - .resume = platform_pm_resume, - .freeze = platform_pm_freeze, - .thaw = platform_pm_thaw, - .poweroff = platform_pm_poweroff, - .restore = platform_pm_restore, - }, +static struct dev_pm_ops platform_dev_pm_ops = { + .prepare = platform_pm_prepare, + .complete = platform_pm_complete, + .suspend = platform_pm_suspend, + .resume = platform_pm_resume, + .freeze = platform_pm_freeze, + .thaw = platform_pm_thaw, + .poweroff = platform_pm_poweroff, + .restore = platform_pm_restore, .suspend_noirq = platform_pm_suspend_noirq, .resume_noirq = platform_pm_resume_noirq, .freeze_noirq = platform_pm_freeze_noirq, @@ -931,7 +935,7 @@ static struct pm_ext_ops platform_pm_ops = { .restore_noirq = platform_pm_restore_noirq, }; -#define PLATFORM_PM_OPS_PTR &platform_pm_ops +#define PLATFORM_PM_OPS_PTR (&platform_dev_pm_ops) #else /* !CONFIG_PM_SLEEP */ diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 692c20ba5144..670c9d6c1407 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -76,7 +76,7 @@ void device_pm_add(struct device *dev) if (dev->parent) { if (dev->parent->power.status >= DPM_SUSPENDING) dev_warn(dev, "parent %s should not be sleeping\n", - dev->parent->bus_id); + dev_name(dev->parent)); } else if (transition_started) { /* * We refuse to register parentless devices while a PM @@ -112,7 +112,8 @@ void device_pm_remove(struct device *dev) * @ops: PM operations to choose from. * @state: PM transition of the system being carried out. */ -static int pm_op(struct device *dev, struct pm_ops *ops, pm_message_t state) +static int pm_op(struct device *dev, struct dev_pm_ops *ops, + pm_message_t state) { int error = 0; @@ -174,7 +175,7 @@ static int pm_op(struct device *dev, struct pm_ops *ops, pm_message_t state) * The operation is executed with interrupts disabled by the only remaining * functional CPU in the system. */ -static int pm_noirq_op(struct device *dev, struct pm_ext_ops *ops, +static int pm_noirq_op(struct device *dev, struct dev_pm_ops *ops, pm_message_t state) { int error = 0; @@ -354,7 +355,7 @@ static int resume_device(struct device *dev, pm_message_t state) if (dev->bus) { if (dev->bus->pm) { pm_dev_dbg(dev, state, ""); - error = pm_op(dev, &dev->bus->pm->base, state); + error = pm_op(dev, dev->bus->pm, state); } else if (dev->bus->resume) { pm_dev_dbg(dev, state, "legacy "); error = dev->bus->resume(dev); @@ -451,9 +452,9 @@ static void complete_device(struct device *dev, pm_message_t state) dev->type->pm->complete(dev); } - if (dev->bus && dev->bus->pm && dev->bus->pm->base.complete) { + if (dev->bus && dev->bus->pm && dev->bus->pm->complete) { pm_dev_dbg(dev, state, "completing "); - dev->bus->pm->base.complete(dev); + dev->bus->pm->complete(dev); } up(&dev->sem); @@ -624,7 +625,7 @@ static int suspend_device(struct device *dev, pm_message_t state) if (dev->bus) { if (dev->bus->pm) { pm_dev_dbg(dev, state, ""); - error = pm_op(dev, &dev->bus->pm->base, state); + error = pm_op(dev, dev->bus->pm, state); } else if (dev->bus->suspend) { pm_dev_dbg(dev, state, "legacy "); error = dev->bus->suspend(dev, state); @@ -685,10 +686,10 @@ static int prepare_device(struct device *dev, pm_message_t state) down(&dev->sem); - if (dev->bus && dev->bus->pm && dev->bus->pm->base.prepare) { + if (dev->bus && dev->bus->pm && dev->bus->pm->prepare) { pm_dev_dbg(dev, state, "preparing "); - error = dev->bus->pm->base.prepare(dev); - suspend_report_result(dev->bus->pm->base.prepare, error); + error = dev->bus->pm->prepare(dev); + suspend_report_result(dev->bus->pm->prepare, error); if (error) goto End; } diff --git a/drivers/base/power/trace.c b/drivers/base/power/trace.c index 2aa6e8fc4def..0a1a2c4dbc6e 100644 --- a/drivers/base/power/trace.c +++ b/drivers/base/power/trace.c @@ -140,7 +140,7 @@ static unsigned int hash_string(unsigned int seed, const char *data, unsigned in void set_trace_device(struct device *dev) { - dev_hash_value = hash_string(DEVSEED, dev->bus_id, DEVHASH); + dev_hash_value = hash_string(DEVSEED, dev_name(dev), DEVHASH); } EXPORT_SYMBOL(set_trace_device); @@ -192,7 +192,7 @@ static int show_dev_hash(unsigned int value) while (entry != &dpm_list) { struct device * dev = to_device(entry); - unsigned int hash = hash_string(DEVSEED, dev->bus_id, DEVHASH); + unsigned int hash = hash_string(DEVSEED, dev_name(dev), DEVHASH); if (hash == value) { dev_info(dev, "hash matches\n"); match++; diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index 61ad8d639ba3..0344a8a8321d 100644 --- a/drivers/block/Kconfig +++ b/drivers/block/Kconfig @@ -21,7 +21,8 @@ config BLK_DEV_FD ---help--- If you want to use the floppy disk drive(s) of your PC under Linux, say Y. Information about this driver, especially important for IBM - Thinkpad users, is contained in <file:Documentation/floppy.txt>. + Thinkpad users, is contained in + <file:Documentation/blockdev/floppy.txt>. That file also contains the location of the Floppy driver FAQ as well as location of the fdutils package used to configure additional parameters of the driver at run time. @@ -76,7 +77,7 @@ config PARIDE your computer's parallel port. Most of them are actually IDE devices using a parallel port IDE adapter. This option enables the PARIDE subsystem which contains drivers for many of these external drives. - Read <file:Documentation/paride.txt> for more information. + Read <file:Documentation/blockdev/paride.txt> for more information. If you have said Y to the "Parallel-port support" configuration option, you may share a single port between your printer and other @@ -114,9 +115,9 @@ config BLK_CPQ_DA help This is the driver for Compaq Smart Array controllers. Everyone using these boards should say Y here. See the file - <file:Documentation/cpqarray.txt> for the current list of boards - supported by this driver, and for further information on the use of - this driver. + <file:Documentation/blockdev/cpqarray.txt> for the current list of + boards supported by this driver, and for further information on the + use of this driver. config BLK_CPQ_CISS_DA tristate "Compaq Smart Array 5xxx support" @@ -124,7 +125,7 @@ config BLK_CPQ_CISS_DA help This is the driver for Compaq Smart Array 5xxx controllers. Everyone using these boards should say Y here. - See <file:Documentation/cciss.txt> for the current list of + See <file:Documentation/blockdev/cciss.txt> for the current list of boards supported by this driver, and for further information on the use of this driver. @@ -135,7 +136,7 @@ config CISS_SCSI_TAPE help When enabled (Y), this option allows SCSI tape drives and SCSI medium changers (tape robots) to be accessed via a Compaq 5xxx array - controller. (See <file:Documentation/cciss.txt> for more details.) + controller. (See <file:Documentation/blockdev/cciss.txt> for more details.) "SCSI support" and "SCSI tape support" must also be enabled for this option to work. @@ -149,8 +150,8 @@ config BLK_DEV_DAC960 help This driver adds support for the Mylex DAC960, AcceleRAID, and eXtremeRAID PCI RAID controllers. See the file - <file:Documentation/README.DAC960> for further information about - this driver. + <file:Documentation/blockdev/README.DAC960> for further information + about this driver. To compile this driver as a module, choose M here: the module will be called DAC960. @@ -278,9 +279,9 @@ config BLK_DEV_NBD userland (making server and client physically the same computer, communicating using the loopback network device). - Read <file:Documentation/nbd.txt> for more information, especially - about where to find the server code, which runs in user space and - does not need special kernel support. + Read <file:Documentation/blockdev/nbd.txt> for more information, + especially about where to find the server code, which runs in user + space and does not need special kernel support. Note that this has nothing to do with the network file systems NFS or Coda; you can say N here even if you intend to use NFS or Coda. @@ -321,8 +322,8 @@ config BLK_DEV_RAM store a copy of a minimal root file system off of a floppy into RAM during the initial install of Linux. - Note that the kernel command line option "ramdisk=XX" is now - obsolete. For details, read <file:Documentation/ramdisk.txt>. + Note that the kernel command line option "ramdisk=XX" is now obsolete. + For details, read <file:Documentation/blockdev/ramdisk.txt>. To compile this driver as a module, choose M here: the module will be called rd. diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index 4023885353e0..9364dc554257 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -96,6 +96,8 @@ static const struct pci_device_id cciss_pci_device_id[] = { {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x3245}, {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x3247}, {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x3249}, + {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x324A}, + {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x324B}, {PCI_VENDOR_ID_HP, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_RAID << 8, 0xffff << 8, 0}, {0,} @@ -133,6 +135,8 @@ static struct board_type products[] = { {0x3245103C, "Smart Array P410i", &SA5_access}, {0x3247103C, "Smart Array P411", &SA5_access}, {0x3249103C, "Smart Array P812", &SA5_access}, + {0x324A103C, "Smart Array P712m", &SA5_access}, + {0x324B103C, "Smart Array P711m", &SA5_access}, {0xFFFF103C, "Unknown Smart Array", &SA5_access}, }; @@ -1366,6 +1370,7 @@ static void cciss_add_disk(ctlr_info_t *h, struct gendisk *disk, disk->first_minor = drv_index << NWD_SHIFT; disk->fops = &cciss_fops; disk->private_data = &h->drv[drv_index]; + disk->driverfs_dev = &h->pdev->dev; /* Set up queue information */ blk_queue_bounce_limit(disk->queue, h->pdev->dma_mask); @@ -2842,7 +2847,7 @@ static void do_cciss_request(struct request_queue *q) h->maxSG = seg; #ifdef CCISS_DEBUG - printk(KERN_DEBUG "cciss: Submitting %d sectors in %d segments\n", + printk(KERN_DEBUG "cciss: Submitting %lu sectors in %d segments\n", creq->nr_sectors, seg); #endif /* CCISS_DEBUG */ @@ -3192,7 +3197,7 @@ static int __devinit cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev) c->paddr = pci_resource_start(pdev, 0); /* addressing mode bits already removed */ #ifdef CCISS_DEBUG - printk("address 0 = %x\n", c->paddr); + printk("address 0 = %lx\n", c->paddr); #endif /* CCISS_DEBUG */ c->vaddr = remap_pci_mem(c->paddr, 0x250); @@ -3219,7 +3224,8 @@ static int __devinit cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev) #endif /* CCISS_DEBUG */ cfg_base_addr_index = find_PCI_BAR_index(pdev, cfg_base_addr); #ifdef CCISS_DEBUG - printk("cfg base address index = %x\n", cfg_base_addr_index); + printk("cfg base address index = %llx\n", + (unsigned long long)cfg_base_addr_index); #endif /* CCISS_DEBUG */ if (cfg_base_addr_index == -1) { printk(KERN_WARNING "cciss: Cannot find cfg_base_addr_index\n"); @@ -3229,7 +3235,7 @@ static int __devinit cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev) cfg_offset = readl(c->vaddr + SA5_CTMEM_OFFSET); #ifdef CCISS_DEBUG - printk("cfg offset = %x\n", cfg_offset); + printk("cfg offset = %llx\n", (unsigned long long)cfg_offset); #endif /* CCISS_DEBUG */ c->cfgtable = remap_pci_mem(pci_resource_start(pdev, cfg_base_addr_index) + @@ -3404,7 +3410,8 @@ static int __devinit cciss_init_one(struct pci_dev *pdev, int i; int j = 0; int rc; - int dac; + int dac, return_code; + InquiryData_struct *inq_buff = NULL; i = alloc_cciss_hba(); if (i < 0) @@ -3510,6 +3517,25 @@ static int __devinit cciss_init_one(struct pci_dev *pdev, /* Turn the interrupts on so we can service requests */ hba[i]->access.set_intr_mask(hba[i], CCISS_INTR_ON); + /* Get the firmware version */ + inq_buff = kzalloc(sizeof(InquiryData_struct), GFP_KERNEL); + if (inq_buff == NULL) { + printk(KERN_ERR "cciss: out of memory\n"); + goto clean4; + } + + return_code = sendcmd_withirq(CISS_INQUIRY, i, inq_buff, + sizeof(InquiryData_struct), 0, 0 , 0, TYPE_CMD); + if (return_code == IO_OK) { + hba[i]->firm_ver[0] = inq_buff->data_byte[32]; + hba[i]->firm_ver[1] = inq_buff->data_byte[33]; + hba[i]->firm_ver[2] = inq_buff->data_byte[34]; + hba[i]->firm_ver[3] = inq_buff->data_byte[35]; + } else { /* send command failed */ + printk(KERN_WARNING "cciss: unable to determine firmware" + " version of controller\n"); + } + cciss_procinit(i); hba[i]->cciss_max_sectors = 2048; @@ -3520,6 +3546,7 @@ static int __devinit cciss_init_one(struct pci_dev *pdev, return 1; clean4: + kfree(inq_buff); #ifdef CONFIG_CISS_SCSI_TAPE kfree(hba[i]->scsi_rejects.complete); #endif diff --git a/drivers/block/cpqarray.c b/drivers/block/cpqarray.c index 47d233c6d0b3..5d39df14ed90 100644 --- a/drivers/block/cpqarray.c +++ b/drivers/block/cpqarray.c @@ -567,7 +567,12 @@ static int __init cpqarray_init(void) num_cntlrs_reg++; } - return(num_cntlrs_reg); + if (num_cntlrs_reg) + return 0; + else { + pci_unregister_driver(&cpqarray_pci_driver); + return -ENODEV; + } } /* Function to find the first free pointer into our hba[] array */ diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index 14db747a636e..cf29cc4e6ab7 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -4124,7 +4124,7 @@ static int __init floppy_setup(char *str) printk("\n"); } else DPRINT("botched floppy option\n"); - DPRINT("Read Documentation/floppy.txt\n"); + DPRINT("Read Documentation/blockdev/floppy.txt\n"); return 0; } diff --git a/drivers/block/ub.c b/drivers/block/ub.c index fccac18d3111..12fb816db7b0 100644 --- a/drivers/block/ub.c +++ b/drivers/block/ub.c @@ -1546,8 +1546,6 @@ static void ub_top_sense_done(struct ub_dev *sc, struct ub_scsi_cmd *scmd) /* * Reset management - * XXX Move usb_reset_device to khubd. Hogging kevent is not a good thing. - * XXX Make usb_sync_reset asynchronous. */ static void ub_reset_enter(struct ub_dev *sc, int try) @@ -1581,7 +1579,7 @@ static void ub_reset_task(struct work_struct *work) struct ub_dev *sc = container_of(work, struct ub_dev, reset_work); unsigned long flags; struct ub_lun *lun; - int lkr, rc; + int rc; if (!sc->reset) { printk(KERN_WARNING "%s: Running reset unrequested\n", @@ -1599,10 +1597,11 @@ static void ub_reset_task(struct work_struct *work) } else if (sc->dev->actconfig->desc.bNumInterfaces != 1) { ; } else { - if ((lkr = usb_lock_device_for_reset(sc->dev, sc->intf)) < 0) { + rc = usb_lock_device_for_reset(sc->dev, sc->intf); + if (rc < 0) { printk(KERN_NOTICE "%s: usb_lock_device_for_reset failed (%d)\n", - sc->name, lkr); + sc->name, rc); } else { rc = usb_reset_device(sc->dev); if (rc < 0) { @@ -1610,9 +1609,7 @@ static void ub_reset_task(struct work_struct *work) "usb_lock_device_for_reset failed (%d)\n", sc->name, rc); } - - if (lkr) - usb_unlock_device(sc->dev); + usb_unlock_device(sc->dev); } } @@ -1633,6 +1630,22 @@ static void ub_reset_task(struct work_struct *work) } /* + * XXX Reset brackets are too much hassle to implement, so just stub them + * in order to prevent forced unbinding (which deadlocks solid when our + * ->disconnect method waits for the reset to complete and this kills keventd). + * + * XXX Tell Alan to move usb_unlock_device inside of usb_reset_device, + * or else the post_reset is invoked, and restats I/O on a locked device. + */ +static int ub_pre_reset(struct usb_interface *iface) { + return 0; +} + +static int ub_post_reset(struct usb_interface *iface) { + return 0; +} + +/* * This is called from a process context. */ static void ub_revalidate(struct ub_dev *sc, struct ub_lun *lun) @@ -2446,6 +2459,8 @@ static struct usb_driver ub_driver = { .probe = ub_probe, .disconnect = ub_disconnect, .id_table = ub_usb_ids, + .pre_reset = ub_pre_reset, + .post_reset = ub_post_reset, }; static int __init ub_init(void) diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c index b220c686089d..2d19f0cc47f2 100644 --- a/drivers/block/xen-blkfront.c +++ b/drivers/block/xen-blkfront.c @@ -338,12 +338,18 @@ wait: static int xlvbd_init_blk_queue(struct gendisk *gd, u16 sector_size) { struct request_queue *rq; + elevator_t *old_e; rq = blk_init_queue(do_blkif_request, &blkif_io_lock); if (rq == NULL) return -1; - elevator_init(rq, "noop"); + old_e = rq->elevator; + if (IS_ERR_VALUE(elevator_init(rq, "noop"))) + printk(KERN_WARNING + "blkfront: Switch elevator failed, use default\n"); + else + elevator_exit(old_e); /* Hard sector size and max sectors impersonate the equiv. hardware. */ blk_queue_hardsect_size(rq, sector_size); diff --git a/drivers/block/xsysace.c b/drivers/block/xsysace.c index ecab9e67d47a..29e1dfafb7c6 100644 --- a/drivers/block/xsysace.c +++ b/drivers/block/xsysace.c @@ -194,7 +194,7 @@ struct ace_device { int in_irq; /* Details of hardware device */ - unsigned long physaddr; + resource_size_t physaddr; void __iomem *baseaddr; int irq; int bus_width; /* 0 := 8 bit; 1 := 16 bit */ @@ -628,8 +628,8 @@ static void ace_fsm_dostate(struct ace_device *ace) /* Okay, it's a data request, set it up for transfer */ dev_dbg(ace->dev, - "request: sec=%lx hcnt=%lx, ccnt=%x, dir=%i\n", - req->sector, req->hard_nr_sectors, + "request: sec=%llx hcnt=%lx, ccnt=%x, dir=%i\n", + (unsigned long long) req->sector, req->hard_nr_sectors, req->current_nr_sectors, rq_data_dir(req)); ace->req = req; @@ -935,7 +935,8 @@ static int __devinit ace_setup(struct ace_device *ace) int rc; dev_dbg(ace->dev, "ace_setup(ace=0x%p)\n", ace); - dev_dbg(ace->dev, "physaddr=0x%lx irq=%i\n", ace->physaddr, ace->irq); + dev_dbg(ace->dev, "physaddr=0x%llx irq=%i\n", + (unsigned long long)ace->physaddr, ace->irq); spin_lock_init(&ace->lock); init_completion(&ace->id_completion); @@ -1017,8 +1018,8 @@ static int __devinit ace_setup(struct ace_device *ace) /* Print the identification */ dev_info(ace->dev, "Xilinx SystemACE revision %i.%i.%i\n", (version >> 12) & 0xf, (version >> 8) & 0x0f, version & 0xff); - dev_dbg(ace->dev, "physaddr 0x%lx, mapped to 0x%p, irq=%i\n", - ace->physaddr, ace->baseaddr, ace->irq); + dev_dbg(ace->dev, "physaddr 0x%llx, mapped to 0x%p, irq=%i\n", + (unsigned long long) ace->physaddr, ace->baseaddr, ace->irq); ace->media_change = 1; ace_revalidate_disk(ace->gd); @@ -1035,8 +1036,8 @@ err_alloc_disk: err_blk_initq: iounmap(ace->baseaddr); err_ioremap: - dev_info(ace->dev, "xsysace: error initializing device at 0x%lx\n", - ace->physaddr); + dev_info(ace->dev, "xsysace: error initializing device at 0x%llx\n", + (unsigned long long) ace->physaddr); return -ENOMEM; } @@ -1059,7 +1060,7 @@ static void __devexit ace_teardown(struct ace_device *ace) } static int __devinit -ace_alloc(struct device *dev, int id, unsigned long physaddr, +ace_alloc(struct device *dev, int id, resource_size_t physaddr, int irq, int bus_width) { struct ace_device *ace; @@ -1119,7 +1120,7 @@ static void __devexit ace_free(struct device *dev) static int __devinit ace_probe(struct platform_device *dev) { - unsigned long physaddr = 0; + resource_size_t physaddr = 0; int bus_width = ACE_BUS_WIDTH_16; /* FIXME: should not be hard coded */ int id = dev->id; int irq = NO_IRQ; @@ -1165,7 +1166,7 @@ static int __devinit ace_of_probe(struct of_device *op, const struct of_device_id *match) { struct resource res; - unsigned long physaddr; + resource_size_t physaddr; const u32 *id; int irq, bus_width, rc; diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 43b35d0369d6..c602b547cc6e 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -124,7 +124,7 @@ config COMPUTONE which give you many serial ports. You would need something like this to connect more than two modems to your Linux box, for instance in order to become a dial-in server. If you have a card like that, say - Y here and read <file:Documentation/computone.txt>. + Y here and read <file:Documentation/serial/computone.txt>. To compile this driver as module, choose M here: the module will be called ip2. @@ -136,7 +136,7 @@ config ROCKETPORT This driver supports Comtrol RocketPort and RocketModem PCI boards. These boards provide 2, 4, 8, 16, or 32 high-speed serial ports or modems. For information about the RocketPort/RocketModem boards - and this driver read <file:Documentation/rocket.txt>. + and this driver read <file:Documentation/serial/rocket.txt>. To compile this driver as a module, choose M here: the module will be called rocket. @@ -154,7 +154,7 @@ config CYCLADES your Linux box, for instance in order to become a dial-in server. For information about the Cyclades-Z card, read - <file:Documentation/README.cycladesZ>. + <file:Documentation/serial/README.cycladesZ>. To compile this driver as a module, choose M here: the module will be called cyclades. @@ -183,7 +183,7 @@ config DIGIEPCA box, for instance in order to become a dial-in server. This driver supports the original PC (ISA) boards as well as PCI, and EISA. If you have a card like this, say Y here and read the file - <file:Documentation/digiepca.txt>. + <file:Documentation/serial/digiepca.txt>. To compile this driver as a module, choose M here: the module will be called epca. @@ -289,7 +289,7 @@ config RISCOM8 which gives you many serial ports. You would need something like this to connect more than two modems to your Linux box, for instance in order to become a dial-in server. If you have a card like that, - say Y here and read the file <file:Documentation/riscom8.txt>. + say Y here and read the file <file:Documentation/serial/riscom8.txt>. Also it's possible to say M here and compile this driver as kernel loadable module; the module will be called riscom8. @@ -304,8 +304,8 @@ config SPECIALIX your Linux box, for instance in order to become a dial-in server. If you have a card like that, say Y here and read the file - <file:Documentation/specialix.txt>. Also it's possible to say M here - and compile this driver as kernel loadable module which will be + <file:Documentation/serial/specialix.txt>. Also it's possible to say + M here and compile this driver as kernel loadable module which will be called specialix. config SX @@ -313,7 +313,7 @@ config SX depends on SERIAL_NONSTANDARD && (PCI || EISA || ISA) help This is a driver for the SX and SI multiport serial cards. - Please read the file <file:Documentation/sx.txt> for details. + Please read the file <file:Documentation/serial/sx.txt> for details. This driver can only be built as a module ( = code which can be inserted in and removed from the running kernel whenever you want). @@ -344,8 +344,8 @@ config STALDRV like this to connect more than two modems to your Linux box, for instance in order to become a dial-in server. If you say Y here, you will be asked for your specific card model in the next - questions. Make sure to read <file:Documentation/stallion.txt> in - this case. If you have never heard about all this, it's safe to + questions. Make sure to read <file:Documentation/serial/stallion.txt> + in this case. If you have never heard about all this, it's safe to say N. config STALLION @@ -354,7 +354,7 @@ config STALLION help If you have an EasyIO or EasyConnection 8/32 multiport Stallion card, then this is for you; say Y. Make sure to read - <file:Documentation/stallion.txt>. + <file:Documentation/serial/stallion.txt>. To compile this driver as a module, choose M here: the module will be called stallion. @@ -365,7 +365,7 @@ config ISTALLION help If you have an EasyConnection 8/64, ONboard, Brumby or Stallion serial multiport card, say Y here. Make sure to read - <file:Documentation/stallion.txt>. + <file:Documentation/serial/stallion.txt>. To compile this driver as a module, choose M here: the module will be called istallion. @@ -622,6 +622,16 @@ config HVC_BEAT help Toshiba's Cell Reference Set Beat Console device driver +config HVC_IUCV + bool "z/VM IUCV Hypervisor console support (VM only)" + depends on S390 + select HVC_DRIVER + select IUCV + default y + help + This driver provides a Hypervisor console (HVC) back-end to access + a Linux (console) terminal via a z/VM IUCV communication path. + config HVC_XEN bool "Xen Hypervisor Console support" depends on XEN @@ -631,6 +641,12 @@ config HVC_XEN help Xen virtual console device driver +config HVC_UDBG + bool "udbg based fake hypervisor console" + depends on PPC && EXPERIMENTAL + select HVC_DRIVER + default n + config VIRTIO_CONSOLE tristate "Virtio console" depends on VIRTIO diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 438f71317c5c..0e5ced85fb2b 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -50,6 +50,8 @@ obj-$(CONFIG_HVC_BEAT) += hvc_beat.o obj-$(CONFIG_HVC_DRIVER) += hvc_console.o obj-$(CONFIG_HVC_IRQ) += hvc_irq.o obj-$(CONFIG_HVC_XEN) += hvc_xen.o +obj-$(CONFIG_HVC_UDBG) += hvc_udbg.o +obj-$(CONFIG_HVC_IUCV) += hvc_iucv.o obj-$(CONFIG_VIRTIO_CONSOLE) += virtio_console.o obj-$(CONFIG_RAW_DRIVER) += raw.o obj-$(CONFIG_SGI_SNSC) += snsc.o snsc_event.o diff --git a/drivers/char/agp/uninorth-agp.c b/drivers/char/agp/uninorth-agp.c index 0f004b65ec03..03f95ec08f59 100644 --- a/drivers/char/agp/uninorth-agp.c +++ b/drivers/char/agp/uninorth-agp.c @@ -27,7 +27,7 @@ static int uninorth_rev; static int is_u3; -static char __devinitdata *aperture = NULL; +static char *aperture = NULL; static int uninorth_fetch_size(void) { diff --git a/drivers/char/hvc_iucv.c b/drivers/char/hvc_iucv.c new file mode 100644 index 000000000000..5ea7d7713fca --- /dev/null +++ b/drivers/char/hvc_iucv.c @@ -0,0 +1,850 @@ +/* + * hvc_iucv.c - z/VM IUCV back-end for the Hypervisor Console (HVC) + * + * This back-end for HVC provides terminal access via + * z/VM IUCV communication paths. + * + * Copyright IBM Corp. 2008. + * + * Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com> + */ +#define KMSG_COMPONENT "hvc_iucv" + +#include <linux/types.h> +#include <asm/ebcdic.h> +#include <linux/mempool.h> +#include <linux/module.h> +#include <linux/tty.h> +#include <net/iucv/iucv.h> + +#include "hvc_console.h" + + +/* HVC backend for z/VM IUCV */ +#define HVC_IUCV_MAGIC 0xc9e4c3e5 +#define MAX_HVC_IUCV_LINES HVC_ALLOC_TTY_ADAPTERS +#define MEMPOOL_MIN_NR (PAGE_SIZE / sizeof(struct iucv_tty_buffer)/4) + +/* IUCV TTY message */ +#define MSG_VERSION 0x02 /* Message version */ +#define MSG_TYPE_ERROR 0x01 /* Error message */ +#define MSG_TYPE_TERMENV 0x02 /* Terminal environment variable */ +#define MSG_TYPE_TERMIOS 0x04 /* Terminal IO struct update */ +#define MSG_TYPE_WINSIZE 0x08 /* Terminal window size update */ +#define MSG_TYPE_DATA 0x10 /* Terminal data */ + +#define MSG_SIZE(s) ((s) + offsetof(struct iucv_tty_msg, data)) +struct iucv_tty_msg { + u8 version; /* Message version */ + u8 type; /* Message type */ +#define MSG_MAX_DATALEN (~(u16)0) + u16 datalen; /* Payload length */ + u8 data[]; /* Payload buffer */ +} __attribute__((packed)); + +enum iucv_state_t { + IUCV_DISCONN = 0, + IUCV_CONNECTED = 1, + IUCV_SEVERED = 2, +}; + +enum tty_state_t { + TTY_CLOSED = 0, + TTY_OPENED = 1, +}; + +struct hvc_iucv_private { + struct hvc_struct *hvc; /* HVC console struct reference */ + u8 srv_name[8]; /* IUCV service name (ebcdic) */ + enum iucv_state_t iucv_state; /* IUCV connection status */ + enum tty_state_t tty_state; /* TTY status */ + struct iucv_path *path; /* IUCV path pointer */ + spinlock_t lock; /* hvc_iucv_private lock */ + struct list_head tty_outqueue; /* outgoing IUCV messages */ + struct list_head tty_inqueue; /* incoming IUCV messages */ +}; + +struct iucv_tty_buffer { + struct list_head list; /* list pointer */ + struct iucv_message msg; /* store an incoming IUCV message */ + size_t offset; /* data buffer offset */ + struct iucv_tty_msg *mbuf; /* buffer to store input/output data */ +}; + +/* IUCV callback handler */ +static int hvc_iucv_path_pending(struct iucv_path *, u8[8], u8[16]); +static void hvc_iucv_path_severed(struct iucv_path *, u8[16]); +static void hvc_iucv_msg_pending(struct iucv_path *, struct iucv_message *); +static void hvc_iucv_msg_complete(struct iucv_path *, struct iucv_message *); + + +/* Kernel module parameters */ +static unsigned long hvc_iucv_devices; + +/* Array of allocated hvc iucv tty lines... */ +static struct hvc_iucv_private *hvc_iucv_table[MAX_HVC_IUCV_LINES]; + +/* Kmem cache and mempool for iucv_tty_buffer elements */ +static struct kmem_cache *hvc_iucv_buffer_cache; +static mempool_t *hvc_iucv_mempool; + +/* IUCV handler callback functions */ +static struct iucv_handler hvc_iucv_handler = { + .path_pending = hvc_iucv_path_pending, + .path_severed = hvc_iucv_path_severed, + .message_complete = hvc_iucv_msg_complete, + .message_pending = hvc_iucv_msg_pending, +}; + + +/** + * hvc_iucv_get_private() - Return a struct hvc_iucv_private instance. + * @num: The HVC virtual terminal number (vtermno) + * + * This function returns the struct hvc_iucv_private instance that corresponds + * to the HVC virtual terminal number specified as parameter @num. + */ +struct hvc_iucv_private *hvc_iucv_get_private(uint32_t num) +{ + if ((num < HVC_IUCV_MAGIC) || (num - HVC_IUCV_MAGIC > hvc_iucv_devices)) + return NULL; + return hvc_iucv_table[num - HVC_IUCV_MAGIC]; +} + +/** + * alloc_tty_buffer() - Returns a new struct iucv_tty_buffer element. + * @size: Size of the internal buffer used to store data. + * @flags: Memory allocation flags passed to mempool. + * + * This function allocates a new struct iucv_tty_buffer element and, optionally, + * allocates an internal data buffer with the specified size @size. + * Note: The total message size arises from the internal buffer size and the + * members of the iucv_tty_msg structure. + * + * The function returns NULL if memory allocation has failed. + */ +static struct iucv_tty_buffer *alloc_tty_buffer(size_t size, gfp_t flags) +{ + struct iucv_tty_buffer *bufp; + + bufp = mempool_alloc(hvc_iucv_mempool, flags); + if (!bufp) + return NULL; + memset(bufp, 0, sizeof(struct iucv_tty_buffer)); + + if (size > 0) { + bufp->msg.length = MSG_SIZE(size); + bufp->mbuf = kmalloc(bufp->msg.length, flags); + if (!bufp->mbuf) { + mempool_free(bufp, hvc_iucv_mempool); + return NULL; + } + bufp->mbuf->version = MSG_VERSION; + bufp->mbuf->type = MSG_TYPE_DATA; + bufp->mbuf->datalen = (u16) size; + } + return bufp; +} + +/** + * destroy_tty_buffer() - destroy struct iucv_tty_buffer element. + * @bufp: Pointer to a struct iucv_tty_buffer element, SHALL NOT be NULL. + * + * The destroy_tty_buffer() function frees the internal data buffer and returns + * the struct iucv_tty_buffer element back to the mempool for freeing. + */ +static void destroy_tty_buffer(struct iucv_tty_buffer *bufp) +{ + kfree(bufp->mbuf); + mempool_free(bufp, hvc_iucv_mempool); +} + +/** + * destroy_tty_buffer_list() - call destroy_tty_buffer() for each list element. + * @list: List head pointer to a list containing struct iucv_tty_buffer + * elements. + * + * Calls destroy_tty_buffer() for each struct iucv_tty_buffer element in the + * list @list. + */ +static void destroy_tty_buffer_list(struct list_head *list) +{ + struct iucv_tty_buffer *ent, *next; + + list_for_each_entry_safe(ent, next, list, list) { + list_del(&ent->list); + destroy_tty_buffer(ent); + } +} + +/** + * hvc_iucv_write() - Receive IUCV message write data to HVC console buffer. + * @priv: Pointer to hvc_iucv_private structure. + * @buf: HVC console buffer for writing received terminal data. + * @count: HVC console buffer size. + * @has_more_data: Pointer to an int variable. + * + * The function picks up pending messages from the input queue and receives + * the message data that is then written to the specified buffer @buf. + * If the buffer size @count is less than the data message size, then the + * message is kept on the input queue and @has_more_data is set to 1. + * If the message data has been entirely written, the message is removed from + * the input queue. + * + * The function returns the number of bytes written to the terminal, zero if + * there are no pending data messages available or if there is no established + * IUCV path. + * If the IUCV path has been severed, then -EPIPE is returned to cause a + * hang up (that is issued by the HVC console layer). + */ +static int hvc_iucv_write(struct hvc_iucv_private *priv, + char *buf, int count, int *has_more_data) +{ + struct iucv_tty_buffer *rb; + int written; + int rc; + + /* Immediately return if there is no IUCV connection */ + if (priv->iucv_state == IUCV_DISCONN) + return 0; + + /* If the IUCV path has been severed, return -EPIPE to inform the + * hvc console layer to hang up the tty device. */ + if (priv->iucv_state == IUCV_SEVERED) + return -EPIPE; + + /* check if there are pending messages */ + if (list_empty(&priv->tty_inqueue)) + return 0; + + /* receive a iucv message and flip data to the tty (ldisc) */ + rb = list_first_entry(&priv->tty_inqueue, struct iucv_tty_buffer, list); + + written = 0; + if (!rb->mbuf) { /* message not yet received ... */ + /* allocate mem to store msg data; if no memory is available + * then leave the buffer on the list and re-try later */ + rb->mbuf = kmalloc(rb->msg.length, GFP_ATOMIC); + if (!rb->mbuf) + return -ENOMEM; + + rc = __iucv_message_receive(priv->path, &rb->msg, 0, + rb->mbuf, rb->msg.length, NULL); + switch (rc) { + case 0: /* Successful */ + break; + case 2: /* No message found */ + case 9: /* Message purged */ + break; + default: + written = -EIO; + } + /* remove buffer if an error has occured or received data + * is not correct */ + if (rc || (rb->mbuf->version != MSG_VERSION) || + (rb->msg.length != MSG_SIZE(rb->mbuf->datalen))) + goto out_remove_buffer; + } + + switch (rb->mbuf->type) { + case MSG_TYPE_DATA: + written = min_t(int, rb->mbuf->datalen - rb->offset, count); + memcpy(buf, rb->mbuf->data + rb->offset, written); + if (written < (rb->mbuf->datalen - rb->offset)) { + rb->offset += written; + *has_more_data = 1; + goto out_written; + } + break; + + case MSG_TYPE_WINSIZE: + if (rb->mbuf->datalen != sizeof(struct winsize)) + break; + hvc_resize(priv->hvc, *((struct winsize *)rb->mbuf->data)); + break; + + case MSG_TYPE_ERROR: /* ignored ... */ + case MSG_TYPE_TERMENV: /* ignored ... */ + case MSG_TYPE_TERMIOS: /* ignored ... */ + break; + } + +out_remove_buffer: + list_del(&rb->list); + destroy_tty_buffer(rb); + *has_more_data = !list_empty(&priv->tty_inqueue); + +out_written: + return written; +} + +/** + * hvc_iucv_get_chars() - HVC get_chars operation. + * @vtermno: HVC virtual terminal number. + * @buf: Pointer to a buffer to store data + * @count: Size of buffer available for writing + * + * The hvc_console thread calls this method to read characters from + * the terminal backend. If an IUCV communication path has been established, + * pending IUCV messages are received and data is copied into buffer @buf + * up to @count bytes. + * + * Locking: The routine gets called under an irqsave() spinlock; and + * the routine locks the struct hvc_iucv_private->lock to call + * helper functions. + */ +static int hvc_iucv_get_chars(uint32_t vtermno, char *buf, int count) +{ + struct hvc_iucv_private *priv = hvc_iucv_get_private(vtermno); + int written; + int has_more_data; + + if (count <= 0) + return 0; + + if (!priv) + return -ENODEV; + + spin_lock(&priv->lock); + has_more_data = 0; + written = hvc_iucv_write(priv, buf, count, &has_more_data); + spin_unlock(&priv->lock); + + /* if there are still messages on the queue... schedule another run */ + if (has_more_data) + hvc_kick(); + + return written; +} + +/** + * hvc_iucv_send() - Send an IUCV message containing terminal data. + * @priv: Pointer to struct hvc_iucv_private instance. + * @buf: Buffer containing data to send. + * @size: Size of buffer and amount of data to send. + * + * If an IUCV communication path is established, the function copies the buffer + * data to a newly allocated struct iucv_tty_buffer element, sends the data and + * puts the element to the outqueue. + * + * If there is no IUCV communication path established, the function returns 0. + * If an existing IUCV communicaton path has been severed, the function returns + * -EPIPE (can be passed to HVC layer to cause a tty hangup). + */ +static int hvc_iucv_send(struct hvc_iucv_private *priv, const char *buf, + int count) +{ + struct iucv_tty_buffer *sb; + int rc; + u16 len; + + if (priv->iucv_state == IUCV_SEVERED) + return -EPIPE; + + if (priv->iucv_state == IUCV_DISCONN) + return 0; + + len = min_t(u16, MSG_MAX_DATALEN, count); + + /* allocate internal buffer to store msg data and also compute total + * message length */ + sb = alloc_tty_buffer(len, GFP_ATOMIC); + if (!sb) + return -ENOMEM; + + sb->mbuf->datalen = len; + memcpy(sb->mbuf->data, buf, len); + + list_add_tail(&sb->list, &priv->tty_outqueue); + + rc = __iucv_message_send(priv->path, &sb->msg, 0, 0, + (void *) sb->mbuf, sb->msg.length); + if (rc) { + list_del(&sb->list); + destroy_tty_buffer(sb); + len = 0; + } + + return len; +} + +/** + * hvc_iucv_put_chars() - HVC put_chars operation. + * @vtermno: HVC virtual terminal number. + * @buf: Pointer to an buffer to read data from + * @count: Size of buffer available for reading + * + * The hvc_console thread calls this method to write characters from + * to the terminal backend. + * The function calls hvc_iucv_send() under the lock of the + * struct hvc_iucv_private instance that corresponds to the tty @vtermno. + * + * Locking: The method gets called under an irqsave() spinlock; and + * locks struct hvc_iucv_private->lock. + */ +static int hvc_iucv_put_chars(uint32_t vtermno, const char *buf, int count) +{ + struct hvc_iucv_private *priv = hvc_iucv_get_private(vtermno); + int sent; + + if (count <= 0) + return 0; + + if (!priv) + return -ENODEV; + + spin_lock(&priv->lock); + sent = hvc_iucv_send(priv, buf, count); + spin_unlock(&priv->lock); + + return sent; +} + +/** + * hvc_iucv_notifier_add() - HVC notifier for opening a TTY for the first time. + * @hp: Pointer to the HVC device (struct hvc_struct) + * @id: Additional data (originally passed to hvc_alloc): the index of an struct + * hvc_iucv_private instance. + * + * The function sets the tty state to TTY_OPEN for the struct hvc_iucv_private + * instance that is derived from @id. Always returns 0. + * + * Locking: struct hvc_iucv_private->lock, spin_lock_bh + */ +static int hvc_iucv_notifier_add(struct hvc_struct *hp, int id) +{ + struct hvc_iucv_private *priv; + + priv = hvc_iucv_get_private(id); + if (!priv) + return 0; + + spin_lock_bh(&priv->lock); + priv->tty_state = TTY_OPENED; + spin_unlock_bh(&priv->lock); + + return 0; +} + +/** + * hvc_iucv_cleanup() - Clean up function if the tty portion is finally closed. + * @priv: Pointer to the struct hvc_iucv_private instance. + * + * The functions severs the established IUCV communication path (if any), and + * destroy struct iucv_tty_buffer elements from the in- and outqueue. Finally, + * the functions resets the states to TTY_CLOSED and IUCV_DISCONN. + */ +static void hvc_iucv_cleanup(struct hvc_iucv_private *priv) +{ + destroy_tty_buffer_list(&priv->tty_outqueue); + destroy_tty_buffer_list(&priv->tty_inqueue); + + priv->tty_state = TTY_CLOSED; + priv->iucv_state = IUCV_DISCONN; +} + +/** + * hvc_iucv_notifier_hangup() - HVC notifier for tty hangups. + * @hp: Pointer to the HVC device (struct hvc_struct) + * @id: Additional data (originally passed to hvc_alloc): the index of an struct + * hvc_iucv_private instance. + * + * This routine notifies the HVC backend that a tty hangup (carrier loss, + * virtual or otherwise) has occured. + * + * The HVC backend for z/VM IUCV ignores virtual hangups (vhangup()), to keep + * an existing IUCV communication path established. + * (Background: vhangup() is called from user space (by getty or login) to + * disable writing to the tty by other applications). + * + * If the tty has been opened (e.g. getty) and an established IUCV path has been + * severed (we caused the tty hangup in that case), then the functions invokes + * hvc_iucv_cleanup() to clean up. + * + * Locking: struct hvc_iucv_private->lock + */ +static void hvc_iucv_notifier_hangup(struct hvc_struct *hp, int id) +{ + struct hvc_iucv_private *priv; + + priv = hvc_iucv_get_private(id); + if (!priv) + return; + + spin_lock_bh(&priv->lock); + /* NOTE: If the hangup was scheduled by ourself (from the iucv + * path_servered callback [IUCV_SEVERED]), then we have to + * finally clean up the tty backend structure and set state to + * TTY_CLOSED. + * + * If the tty was hung up otherwise (e.g. vhangup()), then we + * ignore this hangup and keep an established IUCV path open... + * (...the reason is that we are not able to connect back to the + * client if we disconnect on hang up) */ + priv->tty_state = TTY_CLOSED; + + if (priv->iucv_state == IUCV_SEVERED) + hvc_iucv_cleanup(priv); + spin_unlock_bh(&priv->lock); +} + +/** + * hvc_iucv_notifier_del() - HVC notifier for closing a TTY for the last time. + * @hp: Pointer to the HVC device (struct hvc_struct) + * @id: Additional data (originally passed to hvc_alloc): + * the index of an struct hvc_iucv_private instance. + * + * This routine notifies the HVC backend that the last tty device file + * descriptor has been closed. + * The function calls hvc_iucv_cleanup() to clean up the struct hvc_iucv_private + * instance. + * + * Locking: struct hvc_iucv_private->lock + */ +static void hvc_iucv_notifier_del(struct hvc_struct *hp, int id) +{ + struct hvc_iucv_private *priv; + struct iucv_path *path; + + priv = hvc_iucv_get_private(id); + if (!priv) + return; + + spin_lock_bh(&priv->lock); + path = priv->path; /* save reference to IUCV path */ + priv->path = NULL; + hvc_iucv_cleanup(priv); + spin_unlock_bh(&priv->lock); + + /* sever IUCV path outside of priv->lock due to lock ordering of: + * priv->lock <--> iucv_table_lock */ + if (path) { + iucv_path_sever(path, NULL); + iucv_path_free(path); + } +} + +/** + * hvc_iucv_path_pending() - IUCV handler to process a connection request. + * @path: Pending path (struct iucv_path) + * @ipvmid: Originator z/VM system identifier + * @ipuser: User specified data for this path + * (AF_IUCV: port/service name and originator port) + * + * The function uses the @ipuser data to check to determine if the pending + * path belongs to a terminal managed by this HVC backend. + * If the check is successful, then an additional check is done to ensure + * that a terminal cannot be accessed multiple times (only one connection + * to a terminal is allowed). In that particular case, the pending path is + * severed. If it is the first connection, the pending path is accepted and + * associated to the struct hvc_iucv_private. The iucv state is updated to + * reflect that a communication path has been established. + * + * Returns 0 if the path belongs to a terminal managed by the this HVC backend; + * otherwise returns -ENODEV in order to dispatch this path to other handlers. + * + * Locking: struct hvc_iucv_private->lock + */ +static int hvc_iucv_path_pending(struct iucv_path *path, + u8 ipvmid[8], u8 ipuser[16]) +{ + struct hvc_iucv_private *priv; + u8 nuser_data[16]; + int i, rc; + + priv = NULL; + for (i = 0; i < hvc_iucv_devices; i++) + if (hvc_iucv_table[i] && + (0 == memcmp(hvc_iucv_table[i]->srv_name, ipuser, 8))) { + priv = hvc_iucv_table[i]; + break; + } + + if (!priv) + return -ENODEV; + + spin_lock(&priv->lock); + + /* If the terminal is already connected or being severed, then sever + * this path to enforce that there is only ONE established communication + * path per terminal. */ + if (priv->iucv_state != IUCV_DISCONN) { + iucv_path_sever(path, ipuser); + iucv_path_free(path); + goto out_path_handled; + } + + /* accept path */ + memcpy(nuser_data, ipuser + 8, 8); /* remote service (for af_iucv) */ + memcpy(nuser_data + 8, ipuser, 8); /* local service (for af_iucv) */ + path->msglim = 0xffff; /* IUCV MSGLIMIT */ + path->flags &= ~IUCV_IPRMDATA; /* TODO: use IUCV_IPRMDATA */ + rc = iucv_path_accept(path, &hvc_iucv_handler, nuser_data, priv); + if (rc) { + iucv_path_sever(path, ipuser); + iucv_path_free(path); + goto out_path_handled; + } + priv->path = path; + priv->iucv_state = IUCV_CONNECTED; + +out_path_handled: + spin_unlock(&priv->lock); + return 0; +} + +/** + * hvc_iucv_path_severed() - IUCV handler to process a path sever. + * @path: Pending path (struct iucv_path) + * @ipuser: User specified data for this path + * (AF_IUCV: port/service name and originator port) + * + * The function also severs the path (as required by the IUCV protocol) and + * sets the iucv state to IUCV_SEVERED for the associated struct + * hvc_iucv_private instance. Later, the IUCV_SEVERED state triggers a tty + * hangup (hvc_iucv_get_chars() / hvc_iucv_write()). + * + * If tty portion of the HVC is closed then clean up the outqueue in addition. + * + * Locking: struct hvc_iucv_private->lock + */ +static void hvc_iucv_path_severed(struct iucv_path *path, u8 ipuser[16]) +{ + struct hvc_iucv_private *priv = path->private; + + spin_lock(&priv->lock); + priv->iucv_state = IUCV_SEVERED; + + /* NOTE: If the tty has not yet been opened by a getty program + * (e.g. to see console messages), then cleanup the + * hvc_iucv_private structure to allow re-connects. + * + * If the tty has been opened, the get_chars() callback returns + * -EPIPE to signal the hvc console layer to hang up the tty. */ + priv->path = NULL; + if (priv->tty_state == TTY_CLOSED) + hvc_iucv_cleanup(priv); + spin_unlock(&priv->lock); + + /* finally sever path (outside of priv->lock due to lock ordering) */ + iucv_path_sever(path, ipuser); + iucv_path_free(path); +} + +/** + * hvc_iucv_msg_pending() - IUCV handler to process an incoming IUCV message. + * @path: Pending path (struct iucv_path) + * @msg: Pointer to the IUCV message + * + * The function stores an incoming message on the input queue for later + * processing (by hvc_iucv_get_chars() / hvc_iucv_write()). + * However, if the tty has not yet been opened, the message is rejected. + * + * Locking: struct hvc_iucv_private->lock + */ +static void hvc_iucv_msg_pending(struct iucv_path *path, + struct iucv_message *msg) +{ + struct hvc_iucv_private *priv = path->private; + struct iucv_tty_buffer *rb; + + spin_lock(&priv->lock); + + /* reject messages if tty has not yet been opened */ + if (priv->tty_state == TTY_CLOSED) { + iucv_message_reject(path, msg); + goto unlock_return; + } + + /* allocate buffer an empty buffer element */ + rb = alloc_tty_buffer(0, GFP_ATOMIC); + if (!rb) { + iucv_message_reject(path, msg); + goto unlock_return; /* -ENOMEM */ + } + rb->msg = *msg; + + list_add_tail(&rb->list, &priv->tty_inqueue); + + hvc_kick(); /* wakup hvc console thread */ + +unlock_return: + spin_unlock(&priv->lock); +} + +/** + * hvc_iucv_msg_complete() - IUCV handler to process message completion + * @path: Pending path (struct iucv_path) + * @msg: Pointer to the IUCV message + * + * The function is called upon completion of message delivery and the + * message is removed from the outqueue. Additional delivery information + * can be found in msg->audit: rejected messages (0x040000 (IPADRJCT)) and + * purged messages (0x010000 (IPADPGNR)). + * + * Locking: struct hvc_iucv_private->lock + */ +static void hvc_iucv_msg_complete(struct iucv_path *path, + struct iucv_message *msg) +{ + struct hvc_iucv_private *priv = path->private; + struct iucv_tty_buffer *ent, *next; + LIST_HEAD(list_remove); + + spin_lock(&priv->lock); + list_for_each_entry_safe(ent, next, &priv->tty_outqueue, list) + if (ent->msg.id == msg->id) { + list_move(&ent->list, &list_remove); + break; + } + spin_unlock(&priv->lock); + destroy_tty_buffer_list(&list_remove); +} + + +/* HVC operations */ +static struct hv_ops hvc_iucv_ops = { + .get_chars = hvc_iucv_get_chars, + .put_chars = hvc_iucv_put_chars, + .notifier_add = hvc_iucv_notifier_add, + .notifier_del = hvc_iucv_notifier_del, + .notifier_hangup = hvc_iucv_notifier_hangup, +}; + +/** + * hvc_iucv_alloc() - Allocates a new struct hvc_iucv_private instance + * @id: hvc_iucv_table index + * + * This function allocates a new hvc_iucv_private struct and put the + * instance into hvc_iucv_table at index @id. + * Returns 0 on success; otherwise non-zero. + */ +static int __init hvc_iucv_alloc(int id) +{ + struct hvc_iucv_private *priv; + char name[9]; + int rc; + + priv = kzalloc(sizeof(struct hvc_iucv_private), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + spin_lock_init(&priv->lock); + INIT_LIST_HEAD(&priv->tty_outqueue); + INIT_LIST_HEAD(&priv->tty_inqueue); + + /* Finally allocate hvc */ + priv->hvc = hvc_alloc(HVC_IUCV_MAGIC + id, + HVC_IUCV_MAGIC + id, &hvc_iucv_ops, PAGE_SIZE); + if (IS_ERR(priv->hvc)) { + rc = PTR_ERR(priv->hvc); + kfree(priv); + return rc; + } + + /* setup iucv related information */ + snprintf(name, 9, "ihvc%-4d", id); + memcpy(priv->srv_name, name, 8); + ASCEBC(priv->srv_name, 8); + + hvc_iucv_table[id] = priv; + return 0; +} + +/** + * hvc_iucv_init() - Initialization of HVC backend for z/VM IUCV + */ +static int __init hvc_iucv_init(void) +{ + int rc, i; + + if (!MACHINE_IS_VM) { + pr_warning("The z/VM IUCV Hypervisor console cannot be " + "used without z/VM.\n"); + return -ENODEV; + } + + if (!hvc_iucv_devices) + return -ENODEV; + + if (hvc_iucv_devices > MAX_HVC_IUCV_LINES) + return -EINVAL; + + hvc_iucv_buffer_cache = kmem_cache_create(KMSG_COMPONENT, + sizeof(struct iucv_tty_buffer), + 0, 0, NULL); + if (!hvc_iucv_buffer_cache) { + pr_err("Not enough memory for driver initialization " + "(rs=%d).\n", 1); + return -ENOMEM; + } + + hvc_iucv_mempool = mempool_create_slab_pool(MEMPOOL_MIN_NR, + hvc_iucv_buffer_cache); + if (!hvc_iucv_mempool) { + pr_err("Not enough memory for driver initialization " + "(rs=%d).\n", 2); + kmem_cache_destroy(hvc_iucv_buffer_cache); + return -ENOMEM; + } + + /* allocate hvc_iucv_private structs */ + for (i = 0; i < hvc_iucv_devices; i++) { + rc = hvc_iucv_alloc(i); + if (rc) { + pr_err("Could not create new z/VM IUCV HVC backend " + "rc=%d.\n", rc); + goto out_error_hvc; + } + } + + /* register IUCV callback handler */ + rc = iucv_register(&hvc_iucv_handler, 0); + if (rc) { + pr_err("Could not register iucv handler (rc=%d).\n", rc); + goto out_error_iucv; + } + + return 0; + +out_error_iucv: + iucv_unregister(&hvc_iucv_handler, 0); +out_error_hvc: + for (i = 0; i < hvc_iucv_devices; i++) + if (hvc_iucv_table[i]) { + if (hvc_iucv_table[i]->hvc) + hvc_remove(hvc_iucv_table[i]->hvc); + kfree(hvc_iucv_table[i]); + } + mempool_destroy(hvc_iucv_mempool); + kmem_cache_destroy(hvc_iucv_buffer_cache); + return rc; +} + +/** + * hvc_iucv_console_init() - Early console initialization + */ +static int __init hvc_iucv_console_init(void) +{ + if (!MACHINE_IS_VM || !hvc_iucv_devices) + return -ENODEV; + return hvc_instantiate(HVC_IUCV_MAGIC, 0, &hvc_iucv_ops); +} + +/** + * hvc_iucv_config() - Parsing of hvc_iucv= kernel command line parameter + * @val: Parameter value (numeric) + */ +static int __init hvc_iucv_config(char *val) +{ + return strict_strtoul(val, 10, &hvc_iucv_devices); +} + + +module_init(hvc_iucv_init); +console_initcall(hvc_iucv_console_init); +__setup("hvc_iucv=", hvc_iucv_config); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("HVC back-end for z/VM IUCV."); +MODULE_AUTHOR("Hendrik Brueckner <brueckner@linux.vnet.ibm.com>"); diff --git a/drivers/char/hvc_udbg.c b/drivers/char/hvc_udbg.c new file mode 100644 index 000000000000..bd63ba878a56 --- /dev/null +++ b/drivers/char/hvc_udbg.c @@ -0,0 +1,96 @@ +/* + * udbg interface to hvc_console.c + * + * (C) Copyright David Gibson, IBM Corporation 2008. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/console.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/moduleparam.h> +#include <linux/types.h> +#include <linux/irq.h> + +#include <asm/udbg.h> + +#include "hvc_console.h" + +struct hvc_struct *hvc_udbg_dev; + +static int hvc_udbg_put(uint32_t vtermno, const char *buf, int count) +{ + int i; + + for (i = 0; i < count; i++) + udbg_putc(buf[i]); + + return i; +} + +static int hvc_udbg_get(uint32_t vtermno, char *buf, int count) +{ + int i, c; + + if (!udbg_getc_poll) + return 0; + + for (i = 0; i < count; i++) { + if ((c = udbg_getc_poll()) == -1) + break; + buf[i] = c; + } + + return i; +} + +static struct hv_ops hvc_udbg_ops = { + .get_chars = hvc_udbg_get, + .put_chars = hvc_udbg_put, +}; + +static int __init hvc_udbg_init(void) +{ + struct hvc_struct *hp; + + BUG_ON(hvc_udbg_dev); + + hp = hvc_alloc(0, NO_IRQ, &hvc_udbg_ops, 16); + if (IS_ERR(hp)) + return PTR_ERR(hp); + + hvc_udbg_dev = hp; + + return 0; +} +module_init(hvc_udbg_init); + +static void __exit hvc_udbg_exit(void) +{ + if (hvc_udbg_dev) + hvc_remove(hvc_udbg_dev); +} +module_exit(hvc_udbg_exit); + +static int __init hvc_udbg_console_init(void) +{ + hvc_instantiate(0, 0, &hvc_udbg_ops); + add_preferred_console("hvc", 0, NULL); + + return 0; +} +console_initcall(hvc_udbg_console_init); diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 8a59aaa21be5..7a88dfd4427b 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -422,9 +422,11 @@ struct ipmi_smi { /** * The driver model view of the IPMI messaging driver. */ -static struct device_driver ipmidriver = { - .name = "ipmi", - .bus = &platform_bus_type +static struct platform_driver ipmidriver = { + .driver = { + .name = "ipmi", + .bus = &platform_bus_type + } }; static DEFINE_MUTEX(ipmidriver_mutex); @@ -2384,9 +2386,9 @@ static int ipmi_bmc_register(ipmi_smi_t intf, int ifnum, * representing the interfaced BMC already */ if (bmc->guid_set) - old_bmc = ipmi_find_bmc_guid(&ipmidriver, bmc->guid); + old_bmc = ipmi_find_bmc_guid(&ipmidriver.driver, bmc->guid); else - old_bmc = ipmi_find_bmc_prod_dev_id(&ipmidriver, + old_bmc = ipmi_find_bmc_prod_dev_id(&ipmidriver.driver, bmc->id.product_id, bmc->id.device_id); @@ -2416,7 +2418,7 @@ static int ipmi_bmc_register(ipmi_smi_t intf, int ifnum, snprintf(name, sizeof(name), "ipmi_bmc.%4.4x", bmc->id.product_id); - while (ipmi_find_bmc_prod_dev_id(&ipmidriver, + while (ipmi_find_bmc_prod_dev_id(&ipmidriver.driver, bmc->id.product_id, bmc->id.device_id)) { if (!warn_printed) { @@ -2446,7 +2448,7 @@ static int ipmi_bmc_register(ipmi_smi_t intf, int ifnum, " Unable to allocate platform device\n"); return -ENOMEM; } - bmc->dev->dev.driver = &ipmidriver; + bmc->dev->dev.driver = &ipmidriver.driver; dev_set_drvdata(&bmc->dev->dev, bmc); kref_init(&bmc->refcount); @@ -4247,7 +4249,7 @@ static int ipmi_init_msghandler(void) if (initialized) return 0; - rv = driver_register(&ipmidriver); + rv = driver_register(&ipmidriver.driver); if (rv) { printk(KERN_ERR PFX "Could not register IPMI driver\n"); return rv; @@ -4308,7 +4310,7 @@ static __exit void cleanup_ipmi(void) remove_proc_entry(proc_ipmi_root->name, NULL); #endif /* CONFIG_PROC_FS */ - driver_unregister(&ipmidriver); + driver_unregister(&ipmidriver.driver); initialized = 0; diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index 3123bf57ad91..3000135f2ead 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -114,9 +114,11 @@ static char *si_to_str[] = { "kcs", "smic", "bt" }; #define DEVICE_NAME "ipmi_si" -static struct device_driver ipmi_driver = { - .name = DEVICE_NAME, - .bus = &platform_bus_type +static struct platform_driver ipmi_driver = { + .driver = { + .name = DEVICE_NAME, + .bus = &platform_bus_type + } }; @@ -2868,7 +2870,7 @@ static int try_smi_init(struct smi_info *new_smi) goto out_err; } new_smi->dev = &new_smi->pdev->dev; - new_smi->dev->driver = &ipmi_driver; + new_smi->dev->driver = &ipmi_driver.driver; rv = platform_device_add(new_smi->pdev); if (rv) { @@ -2983,7 +2985,7 @@ static __devinit int init_ipmi_si(void) initialized = 1; /* Register the device drivers. */ - rv = driver_register(&ipmi_driver); + rv = driver_register(&ipmi_driver.driver); if (rv) { printk(KERN_ERR "init_ipmi_si: Unable to register driver: %d\n", @@ -3052,7 +3054,7 @@ static __devinit int init_ipmi_si(void) #ifdef CONFIG_PPC_OF of_unregister_platform_driver(&ipmi_of_platform_driver); #endif - driver_unregister(&ipmi_driver); + driver_unregister(&ipmi_driver.driver); printk(KERN_WARNING "ipmi_si: Unable to find any System Interface(s)\n"); return -ENODEV; @@ -3151,7 +3153,7 @@ static __exit void cleanup_ipmi_si(void) cleanup_one_si(e); mutex_unlock(&smi_infos_lock); - driver_unregister(&ipmi_driver); + driver_unregister(&ipmi_driver.driver); } module_exit(cleanup_ipmi_si); diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c index 7d30ee1d3fca..04e4549299ba 100644 --- a/drivers/char/isicom.c +++ b/drivers/char/isicom.c @@ -7,12 +7,14 @@ * Original driver code supplied by Multi-Tech * * Changes - * 1/9/98 alan@redhat.com Merge to 2.0.x kernel tree + * 1/9/98 alan@lxorguk.ukuu.org.uk + * Merge to 2.0.x kernel tree * Obtain and use official major/minors * Loader switched to a misc device * (fixed range check bug as a side effect) * Printk clean up - * 9/12/98 alan@redhat.com Rough port to 2.1.x + * 9/12/98 alan@lxorguk.ukuu.org.uk + * Rough port to 2.1.x * * 10/6/99 sameer Merged the ISA and PCI drivers to * a new unified driver. diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c index 44e5d60f517e..4b10770fa937 100644 --- a/drivers/char/istallion.c +++ b/drivers/char/istallion.c @@ -3739,7 +3739,7 @@ static int stli_getbrdnr(void) * do is go probing around in the usual places hoping we can find it. */ -static int stli_findeisabrds(void) +static int __init stli_findeisabrds(void) { struct stlibrd *brdp; unsigned int iobase, eid, i; @@ -3935,7 +3935,7 @@ static struct stlibrd *stli_allocbrd(void) * can find. */ -static int stli_initbrds(void) +static int __init stli_initbrds(void) { struct stlibrd *brdp, *nxtbrdp; struct stlconf conf; diff --git a/drivers/char/mwave/mwavedd.c b/drivers/char/mwave/mwavedd.c index 4f8d67fed292..94ad2c3bfc4a 100644 --- a/drivers/char/mwave/mwavedd.c +++ b/drivers/char/mwave/mwavedd.c @@ -663,7 +663,7 @@ static int __init mwave_init(void) #if 0 /* sysfs */ memset(&mwave_device, 0, sizeof (struct device)); - snprintf(mwave_device.bus_id, BUS_ID_SIZE, "mwave"); + dev_set_name(&mwave_device, "mwave"); if (device_register(&mwave_device)) goto cleanup_error; diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c index 8beef50f95a0..047766915411 100644 --- a/drivers/char/mxser.c +++ b/drivers/char/mxser.c @@ -14,7 +14,8 @@ * (at your option) any later version. * * Fed through a cleanup, indent and remove of non 2.6 code by Alan Cox - * <alan@redhat.com>. The original 1.8 code is available on www.moxa.com. + * <alan@lxorguk.ukuu.org.uk>. The original 1.8 code is available on + * www.moxa.com. * - Fixed x86_64 cleanness */ diff --git a/drivers/char/sonypi.c b/drivers/char/sonypi.c index 2457b07dabd6..f4374437a033 100644 --- a/drivers/char/sonypi.c +++ b/drivers/char/sonypi.c @@ -523,7 +523,7 @@ static int acpi_driver_registered; static int sonypi_ec_write(u8 addr, u8 value) { -#ifdef CONFIG_ACPI_EC +#ifdef CONFIG_ACPI if (SONYPI_ACPI_ACTIVE) return ec_write(addr, value); #endif @@ -539,7 +539,7 @@ static int sonypi_ec_write(u8 addr, u8 value) static int sonypi_ec_read(u8 addr, u8 *value) { -#ifdef CONFIG_ACPI_EC +#ifdef CONFIG_ACPI if (SONYPI_ACPI_ACTIVE) return ec_read(addr, value); #endif diff --git a/drivers/char/specialix.c b/drivers/char/specialix.c index 242fd46fda22..a16b94f12eb2 100644 --- a/drivers/char/specialix.c +++ b/drivers/char/specialix.c @@ -72,7 +72,7 @@ /* * There is a bunch of documentation about the card, jumpers, config * settings, restrictions, cables, device names and numbers in - * Documentation/specialix.txt + * Documentation/serial/specialix.txt */ #include <linux/module.h> diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c index ce0d9da52a8a..94966edfb44d 100644 --- a/drivers/char/sysrq.c +++ b/drivers/char/sysrq.c @@ -274,6 +274,22 @@ static struct sysrq_key_op sysrq_showstate_blocked_op = { .enable_mask = SYSRQ_ENABLE_DUMP, }; +#ifdef CONFIG_TRACING +#include <linux/ftrace.h> + +static void sysrq_ftrace_dump(int key, struct tty_struct *tty) +{ + ftrace_dump(); +} +static struct sysrq_key_op sysrq_ftrace_dump_op = { + .handler = sysrq_ftrace_dump, + .help_msg = "dumpZ-ftrace-buffer", + .action_msg = "Dump ftrace buffer", + .enable_mask = SYSRQ_ENABLE_DUMP, +}; +#else +#define sysrq_ftrace_dump_op (*(struct sysrq_key_op *)0) +#endif static void sysrq_handle_showmem(int key, struct tty_struct *tty) { @@ -406,7 +422,7 @@ static struct sysrq_key_op *sysrq_key_table[36] = { NULL, /* x */ /* y: May be registered on sparc64 for global register dump */ NULL, /* y */ - NULL /* z */ + &sysrq_ftrace_dump_op, /* z */ }; /* key2index calculation, -1 on invalid index */ diff --git a/drivers/char/vt.c b/drivers/char/vt.c index d8f83e26e4a4..a5af6072e2b3 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -1644,7 +1644,10 @@ static void reset_terminal(struct vc_data *vc, int do_clear) vc->vc_tab_stop[1] = vc->vc_tab_stop[2] = vc->vc_tab_stop[3] = - vc->vc_tab_stop[4] = 0x01010101; + vc->vc_tab_stop[4] = + vc->vc_tab_stop[5] = + vc->vc_tab_stop[6] = + vc->vc_tab_stop[7] = 0x01010101; vc->vc_bell_pitch = DEFAULT_BELL_PITCH; vc->vc_bell_duration = DEFAULT_BELL_DURATION; @@ -1935,7 +1938,10 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c) vc->vc_tab_stop[1] = vc->vc_tab_stop[2] = vc->vc_tab_stop[3] = - vc->vc_tab_stop[4] = 0; + vc->vc_tab_stop[4] = + vc->vc_tab_stop[5] = + vc->vc_tab_stop[6] = + vc->vc_tab_stop[7] = 0; } return; case 'm': diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.c b/drivers/char/xilinx_hwicap/xilinx_hwicap.c index ed132fe55d3d..d16131949097 100644 --- a/drivers/char/xilinx_hwicap/xilinx_hwicap.c +++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.c @@ -626,7 +626,7 @@ static int __devinit hwicap_setup(struct device *dev, int id, if (!request_mem_region(drvdata->mem_start, drvdata->mem_size, DRIVER_NAME)) { dev_err(dev, "Couldn't lock memory region at %Lx\n", - regs_res->start); + (unsigned long long) regs_res->start); retval = -EBUSY; goto failed1; } @@ -645,9 +645,10 @@ static int __devinit hwicap_setup(struct device *dev, int id, mutex_init(&drvdata->sem); drvdata->is_open = 0; - dev_info(dev, "ioremap %lx to %p with size %Lx\n", - (unsigned long int)drvdata->mem_start, - drvdata->base_address, drvdata->mem_size); + dev_info(dev, "ioremap %llx to %p with size %llx\n", + (unsigned long long) drvdata->mem_start, + drvdata->base_address, + (unsigned long long) drvdata->mem_size); cdev_init(&drvdata->cdev, &hwicap_fops); drvdata->cdev.owner = THIS_MODULE; diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index 5bed73329ef8..8504a2108557 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -65,12 +65,14 @@ static void cpuidle_idle_call(void) return; } +#if 0 + /* shows regressions, re-enable for 2.6.29 */ /* * run any timers that can be run now, at this point * before calculating the idle duration etc. */ hrtimer_peek_ahead_timers(); - +#endif /* ask the governor for the next state */ next_state = cpuidle_curr_governor->select(dev); if (need_resched()) diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c index b6ad3ac5916e..24607669a52b 100644 --- a/drivers/crypto/talitos.c +++ b/drivers/crypto/talitos.c @@ -1357,7 +1357,7 @@ static int hw_supports(struct device *dev, __be32 desc_hdr_template) return ret; } -static int __devexit talitos_remove(struct of_device *ofdev) +static int talitos_remove(struct of_device *ofdev) { struct device *dev = &ofdev->dev; struct talitos_private *priv = dev_get_drvdata(dev); @@ -1622,7 +1622,7 @@ static struct of_platform_driver talitos_driver = { .name = "talitos", .match_table = talitos_match, .probe = talitos_probe, - .remove = __devexit_p(talitos_remove), + .remove = talitos_remove, }; static int __init talitos_init(void) diff --git a/drivers/dca/dca-core.c b/drivers/dca/dca-core.c index ec249d2db633..d883e1b8bb8c 100644 --- a/drivers/dca/dca-core.c +++ b/drivers/dca/dca-core.c @@ -270,6 +270,6 @@ static void __exit dca_exit(void) dca_sysfs_exit(); } -module_init(dca_init); +subsys_initcall(dca_init); module_exit(dca_exit); diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index dc003a3a787d..5317e08221ec 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -399,8 +399,8 @@ int dma_async_device_register(struct dma_device *device) chan->chan_id = chancnt++; chan->dev.class = &dma_devclass; chan->dev.parent = device->dev; - snprintf(chan->dev.bus_id, BUS_ID_SIZE, "dma%dchan%d", - device->dev_id, chan->chan_id); + dev_set_name(&chan->dev, "dma%dchan%d", + device->dev_id, chan->chan_id); rc = device_register(&chan->dev); if (rc) { diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c index d1e381e35a9e..ed9636bfb54a 100644 --- a/drivers/dma/dmatest.c +++ b/drivers/dma/dmatest.c @@ -20,11 +20,11 @@ static unsigned int test_buf_size = 16384; module_param(test_buf_size, uint, S_IRUGO); MODULE_PARM_DESC(test_buf_size, "Size of the memcpy test buffer"); -static char test_channel[BUS_ID_SIZE]; +static char test_channel[20]; module_param_string(channel, test_channel, sizeof(test_channel), S_IRUGO); MODULE_PARM_DESC(channel, "Bus ID of the channel to test (default: any)"); -static char test_device[BUS_ID_SIZE]; +static char test_device[20]; module_param_string(device, test_device, sizeof(test_device), S_IRUGO); MODULE_PARM_DESC(device, "Bus ID of the DMA Engine to test (default: any)"); @@ -80,14 +80,14 @@ static bool dmatest_match_channel(struct dma_chan *chan) { if (test_channel[0] == '\0') return true; - return strcmp(chan->dev.bus_id, test_channel) == 0; + return strcmp(dev_name(&chan->dev), test_channel) == 0; } static bool dmatest_match_device(struct dma_device *device) { if (test_device[0] == '\0') return true; - return strcmp(device->dev->bus_id, test_device) == 0; + return strcmp(dev_name(device->dev), test_device) == 0; } static unsigned long dmatest_random(void) @@ -332,7 +332,7 @@ static enum dma_state_client dmatest_add_channel(struct dma_chan *chan) dtc = kmalloc(sizeof(struct dmatest_chan), GFP_KERNEL); if (!dtc) { - pr_warning("dmatest: No memory for %s\n", chan->dev.bus_id); + pr_warning("dmatest: No memory for %s\n", dev_name(&chan->dev)); return DMA_NAK; } @@ -343,16 +343,16 @@ static enum dma_state_client dmatest_add_channel(struct dma_chan *chan) thread = kzalloc(sizeof(struct dmatest_thread), GFP_KERNEL); if (!thread) { pr_warning("dmatest: No memory for %s-test%u\n", - chan->dev.bus_id, i); + dev_name(&chan->dev), i); break; } thread->chan = dtc->chan; smp_wmb(); thread->task = kthread_run(dmatest_func, thread, "%s-test%u", - chan->dev.bus_id, i); + dev_name(&chan->dev), i); if (IS_ERR(thread->task)) { pr_warning("dmatest: Failed to run thread %s-test%u\n", - chan->dev.bus_id, i); + dev_name(&chan->dev), i); kfree(thread); break; } @@ -362,7 +362,7 @@ static enum dma_state_client dmatest_add_channel(struct dma_chan *chan) list_add_tail(&thread->node, &dtc->threads); } - pr_info("dmatest: Started %u threads using %s\n", i, chan->dev.bus_id); + pr_info("dmatest: Started %u threads using %s\n", i, dev_name(&chan->dev)); list_add_tail(&dtc->node, &dmatest_channels); nr_channels++; @@ -379,7 +379,7 @@ static enum dma_state_client dmatest_remove_channel(struct dma_chan *chan) list_del(&dtc->node); dmatest_cleanup_channel(dtc); pr_debug("dmatest: lost channel %s\n", - chan->dev.bus_id); + dev_name(&chan->dev)); return DMA_ACK; } } @@ -418,7 +418,7 @@ dmatest_event(struct dma_client *client, struct dma_chan *chan, default: pr_info("dmatest: Unhandled event %u (%s)\n", - state, chan->dev.bus_id); + state, dev_name(&chan->dev)); break; } diff --git a/drivers/dma/ioat_dma.c b/drivers/dma/ioat_dma.c index b0438c4f0c30..ecd743f7cc61 100644 --- a/drivers/dma/ioat_dma.c +++ b/drivers/dma/ioat_dma.c @@ -525,7 +525,7 @@ static dma_cookie_t ioat1_tx_submit(struct dma_async_tx_descriptor *tx) } hw->ctl = IOAT_DMA_DESCRIPTOR_CTL_CP_STS; - if (new->async_tx.callback) { + if (first->async_tx.callback) { hw->ctl |= IOAT_DMA_DESCRIPTOR_CTL_INT_GN; if (first != new) { /* move callback into to last desc */ @@ -617,7 +617,7 @@ static dma_cookie_t ioat2_tx_submit(struct dma_async_tx_descriptor *tx) } hw->ctl |= IOAT_DMA_DESCRIPTOR_CTL_CP_STS; - if (new->async_tx.callback) { + if (first->async_tx.callback) { hw->ctl |= IOAT_DMA_DESCRIPTOR_CTL_INT_GN; if (first != new) { /* move callback into to last desc */ @@ -807,6 +807,12 @@ static void ioat_dma_free_chan_resources(struct dma_chan *chan) struct ioat_desc_sw *desc, *_desc; int in_use_descs = 0; + /* Before freeing channel resources first check + * if they have been previously allocated for this channel. + */ + if (ioat_chan->desccount == 0) + return; + tasklet_disable(&ioat_chan->cleanup_task); ioat_dma_memcpy_cleanup(ioat_chan); @@ -869,6 +875,7 @@ static void ioat_dma_free_chan_resources(struct dma_chan *chan) ioat_chan->last_completion = ioat_chan->completion_addr = 0; ioat_chan->pending = 0; ioat_chan->dmacount = 0; + ioat_chan->desccount = 0; ioat_chan->watchdog_completion = 0; ioat_chan->last_compl_desc_addr_hw = 0; ioat_chan->watchdog_tcp_cookie = diff --git a/drivers/dma/iop-adma.c b/drivers/dma/iop-adma.c index 71fba82462cb..c7a9306d951d 100644 --- a/drivers/dma/iop-adma.c +++ b/drivers/dma/iop-adma.c @@ -411,6 +411,7 @@ iop_adma_tx_submit(struct dma_async_tx_descriptor *tx) int slot_cnt; int slots_per_op; dma_cookie_t cookie; + dma_addr_t next_dma; grp_start = sw_desc->group_head; slot_cnt = grp_start->slot_cnt; @@ -425,12 +426,12 @@ iop_adma_tx_submit(struct dma_async_tx_descriptor *tx) &old_chain_tail->chain_node); /* fix up the hardware chain */ - iop_desc_set_next_desc(old_chain_tail, grp_start->async_tx.phys); + next_dma = grp_start->async_tx.phys; + iop_desc_set_next_desc(old_chain_tail, next_dma); + BUG_ON(iop_desc_get_next_desc(old_chain_tail) != next_dma); /* flush */ - /* 1/ don't add pre-chained descriptors - * 2/ dummy read to flush next_desc write - */ - BUG_ON(iop_desc_get_next_desc(sw_desc)); + /* check for pre-chained descriptors */ + iop_paranoia(iop_desc_get_next_desc(sw_desc)); /* increment the pending count by the number of slots * memcpy operations have a 1:1 (slot:operation) relation diff --git a/drivers/dma/iovlock.c b/drivers/dma/iovlock.c index e763d723e4cf..9f6fe46a9b87 100644 --- a/drivers/dma/iovlock.c +++ b/drivers/dma/iovlock.c @@ -55,7 +55,6 @@ struct dma_pinned_list *dma_pin_iovec_pages(struct iovec *iov, size_t len) int nr_iovecs = 0; int iovec_len_used = 0; int iovec_pages_used = 0; - long err; /* don't pin down non-user-based iovecs */ if (segment_eq(get_fs(), KERNEL_DS)) @@ -72,23 +71,21 @@ struct dma_pinned_list *dma_pin_iovec_pages(struct iovec *iov, size_t len) local_list = kmalloc(sizeof(*local_list) + (nr_iovecs * sizeof (struct dma_page_list)) + (iovec_pages_used * sizeof (struct page*)), GFP_KERNEL); - if (!local_list) { - err = -ENOMEM; + if (!local_list) goto out; - } /* list of pages starts right after the page list array */ pages = (struct page **) &local_list->page_list[nr_iovecs]; + local_list->nr_iovecs = 0; + for (i = 0; i < nr_iovecs; i++) { struct dma_page_list *page_list = &local_list->page_list[i]; len -= iov[i].iov_len; - if (!access_ok(VERIFY_WRITE, iov[i].iov_base, iov[i].iov_len)) { - err = -EFAULT; + if (!access_ok(VERIFY_WRITE, iov[i].iov_base, iov[i].iov_len)) goto unpin; - } page_list->nr_pages = num_pages_spanned(&iov[i]); page_list->base_address = iov[i].iov_base; @@ -109,10 +106,8 @@ struct dma_pinned_list *dma_pin_iovec_pages(struct iovec *iov, size_t len) NULL); up_read(¤t->mm->mmap_sem); - if (ret != page_list->nr_pages) { - err = -ENOMEM; + if (ret != page_list->nr_pages) goto unpin; - } local_list->nr_iovecs = i + 1; } @@ -122,7 +117,7 @@ struct dma_pinned_list *dma_pin_iovec_pages(struct iovec *iov, size_t len) unpin: dma_unpin_iovec_pages(local_list); out: - return ERR_PTR(err); + return NULL; } void dma_unpin_iovec_pages(struct dma_pinned_list *pinned_list) diff --git a/drivers/edac/i5000_edac.c b/drivers/edac/i5000_edac.c index f0d9b415db50..d335086f4a26 100644 --- a/drivers/edac/i5000_edac.c +++ b/drivers/edac/i5000_edac.c @@ -1381,6 +1381,7 @@ static int i5000_probe1(struct pci_dev *pdev, int dev_idx) if (mci == NULL) return -ENOMEM; + kobject_get(&mci->edac_mci_kobj); debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci); mci->dev = &pdev->dev; /* record ptr to the generic device */ @@ -1453,6 +1454,7 @@ fail1: i5000_put_devices(mci); fail0: + kobject_put(&mci->edac_mci_kobj); edac_mc_free(mci); return -ENODEV; } @@ -1498,7 +1500,7 @@ static void __devexit i5000_remove_one(struct pci_dev *pdev) /* retrieve references to resources, and free those resources */ i5000_put_devices(mci); - + kobject_put(&mci->edac_mci_kobj); edac_mc_free(mci); } diff --git a/drivers/firewire/fw-device.c b/drivers/firewire/fw-device.c index 3fccdd484100..6b9be42c7b98 100644 --- a/drivers/firewire/fw-device.c +++ b/drivers/firewire/fw-device.c @@ -587,8 +587,7 @@ static void create_units(struct fw_device *device) unit->device.bus = &fw_bus_type; unit->device.type = &fw_unit_type; unit->device.parent = &device->device; - snprintf(unit->device.bus_id, sizeof(unit->device.bus_id), - "%s.%d", device->device.bus_id, i++); + dev_set_name(&unit->device, "%s.%d", dev_name(&device->device), i++); init_fw_attribute_group(&unit->device, fw_unit_attributes, @@ -711,8 +710,7 @@ static void fw_device_init(struct work_struct *work) device->device.type = &fw_device_type; device->device.parent = device->card->device; device->device.devt = MKDEV(fw_cdev_major, minor); - snprintf(device->device.bus_id, sizeof(device->device.bus_id), - "fw%d", minor); + dev_set_name(&device->device, "fw%d", minor); init_fw_attribute_group(&device->device, fw_device_attributes, @@ -741,13 +739,13 @@ static void fw_device_init(struct work_struct *work) if (device->config_rom_retries) fw_notify("created device %s: GUID %08x%08x, S%d00, " "%d config ROM retries\n", - device->device.bus_id, + dev_name(&device->device), device->config_rom[3], device->config_rom[4], 1 << device->max_speed, device->config_rom_retries); else fw_notify("created device %s: GUID %08x%08x, S%d00\n", - device->device.bus_id, + dev_name(&device->device), device->config_rom[3], device->config_rom[4], 1 << device->max_speed); device->config_rom_retries = 0; @@ -883,12 +881,12 @@ static void fw_device_refresh(struct work_struct *work) FW_DEVICE_RUNNING) == FW_DEVICE_SHUTDOWN) goto gone; - fw_notify("refreshed device %s\n", device->device.bus_id); + fw_notify("refreshed device %s\n", dev_name(&device->device)); device->config_rom_retries = 0; goto out; give_up: - fw_notify("giving up on refresh of device %s\n", device->device.bus_id); + fw_notify("giving up on refresh of device %s\n", dev_name(&device->device)); gone: atomic_set(&device->state, FW_DEVICE_SHUTDOWN); fw_device_shutdown(work); diff --git a/drivers/firewire/fw-ohci.c b/drivers/firewire/fw-ohci.c index 8e16bfbdcb3d..46610b090415 100644 --- a/drivers/firewire/fw-ohci.c +++ b/drivers/firewire/fw-ohci.c @@ -2468,7 +2468,7 @@ pci_probe(struct pci_dev *dev, const struct pci_device_id *ent) goto fail_self_id; fw_notify("Added fw-ohci device %s, OHCI version %x.%x\n", - dev->dev.bus_id, version >> 16, version & 0xff); + dev_name(&dev->dev), version >> 16, version & 0xff); return 0; fail_self_id: diff --git a/drivers/firewire/fw-sbp2.c b/drivers/firewire/fw-sbp2.c index d334cac5e1fc..97df6dac3a82 100644 --- a/drivers/firewire/fw-sbp2.c +++ b/drivers/firewire/fw-sbp2.c @@ -1135,7 +1135,7 @@ static int sbp2_probe(struct device *dev) tgt->unit = unit; kref_init(&tgt->kref); INIT_LIST_HEAD(&tgt->lu_list); - tgt->bus_id = unit->device.bus_id; + tgt->bus_id = dev_name(&unit->device); tgt->guid = (u64)device->config_rom[3] << 32 | device->config_rom[4]; if (fw_device_enable_phys_dma(device) < 0) diff --git a/drivers/firmware/dmi-id.c b/drivers/firmware/dmi-id.c index e880d6c8d896..5a76d056b9d0 100644 --- a/drivers/firmware/dmi-id.c +++ b/drivers/firmware/dmi-id.c @@ -223,7 +223,7 @@ static int __init dmi_id_init(void) } dmi_dev->class = &dmi_class; - strcpy(dmi_dev->bus_id, "id"); + dev_set_name(dmi_dev, "id"); dmi_dev->groups = sys_dmi_attribute_groups; ret = device_register(dmi_dev); diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c index 3e526b6d00cb..4a597d8c2f70 100644 --- a/drivers/firmware/dmi_scan.c +++ b/drivers/firmware/dmi_scan.c @@ -81,9 +81,9 @@ static void dmi_table(u8 *buf, int len, int num, const struct dmi_header *dm = (const struct dmi_header *)data; /* - * We want to know the total length (formated area and strings) - * before decoding to make sure we won't run off the table in - * dmi_decode or dmi_string + * We want to know the total length (formatted area and + * strings) before decoding to make sure we won't run off the + * table in dmi_decode or dmi_string */ data += dm->length; while ((data - buf < len - 1) && (data[0] || data[1])) @@ -467,6 +467,17 @@ const char *dmi_get_system_info(int field) } EXPORT_SYMBOL(dmi_get_system_info); +/** + * dmi_name_in_serial - Check if string is in the DMI product serial + * information. + */ +int dmi_name_in_serial(const char *str) +{ + int f = DMI_PRODUCT_SERIAL; + if (dmi_ident[f] && strstr(dmi_ident[f], str)) + return 1; + return 0; +} /** * dmi_name_in_vendors - Check if string is anywhere in the DMI vendor information. diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 7f2ee27fe76b..48f49d93d249 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -65,6 +65,14 @@ config GPIO_SYSFS # put expanders in the right section, in alphabetical order +comment "Memory mapped GPIO expanders:" + +config GPIO_XILINX + bool "Xilinx GPIO support" + depends on PPC_OF + help + Say yes here to support the Xilinx FPGA GPIO device + comment "I2C GPIO expanders:" config GPIO_MAX732X diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 6aafdeb9ad03..49ac64e515e6 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -10,4 +10,5 @@ obj-$(CONFIG_GPIO_MCP23S08) += mcp23s08.o obj-$(CONFIG_GPIO_PCA953X) += pca953x.o obj-$(CONFIG_GPIO_PCF857X) += pcf857x.o obj-$(CONFIG_GPIO_TWL4030) += twl4030-gpio.o +obj-$(CONFIG_GPIO_XILINX) += xilinx_gpio.o obj-$(CONFIG_GPIO_BT8XX) += bt8xxgpio.o diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index faa1cc66e9cf..35e7aea4222c 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1134,7 +1134,7 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip) continue; is_out = test_bit(FLAG_IS_OUT, &gdesc->flags); - seq_printf(s, " gpio-%-3d (%-12s) %s %s", + seq_printf(s, " gpio-%-3d (%-20.20s) %s %s", gpio, gdesc->label, is_out ? "out" : "in ", chip->get @@ -1213,7 +1213,7 @@ static int gpiolib_show(struct seq_file *s, void *unused) if (dev) seq_printf(s, ", %s/%s", dev->bus ? dev->bus->name : "no-bus", - dev->bus_id); + dev_name(dev)); if (chip->label) seq_printf(s, ", %s", chip->label); if (chip->can_sleep) diff --git a/drivers/gpio/xilinx_gpio.c b/drivers/gpio/xilinx_gpio.c new file mode 100644 index 000000000000..3c1177abebd3 --- /dev/null +++ b/drivers/gpio/xilinx_gpio.c @@ -0,0 +1,235 @@ +/* + * Xilinx gpio driver + * + * Copyright 2008 Xilinx, 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/of_device.h> +#include <linux/of_platform.h> +#include <linux/of_gpio.h> +#include <linux/io.h> +#include <linux/gpio.h> + +/* Register Offset Definitions */ +#define XGPIO_DATA_OFFSET (0x0) /* Data register */ +#define XGPIO_TRI_OFFSET (0x4) /* I/O direction register */ + +struct xgpio_instance { + struct of_mm_gpio_chip mmchip; + u32 gpio_state; /* GPIO state shadow register */ + u32 gpio_dir; /* GPIO direction shadow register */ + spinlock_t gpio_lock; /* Lock used for synchronization */ +}; + +/** + * xgpio_get - Read the specified signal of the GPIO device. + * @gc: Pointer to gpio_chip device structure. + * @gpio: GPIO signal number. + * + * This function reads the specified signal of the GPIO device. It returns 0 if + * the signal clear, 1 if signal is set or negative value on error. + */ +static int xgpio_get(struct gpio_chip *gc, unsigned int gpio) +{ + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + + return (in_be32(mm_gc->regs + XGPIO_DATA_OFFSET) >> gpio) & 1; +} + +/** + * xgpio_set - Write the specified signal of the GPIO device. + * @gc: Pointer to gpio_chip device structure. + * @gpio: GPIO signal number. + * @val: Value to be written to specified signal. + * + * This function writes the specified value in to the specified signal of the + * GPIO device. + */ +static void xgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) +{ + unsigned long flags; + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct xgpio_instance *chip = + container_of(mm_gc, struct xgpio_instance, mmchip); + + spin_lock_irqsave(&chip->gpio_lock, flags); + + /* Write to GPIO signal and set its direction to output */ + if (val) + chip->gpio_state |= 1 << gpio; + else + chip->gpio_state &= ~(1 << gpio); + out_be32(mm_gc->regs + XGPIO_DATA_OFFSET, chip->gpio_state); + + spin_unlock_irqrestore(&chip->gpio_lock, flags); +} + +/** + * xgpio_dir_in - Set the direction of the specified GPIO signal as input. + * @gc: Pointer to gpio_chip device structure. + * @gpio: GPIO signal number. + * + * This function sets the direction of specified GPIO signal as input. + * It returns 0 if direction of GPIO signals is set as input otherwise it + * returns negative error value. + */ +static int xgpio_dir_in(struct gpio_chip *gc, unsigned int gpio) +{ + unsigned long flags; + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct xgpio_instance *chip = + container_of(mm_gc, struct xgpio_instance, mmchip); + + spin_lock_irqsave(&chip->gpio_lock, flags); + + /* Set the GPIO bit in shadow register and set direction as input */ + chip->gpio_dir |= (1 << gpio); + out_be32(mm_gc->regs + XGPIO_TRI_OFFSET, chip->gpio_dir); + + spin_unlock_irqrestore(&chip->gpio_lock, flags); + + return 0; +} + +/** + * xgpio_dir_out - Set the direction of the specified GPIO signal as output. + * @gc: Pointer to gpio_chip device structure. + * @gpio: GPIO signal number. + * @val: Value to be written to specified signal. + * + * This function sets the direction of specified GPIO signal as output. If all + * GPIO signals of GPIO chip is configured as input then it returns + * error otherwise it returns 0. + */ +static int xgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) +{ + unsigned long flags; + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct xgpio_instance *chip = + container_of(mm_gc, struct xgpio_instance, mmchip); + + spin_lock_irqsave(&chip->gpio_lock, flags); + + /* Write state of GPIO signal */ + if (val) + chip->gpio_state |= 1 << gpio; + else + chip->gpio_state &= ~(1 << gpio); + out_be32(mm_gc->regs + XGPIO_DATA_OFFSET, chip->gpio_state); + + /* Clear the GPIO bit in shadow register and set direction as output */ + chip->gpio_dir &= (~(1 << gpio)); + out_be32(mm_gc->regs + XGPIO_TRI_OFFSET, chip->gpio_dir); + + spin_unlock_irqrestore(&chip->gpio_lock, flags); + + return 0; +} + +/** + * xgpio_save_regs - Set initial values of GPIO pins + * @mm_gc: pointer to memory mapped GPIO chip structure + */ +static void xgpio_save_regs(struct of_mm_gpio_chip *mm_gc) +{ + struct xgpio_instance *chip = + container_of(mm_gc, struct xgpio_instance, mmchip); + + out_be32(mm_gc->regs + XGPIO_DATA_OFFSET, chip->gpio_state); + out_be32(mm_gc->regs + XGPIO_TRI_OFFSET, chip->gpio_dir); +} + +/** + * xgpio_of_probe - Probe method for the GPIO device. + * @np: pointer to device tree node + * + * This function probes the GPIO device in the device tree. It initializes the + * driver data structure. It returns 0, if the driver is bound to the GPIO + * device, or a negative value if there is an error. + */ +static int __devinit xgpio_of_probe(struct device_node *np) +{ + struct xgpio_instance *chip; + struct of_gpio_chip *ofchip; + int status = 0; + const u32 *tree_info; + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + ofchip = &chip->mmchip.of_gc; + + /* Update GPIO state shadow register with default value */ + tree_info = of_get_property(np, "xlnx,dout-default", NULL); + if (tree_info) + chip->gpio_state = *tree_info; + + /* Update GPIO direction shadow register with default value */ + chip->gpio_dir = 0xFFFFFFFF; /* By default, all pins are inputs */ + tree_info = of_get_property(np, "xlnx,tri-default", NULL); + if (tree_info) + chip->gpio_dir = *tree_info; + + /* Check device node and parent device node for device width */ + ofchip->gc.ngpio = 32; /* By default assume full GPIO controller */ + tree_info = of_get_property(np, "xlnx,gpio-width", NULL); + if (!tree_info) + tree_info = of_get_property(np->parent, + "xlnx,gpio-width", NULL); + if (tree_info) + ofchip->gc.ngpio = *tree_info; + + spin_lock_init(&chip->gpio_lock); + + ofchip->gpio_cells = 2; + ofchip->gc.direction_input = xgpio_dir_in; + ofchip->gc.direction_output = xgpio_dir_out; + ofchip->gc.get = xgpio_get; + ofchip->gc.set = xgpio_set; + + chip->mmchip.save_regs = xgpio_save_regs; + + /* Call the OF gpio helper to setup and register the GPIO device */ + status = of_mm_gpiochip_add(np, &chip->mmchip); + if (status) { + kfree(chip); + pr_err("%s: error in probe function with status %d\n", + np->full_name, status); + return status; + } + pr_info("XGpio: %s: registered\n", np->full_name); + return 0; +} + +static struct of_device_id xgpio_of_match[] __devinitdata = { + { .compatible = "xlnx,xps-gpio-1.00.a", }, + { /* end of list */ }, +}; + +static int __init xgpio_init(void) +{ + struct device_node *np; + + for_each_matching_node(np, xgpio_of_match) + xgpio_of_probe(np); + + return 0; +} + +/* Make sure we get initialized before anyone else tries to use us */ +subsys_initcall(xgpio_init); +/* No exit call at the moment as we cannot unregister of GPIO chips */ + +MODULE_AUTHOR("Xilinx, Inc."); +MODULE_DESCRIPTION("Xilinx GPIO driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 96f416afc3f6..996097acb5e7 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -266,11 +266,19 @@ int drm_init(struct drm_driver *driver) for (i = 0; driver->pci_driver.id_table[i].vendor != 0; i++) { pid = (struct pci_device_id *)&driver->pci_driver.id_table[i]; + /* Loop around setting up a DRM device for each PCI device + * matching our ID and device class. If we had the internal + * function that pci_get_subsys and pci_get_class used, we'd + * be able to just pass pid in instead of doing a two-stage + * thing. + */ pdev = NULL; - /* pass back in pdev to account for multiple identical cards */ while ((pdev = pci_get_subsys(pid->vendor, pid->device, pid->subvendor, pid->subdevice, pdev)) != NULL) { + if ((pdev->class & pid->class_mask) != pid->class) + continue; + /* stealth mode requires a manual probe */ pci_dev_get(pdev); drm_get_dev(pdev, pid, driver); @@ -297,6 +305,8 @@ static void drm_cleanup(struct drm_device * dev) return; } + drm_vblank_cleanup(dev); + drm_lastclose(dev); if (drm_core_has_MTRR(dev) && drm_core_has_AGP(dev) && diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 212a94f715b2..1e787f894b3c 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -94,7 +94,7 @@ static void vblank_disable_fn(unsigned long arg) } } -static void drm_vblank_cleanup(struct drm_device *dev) +void drm_vblank_cleanup(struct drm_device *dev) { /* Bail if the driver didn't call drm_vblank_init() */ if (dev->num_crtcs == 0) @@ -278,10 +278,6 @@ int drm_irq_uninstall(struct drm_device * dev) free_irq(dev->pdev->irq, dev); - drm_vblank_cleanup(dev); - - dev->locked_tasklet_func = NULL; - return 0; } EXPORT_SYMBOL(drm_irq_uninstall); @@ -699,81 +695,3 @@ void drm_handle_vblank(struct drm_device *dev, int crtc) drm_vbl_send_signals(dev, crtc); } EXPORT_SYMBOL(drm_handle_vblank); - -/** - * Tasklet wrapper function. - * - * \param data DRM device in disguise. - * - * Attempts to grab the HW lock and calls the driver callback on success. On - * failure, leave the lock marked as contended so the callback can be called - * from drm_unlock(). - */ -static void drm_locked_tasklet_func(unsigned long data) -{ - struct drm_device *dev = (struct drm_device *)data; - unsigned long irqflags; - void (*tasklet_func)(struct drm_device *); - - spin_lock_irqsave(&dev->tasklet_lock, irqflags); - tasklet_func = dev->locked_tasklet_func; - spin_unlock_irqrestore(&dev->tasklet_lock, irqflags); - - if (!tasklet_func || - !drm_lock_take(&dev->lock, - DRM_KERNEL_CONTEXT)) { - return; - } - - dev->lock.lock_time = jiffies; - atomic_inc(&dev->counts[_DRM_STAT_LOCKS]); - - spin_lock_irqsave(&dev->tasklet_lock, irqflags); - tasklet_func = dev->locked_tasklet_func; - dev->locked_tasklet_func = NULL; - spin_unlock_irqrestore(&dev->tasklet_lock, irqflags); - - if (tasklet_func != NULL) - tasklet_func(dev); - - drm_lock_free(&dev->lock, - DRM_KERNEL_CONTEXT); -} - -/** - * Schedule a tasklet to call back a driver hook with the HW lock held. - * - * \param dev DRM device. - * \param func Driver callback. - * - * This is intended for triggering actions that require the HW lock from an - * interrupt handler. The lock will be grabbed ASAP after the interrupt handler - * completes. Note that the callback may be called from interrupt or process - * context, it must not make any assumptions about this. Also, the HW lock will - * be held with the kernel context or any client context. - */ -void drm_locked_tasklet(struct drm_device *dev, void (*func)(struct drm_device *)) -{ - unsigned long irqflags; - static DECLARE_TASKLET(drm_tasklet, drm_locked_tasklet_func, 0); - - if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ) || - test_bit(TASKLET_STATE_SCHED, &drm_tasklet.state)) - return; - - spin_lock_irqsave(&dev->tasklet_lock, irqflags); - - if (dev->locked_tasklet_func) { - spin_unlock_irqrestore(&dev->tasklet_lock, irqflags); - return; - } - - dev->locked_tasklet_func = func; - - spin_unlock_irqrestore(&dev->tasklet_lock, irqflags); - - drm_tasklet.data = (unsigned long)dev; - - tasklet_hi_schedule(&drm_tasklet); -} -EXPORT_SYMBOL(drm_locked_tasklet); diff --git a/drivers/gpu/drm/drm_lock.c b/drivers/gpu/drm/drm_lock.c index 888159e03d26..1cfa72031f8f 100644 --- a/drivers/gpu/drm/drm_lock.c +++ b/drivers/gpu/drm/drm_lock.c @@ -154,8 +154,6 @@ int drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv) int drm_unlock(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_lock *lock = data; - unsigned long irqflags; - void (*tasklet_func)(struct drm_device *); if (lock->context == DRM_KERNEL_CONTEXT) { DRM_ERROR("Process %d using kernel context %d\n", @@ -163,13 +161,6 @@ int drm_unlock(struct drm_device *dev, void *data, struct drm_file *file_priv) return -EINVAL; } - spin_lock_irqsave(&dev->tasklet_lock, irqflags); - tasklet_func = dev->locked_tasklet_func; - dev->locked_tasklet_func = NULL; - spin_unlock_irqrestore(&dev->tasklet_lock, irqflags); - if (tasklet_func != NULL) - tasklet_func(dev); - atomic_inc(&dev->counts[_DRM_STAT_UNLOCKS]); /* kernel_context_switch isn't used by any of the x86 drm diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index 141e33004a76..66c96ec66672 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c @@ -92,7 +92,6 @@ static int drm_fill_in_dev(struct drm_device * dev, struct pci_dev *pdev, spin_lock_init(&dev->count_lock); spin_lock_init(&dev->drw_lock); - spin_lock_init(&dev->tasklet_lock); spin_lock_init(&dev->lock.spinlock); init_timer(&dev->timer); mutex_init(&dev->struct_mutex); diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c index 1611b9bcbe7f..98a575cccbe7 100644 --- a/drivers/gpu/drm/drm_sysfs.c +++ b/drivers/gpu/drm/drm_sysfs.c @@ -165,7 +165,7 @@ int drm_sysfs_device_add(struct drm_minor *minor) minor->kdev.devt = minor->device; minor_str = "card%d"; - snprintf(minor->kdev.bus_id, BUS_ID_SIZE, minor_str, minor->index); + dev_set_name(&minor->kdev, minor_str, minor->index); err = device_register(&minor->kdev); if (err) { diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index 5ba78e4fd2b5..d8fb5d8ee7ea 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -3,13 +3,14 @@ # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. ccflags-y := -Iinclude/drm -i915-y := i915_drv.o i915_dma.o i915_irq.o i915_mem.o i915_opregion.o \ +i915-y := i915_drv.o i915_dma.o i915_irq.o i915_mem.o \ i915_suspend.o \ i915_gem.o \ i915_gem_debug.o \ i915_gem_proc.o \ i915_gem_tiling.o +i915-$(CONFIG_ACPI) += i915_opregion.o i915-$(CONFIG_COMPAT) += i915_ioc32.o obj-$(CONFIG_DRM_I915) += i915.o diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 01de536e0211..ba89b42f790a 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -154,6 +154,9 @@ static int i915_dma_cleanup(struct drm_device * dev) if (I915_NEED_GFX_HWS(dev)) i915_free_hws(dev); + dev_priv->sarea = NULL; + dev_priv->sarea_priv = NULL; + return 0; } @@ -442,7 +445,7 @@ static void i915_emit_breadcrumb(struct drm_device *dev) BEGIN_LP_RING(4); OUT_RING(MI_STORE_DWORD_INDEX); - OUT_RING(5 << MI_STORE_DWORD_INDEX_SHIFT); + OUT_RING(I915_BREADCRUMB_INDEX << MI_STORE_DWORD_INDEX_SHIFT); OUT_RING(dev_priv->counter); OUT_RING(0); ADVANCE_LP_RING(); @@ -573,7 +576,7 @@ static int i915_dispatch_flip(struct drm_device * dev) BEGIN_LP_RING(4); OUT_RING(MI_STORE_DWORD_INDEX); - OUT_RING(5 << MI_STORE_DWORD_INDEX_SHIFT); + OUT_RING(I915_BREADCRUMB_INDEX << MI_STORE_DWORD_INDEX_SHIFT); OUT_RING(dev_priv->counter); OUT_RING(0); ADVANCE_LP_RING(); @@ -608,7 +611,6 @@ static int i915_batchbuffer(struct drm_device *dev, void *data, struct drm_file *file_priv) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - u32 *hw_status = dev_priv->hw_status_page; drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *) dev_priv->sarea_priv; drm_i915_batchbuffer_t *batch = data; @@ -634,7 +636,7 @@ static int i915_batchbuffer(struct drm_device *dev, void *data, mutex_unlock(&dev->struct_mutex); if (sarea_priv) - sarea_priv->last_dispatch = (int)hw_status[5]; + sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); return ret; } @@ -642,7 +644,6 @@ static int i915_cmdbuffer(struct drm_device *dev, void *data, struct drm_file *file_priv) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - u32 *hw_status = dev_priv->hw_status_page; drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *) dev_priv->sarea_priv; drm_i915_cmdbuffer_t *cmdbuf = data; @@ -670,7 +671,7 @@ static int i915_cmdbuffer(struct drm_device *dev, void *data, } if (sarea_priv) - sarea_priv->last_dispatch = (int)hw_status[5]; + sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); return 0; } @@ -849,13 +850,19 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) * be lost or delayed */ if (!IS_I945G(dev) && !IS_I945GM(dev) && !IS_I965GM(dev)) - if (pci_enable_msi(dev->pdev)) - DRM_ERROR("failed to enable MSI\n"); + pci_enable_msi(dev->pdev); intel_opregion_init(dev); spin_lock_init(&dev_priv->user_irq_lock); + ret = drm_vblank_init(dev, I915_NUM_PIPE); + + if (ret) { + (void) i915_driver_unload(dev); + return ret; + } + return ret; } @@ -960,6 +967,7 @@ struct drm_ioctl_desc i915_ioctls[] = { DRM_IOCTL_DEF(DRM_I915_GEM_SW_FINISH, i915_gem_sw_finish_ioctl, 0), DRM_IOCTL_DEF(DRM_I915_GEM_SET_TILING, i915_gem_set_tiling, 0), DRM_IOCTL_DEF(DRM_I915_GEM_GET_TILING, i915_gem_get_tiling, 0), + DRM_IOCTL_DEF(DRM_I915_GEM_GET_APERTURE, i915_gem_get_aperture_ioctl, 0), }; int i915_max_ioctl = DRM_ARRAY_SIZE(i915_ioctls); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index f20ffe17df71..0a4f39b9a0ec 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -31,6 +31,7 @@ #define _I915_DRV_H_ #include "i915_reg.h" +#include <linux/io-mapping.h> /* General customization: */ @@ -46,6 +47,8 @@ enum pipe { PIPE_B, }; +#define I915_NUM_PIPE 2 + /* Interface history: * * 1.1: Original. @@ -87,13 +90,6 @@ struct mem_block { struct drm_file *file_priv; /* NULL: free, -1: heap, other: real files */ }; -typedef struct _drm_i915_vbl_swap { - struct list_head head; - drm_drawable_t drw_id; - unsigned int pipe; - unsigned int sequence; -} drm_i915_vbl_swap_t; - struct opregion_header; struct opregion_acpi; struct opregion_swsci; @@ -138,6 +134,7 @@ typedef struct drm_i915_private { int user_irq_refcount; /** Cached value of IMR to avoid reads in updating the bitfield */ u32 irq_mask_reg; + u32 pipestat[2]; int tex_lru_log_granularity; int allow_batchbuffer; @@ -145,10 +142,6 @@ typedef struct drm_i915_private { unsigned int sr01, adpa, ppcr, dvob, dvoc, lvds; int vblank_pipe; - spinlock_t swaps_lock; - drm_i915_vbl_swap_t vbl_swaps; - unsigned int swaps_pending; - struct intel_opregion opregion; /* Register state */ @@ -156,6 +149,8 @@ typedef struct drm_i915_private { u32 saveDSPACNTR; u32 saveDSPBCNTR; u32 saveDSPARB; + u32 saveRENDERSTANDBY; + u32 saveHWS; u32 savePIPEACONF; u32 savePIPEBCONF; u32 savePIPEASRC; @@ -240,12 +235,11 @@ typedef struct drm_i915_private { u8 saveDACDATA[256*3]; /* 256 3-byte colors */ u8 saveCR[37]; - /** Work task for vblank-related ring access */ - struct work_struct vblank_work; - struct { struct drm_mm gtt_space; + struct io_mapping *gtt_mapping; + /** * List of objects currently involved in rendering from the * ringbuffer. @@ -441,7 +435,6 @@ extern int i915_irq_wait(struct drm_device *dev, void *data, void i915_user_irq_get(struct drm_device *dev); void i915_user_irq_put(struct drm_device *dev); -extern void i915_vblank_work_handler(struct work_struct *work); extern irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS); extern void i915_driver_irq_preinstall(struct drm_device * dev); extern int i915_driver_irq_postinstall(struct drm_device *dev); @@ -457,6 +450,13 @@ extern int i915_vblank_swap(struct drm_device *dev, void *data, struct drm_file *file_priv); extern void i915_enable_irq(drm_i915_private_t *dev_priv, u32 mask); +void +i915_enable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask); + +void +i915_disable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask); + + /* i915_mem.c */ extern int i915_mem_alloc(struct drm_device *dev, void *data, struct drm_file *file_priv); @@ -502,6 +502,8 @@ int i915_gem_set_tiling(struct drm_device *dev, void *data, struct drm_file *file_priv); int i915_gem_get_tiling(struct drm_device *dev, void *data, struct drm_file *file_priv); +int i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); void i915_gem_load(struct drm_device *dev); int i915_gem_proc_init(struct drm_minor *minor); void i915_gem_proc_cleanup(struct drm_minor *minor); @@ -539,11 +541,18 @@ extern int i915_restore_state(struct drm_device *dev); extern int i915_save_state(struct drm_device *dev); extern int i915_restore_state(struct drm_device *dev); +#ifdef CONFIG_ACPI /* i915_opregion.c */ extern int intel_opregion_init(struct drm_device *dev); extern void intel_opregion_free(struct drm_device *dev); extern void opregion_asle_intr(struct drm_device *dev); extern void opregion_enable_asle(struct drm_device *dev); +#else +static inline int intel_opregion_init(struct drm_device *dev) { return 0; } +static inline void intel_opregion_free(struct drm_device *dev) { return; } +static inline void opregion_asle_intr(struct drm_device *dev) { return; } +static inline void opregion_enable_asle(struct drm_device *dev) { return; } +#endif /** * Lock test for when it's just for synchronization of ring access. @@ -610,8 +619,9 @@ extern void opregion_enable_asle(struct drm_device *dev); * The area from dword 0x20 to 0x3ff is available for driver usage. */ #define READ_HWSP(dev_priv, reg) (((volatile u32*)(dev_priv->hw_status_page))[reg]) -#define READ_BREADCRUMB(dev_priv) READ_HWSP(dev_priv, 5) +#define READ_BREADCRUMB(dev_priv) READ_HWSP(dev_priv, I915_BREADCRUMB_INDEX) #define I915_GEM_HWS_INDEX 0x20 +#define I915_BREADCRUMB_INDEX 0x21 extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 17ae330ff269..54bb0d0e49b8 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -31,6 +31,8 @@ #include "i915_drv.h" #include <linux/swap.h> +#define I915_GEM_GPU_DOMAINS (~(I915_GEM_DOMAIN_CPU | I915_GEM_DOMAIN_GTT)) + static int i915_gem_object_set_domain(struct drm_gem_object *obj, uint32_t read_domains, @@ -79,6 +81,22 @@ i915_gem_init_ioctl(struct drm_device *dev, void *data, return 0; } +int +i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_i915_gem_get_aperture *args = data; + + if (!(dev->driver->driver_features & DRIVER_GEM)) + return -ENODEV; + + args->aper_size = dev->gtt_total; + args->aper_available_size = (args->aper_size - + atomic_read(&dev->pin_memory)); + + return 0; +} + /** * Creates a new mm object and returns a handle to it. @@ -171,35 +189,50 @@ i915_gem_pread_ioctl(struct drm_device *dev, void *data, return 0; } -/* - * Try to write quickly with an atomic kmap. Return true on success. - * - * If this fails (which includes a partial write), we'll redo the whole - * thing with the slow version. - * - * This is a workaround for the low performance of iounmap (approximate - * 10% cpu cost on normal 3D workloads). kmap_atomic on HIGHMEM kernels - * happens to let us map card memory without taking IPIs. When the vmap - * rework lands we should be able to dump this hack. +/* This is the fast write path which cannot handle + * page faults in the source data */ -static inline int fast_user_write(unsigned long pfn, char __user *user_data, - int l, int o) + +static inline int +fast_user_write(struct io_mapping *mapping, + loff_t page_base, int page_offset, + char __user *user_data, + int length) { -#ifdef CONFIG_HIGHMEM - unsigned long unwritten; char *vaddr_atomic; + unsigned long unwritten; - vaddr_atomic = kmap_atomic_pfn(pfn, KM_USER0); -#if WATCH_PWRITE - DRM_INFO("pwrite i %d o %d l %d pfn %ld vaddr %p\n", - i, o, l, pfn, vaddr_atomic); -#endif - unwritten = __copy_from_user_inatomic_nocache(vaddr_atomic + o, user_data, l); - kunmap_atomic(vaddr_atomic, KM_USER0); - return !unwritten; -#else + vaddr_atomic = io_mapping_map_atomic_wc(mapping, page_base); + unwritten = __copy_from_user_inatomic_nocache(vaddr_atomic + page_offset, + user_data, length); + io_mapping_unmap_atomic(vaddr_atomic); + if (unwritten) + return -EFAULT; + return 0; +} + +/* Here's the write path which can sleep for + * page faults + */ + +static inline int +slow_user_write(struct io_mapping *mapping, + loff_t page_base, int page_offset, + char __user *user_data, + int length) +{ + char __iomem *vaddr; + unsigned long unwritten; + + vaddr = io_mapping_map_wc(mapping, page_base); + if (vaddr == NULL) + return -EFAULT; + unwritten = __copy_from_user(vaddr + page_offset, + user_data, length); + io_mapping_unmap(vaddr); + if (unwritten) + return -EFAULT; return 0; -#endif } static int @@ -208,10 +241,12 @@ i915_gem_gtt_pwrite(struct drm_device *dev, struct drm_gem_object *obj, struct drm_file *file_priv) { struct drm_i915_gem_object *obj_priv = obj->driver_private; + drm_i915_private_t *dev_priv = dev->dev_private; ssize_t remain; - loff_t offset; + loff_t offset, page_base; char __user *user_data; - int ret = 0; + int page_offset, page_length; + int ret; user_data = (char __user *) (uintptr_t) args->data_ptr; remain = args->size; @@ -235,57 +270,37 @@ i915_gem_gtt_pwrite(struct drm_device *dev, struct drm_gem_object *obj, obj_priv->dirty = 1; while (remain > 0) { - unsigned long pfn; - int i, o, l; - /* Operation in this page * - * i = page number - * o = offset within page - * l = bytes to copy + * page_base = page offset within aperture + * page_offset = offset within page + * page_length = bytes to copy for this page */ - i = offset >> PAGE_SHIFT; - o = offset & (PAGE_SIZE-1); - l = remain; - if ((o + l) > PAGE_SIZE) - l = PAGE_SIZE - o; - - pfn = (dev->agp->base >> PAGE_SHIFT) + i; - - if (!fast_user_write(pfn, user_data, l, o)) { - unsigned long unwritten; - char __iomem *vaddr; - - vaddr = ioremap_wc(pfn << PAGE_SHIFT, PAGE_SIZE); -#if WATCH_PWRITE - DRM_INFO("pwrite slow i %d o %d l %d " - "pfn %ld vaddr %p\n", - i, o, l, pfn, vaddr); -#endif - if (vaddr == NULL) { - ret = -EFAULT; - goto fail; - } - unwritten = __copy_from_user(vaddr + o, user_data, l); -#if WATCH_PWRITE - DRM_INFO("unwritten %ld\n", unwritten); -#endif - iounmap(vaddr); - if (unwritten) { - ret = -EFAULT; + page_base = (offset & ~(PAGE_SIZE-1)); + page_offset = offset & (PAGE_SIZE-1); + page_length = remain; + if ((page_offset + remain) > PAGE_SIZE) + page_length = PAGE_SIZE - page_offset; + + ret = fast_user_write (dev_priv->mm.gtt_mapping, page_base, + page_offset, user_data, page_length); + + /* If we get a fault while copying data, then (presumably) our + * source page isn't available. In this case, use the + * non-atomic function + */ + if (ret) { + ret = slow_user_write (dev_priv->mm.gtt_mapping, + page_base, page_offset, + user_data, page_length); + if (ret) goto fail; - } } - remain -= l; - user_data += l; - offset += l; + remain -= page_length; + user_data += page_length; + offset += page_length; } -#if WATCH_PWRITE && 1 - i915_gem_clflush_object(obj); - i915_gem_dump_object(obj, args->offset + args->size, __func__, ~0); - i915_gem_clflush_object(obj); -#endif fail: i915_gem_object_unpin(obj); @@ -1436,11 +1451,9 @@ i915_gem_object_set_domain_range(struct drm_gem_object *obj, read_domains, write_domain); /* Wait on any GPU rendering to the object to be flushed. */ - if (obj->write_domain & ~(I915_GEM_DOMAIN_CPU | I915_GEM_DOMAIN_GTT)) { - ret = i915_gem_object_wait_rendering(obj); - if (ret) - return ret; - } + ret = i915_gem_object_wait_rendering(obj); + if (ret) + return ret; if (obj_priv->page_cpu_valid == NULL) { obj_priv->page_cpu_valid = drm_calloc(1, obj->size / PAGE_SIZE, @@ -1503,12 +1516,12 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj, struct drm_i915_gem_exec_object *entry) { struct drm_device *dev = obj->dev; + drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_gem_relocation_entry reloc; struct drm_i915_gem_relocation_entry __user *relocs; struct drm_i915_gem_object *obj_priv = obj->driver_private; int i, ret; - uint32_t last_reloc_offset = -1; - void __iomem *reloc_page = NULL; + void __iomem *reloc_page; /* Choose the GTT offset for our buffer and put it there. */ ret = i915_gem_object_pin(obj, (uint32_t) entry->alignment); @@ -1631,26 +1644,11 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj, * perform. */ reloc_offset = obj_priv->gtt_offset + reloc.offset; - if (reloc_page == NULL || - (last_reloc_offset & ~(PAGE_SIZE - 1)) != - (reloc_offset & ~(PAGE_SIZE - 1))) { - if (reloc_page != NULL) - iounmap(reloc_page); - - reloc_page = ioremap_wc(dev->agp->base + - (reloc_offset & - ~(PAGE_SIZE - 1)), - PAGE_SIZE); - last_reloc_offset = reloc_offset; - if (reloc_page == NULL) { - drm_gem_object_unreference(target_obj); - i915_gem_object_unpin(obj); - return -ENOMEM; - } - } - + reloc_page = io_mapping_map_atomic_wc(dev_priv->mm.gtt_mapping, + (reloc_offset & + ~(PAGE_SIZE - 1))); reloc_entry = (uint32_t __iomem *)(reloc_page + - (reloc_offset & (PAGE_SIZE - 1))); + (reloc_offset & (PAGE_SIZE - 1))); reloc_val = target_obj_priv->gtt_offset + reloc.delta; #if WATCH_BUF @@ -1659,6 +1657,7 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj, readl(reloc_entry), reloc_val); #endif writel(reloc_val, reloc_entry); + io_mapping_unmap_atomic(reloc_page); /* Write the updated presumed offset for this entry back out * to the user. @@ -1674,9 +1673,6 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj, drm_gem_object_unreference(target_obj); } - if (reloc_page != NULL) - iounmap(reloc_page); - #if WATCH_BUF if (0) i915_gem_dump_object(obj, 128, __func__, ~0); @@ -1870,17 +1866,6 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, for (i = 0; i < args->buffer_count; i++) { struct drm_gem_object *obj = object_list[i]; - struct drm_i915_gem_object *obj_priv = obj->driver_private; - - if (obj_priv->gtt_space == NULL) { - /* We evicted the buffer in the process of validating - * our set of buffers in. We could try to recover by - * kicking them everything out and trying again from - * the start. - */ - ret = -ENOMEM; - goto err; - } /* make sure all previous memory operations have passed */ ret = i915_gem_object_set_domain(obj, @@ -2299,29 +2284,52 @@ i915_gem_idle(struct drm_device *dev) i915_gem_retire_requests(dev); - /* Active and flushing should now be empty as we've - * waited for a sequence higher than any pending execbuffer - */ - BUG_ON(!list_empty(&dev_priv->mm.active_list)); - BUG_ON(!list_empty(&dev_priv->mm.flushing_list)); + if (!dev_priv->mm.wedged) { + /* Active and flushing should now be empty as we've + * waited for a sequence higher than any pending execbuffer + */ + WARN_ON(!list_empty(&dev_priv->mm.active_list)); + WARN_ON(!list_empty(&dev_priv->mm.flushing_list)); + /* Request should now be empty as we've also waited + * for the last request in the list + */ + WARN_ON(!list_empty(&dev_priv->mm.request_list)); + } - /* Request should now be empty as we've also waited - * for the last request in the list + /* Empty the active and flushing lists to inactive. If there's + * anything left at this point, it means that we're wedged and + * nothing good's going to happen by leaving them there. So strip + * the GPU domains and just stuff them onto inactive. */ - BUG_ON(!list_empty(&dev_priv->mm.request_list)); + while (!list_empty(&dev_priv->mm.active_list)) { + struct drm_i915_gem_object *obj_priv; + + obj_priv = list_first_entry(&dev_priv->mm.active_list, + struct drm_i915_gem_object, + list); + obj_priv->obj->write_domain &= ~I915_GEM_GPU_DOMAINS; + i915_gem_object_move_to_inactive(obj_priv->obj); + } + + while (!list_empty(&dev_priv->mm.flushing_list)) { + struct drm_i915_gem_object *obj_priv; - /* Move all buffers out of the GTT. */ + obj_priv = list_first_entry(&dev_priv->mm.active_list, + struct drm_i915_gem_object, + list); + obj_priv->obj->write_domain &= ~I915_GEM_GPU_DOMAINS; + i915_gem_object_move_to_inactive(obj_priv->obj); + } + + + /* Move all inactive buffers out of the GTT. */ ret = i915_gem_evict_from_list(dev, &dev_priv->mm.inactive_list); + WARN_ON(!list_empty(&dev_priv->mm.inactive_list)); if (ret) { mutex_unlock(&dev->struct_mutex); return ret; } - BUG_ON(!list_empty(&dev_priv->mm.active_list)); - BUG_ON(!list_empty(&dev_priv->mm.flushing_list)); - BUG_ON(!list_empty(&dev_priv->mm.inactive_list)); - BUG_ON(!list_empty(&dev_priv->mm.request_list)); - i915_gem_cleanup_ringbuffer(dev); mutex_unlock(&dev->struct_mutex); @@ -2518,6 +2526,10 @@ i915_gem_entervt_ioctl(struct drm_device *dev, void *data, if (ret != 0) return ret; + dev_priv->mm.gtt_mapping = io_mapping_create_wc(dev->agp->base, + dev->agp->agp_info.aper_size + * 1024 * 1024); + mutex_lock(&dev->struct_mutex); BUG_ON(!list_empty(&dev_priv->mm.active_list)); BUG_ON(!list_empty(&dev_priv->mm.flushing_list)); @@ -2535,11 +2547,13 @@ int i915_gem_leavevt_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { + drm_i915_private_t *dev_priv = dev->dev_private; int ret; ret = i915_gem_idle(dev); drm_irq_uninstall(dev); + io_mapping_free(dev_priv->mm.gtt_mapping); return ret; } diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 26f48932a51e..fe3d9cc72bf5 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -33,11 +33,23 @@ #define MAX_NOPID ((u32)~0) -/** These are the interrupts used by the driver */ -#define I915_INTERRUPT_ENABLE_MASK (I915_USER_INTERRUPT | \ - I915_ASLE_INTERRUPT | \ - I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | \ - I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) +/** + * Interrupts that are always left unmasked. + * + * Since pipe events are edge-triggered from the PIPESTAT register to IIR, + * we leave them always unmasked in IMR and then control enabling them through + * PIPESTAT alone. + */ +#define I915_INTERRUPT_ENABLE_FIX (I915_ASLE_INTERRUPT | \ + I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | \ + I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) + +/** Interrupts that we mask and unmask at runtime. */ +#define I915_INTERRUPT_ENABLE_VAR (I915_USER_INTERRUPT) + +/** These are all of the interrupts used by the driver */ +#define I915_INTERRUPT_ENABLE_MASK (I915_INTERRUPT_ENABLE_FIX | \ + I915_INTERRUPT_ENABLE_VAR) void i915_enable_irq(drm_i915_private_t *dev_priv, u32 mask) @@ -59,6 +71,41 @@ i915_disable_irq(drm_i915_private_t *dev_priv, u32 mask) } } +static inline u32 +i915_pipestat(int pipe) +{ + if (pipe == 0) + return PIPEASTAT; + if (pipe == 1) + return PIPEBSTAT; + BUG_ON(1); +} + +void +i915_enable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask) +{ + if ((dev_priv->pipestat[pipe] & mask) != mask) { + u32 reg = i915_pipestat(pipe); + + dev_priv->pipestat[pipe] |= mask; + /* Enable the interrupt, clear any pending status */ + I915_WRITE(reg, dev_priv->pipestat[pipe] | (mask >> 16)); + (void) I915_READ(reg); + } +} + +void +i915_disable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask) +{ + if ((dev_priv->pipestat[pipe] & mask) != 0) { + u32 reg = i915_pipestat(pipe); + + dev_priv->pipestat[pipe] &= ~mask; + I915_WRITE(reg, dev_priv->pipestat[pipe]); + (void) I915_READ(reg); + } +} + /** * i915_pipe_enabled - check if a pipe is enabled * @dev: DRM device @@ -80,211 +127,6 @@ i915_pipe_enabled(struct drm_device *dev, int pipe) return 0; } -/** - * Emit blits for scheduled buffer swaps. - * - * This function will be called with the HW lock held. - * Because this function must grab the ring mutex (dev->struct_mutex), - * it can no longer run at soft irq time. We'll fix this when we do - * the DRI2 swap buffer work. - */ -static void i915_vblank_tasklet(struct drm_device *dev) -{ - drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - unsigned long irqflags; - struct list_head *list, *tmp, hits, *hit; - int nhits, nrects, slice[2], upper[2], lower[2], i; - unsigned counter[2]; - struct drm_drawable_info *drw; - drm_i915_sarea_t *sarea_priv = dev_priv->sarea_priv; - u32 cpp = dev_priv->cpp; - u32 cmd = (cpp == 4) ? (XY_SRC_COPY_BLT_CMD | - XY_SRC_COPY_BLT_WRITE_ALPHA | - XY_SRC_COPY_BLT_WRITE_RGB) - : XY_SRC_COPY_BLT_CMD; - u32 src_pitch = sarea_priv->pitch * cpp; - u32 dst_pitch = sarea_priv->pitch * cpp; - u32 ropcpp = (0xcc << 16) | ((cpp - 1) << 24); - RING_LOCALS; - - mutex_lock(&dev->struct_mutex); - - if (IS_I965G(dev) && sarea_priv->front_tiled) { - cmd |= XY_SRC_COPY_BLT_DST_TILED; - dst_pitch >>= 2; - } - if (IS_I965G(dev) && sarea_priv->back_tiled) { - cmd |= XY_SRC_COPY_BLT_SRC_TILED; - src_pitch >>= 2; - } - - counter[0] = drm_vblank_count(dev, 0); - counter[1] = drm_vblank_count(dev, 1); - - DRM_DEBUG("\n"); - - INIT_LIST_HEAD(&hits); - - nhits = nrects = 0; - - spin_lock_irqsave(&dev_priv->swaps_lock, irqflags); - - /* Find buffer swaps scheduled for this vertical blank */ - list_for_each_safe(list, tmp, &dev_priv->vbl_swaps.head) { - drm_i915_vbl_swap_t *vbl_swap = - list_entry(list, drm_i915_vbl_swap_t, head); - int pipe = vbl_swap->pipe; - - if ((counter[pipe] - vbl_swap->sequence) > (1<<23)) - continue; - - list_del(list); - dev_priv->swaps_pending--; - drm_vblank_put(dev, pipe); - - spin_unlock(&dev_priv->swaps_lock); - spin_lock(&dev->drw_lock); - - drw = drm_get_drawable_info(dev, vbl_swap->drw_id); - - list_for_each(hit, &hits) { - drm_i915_vbl_swap_t *swap_cmp = - list_entry(hit, drm_i915_vbl_swap_t, head); - struct drm_drawable_info *drw_cmp = - drm_get_drawable_info(dev, swap_cmp->drw_id); - - /* Make sure both drawables are still - * around and have some rectangles before - * we look inside to order them for the - * blts below. - */ - if (drw_cmp && drw_cmp->num_rects > 0 && - drw && drw->num_rects > 0 && - drw_cmp->rects[0].y1 > drw->rects[0].y1) { - list_add_tail(list, hit); - break; - } - } - - spin_unlock(&dev->drw_lock); - - /* List of hits was empty, or we reached the end of it */ - if (hit == &hits) - list_add_tail(list, hits.prev); - - nhits++; - - spin_lock(&dev_priv->swaps_lock); - } - - if (nhits == 0) { - spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags); - mutex_unlock(&dev->struct_mutex); - return; - } - - spin_unlock(&dev_priv->swaps_lock); - - i915_kernel_lost_context(dev); - - if (IS_I965G(dev)) { - BEGIN_LP_RING(4); - - OUT_RING(GFX_OP_DRAWRECT_INFO_I965); - OUT_RING(0); - OUT_RING(((sarea_priv->width - 1) & 0xffff) | ((sarea_priv->height - 1) << 16)); - OUT_RING(0); - ADVANCE_LP_RING(); - } else { - BEGIN_LP_RING(6); - - OUT_RING(GFX_OP_DRAWRECT_INFO); - OUT_RING(0); - OUT_RING(0); - OUT_RING(sarea_priv->width | sarea_priv->height << 16); - OUT_RING(sarea_priv->width | sarea_priv->height << 16); - OUT_RING(0); - - ADVANCE_LP_RING(); - } - - sarea_priv->ctxOwner = DRM_KERNEL_CONTEXT; - - upper[0] = upper[1] = 0; - slice[0] = max(sarea_priv->pipeA_h / nhits, 1); - slice[1] = max(sarea_priv->pipeB_h / nhits, 1); - lower[0] = sarea_priv->pipeA_y + slice[0]; - lower[1] = sarea_priv->pipeB_y + slice[0]; - - spin_lock(&dev->drw_lock); - - /* Emit blits for buffer swaps, partitioning both outputs into as many - * slices as there are buffer swaps scheduled in order to avoid tearing - * (based on the assumption that a single buffer swap would always - * complete before scanout starts). - */ - for (i = 0; i++ < nhits; - upper[0] = lower[0], lower[0] += slice[0], - upper[1] = lower[1], lower[1] += slice[1]) { - if (i == nhits) - lower[0] = lower[1] = sarea_priv->height; - - list_for_each(hit, &hits) { - drm_i915_vbl_swap_t *swap_hit = - list_entry(hit, drm_i915_vbl_swap_t, head); - struct drm_clip_rect *rect; - int num_rects, pipe; - unsigned short top, bottom; - - drw = drm_get_drawable_info(dev, swap_hit->drw_id); - - /* The drawable may have been destroyed since - * the vblank swap was queued - */ - if (!drw) - continue; - - rect = drw->rects; - pipe = swap_hit->pipe; - top = upper[pipe]; - bottom = lower[pipe]; - - for (num_rects = drw->num_rects; num_rects--; rect++) { - int y1 = max(rect->y1, top); - int y2 = min(rect->y2, bottom); - - if (y1 >= y2) - continue; - - BEGIN_LP_RING(8); - - OUT_RING(cmd); - OUT_RING(ropcpp | dst_pitch); - OUT_RING((y1 << 16) | rect->x1); - OUT_RING((y2 << 16) | rect->x2); - OUT_RING(sarea_priv->front_offset); - OUT_RING((y1 << 16) | rect->x1); - OUT_RING(src_pitch); - OUT_RING(sarea_priv->back_offset); - - ADVANCE_LP_RING(); - } - } - } - - spin_unlock_irqrestore(&dev->drw_lock, irqflags); - mutex_unlock(&dev->struct_mutex); - - list_for_each_safe(hit, tmp, &hits) { - drm_i915_vbl_swap_t *swap_hit = - list_entry(hit, drm_i915_vbl_swap_t, head); - - list_del(hit); - - drm_free(swap_hit, sizeof(*swap_hit), DRM_MEM_DRIVER); - } -} - /* Called from drm generic code, passed a 'crtc', which * we use as a pipe index */ @@ -322,121 +164,106 @@ u32 i915_get_vblank_counter(struct drm_device *dev, int pipe) return count; } -void -i915_vblank_work_handler(struct work_struct *work) -{ - drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t, - vblank_work); - struct drm_device *dev = dev_priv->dev; - unsigned long irqflags; - - if (dev->lock.hw_lock == NULL) { - i915_vblank_tasklet(dev); - return; - } - - spin_lock_irqsave(&dev->tasklet_lock, irqflags); - dev->locked_tasklet_func = i915_vblank_tasklet; - spin_unlock_irqrestore(&dev->tasklet_lock, irqflags); - - /* Try to get the lock now, if this fails, the lock - * holder will execute the tasklet during unlock - */ - if (!drm_lock_take(&dev->lock, DRM_KERNEL_CONTEXT)) - return; - - dev->lock.lock_time = jiffies; - atomic_inc(&dev->counts[_DRM_STAT_LOCKS]); - - spin_lock_irqsave(&dev->tasklet_lock, irqflags); - dev->locked_tasklet_func = NULL; - spin_unlock_irqrestore(&dev->tasklet_lock, irqflags); - - i915_vblank_tasklet(dev); - drm_lock_free(&dev->lock, DRM_KERNEL_CONTEXT); -} - irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) { struct drm_device *dev = (struct drm_device *) arg; drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - u32 iir; + u32 iir, new_iir; u32 pipea_stats, pipeb_stats; + u32 vblank_status; + u32 vblank_enable; int vblank = 0; + unsigned long irqflags; + int irq_received; + int ret = IRQ_NONE; atomic_inc(&dev_priv->irq_received); - if (dev->pdev->msi_enabled) - I915_WRITE(IMR, ~0); iir = I915_READ(IIR); - if (iir == 0) { - if (dev->pdev->msi_enabled) { - I915_WRITE(IMR, dev_priv->irq_mask_reg); - (void) I915_READ(IMR); - } - return IRQ_NONE; + if (IS_I965G(dev)) { + vblank_status = I915_START_VBLANK_INTERRUPT_STATUS; + vblank_enable = PIPE_START_VBLANK_INTERRUPT_ENABLE; + } else { + vblank_status = I915_VBLANK_INTERRUPT_STATUS; + vblank_enable = I915_VBLANK_INTERRUPT_ENABLE; } - /* - * Clear the PIPE(A|B)STAT regs before the IIR otherwise - * we may get extra interrupts. - */ - if (iir & I915_DISPLAY_PIPE_A_EVENT_INTERRUPT) { + for (;;) { + irq_received = iir != 0; + + /* Can't rely on pipestat interrupt bit in iir as it might + * have been cleared after the pipestat interrupt was received. + * It doesn't set the bit in iir again, but it still produces + * interrupts (for non-MSI). + */ + spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags); pipea_stats = I915_READ(PIPEASTAT); - if (!(dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_A)) - pipea_stats &= ~(PIPE_START_VBLANK_INTERRUPT_ENABLE | - PIPE_VBLANK_INTERRUPT_ENABLE); - else if (pipea_stats & (PIPE_START_VBLANK_INTERRUPT_STATUS| - PIPE_VBLANK_INTERRUPT_STATUS)) { + pipeb_stats = I915_READ(PIPEBSTAT); + /* + * Clear the PIPE(A|B)STAT regs before the IIR + */ + if (pipea_stats & 0x8000ffff) { + I915_WRITE(PIPEASTAT, pipea_stats); + irq_received = 1; + } + + if (pipeb_stats & 0x8000ffff) { + I915_WRITE(PIPEBSTAT, pipeb_stats); + irq_received = 1; + } + spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags); + + if (!irq_received) + break; + + ret = IRQ_HANDLED; + + I915_WRITE(IIR, iir); + new_iir = I915_READ(IIR); /* Flush posted writes */ + + if (dev_priv->sarea_priv) + dev_priv->sarea_priv->last_dispatch = + READ_BREADCRUMB(dev_priv); + + if (iir & I915_USER_INTERRUPT) { + dev_priv->mm.irq_gem_seqno = i915_get_gem_seqno(dev); + DRM_WAKEUP(&dev_priv->irq_queue); + } + + if (pipea_stats & vblank_status) { vblank++; drm_handle_vblank(dev, 0); } - I915_WRITE(PIPEASTAT, pipea_stats); - } - if (iir & I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) { - pipeb_stats = I915_READ(PIPEBSTAT); - /* Ack the event */ - I915_WRITE(PIPEBSTAT, pipeb_stats); - - /* The vblank interrupt gets enabled even if we didn't ask for - it, so make sure it's shut down again */ - if (!(dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_B)) - pipeb_stats &= ~(PIPE_START_VBLANK_INTERRUPT_ENABLE | - PIPE_VBLANK_INTERRUPT_ENABLE); - else if (pipeb_stats & (PIPE_START_VBLANK_INTERRUPT_STATUS| - PIPE_VBLANK_INTERRUPT_STATUS)) { + if (pipeb_stats & vblank_status) { vblank++; drm_handle_vblank(dev, 1); } - if (pipeb_stats & I915_LEGACY_BLC_EVENT_STATUS) + if ((pipeb_stats & I915_LEGACY_BLC_EVENT_STATUS) || + (iir & I915_ASLE_INTERRUPT)) opregion_asle_intr(dev); - I915_WRITE(PIPEBSTAT, pipeb_stats); - } - - I915_WRITE(IIR, iir); - if (dev->pdev->msi_enabled) - I915_WRITE(IMR, dev_priv->irq_mask_reg); - (void) I915_READ(IIR); /* Flush posted writes */ - - if (dev_priv->sarea_priv) - dev_priv->sarea_priv->last_dispatch = - READ_BREADCRUMB(dev_priv); - if (iir & I915_USER_INTERRUPT) { - dev_priv->mm.irq_gem_seqno = i915_get_gem_seqno(dev); - DRM_WAKEUP(&dev_priv->irq_queue); + /* With MSI, interrupts are only generated when iir + * transitions from zero to nonzero. If another bit got + * set while we were handling the existing iir bits, then + * we would never get another interrupt. + * + * This is fine on non-MSI as well, as if we hit this path + * we avoid exiting the interrupt handler only to generate + * another one. + * + * Note that for MSI this could cause a stray interrupt report + * if an interrupt landed in the time between writing IIR and + * the posting read. This should be rare enough to never + * trigger the 99% of 100,000 interrupts test for disabling + * stray interrupts. + */ + iir = new_iir; } - if (iir & I915_ASLE_INTERRUPT) - opregion_asle_intr(dev); - - if (vblank && dev_priv->swaps_pending > 0) - schedule_work(&dev_priv->vblank_work); - - return IRQ_HANDLED; + return ret; } static int i915_emit_irq(struct drm_device * dev) @@ -454,12 +281,10 @@ static int i915_emit_irq(struct drm_device * dev) if (dev_priv->sarea_priv) dev_priv->sarea_priv->last_enqueue = dev_priv->counter; - BEGIN_LP_RING(6); + BEGIN_LP_RING(4); OUT_RING(MI_STORE_DWORD_INDEX); - OUT_RING(5 << MI_STORE_DWORD_INDEX_SHIFT); + OUT_RING(I915_BREADCRUMB_INDEX << MI_STORE_DWORD_INDEX_SHIFT); OUT_RING(dev_priv->counter); - OUT_RING(0); - OUT_RING(0); OUT_RING(MI_USER_INTERRUPT); ADVANCE_LP_RING(); @@ -574,48 +399,16 @@ int i915_irq_wait(struct drm_device *dev, void *data, int i915_enable_vblank(struct drm_device *dev, int pipe) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - u32 pipestat_reg = 0; - u32 pipestat; - u32 interrupt = 0; unsigned long irqflags; - switch (pipe) { - case 0: - pipestat_reg = PIPEASTAT; - interrupt = I915_DISPLAY_PIPE_A_EVENT_INTERRUPT; - break; - case 1: - pipestat_reg = PIPEBSTAT; - interrupt = I915_DISPLAY_PIPE_B_EVENT_INTERRUPT; - break; - default: - DRM_ERROR("tried to enable vblank on non-existent pipe %d\n", - pipe); - return 0; - } - spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags); - /* Enabling vblank events in IMR comes before PIPESTAT write, or - * there's a race where the PIPESTAT vblank bit gets set to 1, so - * the OR of enabled PIPESTAT bits goes to 1, so the PIPExEVENT in - * ISR flashes to 1, but the IIR bit doesn't get set to 1 because - * IMR masks it. It doesn't ever get set after we clear the masking - * in IMR because the ISR bit is edge, not level-triggered, on the - * OR of PIPESTAT bits. - */ - i915_enable_irq(dev_priv, interrupt); - pipestat = I915_READ(pipestat_reg); if (IS_I965G(dev)) - pipestat |= PIPE_START_VBLANK_INTERRUPT_ENABLE; + i915_enable_pipestat(dev_priv, pipe, + PIPE_START_VBLANK_INTERRUPT_ENABLE); else - pipestat |= PIPE_VBLANK_INTERRUPT_ENABLE; - /* Clear any stale interrupt status */ - pipestat |= (PIPE_START_VBLANK_INTERRUPT_STATUS | - PIPE_VBLANK_INTERRUPT_STATUS); - I915_WRITE(pipestat_reg, pipestat); - (void) I915_READ(pipestat_reg); /* Posting read */ + i915_enable_pipestat(dev_priv, pipe, + PIPE_VBLANK_INTERRUPT_ENABLE); spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags); - return 0; } @@ -625,37 +418,12 @@ int i915_enable_vblank(struct drm_device *dev, int pipe) void i915_disable_vblank(struct drm_device *dev, int pipe) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - u32 pipestat_reg = 0; - u32 pipestat; - u32 interrupt = 0; unsigned long irqflags; - switch (pipe) { - case 0: - pipestat_reg = PIPEASTAT; - interrupt = I915_DISPLAY_PIPE_A_EVENT_INTERRUPT; - break; - case 1: - pipestat_reg = PIPEBSTAT; - interrupt = I915_DISPLAY_PIPE_B_EVENT_INTERRUPT; - break; - default: - DRM_ERROR("tried to disable vblank on non-existent pipe %d\n", - pipe); - return; - break; - } - spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags); - i915_disable_irq(dev_priv, interrupt); - pipestat = I915_READ(pipestat_reg); - pipestat &= ~(PIPE_START_VBLANK_INTERRUPT_ENABLE | - PIPE_VBLANK_INTERRUPT_ENABLE); - /* Clear any stale interrupt status */ - pipestat |= (PIPE_START_VBLANK_INTERRUPT_STATUS | - PIPE_VBLANK_INTERRUPT_STATUS); - I915_WRITE(pipestat_reg, pipestat); - (void) I915_READ(pipestat_reg); /* Posting read */ + i915_disable_pipestat(dev_priv, pipe, + PIPE_VBLANK_INTERRUPT_ENABLE | + PIPE_START_VBLANK_INTERRUPT_ENABLE); spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags); } @@ -696,123 +464,21 @@ int i915_vblank_pipe_get(struct drm_device *dev, void *data, int i915_vblank_swap(struct drm_device *dev, void *data, struct drm_file *file_priv) { - drm_i915_private_t *dev_priv = dev->dev_private; - drm_i915_vblank_swap_t *swap = data; - drm_i915_vbl_swap_t *vbl_swap, *vbl_old; - unsigned int pipe, seqtype, curseq; - unsigned long irqflags; - struct list_head *list; - int ret; - - if (!dev_priv || !dev_priv->sarea_priv) { - DRM_ERROR("%s called with no initialization\n", __func__); - return -EINVAL; - } - - if (dev_priv->sarea_priv->rotation) { - DRM_DEBUG("Rotation not supported\n"); - return -EINVAL; - } - - if (swap->seqtype & ~(_DRM_VBLANK_RELATIVE | _DRM_VBLANK_ABSOLUTE | - _DRM_VBLANK_SECONDARY | _DRM_VBLANK_NEXTONMISS)) { - DRM_ERROR("Invalid sequence type 0x%x\n", swap->seqtype); - return -EINVAL; - } - - pipe = (swap->seqtype & _DRM_VBLANK_SECONDARY) ? 1 : 0; - - seqtype = swap->seqtype & (_DRM_VBLANK_RELATIVE | _DRM_VBLANK_ABSOLUTE); - - if (!(dev_priv->vblank_pipe & (1 << pipe))) { - DRM_ERROR("Invalid pipe %d\n", pipe); - return -EINVAL; - } - - spin_lock_irqsave(&dev->drw_lock, irqflags); - - if (!drm_get_drawable_info(dev, swap->drawable)) { - spin_unlock_irqrestore(&dev->drw_lock, irqflags); - DRM_DEBUG("Invalid drawable ID %d\n", swap->drawable); - return -EINVAL; - } - - spin_unlock_irqrestore(&dev->drw_lock, irqflags); - - /* - * We take the ref here and put it when the swap actually completes - * in the tasklet. + /* The delayed swap mechanism was fundamentally racy, and has been + * removed. The model was that the client requested a delayed flip/swap + * from the kernel, then waited for vblank before continuing to perform + * rendering. The problem was that the kernel might wake the client + * up before it dispatched the vblank swap (since the lock has to be + * held while touching the ringbuffer), in which case the client would + * clear and start the next frame before the swap occurred, and + * flicker would occur in addition to likely missing the vblank. + * + * In the absence of this ioctl, userland falls back to a correct path + * of waiting for a vblank, then dispatching the swap on its own. + * Context switching to userland and back is plenty fast enough for + * meeting the requirements of vblank swapping. */ - ret = drm_vblank_get(dev, pipe); - if (ret) - return ret; - curseq = drm_vblank_count(dev, pipe); - - if (seqtype == _DRM_VBLANK_RELATIVE) - swap->sequence += curseq; - - if ((curseq - swap->sequence) <= (1<<23)) { - if (swap->seqtype & _DRM_VBLANK_NEXTONMISS) { - swap->sequence = curseq + 1; - } else { - DRM_DEBUG("Missed target sequence\n"); - drm_vblank_put(dev, pipe); - return -EINVAL; - } - } - - vbl_swap = drm_calloc(1, sizeof(*vbl_swap), DRM_MEM_DRIVER); - - if (!vbl_swap) { - DRM_ERROR("Failed to allocate memory to queue swap\n"); - drm_vblank_put(dev, pipe); - return -ENOMEM; - } - - vbl_swap->drw_id = swap->drawable; - vbl_swap->pipe = pipe; - vbl_swap->sequence = swap->sequence; - - spin_lock_irqsave(&dev_priv->swaps_lock, irqflags); - - list_for_each(list, &dev_priv->vbl_swaps.head) { - vbl_old = list_entry(list, drm_i915_vbl_swap_t, head); - - if (vbl_old->drw_id == swap->drawable && - vbl_old->pipe == pipe && - vbl_old->sequence == swap->sequence) { - spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags); - drm_vblank_put(dev, pipe); - drm_free(vbl_swap, sizeof(*vbl_swap), DRM_MEM_DRIVER); - DRM_DEBUG("Already scheduled\n"); - return 0; - } - } - - if (dev_priv->swaps_pending >= 10) { - DRM_DEBUG("Too many swaps queued\n"); - DRM_DEBUG(" pipe 0: %d pipe 1: %d\n", - drm_vblank_count(dev, 0), - drm_vblank_count(dev, 1)); - - list_for_each(list, &dev_priv->vbl_swaps.head) { - vbl_old = list_entry(list, drm_i915_vbl_swap_t, head); - DRM_DEBUG("\tdrw %x pipe %d seq %x\n", - vbl_old->drw_id, vbl_old->pipe, - vbl_old->sequence); - } - spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags); - drm_vblank_put(dev, pipe); - drm_free(vbl_swap, sizeof(*vbl_swap), DRM_MEM_DRIVER); - return -EBUSY; - } - - list_add_tail(&vbl_swap->head, &dev_priv->vbl_swaps.head); - dev_priv->swaps_pending++; - - spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags); - - return 0; + return -EINVAL; } /* drm_dma.h hooks @@ -822,37 +488,35 @@ void i915_driver_irq_preinstall(struct drm_device * dev) drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; I915_WRITE(HWSTAM, 0xeffe); + I915_WRITE(PIPEASTAT, 0); + I915_WRITE(PIPEBSTAT, 0); I915_WRITE(IMR, 0xffffffff); I915_WRITE(IER, 0x0); + (void) I915_READ(IER); } int i915_driver_irq_postinstall(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - int ret, num_pipes = 2; - - spin_lock_init(&dev_priv->swaps_lock); - INIT_LIST_HEAD(&dev_priv->vbl_swaps.head); - INIT_WORK(&dev_priv->vblank_work, i915_vblank_work_handler); - dev_priv->swaps_pending = 0; - - /* Set initial unmasked IRQs to just the selected vblank pipes. */ - dev_priv->irq_mask_reg = ~0; - - ret = drm_vblank_init(dev, num_pipes); - if (ret) - return ret; dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; - dev_priv->irq_mask_reg &= ~I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT; - dev_priv->irq_mask_reg &= ~I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT; dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */ - dev_priv->irq_mask_reg &= I915_INTERRUPT_ENABLE_MASK; + /* Unmask the interrupts that we always want on. */ + dev_priv->irq_mask_reg = ~I915_INTERRUPT_ENABLE_FIX; + + dev_priv->pipestat[0] = 0; + dev_priv->pipestat[1] = 0; + + /* Disable pipe interrupt enables, clear pending pipe status */ + I915_WRITE(PIPEASTAT, I915_READ(PIPEASTAT) & 0x8000ffff); + I915_WRITE(PIPEBSTAT, I915_READ(PIPEBSTAT) & 0x8000ffff); + /* Clear pending interrupt status */ + I915_WRITE(IIR, I915_READ(IIR)); - I915_WRITE(IMR, dev_priv->irq_mask_reg); I915_WRITE(IER, I915_INTERRUPT_ENABLE_MASK); + I915_WRITE(IMR, dev_priv->irq_mask_reg); (void) I915_READ(IER); opregion_enable_asle(dev); @@ -864,7 +528,6 @@ int i915_driver_irq_postinstall(struct drm_device *dev) void i915_driver_irq_uninstall(struct drm_device * dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - u32 temp; if (!dev_priv) return; @@ -872,13 +535,12 @@ void i915_driver_irq_uninstall(struct drm_device * dev) dev_priv->vblank_pipe = 0; I915_WRITE(HWSTAM, 0xffffffff); + I915_WRITE(PIPEASTAT, 0); + I915_WRITE(PIPEBSTAT, 0); I915_WRITE(IMR, 0xffffffff); I915_WRITE(IER, 0x0); - temp = I915_READ(PIPEASTAT); - I915_WRITE(PIPEASTAT, temp); - temp = I915_READ(PIPEBSTAT); - I915_WRITE(PIPEBSTAT, temp); - temp = I915_READ(IIR); - I915_WRITE(IIR, temp); + I915_WRITE(PIPEASTAT, I915_READ(PIPEASTAT) & 0x8000ffff); + I915_WRITE(PIPEBSTAT, I915_READ(PIPEBSTAT) & 0x8000ffff); + I915_WRITE(IIR, I915_READ(IIR)); } diff --git a/drivers/gpu/drm/i915/i915_opregion.c b/drivers/gpu/drm/i915/i915_opregion.c index 1787a0c7e3ab..13ae731a33db 100644 --- a/drivers/gpu/drm/i915/i915_opregion.c +++ b/drivers/gpu/drm/i915/i915_opregion.c @@ -235,17 +235,15 @@ void opregion_enable_asle(struct drm_device *dev) struct opregion_asle *asle = dev_priv->opregion.asle; if (asle) { - u32 pipeb_stats = I915_READ(PIPEBSTAT); if (IS_MOBILE(dev)) { - /* Many devices trigger events with a write to the - legacy backlight controller, so we need to ensure - that it's able to generate interrupts */ - I915_WRITE(PIPEBSTAT, pipeb_stats |= - I915_LEGACY_BLC_EVENT_ENABLE); - i915_enable_irq(dev_priv, I915_ASLE_INTERRUPT | - I915_DISPLAY_PIPE_B_EVENT_INTERRUPT); - } else - i915_enable_irq(dev_priv, I915_ASLE_INTERRUPT); + unsigned long irqflags; + + spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags); + i915_enable_pipestat(dev_priv, 1, + I915_LEGACY_BLC_EVENT_ENABLE); + spin_unlock_irqrestore(&dev_priv->user_irq_lock, + irqflags); + } asle->tche = ASLE_ALS_EN | ASLE_BLC_EN | ASLE_PFIT_EN | ASLE_PFMB_EN; diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 5c2d9f206d05..0e476eba36e6 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -527,6 +527,9 @@ #define C0DRB3 0x10206 #define C1DRB3 0x10606 +/** GM965 GM45 render standby register */ +#define MCHBAR_RENDER_STANDBY 0x111B8 + /* * Overlay regs */ diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c index 603fe742ccd4..5d84027ee8f3 100644 --- a/drivers/gpu/drm/i915/i915_suspend.c +++ b/drivers/gpu/drm/i915/i915_suspend.c @@ -240,6 +240,13 @@ int i915_save_state(struct drm_device *dev) pci_read_config_byte(dev->pdev, LBB, &dev_priv->saveLBB); + /* Render Standby */ + if (IS_I965G(dev) && IS_MOBILE(dev)) + dev_priv->saveRENDERSTANDBY = I915_READ(MCHBAR_RENDER_STANDBY); + + /* Hardware status page */ + dev_priv->saveHWS = I915_READ(HWS_PGA); + /* Display arbitration control */ dev_priv->saveDSPARB = I915_READ(DSPARB); @@ -365,6 +372,14 @@ int i915_restore_state(struct drm_device *dev) pci_write_config_byte(dev->pdev, LBB, dev_priv->saveLBB); + /* Render Standby */ + if (IS_I965G(dev) && IS_MOBILE(dev)) + I915_WRITE(MCHBAR_RENDER_STANDBY, dev_priv->saveRENDERSTANDBY); + + /* Hardware status page */ + I915_WRITE(HWS_PGA, dev_priv->saveHWS); + + /* Display arbitration */ I915_WRITE(DSPARB, dev_priv->saveDSPARB); /* Pipe & plane A info */ diff --git a/drivers/gpu/drm/mga/mga_dma.c b/drivers/gpu/drm/mga/mga_dma.c index c1d12dbfa8d8..b49c5ff29585 100644 --- a/drivers/gpu/drm/mga/mga_dma.c +++ b/drivers/gpu/drm/mga/mga_dma.c @@ -396,6 +396,7 @@ int mga_freelist_put(struct drm_device * dev, struct drm_buf * buf) int mga_driver_load(struct drm_device * dev, unsigned long flags) { drm_mga_private_t *dev_priv; + int ret; dev_priv = drm_alloc(sizeof(drm_mga_private_t), DRM_MEM_DRIVER); if (!dev_priv) @@ -415,6 +416,13 @@ int mga_driver_load(struct drm_device * dev, unsigned long flags) dev->types[7] = _DRM_STAT_PRIMARY; dev->types[8] = _DRM_STAT_SECONDARY; + ret = drm_vblank_init(dev, 1); + + if (ret) { + (void) mga_driver_unload(dev); + return ret; + } + return 0; } diff --git a/drivers/gpu/drm/mga/mga_irq.c b/drivers/gpu/drm/mga/mga_irq.c index bab42f41188b..daa6041a483a 100644 --- a/drivers/gpu/drm/mga/mga_irq.c +++ b/drivers/gpu/drm/mga/mga_irq.c @@ -152,11 +152,6 @@ void mga_driver_irq_preinstall(struct drm_device * dev) int mga_driver_irq_postinstall(struct drm_device *dev) { drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private; - int ret; - - ret = drm_vblank_init(dev, 1); - if (ret) - return ret; DRM_INIT_WAITQUEUE(&dev_priv->fence_queue); diff --git a/drivers/gpu/drm/r128/r128_drv.c b/drivers/gpu/drm/r128/r128_drv.c index 3265d53ba91f..601f4c0e5da5 100644 --- a/drivers/gpu/drm/r128/r128_drv.c +++ b/drivers/gpu/drm/r128/r128_drv.c @@ -45,6 +45,7 @@ static struct drm_driver driver = { DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_PCI_DMA | DRIVER_SG | DRIVER_HAVE_DMA | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED, .dev_priv_size = sizeof(drm_r128_buf_priv_t), + .load = r128_driver_load, .preclose = r128_driver_preclose, .lastclose = r128_driver_lastclose, .get_vblank_counter = r128_get_vblank_counter, @@ -84,6 +85,11 @@ static struct drm_driver driver = { .patchlevel = DRIVER_PATCHLEVEL, }; +int r128_driver_load(struct drm_device * dev, unsigned long flags) +{ + return drm_vblank_init(dev, 1); +} + static int __init r128_init(void) { driver.num_ioctls = r128_max_ioctl; diff --git a/drivers/gpu/drm/r128/r128_drv.h b/drivers/gpu/drm/r128/r128_drv.h index 5898b274279d..797a26c42dab 100644 --- a/drivers/gpu/drm/r128/r128_drv.h +++ b/drivers/gpu/drm/r128/r128_drv.h @@ -159,6 +159,7 @@ extern void r128_driver_irq_preinstall(struct drm_device * dev); extern int r128_driver_irq_postinstall(struct drm_device *dev); extern void r128_driver_irq_uninstall(struct drm_device * dev); extern void r128_driver_lastclose(struct drm_device * dev); +extern int r128_driver_load(struct drm_device * dev, unsigned long flags); extern void r128_driver_preclose(struct drm_device * dev, struct drm_file *file_priv); diff --git a/drivers/gpu/drm/r128/r128_irq.c b/drivers/gpu/drm/r128/r128_irq.c index d7349012a680..69810fb8ac49 100644 --- a/drivers/gpu/drm/r128/r128_irq.c +++ b/drivers/gpu/drm/r128/r128_irq.c @@ -102,7 +102,7 @@ void r128_driver_irq_preinstall(struct drm_device * dev) int r128_driver_irq_postinstall(struct drm_device *dev) { - return drm_vblank_init(dev, 1); + return 0; } void r128_driver_irq_uninstall(struct drm_device * dev) diff --git a/drivers/gpu/drm/radeon/radeon_cp.c b/drivers/gpu/drm/radeon/radeon_cp.c index 59a2132a8f57..dcebb4bee7aa 100644 --- a/drivers/gpu/drm/radeon/radeon_cp.c +++ b/drivers/gpu/drm/radeon/radeon_cp.c @@ -653,15 +653,16 @@ static void radeon_cp_init_ring_buffer(struct drm_device * dev, RADEON_WRITE(RADEON_SCRATCH_UMSK, 0x7); /* Turn on bus mastering */ - if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS400) || - ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS690) || + if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS690) || ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS740)) { - /* rs400, rs690/rs740 */ - tmp = RADEON_READ(RADEON_BUS_CNTL) & ~RS400_BUS_MASTER_DIS; + /* rs600/rs690/rs740 */ + tmp = RADEON_READ(RADEON_BUS_CNTL) & ~RS600_BUS_MASTER_DIS; RADEON_WRITE(RADEON_BUS_CNTL, tmp); - } else if (!(((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV380) || - ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R423))) { - /* r1xx, r2xx, r300, r(v)350, r420/r481, rs480 */ + } else if (((dev_priv->flags & RADEON_FAMILY_MASK) <= CHIP_RV350) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_R420) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS400) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS480)) { + /* r1xx, r2xx, r300, r(v)350, r420/r481, rs400/rs480 */ tmp = RADEON_READ(RADEON_BUS_CNTL) & ~RADEON_BUS_MASTER_DIS; RADEON_WRITE(RADEON_BUS_CNTL, tmp); } /* PCIE cards appears to not need this */ @@ -1750,6 +1751,18 @@ int radeon_driver_load(struct drm_device *dev, unsigned long flags) else dev_priv->flags |= RADEON_IS_PCI; + ret = drm_addmap(dev, drm_get_resource_start(dev, 2), + drm_get_resource_len(dev, 2), _DRM_REGISTERS, + _DRM_READ_ONLY | _DRM_DRIVER, &dev_priv->mmio); + if (ret != 0) + return ret; + + ret = drm_vblank_init(dev, 2); + if (ret) { + radeon_driver_unload(dev); + return ret; + } + DRM_DEBUG("%s card detected\n", ((dev_priv->flags & RADEON_IS_AGP) ? "AGP" : (((dev_priv->flags & RADEON_IS_PCIE) ? "PCIE" : "PCI")))); return ret; @@ -1766,12 +1779,6 @@ int radeon_driver_firstopen(struct drm_device *dev) dev_priv->gart_info.table_size = RADEON_PCIGART_TABLE_SIZE; - ret = drm_addmap(dev, drm_get_resource_start(dev, 2), - drm_get_resource_len(dev, 2), _DRM_REGISTERS, - _DRM_READ_ONLY, &dev_priv->mmio); - if (ret != 0) - return ret; - dev_priv->fb_aper_offset = drm_get_resource_start(dev, 0); ret = drm_addmap(dev, dev_priv->fb_aper_offset, drm_get_resource_len(dev, 0), _DRM_FRAME_BUFFER, @@ -1787,6 +1794,9 @@ int radeon_driver_unload(struct drm_device *dev) drm_radeon_private_t *dev_priv = dev->dev_private; DRM_DEBUG("\n"); + + drm_rmmap(dev, dev_priv->mmio); + drm_free(dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER); dev->dev_private = NULL; diff --git a/drivers/gpu/drm/radeon/radeon_drv.h b/drivers/gpu/drm/radeon/radeon_drv.h index 4dbb813910c3..7a183789be97 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.h +++ b/drivers/gpu/drm/radeon/radeon_drv.h @@ -287,7 +287,6 @@ typedef struct drm_radeon_private { unsigned long gart_textures_offset; drm_local_map_t *sarea; - drm_local_map_t *mmio; drm_local_map_t *cp_ring; drm_local_map_t *ring_rptr; drm_local_map_t *gart_textures; @@ -318,6 +317,7 @@ typedef struct drm_radeon_private { int num_gb_pipes; int track_flush; + drm_local_map_t *mmio; } drm_radeon_private_t; typedef struct drm_radeon_buf_priv { @@ -447,12 +447,12 @@ extern int r300_do_cp_cmdbuf(struct drm_device *dev, * handling, not bus mastering itself. */ #define RADEON_BUS_CNTL 0x0030 -/* r1xx, r2xx, r300, r(v)350, r420/r481, rs480 */ +/* r1xx, r2xx, r300, r(v)350, r420/r481, rs400/rs480 */ # define RADEON_BUS_MASTER_DIS (1 << 6) -/* rs400, rs690/rs740 */ -# define RS400_BUS_MASTER_DIS (1 << 14) -# define RS400_MSI_REARM (1 << 20) -/* see RS480_MSI_REARM in AIC_CNTL for rs480 */ +/* rs600/rs690/rs740 */ +# define RS600_BUS_MASTER_DIS (1 << 14) +# define RS600_MSI_REARM (1 << 20) +/* see RS400_MSI_REARM in AIC_CNTL for rs480 */ #define RADEON_BUS_CNTL1 0x0034 # define RADEON_PMI_BM_DIS (1 << 2) @@ -937,7 +937,7 @@ extern int r300_do_cp_cmdbuf(struct drm_device *dev, #define RADEON_AIC_CNTL 0x01d0 # define RADEON_PCIGART_TRANSLATE_EN (1 << 0) -# define RS480_MSI_REARM (1 << 3) +# define RS400_MSI_REARM (1 << 3) #define RADEON_AIC_STAT 0x01d4 #define RADEON_AIC_PT_BASE 0x01d8 #define RADEON_AIC_LO_ADDR 0x01dc diff --git a/drivers/gpu/drm/radeon/radeon_irq.c b/drivers/gpu/drm/radeon/radeon_irq.c index 5079f7054a2f..97c0599fdb1e 100644 --- a/drivers/gpu/drm/radeon/radeon_irq.c +++ b/drivers/gpu/drm/radeon/radeon_irq.c @@ -337,15 +337,10 @@ int radeon_driver_irq_postinstall(struct drm_device *dev) { drm_radeon_private_t *dev_priv = (drm_radeon_private_t *) dev->dev_private; - int ret; atomic_set(&dev_priv->swi_emitted, 0); DRM_INIT_WAITQUEUE(&dev_priv->swi_queue); - ret = drm_vblank_init(dev, 2); - if (ret) - return ret; - dev->max_vblank_count = 0x001fffff; radeon_irq_set_state(dev, RADEON_SW_INT_ENABLE, 1); diff --git a/drivers/gpu/drm/via/via_irq.c b/drivers/gpu/drm/via/via_irq.c index 665d319b927b..c248c1d37268 100644 --- a/drivers/gpu/drm/via/via_irq.c +++ b/drivers/gpu/drm/via/via_irq.c @@ -314,7 +314,6 @@ int via_driver_irq_postinstall(struct drm_device *dev) if (!dev_priv) return -EINVAL; - drm_vblank_init(dev, 1); status = VIA_READ(VIA_REG_INTERRUPT); VIA_WRITE(VIA_REG_INTERRUPT, status | VIA_IRQ_GLOBAL | dev_priv->irq_enable_mask); diff --git a/drivers/gpu/drm/via/via_map.c b/drivers/gpu/drm/via/via_map.c index a967556be014..2c4f0b485792 100644 --- a/drivers/gpu/drm/via/via_map.c +++ b/drivers/gpu/drm/via/via_map.c @@ -107,8 +107,17 @@ int via_driver_load(struct drm_device *dev, unsigned long chipset) ret = drm_sman_init(&dev_priv->sman, 2, 12, 8); if (ret) { drm_free(dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER); + return ret; } - return ret; + + ret = drm_vblank_init(dev, 1); + if (ret) { + drm_sman_takedown(&dev_priv->sman); + drm_free(dev_priv, sizeof(drm_via_private_t), DRM_MEM_DRIVER); + return ret; + } + + return 0; } int via_driver_unload(struct drm_device *dev) diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index b4fd8ca701a4..aadef9abc05d 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -107,13 +107,6 @@ config HID_BELKIN ---help--- Support for Belkin Flip KVM and Wireless keyboard. -config HID_BRIGHT - tristate "Bright" if EMBEDDED - depends on USB_HID - default y - ---help--- - Support for Bright ABNT-2 keyboard. - config HID_CHERRY tristate "Cherry" if EMBEDDED depends on USB_HID @@ -135,14 +128,6 @@ config HID_CYPRESS ---help--- Support for cypress mouse and barcode readers. -config HID_DELL - tristate "Dell" if EMBEDDED - depends on USB_HID - default y - ---help--- - Support for quirky Dell HID hardware that require - special LED handling (W7658 and SK8115 models) - config HID_EZKEY tristate "Ezkey" if EMBEDDED depends on USB_HID @@ -202,6 +187,13 @@ config HID_MONTEREY ---help--- Support for Monterey Genius KB29E. +config HID_NTRIG + tristate "NTrig" if EMBEDDED + depends on USB_HID + default y + ---help--- + Support for N-Trig touch screen. + config HID_PANTHERLORD tristate "Pantherlord devices support" if EMBEDDED depends on USB_HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index b09e43e7413e..7d34e8bf3de5 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -23,16 +23,15 @@ endif obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o obj-$(CONFIG_HID_APPLE) += hid-apple.o obj-$(CONFIG_HID_BELKIN) += hid-belkin.o -obj-$(CONFIG_HID_BRIGHT) += hid-bright.o obj-$(CONFIG_HID_CHERRY) += hid-cherry.o obj-$(CONFIG_HID_CHICONY) += hid-chicony.o obj-$(CONFIG_HID_CYPRESS) += hid-cypress.o -obj-$(CONFIG_HID_DELL) += hid-dell.o obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o obj-$(CONFIG_HID_GYRATION) += hid-gyration.o obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o obj-$(CONFIG_HID_MONTEREY) += hid-monterey.o +obj-$(CONFIG_HID_NTRIG) += hid-ntrig.o obj-$(CONFIG_HID_PANTHERLORD) += hid-pl.o obj-$(CONFIG_HID_PETALYNX) += hid-petalynx.o obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index c6ab4ba60c52..aa28aed0e46c 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -55,10 +55,11 @@ struct apple_key_translation { static struct apple_key_translation apple_fn_keys[] = { { KEY_BACKSPACE, KEY_DELETE }, + { KEY_ENTER, KEY_INSERT }, { KEY_F1, KEY_BRIGHTNESSDOWN, APPLE_FLAG_FKEY }, { KEY_F2, KEY_BRIGHTNESSUP, APPLE_FLAG_FKEY }, - { KEY_F3, KEY_FN_F5, APPLE_FLAG_FKEY }, /* Exposé */ - { KEY_F4, KEY_FN_F4, APPLE_FLAG_FKEY }, /* Dashboard */ + { KEY_F3, KEY_SCALE, APPLE_FLAG_FKEY }, + { KEY_F4, KEY_DASHBOARD, APPLE_FLAG_FKEY }, { KEY_F5, KEY_KBDILLUMDOWN, APPLE_FLAG_FKEY }, { KEY_F6, KEY_KBDILLUMUP, APPLE_FLAG_FKEY }, { KEY_F7, KEY_PREVIOUSSONG, APPLE_FLAG_FKEY }, @@ -399,12 +400,12 @@ static const struct hid_device_id apple_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_JIS), .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | APPLE_RDESC_JIS }, - { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI), + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI), .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, - { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO), + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO), .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | APPLE_ISO_KEYBOARD }, - { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS), + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS), .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ANSI), .driver_data = APPLE_HAS_FN }, @@ -418,6 +419,12 @@ static const struct hid_device_id apple_devices[] = { .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_JIS), .driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI), + .driver_data = APPLE_HAS_FN }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING3_ISO), + .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING3_JIS), + .driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY), .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY), diff --git a/drivers/hid/hid-bright.c b/drivers/hid/hid-bright.c deleted file mode 100644 index 38517a117dfd..000000000000 --- a/drivers/hid/hid-bright.c +++ /dev/null @@ -1,71 +0,0 @@ -/* - * HID driver for some bright "special" devices - * - * Copyright (c) 2008 Mauro Carvalho Chehab <mchehab@redhat.com> - * - * Based on hid-dell driver - */ - -/* - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - */ - -#include <linux/device.h> -#include <linux/hid.h> -#include <linux/module.h> - -#include "hid-ids.h" - -static int bright_probe(struct hid_device *hdev, const struct hid_device_id *id) -{ - int ret; - - ret = hid_parse(hdev); - if (ret) { - dev_err(&hdev->dev, "parse failed\n"); - goto err_free; - } - - ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); - if (ret) { - dev_err(&hdev->dev, "hw start failed\n"); - goto err_free; - } - - usbhid_set_leds(hdev); - - return 0; -err_free: - return ret; -} - -static const struct hid_device_id bright_devices[] = { - { HID_USB_DEVICE(USB_VENDOR_ID_BRIGHT, USB_DEVICE_ID_BRIGHT_ABNT2) }, - { } -}; -MODULE_DEVICE_TABLE(hid, bright_devices); - -static struct hid_driver bright_driver = { - .name = "bright", - .id_table = bright_devices, - .probe = bright_probe, -}; - -static int bright_init(void) -{ - return hid_register_driver(&bright_driver); -} - -static void bright_exit(void) -{ - hid_unregister_driver(&bright_driver); -} - -module_init(bright_init); -module_exit(bright_exit); -MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(bright); diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 1903e7515650..34cc3b0d01f6 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1241,29 +1241,30 @@ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ANSI) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ISO) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_JIS) }, - { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI) }, - { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO) }, - { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ANSI) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ISO) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_JIS) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ISO) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_JIS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING3_ISO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING3_JIS) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) }, - { HID_USB_DEVICE(USB_VENDOR_ID_AVERMEDIA, USB_DEVICE_ID_AVER_FM_MR800) }, { HID_USB_DEVICE(USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM) }, - { HID_USB_DEVICE(USB_VENDOR_ID_BRIGHT, USB_DEVICE_ID_BRIGHT_ABNT2) }, { HID_USB_DEVICE(USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION) }, { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_TACTICAL_PAD) }, { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1) }, { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2) }, { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE) }, - { HID_USB_DEVICE(USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_W7658) }, - { HID_USB_DEVICE(USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_SK8115) }, { HID_USB_DEVICE(USB_VENDOR_ID_EZKEY, USB_DEVICE_ID_BTC_8193) }, - { HID_USB_DEVICE(USB_VENDOR_ID_GENERIC_13BA, USB_DEVICE_ID_GENERIC_13BA_KBD_MOUSE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PCS_ADAPTOR) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GREENASIA, 0x0003) }, { HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE) }, { HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_2) }, { HID_USB_DEVICE(USB_VENDOR_ID_LABTEC, USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD) }, @@ -1274,7 +1275,6 @@ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI) }, - { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_KBD) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D) }, @@ -1292,6 +1292,7 @@ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_USB) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) }, { HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) }, + { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) }, { HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) }, { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) }, @@ -1303,12 +1304,92 @@ static const struct hid_device_id hid_blacklist[] = { { } }; +struct hid_dynid { + struct list_head list; + struct hid_device_id id; +}; + +/** + * store_new_id - add a new HID device ID to this driver and re-probe devices + * @driver: target device driver + * @buf: buffer for scanning device ID data + * @count: input size + * + * Adds a new dynamic hid device ID to this driver, + * and causes the driver to probe for all devices again. + */ +static ssize_t store_new_id(struct device_driver *drv, const char *buf, + size_t count) +{ + struct hid_driver *hdrv = container_of(drv, struct hid_driver, driver); + struct hid_dynid *dynid; + __u32 bus, vendor, product; + unsigned long driver_data = 0; + int ret; + + ret = sscanf(buf, "%x %x %x %lx", + &bus, &vendor, &product, &driver_data); + if (ret < 3) + return -EINVAL; + + dynid = kzalloc(sizeof(*dynid), GFP_KERNEL); + if (!dynid) + return -ENOMEM; + + dynid->id.bus = bus; + dynid->id.vendor = vendor; + dynid->id.product = product; + dynid->id.driver_data = driver_data; + + spin_lock(&hdrv->dyn_lock); + list_add_tail(&dynid->list, &hdrv->dyn_list); + spin_unlock(&hdrv->dyn_lock); + + ret = 0; + if (get_driver(&hdrv->driver)) { + ret = driver_attach(&hdrv->driver); + put_driver(&hdrv->driver); + } + + return ret ? : count; +} +static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id); + +static void hid_free_dynids(struct hid_driver *hdrv) +{ + struct hid_dynid *dynid, *n; + + spin_lock(&hdrv->dyn_lock); + list_for_each_entry_safe(dynid, n, &hdrv->dyn_list, list) { + list_del(&dynid->list); + kfree(dynid); + } + spin_unlock(&hdrv->dyn_lock); +} + +static const struct hid_device_id *hid_match_device(struct hid_device *hdev, + struct hid_driver *hdrv) +{ + struct hid_dynid *dynid; + + spin_lock(&hdrv->dyn_lock); + list_for_each_entry(dynid, &hdrv->dyn_list, list) { + if (hid_match_one_id(hdev, &dynid->id)) { + spin_unlock(&hdrv->dyn_lock); + return &dynid->id; + } + } + spin_unlock(&hdrv->dyn_lock); + + return hid_match_id(hdev, hdrv->id_table); +} + static int hid_bus_match(struct device *dev, struct device_driver *drv) { struct hid_driver *hdrv = container_of(drv, struct hid_driver, driver); struct hid_device *hdev = container_of(dev, struct hid_device, dev); - if (!hid_match_id(hdev, hdrv->id_table)) + if (!hid_match_device(hdev, hdrv)) return 0; /* generic wants all non-blacklisted */ @@ -1327,7 +1408,7 @@ static int hid_device_probe(struct device *dev) int ret = 0; if (!hdev->driver) { - id = hid_match_id(hdev, hdrv->id_table); + id = hid_match_device(hdev, hdrv); if (id == NULL) return -ENODEV; @@ -1408,6 +1489,7 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_USBRS232) }, { HID_USB_DEVICE(USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUS_LCM)}, { HID_USB_DEVICE(USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUS_LCM2)}, + { HID_USB_DEVICE(USB_VENDOR_ID_AVERMEDIA, USB_DEVICE_ID_AVER_FM_MR800) }, { HID_USB_DEVICE(USB_VENDOR_ID_BERKSHIRE, USB_DEVICE_ID_BERKSHIRE_PCWD) }, { HID_USB_DEVICE(USB_VENDOR_ID_CIDC, 0x0103) }, { HID_USB_DEVICE(USB_VENDOR_ID_CYGNAL, USB_DEVICE_ID_CYGNAL_RADIO_SI470X) }, @@ -1417,7 +1499,6 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE) }, { HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20) }, { HID_USB_DEVICE(USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5) }, - { HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR) }, { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0001) }, { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0002) }, { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0003) }, @@ -1433,7 +1514,6 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_SUPER_Q2) }, { HID_USB_DEVICE(USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_GOGOPEN) }, { HID_USB_DEVICE(USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_PENPOWER) }, - { HID_USB_DEVICE(USB_VENDOR_ID_GREENASIA, 0x0003) }, { HID_USB_DEVICE(USB_VENDOR_ID_GRETAGMACBETH, USB_DEVICE_ID_GRETAGMACBETH_HUEY) }, { HID_USB_DEVICE(USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_POWERMATE) }, { HID_USB_DEVICE(USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_SOUNDKNOB) }, @@ -1485,6 +1565,7 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1007) }, { HID_USB_DEVICE(USB_VENDOR_ID_IMATION, USB_DEVICE_ID_DISC_STAKKA) }, { HID_USB_DEVICE(USB_VENDOR_ID_KBGEAR, USB_DEVICE_ID_KBGEAR_JAMSTUDIO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_KWORLD, USB_DEVICE_ID_KWORLD_RADIO_FM700) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_GPEN_560) }, { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CASSY) }, { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POCKETCASSY) }, @@ -1572,6 +1653,9 @@ static const struct hid_device_id hid_mouse_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ISO) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_JIS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING3_ISO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING3_JIS) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) }, { } @@ -1613,9 +1697,10 @@ int hid_add_device(struct hid_device *hdev) if (hid_ignore(hdev)) return -ENODEV; - /* XXX hack, any other cleaner solution < 20 bus_id bytes? */ - sprintf(hdev->dev.bus_id, "%04X:%04X:%04X.%04X", hdev->bus, - hdev->vendor, hdev->product, atomic_inc_return(&id)); + /* XXX hack, any other cleaner solution after the driver core + * is converted to allow more than 20 bytes as the device name? */ + dev_set_name(&hdev->dev, "%04X:%04X:%04X.%04X", hdev->bus, + hdev->vendor, hdev->product, atomic_inc_return(&id)); ret = device_add(&hdev->dev); if (!ret) @@ -1690,18 +1775,33 @@ EXPORT_SYMBOL_GPL(hid_destroy_device); int __hid_register_driver(struct hid_driver *hdrv, struct module *owner, const char *mod_name) { + int ret; + hdrv->driver.name = hdrv->name; hdrv->driver.bus = &hid_bus_type; hdrv->driver.owner = owner; hdrv->driver.mod_name = mod_name; - return driver_register(&hdrv->driver); + INIT_LIST_HEAD(&hdrv->dyn_list); + spin_lock_init(&hdrv->dyn_lock); + + ret = driver_register(&hdrv->driver); + if (ret) + return ret; + + ret = driver_create_file(&hdrv->driver, &driver_attr_new_id); + if (ret) + driver_unregister(&hdrv->driver); + + return ret; } EXPORT_SYMBOL_GPL(__hid_register_driver); void hid_unregister_driver(struct hid_driver *hdrv) { + driver_remove_file(&hdrv->driver, &driver_attr_new_id); driver_unregister(&hdrv->driver); + hid_free_dynids(hdrv); } EXPORT_SYMBOL_GPL(hid_unregister_driver); @@ -1729,7 +1829,7 @@ static int __init hid_init(void) goto err_bus; #ifdef CONFIG_HID_COMPAT - hid_compat_wq = create_workqueue("hid_compat"); + hid_compat_wq = create_singlethread_workqueue("hid_compat"); if (!hid_compat_wq) { hidraw_exit(); goto err; diff --git a/drivers/hid/hid-dell.c b/drivers/hid/hid-dell.c deleted file mode 100644 index f5474300b83a..000000000000 --- a/drivers/hid/hid-dell.c +++ /dev/null @@ -1,76 +0,0 @@ -/* - * HID driver for some dell "special" devices - * - * Copyright (c) 1999 Andreas Gal - * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> - * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc - * Copyright (c) 2006-2007 Jiri Kosina - * Copyright (c) 2007 Paul Walmsley - * Copyright (c) 2008 Jiri Slaby - */ - -/* - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - */ - -#include <linux/device.h> -#include <linux/hid.h> -#include <linux/module.h> - -#include "hid-ids.h" - -static int dell_probe(struct hid_device *hdev, const struct hid_device_id *id) -{ - int ret; - - ret = hid_parse(hdev); - if (ret) { - dev_err(&hdev->dev, "parse failed\n"); - goto err_free; - } - - ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); - if (ret) { - dev_err(&hdev->dev, "hw start failed\n"); - goto err_free; - } - - usbhid_set_leds(hdev); - - return 0; -err_free: - return ret; -} - -static const struct hid_device_id dell_devices[] = { - { HID_USB_DEVICE(USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_W7658) }, - { HID_USB_DEVICE(USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_SK8115) }, - { HID_USB_DEVICE(USB_VENDOR_ID_GENERIC_13BA, USB_DEVICE_ID_GENERIC_13BA_KBD_MOUSE) }, - { } -}; -MODULE_DEVICE_TABLE(hid, dell_devices); - -static struct hid_driver dell_driver = { - .name = "dell", - .id_table = dell_devices, - .probe = dell_probe, -}; - -static int dell_init(void) -{ - return hid_register_driver(&dell_driver); -} - -static void dell_exit(void) -{ - hid_unregister_driver(&dell_driver); -} - -module_init(dell_init); -module_exit(dell_exit); -MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(dell); diff --git a/drivers/hid/hid-dummy.c b/drivers/hid/hid-dummy.c index e148f86fb58e..4a6af3cf192d 100644 --- a/drivers/hid/hid-dummy.c +++ b/drivers/hid/hid-dummy.c @@ -43,6 +43,9 @@ static int __init hid_dummy_init(void) #ifdef CONFIG_HID_MONTEREY_MODULE HID_COMPAT_CALL_DRIVER(monterey); #endif +#ifdef CONFIG_HID_NTRIG_MODULE + HID_COMPAT_CALL_DRIVER(ntrig); +#endif #ifdef CONFIG_HID_PANTHERLORD_MODULE HID_COMPAT_CALL_DRIVER(pantherlord); #endif diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 5cc404291736..2b7b6eeae8c6 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -82,6 +82,9 @@ #define USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI 0x0230 #define USB_DEVICE_ID_APPLE_WELLSPRING2_ISO 0x0231 #define USB_DEVICE_ID_APPLE_WELLSPRING2_JIS 0x0232 +#define USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI 0x0236 +#define USB_DEVICE_ID_APPLE_WELLSPRING3_ISO 0x0237 +#define USB_DEVICE_ID_APPLE_WELLSPRING3_JIS 0x0238 #define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a #define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b #define USB_DEVICE_ID_APPLE_ATV_IRCONTROL 0x8241 @@ -104,9 +107,6 @@ #define USB_VENDOR_ID_BELKIN 0x050d #define USB_DEVICE_ID_FLIP_KVM 0x3201 -#define USB_VENDOR_ID_BRIGHT 0x1241 -#define USB_DEVICE_ID_BRIGHT_ABNT2 0x1503 - #define USB_VENDOR_ID_BERKSHIRE 0x0c98 #define USB_DEVICE_ID_BERKSHIRE_PCWD 0x1140 @@ -138,10 +138,6 @@ #define USB_DEVICE_ID_CYPRESS_BARCODE_1 0xde61 #define USB_DEVICE_ID_CYPRESS_BARCODE_2 0xde64 -#define USB_VENDOR_ID_DELL 0x413c -#define USB_DEVICE_ID_DELL_W7658 0x2005 -#define USB_DEVICE_ID_DELL_SK8115 0x2105 - #define USB_VENDOR_ID_DELORME 0x1163 #define USB_DEVICE_ID_DELORME_EARTHMATE 0x0100 #define USB_DEVICE_ID_DELORME_EM_LT20 0x0200 @@ -160,12 +156,10 @@ #define USB_VENDOR_ID_GAMERON 0x0810 #define USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR 0x0001 +#define USB_DEVICE_ID_GAMERON_DUAL_PCS_ADAPTOR 0x0002 #define USB_VENDOR_ID_GENERAL_TOUCH 0x0dfc -#define USB_VENDOR_ID_GENERIC_13BA 0x13ba -#define USB_DEVICE_ID_GENERIC_13BA_KBD_MOUSE 0x0017 - #define USB_VENDOR_ID_GLAB 0x06c2 #define USB_DEVICE_ID_4_PHIDGETSERVO_30 0x0038 #define USB_DEVICE_ID_1_PHIDGETSERVO_30 0x0039 @@ -253,6 +247,9 @@ #define USB_VENDOR_ID_KBGEAR 0x084e #define USB_DEVICE_ID_KBGEAR_JAMSTUDIO 0x1001 +#define USB_VENDOR_ID_KWORLD 0x1b80 +#define USB_DEVICE_ID_KWORLD_RADIO_FM700 0xd700 + #define USB_VENDOR_ID_LABTEC 0x1020 #define USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD 0x0006 @@ -285,7 +282,6 @@ #define USB_DEVICE_ID_LOGITECH_WHEEL 0xc294 #define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL 0xc295 #define USB_DEVICE_ID_LOGITECH_ELITE_KBD 0xc30a -#define USB_DEVICE_ID_LOGITECH_KBD 0xc311 #define USB_DEVICE_ID_S510_RECEIVER 0xc50c #define USB_DEVICE_ID_S510_RECEIVER_2 0xc517 #define USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500 0xc512 @@ -332,6 +328,9 @@ #define USB_VENDOR_ID_NEC 0x073e #define USB_DEVICE_ID_NEC_USB_GAME_PAD 0x0301 +#define USB_VENDOR_ID_NTRIG 0x1b96 +#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN 0x0001 + #define USB_VENDOR_ID_ONTRAK 0x0a07 #define USB_DEVICE_ID_ONTRAK_ADU100 0x0064 diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c index 2bae340eafe2..83e07c9f4144 100644 --- a/drivers/hid/hid-lg.c +++ b/drivers/hid/hid-lg.c @@ -26,7 +26,6 @@ #define LG_RDESC 0x001 #define LG_BAD_RELATIVE_KEYS 0x002 #define LG_DUPLICATE_USAGES 0x004 -#define LG_RESET_LEDS 0x008 #define LG_EXPANDED_KEYMAP 0x010 #define LG_IGNORE_DOUBLED_WHEEL 0x020 #define LG_WIRELESS 0x040 @@ -248,9 +247,6 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id) goto err_free; } - if (quirks & LG_RESET_LEDS) - usbhid_set_leds(hdev); - if (quirks & LG_FF) lgff_init(hdev); if (quirks & LG_FF2) @@ -279,9 +275,6 @@ static const struct hid_device_id lg_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI), .driver_data = LG_DUPLICATE_USAGES }, - { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_KBD), - .driver_data = LG_RESET_LEDS }, - { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD), .driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500), diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c new file mode 100644 index 000000000000..db44fbd7bdf6 --- /dev/null +++ b/drivers/hid/hid-ntrig.c @@ -0,0 +1,82 @@ +/* + * HID driver for some ntrig "special" devices + * + * Copyright (c) 1999 Andreas Gal + * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> + * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc + * Copyright (c) 2006-2007 Jiri Kosina + * Copyright (c) 2007 Paul Walmsley + * Copyright (c) 2008 Jiri Slaby + * Copyright (c) 2008 Rafi Rubin + * + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include <linux/device.h> +#include <linux/hid.h> +#include <linux/module.h> + +#include "hid-ids.h" + +#define NTRIG_DUPLICATE_USAGES 0x001 + +#define nt_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \ + EV_KEY, (c)) + +static int ntrig_input_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + if ((usage->hid & HID_USAGE_PAGE) == HID_UP_DIGITIZER && + (usage->hid & 0xff) == 0x47) { + nt_map_key_clear(BTN_TOOL_DOUBLETAP); + return 1; + } + return 0; +} + +static int ntrig_input_mapped(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + if (usage->type == EV_KEY || usage->type == EV_REL + || usage->type == EV_ABS) + clear_bit(usage->code, *bit); + + return 0; +} +static const struct hid_device_id ntrig_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN), + .driver_data = NTRIG_DUPLICATE_USAGES }, + { } +}; +MODULE_DEVICE_TABLE(hid, ntrig_devices); + +static struct hid_driver ntrig_driver = { + .name = "ntrig", + .id_table = ntrig_devices, + .input_mapping = ntrig_input_mapping, + .input_mapped = ntrig_input_mapped, +}; + +static int ntrig_init(void) +{ + return hid_register_driver(&ntrig_driver); +} + +static void ntrig_exit(void) +{ + hid_unregister_driver(&ntrig_driver); +} + +module_init(ntrig_init); +module_exit(ntrig_exit); +MODULE_LICENSE("GPL"); + +HID_COMPAT_LOAD_DRIVER(ntrig); diff --git a/drivers/hid/hid-pl.c b/drivers/hid/hid-pl.c index acd815586182..46941f979b9d 100644 --- a/drivers/hid/hid-pl.c +++ b/drivers/hid/hid-pl.c @@ -178,6 +178,8 @@ err: static const struct hid_device_id pl_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR), .driver_data = 1 }, /* Twin USB Joystick */ + { HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PCS_ADAPTOR), + .driver_data = 1 }, /* Twin USB Joystick */ { HID_USB_DEVICE(USB_VENDOR_ID_GREENASIA, 0x0003), }, /* GreenAsia Inc. USB Joystick */ { } }; diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index 894d52e05bf9..975edd88a3db 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -38,7 +38,7 @@ static int hidraw_major; static struct cdev hidraw_cdev; static struct class *hidraw_class; static struct hidraw *hidraw_table[HIDRAW_MAX_DEVICES]; -static DEFINE_SPINLOCK(minors_lock); +static DEFINE_MUTEX(minors_lock); static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) { @@ -159,13 +159,13 @@ static int hidraw_open(struct inode *inode, struct file *file) struct hidraw_list *list; int err = 0; - lock_kernel(); if (!(list = kzalloc(sizeof(struct hidraw_list), GFP_KERNEL))) { err = -ENOMEM; goto out; } - spin_lock(&minors_lock); + lock_kernel(); + mutex_lock(&minors_lock); if (!hidraw_table[minor]) { printk(KERN_EMERG "hidraw device with minor %d doesn't exist\n", minor); @@ -180,13 +180,16 @@ static int hidraw_open(struct inode *inode, struct file *file) file->private_data = list; dev = hidraw_table[minor]; - if (!dev->open++) - dev->hid->ll_driver->open(dev->hid); + if (!dev->open++) { + err = dev->hid->ll_driver->open(dev->hid); + if (err < 0) + dev->open--; + } out_unlock: - spin_unlock(&minors_lock); -out: + mutex_unlock(&minors_lock); unlock_kernel(); +out: return err; } @@ -262,6 +265,34 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd, break; } default: + { + struct hid_device *hid = dev->hid; + if (_IOC_TYPE(cmd) != 'H' || _IOC_DIR(cmd) != _IOC_READ) + return -EINVAL; + + if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGRAWNAME(0))) { + int len; + if (!hid->name) + return 0; + len = strlen(hid->name) + 1; + if (len > _IOC_SIZE(cmd)) + len = _IOC_SIZE(cmd); + return copy_to_user(user_arg, hid->name, len) ? + -EFAULT : len; + } + + if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGRAWPHYS(0))) { + int len; + if (!hid->phys) + return 0; + len = strlen(hid->phys) + 1; + if (len > _IOC_SIZE(cmd)) + len = _IOC_SIZE(cmd); + return copy_to_user(user_arg, hid->phys, len) ? + -EFAULT : len; + } + } + ret = -ENOTTY; } unlock_kernel(); @@ -310,7 +341,7 @@ int hidraw_connect(struct hid_device *hid) result = -EINVAL; - spin_lock(&minors_lock); + mutex_lock(&minors_lock); for (minor = 0; minor < HIDRAW_MAX_DEVICES; minor++) { if (hidraw_table[minor]) @@ -320,9 +351,8 @@ int hidraw_connect(struct hid_device *hid) break; } - spin_unlock(&minors_lock); - if (result) { + mutex_unlock(&minors_lock); kfree(dev); goto out; } @@ -331,14 +361,14 @@ int hidraw_connect(struct hid_device *hid) NULL, "%s%d", "hidraw", minor); if (IS_ERR(dev->dev)) { - spin_lock(&minors_lock); hidraw_table[minor] = NULL; - spin_unlock(&minors_lock); + mutex_unlock(&minors_lock); result = PTR_ERR(dev->dev); kfree(dev); goto out; } + mutex_unlock(&minors_lock); init_waitqueue_head(&dev->wait); INIT_LIST_HEAD(&dev->list); @@ -360,9 +390,9 @@ void hidraw_disconnect(struct hid_device *hid) hidraw->exist = 0; - spin_lock(&minors_lock); + mutex_lock(&minors_lock); hidraw_table[hidraw->minor] = NULL; - spin_unlock(&minors_lock); + mutex_unlock(&minors_lock); device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor)); diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 18e5ddd722cd..f0a0f72238ab 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -4,7 +4,7 @@ * Copyright (c) 1999 Andreas Gal * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc - * Copyright (c) 2006-2007 Jiri Kosina + * Copyright (c) 2006-2008 Jiri Kosina */ /* @@ -102,7 +102,7 @@ static void hid_reset(struct work_struct *work) struct usbhid_device *usbhid = container_of(work, struct usbhid_device, reset_work); struct hid_device *hid = usbhid->hid; - int rc_lock, rc = 0; + int rc = 0; if (test_bit(HID_CLEAR_HALT, &usbhid->iofl)) { dev_dbg(&usbhid->intf->dev, "clear halt\n"); @@ -113,11 +113,10 @@ static void hid_reset(struct work_struct *work) else if (test_bit(HID_RESET_PENDING, &usbhid->iofl)) { dev_dbg(&usbhid->intf->dev, "resetting device\n"); - rc = rc_lock = usb_lock_device_for_reset(hid_to_usb_dev(hid), usbhid->intf); - if (rc_lock >= 0) { + rc = usb_lock_device_for_reset(hid_to_usb_dev(hid), usbhid->intf); + if (rc == 0) { rc = usb_reset_device(hid_to_usb_dev(hid)); - if (rc_lock) - usb_unlock_device(hid_to_usb_dev(hid)); + usb_unlock_device(hid_to_usb_dev(hid)); } clear_bit(HID_RESET_PENDING, &usbhid->iofl); } @@ -641,9 +640,7 @@ static void hid_find_max_report(struct hid_device *hid, unsigned int type, unsigned int size; list_for_each_entry(report, &hid->report_enum[type].report_list, list) { - size = ((report->size - 1) >> 3) + 1; - if (type == HID_INPUT_REPORT && hid->report_enum[type].numbered) - size++; + size = ((report->size - 1) >> 3) + 1 + hid->report_enum[type].numbered; if (*max < size) *max = size; } @@ -653,13 +650,16 @@ static int hid_alloc_buffers(struct usb_device *dev, struct hid_device *hid) { struct usbhid_device *usbhid = hid->driver_data; - if (!(usbhid->inbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_ATOMIC, &usbhid->inbuf_dma))) - return -1; - if (!(usbhid->outbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_ATOMIC, &usbhid->outbuf_dma))) - return -1; - if (!(usbhid->cr = usb_buffer_alloc(dev, sizeof(*(usbhid->cr)), GFP_ATOMIC, &usbhid->cr_dma))) - return -1; - if (!(usbhid->ctrlbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_ATOMIC, &usbhid->ctrlbuf_dma))) + usbhid->inbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_KERNEL, + &usbhid->inbuf_dma); + usbhid->outbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_KERNEL, + &usbhid->outbuf_dma); + usbhid->cr = usb_buffer_alloc(dev, sizeof(*usbhid->cr), GFP_KERNEL, + &usbhid->cr_dma); + usbhid->ctrlbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_KERNEL, + &usbhid->ctrlbuf_dma); + if (!usbhid->inbuf || !usbhid->outbuf || !usbhid->cr || + !usbhid->ctrlbuf) return -1; return 0; @@ -781,6 +781,8 @@ static int usbhid_start(struct hid_device *hid) unsigned int n, insize = 0; int ret; + clear_bit(HID_DISCONNECTED, &usbhid->iofl); + usbhid->bufsize = HID_MIN_BUFFER_SIZE; hid_find_max_report(hid, HID_INPUT_REPORT, &usbhid->bufsize); hid_find_max_report(hid, HID_OUTPUT_REPORT, &usbhid->bufsize); @@ -794,7 +796,6 @@ static int usbhid_start(struct hid_device *hid) if (insize > HID_MAX_BUFFER_SIZE) insize = HID_MAX_BUFFER_SIZE; - mutex_lock(&usbhid->setup); if (hid_alloc_buffers(dev, hid)) { ret = -ENOMEM; goto fail; @@ -806,7 +807,7 @@ static int usbhid_start(struct hid_device *hid) int interval; endpoint = &interface->endpoint[n].desc; - if ((endpoint->bmAttributes & 3) != 3) /* Not an interrupt endpoint */ + if (!usb_endpoint_xfer_int(endpoint)) continue; interval = endpoint->bInterval; @@ -847,12 +848,6 @@ static int usbhid_start(struct hid_device *hid) } } - if (!usbhid->urbin) { - err_hid("couldn't find an input interrupt endpoint"); - ret = -ENODEV; - goto fail; - } - init_waitqueue_head(&usbhid->wait); INIT_WORK(&usbhid->reset_work, hid_reset); setup_timer(&usbhid->io_retry, hid_retry_timeout, (unsigned long) hid); @@ -880,7 +875,15 @@ static int usbhid_start(struct hid_device *hid) hid_dump_device(hid); set_bit(HID_STARTED, &usbhid->iofl); - mutex_unlock(&usbhid->setup); + + /* Some keyboards don't work until their LEDs have been set. + * Since BIOSes do set the LEDs, it must be safe for any device + * that supports the keyboard boot protocol. + */ + if (interface->desc.bInterfaceSubClass == USB_INTERFACE_SUBCLASS_BOOT && + interface->desc.bInterfaceProtocol == + USB_INTERFACE_PROTOCOL_KEYBOARD) + usbhid_set_leds(hid); return 0; @@ -888,8 +891,10 @@ fail: usb_free_urb(usbhid->urbin); usb_free_urb(usbhid->urbout); usb_free_urb(usbhid->urbctrl); + usbhid->urbin = NULL; + usbhid->urbout = NULL; + usbhid->urbctrl = NULL; hid_free_buffers(dev, hid); - mutex_unlock(&usbhid->setup); return ret; } @@ -900,7 +905,6 @@ static void usbhid_stop(struct hid_device *hid) if (WARN_ON(!usbhid)) return; - mutex_lock(&usbhid->setup); clear_bit(HID_STARTED, &usbhid->iofl); spin_lock_irq(&usbhid->inlock); /* Sync with error handler */ set_bit(HID_DISCONNECTED, &usbhid->iofl); @@ -924,9 +928,11 @@ static void usbhid_stop(struct hid_device *hid) usb_free_urb(usbhid->urbin); usb_free_urb(usbhid->urbctrl); usb_free_urb(usbhid->urbout); + usbhid->urbin = NULL; /* don't mess up next start */ + usbhid->urbctrl = NULL; + usbhid->urbout = NULL; hid_free_buffers(hid_to_usb_dev(hid), hid); - mutex_unlock(&usbhid->setup); } static struct hid_ll_driver usb_hid_driver = { @@ -940,15 +946,26 @@ static struct hid_ll_driver usb_hid_driver = { static int hid_probe(struct usb_interface *intf, const struct usb_device_id *id) { + struct usb_host_interface *interface = intf->cur_altsetting; struct usb_device *dev = interface_to_usbdev(intf); struct usbhid_device *usbhid; struct hid_device *hid; + unsigned int n, has_in = 0; size_t len; int ret; dbg_hid("HID probe called for ifnum %d\n", intf->altsetting->desc.bInterfaceNumber); + for (n = 0; n < interface->desc.bNumEndpoints; n++) + if (usb_endpoint_is_int_in(&interface->endpoint[n].desc)) + has_in++; + if (!has_in) { + dev_err(&intf->dev, "couldn't find an input interrupt " + "endpoint\n"); + return -ENODEV; + } + hid = hid_allocate_device(); if (IS_ERR(hid)) return PTR_ERR(hid); @@ -1003,7 +1020,6 @@ static int hid_probe(struct usb_interface *intf, const struct usb_device_id *id) hid->driver_data = usbhid; usbhid->hid = hid; - mutex_init(&usbhid->setup); /* needed on suspend/resume */ ret = hid_add_device(hid); if (ret) { @@ -1038,18 +1054,14 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message) struct hid_device *hid = usb_get_intfdata (intf); struct usbhid_device *usbhid = hid->driver_data; - mutex_lock(&usbhid->setup); - if (!test_bit(HID_STARTED, &usbhid->iofl)) { - mutex_unlock(&usbhid->setup); + if (!test_bit(HID_STARTED, &usbhid->iofl)) return 0; - } spin_lock_irq(&usbhid->inlock); /* Sync with error handler */ set_bit(HID_SUSPENDED, &usbhid->iofl); spin_unlock_irq(&usbhid->inlock); del_timer_sync(&usbhid->io_retry); usb_kill_urb(usbhid->urbin); - mutex_unlock(&usbhid->setup); dev_dbg(&intf->dev, "suspend\n"); return 0; } @@ -1060,16 +1072,12 @@ static int hid_resume(struct usb_interface *intf) struct usbhid_device *usbhid = hid->driver_data; int status; - mutex_lock(&usbhid->setup); - if (!test_bit(HID_STARTED, &usbhid->iofl)) { - mutex_unlock(&usbhid->setup); + if (!test_bit(HID_STARTED, &usbhid->iofl)) return 0; - } clear_bit(HID_SUSPENDED, &usbhid->iofl); usbhid->retry_delay = 0; status = hid_start_in(hid); - mutex_unlock(&usbhid->setup); dev_dbg(&intf->dev, "resume status %d\n", status); return status; } diff --git a/drivers/hid/usbhid/usbhid.h b/drivers/hid/usbhid/usbhid.h index 55973ff54008..9eb30564be9c 100644 --- a/drivers/hid/usbhid/usbhid.h +++ b/drivers/hid/usbhid/usbhid.h @@ -40,6 +40,16 @@ int usbhid_open(struct hid_device *hid); void usbhid_init_reports(struct hid_device *hid); void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir); +/* iofl flags */ +#define HID_CTRL_RUNNING 1 +#define HID_OUT_RUNNING 2 +#define HID_IN_RUNNING 3 +#define HID_RESET_PENDING 4 +#define HID_SUSPENDED 5 +#define HID_CLEAR_HALT 6 +#define HID_DISCONNECTED 7 +#define HID_STARTED 8 + /* * USB-specific HID struct, to be pointed to * from struct hid_device->driver_data @@ -74,7 +84,6 @@ struct usbhid_device { dma_addr_t outbuf_dma; /* Output buffer dma */ spinlock_t outlock; /* Output fifo spinlock */ - struct mutex setup; unsigned long iofl; /* I/O flags (CTRL_RUNNING, OUT_RUNNING) */ struct timer_list io_retry; /* Retry timer */ unsigned long stop_retry; /* Time to give up, in jiffies */ diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 6de1e0ffd391..ba9cd2a74f28 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -159,6 +159,16 @@ config SENSORS_ADM9240 This driver can also be built as a module. If so, the module will be called adm9240. +config SENSORS_ADT7462 + tristate "Analog Devices ADT7462" + depends on I2C && EXPERIMENTAL + help + If you say yes here you get support for the Analog Devices + ADT7462 temperature monitoring chips. + + This driver can also be built as a module. If so, the module + will be called adt7462. + config SENSORS_ADT7470 tristate "Analog Devices ADT7470" depends on I2C && EXPERIMENTAL @@ -389,7 +399,8 @@ config SENSORS_IT87 select HWMON_VID help If you say yes here you get support for ITE IT8705F, IT8712F, - IT8716F, IT8718F and IT8726F sensor chips, and the SiS960 clone. + IT8716F, IT8718F, IT8720F and IT8726F sensor chips, and the + SiS960 clone. This driver can also be built as a module. If so, the module will be called it87. @@ -407,11 +418,12 @@ config SENSORS_LM63 will be called lm63. config SENSORS_LM70 - tristate "National Semiconductor LM70" + tristate "National Semiconductor LM70 / Texas Instruments TMP121" depends on SPI_MASTER && EXPERIMENTAL help If you say yes here you get support for the National Semiconductor - LM70 digital temperature sensor chip. + LM70 and Texas Instruments TMP121/TMP123 digital temperature + sensor chips. This driver can also be built as a module. If so, the module will be called lm70. @@ -538,6 +550,17 @@ config SENSORS_LM93 This driver can also be built as a module. If so, the module will be called lm93. +config SENSORS_LTC4245 + tristate "Linear Technology LTC4245" + depends on I2C && EXPERIMENTAL + default n + help + If you say yes here you get support for Linear Technology LTC4245 + Multiple Supply Hot Swap Controller I2C interface. + + This driver can also be built as a module. If so, the module will + be called ltc4245. + config SENSORS_MAX1111 tristate "Maxim MAX1111 Multichannel, Serial 8-bit ADC chip" depends on SPI_MASTER @@ -825,6 +848,25 @@ config SENSORS_HDAPS Say Y here if you have an applicable laptop and want to experience the awesome power of hdaps. +config SENSORS_LIS3LV02D + tristate "STMicroeletronics LIS3LV02Dx three-axis digital accelerometer" + depends on ACPI && INPUT + default n + help + This driver provides support for the LIS3LV02Dx accelerometer. In + particular, it can be found in a number of HP laptops, which have the + "Mobile Data Protection System 3D" or "3D DriveGuard" feature. On such + systems the driver should load automatically (via ACPI). The + accelerometer might also be found in other systems, connected via SPI + or I2C. The accelerometer data is readable via + /sys/devices/platform/lis3lv02d. + + This driver also provides an absolute input class device, allowing + the laptop to act as a pinball machine-esque joystick. + + This driver can also be built as a module. If so, the module + will be called lis3lv02d. + config SENSORS_APPLESMC tristate "Apple SMC (Motion sensor, light sensor, keyboard backlight)" depends on INPUT && X86 diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 042d5a78622e..8fd124eff646 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_SENSORS_ADM1029) += adm1029.o obj-$(CONFIG_SENSORS_ADM1031) += adm1031.o obj-$(CONFIG_SENSORS_ADM9240) += adm9240.o obj-$(CONFIG_SENSORS_ADS7828) += ads7828.o +obj-$(CONFIG_SENSORS_ADT7462) += adt7462.o obj-$(CONFIG_SENSORS_ADT7470) += adt7470.o obj-$(CONFIG_SENSORS_ADT7473) += adt7473.o obj-$(CONFIG_SENSORS_APPLESMC) += applesmc.o @@ -48,6 +49,7 @@ obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o obj-$(CONFIG_SENSORS_IT87) += it87.o obj-$(CONFIG_SENSORS_K8TEMP) += k8temp.o +obj-$(CONFIG_SENSORS_LIS3LV02D) += lis3lv02d.o obj-$(CONFIG_SENSORS_LM63) += lm63.o obj-$(CONFIG_SENSORS_LM70) += lm70.o obj-$(CONFIG_SENSORS_LM75) += lm75.o @@ -60,6 +62,7 @@ obj-$(CONFIG_SENSORS_LM87) += lm87.o obj-$(CONFIG_SENSORS_LM90) += lm90.o obj-$(CONFIG_SENSORS_LM92) += lm92.o obj-$(CONFIG_SENSORS_LM93) += lm93.o +obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o obj-$(CONFIG_SENSORS_MAX1111) += max1111.o obj-$(CONFIG_SENSORS_MAX1619) += max1619.o obj-$(CONFIG_SENSORS_MAX6650) += max6650.o diff --git a/drivers/hwmon/adt7462.c b/drivers/hwmon/adt7462.c new file mode 100644 index 000000000000..66107b4dc12a --- /dev/null +++ b/drivers/hwmon/adt7462.c @@ -0,0 +1,2002 @@ +/* + * A hwmon driver for the Analog Devices ADT7462 + * Copyright (C) 2008 IBM + * + * Author: Darrick J. Wong <djwong@us.ibm.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/module.h> +#include <linux/jiffies.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/mutex.h> +#include <linux/delay.h> +#include <linux/log2.h> + +/* Addresses to scan */ +static const unsigned short normal_i2c[] = { 0x58, 0x5C, I2C_CLIENT_END }; + +/* Insmod parameters */ +I2C_CLIENT_INSMOD_1(adt7462); + +/* ADT7462 registers */ +#define ADT7462_REG_DEVICE 0x3D +#define ADT7462_REG_VENDOR 0x3E +#define ADT7462_REG_REVISION 0x3F + +#define ADT7462_REG_MIN_TEMP_BASE_ADDR 0x44 +#define ADT7462_REG_MIN_TEMP_MAX_ADDR 0x47 +#define ADT7462_REG_MAX_TEMP_BASE_ADDR 0x48 +#define ADT7462_REG_MAX_TEMP_MAX_ADDR 0x4B +#define ADT7462_REG_TEMP_BASE_ADDR 0x88 +#define ADT7462_REG_TEMP_MAX_ADDR 0x8F + +#define ADT7462_REG_FAN_BASE_ADDR 0x98 +#define ADT7462_REG_FAN_MAX_ADDR 0x9F +#define ADT7462_REG_FAN2_BASE_ADDR 0xA2 +#define ADT7462_REG_FAN2_MAX_ADDR 0xA9 +#define ADT7462_REG_FAN_ENABLE 0x07 +#define ADT7462_REG_FAN_MIN_BASE_ADDR 0x78 +#define ADT7462_REG_FAN_MIN_MAX_ADDR 0x7F + +#define ADT7462_REG_CFG2 0x02 +#define ADT7462_FSPD_MASK 0x20 + +#define ADT7462_REG_PWM_BASE_ADDR 0xAA +#define ADT7462_REG_PWM_MAX_ADDR 0xAD +#define ADT7462_REG_PWM_MIN_BASE_ADDR 0x28 +#define ADT7462_REG_PWM_MIN_MAX_ADDR 0x2B +#define ADT7462_REG_PWM_MAX 0x2C +#define ADT7462_REG_PWM_TEMP_MIN_BASE_ADDR 0x5C +#define ADT7462_REG_PWM_TEMP_MIN_MAX_ADDR 0x5F +#define ADT7462_REG_PWM_TEMP_RANGE_BASE_ADDR 0x60 +#define ADT7462_REG_PWM_TEMP_RANGE_MAX_ADDR 0x63 +#define ADT7462_PWM_HYST_MASK 0x0F +#define ADT7462_PWM_RANGE_MASK 0xF0 +#define ADT7462_PWM_RANGE_SHIFT 4 +#define ADT7462_REG_PWM_CFG_BASE_ADDR 0x21 +#define ADT7462_REG_PWM_CFG_MAX_ADDR 0x24 +#define ADT7462_PWM_CHANNEL_MASK 0xE0 +#define ADT7462_PWM_CHANNEL_SHIFT 5 + +#define ADT7462_REG_PIN_CFG_BASE_ADDR 0x10 +#define ADT7462_REG_PIN_CFG_MAX_ADDR 0x13 +#define ADT7462_PIN7_INPUT 0x01 /* cfg0 */ +#define ADT7462_DIODE3_INPUT 0x20 +#define ADT7462_DIODE1_INPUT 0x40 +#define ADT7462_VID_INPUT 0x80 +#define ADT7462_PIN22_INPUT 0x04 /* cfg1 */ +#define ADT7462_PIN21_INPUT 0x08 +#define ADT7462_PIN19_INPUT 0x10 +#define ADT7462_PIN15_INPUT 0x20 +#define ADT7462_PIN13_INPUT 0x40 +#define ADT7462_PIN8_INPUT 0x80 +#define ADT7462_PIN23_MASK 0x03 +#define ADT7462_PIN23_SHIFT 0 +#define ADT7462_PIN26_MASK 0x0C /* cfg2 */ +#define ADT7462_PIN26_SHIFT 2 +#define ADT7462_PIN25_MASK 0x30 +#define ADT7462_PIN25_SHIFT 4 +#define ADT7462_PIN24_MASK 0xC0 +#define ADT7462_PIN24_SHIFT 6 +#define ADT7462_PIN26_VOLT_INPUT 0x08 +#define ADT7462_PIN25_VOLT_INPUT 0x20 +#define ADT7462_PIN28_SHIFT 6 /* cfg3 */ +#define ADT7462_PIN28_VOLT 0x5 + +#define ADT7462_REG_ALARM1 0xB8 +#define ADT7462_LT_ALARM 0x02 +#define ADT7462_R1T_ALARM 0x04 +#define ADT7462_R2T_ALARM 0x08 +#define ADT7462_R3T_ALARM 0x10 +#define ADT7462_REG_ALARM2 0xBB +#define ADT7462_V0_ALARM 0x01 +#define ADT7462_V1_ALARM 0x02 +#define ADT7462_V2_ALARM 0x04 +#define ADT7462_V3_ALARM 0x08 +#define ADT7462_V4_ALARM 0x10 +#define ADT7462_V5_ALARM 0x20 +#define ADT7462_V6_ALARM 0x40 +#define ADT7462_V7_ALARM 0x80 +#define ADT7462_REG_ALARM3 0xBC +#define ADT7462_V8_ALARM 0x08 +#define ADT7462_V9_ALARM 0x10 +#define ADT7462_V10_ALARM 0x20 +#define ADT7462_V11_ALARM 0x40 +#define ADT7462_V12_ALARM 0x80 +#define ADT7462_REG_ALARM4 0xBD +#define ADT7462_F0_ALARM 0x01 +#define ADT7462_F1_ALARM 0x02 +#define ADT7462_F2_ALARM 0x04 +#define ADT7462_F3_ALARM 0x08 +#define ADT7462_F4_ALARM 0x10 +#define ADT7462_F5_ALARM 0x20 +#define ADT7462_F6_ALARM 0x40 +#define ADT7462_F7_ALARM 0x80 +#define ADT7462_ALARM1 0x0000 +#define ADT7462_ALARM2 0x0100 +#define ADT7462_ALARM3 0x0200 +#define ADT7462_ALARM4 0x0300 +#define ADT7462_ALARM_REG_SHIFT 8 +#define ADT7462_ALARM_FLAG_MASK 0x0F + +#define ADT7462_TEMP_COUNT 4 +#define ADT7462_TEMP_REG(x) (ADT7462_REG_TEMP_BASE_ADDR + (x * 2)) +#define ADT7462_TEMP_MIN_REG(x) (ADT7462_REG_MIN_TEMP_BASE_ADDR + (x)) +#define ADT7462_TEMP_MAX_REG(x) (ADT7462_REG_MAX_TEMP_BASE_ADDR + (x)) +#define TEMP_FRAC_OFFSET 6 + +#define ADT7462_FAN_COUNT 8 +#define ADT7462_REG_FAN_MIN(x) (ADT7462_REG_FAN_MIN_BASE_ADDR + (x)) + +#define ADT7462_PWM_COUNT 4 +#define ADT7462_REG_PWM(x) (ADT7462_REG_PWM_BASE_ADDR + (x)) +#define ADT7462_REG_PWM_MIN(x) (ADT7462_REG_PWM_MIN_BASE_ADDR + (x)) +#define ADT7462_REG_PWM_TMIN(x) \ + (ADT7462_REG_PWM_TEMP_MIN_BASE_ADDR + (x)) +#define ADT7462_REG_PWM_TRANGE(x) \ + (ADT7462_REG_PWM_TEMP_RANGE_BASE_ADDR + (x)) + +#define ADT7462_PIN_CFG_REG_COUNT 4 +#define ADT7462_REG_PIN_CFG(x) (ADT7462_REG_PIN_CFG_BASE_ADDR + (x)) +#define ADT7462_REG_PWM_CFG(x) (ADT7462_REG_PWM_CFG_BASE_ADDR + (x)) + +#define ADT7462_ALARM_REG_COUNT 4 + +/* + * The chip can measure 13 different voltage sources: + * + * 1. +12V1 (pin 7) + * 2. Vccp1/+2.5V/+1.8V/+1.5V (pin 23) + * 3. +12V3 (pin 22) + * 4. +5V (pin 21) + * 5. +1.25V/+0.9V (pin 19) + * 6. +2.5V/+1.8V (pin 15) + * 7. +3.3v (pin 13) + * 8. +12V2 (pin 8) + * 9. Vbatt/FSB_Vtt (pin 26) + * A. +3.3V/+1.2V1 (pin 25) + * B. Vccp2/+2.5V/+1.8V/+1.5V (pin 24) + * C. +1.5V ICH (only if BOTH pin 28/29 are set to +1.5V) + * D. +1.5V 3GPIO (only if BOTH pin 28/29 are set to +1.5V) + * + * Each of these 13 has a factor to convert raw to voltage. Even better, + * the pins can be connected to other sensors (tach/gpio/hot/etc), which + * makes the bookkeeping tricky. + * + * Some, but not all, of these voltages have low/high limits. + */ +#define ADT7462_VOLT_COUNT 12 + +#define ADT7462_VENDOR 0x41 +#define ADT7462_DEVICE 0x62 +/* datasheet only mentions a revision 4 */ +#define ADT7462_REVISION 0x04 + +/* How often do we reread sensors values? (In jiffies) */ +#define SENSOR_REFRESH_INTERVAL (2 * HZ) + +/* How often do we reread sensor limit values? (In jiffies) */ +#define LIMIT_REFRESH_INTERVAL (60 * HZ) + +/* datasheet says to divide this number by the fan reading to get fan rpm */ +#define FAN_PERIOD_TO_RPM(x) ((90000 * 60) / (x)) +#define FAN_RPM_TO_PERIOD FAN_PERIOD_TO_RPM +#define FAN_PERIOD_INVALID 65535 +#define FAN_DATA_VALID(x) ((x) && (x) != FAN_PERIOD_INVALID) + +#define MASK_AND_SHIFT(value, prefix) \ + (((value) & prefix##_MASK) >> prefix##_SHIFT) + +#define ROUND_DIV(x, divisor) (((x) + ((divisor) / 2)) / (divisor)) + +struct adt7462_data { + struct device *hwmon_dev; + struct attribute_group attrs; + struct mutex lock; + char sensors_valid; + char limits_valid; + unsigned long sensors_last_updated; /* In jiffies */ + unsigned long limits_last_updated; /* In jiffies */ + + u8 temp[ADT7462_TEMP_COUNT]; + /* bits 6-7 are quarter pieces of temp */ + u8 temp_frac[ADT7462_TEMP_COUNT]; + u8 temp_min[ADT7462_TEMP_COUNT]; + u8 temp_max[ADT7462_TEMP_COUNT]; + u16 fan[ADT7462_FAN_COUNT]; + u8 fan_enabled; + u8 fan_min[ADT7462_FAN_COUNT]; + u8 cfg2; + u8 pwm[ADT7462_PWM_COUNT]; + u8 pin_cfg[ADT7462_PIN_CFG_REG_COUNT]; + u8 voltages[ADT7462_VOLT_COUNT]; + u8 volt_max[ADT7462_VOLT_COUNT]; + u8 volt_min[ADT7462_VOLT_COUNT]; + u8 pwm_min[ADT7462_PWM_COUNT]; + u8 pwm_tmin[ADT7462_PWM_COUNT]; + u8 pwm_trange[ADT7462_PWM_COUNT]; + u8 pwm_max; /* only one per chip */ + u8 pwm_cfg[ADT7462_PWM_COUNT]; + u8 alarms[ADT7462_ALARM_REG_COUNT]; +}; + +static int adt7462_probe(struct i2c_client *client, + const struct i2c_device_id *id); +static int adt7462_detect(struct i2c_client *client, int kind, + struct i2c_board_info *info); +static int adt7462_remove(struct i2c_client *client); + +static const struct i2c_device_id adt7462_id[] = { + { "adt7462", adt7462 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adt7462_id); + +static struct i2c_driver adt7462_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "adt7462", + }, + .probe = adt7462_probe, + .remove = adt7462_remove, + .id_table = adt7462_id, + .detect = adt7462_detect, + .address_data = &addr_data, +}; + +/* + * 16-bit registers on the ADT7462 are low-byte first. The data sheet says + * that the low byte must be read before the high byte. + */ +static inline int adt7462_read_word_data(struct i2c_client *client, u8 reg) +{ + u16 foo; + foo = i2c_smbus_read_byte_data(client, reg); + foo |= ((u16)i2c_smbus_read_byte_data(client, reg + 1) << 8); + return foo; +} + +/* For some reason these registers are not contiguous. */ +static int ADT7462_REG_FAN(int fan) +{ + if (fan < 4) + return ADT7462_REG_FAN_BASE_ADDR + (2 * fan); + return ADT7462_REG_FAN2_BASE_ADDR + (2 * (fan - 4)); +} + +/* Voltage registers are scattered everywhere */ +static int ADT7462_REG_VOLT_MAX(struct adt7462_data *data, int which) +{ + switch (which) { + case 0: + if (!(data->pin_cfg[0] & ADT7462_PIN7_INPUT)) + return 0x7C; + break; + case 1: + return 0x69; + case 2: + if (!(data->pin_cfg[1] & ADT7462_PIN22_INPUT)) + return 0x7F; + break; + case 3: + if (!(data->pin_cfg[1] & ADT7462_PIN21_INPUT)) + return 0x7E; + break; + case 4: + if (!(data->pin_cfg[0] & ADT7462_DIODE3_INPUT)) + return 0x4B; + break; + case 5: + if (!(data->pin_cfg[0] & ADT7462_DIODE1_INPUT)) + return 0x49; + break; + case 6: + if (!(data->pin_cfg[1] & ADT7462_PIN13_INPUT)) + return 0x68; + break; + case 7: + if (!(data->pin_cfg[1] & ADT7462_PIN8_INPUT)) + return 0x7D; + break; + case 8: + if (!(data->pin_cfg[2] & ADT7462_PIN26_VOLT_INPUT)) + return 0x6C; + break; + case 9: + if (!(data->pin_cfg[2] & ADT7462_PIN25_VOLT_INPUT)) + return 0x6B; + break; + case 10: + return 0x6A; + case 11: + if (data->pin_cfg[3] >> ADT7462_PIN28_SHIFT == + ADT7462_PIN28_VOLT && + !(data->pin_cfg[0] & ADT7462_VID_INPUT)) + return 0x50; + break; + case 12: + if (data->pin_cfg[3] >> ADT7462_PIN28_SHIFT == + ADT7462_PIN28_VOLT && + !(data->pin_cfg[0] & ADT7462_VID_INPUT)) + return 0x4C; + break; + } + return -ENODEV; +} + +static int ADT7462_REG_VOLT_MIN(struct adt7462_data *data, int which) +{ + switch (which) { + case 0: + if (!(data->pin_cfg[0] & ADT7462_PIN7_INPUT)) + return 0x6D; + break; + case 1: + return 0x72; + case 2: + if (!(data->pin_cfg[1] & ADT7462_PIN22_INPUT)) + return 0x6F; + break; + case 3: + if (!(data->pin_cfg[1] & ADT7462_PIN21_INPUT)) + return 0x71; + break; + case 4: + if (!(data->pin_cfg[0] & ADT7462_DIODE3_INPUT)) + return 0x47; + break; + case 5: + if (!(data->pin_cfg[0] & ADT7462_DIODE1_INPUT)) + return 0x45; + break; + case 6: + if (!(data->pin_cfg[1] & ADT7462_PIN13_INPUT)) + return 0x70; + break; + case 7: + if (!(data->pin_cfg[1] & ADT7462_PIN8_INPUT)) + return 0x6E; + break; + case 8: + if (!(data->pin_cfg[2] & ADT7462_PIN26_VOLT_INPUT)) + return 0x75; + break; + case 9: + if (!(data->pin_cfg[2] & ADT7462_PIN25_VOLT_INPUT)) + return 0x74; + break; + case 10: + return 0x73; + case 11: + if (data->pin_cfg[3] >> ADT7462_PIN28_SHIFT == + ADT7462_PIN28_VOLT && + !(data->pin_cfg[0] & ADT7462_VID_INPUT)) + return 0x76; + break; + case 12: + if (data->pin_cfg[3] >> ADT7462_PIN28_SHIFT == + ADT7462_PIN28_VOLT && + !(data->pin_cfg[0] & ADT7462_VID_INPUT)) + return 0x77; + break; + } + return -ENODEV; +} + +static int ADT7462_REG_VOLT(struct adt7462_data *data, int which) +{ + switch (which) { + case 0: + if (!(data->pin_cfg[0] & ADT7462_PIN7_INPUT)) + return 0xA3; + break; + case 1: + return 0x90; + case 2: + if (!(data->pin_cfg[1] & ADT7462_PIN22_INPUT)) + return 0xA9; + break; + case 3: + if (!(data->pin_cfg[1] & ADT7462_PIN21_INPUT)) + return 0xA7; + break; + case 4: + if (!(data->pin_cfg[0] & ADT7462_DIODE3_INPUT)) + return 0x8F; + break; + case 5: + if (!(data->pin_cfg[0] & ADT7462_DIODE1_INPUT)) + return 0x8B; + break; + case 6: + if (!(data->pin_cfg[1] & ADT7462_PIN13_INPUT)) + return 0x96; + break; + case 7: + if (!(data->pin_cfg[1] & ADT7462_PIN8_INPUT)) + return 0xA5; + break; + case 8: + if (!(data->pin_cfg[2] & ADT7462_PIN26_VOLT_INPUT)) + return 0x93; + break; + case 9: + if (!(data->pin_cfg[2] & ADT7462_PIN25_VOLT_INPUT)) + return 0x92; + break; + case 10: + return 0x91; + case 11: + if (data->pin_cfg[3] >> ADT7462_PIN28_SHIFT == + ADT7462_PIN28_VOLT && + !(data->pin_cfg[0] & ADT7462_VID_INPUT)) + return 0x94; + break; + case 12: + if (data->pin_cfg[3] >> ADT7462_PIN28_SHIFT == + ADT7462_PIN28_VOLT && + !(data->pin_cfg[0] & ADT7462_VID_INPUT)) + return 0x95; + break; + } + return -ENODEV; +} + +/* Provide labels for sysfs */ +static const char *voltage_label(struct adt7462_data *data, int which) +{ + switch (which) { + case 0: + if (!(data->pin_cfg[0] & ADT7462_PIN7_INPUT)) + return "+12V1"; + break; + case 1: + switch (MASK_AND_SHIFT(data->pin_cfg[1], ADT7462_PIN23)) { + case 0: + return "Vccp1"; + case 1: + return "+2.5V"; + case 2: + return "+1.8V"; + case 3: + return "+1.5V"; + } + case 2: + if (!(data->pin_cfg[1] & ADT7462_PIN22_INPUT)) + return "+12V3"; + break; + case 3: + if (!(data->pin_cfg[1] & ADT7462_PIN21_INPUT)) + return "+5V"; + break; + case 4: + if (!(data->pin_cfg[0] & ADT7462_DIODE3_INPUT)) { + if (data->pin_cfg[1] & ADT7462_PIN19_INPUT) + return "+0.9V"; + return "+1.25V"; + } + break; + case 5: + if (!(data->pin_cfg[0] & ADT7462_DIODE1_INPUT)) { + if (data->pin_cfg[1] & ADT7462_PIN19_INPUT) + return "+1.8V"; + return "+2.5V"; + } + break; + case 6: + if (!(data->pin_cfg[1] & ADT7462_PIN13_INPUT)) + return "+3.3V"; + break; + case 7: + if (!(data->pin_cfg[1] & ADT7462_PIN8_INPUT)) + return "+12V2"; + break; + case 8: + switch (MASK_AND_SHIFT(data->pin_cfg[2], ADT7462_PIN26)) { + case 0: + return "Vbatt"; + case 1: + return "FSB_Vtt"; + } + break; + case 9: + switch (MASK_AND_SHIFT(data->pin_cfg[2], ADT7462_PIN25)) { + case 0: + return "+3.3V"; + case 1: + return "+1.2V1"; + } + break; + case 10: + switch (MASK_AND_SHIFT(data->pin_cfg[2], ADT7462_PIN24)) { + case 0: + return "Vccp2"; + case 1: + return "+2.5V"; + case 2: + return "+1.8V"; + case 3: + return "+1.5"; + } + case 11: + if (data->pin_cfg[3] >> ADT7462_PIN28_SHIFT == + ADT7462_PIN28_VOLT && + !(data->pin_cfg[0] & ADT7462_VID_INPUT)) + return "+1.5V ICH"; + break; + case 12: + if (data->pin_cfg[3] >> ADT7462_PIN28_SHIFT == + ADT7462_PIN28_VOLT && + !(data->pin_cfg[0] & ADT7462_VID_INPUT)) + return "+1.5V 3GPIO"; + break; + } + return "N/A"; +} + +/* Multipliers are actually in uV, not mV. */ +static int voltage_multiplier(struct adt7462_data *data, int which) +{ + switch (which) { + case 0: + if (!(data->pin_cfg[0] & ADT7462_PIN7_INPUT)) + return 62500; + break; + case 1: + switch (MASK_AND_SHIFT(data->pin_cfg[1], ADT7462_PIN23)) { + case 0: + if (data->pin_cfg[0] & ADT7462_VID_INPUT) + return 12500; + return 6250; + case 1: + return 13000; + case 2: + return 9400; + case 3: + return 7800; + } + case 2: + if (!(data->pin_cfg[1] & ADT7462_PIN22_INPUT)) + return 62500; + break; + case 3: + if (!(data->pin_cfg[1] & ADT7462_PIN21_INPUT)) + return 26000; + break; + case 4: + if (!(data->pin_cfg[0] & ADT7462_DIODE3_INPUT)) { + if (data->pin_cfg[1] & ADT7462_PIN19_INPUT) + return 4690; + return 6500; + } + break; + case 5: + if (!(data->pin_cfg[0] & ADT7462_DIODE1_INPUT)) { + if (data->pin_cfg[1] & ADT7462_PIN15_INPUT) + return 9400; + return 13000; + } + break; + case 6: + if (!(data->pin_cfg[1] & ADT7462_PIN13_INPUT)) + return 17200; + break; + case 7: + if (!(data->pin_cfg[1] & ADT7462_PIN8_INPUT)) + return 62500; + break; + case 8: + switch (MASK_AND_SHIFT(data->pin_cfg[2], ADT7462_PIN26)) { + case 0: + return 15600; + case 1: + return 6250; + } + break; + case 9: + switch (MASK_AND_SHIFT(data->pin_cfg[2], ADT7462_PIN25)) { + case 0: + return 17200; + case 1: + return 6250; + } + break; + case 10: + switch (MASK_AND_SHIFT(data->pin_cfg[2], ADT7462_PIN24)) { + case 0: + return 6250; + case 1: + return 13000; + case 2: + return 9400; + case 3: + return 7800; + } + case 11: + case 12: + if (data->pin_cfg[3] >> ADT7462_PIN28_SHIFT == + ADT7462_PIN28_VOLT && + !(data->pin_cfg[0] & ADT7462_VID_INPUT)) + return 7800; + } + return 0; +} + +static int temp_enabled(struct adt7462_data *data, int which) +{ + switch (which) { + case 0: + case 2: + return 1; + case 1: + if (data->pin_cfg[0] & ADT7462_DIODE1_INPUT) + return 1; + break; + case 3: + if (data->pin_cfg[0] & ADT7462_DIODE3_INPUT) + return 1; + break; + } + return 0; +} + +static const char *temp_label(struct adt7462_data *data, int which) +{ + switch (which) { + case 0: + return "local"; + case 1: + if (data->pin_cfg[0] & ADT7462_DIODE1_INPUT) + return "remote1"; + break; + case 2: + return "remote2"; + case 3: + if (data->pin_cfg[0] & ADT7462_DIODE3_INPUT) + return "remote3"; + break; + } + return "N/A"; +} + +/* Map Trange register values to mC */ +#define NUM_TRANGE_VALUES 16 +static const int trange_values[NUM_TRANGE_VALUES] = { + 2000, + 2500, + 3300, + 4000, + 5000, + 6700, + 8000, + 10000, + 13300, + 16000, + 20000, + 26700, + 32000, + 40000, + 53300, + 80000 +}; + +static int find_trange_value(int trange) +{ + int i; + + for (i = 0; i < NUM_TRANGE_VALUES; i++) + if (trange_values[i] == trange) + return i; + + return -ENODEV; +} + +static struct adt7462_data *adt7462_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adt7462_data *data = i2c_get_clientdata(client); + unsigned long local_jiffies = jiffies; + int i; + + mutex_lock(&data->lock); + if (time_before(local_jiffies, data->sensors_last_updated + + SENSOR_REFRESH_INTERVAL) + && data->sensors_valid) + goto no_sensor_update; + + for (i = 0; i < ADT7462_TEMP_COUNT; i++) { + /* + * Reading the fractional register locks the integral + * register until both have been read. + */ + data->temp_frac[i] = i2c_smbus_read_byte_data(client, + ADT7462_TEMP_REG(i)); + data->temp[i] = i2c_smbus_read_byte_data(client, + ADT7462_TEMP_REG(i) + 1); + } + + for (i = 0; i < ADT7462_FAN_COUNT; i++) + data->fan[i] = adt7462_read_word_data(client, + ADT7462_REG_FAN(i)); + + data->fan_enabled = i2c_smbus_read_byte_data(client, + ADT7462_REG_FAN_ENABLE); + + for (i = 0; i < ADT7462_PWM_COUNT; i++) + data->pwm[i] = i2c_smbus_read_byte_data(client, + ADT7462_REG_PWM(i)); + + for (i = 0; i < ADT7462_PIN_CFG_REG_COUNT; i++) + data->pin_cfg[i] = i2c_smbus_read_byte_data(client, + ADT7462_REG_PIN_CFG(i)); + + for (i = 0; i < ADT7462_VOLT_COUNT; i++) { + int reg = ADT7462_REG_VOLT(data, i); + if (!reg) + data->voltages[i] = 0; + else + data->voltages[i] = i2c_smbus_read_byte_data(client, + reg); + } + + data->alarms[0] = i2c_smbus_read_byte_data(client, ADT7462_REG_ALARM1); + data->alarms[1] = i2c_smbus_read_byte_data(client, ADT7462_REG_ALARM2); + data->alarms[2] = i2c_smbus_read_byte_data(client, ADT7462_REG_ALARM3); + data->alarms[3] = i2c_smbus_read_byte_data(client, ADT7462_REG_ALARM4); + + data->sensors_last_updated = local_jiffies; + data->sensors_valid = 1; + +no_sensor_update: + if (time_before(local_jiffies, data->limits_last_updated + + LIMIT_REFRESH_INTERVAL) + && data->limits_valid) + goto out; + + for (i = 0; i < ADT7462_TEMP_COUNT; i++) { + data->temp_min[i] = i2c_smbus_read_byte_data(client, + ADT7462_TEMP_MIN_REG(i)); + data->temp_max[i] = i2c_smbus_read_byte_data(client, + ADT7462_TEMP_MAX_REG(i)); + } + + for (i = 0; i < ADT7462_FAN_COUNT; i++) + data->fan_min[i] = i2c_smbus_read_byte_data(client, + ADT7462_REG_FAN_MIN(i)); + + for (i = 0; i < ADT7462_VOLT_COUNT; i++) { + int reg = ADT7462_REG_VOLT_MAX(data, i); + data->volt_max[i] = + (reg ? i2c_smbus_read_byte_data(client, reg) : 0); + + reg = ADT7462_REG_VOLT_MIN(data, i); + data->volt_min[i] = + (reg ? i2c_smbus_read_byte_data(client, reg) : 0); + } + + for (i = 0; i < ADT7462_PWM_COUNT; i++) { + data->pwm_min[i] = i2c_smbus_read_byte_data(client, + ADT7462_REG_PWM_MIN(i)); + data->pwm_tmin[i] = i2c_smbus_read_byte_data(client, + ADT7462_REG_PWM_TMIN(i)); + data->pwm_trange[i] = i2c_smbus_read_byte_data(client, + ADT7462_REG_PWM_TRANGE(i)); + data->pwm_cfg[i] = i2c_smbus_read_byte_data(client, + ADT7462_REG_PWM_CFG(i)); + } + + data->pwm_max = i2c_smbus_read_byte_data(client, ADT7462_REG_PWM_MAX); + + data->cfg2 = i2c_smbus_read_byte_data(client, ADT7462_REG_CFG2); + + data->limits_last_updated = local_jiffies; + data->limits_valid = 1; + +out: + mutex_unlock(&data->lock); + return data; +} + +static ssize_t show_temp_min(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7462_data *data = adt7462_update_device(dev); + + if (!temp_enabled(data, attr->index)) + return sprintf(buf, "0\n"); + + return sprintf(buf, "%d\n", 1000 * (data->temp_min[attr->index] - 64)); +} + +static ssize_t set_temp_min(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct i2c_client *client = to_i2c_client(dev); + struct adt7462_data *data = i2c_get_clientdata(client); + long temp; + + if (strict_strtol(buf, 10, &temp) || !temp_enabled(data, attr->index)) + return -EINVAL; + + temp = ROUND_DIV(temp, 1000) + 64; + temp = SENSORS_LIMIT(temp, 0, 255); + + mutex_lock(&data->lock); + data->temp_min[attr->index] = temp; + i2c_smbus_write_byte_data(client, ADT7462_TEMP_MIN_REG(attr->index), + temp); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_temp_max(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7462_data *data = adt7462_update_device(dev); + + if (!temp_enabled(data, attr->index)) + return sprintf(buf, "0\n"); + + return sprintf(buf, "%d\n", 1000 * (data->temp_max[attr->index] - 64)); +} + +static ssize_t set_temp_max(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct i2c_client *client = to_i2c_client(dev); + struct adt7462_data *data = i2c_get_clientdata(client); + long temp; + + if (strict_strtol(buf, 10, &temp) || !temp_enabled(data, attr->index)) + return -EINVAL; + + temp = ROUND_DIV(temp, 1000) + 64; + temp = SENSORS_LIMIT(temp, 0, 255); + + mutex_lock(&data->lock); + data->temp_max[attr->index] = temp; + i2c_smbus_write_byte_data(client, ADT7462_TEMP_MAX_REG(attr->index), + temp); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_temp(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7462_data *data = adt7462_update_device(dev); + u8 frac = data->temp_frac[attr->index] >> TEMP_FRAC_OFFSET; + + if (!temp_enabled(data, attr->index)) + return sprintf(buf, "0\n"); + + return sprintf(buf, "%d\n", 1000 * (data->temp[attr->index] - 64) + + 250 * frac); +} + +static ssize_t show_temp_label(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7462_data *data = adt7462_update_device(dev); + + return sprintf(buf, "%s\n", temp_label(data, attr->index)); +} + +static ssize_t show_volt_max(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7462_data *data = adt7462_update_device(dev); + int x = voltage_multiplier(data, attr->index); + + x *= data->volt_max[attr->index]; + x /= 1000; /* convert from uV to mV */ + + return sprintf(buf, "%d\n", x); +} + +static ssize_t set_volt_max(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct i2c_client *client = to_i2c_client(dev); + struct adt7462_data *data = i2c_get_clientdata(client); + int x = voltage_multiplier(data, attr->index); + long temp; + + if (strict_strtol(buf, 10, &temp) || !x) + return -EINVAL; + + temp *= 1000; /* convert mV to uV */ + temp = ROUND_DIV(temp, x); + temp = SENSORS_LIMIT(temp, 0, 255); + + mutex_lock(&data->lock); + data->volt_max[attr->index] = temp; + i2c_smbus_write_byte_data(client, + ADT7462_REG_VOLT_MAX(data, attr->index), + temp); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_volt_min(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7462_data *data = adt7462_update_device(dev); + int x = voltage_multiplier(data, attr->index); + + x *= data->volt_min[attr->index]; + x /= 1000; /* convert from uV to mV */ + + return sprintf(buf, "%d\n", x); +} + +static ssize_t set_volt_min(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct i2c_client *client = to_i2c_client(dev); + struct adt7462_data *data = i2c_get_clientdata(client); + int x = voltage_multiplier(data, attr->index); + long temp; + + if (strict_strtol(buf, 10, &temp) || !x) + return -EINVAL; + + temp *= 1000; /* convert mV to uV */ + temp = ROUND_DIV(temp, x); + temp = SENSORS_LIMIT(temp, 0, 255); + + mutex_lock(&data->lock); + data->volt_min[attr->index] = temp; + i2c_smbus_write_byte_data(client, + ADT7462_REG_VOLT_MIN(data, attr->index), + temp); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_voltage(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7462_data *data = adt7462_update_device(dev); + int x = voltage_multiplier(data, attr->index); + + x *= data->voltages[attr->index]; + x /= 1000; /* convert from uV to mV */ + + return sprintf(buf, "%d\n", x); +} + +static ssize_t show_voltage_label(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7462_data *data = adt7462_update_device(dev); + + return sprintf(buf, "%s\n", voltage_label(data, attr->index)); +} + +static ssize_t show_alarm(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7462_data *data = adt7462_update_device(dev); + int reg = attr->index >> ADT7462_ALARM_REG_SHIFT; + int mask = attr->index & ADT7462_ALARM_FLAG_MASK; + + if (data->alarms[reg] & mask) + return sprintf(buf, "1\n"); + else + return sprintf(buf, "0\n"); +} + +static int fan_enabled(struct adt7462_data *data, int fan) +{ + return data->fan_enabled & (1 << fan); +} + +static ssize_t show_fan_min(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7462_data *data = adt7462_update_device(dev); + u16 temp; + + /* Only the MSB of the min fan period is stored... */ + temp = data->fan_min[attr->index]; + temp <<= 8; + + if (!fan_enabled(data, attr->index) || + !FAN_DATA_VALID(temp)) + return sprintf(buf, "0\n"); + + return sprintf(buf, "%d\n", FAN_PERIOD_TO_RPM(temp)); +} + +static ssize_t set_fan_min(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct i2c_client *client = to_i2c_client(dev); + struct adt7462_data *data = i2c_get_clientdata(client); + long temp; + + if (strict_strtol(buf, 10, &temp) || !temp || + !fan_enabled(data, attr->index)) + return -EINVAL; + + temp = FAN_RPM_TO_PERIOD(temp); + temp >>= 8; + temp = SENSORS_LIMIT(temp, 1, 255); + + mutex_lock(&data->lock); + data->fan_min[attr->index] = temp; + i2c_smbus_write_byte_data(client, ADT7462_REG_FAN_MIN(attr->index), + temp); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_fan(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7462_data *data = adt7462_update_device(dev); + + if (!fan_enabled(data, attr->index) || + !FAN_DATA_VALID(data->fan[attr->index])) + return sprintf(buf, "0\n"); + + return sprintf(buf, "%d\n", + FAN_PERIOD_TO_RPM(data->fan[attr->index])); +} + +static ssize_t show_force_pwm_max(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct adt7462_data *data = adt7462_update_device(dev); + return sprintf(buf, "%d\n", (data->cfg2 & ADT7462_FSPD_MASK ? 1 : 0)); +} + +static ssize_t set_force_pwm_max(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adt7462_data *data = i2c_get_clientdata(client); + long temp; + u8 reg; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + mutex_lock(&data->lock); + reg = i2c_smbus_read_byte_data(client, ADT7462_REG_CFG2); + if (temp) + reg |= ADT7462_FSPD_MASK; + else + reg &= ~ADT7462_FSPD_MASK; + data->cfg2 = reg; + i2c_smbus_write_byte_data(client, ADT7462_REG_CFG2, reg); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_pwm(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7462_data *data = adt7462_update_device(dev); + return sprintf(buf, "%d\n", data->pwm[attr->index]); +} + +static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct i2c_client *client = to_i2c_client(dev); + struct adt7462_data *data = i2c_get_clientdata(client); + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + temp = SENSORS_LIMIT(temp, 0, 255); + + mutex_lock(&data->lock); + data->pwm[attr->index] = temp; + i2c_smbus_write_byte_data(client, ADT7462_REG_PWM(attr->index), temp); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_pwm_max(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct adt7462_data *data = adt7462_update_device(dev); + return sprintf(buf, "%d\n", data->pwm_max); +} + +static ssize_t set_pwm_max(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adt7462_data *data = i2c_get_clientdata(client); + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + temp = SENSORS_LIMIT(temp, 0, 255); + + mutex_lock(&data->lock); + data->pwm_max = temp; + i2c_smbus_write_byte_data(client, ADT7462_REG_PWM_MAX, temp); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_pwm_min(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7462_data *data = adt7462_update_device(dev); + return sprintf(buf, "%d\n", data->pwm_min[attr->index]); +} + +static ssize_t set_pwm_min(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct i2c_client *client = to_i2c_client(dev); + struct adt7462_data *data = i2c_get_clientdata(client); + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + temp = SENSORS_LIMIT(temp, 0, 255); + + mutex_lock(&data->lock); + data->pwm_min[attr->index] = temp; + i2c_smbus_write_byte_data(client, ADT7462_REG_PWM_MIN(attr->index), + temp); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_pwm_hyst(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7462_data *data = adt7462_update_device(dev); + return sprintf(buf, "%d\n", 1000 * + (data->pwm_trange[attr->index] & ADT7462_PWM_HYST_MASK)); +} + +static ssize_t set_pwm_hyst(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct i2c_client *client = to_i2c_client(dev); + struct adt7462_data *data = i2c_get_clientdata(client); + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + temp = ROUND_DIV(temp, 1000); + temp = SENSORS_LIMIT(temp, 0, 15); + + /* package things up */ + temp &= ADT7462_PWM_HYST_MASK; + temp |= data->pwm_trange[attr->index] & ADT7462_PWM_RANGE_MASK; + + mutex_lock(&data->lock); + data->pwm_trange[attr->index] = temp; + i2c_smbus_write_byte_data(client, ADT7462_REG_PWM_TRANGE(attr->index), + temp); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_pwm_tmax(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7462_data *data = adt7462_update_device(dev); + + /* tmax = tmin + trange */ + int trange = trange_values[data->pwm_trange[attr->index] >> + ADT7462_PWM_RANGE_SHIFT]; + int tmin = (data->pwm_tmin[attr->index] - 64) * 1000; + + return sprintf(buf, "%d\n", tmin + trange); +} + +static ssize_t set_pwm_tmax(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + int temp; + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct i2c_client *client = to_i2c_client(dev); + struct adt7462_data *data = i2c_get_clientdata(client); + int tmin, trange_value; + long trange; + + if (strict_strtol(buf, 10, &trange)) + return -EINVAL; + + /* trange = tmax - tmin */ + tmin = (data->pwm_tmin[attr->index] - 64) * 1000; + trange_value = find_trange_value(trange - tmin); + + if (trange_value < 0) + return -EINVAL; + + temp = trange_value << ADT7462_PWM_RANGE_SHIFT; + temp |= data->pwm_trange[attr->index] & ADT7462_PWM_HYST_MASK; + + mutex_lock(&data->lock); + data->pwm_trange[attr->index] = temp; + i2c_smbus_write_byte_data(client, ADT7462_REG_PWM_TRANGE(attr->index), + temp); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_pwm_tmin(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7462_data *data = adt7462_update_device(dev); + return sprintf(buf, "%d\n", 1000 * (data->pwm_tmin[attr->index] - 64)); +} + +static ssize_t set_pwm_tmin(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct i2c_client *client = to_i2c_client(dev); + struct adt7462_data *data = i2c_get_clientdata(client); + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + temp = ROUND_DIV(temp, 1000) + 64; + temp = SENSORS_LIMIT(temp, 0, 255); + + mutex_lock(&data->lock); + data->pwm_tmin[attr->index] = temp; + i2c_smbus_write_byte_data(client, ADT7462_REG_PWM_TMIN(attr->index), + temp); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_pwm_auto(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7462_data *data = adt7462_update_device(dev); + int cfg = data->pwm_cfg[attr->index] >> ADT7462_PWM_CHANNEL_SHIFT; + + switch (cfg) { + case 4: /* off */ + return sprintf(buf, "0\n"); + case 7: /* manual */ + return sprintf(buf, "1\n"); + default: /* automatic */ + return sprintf(buf, "2\n"); + } +} + +static void set_pwm_channel(struct i2c_client *client, + struct adt7462_data *data, + int which, + int value) +{ + int temp = data->pwm_cfg[which] & ~ADT7462_PWM_CHANNEL_MASK; + temp |= value << ADT7462_PWM_CHANNEL_SHIFT; + + mutex_lock(&data->lock); + data->pwm_cfg[which] = temp; + i2c_smbus_write_byte_data(client, ADT7462_REG_PWM_CFG(which), temp); + mutex_unlock(&data->lock); +} + +static ssize_t set_pwm_auto(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct i2c_client *client = to_i2c_client(dev); + struct adt7462_data *data = i2c_get_clientdata(client); + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + switch (temp) { + case 0: /* off */ + set_pwm_channel(client, data, attr->index, 4); + return count; + case 1: /* manual */ + set_pwm_channel(client, data, attr->index, 7); + return count; + default: + return -EINVAL; + } +} + +static ssize_t show_pwm_auto_temp(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7462_data *data = adt7462_update_device(dev); + int channel = data->pwm_cfg[attr->index] >> ADT7462_PWM_CHANNEL_SHIFT; + + switch (channel) { + case 0: /* temp[1234] only */ + case 1: + case 2: + case 3: + return sprintf(buf, "%d\n", (1 << channel)); + case 5: /* temp1 & temp4 */ + return sprintf(buf, "9\n"); + case 6: + return sprintf(buf, "15\n"); + default: + return sprintf(buf, "0\n"); + } +} + +static int cvt_auto_temp(int input) +{ + if (input == 0xF) + return 6; + if (input == 0x9) + return 5; + if (input < 1 || !is_power_of_2(input)) + return -EINVAL; + return ilog2(input); +} + +static ssize_t set_pwm_auto_temp(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct i2c_client *client = to_i2c_client(dev); + struct adt7462_data *data = i2c_get_clientdata(client); + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + temp = cvt_auto_temp(temp); + if (temp < 0) + return temp; + + set_pwm_channel(client, data, attr->index, temp); + + return count; +} + +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max, + set_temp_max, 0); +static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp_max, + set_temp_max, 1); +static SENSOR_DEVICE_ATTR(temp3_max, S_IWUSR | S_IRUGO, show_temp_max, + set_temp_max, 2); +static SENSOR_DEVICE_ATTR(temp4_max, S_IWUSR | S_IRUGO, show_temp_max, + set_temp_max, 3); + +static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp_min, + set_temp_min, 0); +static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp_min, + set_temp_min, 1); +static SENSOR_DEVICE_ATTR(temp3_min, S_IWUSR | S_IRUGO, show_temp_min, + set_temp_min, 2); +static SENSOR_DEVICE_ATTR(temp4_min, S_IWUSR | S_IRUGO, show_temp_min, + set_temp_min, 3); + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0); +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1); +static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2); +static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp, NULL, 3); + +static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_temp_label, NULL, 0); +static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, show_temp_label, NULL, 1); +static SENSOR_DEVICE_ATTR(temp3_label, S_IRUGO, show_temp_label, NULL, 2); +static SENSOR_DEVICE_ATTR(temp4_label, S_IRUGO, show_temp_label, NULL, 3); + +static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM1 | ADT7462_LT_ALARM); +static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM1 | ADT7462_R1T_ALARM); +static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM1 | ADT7462_R2T_ALARM); +static SENSOR_DEVICE_ATTR(temp4_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM1 | ADT7462_R3T_ALARM); + +static SENSOR_DEVICE_ATTR(in1_max, S_IWUSR | S_IRUGO, show_volt_max, + set_volt_max, 0); +static SENSOR_DEVICE_ATTR(in2_max, S_IWUSR | S_IRUGO, show_volt_max, + set_volt_max, 1); +static SENSOR_DEVICE_ATTR(in3_max, S_IWUSR | S_IRUGO, show_volt_max, + set_volt_max, 2); +static SENSOR_DEVICE_ATTR(in4_max, S_IWUSR | S_IRUGO, show_volt_max, + set_volt_max, 3); +static SENSOR_DEVICE_ATTR(in5_max, S_IWUSR | S_IRUGO, show_volt_max, + set_volt_max, 4); +static SENSOR_DEVICE_ATTR(in6_max, S_IWUSR | S_IRUGO, show_volt_max, + set_volt_max, 5); +static SENSOR_DEVICE_ATTR(in7_max, S_IWUSR | S_IRUGO, show_volt_max, + set_volt_max, 6); +static SENSOR_DEVICE_ATTR(in8_max, S_IWUSR | S_IRUGO, show_volt_max, + set_volt_max, 7); +static SENSOR_DEVICE_ATTR(in9_max, S_IWUSR | S_IRUGO, show_volt_max, + set_volt_max, 8); +static SENSOR_DEVICE_ATTR(in10_max, S_IWUSR | S_IRUGO, show_volt_max, + set_volt_max, 9); +static SENSOR_DEVICE_ATTR(in11_max, S_IWUSR | S_IRUGO, show_volt_max, + set_volt_max, 10); +static SENSOR_DEVICE_ATTR(in12_max, S_IWUSR | S_IRUGO, show_volt_max, + set_volt_max, 11); +static SENSOR_DEVICE_ATTR(in13_max, S_IWUSR | S_IRUGO, show_volt_max, + set_volt_max, 12); + +static SENSOR_DEVICE_ATTR(in1_min, S_IWUSR | S_IRUGO, show_volt_min, + set_volt_min, 0); +static SENSOR_DEVICE_ATTR(in2_min, S_IWUSR | S_IRUGO, show_volt_min, + set_volt_min, 1); +static SENSOR_DEVICE_ATTR(in3_min, S_IWUSR | S_IRUGO, show_volt_min, + set_volt_min, 2); +static SENSOR_DEVICE_ATTR(in4_min, S_IWUSR | S_IRUGO, show_volt_min, + set_volt_min, 3); +static SENSOR_DEVICE_ATTR(in5_min, S_IWUSR | S_IRUGO, show_volt_min, + set_volt_min, 4); +static SENSOR_DEVICE_ATTR(in6_min, S_IWUSR | S_IRUGO, show_volt_min, + set_volt_min, 5); +static SENSOR_DEVICE_ATTR(in7_min, S_IWUSR | S_IRUGO, show_volt_min, + set_volt_min, 6); +static SENSOR_DEVICE_ATTR(in8_min, S_IWUSR | S_IRUGO, show_volt_min, + set_volt_min, 7); +static SENSOR_DEVICE_ATTR(in9_min, S_IWUSR | S_IRUGO, show_volt_min, + set_volt_min, 8); +static SENSOR_DEVICE_ATTR(in10_min, S_IWUSR | S_IRUGO, show_volt_min, + set_volt_min, 9); +static SENSOR_DEVICE_ATTR(in11_min, S_IWUSR | S_IRUGO, show_volt_min, + set_volt_min, 10); +static SENSOR_DEVICE_ATTR(in12_min, S_IWUSR | S_IRUGO, show_volt_min, + set_volt_min, 11); +static SENSOR_DEVICE_ATTR(in13_min, S_IWUSR | S_IRUGO, show_volt_min, + set_volt_min, 12); + +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_voltage, NULL, 0); +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_voltage, NULL, 1); +static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_voltage, NULL, 2); +static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, show_voltage, NULL, 3); +static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, show_voltage, NULL, 4); +static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, show_voltage, NULL, 5); +static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, show_voltage, NULL, 6); +static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, show_voltage, NULL, 7); +static SENSOR_DEVICE_ATTR(in9_input, S_IRUGO, show_voltage, NULL, 8); +static SENSOR_DEVICE_ATTR(in10_input, S_IRUGO, show_voltage, NULL, 9); +static SENSOR_DEVICE_ATTR(in11_input, S_IRUGO, show_voltage, NULL, 10); +static SENSOR_DEVICE_ATTR(in12_input, S_IRUGO, show_voltage, NULL, 11); +static SENSOR_DEVICE_ATTR(in13_input, S_IRUGO, show_voltage, NULL, 12); + +static SENSOR_DEVICE_ATTR(in1_label, S_IRUGO, show_voltage_label, NULL, 0); +static SENSOR_DEVICE_ATTR(in2_label, S_IRUGO, show_voltage_label, NULL, 1); +static SENSOR_DEVICE_ATTR(in3_label, S_IRUGO, show_voltage_label, NULL, 2); +static SENSOR_DEVICE_ATTR(in4_label, S_IRUGO, show_voltage_label, NULL, 3); +static SENSOR_DEVICE_ATTR(in5_label, S_IRUGO, show_voltage_label, NULL, 4); +static SENSOR_DEVICE_ATTR(in6_label, S_IRUGO, show_voltage_label, NULL, 5); +static SENSOR_DEVICE_ATTR(in7_label, S_IRUGO, show_voltage_label, NULL, 6); +static SENSOR_DEVICE_ATTR(in8_label, S_IRUGO, show_voltage_label, NULL, 7); +static SENSOR_DEVICE_ATTR(in9_label, S_IRUGO, show_voltage_label, NULL, 8); +static SENSOR_DEVICE_ATTR(in10_label, S_IRUGO, show_voltage_label, NULL, 9); +static SENSOR_DEVICE_ATTR(in11_label, S_IRUGO, show_voltage_label, NULL, 10); +static SENSOR_DEVICE_ATTR(in12_label, S_IRUGO, show_voltage_label, NULL, 11); +static SENSOR_DEVICE_ATTR(in13_label, S_IRUGO, show_voltage_label, NULL, 12); + +static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM2 | ADT7462_V0_ALARM); +static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM2 | ADT7462_V7_ALARM); +static SENSOR_DEVICE_ATTR(in3_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM2 | ADT7462_V2_ALARM); +static SENSOR_DEVICE_ATTR(in4_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM2 | ADT7462_V6_ALARM); +static SENSOR_DEVICE_ATTR(in5_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM2 | ADT7462_V5_ALARM); +static SENSOR_DEVICE_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM2 | ADT7462_V4_ALARM); +static SENSOR_DEVICE_ATTR(in7_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM2 | ADT7462_V3_ALARM); +static SENSOR_DEVICE_ATTR(in8_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM2 | ADT7462_V1_ALARM); +static SENSOR_DEVICE_ATTR(in9_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM3 | ADT7462_V10_ALARM); +static SENSOR_DEVICE_ATTR(in10_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM3 | ADT7462_V9_ALARM); +static SENSOR_DEVICE_ATTR(in11_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM3 | ADT7462_V8_ALARM); +static SENSOR_DEVICE_ATTR(in12_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM3 | ADT7462_V11_ALARM); +static SENSOR_DEVICE_ATTR(in13_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM3 | ADT7462_V12_ALARM); + +static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min, + set_fan_min, 0); +static SENSOR_DEVICE_ATTR(fan2_min, S_IWUSR | S_IRUGO, show_fan_min, + set_fan_min, 1); +static SENSOR_DEVICE_ATTR(fan3_min, S_IWUSR | S_IRUGO, show_fan_min, + set_fan_min, 2); +static SENSOR_DEVICE_ATTR(fan4_min, S_IWUSR | S_IRUGO, show_fan_min, + set_fan_min, 3); +static SENSOR_DEVICE_ATTR(fan5_min, S_IWUSR | S_IRUGO, show_fan_min, + set_fan_min, 4); +static SENSOR_DEVICE_ATTR(fan6_min, S_IWUSR | S_IRUGO, show_fan_min, + set_fan_min, 5); +static SENSOR_DEVICE_ATTR(fan7_min, S_IWUSR | S_IRUGO, show_fan_min, + set_fan_min, 6); +static SENSOR_DEVICE_ATTR(fan8_min, S_IWUSR | S_IRUGO, show_fan_min, + set_fan_min, 7); + +static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0); +static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1); +static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2); +static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 3); +static SENSOR_DEVICE_ATTR(fan5_input, S_IRUGO, show_fan, NULL, 4); +static SENSOR_DEVICE_ATTR(fan6_input, S_IRUGO, show_fan, NULL, 5); +static SENSOR_DEVICE_ATTR(fan7_input, S_IRUGO, show_fan, NULL, 6); +static SENSOR_DEVICE_ATTR(fan8_input, S_IRUGO, show_fan, NULL, 7); + +static SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM4 | ADT7462_F0_ALARM); +static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM4 | ADT7462_F1_ALARM); +static SENSOR_DEVICE_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM4 | ADT7462_F2_ALARM); +static SENSOR_DEVICE_ATTR(fan4_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM4 | ADT7462_F3_ALARM); +static SENSOR_DEVICE_ATTR(fan5_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM4 | ADT7462_F4_ALARM); +static SENSOR_DEVICE_ATTR(fan6_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM4 | ADT7462_F5_ALARM); +static SENSOR_DEVICE_ATTR(fan7_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM4 | ADT7462_F6_ALARM); +static SENSOR_DEVICE_ATTR(fan8_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM4 | ADT7462_F7_ALARM); + +static SENSOR_DEVICE_ATTR(force_pwm_max, S_IWUSR | S_IRUGO, + show_force_pwm_max, set_force_pwm_max, 0); + +static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 0); +static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 1); +static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 2); +static SENSOR_DEVICE_ATTR(pwm4, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 3); + +static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IWUSR | S_IRUGO, + show_pwm_min, set_pwm_min, 0); +static SENSOR_DEVICE_ATTR(pwm2_auto_point1_pwm, S_IWUSR | S_IRUGO, + show_pwm_min, set_pwm_min, 1); +static SENSOR_DEVICE_ATTR(pwm3_auto_point1_pwm, S_IWUSR | S_IRUGO, + show_pwm_min, set_pwm_min, 2); +static SENSOR_DEVICE_ATTR(pwm4_auto_point1_pwm, S_IWUSR | S_IRUGO, + show_pwm_min, set_pwm_min, 3); + +static SENSOR_DEVICE_ATTR(pwm1_auto_point2_pwm, S_IWUSR | S_IRUGO, + show_pwm_max, set_pwm_max, 0); +static SENSOR_DEVICE_ATTR(pwm2_auto_point2_pwm, S_IWUSR | S_IRUGO, + show_pwm_max, set_pwm_max, 1); +static SENSOR_DEVICE_ATTR(pwm3_auto_point2_pwm, S_IWUSR | S_IRUGO, + show_pwm_max, set_pwm_max, 2); +static SENSOR_DEVICE_ATTR(pwm4_auto_point2_pwm, S_IWUSR | S_IRUGO, + show_pwm_max, set_pwm_max, 3); + +static SENSOR_DEVICE_ATTR(temp1_auto_point1_hyst, S_IWUSR | S_IRUGO, + show_pwm_hyst, set_pwm_hyst, 0); +static SENSOR_DEVICE_ATTR(temp2_auto_point1_hyst, S_IWUSR | S_IRUGO, + show_pwm_hyst, set_pwm_hyst, 1); +static SENSOR_DEVICE_ATTR(temp3_auto_point1_hyst, S_IWUSR | S_IRUGO, + show_pwm_hyst, set_pwm_hyst, 2); +static SENSOR_DEVICE_ATTR(temp4_auto_point1_hyst, S_IWUSR | S_IRUGO, + show_pwm_hyst, set_pwm_hyst, 3); + +static SENSOR_DEVICE_ATTR(temp1_auto_point2_hyst, S_IWUSR | S_IRUGO, + show_pwm_hyst, set_pwm_hyst, 0); +static SENSOR_DEVICE_ATTR(temp2_auto_point2_hyst, S_IWUSR | S_IRUGO, + show_pwm_hyst, set_pwm_hyst, 1); +static SENSOR_DEVICE_ATTR(temp3_auto_point2_hyst, S_IWUSR | S_IRUGO, + show_pwm_hyst, set_pwm_hyst, 2); +static SENSOR_DEVICE_ATTR(temp4_auto_point2_hyst, S_IWUSR | S_IRUGO, + show_pwm_hyst, set_pwm_hyst, 3); + +static SENSOR_DEVICE_ATTR(temp1_auto_point1_temp, S_IWUSR | S_IRUGO, + show_pwm_tmin, set_pwm_tmin, 0); +static SENSOR_DEVICE_ATTR(temp2_auto_point1_temp, S_IWUSR | S_IRUGO, + show_pwm_tmin, set_pwm_tmin, 1); +static SENSOR_DEVICE_ATTR(temp3_auto_point1_temp, S_IWUSR | S_IRUGO, + show_pwm_tmin, set_pwm_tmin, 2); +static SENSOR_DEVICE_ATTR(temp4_auto_point1_temp, S_IWUSR | S_IRUGO, + show_pwm_tmin, set_pwm_tmin, 3); + +static SENSOR_DEVICE_ATTR(temp1_auto_point2_temp, S_IWUSR | S_IRUGO, + show_pwm_tmax, set_pwm_tmax, 0); +static SENSOR_DEVICE_ATTR(temp2_auto_point2_temp, S_IWUSR | S_IRUGO, + show_pwm_tmax, set_pwm_tmax, 1); +static SENSOR_DEVICE_ATTR(temp3_auto_point2_temp, S_IWUSR | S_IRUGO, + show_pwm_tmax, set_pwm_tmax, 2); +static SENSOR_DEVICE_ATTR(temp4_auto_point2_temp, S_IWUSR | S_IRUGO, + show_pwm_tmax, set_pwm_tmax, 3); + +static SENSOR_DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, show_pwm_auto, + set_pwm_auto, 0); +static SENSOR_DEVICE_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, show_pwm_auto, + set_pwm_auto, 1); +static SENSOR_DEVICE_ATTR(pwm3_enable, S_IWUSR | S_IRUGO, show_pwm_auto, + set_pwm_auto, 2); +static SENSOR_DEVICE_ATTR(pwm4_enable, S_IWUSR | S_IRUGO, show_pwm_auto, + set_pwm_auto, 3); + +static SENSOR_DEVICE_ATTR(pwm1_auto_channels_temp, S_IWUSR | S_IRUGO, + show_pwm_auto_temp, set_pwm_auto_temp, 0); +static SENSOR_DEVICE_ATTR(pwm2_auto_channels_temp, S_IWUSR | S_IRUGO, + show_pwm_auto_temp, set_pwm_auto_temp, 1); +static SENSOR_DEVICE_ATTR(pwm3_auto_channels_temp, S_IWUSR | S_IRUGO, + show_pwm_auto_temp, set_pwm_auto_temp, 2); +static SENSOR_DEVICE_ATTR(pwm4_auto_channels_temp, S_IWUSR | S_IRUGO, + show_pwm_auto_temp, set_pwm_auto_temp, 3); + +static struct attribute *adt7462_attr[] = +{ + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp3_max.dev_attr.attr, + &sensor_dev_attr_temp4_max.dev_attr.attr, + + &sensor_dev_attr_temp1_min.dev_attr.attr, + &sensor_dev_attr_temp2_min.dev_attr.attr, + &sensor_dev_attr_temp3_min.dev_attr.attr, + &sensor_dev_attr_temp4_min.dev_attr.attr, + + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp4_input.dev_attr.attr, + + &sensor_dev_attr_temp1_label.dev_attr.attr, + &sensor_dev_attr_temp2_label.dev_attr.attr, + &sensor_dev_attr_temp3_label.dev_attr.attr, + &sensor_dev_attr_temp4_label.dev_attr.attr, + + &sensor_dev_attr_temp1_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_alarm.dev_attr.attr, + &sensor_dev_attr_temp4_alarm.dev_attr.attr, + + &sensor_dev_attr_in1_max.dev_attr.attr, + &sensor_dev_attr_in2_max.dev_attr.attr, + &sensor_dev_attr_in3_max.dev_attr.attr, + &sensor_dev_attr_in4_max.dev_attr.attr, + &sensor_dev_attr_in5_max.dev_attr.attr, + &sensor_dev_attr_in6_max.dev_attr.attr, + &sensor_dev_attr_in7_max.dev_attr.attr, + &sensor_dev_attr_in8_max.dev_attr.attr, + &sensor_dev_attr_in9_max.dev_attr.attr, + &sensor_dev_attr_in10_max.dev_attr.attr, + &sensor_dev_attr_in11_max.dev_attr.attr, + &sensor_dev_attr_in12_max.dev_attr.attr, + &sensor_dev_attr_in13_max.dev_attr.attr, + + &sensor_dev_attr_in1_min.dev_attr.attr, + &sensor_dev_attr_in2_min.dev_attr.attr, + &sensor_dev_attr_in3_min.dev_attr.attr, + &sensor_dev_attr_in4_min.dev_attr.attr, + &sensor_dev_attr_in5_min.dev_attr.attr, + &sensor_dev_attr_in6_min.dev_attr.attr, + &sensor_dev_attr_in7_min.dev_attr.attr, + &sensor_dev_attr_in8_min.dev_attr.attr, + &sensor_dev_attr_in9_min.dev_attr.attr, + &sensor_dev_attr_in10_min.dev_attr.attr, + &sensor_dev_attr_in11_min.dev_attr.attr, + &sensor_dev_attr_in12_min.dev_attr.attr, + &sensor_dev_attr_in13_min.dev_attr.attr, + + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in3_input.dev_attr.attr, + &sensor_dev_attr_in4_input.dev_attr.attr, + &sensor_dev_attr_in5_input.dev_attr.attr, + &sensor_dev_attr_in6_input.dev_attr.attr, + &sensor_dev_attr_in7_input.dev_attr.attr, + &sensor_dev_attr_in8_input.dev_attr.attr, + &sensor_dev_attr_in9_input.dev_attr.attr, + &sensor_dev_attr_in10_input.dev_attr.attr, + &sensor_dev_attr_in11_input.dev_attr.attr, + &sensor_dev_attr_in12_input.dev_attr.attr, + &sensor_dev_attr_in13_input.dev_attr.attr, + + &sensor_dev_attr_in1_label.dev_attr.attr, + &sensor_dev_attr_in2_label.dev_attr.attr, + &sensor_dev_attr_in3_label.dev_attr.attr, + &sensor_dev_attr_in4_label.dev_attr.attr, + &sensor_dev_attr_in5_label.dev_attr.attr, + &sensor_dev_attr_in6_label.dev_attr.attr, + &sensor_dev_attr_in7_label.dev_attr.attr, + &sensor_dev_attr_in8_label.dev_attr.attr, + &sensor_dev_attr_in9_label.dev_attr.attr, + &sensor_dev_attr_in10_label.dev_attr.attr, + &sensor_dev_attr_in11_label.dev_attr.attr, + &sensor_dev_attr_in12_label.dev_attr.attr, + &sensor_dev_attr_in13_label.dev_attr.attr, + + &sensor_dev_attr_in1_alarm.dev_attr.attr, + &sensor_dev_attr_in2_alarm.dev_attr.attr, + &sensor_dev_attr_in3_alarm.dev_attr.attr, + &sensor_dev_attr_in4_alarm.dev_attr.attr, + &sensor_dev_attr_in5_alarm.dev_attr.attr, + &sensor_dev_attr_in6_alarm.dev_attr.attr, + &sensor_dev_attr_in7_alarm.dev_attr.attr, + &sensor_dev_attr_in8_alarm.dev_attr.attr, + &sensor_dev_attr_in9_alarm.dev_attr.attr, + &sensor_dev_attr_in10_alarm.dev_attr.attr, + &sensor_dev_attr_in11_alarm.dev_attr.attr, + &sensor_dev_attr_in12_alarm.dev_attr.attr, + &sensor_dev_attr_in13_alarm.dev_attr.attr, + + &sensor_dev_attr_fan1_min.dev_attr.attr, + &sensor_dev_attr_fan2_min.dev_attr.attr, + &sensor_dev_attr_fan3_min.dev_attr.attr, + &sensor_dev_attr_fan4_min.dev_attr.attr, + &sensor_dev_attr_fan5_min.dev_attr.attr, + &sensor_dev_attr_fan6_min.dev_attr.attr, + &sensor_dev_attr_fan7_min.dev_attr.attr, + &sensor_dev_attr_fan8_min.dev_attr.attr, + + &sensor_dev_attr_fan1_input.dev_attr.attr, + &sensor_dev_attr_fan2_input.dev_attr.attr, + &sensor_dev_attr_fan3_input.dev_attr.attr, + &sensor_dev_attr_fan4_input.dev_attr.attr, + &sensor_dev_attr_fan5_input.dev_attr.attr, + &sensor_dev_attr_fan6_input.dev_attr.attr, + &sensor_dev_attr_fan7_input.dev_attr.attr, + &sensor_dev_attr_fan8_input.dev_attr.attr, + + &sensor_dev_attr_fan1_alarm.dev_attr.attr, + &sensor_dev_attr_fan2_alarm.dev_attr.attr, + &sensor_dev_attr_fan3_alarm.dev_attr.attr, + &sensor_dev_attr_fan4_alarm.dev_attr.attr, + &sensor_dev_attr_fan5_alarm.dev_attr.attr, + &sensor_dev_attr_fan6_alarm.dev_attr.attr, + &sensor_dev_attr_fan7_alarm.dev_attr.attr, + &sensor_dev_attr_fan8_alarm.dev_attr.attr, + + &sensor_dev_attr_force_pwm_max.dev_attr.attr, + &sensor_dev_attr_pwm1.dev_attr.attr, + &sensor_dev_attr_pwm2.dev_attr.attr, + &sensor_dev_attr_pwm3.dev_attr.attr, + &sensor_dev_attr_pwm4.dev_attr.attr, + + &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr, + &sensor_dev_attr_pwm4_auto_point1_pwm.dev_attr.attr, + + &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point2_pwm.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_point2_pwm.dev_attr.attr, + &sensor_dev_attr_pwm4_auto_point2_pwm.dev_attr.attr, + + &sensor_dev_attr_temp1_auto_point1_hyst.dev_attr.attr, + &sensor_dev_attr_temp2_auto_point1_hyst.dev_attr.attr, + &sensor_dev_attr_temp3_auto_point1_hyst.dev_attr.attr, + &sensor_dev_attr_temp4_auto_point1_hyst.dev_attr.attr, + + &sensor_dev_attr_temp1_auto_point2_hyst.dev_attr.attr, + &sensor_dev_attr_temp2_auto_point2_hyst.dev_attr.attr, + &sensor_dev_attr_temp3_auto_point2_hyst.dev_attr.attr, + &sensor_dev_attr_temp4_auto_point2_hyst.dev_attr.attr, + + &sensor_dev_attr_temp1_auto_point1_temp.dev_attr.attr, + &sensor_dev_attr_temp2_auto_point1_temp.dev_attr.attr, + &sensor_dev_attr_temp3_auto_point1_temp.dev_attr.attr, + &sensor_dev_attr_temp4_auto_point1_temp.dev_attr.attr, + + &sensor_dev_attr_temp1_auto_point2_temp.dev_attr.attr, + &sensor_dev_attr_temp2_auto_point2_temp.dev_attr.attr, + &sensor_dev_attr_temp3_auto_point2_temp.dev_attr.attr, + &sensor_dev_attr_temp4_auto_point2_temp.dev_attr.attr, + + &sensor_dev_attr_pwm1_enable.dev_attr.attr, + &sensor_dev_attr_pwm2_enable.dev_attr.attr, + &sensor_dev_attr_pwm3_enable.dev_attr.attr, + &sensor_dev_attr_pwm4_enable.dev_attr.attr, + + &sensor_dev_attr_pwm1_auto_channels_temp.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_channels_temp.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_channels_temp.dev_attr.attr, + &sensor_dev_attr_pwm4_auto_channels_temp.dev_attr.attr, + NULL +}; + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int adt7462_detect(struct i2c_client *client, int kind, + struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = client->adapter; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + if (kind <= 0) { + int vendor, device, revision; + + vendor = i2c_smbus_read_byte_data(client, ADT7462_REG_VENDOR); + if (vendor != ADT7462_VENDOR) + return -ENODEV; + + device = i2c_smbus_read_byte_data(client, ADT7462_REG_DEVICE); + if (device != ADT7462_DEVICE) + return -ENODEV; + + revision = i2c_smbus_read_byte_data(client, + ADT7462_REG_REVISION); + if (revision != ADT7462_REVISION) + return -ENODEV; + } else + dev_dbg(&adapter->dev, "detection forced\n"); + + strlcpy(info->type, "adt7462", I2C_NAME_SIZE); + + return 0; +} + +static int adt7462_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adt7462_data *data; + int err; + + data = kzalloc(sizeof(struct adt7462_data), GFP_KERNEL); + if (!data) { + err = -ENOMEM; + goto exit; + } + + i2c_set_clientdata(client, data); + mutex_init(&data->lock); + + dev_info(&client->dev, "%s chip found\n", client->name); + + /* Register sysfs hooks */ + data->attrs.attrs = adt7462_attr; + err = sysfs_create_group(&client->dev.kobj, &data->attrs); + if (err) + goto exit_free; + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + err = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &data->attrs); +exit_free: + kfree(data); +exit: + return err; +} + +static int adt7462_remove(struct i2c_client *client) +{ + struct adt7462_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &data->attrs); + kfree(data); + return 0; +} + +static int __init adt7462_init(void) +{ + return i2c_add_driver(&adt7462_driver); +} + +static void __exit adt7462_exit(void) +{ + i2c_del_driver(&adt7462_driver); +} + +MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>"); +MODULE_DESCRIPTION("ADT7462 driver"); +MODULE_LICENSE("GPL"); + +module_init(adt7462_init); +module_exit(adt7462_exit); diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index d368d8f845e1..1311a595147e 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -137,6 +137,8 @@ I2C_CLIENT_INSMOD_1(adt7470); #define FAN_PERIOD_INVALID 65535 #define FAN_DATA_VALID(x) ((x) && (x) != FAN_PERIOD_INVALID) +#define ROUND_DIV(x, divisor) (((x) + ((divisor) / 2)) / (divisor)) + struct adt7470_data { struct device *hwmon_dev; struct attribute_group attrs; @@ -353,7 +355,13 @@ static ssize_t set_temp_min(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adt7470_data *data = i2c_get_clientdata(client); - int temp = simple_strtol(buf, NULL, 10) / 1000; + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + temp = ROUND_DIV(temp, 1000); + temp = SENSORS_LIMIT(temp, 0, 255); mutex_lock(&data->lock); data->temp_min[attr->index] = temp; @@ -381,7 +389,13 @@ static ssize_t set_temp_max(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adt7470_data *data = i2c_get_clientdata(client); - int temp = simple_strtol(buf, NULL, 10) / 1000; + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + temp = ROUND_DIV(temp, 1000); + temp = SENSORS_LIMIT(temp, 0, 255); mutex_lock(&data->lock); data->temp_max[attr->index] = temp; @@ -430,11 +444,13 @@ static ssize_t set_fan_max(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adt7470_data *data = i2c_get_clientdata(client); - int temp = simple_strtol(buf, NULL, 10); + long temp; - if (!temp) + if (strict_strtol(buf, 10, &temp) || !temp) return -EINVAL; + temp = FAN_RPM_TO_PERIOD(temp); + temp = SENSORS_LIMIT(temp, 1, 65534); mutex_lock(&data->lock); data->fan_max[attr->index] = temp; @@ -465,11 +481,13 @@ static ssize_t set_fan_min(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adt7470_data *data = i2c_get_clientdata(client); - int temp = simple_strtol(buf, NULL, 10); + long temp; - if (!temp) + if (strict_strtol(buf, 10, &temp) || !temp) return -EINVAL; + temp = FAN_RPM_TO_PERIOD(temp); + temp = SENSORS_LIMIT(temp, 1, 65534); mutex_lock(&data->lock); data->fan_min[attr->index] = temp; @@ -507,9 +525,12 @@ static ssize_t set_force_pwm_max(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct adt7470_data *data = i2c_get_clientdata(client); - int temp = simple_strtol(buf, NULL, 10); + long temp; u8 reg; + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + mutex_lock(&data->lock); data->force_pwm_max = temp; reg = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG); @@ -537,7 +558,12 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adt7470_data *data = i2c_get_clientdata(client); - int temp = simple_strtol(buf, NULL, 10); + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + temp = SENSORS_LIMIT(temp, 0, 255); mutex_lock(&data->lock); data->pwm[attr->index] = temp; @@ -564,7 +590,12 @@ static ssize_t set_pwm_max(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adt7470_data *data = i2c_get_clientdata(client); - int temp = simple_strtol(buf, NULL, 10); + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + temp = SENSORS_LIMIT(temp, 0, 255); mutex_lock(&data->lock); data->pwm_max[attr->index] = temp; @@ -592,7 +623,12 @@ static ssize_t set_pwm_min(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adt7470_data *data = i2c_get_clientdata(client); - int temp = simple_strtol(buf, NULL, 10); + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + temp = SENSORS_LIMIT(temp, 0, 255); mutex_lock(&data->lock); data->pwm_min[attr->index] = temp; @@ -630,7 +666,13 @@ static ssize_t set_pwm_tmin(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adt7470_data *data = i2c_get_clientdata(client); - int temp = simple_strtol(buf, NULL, 10) / 1000; + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + temp = ROUND_DIV(temp, 1000); + temp = SENSORS_LIMIT(temp, 0, 255); mutex_lock(&data->lock); data->pwm_tmin[attr->index] = temp; @@ -658,11 +700,14 @@ static ssize_t set_pwm_auto(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adt7470_data *data = i2c_get_clientdata(client); - int temp = simple_strtol(buf, NULL, 10); int pwm_auto_reg = ADT7470_REG_PWM_CFG(attr->index); int pwm_auto_reg_mask; + long temp; u8 reg; + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + if (attr->index % 2) pwm_auto_reg_mask = ADT7470_PWM2_AUTO_MASK; else @@ -716,10 +761,14 @@ static ssize_t set_pwm_auto_temp(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adt7470_data *data = i2c_get_clientdata(client); - int temp = cvt_auto_temp(simple_strtol(buf, NULL, 10)); int pwm_auto_reg = ADT7470_REG_PWM_AUTO_TEMP(attr->index); + long temp; u8 reg; + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + temp = cvt_auto_temp(temp); if (temp < 0) return temp; diff --git a/drivers/hwmon/adt7473.c b/drivers/hwmon/adt7473.c index b9a8ea30c99c..18aa30866a6c 100644 --- a/drivers/hwmon/adt7473.c +++ b/drivers/hwmon/adt7473.c @@ -129,6 +129,8 @@ I2C_CLIENT_INSMOD_1(adt7473); #define FAN_PERIOD_INVALID 65535 #define FAN_DATA_VALID(x) ((x) && (x) != FAN_PERIOD_INVALID) +#define ROUND_DIV(x, divisor) (((x) + ((divisor) / 2)) / (divisor)) + struct adt7473_data { struct device *hwmon_dev; struct attribute_group attrs; @@ -357,7 +359,12 @@ static ssize_t set_volt_min(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adt7473_data *data = i2c_get_clientdata(client); - int volt = encode_volt(attr->index, simple_strtol(buf, NULL, 10)); + long volt; + + if (strict_strtol(buf, 10, &volt)) + return -EINVAL; + + volt = encode_volt(attr->index, volt); mutex_lock(&data->lock); data->volt_min[attr->index] = volt; @@ -386,7 +393,12 @@ static ssize_t set_volt_max(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adt7473_data *data = i2c_get_clientdata(client); - int volt = encode_volt(attr->index, simple_strtol(buf, NULL, 10)); + long volt; + + if (strict_strtol(buf, 10, &volt)) + return -EINVAL; + + volt = encode_volt(attr->index, volt); mutex_lock(&data->lock); data->volt_max[attr->index] = volt; @@ -419,7 +431,8 @@ static int decode_temp(u8 twos_complement, u8 raw) static u8 encode_temp(u8 twos_complement, int cooked) { - return twos_complement ? cooked & 0xFF : cooked + 64; + u8 ret = twos_complement ? cooked & 0xFF : cooked + 64; + return SENSORS_LIMIT(ret, 0, 255); } static ssize_t show_temp_min(struct device *dev, @@ -441,7 +454,12 @@ static ssize_t set_temp_min(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adt7473_data *data = i2c_get_clientdata(client); - int temp = simple_strtol(buf, NULL, 10) / 1000; + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + temp = ROUND_DIV(temp, 1000); temp = encode_temp(data->temp_twos_complement, temp); mutex_lock(&data->lock); @@ -472,7 +490,12 @@ static ssize_t set_temp_max(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adt7473_data *data = i2c_get_clientdata(client); - int temp = simple_strtol(buf, NULL, 10) / 1000; + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + temp = ROUND_DIV(temp, 1000); temp = encode_temp(data->temp_twos_complement, temp); mutex_lock(&data->lock); @@ -515,11 +538,13 @@ static ssize_t set_fan_min(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adt7473_data *data = i2c_get_clientdata(client); - int temp = simple_strtol(buf, NULL, 10); + long temp; - if (!temp) + if (strict_strtol(buf, 10, &temp) || !temp) return -EINVAL; + temp = FAN_RPM_TO_PERIOD(temp); + temp = SENSORS_LIMIT(temp, 1, 65534); mutex_lock(&data->lock); data->fan_min[attr->index] = temp; @@ -558,7 +583,10 @@ static ssize_t set_max_duty_at_crit(struct device *dev, u8 reg; struct i2c_client *client = to_i2c_client(dev); struct adt7473_data *data = i2c_get_clientdata(client); - int temp = simple_strtol(buf, NULL, 10); + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; mutex_lock(&data->lock); data->max_duty_at_overheat = !!temp; @@ -587,7 +615,12 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adt7473_data *data = i2c_get_clientdata(client); - int temp = simple_strtol(buf, NULL, 10); + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + temp = SENSORS_LIMIT(temp, 0, 255); mutex_lock(&data->lock); data->pwm[attr->index] = temp; @@ -614,7 +647,12 @@ static ssize_t set_pwm_max(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adt7473_data *data = i2c_get_clientdata(client); - int temp = simple_strtol(buf, NULL, 10); + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + temp = SENSORS_LIMIT(temp, 0, 255); mutex_lock(&data->lock); data->pwm_max[attr->index] = temp; @@ -642,7 +680,12 @@ static ssize_t set_pwm_min(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adt7473_data *data = i2c_get_clientdata(client); - int temp = simple_strtol(buf, NULL, 10); + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + temp = SENSORS_LIMIT(temp, 0, 255); mutex_lock(&data->lock); data->pwm_min[attr->index] = temp; @@ -672,7 +715,12 @@ static ssize_t set_temp_tmax(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adt7473_data *data = i2c_get_clientdata(client); - int temp = simple_strtol(buf, NULL, 10) / 1000; + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + temp = ROUND_DIV(temp, 1000); temp = encode_temp(data->temp_twos_complement, temp); mutex_lock(&data->lock); @@ -703,7 +751,12 @@ static ssize_t set_temp_tmin(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adt7473_data *data = i2c_get_clientdata(client); - int temp = simple_strtol(buf, NULL, 10) / 1000; + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + temp = ROUND_DIV(temp, 1000); temp = encode_temp(data->temp_twos_complement, temp); mutex_lock(&data->lock); @@ -741,7 +794,10 @@ static ssize_t set_pwm_enable(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adt7473_data *data = i2c_get_clientdata(client); - int temp = simple_strtol(buf, NULL, 10); + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; switch (temp) { case 0: @@ -805,7 +861,10 @@ static ssize_t set_pwm_auto_temp(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adt7473_data *data = i2c_get_clientdata(client); - int temp = simple_strtol(buf, NULL, 10); + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; switch (temp) { case 1: diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c index bc011da79e14..f7dce8b9f64b 100644 --- a/drivers/hwmon/applesmc.c +++ b/drivers/hwmon/applesmc.c @@ -116,6 +116,21 @@ static const char* temperature_sensors_sets[][36] = { /* Set 9: Macbook Pro 3,1 (Santa Rosa) */ { "TALP", "TB0T", "TC0D", "TC0P", "TG0D", "TG0H", "TTF0", "TW0P", "Th0H", "Th1H", "Th2H", "Tm0P", "Ts0P", NULL }, +/* Set 10: iMac 5,1 */ + { "TA0P", "TC0D", "TC0P", "TG0D", "TH0P", "TO0P", "Tm0P", NULL }, +/* Set 11: Macbook 5,1 */ + { "TB0T", "TB1T", "TB2T", "TB3T", "TC0D", "TC0P", "TN0D", "TN0P", + "TTF0", "Th0H", "Th1H", "ThFH", "Ts0P", "Ts0S", NULL }, +/* Set 12: Macbook Pro 5,1 */ + { "TB0T", "TB1T", "TB2T", "TB3T", "TC0D", "TC0F", "TC0P", "TG0D", + "TG0F", "TG0H", "TG0P", "TG0T", "TG1H", "TN0D", "TN0P", "TTF0", + "Th2H", "Tm0P", "Ts0P", "Ts0S", NULL }, +/* Set 13: iMac 8,1 */ + { "TA0P", "TC0D", "TC0H", "TC0P", "TG0D", "TG0H", "TG0P", "TH0P", + "TL0P", "TO0P", "TW0P", "Tm0P", "Tp0P", NULL }, +/* Set 14: iMac 6,1 */ + { "TA0P", "TC0D", "TC0H", "TC0P", "TG0D", "TG0H", "TG0P", "TH0P", + "TO0P", "Tp0P", NULL }, }; /* List of keys used to read/write fan speeds */ @@ -1268,7 +1283,7 @@ static __initdata struct dmi_match_data applesmc_dmi_data[] = { { .accelerometer = 0, .light = 0, .temperature_set = 4 }, /* iMac: temperature set 5 */ { .accelerometer = 0, .light = 0, .temperature_set = 5 }, -/* MacBook3: accelerometer and temperature set 6 */ +/* MacBook3, MacBook4: accelerometer and temperature set 6 */ { .accelerometer = 1, .light = 0, .temperature_set = 6 }, /* MacBook Air: accelerometer, backlight and temperature set 7 */ { .accelerometer = 1, .light = 1, .temperature_set = 7 }, @@ -1276,6 +1291,16 @@ static __initdata struct dmi_match_data applesmc_dmi_data[] = { { .accelerometer = 1, .light = 1, .temperature_set = 8 }, /* MacBook Pro 3: accelerometer, backlight and temperature set 9 */ { .accelerometer = 1, .light = 1, .temperature_set = 9 }, +/* iMac 5: light sensor only, temperature set 10 */ + { .accelerometer = 0, .light = 0, .temperature_set = 10 }, +/* MacBook 5: accelerometer, backlight and temperature set 11 */ + { .accelerometer = 1, .light = 1, .temperature_set = 11 }, +/* MacBook Pro 5: accelerometer, backlight and temperature set 12 */ + { .accelerometer = 1, .light = 1, .temperature_set = 12 }, +/* iMac 8: light sensor only, temperature set 13 */ + { .accelerometer = 0, .light = 0, .temperature_set = 13 }, +/* iMac 6: light sensor only, temperature set 14 */ + { .accelerometer = 0, .light = 0, .temperature_set = 14 }, }; /* Note that DMI_MATCH(...,"MacBook") will match "MacBookPro1,1". @@ -1285,6 +1310,10 @@ static __initdata struct dmi_system_id applesmc_whitelist[] = { DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir") }, &applesmc_dmi_data[7]}, + { applesmc_dmi_match, "Apple MacBook Pro 5", { + DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5") }, + &applesmc_dmi_data[12]}, { applesmc_dmi_match, "Apple MacBook Pro 4", { DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro4") }, @@ -1305,6 +1334,14 @@ static __initdata struct dmi_system_id applesmc_whitelist[] = { DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), DMI_MATCH(DMI_PRODUCT_NAME,"MacBook3") }, &applesmc_dmi_data[6]}, + { applesmc_dmi_match, "Apple MacBook 4", { + DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBook4") }, + &applesmc_dmi_data[6]}, + { applesmc_dmi_match, "Apple MacBook 5", { + DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBook5") }, + &applesmc_dmi_data[11]}, { applesmc_dmi_match, "Apple MacBook", { DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), DMI_MATCH(DMI_PRODUCT_NAME,"MacBook") }, @@ -1317,6 +1354,22 @@ static __initdata struct dmi_system_id applesmc_whitelist[] = { DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), DMI_MATCH(DMI_PRODUCT_NAME,"MacPro2") }, &applesmc_dmi_data[4]}, + { applesmc_dmi_match, "Apple MacPro", { + DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), + DMI_MATCH(DMI_PRODUCT_NAME, "MacPro") }, + &applesmc_dmi_data[4]}, + { applesmc_dmi_match, "Apple iMac 8", { + DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), + DMI_MATCH(DMI_PRODUCT_NAME, "iMac8") }, + &applesmc_dmi_data[13]}, + { applesmc_dmi_match, "Apple iMac 6", { + DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), + DMI_MATCH(DMI_PRODUCT_NAME, "iMac6") }, + &applesmc_dmi_data[14]}, + { applesmc_dmi_match, "Apple iMac 5", { + DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), + DMI_MATCH(DMI_PRODUCT_NAME, "iMac5") }, + &applesmc_dmi_data[10]}, { applesmc_dmi_match, "Apple iMac", { DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), DMI_MATCH(DMI_PRODUCT_NAME,"iMac") }, diff --git a/drivers/hwmon/asb100.c b/drivers/hwmon/asb100.c index 8a45a2e6ba8a..8acf82977e7b 100644 --- a/drivers/hwmon/asb100.c +++ b/drivers/hwmon/asb100.c @@ -53,7 +53,10 @@ static const unsigned short normal_i2c[] = { 0x2d, I2C_CLIENT_END }; /* Insmod parameters */ I2C_CLIENT_INSMOD_1(asb100); -I2C_CLIENT_MODULE_PARM(force_subclients, "List of subclient addresses: " + +static unsigned short force_subclients[4]; +module_param_array(force_subclients, short, NULL, 0); +MODULE_PARM_DESC(force_subclients, "List of subclient addresses: " "{bus, clientaddr, subclientaddr1, subclientaddr2}"); /* Voltage IN registers 0-6 */ diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c index 67067e9a323e..bca7610bd6d2 100644 --- a/drivers/hwmon/f71882fg.c +++ b/drivers/hwmon/f71882fg.c @@ -1,6 +1,6 @@ /*************************************************************************** * Copyright (C) 2006 by Hans Edgington <hans@edgington.nl> * - * Copyright (C) 2007 by Hans de Goede <j.w.r.degoede@hhs.nl> * + * Copyright (C) 2007,2008 by Hans de Goede <hdegoede@redhat.com> * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -27,11 +27,11 @@ #include <linux/hwmon-sysfs.h> #include <linux/err.h> #include <linux/mutex.h> -#include <asm/io.h> +#include <linux/io.h> #define DRVNAME "f71882fg" -#define SIO_F71882FG_LD_HWM 0x04 /* Hardware monitor logical device*/ +#define SIO_F71882FG_LD_HWM 0x04 /* Hardware monitor logical device */ #define SIO_UNLOCK_KEY 0x87 /* Key to enable Super-I/O */ #define SIO_LOCK_KEY 0xAA /* Key to diasble Super-I/O */ @@ -43,6 +43,7 @@ #define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */ #define SIO_FINTEK_ID 0x1934 /* Manufacturers ID */ +#define SIO_F71862_ID 0x0601 /* Chipset ID */ #define SIO_F71882_ID 0x0541 /* Chipset ID */ #define REGION_LENGTH 8 @@ -51,12 +52,14 @@ #define F71882FG_REG_PECI 0x0A -#define F71882FG_REG_IN_STATUS 0x12 -#define F71882FG_REG_IN_BEEP 0x13 +#define F71882FG_REG_IN_STATUS 0x12 /* f71882fg only */ +#define F71882FG_REG_IN_BEEP 0x13 /* f71882fg only */ #define F71882FG_REG_IN(nr) (0x20 + (nr)) -#define F71882FG_REG_IN1_HIGH 0x32 +#define F71882FG_REG_IN1_HIGH 0x32 /* f71882fg only */ #define F71882FG_REG_FAN(nr) (0xA0 + (16 * (nr))) +#define F71882FG_REG_FAN_TARGET(nr) (0xA2 + (16 * (nr))) +#define F71882FG_REG_FAN_FULL_SPEED(nr) (0xA4 + (16 * (nr))) #define F71882FG_REG_FAN_STATUS 0x92 #define F71882FG_REG_FAN_BEEP 0x93 @@ -70,6 +73,17 @@ #define F71882FG_REG_TEMP_TYPE 0x6B #define F71882FG_REG_TEMP_DIODE_OPEN 0x6F +#define F71882FG_REG_PWM(nr) (0xA3 + (16 * (nr))) +#define F71882FG_REG_PWM_TYPE 0x94 +#define F71882FG_REG_PWM_ENABLE 0x96 + +#define F71882FG_REG_FAN_HYST0 0x98 +#define F71882FG_REG_FAN_HYST1 0x99 + +#define F71882FG_REG_POINT_PWM(pwm, point) (0xAA + (point) + (16 * (pwm))) +#define F71882FG_REG_POINT_TEMP(pwm, point) (0xA6 + (point) + (16 * (pwm))) +#define F71882FG_REG_POINT_MAPPING(nr) (0xAF + 16 * (nr)) + #define F71882FG_REG_START 0x01 #define FAN_MIN_DETECT 366 /* Lowest detectable fanspeed */ @@ -78,7 +92,20 @@ static unsigned short force_id; module_param(force_id, ushort, 0); MODULE_PARM_DESC(force_id, "Override the detected device ID"); -static struct platform_device *f71882fg_pdev = NULL; +static int fan_mode[4] = { 0, 0, 0, 0 }; +module_param_array(fan_mode, int, NULL, 0644); +MODULE_PARM_DESC(fan_mode, "List of fan control modes (f71882fg only) " + "(0=don't change, 1=pwm, 2=rpm)\n" + "Note: this needs a write to pwm#_enable to take effect"); + +enum chips { f71862fg, f71882fg }; + +static const char *f71882fg_names[] = { + "f71862fg", + "f71882fg", +}; + +static struct platform_device *f71882fg_pdev; /* Super-I/O Function prototypes */ static inline int superio_inb(int base, int reg); @@ -87,8 +114,13 @@ static inline void superio_enter(int base); static inline void superio_select(int base, int ld); static inline void superio_exit(int base); +struct f71882fg_sio_data { + enum chips type; +}; + struct f71882fg_data { unsigned short addr; + enum chips type; struct device *hwmon_dev; struct mutex update_lock; @@ -102,6 +134,8 @@ struct f71882fg_data { u8 in_status; u8 in_beep; u16 fan[4]; + u16 fan_target[4]; + u16 fan_full_speed[4]; u8 fan_status; u8 fan_beep; u8 temp[3]; @@ -112,9 +146,15 @@ struct f71882fg_data { u8 temp_status; u8 temp_beep; u8 temp_diode_open; + u8 pwm[4]; + u8 pwm_enable; + u8 pwm_auto_point_hyst[2]; + u8 pwm_auto_point_mapping[4]; + u8 pwm_auto_point_pwm[4][5]; + u8 pwm_auto_point_temp[4][4]; }; -/* Sysfs in*/ +/* Sysfs in */ static ssize_t show_in(struct device *dev, struct device_attribute *devattr, char *buf); static ssize_t show_in_max(struct device *dev, struct device_attribute @@ -130,6 +170,10 @@ static ssize_t show_in_alarm(struct device *dev, struct device_attribute /* Sysfs Fan */ static ssize_t show_fan(struct device *dev, struct device_attribute *devattr, char *buf); +static ssize_t show_fan_full_speed(struct device *dev, + struct device_attribute *devattr, char *buf); +static ssize_t store_fan_full_speed(struct device *dev, + struct device_attribute *devattr, const char *buf, size_t count); static ssize_t show_fan_beep(struct device *dev, struct device_attribute *devattr, char *buf); static ssize_t store_fan_beep(struct device *dev, struct device_attribute @@ -163,16 +207,41 @@ static ssize_t show_temp_alarm(struct device *dev, struct device_attribute *devattr, char *buf); static ssize_t show_temp_fault(struct device *dev, struct device_attribute *devattr, char *buf); +/* PWM and Auto point control */ +static ssize_t show_pwm(struct device *dev, struct device_attribute *devattr, + char *buf); +static ssize_t store_pwm(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count); +static ssize_t show_pwm_enable(struct device *dev, + struct device_attribute *devattr, char *buf); +static ssize_t store_pwm_enable(struct device *dev, + struct device_attribute *devattr, const char *buf, size_t count); +static ssize_t show_pwm_interpolate(struct device *dev, + struct device_attribute *devattr, char *buf); +static ssize_t store_pwm_interpolate(struct device *dev, + struct device_attribute *devattr, const char *buf, size_t count); +static ssize_t show_pwm_auto_point_channel(struct device *dev, + struct device_attribute *devattr, char *buf); +static ssize_t store_pwm_auto_point_channel(struct device *dev, + struct device_attribute *devattr, const char *buf, size_t count); +static ssize_t show_pwm_auto_point_temp_hyst(struct device *dev, + struct device_attribute *devattr, char *buf); +static ssize_t store_pwm_auto_point_temp_hyst(struct device *dev, + struct device_attribute *devattr, const char *buf, size_t count); +static ssize_t show_pwm_auto_point_pwm(struct device *dev, + struct device_attribute *devattr, char *buf); +static ssize_t store_pwm_auto_point_pwm(struct device *dev, + struct device_attribute *devattr, const char *buf, size_t count); +static ssize_t show_pwm_auto_point_temp(struct device *dev, + struct device_attribute *devattr, char *buf); +static ssize_t store_pwm_auto_point_temp(struct device *dev, + struct device_attribute *devattr, const char *buf, size_t count); /* Sysfs misc */ static ssize_t show_name(struct device *dev, struct device_attribute *devattr, char *buf); static int __devinit f71882fg_probe(struct platform_device * pdev); -static int __devexit f71882fg_remove(struct platform_device *pdev); -static int __init f71882fg_init(void); -static int __init f71882fg_find(int sioaddr, unsigned short *address); -static int __init f71882fg_device_add(unsigned short address); -static void __exit f71882fg_exit(void); +static int f71882fg_remove(struct platform_device *pdev); static struct platform_driver f71882fg_driver = { .driver = { @@ -183,84 +252,329 @@ static struct platform_driver f71882fg_driver = { .remove = __devexit_p(f71882fg_remove), }; -static struct device_attribute f71882fg_dev_attr[] = -{ - __ATTR( name, S_IRUGO, show_name, NULL ), +DEVICE_ATTR(name, S_IRUGO, show_name, NULL); + +static struct sensor_device_attribute_2 f718x2fg_in_temp_attr[] = { + SENSOR_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0), + SENSOR_ATTR_2(in1_input, S_IRUGO, show_in, NULL, 0, 1), + SENSOR_ATTR_2(in2_input, S_IRUGO, show_in, NULL, 0, 2), + SENSOR_ATTR_2(in3_input, S_IRUGO, show_in, NULL, 0, 3), + SENSOR_ATTR_2(in4_input, S_IRUGO, show_in, NULL, 0, 4), + SENSOR_ATTR_2(in5_input, S_IRUGO, show_in, NULL, 0, 5), + SENSOR_ATTR_2(in6_input, S_IRUGO, show_in, NULL, 0, 6), + SENSOR_ATTR_2(in7_input, S_IRUGO, show_in, NULL, 0, 7), + SENSOR_ATTR_2(in8_input, S_IRUGO, show_in, NULL, 0, 8), + SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0), + SENSOR_ATTR_2(temp1_max, S_IRUGO|S_IWUSR, show_temp_max, + store_temp_max, 0, 0), + SENSOR_ATTR_2(temp1_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst, + store_temp_max_hyst, 0, 0), + SENSOR_ATTR_2(temp1_crit, S_IRUGO|S_IWUSR, show_temp_crit, + store_temp_crit, 0, 0), + SENSOR_ATTR_2(temp1_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, + 0, 0), + SENSOR_ATTR_2(temp1_type, S_IRUGO, show_temp_type, NULL, 0, 0), + SENSOR_ATTR_2(temp1_beep, S_IRUGO|S_IWUSR, show_temp_beep, + store_temp_beep, 0, 0), + SENSOR_ATTR_2(temp1_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 0), + SENSOR_ATTR_2(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0, 0), + SENSOR_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 0, 1), + SENSOR_ATTR_2(temp2_max, S_IRUGO|S_IWUSR, show_temp_max, + store_temp_max, 0, 1), + SENSOR_ATTR_2(temp2_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst, + store_temp_max_hyst, 0, 1), + SENSOR_ATTR_2(temp2_crit, S_IRUGO|S_IWUSR, show_temp_crit, + store_temp_crit, 0, 1), + SENSOR_ATTR_2(temp2_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, + 0, 1), + SENSOR_ATTR_2(temp2_type, S_IRUGO, show_temp_type, NULL, 0, 1), + SENSOR_ATTR_2(temp2_beep, S_IRUGO|S_IWUSR, show_temp_beep, + store_temp_beep, 0, 1), + SENSOR_ATTR_2(temp2_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 1), + SENSOR_ATTR_2(temp2_fault, S_IRUGO, show_temp_fault, NULL, 0, 1), + SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 0, 2), + SENSOR_ATTR_2(temp3_max, S_IRUGO|S_IWUSR, show_temp_max, + store_temp_max, 0, 2), + SENSOR_ATTR_2(temp3_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst, + store_temp_max_hyst, 0, 2), + SENSOR_ATTR_2(temp3_crit, S_IRUGO|S_IWUSR, show_temp_crit, + store_temp_crit, 0, 2), + SENSOR_ATTR_2(temp3_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, + 0, 2), + SENSOR_ATTR_2(temp3_type, S_IRUGO, show_temp_type, NULL, 0, 2), + SENSOR_ATTR_2(temp3_beep, S_IRUGO|S_IWUSR, show_temp_beep, + store_temp_beep, 0, 2), + SENSOR_ATTR_2(temp3_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 2), + SENSOR_ATTR_2(temp3_fault, S_IRUGO, show_temp_fault, NULL, 0, 2), +}; + +static struct sensor_device_attribute_2 f71882fg_in_temp_attr[] = { + SENSOR_ATTR_2(in1_max, S_IRUGO|S_IWUSR, show_in_max, store_in_max, + 0, 1), + SENSOR_ATTR_2(in1_beep, S_IRUGO|S_IWUSR, show_in_beep, store_in_beep, + 0, 1), + SENSOR_ATTR_2(in1_alarm, S_IRUGO, show_in_alarm, NULL, 0, 1), +}; + +static struct sensor_device_attribute_2 f718x2fg_fan_attr[] = { + SENSOR_ATTR_2(fan1_input, S_IRUGO, show_fan, NULL, 0, 0), + SENSOR_ATTR_2(fan1_full_speed, S_IRUGO|S_IWUSR, + show_fan_full_speed, + store_fan_full_speed, 0, 0), + SENSOR_ATTR_2(fan1_beep, S_IRUGO|S_IWUSR, show_fan_beep, + store_fan_beep, 0, 0), + SENSOR_ATTR_2(fan1_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 0), + SENSOR_ATTR_2(fan2_input, S_IRUGO, show_fan, NULL, 0, 1), + SENSOR_ATTR_2(fan2_full_speed, S_IRUGO|S_IWUSR, + show_fan_full_speed, + store_fan_full_speed, 0, 1), + SENSOR_ATTR_2(fan2_beep, S_IRUGO|S_IWUSR, show_fan_beep, + store_fan_beep, 0, 1), + SENSOR_ATTR_2(fan2_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 1), + SENSOR_ATTR_2(fan3_input, S_IRUGO, show_fan, NULL, 0, 2), + SENSOR_ATTR_2(fan3_full_speed, S_IRUGO|S_IWUSR, + show_fan_full_speed, + store_fan_full_speed, 0, 2), + SENSOR_ATTR_2(fan3_beep, S_IRUGO|S_IWUSR, show_fan_beep, + store_fan_beep, 0, 2), + SENSOR_ATTR_2(fan3_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 2), + + SENSOR_ATTR_2(pwm1, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 0), + SENSOR_ATTR_2(pwm1_enable, S_IRUGO|S_IWUSR, show_pwm_enable, + store_pwm_enable, 0, 0), + SENSOR_ATTR_2(pwm1_interpolate, S_IRUGO|S_IWUSR, + show_pwm_interpolate, store_pwm_interpolate, 0, 0), + SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_channel, + store_pwm_auto_point_channel, 0, 0), + + SENSOR_ATTR_2(pwm2, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 1), + SENSOR_ATTR_2(pwm2_enable, S_IRUGO|S_IWUSR, show_pwm_enable, + store_pwm_enable, 0, 1), + SENSOR_ATTR_2(pwm2_interpolate, S_IRUGO|S_IWUSR, + show_pwm_interpolate, store_pwm_interpolate, 0, 1), + SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_channel, + store_pwm_auto_point_channel, 0, 1), + + SENSOR_ATTR_2(pwm3, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 2), + SENSOR_ATTR_2(pwm3_enable, S_IRUGO|S_IWUSR, show_pwm_enable, + store_pwm_enable, 0, 2), + SENSOR_ATTR_2(pwm3_interpolate, S_IRUGO|S_IWUSR, + show_pwm_interpolate, store_pwm_interpolate, 0, 2), + SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_channel, + store_pwm_auto_point_channel, 0, 2), }; -static struct sensor_device_attribute f71882fg_in_temp_attr[] = -{ - SENSOR_ATTR(in0_input, S_IRUGO, show_in, NULL, 0), - SENSOR_ATTR(in1_input, S_IRUGO, show_in, NULL, 1), - SENSOR_ATTR(in1_max, S_IRUGO|S_IWUSR, show_in_max, store_in_max, 1), - SENSOR_ATTR(in1_beep, S_IRUGO|S_IWUSR, show_in_beep, store_in_beep, 1), - SENSOR_ATTR(in1_alarm, S_IRUGO, show_in_alarm, NULL, 1), - SENSOR_ATTR(in2_input, S_IRUGO, show_in, NULL, 2), - SENSOR_ATTR(in3_input, S_IRUGO, show_in, NULL, 3), - SENSOR_ATTR(in4_input, S_IRUGO, show_in, NULL, 4), - SENSOR_ATTR(in5_input, S_IRUGO, show_in, NULL, 5), - SENSOR_ATTR(in6_input, S_IRUGO, show_in, NULL, 6), - SENSOR_ATTR(in7_input, S_IRUGO, show_in, NULL, 7), - SENSOR_ATTR(in8_input, S_IRUGO, show_in, NULL, 8), - SENSOR_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0), - SENSOR_ATTR(temp1_max, S_IRUGO|S_IWUSR, show_temp_max, - store_temp_max, 0), - SENSOR_ATTR(temp1_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst, - store_temp_max_hyst, 0), - SENSOR_ATTR(temp1_crit, S_IRUGO|S_IWUSR, show_temp_crit, - store_temp_crit, 0), - SENSOR_ATTR(temp1_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, 0), - SENSOR_ATTR(temp1_type, S_IRUGO, show_temp_type, NULL, 0), - SENSOR_ATTR(temp1_beep, S_IRUGO|S_IWUSR, show_temp_beep, - store_temp_beep, 0), - SENSOR_ATTR(temp1_alarm, S_IRUGO, show_temp_alarm, NULL, 0), - SENSOR_ATTR(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0), - SENSOR_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1), - SENSOR_ATTR(temp2_max, S_IRUGO|S_IWUSR, show_temp_max, - store_temp_max, 1), - SENSOR_ATTR(temp2_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst, - store_temp_max_hyst, 1), - SENSOR_ATTR(temp2_crit, S_IRUGO|S_IWUSR, show_temp_crit, - store_temp_crit, 1), - SENSOR_ATTR(temp2_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, 1), - SENSOR_ATTR(temp2_type, S_IRUGO, show_temp_type, NULL, 1), - SENSOR_ATTR(temp2_beep, S_IRUGO|S_IWUSR, show_temp_beep, - store_temp_beep, 1), - SENSOR_ATTR(temp2_alarm, S_IRUGO, show_temp_alarm, NULL, 1), - SENSOR_ATTR(temp2_fault, S_IRUGO, show_temp_fault, NULL, 1), - SENSOR_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2), - SENSOR_ATTR(temp3_max, S_IRUGO|S_IWUSR, show_temp_max, - store_temp_max, 2), - SENSOR_ATTR(temp3_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst, - store_temp_max_hyst, 2), - SENSOR_ATTR(temp3_crit, S_IRUGO|S_IWUSR, show_temp_crit, - store_temp_crit, 2), - SENSOR_ATTR(temp3_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, 2), - SENSOR_ATTR(temp3_type, S_IRUGO, show_temp_type, NULL, 2), - SENSOR_ATTR(temp3_beep, S_IRUGO|S_IWUSR, show_temp_beep, - store_temp_beep, 2), - SENSOR_ATTR(temp3_alarm, S_IRUGO, show_temp_alarm, NULL, 2), - SENSOR_ATTR(temp3_fault, S_IRUGO, show_temp_fault, NULL, 2) +static struct sensor_device_attribute_2 f71862fg_fan_attr[] = { + SENSOR_ATTR_2(pwm1_auto_point1_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 1, 0), + SENSOR_ATTR_2(pwm1_auto_point2_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 4, 0), + SENSOR_ATTR_2(pwm1_auto_point1_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 0, 0), + SENSOR_ATTR_2(pwm1_auto_point2_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 3, 0), + SENSOR_ATTR_2(pwm1_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp_hyst, + store_pwm_auto_point_temp_hyst, + 0, 0), + SENSOR_ATTR_2(pwm1_auto_point2_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 3, 0), + + SENSOR_ATTR_2(pwm2_auto_point1_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 1, 1), + SENSOR_ATTR_2(pwm2_auto_point2_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 4, 1), + SENSOR_ATTR_2(pwm2_auto_point1_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 0, 1), + SENSOR_ATTR_2(pwm2_auto_point2_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 3, 1), + SENSOR_ATTR_2(pwm2_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp_hyst, + store_pwm_auto_point_temp_hyst, + 0, 1), + SENSOR_ATTR_2(pwm2_auto_point2_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 3, 1), }; -static struct sensor_device_attribute f71882fg_fan_attr[] = -{ - SENSOR_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0), - SENSOR_ATTR(fan1_beep, S_IRUGO|S_IWUSR, show_fan_beep, - store_fan_beep, 0), - SENSOR_ATTR(fan1_alarm, S_IRUGO, show_fan_alarm, NULL, 0), - SENSOR_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1), - SENSOR_ATTR(fan2_beep, S_IRUGO|S_IWUSR, show_fan_beep, - store_fan_beep, 1), - SENSOR_ATTR(fan2_alarm, S_IRUGO, show_fan_alarm, NULL, 1), - SENSOR_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2), - SENSOR_ATTR(fan3_beep, S_IRUGO|S_IWUSR, show_fan_beep, - store_fan_beep, 2), - SENSOR_ATTR(fan3_alarm, S_IRUGO, show_fan_alarm, NULL, 2), - SENSOR_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 3), - SENSOR_ATTR(fan4_beep, S_IRUGO|S_IWUSR, show_fan_beep, - store_fan_beep, 3), - SENSOR_ATTR(fan4_alarm, S_IRUGO, show_fan_alarm, NULL, 3) +static struct sensor_device_attribute_2 f71882fg_fan_attr[] = { + SENSOR_ATTR_2(fan4_input, S_IRUGO, show_fan, NULL, 0, 3), + SENSOR_ATTR_2(fan4_full_speed, S_IRUGO|S_IWUSR, + show_fan_full_speed, + store_fan_full_speed, 0, 3), + SENSOR_ATTR_2(fan4_beep, S_IRUGO|S_IWUSR, show_fan_beep, + store_fan_beep, 0, 3), + SENSOR_ATTR_2(fan4_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 3), + + SENSOR_ATTR_2(pwm1_auto_point1_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 0, 0), + SENSOR_ATTR_2(pwm1_auto_point2_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 1, 0), + SENSOR_ATTR_2(pwm1_auto_point3_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 2, 0), + SENSOR_ATTR_2(pwm1_auto_point4_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 3, 0), + SENSOR_ATTR_2(pwm1_auto_point5_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 4, 0), + SENSOR_ATTR_2(pwm1_auto_point1_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 0, 0), + SENSOR_ATTR_2(pwm1_auto_point2_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 1, 0), + SENSOR_ATTR_2(pwm1_auto_point3_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 2, 0), + SENSOR_ATTR_2(pwm1_auto_point4_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 3, 0), + SENSOR_ATTR_2(pwm1_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp_hyst, + store_pwm_auto_point_temp_hyst, + 0, 0), + SENSOR_ATTR_2(pwm1_auto_point2_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 1, 0), + SENSOR_ATTR_2(pwm1_auto_point3_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 2, 0), + SENSOR_ATTR_2(pwm1_auto_point4_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 3, 0), + + SENSOR_ATTR_2(pwm2_auto_point1_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 0, 1), + SENSOR_ATTR_2(pwm2_auto_point2_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 1, 1), + SENSOR_ATTR_2(pwm2_auto_point3_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 2, 1), + SENSOR_ATTR_2(pwm2_auto_point4_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 3, 1), + SENSOR_ATTR_2(pwm2_auto_point5_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 4, 1), + SENSOR_ATTR_2(pwm2_auto_point1_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 0, 1), + SENSOR_ATTR_2(pwm2_auto_point2_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 1, 1), + SENSOR_ATTR_2(pwm2_auto_point3_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 2, 1), + SENSOR_ATTR_2(pwm2_auto_point4_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 3, 1), + SENSOR_ATTR_2(pwm2_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp_hyst, + store_pwm_auto_point_temp_hyst, + 0, 1), + SENSOR_ATTR_2(pwm2_auto_point2_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 1, 1), + SENSOR_ATTR_2(pwm2_auto_point3_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 2, 1), + SENSOR_ATTR_2(pwm2_auto_point4_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 3, 1), + + SENSOR_ATTR_2(pwm3_auto_point1_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 0, 2), + SENSOR_ATTR_2(pwm3_auto_point2_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 1, 2), + SENSOR_ATTR_2(pwm3_auto_point3_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 2, 2), + SENSOR_ATTR_2(pwm3_auto_point4_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 3, 2), + SENSOR_ATTR_2(pwm3_auto_point5_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 4, 2), + SENSOR_ATTR_2(pwm3_auto_point1_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 0, 2), + SENSOR_ATTR_2(pwm3_auto_point2_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 1, 2), + SENSOR_ATTR_2(pwm3_auto_point3_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 2, 2), + SENSOR_ATTR_2(pwm3_auto_point4_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 3, 2), + SENSOR_ATTR_2(pwm3_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp_hyst, + store_pwm_auto_point_temp_hyst, + 0, 2), + SENSOR_ATTR_2(pwm3_auto_point2_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 1, 2), + SENSOR_ATTR_2(pwm3_auto_point3_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 2, 2), + SENSOR_ATTR_2(pwm3_auto_point4_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 3, 2), + + SENSOR_ATTR_2(pwm4, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 3), + SENSOR_ATTR_2(pwm4_enable, S_IRUGO|S_IWUSR, show_pwm_enable, + store_pwm_enable, 0, 3), + SENSOR_ATTR_2(pwm4_interpolate, S_IRUGO|S_IWUSR, + show_pwm_interpolate, store_pwm_interpolate, 0, 3), + SENSOR_ATTR_2(pwm4_auto_channels_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_channel, + store_pwm_auto_point_channel, 0, 3), + SENSOR_ATTR_2(pwm4_auto_point1_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 0, 3), + SENSOR_ATTR_2(pwm4_auto_point2_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 1, 3), + SENSOR_ATTR_2(pwm4_auto_point3_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 2, 3), + SENSOR_ATTR_2(pwm4_auto_point4_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 3, 3), + SENSOR_ATTR_2(pwm4_auto_point5_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 4, 3), + SENSOR_ATTR_2(pwm4_auto_point1_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 0, 3), + SENSOR_ATTR_2(pwm4_auto_point2_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 1, 3), + SENSOR_ATTR_2(pwm4_auto_point3_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 2, 3), + SENSOR_ATTR_2(pwm4_auto_point4_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 3, 3), + SENSOR_ATTR_2(pwm4_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp_hyst, + store_pwm_auto_point_temp_hyst, + 0, 3), + SENSOR_ATTR_2(pwm4_auto_point2_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 1, 3), + SENSOR_ATTR_2(pwm4_auto_point3_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 2, 3), + SENSOR_ATTR_2(pwm4_auto_point4_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 3, 3), }; @@ -304,6 +618,11 @@ static inline u16 fan_from_reg(u16 reg) return reg ? (1500000 / reg) : 0; } +static inline u16 fan_to_reg(u16 fan) +{ + return fan ? (1500000 / fan) : 0; +} + static u8 f71882fg_read8(struct f71882fg_data *data, u8 reg) { u8 val; @@ -332,18 +651,31 @@ static void f71882fg_write8(struct f71882fg_data *data, u8 reg, u8 val) outb(val, data->addr + DATA_REG_OFFSET); } -static struct f71882fg_data *f71882fg_update_device(struct device * dev) +static void f71882fg_write16(struct f71882fg_data *data, u8 reg, u16 val) +{ + outb(reg++, data->addr + ADDR_REG_OFFSET); + outb(val >> 8, data->addr + DATA_REG_OFFSET); + outb(reg, data->addr + ADDR_REG_OFFSET); + outb(val & 255, data->addr + DATA_REG_OFFSET); +} + +static struct f71882fg_data *f71882fg_update_device(struct device *dev) { struct f71882fg_data *data = dev_get_drvdata(dev); int nr, reg, reg2; + int nr_fans = (data->type == f71862fg) ? 3 : 4; mutex_lock(&data->update_lock); /* Update once every 60 seconds */ if ( time_after(jiffies, data->last_limits + 60 * HZ ) || !data->valid) { - data->in1_max = f71882fg_read8(data, F71882FG_REG_IN1_HIGH); - data->in_beep = f71882fg_read8(data, F71882FG_REG_IN_BEEP); + if (data->type == f71882fg) { + data->in1_max = + f71882fg_read8(data, F71882FG_REG_IN1_HIGH); + data->in_beep = + f71882fg_read8(data, F71882FG_REG_IN_BEEP); + } /* Get High & boundary temps*/ for (nr = 0; nr < 3; nr++) { @@ -378,6 +710,50 @@ static struct f71882fg_data *f71882fg_update_device(struct device * dev) data->fan_beep = f71882fg_read8(data, F71882FG_REG_FAN_BEEP); + data->pwm_enable = f71882fg_read8(data, + F71882FG_REG_PWM_ENABLE); + data->pwm_auto_point_hyst[0] = f71882fg_read8(data, + F71882FG_REG_FAN_HYST0); + data->pwm_auto_point_hyst[1] = f71882fg_read8(data, + F71882FG_REG_FAN_HYST1); + for (nr = 0; nr < nr_fans; nr++) { + data->pwm_auto_point_mapping[nr] = + f71882fg_read8(data, + F71882FG_REG_POINT_MAPPING(nr)); + + if (data->type == f71882fg) { + int point; + for (point = 0; point < 5; point++) { + data->pwm_auto_point_pwm[nr][point] = + f71882fg_read8(data, + F71882FG_REG_POINT_PWM + (nr, point)); + } + for (point = 0; point < 4; point++) { + data->pwm_auto_point_temp[nr][point] = + f71882fg_read8(data, + F71882FG_REG_POINT_TEMP + (nr, point)); + } + } else { + data->pwm_auto_point_pwm[nr][1] = + f71882fg_read8(data, + F71882FG_REG_POINT_PWM + (nr, 1)); + data->pwm_auto_point_pwm[nr][4] = + f71882fg_read8(data, + F71882FG_REG_POINT_PWM + (nr, 4)); + data->pwm_auto_point_temp[nr][0] = + f71882fg_read8(data, + F71882FG_REG_POINT_TEMP + (nr, 0)); + data->pwm_auto_point_temp[nr][3] = + f71882fg_read8(data, + F71882FG_REG_POINT_TEMP + (nr, 3)); + } + } data->last_limits = jiffies; } @@ -393,11 +769,20 @@ static struct f71882fg_data *f71882fg_update_device(struct device * dev) data->fan_status = f71882fg_read8(data, F71882FG_REG_FAN_STATUS); - for (nr = 0; nr < 4; nr++) + for (nr = 0; nr < nr_fans; nr++) { data->fan[nr] = f71882fg_read16(data, F71882FG_REG_FAN(nr)); + data->fan_target[nr] = + f71882fg_read16(data, F71882FG_REG_FAN_TARGET(nr)); + data->fan_full_speed[nr] = + f71882fg_read16(data, + F71882FG_REG_FAN_FULL_SPEED(nr)); + data->pwm[nr] = + f71882fg_read8(data, F71882FG_REG_PWM(nr)); + } - data->in_status = f71882fg_read8(data, + if (data->type == f71882fg) + data->in_status = f71882fg_read8(data, F71882FG_REG_IN_STATUS); for (nr = 0; nr < 9; nr++) data->in[nr] = f71882fg_read8(data, @@ -417,7 +802,7 @@ static ssize_t show_fan(struct device *dev, struct device_attribute *devattr, char *buf) { struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->index; int speed = fan_from_reg(data->fan[nr]); if (speed == FAN_MIN_DETECT) @@ -426,11 +811,45 @@ static ssize_t show_fan(struct device *dev, struct device_attribute *devattr, return sprintf(buf, "%d\n", speed); } +static ssize_t show_fan_full_speed(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + int speed = fan_from_reg(data->fan_full_speed[nr]); + return sprintf(buf, "%d\n", speed); +} + +static ssize_t store_fan_full_speed(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct f71882fg_data *data = dev_get_drvdata(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + long val = simple_strtol(buf, NULL, 10); + + val = SENSORS_LIMIT(val, 23, 1500000); + val = fan_to_reg(val); + + mutex_lock(&data->update_lock); + if (data->pwm_enable & (1 << (2 * nr))) + /* PWM mode */ + count = -EINVAL; + else { + /* RPM mode */ + f71882fg_write16(data, F71882FG_REG_FAN_FULL_SPEED(nr), val); + data->fan_full_speed[nr] = val; + } + mutex_unlock(&data->update_lock); + + return count; +} + static ssize_t show_fan_beep(struct device *dev, struct device_attribute *devattr, char *buf) { struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->index; if (data->fan_beep & (1 << nr)) return sprintf(buf, "1\n"); @@ -442,7 +861,7 @@ static ssize_t store_fan_beep(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { struct f71882fg_data *data = dev_get_drvdata(dev); - int nr = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->index; int val = simple_strtoul(buf, NULL, 10); mutex_lock(&data->update_lock); @@ -461,7 +880,7 @@ static ssize_t show_fan_alarm(struct device *dev, struct device_attribute *devattr, char *buf) { struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->index; if (data->fan_status & (1 << nr)) return sprintf(buf, "1\n"); @@ -473,7 +892,7 @@ static ssize_t show_in(struct device *dev, struct device_attribute *devattr, char *buf) { struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->index; return sprintf(buf, "%d\n", data->in[nr] * 8); } @@ -507,7 +926,7 @@ static ssize_t show_in_beep(struct device *dev, struct device_attribute *devattr, char *buf) { struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->index; if (data->in_beep & (1 << nr)) return sprintf(buf, "1\n"); @@ -519,7 +938,7 @@ static ssize_t store_in_beep(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { struct f71882fg_data *data = dev_get_drvdata(dev); - int nr = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->index; int val = simple_strtoul(buf, NULL, 10); mutex_lock(&data->update_lock); @@ -538,7 +957,7 @@ static ssize_t show_in_alarm(struct device *dev, struct device_attribute *devattr, char *buf) { struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->index; if (data->in_status & (1 << nr)) return sprintf(buf, "1\n"); @@ -550,7 +969,7 @@ static ssize_t show_temp(struct device *dev, struct device_attribute *devattr, char *buf) { struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->index; return sprintf(buf, "%d\n", data->temp[nr] * 1000); } @@ -559,7 +978,7 @@ static ssize_t show_temp_max(struct device *dev, struct device_attribute *devattr, char *buf) { struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->index; return sprintf(buf, "%d\n", data->temp_high[nr] * 1000); } @@ -568,7 +987,7 @@ static ssize_t store_temp_max(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { struct f71882fg_data *data = dev_get_drvdata(dev); - int nr = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->index; int val = simple_strtoul(buf, NULL, 10) / 1000; if (val > 255) @@ -586,7 +1005,7 @@ static ssize_t show_temp_max_hyst(struct device *dev, struct device_attribute *devattr, char *buf) { struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->index; return sprintf(buf, "%d\n", (data->temp_high[nr] - data->temp_hyst[nr]) * 1000); @@ -596,7 +1015,7 @@ static ssize_t store_temp_max_hyst(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { struct f71882fg_data *data = dev_get_drvdata(dev); - int nr = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->index; int val = simple_strtoul(buf, NULL, 10) / 1000; ssize_t ret = count; @@ -636,7 +1055,7 @@ static ssize_t show_temp_crit(struct device *dev, struct device_attribute *devattr, char *buf) { struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->index; return sprintf(buf, "%d\n", data->temp_ovt[nr] * 1000); } @@ -645,7 +1064,7 @@ static ssize_t store_temp_crit(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { struct f71882fg_data *data = dev_get_drvdata(dev); - int nr = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->index; int val = simple_strtoul(buf, NULL, 10) / 1000; if (val > 255) @@ -663,7 +1082,7 @@ static ssize_t show_temp_crit_hyst(struct device *dev, struct device_attribute *devattr, char *buf) { struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->index; return sprintf(buf, "%d\n", (data->temp_ovt[nr] - data->temp_hyst[nr]) * 1000); @@ -673,7 +1092,7 @@ static ssize_t show_temp_type(struct device *dev, struct device_attribute *devattr, char *buf) { struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->index; return sprintf(buf, "%d\n", data->temp_type[nr]); } @@ -682,7 +1101,7 @@ static ssize_t show_temp_beep(struct device *dev, struct device_attribute *devattr, char *buf) { struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->index; if (data->temp_beep & (1 << (nr + 1))) return sprintf(buf, "1\n"); @@ -694,7 +1113,7 @@ static ssize_t store_temp_beep(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { struct f71882fg_data *data = dev_get_drvdata(dev); - int nr = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->index; int val = simple_strtoul(buf, NULL, 10); mutex_lock(&data->update_lock); @@ -713,7 +1132,7 @@ static ssize_t show_temp_alarm(struct device *dev, struct device_attribute *devattr, char *buf) { struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->index; if (data->temp_status & (1 << (nr + 1))) return sprintf(buf, "1\n"); @@ -725,7 +1144,7 @@ static ssize_t show_temp_fault(struct device *dev, struct device_attribute *devattr, char *buf) { struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->index; if (data->temp_diode_open & (1 << (nr + 1))) return sprintf(buf, "1\n"); @@ -733,91 +1152,462 @@ static ssize_t show_temp_fault(struct device *dev, struct device_attribute return sprintf(buf, "0\n"); } +static ssize_t show_pwm(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int val, nr = to_sensor_dev_attr_2(devattr)->index; + if (data->pwm_enable & (1 << (2 * nr))) + /* PWM mode */ + val = data->pwm[nr]; + else { + /* RPM mode */ + mutex_lock(&data->update_lock); + val = 255 * fan_from_reg(data->fan_target[nr]) + / fan_from_reg(data->fan_full_speed[nr]); + mutex_unlock(&data->update_lock); + } + return sprintf(buf, "%d\n", val); +} + +static ssize_t store_pwm(struct device *dev, + struct device_attribute *devattr, const char *buf, + size_t count) +{ + /* struct f71882fg_data *data = dev_get_drvdata(dev); */ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + long val = simple_strtol(buf, NULL, 10); + val = SENSORS_LIMIT(val, 0, 255); + + mutex_lock(&data->update_lock); + if (data->pwm_enable & (1 << (2 * nr))) { + /* PWM mode */ + f71882fg_write8(data, F71882FG_REG_PWM(nr), val); + data->pwm[nr] = val; + } else { + /* RPM mode */ + int target = val * fan_from_reg(data->fan_full_speed[nr]) / 255; + f71882fg_write16(data, F71882FG_REG_FAN_TARGET(nr), + fan_to_reg(target)); + data->fan_target[nr] = fan_to_reg(target); + } + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_pwm_enable(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int result; + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + + if (data->pwm_enable & (2 << (2 * nr))) + result = 1; + else + result = 2; + + return sprintf(buf, "%d\n", result); +} + +static ssize_t store_pwm_enable(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count) +{ + struct f71882fg_data *data = dev_get_drvdata(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + long val = simple_strtol(buf, NULL, 10); + if (val < 1 || val > 2) + return -EINVAL; + + mutex_lock(&data->update_lock); + switch (val) { + case 1: + data->pwm_enable |= 2 << (2 * nr); + break; /* Manual */ + case 2: + data->pwm_enable &= ~(2 << (2 * nr)); + break; /* Temperature ctrl */ + } + if (data->type == f71882fg) { + switch (fan_mode[nr]) { + case 1: + data->pwm_enable |= 1 << (2 * nr); + break; /* Duty cycle mode */ + case 2: + data->pwm_enable &= ~(1 << (2 * nr)); + break; /* RPM mode */ + } + } + f71882fg_write8(data, F71882FG_REG_PWM_ENABLE, data->pwm_enable); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_pwm_auto_point_pwm(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + int result; + struct f71882fg_data *data = f71882fg_update_device(dev); + int pwm = to_sensor_dev_attr_2(devattr)->index; + int point = to_sensor_dev_attr_2(devattr)->nr; + + if (data->pwm_enable & (1 << (2 * pwm))) { + /* PWM mode */ + result = data->pwm_auto_point_pwm[pwm][point]; + } else { + /* RPM mode */ + result = 32 * 255 / (32 + data->pwm_auto_point_pwm[pwm][point]); + } + + return sprintf(buf, "%d\n", result); +} + +static ssize_t store_pwm_auto_point_pwm(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + /* struct f71882fg_data *data = dev_get_drvdata(dev); */ + struct f71882fg_data *data = f71882fg_update_device(dev); + int pwm = to_sensor_dev_attr_2(devattr)->index; + int point = to_sensor_dev_attr_2(devattr)->nr; + int val = simple_strtoul(buf, NULL, 10); + val = SENSORS_LIMIT(val, 0, 255); + + mutex_lock(&data->update_lock); + if (data->pwm_enable & (1 << (2 * pwm))) { + /* PWM mode */ + } else { + /* RPM mode */ + if (val < 29) /* Prevent negative numbers */ + val = 255; + else + val = (255 - val) * 32 / val; + } + f71882fg_write8(data, F71882FG_REG_POINT_PWM(pwm, point), val); + data->pwm_auto_point_pwm[pwm][point] = val; + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_pwm_auto_point_temp_hyst(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + int result = 0; + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + int point = to_sensor_dev_attr_2(devattr)->nr; + + mutex_lock(&data->update_lock); + switch (nr) { + case 0: + result = data->pwm_auto_point_hyst[0] & 0x0f; + break; + case 1: + result = data->pwm_auto_point_hyst[0] >> 4; + break; + case 2: + result = data->pwm_auto_point_hyst[1] & 0x0f; + break; + case 3: + result = data->pwm_auto_point_hyst[1] >> 4; + break; + } + result = 1000 * (data->pwm_auto_point_temp[nr][point] - result); + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%d\n", result); +} + +static ssize_t store_pwm_auto_point_temp_hyst(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + int point = to_sensor_dev_attr_2(devattr)->nr; + long val = simple_strtol(buf, NULL, 10) / 1000; + + mutex_lock(&data->update_lock); + val = SENSORS_LIMIT(val, data->pwm_auto_point_temp[nr][point] - 15, + data->pwm_auto_point_temp[nr][point]); + val = data->pwm_auto_point_temp[nr][point] - val; + + switch (nr) { + case 0: + val = (data->pwm_auto_point_hyst[0] & 0xf0) | val; + break; + case 1: + val = (data->pwm_auto_point_hyst[0] & 0x0f) | (val << 4); + break; + case 2: + val = (data->pwm_auto_point_hyst[1] & 0xf0) | val; + break; + case 3: + val = (data->pwm_auto_point_hyst[1] & 0x0f) | (val << 4); + break; + } + if (nr == 0 || nr == 1) { + f71882fg_write8(data, F71882FG_REG_FAN_HYST0, val); + data->pwm_auto_point_hyst[0] = val; + } else { + f71882fg_write8(data, F71882FG_REG_FAN_HYST1, val); + data->pwm_auto_point_hyst[1] = val; + } + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_pwm_interpolate(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int result; + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + + result = (data->pwm_auto_point_mapping[nr] >> 4) & 1; + + return sprintf(buf, "%d\n", result); +} + +static ssize_t store_pwm_interpolate(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + /* struct f71882fg_data *data = dev_get_drvdata(dev); */ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + int val = simple_strtoul(buf, NULL, 10); + mutex_lock(&data->update_lock); + if (val) + val = data->pwm_auto_point_mapping[nr] | (1 << 4); + else + val = data->pwm_auto_point_mapping[nr] & (~(1 << 4)); + f71882fg_write8(data, F71882FG_REG_POINT_MAPPING(nr), val); + data->pwm_auto_point_mapping[nr] = val; + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_pwm_auto_point_channel(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + int result; + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + + result = 1 << ((data->pwm_auto_point_mapping[nr] & 3) - 1); + + return sprintf(buf, "%d\n", result); +} + +static ssize_t store_pwm_auto_point_channel(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + /* struct f71882fg_data *data = dev_get_drvdata(dev); */ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + long val = simple_strtol(buf, NULL, 10); + switch (val) { + case 1: + val = 1; + break; + case 2: + val = 2; + break; + case 4: + val = 3; + break; + default: + return -EINVAL; + } + mutex_lock(&data->update_lock); + val = (data->pwm_auto_point_mapping[nr] & 0xfc) | val; + f71882fg_write8(data, F71882FG_REG_POINT_MAPPING(nr), val); + data->pwm_auto_point_mapping[nr] = val; + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_pwm_auto_point_temp(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + int result; + struct f71882fg_data *data = f71882fg_update_device(dev); + int pwm = to_sensor_dev_attr_2(devattr)->index; + int point = to_sensor_dev_attr_2(devattr)->nr; + + result = data->pwm_auto_point_temp[pwm][point]; + return sprintf(buf, "%d\n", 1000 * result); +} + +static ssize_t store_pwm_auto_point_temp(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + /* struct f71882fg_data *data = dev_get_drvdata(dev); */ + struct f71882fg_data *data = f71882fg_update_device(dev); + int pwm = to_sensor_dev_attr_2(devattr)->index; + int point = to_sensor_dev_attr_2(devattr)->nr; + long val = simple_strtol(buf, NULL, 10) / 1000; + val = SENSORS_LIMIT(val, 0, 255); + + mutex_lock(&data->update_lock); + f71882fg_write8(data, F71882FG_REG_POINT_TEMP(pwm, point), val); + data->pwm_auto_point_temp[pwm][point] = val; + mutex_unlock(&data->update_lock); + + return count; +} + static ssize_t show_name(struct device *dev, struct device_attribute *devattr, char *buf) { - return sprintf(buf, DRVNAME "\n"); + struct f71882fg_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%s\n", f71882fg_names[data->type]); } +static int __devinit f71882fg_create_sysfs_files(struct platform_device *pdev, + struct sensor_device_attribute_2 *attr, int count) +{ + int err, i; + + for (i = 0; i < count; i++) { + err = device_create_file(&pdev->dev, &attr[i].dev_attr); + if (err) + return err; + } + return 0; +} -static int __devinit f71882fg_probe(struct platform_device * pdev) +static int __devinit f71882fg_probe(struct platform_device *pdev) { struct f71882fg_data *data; - int err, i; + struct f71882fg_sio_data *sio_data = pdev->dev.platform_data; + int err; u8 start_reg; - if (!(data = kzalloc(sizeof(struct f71882fg_data), GFP_KERNEL))) + data = kzalloc(sizeof(struct f71882fg_data), GFP_KERNEL); + if (!data) return -ENOMEM; data->addr = platform_get_resource(pdev, IORESOURCE_IO, 0)->start; + data->type = sio_data->type; mutex_init(&data->update_lock); platform_set_drvdata(pdev, data); + start_reg = f71882fg_read8(data, F71882FG_REG_START); + if (!(start_reg & 0x03)) { + dev_warn(&pdev->dev, "Hardware monitoring not activated\n"); + err = -ENODEV; + goto exit_free; + } + + /* If it is a 71862 and the fan / pwm part is enabled sanity check + the pwm settings */ + if (data->type == f71862fg && (start_reg & 0x02)) { + u8 reg = f71882fg_read8(data, F71882FG_REG_PWM_ENABLE); + if ((reg & 0x15) != 0x15) { + dev_err(&pdev->dev, + "Invalid (reserved) pwm settings: 0x%02x\n", + (unsigned int)reg); + err = -ENODEV; + goto exit_free; + } + } + /* Register sysfs interface files */ - for (i = 0; i < ARRAY_SIZE(f71882fg_dev_attr); i++) { - err = device_create_file(&pdev->dev, &f71882fg_dev_attr[i]); + err = device_create_file(&pdev->dev, &dev_attr_name); + if (err) + goto exit_unregister_sysfs; + + if (start_reg & 0x01) { + err = f71882fg_create_sysfs_files(pdev, f718x2fg_in_temp_attr, + ARRAY_SIZE(f718x2fg_in_temp_attr)); if (err) goto exit_unregister_sysfs; - } - start_reg = f71882fg_read8(data, F71882FG_REG_START); - if (start_reg & 0x01) { - for (i = 0; i < ARRAY_SIZE(f71882fg_in_temp_attr); i++) { - err = device_create_file(&pdev->dev, - &f71882fg_in_temp_attr[i].dev_attr); + if (data->type == f71882fg) { + err = f71882fg_create_sysfs_files(pdev, + f71882fg_in_temp_attr, + ARRAY_SIZE(f71882fg_in_temp_attr)); if (err) goto exit_unregister_sysfs; } } if (start_reg & 0x02) { - for (i = 0; i < ARRAY_SIZE(f71882fg_fan_attr); i++) { - err = device_create_file(&pdev->dev, - &f71882fg_fan_attr[i].dev_attr); - if (err) - goto exit_unregister_sysfs; + err = f71882fg_create_sysfs_files(pdev, f718x2fg_fan_attr, + ARRAY_SIZE(f718x2fg_fan_attr)); + if (err) + goto exit_unregister_sysfs; + + if (data->type == f71862fg) { + err = f71882fg_create_sysfs_files(pdev, + f71862fg_fan_attr, + ARRAY_SIZE(f71862fg_fan_attr)); + } else { + err = f71882fg_create_sysfs_files(pdev, + f71882fg_fan_attr, + ARRAY_SIZE(f71882fg_fan_attr)); } + if (err) + goto exit_unregister_sysfs; } data->hwmon_dev = hwmon_device_register(&pdev->dev); if (IS_ERR(data->hwmon_dev)) { err = PTR_ERR(data->hwmon_dev); + data->hwmon_dev = NULL; goto exit_unregister_sysfs; } return 0; exit_unregister_sysfs: - for (i = 0; i < ARRAY_SIZE(f71882fg_dev_attr); i++) - device_remove_file(&pdev->dev, &f71882fg_dev_attr[i]); - - for (i = 0; i < ARRAY_SIZE(f71882fg_in_temp_attr); i++) - device_remove_file(&pdev->dev, - &f71882fg_in_temp_attr[i].dev_attr); - - for (i = 0; i < ARRAY_SIZE(f71882fg_fan_attr); i++) - device_remove_file(&pdev->dev, &f71882fg_fan_attr[i].dev_attr); - + f71882fg_remove(pdev); /* Will unregister the sysfs files for us */ + return err; /* f71882fg_remove() also frees our data */ +exit_free: kfree(data); - return err; } -static int __devexit f71882fg_remove(struct platform_device *pdev) +static int f71882fg_remove(struct platform_device *pdev) { int i; struct f71882fg_data *data = platform_get_drvdata(pdev); platform_set_drvdata(pdev, NULL); - hwmon_device_unregister(data->hwmon_dev); + if (data->hwmon_dev) + hwmon_device_unregister(data->hwmon_dev); - for (i = 0; i < ARRAY_SIZE(f71882fg_dev_attr); i++) - device_remove_file(&pdev->dev, &f71882fg_dev_attr[i]); + device_remove_file(&pdev->dev, &dev_attr_name); + + for (i = 0; i < ARRAY_SIZE(f718x2fg_in_temp_attr); i++) + device_remove_file(&pdev->dev, + &f718x2fg_in_temp_attr[i].dev_attr); for (i = 0; i < ARRAY_SIZE(f71882fg_in_temp_attr); i++) device_remove_file(&pdev->dev, &f71882fg_in_temp_attr[i].dev_attr); + for (i = 0; i < ARRAY_SIZE(f718x2fg_fan_attr); i++) + device_remove_file(&pdev->dev, &f718x2fg_fan_attr[i].dev_attr); + + for (i = 0; i < ARRAY_SIZE(f71862fg_fan_attr); i++) + device_remove_file(&pdev->dev, &f71862fg_fan_attr[i].dev_attr); + for (i = 0; i < ARRAY_SIZE(f71882fg_fan_attr); i++) device_remove_file(&pdev->dev, &f71882fg_fan_attr[i].dev_attr); @@ -826,12 +1616,11 @@ static int __devexit f71882fg_remove(struct platform_device *pdev) return 0; } -static int __init f71882fg_find(int sioaddr, unsigned short *address) +static int __init f71882fg_find(int sioaddr, unsigned short *address, + struct f71882fg_sio_data *sio_data) { int err = -ENODEV; u16 devid; - u8 start_reg; - struct f71882fg_data data; superio_enter(sioaddr); @@ -842,7 +1631,14 @@ static int __init f71882fg_find(int sioaddr, unsigned short *address) } devid = force_id ? force_id : superio_inw(sioaddr, SIO_REG_DEVID); - if (devid != SIO_F71882_ID) { + switch (devid) { + case SIO_F71862_ID: + sio_data->type = f71862fg; + break; + case SIO_F71882_ID: + sio_data->type = f71882fg; + break; + default: printk(KERN_INFO DRVNAME ": Unsupported Fintek device\n"); goto exit; } @@ -861,24 +1657,17 @@ static int __init f71882fg_find(int sioaddr, unsigned short *address) } *address &= ~(REGION_LENGTH - 1); /* Ignore 3 LSB */ - data.addr = *address; - start_reg = f71882fg_read8(&data, F71882FG_REG_START); - if (!(start_reg & 0x03)) { - printk(KERN_WARNING DRVNAME - ": Hardware monitoring not activated\n"); - goto exit; - } - err = 0; - printk(KERN_INFO DRVNAME ": Found F71882FG chip at %#x, revision %d\n", - (unsigned int)*address, + printk(KERN_INFO DRVNAME ": Found %s chip at %#x, revision %d\n", + f71882fg_names[sio_data->type], (unsigned int)*address, (int)superio_inb(sioaddr, SIO_REG_DEVREV)); exit: superio_exit(sioaddr); return err; } -static int __init f71882fg_device_add(unsigned short address) +static int __init f71882fg_device_add(unsigned short address, + const struct f71882fg_sio_data *sio_data) { struct resource res = { .start = address, @@ -898,6 +1687,13 @@ static int __init f71882fg_device_add(unsigned short address) goto exit_device_put; } + err = platform_device_add_data(f71882fg_pdev, sio_data, + sizeof(struct f71882fg_sio_data)); + if (err) { + printk(KERN_ERR DRVNAME ": Platform data allocation failed\n"); + goto exit_device_put; + } + err = platform_device_add(f71882fg_pdev); if (err) { printk(KERN_ERR DRVNAME ": Device addition failed\n"); @@ -916,14 +1712,20 @@ static int __init f71882fg_init(void) { int err = -ENODEV; unsigned short address; + struct f71882fg_sio_data sio_data; + + memset(&sio_data, 0, sizeof(sio_data)); - if (f71882fg_find(0x2e, &address) && f71882fg_find(0x4e, &address)) + if (f71882fg_find(0x2e, &address, &sio_data) && + f71882fg_find(0x4e, &address, &sio_data)) goto exit; - if ((err = platform_driver_register(&f71882fg_driver))) + err = platform_driver_register(&f71882fg_driver); + if (err) goto exit; - if ((err = f71882fg_device_add(address))) + err = f71882fg_device_add(address, &sio_data); + if (err) goto exit_driver; return 0; @@ -941,7 +1743,7 @@ static void __exit f71882fg_exit(void) } MODULE_DESCRIPTION("F71882FG Hardware Monitoring Driver"); -MODULE_AUTHOR("Hans Edgington (hans@edgington.nl)"); +MODULE_AUTHOR("Hans Edgington, Hans de Goede (hdegoede@redhat.com)"); MODULE_LICENSE("GPL"); module_init(f71882fg_init); diff --git a/drivers/hwmon/fschmd.c b/drivers/hwmon/fschmd.c index 967170368933..c188b7135025 100644 --- a/drivers/hwmon/fschmd.c +++ b/drivers/hwmon/fschmd.c @@ -1,6 +1,6 @@ /* fschmd.c * - * Copyright (C) 2007 Hans de Goede <j.w.r.degoede@hhs.nl> + * Copyright (C) 2007,2008 Hans de Goede <hdegoede@redhat.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -63,7 +63,7 @@ I2C_CLIENT_INSMOD_5(fscpos, fscher, fscscy, fschrc, fschmd); #define FSCHMD_REG_EVENT_STATE 0x04 #define FSCHMD_REG_CONTROL 0x05 -#define FSCHMD_CONTROL_ALERT_LED_MASK 0x01 +#define FSCHMD_CONTROL_ALERT_LED 0x01 /* watchdog (support to be implemented) */ #define FSCHMD_REG_WDOG_PRESET 0x28 @@ -115,8 +115,8 @@ static const u8 FSCHMD_REG_FAN_RIPPLE[5][6] = { static const int FSCHMD_NO_FAN_SENSORS[5] = { 3, 3, 6, 4, 5 }; /* Fan status register bitmasks */ -#define FSCHMD_FAN_ALARM_MASK 0x04 /* called fault by FSC! */ -#define FSCHMD_FAN_NOT_PRESENT_MASK 0x08 /* not documented */ +#define FSCHMD_FAN_ALARM 0x04 /* called fault by FSC! */ +#define FSCHMD_FAN_NOT_PRESENT 0x08 /* not documented */ /* actual temperature registers */ @@ -158,14 +158,11 @@ static const u8 FSCHER_REG_TEMP_AUTOP2[] = { 0x75, 0x85, 0x95 }; */ static const int FSCHMD_NO_TEMP_SENSORS[5] = { 3, 3, 4, 3, 5 }; /* temp status register bitmasks */ -#define FSCHMD_TEMP_WORKING_MASK 0x01 -#define FSCHMD_TEMP_ALERT_MASK 0x02 +#define FSCHMD_TEMP_WORKING 0x01 +#define FSCHMD_TEMP_ALERT 0x02 /* there only really is an alarm if the sensor is working and alert == 1 */ #define FSCHMD_TEMP_ALARM_MASK \ - (FSCHMD_TEMP_WORKING_MASK | FSCHMD_TEMP_ALERT_MASK) - -/* our driver name */ -#define FSCHMD_NAME "fschmd" + (FSCHMD_TEMP_WORKING | FSCHMD_TEMP_ALERT) /* * Functions declarations @@ -195,7 +192,7 @@ MODULE_DEVICE_TABLE(i2c, fschmd_id); static struct i2c_driver fschmd_driver = { .class = I2C_CLASS_HWMON, .driver = { - .name = FSCHMD_NAME, + .name = "fschmd", }, .probe = fschmd_probe, .remove = fschmd_remove, @@ -300,7 +297,7 @@ static ssize_t show_temp_fault(struct device *dev, struct fschmd_data *data = fschmd_update_device(dev); /* bit 0 set means sensor working ok, so no fault! */ - if (data->temp_status[index] & FSCHMD_TEMP_WORKING_MASK) + if (data->temp_status[index] & FSCHMD_TEMP_WORKING) return sprintf(buf, "0\n"); else return sprintf(buf, "1\n"); @@ -385,7 +382,7 @@ static ssize_t show_fan_alarm(struct device *dev, int index = to_sensor_dev_attr(devattr)->index; struct fschmd_data *data = fschmd_update_device(dev); - if (data->fan_status[index] & FSCHMD_FAN_ALARM_MASK) + if (data->fan_status[index] & FSCHMD_FAN_ALARM) return sprintf(buf, "1\n"); else return sprintf(buf, "0\n"); @@ -397,7 +394,7 @@ static ssize_t show_fan_fault(struct device *dev, int index = to_sensor_dev_attr(devattr)->index; struct fschmd_data *data = fschmd_update_device(dev); - if (data->fan_status[index] & FSCHMD_FAN_NOT_PRESENT_MASK) + if (data->fan_status[index] & FSCHMD_FAN_NOT_PRESENT) return sprintf(buf, "1\n"); else return sprintf(buf, "0\n"); @@ -449,7 +446,7 @@ static ssize_t show_alert_led(struct device *dev, { struct fschmd_data *data = fschmd_update_device(dev); - if (data->global_control & FSCHMD_CONTROL_ALERT_LED_MASK) + if (data->global_control & FSCHMD_CONTROL_ALERT_LED) return sprintf(buf, "1\n"); else return sprintf(buf, "0\n"); @@ -467,9 +464,9 @@ static ssize_t store_alert_led(struct device *dev, reg = i2c_smbus_read_byte_data(to_i2c_client(dev), FSCHMD_REG_CONTROL); if (v) - reg |= FSCHMD_CONTROL_ALERT_LED_MASK; + reg |= FSCHMD_CONTROL_ALERT_LED; else - reg &= ~FSCHMD_CONTROL_ALERT_LED_MASK; + reg &= ~FSCHMD_CONTROL_ALERT_LED; i2c_smbus_write_byte_data(to_i2c_client(dev), FSCHMD_REG_CONTROL, reg); @@ -683,11 +680,11 @@ static int fschmd_probe(struct i2c_client *client, } /* Read the special DMI table for fscher and newer chips */ - if (kind == fscher || kind >= fschrc) { + if ((kind == fscher || kind >= fschrc) && dmi_vref == -1) { dmi_walk(fschmd_dmi_decode); if (dmi_vref == -1) { - printk(KERN_WARNING FSCHMD_NAME - ": Couldn't get voltage scaling factors from " + dev_warn(&client->dev, + "Couldn't get voltage scaling factors from " "BIOS DMI table, using builtin defaults\n"); dmi_vref = 33; } @@ -736,7 +733,7 @@ static int fschmd_probe(struct i2c_client *client, } revision = i2c_smbus_read_byte_data(client, FSCHMD_REG_REVISION); - printk(KERN_INFO FSCHMD_NAME ": Detected FSC %s chip, revision: %d\n", + dev_info(&client->dev, "Detected FSC %s chip, revision: %d\n", names[data->kind], (int) revision); return 0; @@ -798,7 +795,7 @@ static struct fschmd_data *fschmd_update_device(struct device *dev) data->temp_act[i] < data->temp_max[i]) i2c_smbus_write_byte_data(client, FSCHMD_REG_TEMP_STATE[data->kind][i], - FSCHMD_TEMP_ALERT_MASK); + FSCHMD_TEMP_ALERT); } for (i = 0; i < FSCHMD_NO_FAN_SENSORS[data->kind]; i++) { @@ -816,11 +813,11 @@ static struct fschmd_data *fschmd_update_device(struct device *dev) FSCHMD_REG_FAN_MIN[data->kind][i]); /* reset fan status if speed is back to > 0 */ - if ((data->fan_status[i] & FSCHMD_FAN_ALARM_MASK) && + if ((data->fan_status[i] & FSCHMD_FAN_ALARM) && data->fan_act[i]) i2c_smbus_write_byte_data(client, FSCHMD_REG_FAN_STATE[data->kind][i], - FSCHMD_FAN_ALARM_MASK); + FSCHMD_FAN_ALARM); } for (i = 0; i < 3; i++) @@ -857,7 +854,7 @@ static void __exit fschmd_exit(void) i2c_del_driver(&fschmd_driver); } -MODULE_AUTHOR("Hans de Goede <j.w.r.degoede@hhs.nl>"); +MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); MODULE_DESCRIPTION("FSC Poseidon, Hermes, Scylla, Heracles and " "Heimdall driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index 076a59cdabe9..e15c3e7b07e9 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -76,7 +76,7 @@ void hwmon_device_unregister(struct device *dev) { int id; - if (likely(sscanf(dev->bus_id, HWMON_ID_FORMAT, &id) == 1)) { + if (likely(sscanf(dev_name(dev), HWMON_ID_FORMAT, &id) == 1)) { device_unregister(dev); spin_lock(&idr_lock); idr_remove(&hwmon_idr, id); diff --git a/drivers/hwmon/i5k_amb.c b/drivers/hwmon/i5k_amb.c index 2ede9388096b..27d7f72a5f11 100644 --- a/drivers/hwmon/i5k_amb.c +++ b/drivers/hwmon/i5k_amb.c @@ -490,6 +490,13 @@ static unsigned long chipset_ids[] = { 0 }; +static struct pci_device_id i5k_amb_ids[] __devinitdata = { + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5000_ERR) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5400_ERR) }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, i5k_amb_ids); + static int __devinit i5k_amb_probe(struct platform_device *pdev) { struct i5k_amb_data *data; diff --git a/drivers/hwmon/ibmaem.c b/drivers/hwmon/ibmaem.c index 7b0ed5dea399..fe74609a7feb 100644 --- a/drivers/hwmon/ibmaem.c +++ b/drivers/hwmon/ibmaem.c @@ -88,9 +88,11 @@ static DEFINE_IDR(aem_idr); static DEFINE_SPINLOCK(aem_idr_lock); -static struct device_driver aem_driver = { - .name = DRVNAME, - .bus = &platform_bus_type, +static struct platform_driver aem_driver = { + .driver = { + .name = DRVNAME, + .bus = &platform_bus_type, + } }; struct aem_ipmi_data { @@ -583,7 +585,7 @@ static int aem_init_aem1_inst(struct aem_ipmi_data *probe, u8 module_handle) data->pdev = platform_device_alloc(DRVNAME, data->id); if (!data->pdev) goto dev_err; - data->pdev->dev.driver = &aem_driver; + data->pdev->dev.driver = &aem_driver.driver; res = platform_device_add(data->pdev); if (res) @@ -716,7 +718,7 @@ static int aem_init_aem2_inst(struct aem_ipmi_data *probe, data->pdev = platform_device_alloc(DRVNAME, data->id); if (!data->pdev) goto dev_err; - data->pdev->dev.driver = &aem_driver; + data->pdev->dev.driver = &aem_driver.driver; res = platform_device_add(data->pdev); if (res) @@ -1085,7 +1087,7 @@ static int __init aem_init(void) { int res; - res = driver_register(&aem_driver); + res = driver_register(&aem_driver.driver); if (res) { printk(KERN_ERR "Can't register aem driver\n"); return res; @@ -1097,7 +1099,7 @@ static int __init aem_init(void) return 0; ipmi_reg_err: - driver_unregister(&aem_driver); + driver_unregister(&aem_driver.driver); return res; } @@ -1107,7 +1109,7 @@ static void __exit aem_exit(void) struct aem_data *p1, *next1; ipmi_smi_watcher_unregister(&driver_data.bmc_events); - driver_unregister(&aem_driver); + driver_unregister(&aem_driver.driver); list_for_each_entry_safe(p1, next1, &driver_data.aem_devices, list) aem_delete(p1); } diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index b74c95735f95..0e0d692f0c9e 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -14,6 +14,7 @@ IT8712F Super I/O chip w/LPC interface IT8716F Super I/O chip w/LPC interface IT8718F Super I/O chip w/LPC interface + IT8720F Super I/O chip w/LPC interface IT8726F Super I/O chip w/LPC interface Sis950 A clone of the IT8705F @@ -52,7 +53,7 @@ #define DRVNAME "it87" -enum chips { it87, it8712, it8716, it8718 }; +enum chips { it87, it8712, it8716, it8718, it8720 }; static unsigned short force_id; module_param(force_id, ushort, 0); @@ -64,7 +65,10 @@ static struct platform_device *pdev; #define DEV 0x07 /* Register: Logical device select */ #define VAL 0x2f /* The value to read/write */ #define PME 0x04 /* The device with the fan registers in it */ -#define GPIO 0x07 /* The device with the IT8718F VID value in it */ + +/* The device with the IT8718F/IT8720F VID value in it */ +#define GPIO 0x07 + #define DEVID 0x20 /* Register: Device ID */ #define DEVREV 0x22 /* Register: Device Revision */ @@ -113,6 +117,7 @@ superio_exit(void) #define IT8705F_DEVID 0x8705 #define IT8716F_DEVID 0x8716 #define IT8718F_DEVID 0x8718 +#define IT8720F_DEVID 0x8720 #define IT8726F_DEVID 0x8726 #define IT87_ACT_REG 0x30 #define IT87_BASE_REG 0x60 @@ -150,8 +155,8 @@ static int fix_pwm_polarity; #define IT87_REG_ALARM2 0x02 #define IT87_REG_ALARM3 0x03 -/* The IT8718F has the VID value in a different register, in Super-I/O - configuration space. */ +/* The IT8718F and IT8720F have the VID value in a different register, in + Super-I/O configuration space. */ #define IT87_REG_VID 0x0a /* The IT8705F and IT8712F earlier than revision 0x08 use register 0x0b for fan divisors. Later IT8712F revisions must use 16-bit tachometer @@ -282,7 +287,8 @@ static inline int has_16bit_fans(const struct it87_data *data) return (data->type == it87 && data->revision >= 0x03) || (data->type == it8712 && data->revision >= 0x08) || data->type == it8716 - || data->type == it8718; + || data->type == it8718 + || data->type == it8720; } static int it87_probe(struct platform_device *pdev); @@ -992,6 +998,9 @@ static int __init it87_find(unsigned short *address, case IT8718F_DEVID: sio_data->type = it8718; break; + case IT8720F_DEVID: + sio_data->type = it8720; + break; case 0xffff: /* No device at all */ goto exit; default: @@ -1022,7 +1031,8 @@ static int __init it87_find(unsigned short *address, int reg; superio_select(GPIO); - if (chip_type == it8718) + if ((chip_type == it8718) || + (chip_type == it8720)) sio_data->vid_value = superio_inb(IT87_SIO_VID_REG); reg = superio_inb(IT87_SIO_PINX2_REG); @@ -1068,6 +1078,7 @@ static int __devinit it87_probe(struct platform_device *pdev) "it8712", "it8716", "it8718", + "it8720", }; res = platform_get_resource(pdev, IORESOURCE_IO, 0); @@ -1226,7 +1237,7 @@ static int __devinit it87_probe(struct platform_device *pdev) } if (data->type == it8712 || data->type == it8716 - || data->type == it8718) { + || data->type == it8718 || data->type == it8720) { data->vrm = vid_which_vrm(); /* VID reading from Super-I/O config space if available */ data->vid = sio_data->vid_value; @@ -1513,7 +1524,8 @@ static struct it87_data *it87_update_device(struct device *dev) data->sensor = it87_read_value(data, IT87_REG_TEMP_ENABLE); /* The 8705 does not have VID capability. - The 8718 does not use IT87_REG_VID for the same purpose. */ + The 8718 and the 8720 don't use IT87_REG_VID for the + same purpose. */ if (data->type == it8712 || data->type == it8716) { data->vid = it87_read_value(data, IT87_REG_VID); /* The older IT8712F revisions had only 5 VID pins, @@ -1608,7 +1620,7 @@ static void __exit sm_it87_exit(void) MODULE_AUTHOR("Chris Gauthron, " "Jean Delvare <khali@linux-fr.org>"); -MODULE_DESCRIPTION("IT8705F/8712F/8716F/8718F/8726F, SiS950 driver"); +MODULE_DESCRIPTION("IT8705F/8712F/8716F/8718F/8720F/8726F, SiS950 driver"); module_param(update_vbat, bool, 0); MODULE_PARM_DESC(update_vbat, "Update vbat if set else return powerup value"); module_param(fix_pwm_polarity, bool, 0); diff --git a/drivers/hwmon/k8temp.c b/drivers/hwmon/k8temp.c index bd2bde0ef95e..3b903845e12c 100644 --- a/drivers/hwmon/k8temp.c +++ b/drivers/hwmon/k8temp.c @@ -34,6 +34,7 @@ #define TEMP_FROM_REG(val) (((((val) >> 16) & 0xff) - 49) * 1000) #define REG_TEMP 0xe4 +#define REG_CPUID 0xfc #define SEL_PLACE 0x40 #define SEL_CORE 0x04 @@ -47,6 +48,7 @@ struct k8temp_data { /* registers values */ u8 sensorsp; /* sensor presence bits - SEL_CORE & SEL_PLACE */ u32 temp[2][2]; /* core, place */ + u8 fam; }; static struct k8temp_data *k8temp_update_device(struct device *dev) @@ -155,6 +157,19 @@ static int __devinit k8temp_probe(struct pci_dev *pdev, goto exit; } + /* get real PCI based cpuid, prior revF of fam 0Fh, this reg is 0 */ + pci_read_config_dword(pdev, REG_CPUID, &cpuid); + + data->fam = (cpuid & 0x00000f00) >> 8; + data->fam += (cpuid & 0x0ff00000) >> 20; + + switch (data->fam) { + case 0xf: + dev_warn(&pdev->dev, "Temperature readouts might be wrong" + " - check errata #141\n"); + break; + } + pci_read_config_byte(pdev, REG_TEMP, &scfg); scfg &= ~(SEL_PLACE | SEL_CORE); /* Select sensor 0, core0 */ pci_write_config_byte(pdev, REG_TEMP, scfg); diff --git a/drivers/hwmon/lis3lv02d.c b/drivers/hwmon/lis3lv02d.c new file mode 100644 index 000000000000..c002144c76bc --- /dev/null +++ b/drivers/hwmon/lis3lv02d.c @@ -0,0 +1,581 @@ +/* + * lis3lv02d.c - ST LIS3LV02DL accelerometer driver + * + * Copyright (C) 2007-2008 Yan Burman + * Copyright (C) 2008 Eric Piel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/dmi.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/input.h> +#include <linux/kthread.h> +#include <linux/semaphore.h> +#include <linux/delay.h> +#include <linux/wait.h> +#include <linux/poll.h> +#include <linux/freezer.h> +#include <linux/uaccess.h> +#include <acpi/acpi_drivers.h> +#include <asm/atomic.h> +#include "lis3lv02d.h" + +#define DRIVER_NAME "lis3lv02d" +#define ACPI_MDPS_CLASS "accelerometer" + +/* joystick device poll interval in milliseconds */ +#define MDPS_POLL_INTERVAL 50 +/* + * The sensor can also generate interrupts (DRDY) but it's pretty pointless + * because their are generated even if the data do not change. So it's better + * to keep the interrupt for the free-fall event. The values are updated at + * 40Hz (at the lowest frequency), but as it can be pretty time consuming on + * some low processor, we poll the sensor only at 20Hz... enough for the + * joystick. + */ + +/* Maximum value our axis may get for the input device (signed 12 bits) */ +#define MDPS_MAX_VAL 2048 + +struct axis_conversion { + s8 x; + s8 y; + s8 z; +}; + +struct acpi_lis3lv02d { + struct acpi_device *device; /* The ACPI device */ + struct input_dev *idev; /* input device */ + struct task_struct *kthread; /* kthread for input */ + struct mutex lock; + struct platform_device *pdev; /* platform device */ + atomic_t count; /* interrupt count after last read */ + int xcalib; /* calibrated null value for x */ + int ycalib; /* calibrated null value for y */ + int zcalib; /* calibrated null value for z */ + unsigned char is_on; /* whether the device is on or off */ + unsigned char usage; /* usage counter */ + struct axis_conversion ac; /* hw -> logical axis */ +}; + +static struct acpi_lis3lv02d adev; + +static int lis3lv02d_remove_fs(void); +static int lis3lv02d_add_fs(struct acpi_device *device); + +/* For automatic insertion of the module */ +static struct acpi_device_id lis3lv02d_device_ids[] = { + {"HPQ0004", 0}, /* HP Mobile Data Protection System PNP */ + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, lis3lv02d_device_ids); + +/** + * lis3lv02d_acpi_init - ACPI _INI method: initialize the device. + * @handle: the handle of the device + * + * Returns AE_OK on success. + */ +static inline acpi_status lis3lv02d_acpi_init(acpi_handle handle) +{ + return acpi_evaluate_object(handle, METHOD_NAME__INI, NULL, NULL); +} + +/** + * lis3lv02d_acpi_read - ACPI ALRD method: read a register + * @handle: the handle of the device + * @reg: the register to read + * @ret: result of the operation + * + * Returns AE_OK on success. + */ +static acpi_status lis3lv02d_acpi_read(acpi_handle handle, int reg, u8 *ret) +{ + union acpi_object arg0 = { ACPI_TYPE_INTEGER }; + struct acpi_object_list args = { 1, &arg0 }; + unsigned long long lret; + acpi_status status; + + arg0.integer.value = reg; + + status = acpi_evaluate_integer(handle, "ALRD", &args, &lret); + *ret = lret; + return status; +} + +/** + * lis3lv02d_acpi_write - ACPI ALWR method: write to a register + * @handle: the handle of the device + * @reg: the register to write to + * @val: the value to write + * + * Returns AE_OK on success. + */ +static acpi_status lis3lv02d_acpi_write(acpi_handle handle, int reg, u8 val) +{ + unsigned long long ret; /* Not used when writting */ + union acpi_object in_obj[2]; + struct acpi_object_list args = { 2, in_obj }; + + in_obj[0].type = ACPI_TYPE_INTEGER; + in_obj[0].integer.value = reg; + in_obj[1].type = ACPI_TYPE_INTEGER; + in_obj[1].integer.value = val; + + return acpi_evaluate_integer(handle, "ALWR", &args, &ret); +} + +static s16 lis3lv02d_read_16(acpi_handle handle, int reg) +{ + u8 lo, hi; + + lis3lv02d_acpi_read(handle, reg, &lo); + lis3lv02d_acpi_read(handle, reg + 1, &hi); + /* In "12 bit right justified" mode, bit 6, bit 7, bit 8 = bit 5 */ + return (s16)((hi << 8) | lo); +} + +/** + * lis3lv02d_get_axis - For the given axis, give the value converted + * @axis: 1,2,3 - can also be negative + * @hw_values: raw values returned by the hardware + * + * Returns the converted value. + */ +static inline int lis3lv02d_get_axis(s8 axis, int hw_values[3]) +{ + if (axis > 0) + return hw_values[axis - 1]; + else + return -hw_values[-axis - 1]; +} + +/** + * lis3lv02d_get_xyz - Get X, Y and Z axis values from the accelerometer + * @handle: the handle to the device + * @x: where to store the X axis value + * @y: where to store the Y axis value + * @z: where to store the Z axis value + * + * Note that 40Hz input device can eat up about 10% CPU at 800MHZ + */ +static void lis3lv02d_get_xyz(acpi_handle handle, int *x, int *y, int *z) +{ + int position[3]; + + position[0] = lis3lv02d_read_16(handle, OUTX_L); + position[1] = lis3lv02d_read_16(handle, OUTY_L); + position[2] = lis3lv02d_read_16(handle, OUTZ_L); + + *x = lis3lv02d_get_axis(adev.ac.x, position); + *y = lis3lv02d_get_axis(adev.ac.y, position); + *z = lis3lv02d_get_axis(adev.ac.z, position); +} + +static inline void lis3lv02d_poweroff(acpi_handle handle) +{ + adev.is_on = 0; + /* disable X,Y,Z axis and power down */ + lis3lv02d_acpi_write(handle, CTRL_REG1, 0x00); +} + +static void lis3lv02d_poweron(acpi_handle handle) +{ + u8 val; + + adev.is_on = 1; + lis3lv02d_acpi_init(handle); + lis3lv02d_acpi_write(handle, FF_WU_CFG, 0); + /* + * BDU: LSB and MSB values are not updated until both have been read. + * So the value read will always be correct. + * IEN: Interrupt for free-fall and DD, not for data-ready. + */ + lis3lv02d_acpi_read(handle, CTRL_REG2, &val); + val |= CTRL2_BDU | CTRL2_IEN; + lis3lv02d_acpi_write(handle, CTRL_REG2, val); +} + +#ifdef CONFIG_PM +static int lis3lv02d_suspend(struct acpi_device *device, pm_message_t state) +{ + /* make sure the device is off when we suspend */ + lis3lv02d_poweroff(device->handle); + return 0; +} + +static int lis3lv02d_resume(struct acpi_device *device) +{ + /* put back the device in the right state (ACPI might turn it on) */ + mutex_lock(&adev.lock); + if (adev.usage > 0) + lis3lv02d_poweron(device->handle); + else + lis3lv02d_poweroff(device->handle); + mutex_unlock(&adev.lock); + return 0; +} +#else +#define lis3lv02d_suspend NULL +#define lis3lv02d_resume NULL +#endif + + +/* + * To be called before starting to use the device. It makes sure that the + * device will always be on until a call to lis3lv02d_decrease_use(). Not to be + * used from interrupt context. + */ +static void lis3lv02d_increase_use(struct acpi_lis3lv02d *dev) +{ + mutex_lock(&dev->lock); + dev->usage++; + if (dev->usage == 1) { + if (!dev->is_on) + lis3lv02d_poweron(dev->device->handle); + } + mutex_unlock(&dev->lock); +} + +/* + * To be called whenever a usage of the device is stopped. + * It will make sure to turn off the device when there is not usage. + */ +static void lis3lv02d_decrease_use(struct acpi_lis3lv02d *dev) +{ + mutex_lock(&dev->lock); + dev->usage--; + if (dev->usage == 0) + lis3lv02d_poweroff(dev->device->handle); + mutex_unlock(&dev->lock); +} + +/** + * lis3lv02d_joystick_kthread - Kthread polling function + * @data: unused - here to conform to threadfn prototype + */ +static int lis3lv02d_joystick_kthread(void *data) +{ + int x, y, z; + + while (!kthread_should_stop()) { + lis3lv02d_get_xyz(adev.device->handle, &x, &y, &z); + input_report_abs(adev.idev, ABS_X, x - adev.xcalib); + input_report_abs(adev.idev, ABS_Y, y - adev.ycalib); + input_report_abs(adev.idev, ABS_Z, z - adev.zcalib); + + input_sync(adev.idev); + + try_to_freeze(); + msleep_interruptible(MDPS_POLL_INTERVAL); + } + + return 0; +} + +static int lis3lv02d_joystick_open(struct input_dev *input) +{ + lis3lv02d_increase_use(&adev); + adev.kthread = kthread_run(lis3lv02d_joystick_kthread, NULL, "klis3lv02d"); + if (IS_ERR(adev.kthread)) { + lis3lv02d_decrease_use(&adev); + return PTR_ERR(adev.kthread); + } + + return 0; +} + +static void lis3lv02d_joystick_close(struct input_dev *input) +{ + kthread_stop(adev.kthread); + lis3lv02d_decrease_use(&adev); +} + + +static inline void lis3lv02d_calibrate_joystick(void) +{ + lis3lv02d_get_xyz(adev.device->handle, &adev.xcalib, &adev.ycalib, &adev.zcalib); +} + +static int lis3lv02d_joystick_enable(void) +{ + int err; + + if (adev.idev) + return -EINVAL; + + adev.idev = input_allocate_device(); + if (!adev.idev) + return -ENOMEM; + + lis3lv02d_calibrate_joystick(); + + adev.idev->name = "ST LIS3LV02DL Accelerometer"; + adev.idev->phys = DRIVER_NAME "/input0"; + adev.idev->id.bustype = BUS_HOST; + adev.idev->id.vendor = 0; + adev.idev->dev.parent = &adev.pdev->dev; + adev.idev->open = lis3lv02d_joystick_open; + adev.idev->close = lis3lv02d_joystick_close; + + set_bit(EV_ABS, adev.idev->evbit); + input_set_abs_params(adev.idev, ABS_X, -MDPS_MAX_VAL, MDPS_MAX_VAL, 3, 3); + input_set_abs_params(adev.idev, ABS_Y, -MDPS_MAX_VAL, MDPS_MAX_VAL, 3, 3); + input_set_abs_params(adev.idev, ABS_Z, -MDPS_MAX_VAL, MDPS_MAX_VAL, 3, 3); + + err = input_register_device(adev.idev); + if (err) { + input_free_device(adev.idev); + adev.idev = NULL; + } + + return err; +} + +static void lis3lv02d_joystick_disable(void) +{ + if (!adev.idev) + return; + + input_unregister_device(adev.idev); + adev.idev = NULL; +} + + +/* + * Initialise the accelerometer and the various subsystems. + * Should be rather independant of the bus system. + */ +static int lis3lv02d_init_device(struct acpi_lis3lv02d *dev) +{ + mutex_init(&dev->lock); + lis3lv02d_add_fs(dev->device); + lis3lv02d_increase_use(dev); + + if (lis3lv02d_joystick_enable()) + printk(KERN_ERR DRIVER_NAME ": joystick initialization failed\n"); + + lis3lv02d_decrease_use(dev); + return 0; +} + +static int lis3lv02d_dmi_matched(const struct dmi_system_id *dmi) +{ + adev.ac = *((struct axis_conversion *)dmi->driver_data); + printk(KERN_INFO DRIVER_NAME ": hardware type %s found.\n", dmi->ident); + + return 1; +} + +/* Represents, for each axis seen by userspace, the corresponding hw axis (+1). + * If the value is negative, the opposite of the hw value is used. */ +static struct axis_conversion lis3lv02d_axis_normal = {1, 2, 3}; +static struct axis_conversion lis3lv02d_axis_y_inverted = {1, -2, 3}; +static struct axis_conversion lis3lv02d_axis_x_inverted = {-1, 2, 3}; +static struct axis_conversion lis3lv02d_axis_z_inverted = {1, 2, -3}; +static struct axis_conversion lis3lv02d_axis_xy_rotated_left = {-2, 1, 3}; +static struct axis_conversion lis3lv02d_axis_xy_swap_inverted = {-2, -1, 3}; + +#define AXIS_DMI_MATCH(_ident, _name, _axis) { \ + .ident = _ident, \ + .callback = lis3lv02d_dmi_matched, \ + .matches = { \ + DMI_MATCH(DMI_PRODUCT_NAME, _name) \ + }, \ + .driver_data = &lis3lv02d_axis_##_axis \ +} +static struct dmi_system_id lis3lv02d_dmi_ids[] = { + /* product names are truncated to match all kinds of a same model */ + AXIS_DMI_MATCH("NC64x0", "HP Compaq nc64", x_inverted), + AXIS_DMI_MATCH("NC84x0", "HP Compaq nc84", z_inverted), + AXIS_DMI_MATCH("NX9420", "HP Compaq nx9420", x_inverted), + AXIS_DMI_MATCH("NW9440", "HP Compaq nw9440", x_inverted), + AXIS_DMI_MATCH("NC2510", "HP Compaq 2510", y_inverted), + AXIS_DMI_MATCH("NC8510", "HP Compaq 8510", xy_swap_inverted), + AXIS_DMI_MATCH("HP2133", "HP 2133", xy_rotated_left), + { NULL, } +/* Laptop models without axis info (yet): + * "NC651xx" "HP Compaq 651" + * "NC671xx" "HP Compaq 671" + * "NC6910" "HP Compaq 6910" + * HP Compaq 8710x Notebook PC / Mobile Workstation + * "NC2400" "HP Compaq nc2400" + * "NX74x0" "HP Compaq nx74" + * "NX6325" "HP Compaq nx6325" + * "NC4400" "HP Compaq nc4400" + */ +}; + +static int lis3lv02d_add(struct acpi_device *device) +{ + u8 val; + + if (!device) + return -EINVAL; + + adev.device = device; + strcpy(acpi_device_name(device), DRIVER_NAME); + strcpy(acpi_device_class(device), ACPI_MDPS_CLASS); + device->driver_data = &adev; + + lis3lv02d_acpi_read(device->handle, WHO_AM_I, &val); + if ((val != LIS3LV02DL_ID) && (val != LIS302DL_ID)) { + printk(KERN_ERR DRIVER_NAME + ": Accelerometer chip not LIS3LV02D{L,Q}\n"); + } + + /* If possible use a "standard" axes order */ + if (dmi_check_system(lis3lv02d_dmi_ids) == 0) { + printk(KERN_INFO DRIVER_NAME ": laptop model unknown, " + "using default axes configuration\n"); + adev.ac = lis3lv02d_axis_normal; + } + + return lis3lv02d_init_device(&adev); +} + +static int lis3lv02d_remove(struct acpi_device *device, int type) +{ + if (!device) + return -EINVAL; + + lis3lv02d_joystick_disable(); + lis3lv02d_poweroff(device->handle); + + return lis3lv02d_remove_fs(); +} + + +/* Sysfs stuff */ +static ssize_t lis3lv02d_position_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int x, y, z; + + lis3lv02d_increase_use(&adev); + lis3lv02d_get_xyz(adev.device->handle, &x, &y, &z); + lis3lv02d_decrease_use(&adev); + return sprintf(buf, "(%d,%d,%d)\n", x, y, z); +} + +static ssize_t lis3lv02d_calibrate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "(%d,%d,%d)\n", adev.xcalib, adev.ycalib, adev.zcalib); +} + +static ssize_t lis3lv02d_calibrate_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + lis3lv02d_increase_use(&adev); + lis3lv02d_calibrate_joystick(); + lis3lv02d_decrease_use(&adev); + return count; +} + +/* conversion btw sampling rate and the register values */ +static int lis3lv02dl_df_val[4] = {40, 160, 640, 2560}; +static ssize_t lis3lv02d_rate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 ctrl; + int val; + + lis3lv02d_increase_use(&adev); + lis3lv02d_acpi_read(adev.device->handle, CTRL_REG1, &ctrl); + lis3lv02d_decrease_use(&adev); + val = (ctrl & (CTRL1_DF0 | CTRL1_DF1)) >> 4; + return sprintf(buf, "%d\n", lis3lv02dl_df_val[val]); +} + +static DEVICE_ATTR(position, S_IRUGO, lis3lv02d_position_show, NULL); +static DEVICE_ATTR(calibrate, S_IRUGO|S_IWUSR, lis3lv02d_calibrate_show, + lis3lv02d_calibrate_store); +static DEVICE_ATTR(rate, S_IRUGO, lis3lv02d_rate_show, NULL); + +static struct attribute *lis3lv02d_attributes[] = { + &dev_attr_position.attr, + &dev_attr_calibrate.attr, + &dev_attr_rate.attr, + NULL +}; + +static struct attribute_group lis3lv02d_attribute_group = { + .attrs = lis3lv02d_attributes +}; + +static int lis3lv02d_add_fs(struct acpi_device *device) +{ + adev.pdev = platform_device_register_simple(DRIVER_NAME, -1, NULL, 0); + if (IS_ERR(adev.pdev)) + return PTR_ERR(adev.pdev); + + return sysfs_create_group(&adev.pdev->dev.kobj, &lis3lv02d_attribute_group); +} + +static int lis3lv02d_remove_fs(void) +{ + sysfs_remove_group(&adev.pdev->dev.kobj, &lis3lv02d_attribute_group); + platform_device_unregister(adev.pdev); + return 0; +} + +/* For the HP MDPS aka 3D Driveguard */ +static struct acpi_driver lis3lv02d_driver = { + .name = DRIVER_NAME, + .class = ACPI_MDPS_CLASS, + .ids = lis3lv02d_device_ids, + .ops = { + .add = lis3lv02d_add, + .remove = lis3lv02d_remove, + .suspend = lis3lv02d_suspend, + .resume = lis3lv02d_resume, + } +}; + +static int __init lis3lv02d_init_module(void) +{ + int ret; + + if (acpi_disabled) + return -ENODEV; + + ret = acpi_bus_register_driver(&lis3lv02d_driver); + if (ret < 0) + return ret; + + printk(KERN_INFO DRIVER_NAME " driver loaded.\n"); + + return 0; +} + +static void __exit lis3lv02d_exit_module(void) +{ + acpi_bus_unregister_driver(&lis3lv02d_driver); +} + +MODULE_DESCRIPTION("ST LIS3LV02Dx three-axis digital accelerometer driver"); +MODULE_AUTHOR("Yan Burman and Eric Piel"); +MODULE_LICENSE("GPL"); + +module_init(lis3lv02d_init_module); +module_exit(lis3lv02d_exit_module); diff --git a/drivers/hwmon/lis3lv02d.h b/drivers/hwmon/lis3lv02d.h new file mode 100644 index 000000000000..330cfc60e948 --- /dev/null +++ b/drivers/hwmon/lis3lv02d.h @@ -0,0 +1,149 @@ +/* + * lis3lv02d.h - ST LIS3LV02DL accelerometer driver + * + * Copyright (C) 2007-2008 Yan Burman + * Copyright (C) 2008 Eric Piel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * The actual chip is STMicroelectronics LIS3LV02DL or LIS3LV02DQ that seems to + * be connected via SPI. There exists also several similar chips (such as LIS302DL or + * LIS3L02DQ) but not in the HP laptops and they have slightly different registers. + * They can also be connected via I²C. + */ + +#define LIS3LV02DL_ID 0x3A /* Also the LIS3LV02DQ */ +#define LIS302DL_ID 0x3B /* Also the LIS202DL! */ + +enum lis3lv02d_reg { + WHO_AM_I = 0x0F, + OFFSET_X = 0x16, + OFFSET_Y = 0x17, + OFFSET_Z = 0x18, + GAIN_X = 0x19, + GAIN_Y = 0x1A, + GAIN_Z = 0x1B, + CTRL_REG1 = 0x20, + CTRL_REG2 = 0x21, + CTRL_REG3 = 0x22, + HP_FILTER_RESET = 0x23, + STATUS_REG = 0x27, + OUTX_L = 0x28, + OUTX_H = 0x29, + OUTY_L = 0x2A, + OUTY_H = 0x2B, + OUTZ_L = 0x2C, + OUTZ_H = 0x2D, + FF_WU_CFG = 0x30, + FF_WU_SRC = 0x31, + FF_WU_ACK = 0x32, + FF_WU_THS_L = 0x34, + FF_WU_THS_H = 0x35, + FF_WU_DURATION = 0x36, + DD_CFG = 0x38, + DD_SRC = 0x39, + DD_ACK = 0x3A, + DD_THSI_L = 0x3C, + DD_THSI_H = 0x3D, + DD_THSE_L = 0x3E, + DD_THSE_H = 0x3F, +}; + +enum lis3lv02d_ctrl1 { + CTRL1_Xen = 0x01, + CTRL1_Yen = 0x02, + CTRL1_Zen = 0x04, + CTRL1_ST = 0x08, + CTRL1_DF0 = 0x10, + CTRL1_DF1 = 0x20, + CTRL1_PD0 = 0x40, + CTRL1_PD1 = 0x80, +}; +enum lis3lv02d_ctrl2 { + CTRL2_DAS = 0x01, + CTRL2_SIM = 0x02, + CTRL2_DRDY = 0x04, + CTRL2_IEN = 0x08, + CTRL2_BOOT = 0x10, + CTRL2_BLE = 0x20, + CTRL2_BDU = 0x40, /* Block Data Update */ + CTRL2_FS = 0x80, /* Full Scale selection */ +}; + + +enum lis3lv02d_ctrl3 { + CTRL3_CFS0 = 0x01, + CTRL3_CFS1 = 0x02, + CTRL3_FDS = 0x10, + CTRL3_HPFF = 0x20, + CTRL3_HPDD = 0x40, + CTRL3_ECK = 0x80, +}; + +enum lis3lv02d_status_reg { + STATUS_XDA = 0x01, + STATUS_YDA = 0x02, + STATUS_ZDA = 0x04, + STATUS_XYZDA = 0x08, + STATUS_XOR = 0x10, + STATUS_YOR = 0x20, + STATUS_ZOR = 0x40, + STATUS_XYZOR = 0x80, +}; + +enum lis3lv02d_ff_wu_cfg { + FF_WU_CFG_XLIE = 0x01, + FF_WU_CFG_XHIE = 0x02, + FF_WU_CFG_YLIE = 0x04, + FF_WU_CFG_YHIE = 0x08, + FF_WU_CFG_ZLIE = 0x10, + FF_WU_CFG_ZHIE = 0x20, + FF_WU_CFG_LIR = 0x40, + FF_WU_CFG_AOI = 0x80, +}; + +enum lis3lv02d_ff_wu_src { + FF_WU_SRC_XL = 0x01, + FF_WU_SRC_XH = 0x02, + FF_WU_SRC_YL = 0x04, + FF_WU_SRC_YH = 0x08, + FF_WU_SRC_ZL = 0x10, + FF_WU_SRC_ZH = 0x20, + FF_WU_SRC_IA = 0x40, +}; + +enum lis3lv02d_dd_cfg { + DD_CFG_XLIE = 0x01, + DD_CFG_XHIE = 0x02, + DD_CFG_YLIE = 0x04, + DD_CFG_YHIE = 0x08, + DD_CFG_ZLIE = 0x10, + DD_CFG_ZHIE = 0x20, + DD_CFG_LIR = 0x40, + DD_CFG_IEND = 0x80, +}; + +enum lis3lv02d_dd_src { + DD_SRC_XL = 0x01, + DD_SRC_XH = 0x02, + DD_SRC_YL = 0x04, + DD_SRC_YH = 0x08, + DD_SRC_ZL = 0x10, + DD_SRC_ZH = 0x20, + DD_SRC_IA = 0x40, +}; + diff --git a/drivers/hwmon/lm70.c b/drivers/hwmon/lm70.c index d435f003292d..ae6204f33214 100644 --- a/drivers/hwmon/lm70.c +++ b/drivers/hwmon/lm70.c @@ -37,9 +37,13 @@ #define DRVNAME "lm70" +#define LM70_CHIP_LM70 0 /* original NS LM70 */ +#define LM70_CHIP_TMP121 1 /* TI TMP121/TMP123 */ + struct lm70 { struct device *hwmon_dev; struct mutex lock; + unsigned int chip; }; /* sysfs hook function */ @@ -47,7 +51,7 @@ static ssize_t lm70_sense_temp(struct device *dev, struct device_attribute *attr, char *buf) { struct spi_device *spi = to_spi_device(dev); - int status, val; + int status, val = 0; u8 rxbuf[2]; s16 raw=0; struct lm70 *p_lm70 = dev_get_drvdata(&spi->dev); @@ -65,12 +69,12 @@ static ssize_t lm70_sense_temp(struct device *dev, "spi_write_then_read failed with status %d\n", status); goto out; } - dev_dbg(dev, "rxbuf[1] : 0x%x rxbuf[0] : 0x%x\n", rxbuf[1], rxbuf[0]); - - raw = (rxbuf[1] << 8) + rxbuf[0]; - dev_dbg(dev, "raw=0x%x\n", raw); + raw = (rxbuf[0] << 8) + rxbuf[1]; + dev_dbg(dev, "rxbuf[0] : 0x%02x rxbuf[1] : 0x%02x raw=0x%04x\n", + rxbuf[0], rxbuf[1], raw); /* + * LM70: * The "raw" temperature read into rxbuf[] is a 16-bit signed 2's * complement value. Only the MSB 11 bits (1 sign + 10 temperature * bits) are meaningful; the LSB 5 bits are to be discarded. @@ -80,8 +84,21 @@ static ssize_t lm70_sense_temp(struct device *dev, * by 0.25. Also multiply by 1000 to represent in millidegrees * Celsius. * So it's equivalent to multiplying by 0.25 * 1000 = 250. + * + * TMP121/TMP123: + * 13 bits of 2's complement data, discard LSB 3 bits, + * resolution 0.0625 degrees celsius. */ - val = ((int)raw/32) * 250; + switch (p_lm70->chip) { + case LM70_CHIP_LM70: + val = ((int)raw / 32) * 250; + break; + + case LM70_CHIP_TMP121: + val = ((int)raw / 8) * 625 / 10; + break; + } + status = sprintf(buf, "%d\n", val); /* millidegrees Celsius */ out: mutex_unlock(&p_lm70->lock); @@ -93,27 +110,39 @@ static DEVICE_ATTR(temp1_input, S_IRUGO, lm70_sense_temp, NULL); static ssize_t lm70_show_name(struct device *dev, struct device_attribute *devattr, char *buf) { - return sprintf(buf, "lm70\n"); + struct lm70 *p_lm70 = dev_get_drvdata(dev); + int ret; + + switch (p_lm70->chip) { + case LM70_CHIP_LM70: + ret = sprintf(buf, "lm70\n"); + break; + case LM70_CHIP_TMP121: + ret = sprintf(buf, "tmp121\n"); + break; + default: + ret = -EINVAL; + } + return ret; } static DEVICE_ATTR(name, S_IRUGO, lm70_show_name, NULL); /*----------------------------------------------------------------------*/ -static int __devinit lm70_probe(struct spi_device *spi) +static int __devinit common_probe(struct spi_device *spi, int chip) { struct lm70 *p_lm70; int status; - /* signaling is SPI_MODE_0 on a 3-wire link (shared SI/SO) */ - if ((spi->mode & (SPI_CPOL|SPI_CPHA)) || !(spi->mode & SPI_3WIRE)) - return -EINVAL; + /* NOTE: we assume 8-bit words, and convert to 16 bits manually */ p_lm70 = kzalloc(sizeof *p_lm70, GFP_KERNEL); if (!p_lm70) return -ENOMEM; mutex_init(&p_lm70->lock); + p_lm70->chip = chip; /* sysfs hook */ p_lm70->hwmon_dev = hwmon_device_register(&spi->dev); @@ -141,6 +170,24 @@ out_dev_reg_failed: return status; } +static int __devinit lm70_probe(struct spi_device *spi) +{ + /* signaling is SPI_MODE_0 on a 3-wire link (shared SI/SO) */ + if ((spi->mode & (SPI_CPOL | SPI_CPHA)) || !(spi->mode & SPI_3WIRE)) + return -EINVAL; + + return common_probe(spi, LM70_CHIP_LM70); +} + +static int __devinit tmp121_probe(struct spi_device *spi) +{ + /* signaling is SPI_MODE_0 with only MISO connected */ + if (spi->mode & (SPI_CPOL | SPI_CPHA)) + return -EINVAL; + + return common_probe(spi, LM70_CHIP_TMP121); +} + static int __devexit lm70_remove(struct spi_device *spi) { struct lm70 *p_lm70 = dev_get_drvdata(&spi->dev); @@ -154,6 +201,15 @@ static int __devexit lm70_remove(struct spi_device *spi) return 0; } +static struct spi_driver tmp121_driver = { + .driver = { + .name = "tmp121", + .owner = THIS_MODULE, + }, + .probe = tmp121_probe, + .remove = __devexit_p(lm70_remove), +}; + static struct spi_driver lm70_driver = { .driver = { .name = "lm70", @@ -165,17 +221,26 @@ static struct spi_driver lm70_driver = { static int __init init_lm70(void) { - return spi_register_driver(&lm70_driver); + int ret = spi_register_driver(&lm70_driver); + if (ret) + return ret; + + ret = spi_register_driver(&tmp121_driver); + if (ret) + spi_unregister_driver(&lm70_driver); + + return ret; } static void __exit cleanup_lm70(void) { spi_unregister_driver(&lm70_driver); + spi_unregister_driver(&tmp121_driver); } module_init(init_lm70); module_exit(cleanup_lm70); MODULE_AUTHOR("Kaiwan N Billimoria"); -MODULE_DESCRIPTION("National Semiconductor LM70 Linux driver"); +MODULE_DESCRIPTION("NS LM70 / TI TMP121/TMP123 Linux driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c index 8f9595f2fb53..55bd87c15c9a 100644 --- a/drivers/hwmon/lm75.c +++ b/drivers/hwmon/lm75.c @@ -190,7 +190,7 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id) } dev_info(&client->dev, "%s: sensor '%s'\n", - data->hwmon_dev->bus_id, client->name); + dev_name(data->hwmon_dev), client->name); return 0; diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c index 3ff0285396fa..cfc1ee90f5a3 100644 --- a/drivers/hwmon/lm85.c +++ b/drivers/hwmon/lm85.c @@ -39,7 +39,8 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END }; /* Insmod parameters */ -I2C_CLIENT_INSMOD_6(lm85b, lm85c, adm1027, adt7463, emc6d100, emc6d102); +I2C_CLIENT_INSMOD_7(lm85b, lm85c, adm1027, adt7463, adt7468, emc6d100, + emc6d102); /* The LM85 registers */ @@ -59,6 +60,12 @@ I2C_CLIENT_INSMOD_6(lm85b, lm85c, adm1027, adt7463, emc6d100, emc6d102); #define LM85_REG_COMPANY 0x3e #define LM85_REG_VERSTEP 0x3f + +#define ADT7468_REG_CFG5 0x7c +#define ADT7468_OFF64 0x01 +#define IS_ADT7468_OFF64(data) \ + ((data)->type == adt7468 && !((data)->cfg5 & ADT7468_OFF64)) + /* These are the recognized values for the above regs */ #define LM85_COMPANY_NATIONAL 0x01 #define LM85_COMPANY_ANALOG_DEV 0x41 @@ -70,6 +77,8 @@ I2C_CLIENT_INSMOD_6(lm85b, lm85c, adm1027, adt7463, emc6d100, emc6d102); #define LM85_VERSTEP_ADM1027 0x60 #define LM85_VERSTEP_ADT7463 0x62 #define LM85_VERSTEP_ADT7463C 0x6A +#define LM85_VERSTEP_ADT7468_1 0x71 +#define LM85_VERSTEP_ADT7468_2 0x72 #define LM85_VERSTEP_EMC6D100_A0 0x60 #define LM85_VERSTEP_EMC6D100_A1 0x61 #define LM85_VERSTEP_EMC6D102 0x65 @@ -306,6 +315,7 @@ struct lm85_data { u8 vid; /* Register value */ u8 vrm; /* VRM version */ u32 alarms; /* Register encoding, combined */ + u8 cfg5; /* Config Register 5 on ADT7468 */ struct lm85_autofan autofan[3]; struct lm85_zone zone[3]; }; @@ -685,6 +695,9 @@ static ssize_t set_temp_min(struct device *dev, struct device_attribute *attr, struct lm85_data *data = i2c_get_clientdata(client); long val = simple_strtol(buf, NULL, 10); + if (IS_ADT7468_OFF64(data)) + val += 64; + mutex_lock(&data->update_lock); data->temp_min[nr] = TEMP_TO_REG(val); lm85_write_value(client, LM85_REG_TEMP_MIN(nr), data->temp_min[nr]); @@ -708,6 +721,9 @@ static ssize_t set_temp_max(struct device *dev, struct device_attribute *attr, struct lm85_data *data = i2c_get_clientdata(client); long val = simple_strtol(buf, NULL, 10); + if (IS_ADT7468_OFF64(data)) + val += 64; + mutex_lock(&data->update_lock); data->temp_max[nr] = TEMP_TO_REG(val); lm85_write_value(client, LM85_REG_TEMP_MAX(nr), data->temp_max[nr]); @@ -1163,6 +1179,10 @@ static int lm85_detect(struct i2c_client *client, int kind, case LM85_VERSTEP_ADT7463C: kind = adt7463; break; + case LM85_VERSTEP_ADT7468_1: + case LM85_VERSTEP_ADT7468_2: + kind = adt7468; + break; } } else if (company == LM85_COMPANY_SMSC) { switch (verstep) { @@ -1195,6 +1215,9 @@ static int lm85_detect(struct i2c_client *client, int kind, case adt7463: type_name = "adt7463"; break; + case adt7468: + type_name = "adt7468"; + break; case emc6d100: type_name = "emc6d100"; break; @@ -1246,10 +1269,11 @@ static int lm85_probe(struct i2c_client *client, if (err) goto err_kfree; - /* The ADT7463 has an optional VRM 10 mode where pin 21 is used + /* The ADT7463/68 have an optional VRM 10 mode where pin 21 is used as a sixth digital VID input rather than an analog input. */ data->vid = lm85_read_value(client, LM85_REG_VID); - if (!(data->type == adt7463 && (data->vid & 0x80))) + if (!((data->type == adt7463 || data->type == adt7468) && + (data->vid & 0x80))) if ((err = sysfs_create_group(&client->dev.kobj, &lm85_group_in4))) goto err_remove_files; @@ -1357,7 +1381,8 @@ static struct lm85_data *lm85_update_device(struct device *dev) * There are 2 additional resolution bits per channel and we * have room for 4, so we shift them to the left. */ - if (data->type == adm1027 || data->type == adt7463) { + if (data->type == adm1027 || data->type == adt7463 || + data->type == adt7468) { int ext1 = lm85_read_value(client, ADM1027_REG_EXTEND_ADC1); int ext2 = lm85_read_value(client, @@ -1382,16 +1407,23 @@ static struct lm85_data *lm85_update_device(struct device *dev) lm85_read_value(client, LM85_REG_FAN(i)); } - if (!(data->type == adt7463 && (data->vid & 0x80))) { + if (!((data->type == adt7463 || data->type == adt7468) && + (data->vid & 0x80))) { data->in[4] = lm85_read_value(client, LM85_REG_IN(4)); } + if (data->type == adt7468) + data->cfg5 = lm85_read_value(client, ADT7468_REG_CFG5); + for (i = 0; i <= 2; ++i) { data->temp[i] = lm85_read_value(client, LM85_REG_TEMP(i)); data->pwm[i] = lm85_read_value(client, LM85_REG_PWM(i)); + + if (IS_ADT7468_OFF64(data)) + data->temp[i] -= 64; } data->alarms = lm85_read_value(client, LM85_REG_ALARM1); @@ -1446,7 +1478,8 @@ static struct lm85_data *lm85_update_device(struct device *dev) lm85_read_value(client, LM85_REG_FAN_MIN(i)); } - if (!(data->type == adt7463 && (data->vid & 0x80))) { + if (!((data->type == adt7463 || data->type == adt7468) && + (data->vid & 0x80))) { data->in_min[4] = lm85_read_value(client, LM85_REG_IN_MIN(4)); data->in_max[4] = lm85_read_value(client, @@ -1481,6 +1514,13 @@ static struct lm85_data *lm85_update_device(struct device *dev) lm85_read_value(client, LM85_REG_AFAN_LIMIT(i)); data->zone[i].critical = lm85_read_value(client, LM85_REG_AFAN_CRITICAL(i)); + + if (IS_ADT7468_OFF64(data)) { + data->temp_min[i] -= 64; + data->temp_max[i] -= 64; + data->zone[i].limit -= 64; + data->zone[i].critical -= 64; + } } i = lm85_read_value(client, LM85_REG_AFAN_SPIKE1); diff --git a/drivers/hwmon/ltc4245.c b/drivers/hwmon/ltc4245.c new file mode 100644 index 000000000000..034b2c515848 --- /dev/null +++ b/drivers/hwmon/ltc4245.c @@ -0,0 +1,567 @@ +/* + * Driver for Linear Technology LTC4245 I2C Multiple Supply Hot Swap Controller + * + * Copyright (C) 2008 Ira W. Snyder <iws@ovro.caltech.edu> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This driver is based on the ds1621 and ina209 drivers. + * + * Datasheet: + * http://www.linear.com/pc/downloadDocument.do?navId=H0,C1,C1003,C1006,C1140,P19392,D13517 + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> + +/* Valid addresses are 0x20 - 0x3f + * + * For now, we do not probe, since some of these addresses + * are known to be unfriendly to probing */ +static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; + +/* Insmod parameters */ +I2C_CLIENT_INSMOD_1(ltc4245); + +/* Here are names of the chip's registers (a.k.a. commands) */ +enum ltc4245_cmd { + LTC4245_STATUS = 0x00, /* readonly */ + LTC4245_ALERT = 0x01, + LTC4245_CONTROL = 0x02, + LTC4245_ON = 0x03, + LTC4245_FAULT1 = 0x04, + LTC4245_FAULT2 = 0x05, + LTC4245_GPIO = 0x06, + LTC4245_ADCADR = 0x07, + + LTC4245_12VIN = 0x10, + LTC4245_12VSENSE = 0x11, + LTC4245_12VOUT = 0x12, + LTC4245_5VIN = 0x13, + LTC4245_5VSENSE = 0x14, + LTC4245_5VOUT = 0x15, + LTC4245_3VIN = 0x16, + LTC4245_3VSENSE = 0x17, + LTC4245_3VOUT = 0x18, + LTC4245_VEEIN = 0x19, + LTC4245_VEESENSE = 0x1a, + LTC4245_VEEOUT = 0x1b, + LTC4245_GPIOADC1 = 0x1c, + LTC4245_GPIOADC2 = 0x1d, + LTC4245_GPIOADC3 = 0x1e, +}; + +struct ltc4245_data { + struct device *hwmon_dev; + + struct mutex update_lock; + bool valid; + unsigned long last_updated; /* in jiffies */ + + /* Control registers */ + u8 cregs[0x08]; + + /* Voltage registers */ + u8 vregs[0x0f]; +}; + +static struct ltc4245_data *ltc4245_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ltc4245_data *data = i2c_get_clientdata(client); + s32 val; + int i; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { + + dev_dbg(&client->dev, "Starting ltc4245 update\n"); + + /* Read control registers -- 0x00 to 0x07 */ + for (i = 0; i < ARRAY_SIZE(data->cregs); i++) { + val = i2c_smbus_read_byte_data(client, i); + if (unlikely(val < 0)) + data->cregs[i] = 0; + else + data->cregs[i] = val; + } + + /* Read voltage registers -- 0x10 to 0x1f */ + for (i = 0; i < ARRAY_SIZE(data->vregs); i++) { + val = i2c_smbus_read_byte_data(client, i+0x10); + if (unlikely(val < 0)) + data->vregs[i] = 0; + else + data->vregs[i] = val; + } + + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + + return data; +} + +/* Return the voltage from the given register in millivolts */ +static int ltc4245_get_voltage(struct device *dev, u8 reg) +{ + struct ltc4245_data *data = ltc4245_update_device(dev); + const u8 regval = data->vregs[reg - 0x10]; + u32 voltage = 0; + + switch (reg) { + case LTC4245_12VIN: + case LTC4245_12VOUT: + voltage = regval * 55; + break; + case LTC4245_5VIN: + case LTC4245_5VOUT: + voltage = regval * 22; + break; + case LTC4245_3VIN: + case LTC4245_3VOUT: + voltage = regval * 15; + break; + case LTC4245_VEEIN: + case LTC4245_VEEOUT: + voltage = regval * -55; + break; + case LTC4245_GPIOADC1: + case LTC4245_GPIOADC2: + case LTC4245_GPIOADC3: + voltage = regval * 10; + break; + default: + /* If we get here, the developer messed up */ + WARN_ON_ONCE(1); + break; + } + + return voltage; +} + +/* Return the current in the given sense register in milliAmperes */ +static unsigned int ltc4245_get_current(struct device *dev, u8 reg) +{ + struct ltc4245_data *data = ltc4245_update_device(dev); + const u8 regval = data->vregs[reg - 0x10]; + unsigned int voltage; + unsigned int curr; + + /* The strange looking conversions that follow are fixed-point + * math, since we cannot do floating point in the kernel. + * + * Step 1: convert sense register to microVolts + * Step 2: convert voltage to milliAmperes + * + * If you play around with the V=IR equation, you come up with + * the following: X uV / Y mOhm == Z mA + * + * With the resistors that are fractions of a milliOhm, we multiply + * the voltage and resistance by 10, to shift the decimal point. + * Now we can use the normal division operator again. + */ + + switch (reg) { + case LTC4245_12VSENSE: + voltage = regval * 250; /* voltage in uV */ + curr = voltage / 50; /* sense resistor 50 mOhm */ + break; + case LTC4245_5VSENSE: + voltage = regval * 125; /* voltage in uV */ + curr = (voltage * 10) / 35; /* sense resistor 3.5 mOhm */ + break; + case LTC4245_3VSENSE: + voltage = regval * 125; /* voltage in uV */ + curr = (voltage * 10) / 25; /* sense resistor 2.5 mOhm */ + break; + case LTC4245_VEESENSE: + voltage = regval * 250; /* voltage in uV */ + curr = voltage / 100; /* sense resistor 100 mOhm */ + break; + default: + /* If we get here, the developer messed up */ + WARN_ON_ONCE(1); + curr = 0; + break; + } + + return curr; +} + +static ssize_t ltc4245_show_voltage(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + const int voltage = ltc4245_get_voltage(dev, attr->index); + + return snprintf(buf, PAGE_SIZE, "%d\n", voltage); +} + +static ssize_t ltc4245_show_current(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + const unsigned int curr = ltc4245_get_current(dev, attr->index); + + return snprintf(buf, PAGE_SIZE, "%u\n", curr); +} + +static ssize_t ltc4245_show_power(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + const unsigned int curr = ltc4245_get_current(dev, attr->index); + const int output_voltage = ltc4245_get_voltage(dev, attr->index+1); + + /* current in mA * voltage in mV == power in uW */ + const unsigned int power = abs(output_voltage * curr); + + return snprintf(buf, PAGE_SIZE, "%u\n", power); +} + +static ssize_t ltc4245_show_alarm(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(da); + struct ltc4245_data *data = ltc4245_update_device(dev); + const u8 reg = data->cregs[attr->index]; + const u32 mask = attr->nr; + + return snprintf(buf, PAGE_SIZE, "%u\n", (reg & mask) ? 1 : 0); +} + +/* These macros are used below in constructing device attribute objects + * for use with sysfs_create_group() to make a sysfs device file + * for each register. + */ + +#define LTC4245_VOLTAGE(name, ltc4245_cmd_idx) \ + static SENSOR_DEVICE_ATTR(name, S_IRUGO, \ + ltc4245_show_voltage, NULL, ltc4245_cmd_idx) + +#define LTC4245_CURRENT(name, ltc4245_cmd_idx) \ + static SENSOR_DEVICE_ATTR(name, S_IRUGO, \ + ltc4245_show_current, NULL, ltc4245_cmd_idx) + +#define LTC4245_POWER(name, ltc4245_cmd_idx) \ + static SENSOR_DEVICE_ATTR(name, S_IRUGO, \ + ltc4245_show_power, NULL, ltc4245_cmd_idx) + +#define LTC4245_ALARM(name, mask, reg) \ + static SENSOR_DEVICE_ATTR_2(name, S_IRUGO, \ + ltc4245_show_alarm, NULL, (mask), reg) + +/* Construct a sensor_device_attribute structure for each register */ + +/* Input voltages */ +LTC4245_VOLTAGE(in1_input, LTC4245_12VIN); +LTC4245_VOLTAGE(in2_input, LTC4245_5VIN); +LTC4245_VOLTAGE(in3_input, LTC4245_3VIN); +LTC4245_VOLTAGE(in4_input, LTC4245_VEEIN); + +/* Input undervoltage alarms */ +LTC4245_ALARM(in1_min_alarm, (1 << 0), LTC4245_FAULT1); +LTC4245_ALARM(in2_min_alarm, (1 << 1), LTC4245_FAULT1); +LTC4245_ALARM(in3_min_alarm, (1 << 2), LTC4245_FAULT1); +LTC4245_ALARM(in4_min_alarm, (1 << 3), LTC4245_FAULT1); + +/* Currents (via sense resistor) */ +LTC4245_CURRENT(curr1_input, LTC4245_12VSENSE); +LTC4245_CURRENT(curr2_input, LTC4245_5VSENSE); +LTC4245_CURRENT(curr3_input, LTC4245_3VSENSE); +LTC4245_CURRENT(curr4_input, LTC4245_VEESENSE); + +/* Overcurrent alarms */ +LTC4245_ALARM(curr1_max_alarm, (1 << 4), LTC4245_FAULT1); +LTC4245_ALARM(curr2_max_alarm, (1 << 5), LTC4245_FAULT1); +LTC4245_ALARM(curr3_max_alarm, (1 << 6), LTC4245_FAULT1); +LTC4245_ALARM(curr4_max_alarm, (1 << 7), LTC4245_FAULT1); + +/* Output voltages */ +LTC4245_VOLTAGE(in5_input, LTC4245_12VOUT); +LTC4245_VOLTAGE(in6_input, LTC4245_5VOUT); +LTC4245_VOLTAGE(in7_input, LTC4245_3VOUT); +LTC4245_VOLTAGE(in8_input, LTC4245_VEEOUT); + +/* Power Bad alarms */ +LTC4245_ALARM(in5_min_alarm, (1 << 0), LTC4245_FAULT2); +LTC4245_ALARM(in6_min_alarm, (1 << 1), LTC4245_FAULT2); +LTC4245_ALARM(in7_min_alarm, (1 << 2), LTC4245_FAULT2); +LTC4245_ALARM(in8_min_alarm, (1 << 3), LTC4245_FAULT2); + +/* GPIO voltages */ +LTC4245_VOLTAGE(in9_input, LTC4245_GPIOADC1); +LTC4245_VOLTAGE(in10_input, LTC4245_GPIOADC2); +LTC4245_VOLTAGE(in11_input, LTC4245_GPIOADC3); + +/* Power Consumption (virtual) */ +LTC4245_POWER(power1_input, LTC4245_12VSENSE); +LTC4245_POWER(power2_input, LTC4245_5VSENSE); +LTC4245_POWER(power3_input, LTC4245_3VSENSE); +LTC4245_POWER(power4_input, LTC4245_VEESENSE); + +/* Finally, construct an array of pointers to members of the above objects, + * as required for sysfs_create_group() + */ +static struct attribute *ltc4245_attributes[] = { + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in3_input.dev_attr.attr, + &sensor_dev_attr_in4_input.dev_attr.attr, + + &sensor_dev_attr_in1_min_alarm.dev_attr.attr, + &sensor_dev_attr_in2_min_alarm.dev_attr.attr, + &sensor_dev_attr_in3_min_alarm.dev_attr.attr, + &sensor_dev_attr_in4_min_alarm.dev_attr.attr, + + &sensor_dev_attr_curr1_input.dev_attr.attr, + &sensor_dev_attr_curr2_input.dev_attr.attr, + &sensor_dev_attr_curr3_input.dev_attr.attr, + &sensor_dev_attr_curr4_input.dev_attr.attr, + + &sensor_dev_attr_curr1_max_alarm.dev_attr.attr, + &sensor_dev_attr_curr2_max_alarm.dev_attr.attr, + &sensor_dev_attr_curr3_max_alarm.dev_attr.attr, + &sensor_dev_attr_curr4_max_alarm.dev_attr.attr, + + &sensor_dev_attr_in5_input.dev_attr.attr, + &sensor_dev_attr_in6_input.dev_attr.attr, + &sensor_dev_attr_in7_input.dev_attr.attr, + &sensor_dev_attr_in8_input.dev_attr.attr, + + &sensor_dev_attr_in5_min_alarm.dev_attr.attr, + &sensor_dev_attr_in6_min_alarm.dev_attr.attr, + &sensor_dev_attr_in7_min_alarm.dev_attr.attr, + &sensor_dev_attr_in8_min_alarm.dev_attr.attr, + + &sensor_dev_attr_in9_input.dev_attr.attr, + &sensor_dev_attr_in10_input.dev_attr.attr, + &sensor_dev_attr_in11_input.dev_attr.attr, + + &sensor_dev_attr_power1_input.dev_attr.attr, + &sensor_dev_attr_power2_input.dev_attr.attr, + &sensor_dev_attr_power3_input.dev_attr.attr, + &sensor_dev_attr_power4_input.dev_attr.attr, + + NULL, +}; + +static const struct attribute_group ltc4245_group = { + .attrs = ltc4245_attributes, +}; + +static int ltc4245_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ltc4245_data *data; + int ret; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto out_kzalloc; + } + + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + /* Initialize the LTC4245 chip */ + /* TODO */ + + /* Register sysfs hooks */ + ret = sysfs_create_group(&client->dev.kobj, <c4245_group); + if (ret) + goto out_sysfs_create_group; + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + ret = PTR_ERR(data->hwmon_dev); + goto out_hwmon_device_register; + } + + return 0; + +out_hwmon_device_register: + sysfs_remove_group(&client->dev.kobj, <c4245_group); +out_sysfs_create_group: + kfree(data); +out_kzalloc: + return ret; +} + +static int ltc4245_remove(struct i2c_client *client) +{ + struct ltc4245_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, <c4245_group); + + kfree(data); + + return 0; +} + +/* Check that some bits in a control register appear at all possible + * locations without changing value + * + * @client: the i2c client to use + * @reg: the register to read + * @bits: the bits to check (0xff checks all bits, + * 0x03 checks only the last two bits) + * + * return -ERRNO if the register read failed + * return -ENODEV if the register value doesn't stay constant at all + * possible addresses + * + * return 0 for success + */ +static int ltc4245_check_control_reg(struct i2c_client *client, u8 reg, u8 bits) +{ + int i; + s32 v, voff1, voff2; + + /* Read register and check for error */ + v = i2c_smbus_read_byte_data(client, reg); + if (v < 0) + return v; + + v &= bits; + + for (i = 0x00; i < 0xff; i += 0x20) { + + voff1 = i2c_smbus_read_byte_data(client, reg + i); + if (voff1 < 0) + return voff1; + + voff2 = i2c_smbus_read_byte_data(client, reg + i + 0x08); + if (voff2 < 0) + return voff2; + + voff1 &= bits; + voff2 &= bits; + + if (v != voff1 || v != voff2) + return -ENODEV; + } + + return 0; +} + +static int ltc4245_detect(struct i2c_client *client, + int kind, + struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = client->adapter; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + if (kind < 0) { /* probed detection - check the chip type */ + s32 v; /* 8 bits from the chip, or -ERRNO */ + + /* Chip registers 0x00-0x07 are control registers + * Chip registers 0x10-0x1f are data registers + * + * Address bits b7-b5 are ignored. This makes the chip "repeat" + * in steps of 0x20. Any control registers should appear with + * the same values across all duplicated addresses. + * + * Register 0x02 bit b2 is reserved, expect 0 + * Register 0x07 bits b7 to b4 are reserved, expect 0 + * + * Registers 0x01, 0x02 are control registers and should not + * change on their own. + * + * Register 0x06 bits b6 and b7 are control bits, and should + * not change on their own. + * + * Register 0x07 bits b3 to b0 are control bits, and should + * not change on their own. + */ + + /* read register 0x02 reserved bit, expect 0 */ + v = i2c_smbus_read_byte_data(client, LTC4245_CONTROL); + if (v < 0 || (v & 0x04) != 0) + return -ENODEV; + + /* read register 0x07 reserved bits, expect 0 */ + v = i2c_smbus_read_byte_data(client, LTC4245_ADCADR); + if (v < 0 || (v & 0xf0) != 0) + return -ENODEV; + + /* check that the alert register appears at all locations */ + if (ltc4245_check_control_reg(client, LTC4245_ALERT, 0xff)) + return -ENODEV; + + /* check that the control register appears at all locations */ + if (ltc4245_check_control_reg(client, LTC4245_CONTROL, 0xff)) + return -ENODEV; + + /* check that register 0x06 bits b6 and b7 stay constant */ + if (ltc4245_check_control_reg(client, LTC4245_GPIO, 0xc0)) + return -ENODEV; + + /* check that register 0x07 bits b3-b0 stay constant */ + if (ltc4245_check_control_reg(client, LTC4245_ADCADR, 0x0f)) + return -ENODEV; + } + + strlcpy(info->type, "ltc4245", I2C_NAME_SIZE); + dev_info(&adapter->dev, "ltc4245 %s at address 0x%02x\n", + kind < 0 ? "probed" : "forced", + client->addr); + + return 0; +} + +static const struct i2c_device_id ltc4245_id[] = { + { "ltc4245", ltc4245 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ltc4245_id); + +/* This is the driver that will be inserted */ +static struct i2c_driver ltc4245_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "ltc4245", + }, + .probe = ltc4245_probe, + .remove = ltc4245_remove, + .id_table = ltc4245_id, + .detect = ltc4245_detect, + .address_data = &addr_data, +}; + +static int __init ltc4245_init(void) +{ + return i2c_add_driver(<c4245_driver); +} + +static void __exit ltc4245_exit(void) +{ + i2c_del_driver(<c4245_driver); +} + +MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>"); +MODULE_DESCRIPTION("LTC4245 driver"); +MODULE_LICENSE("GPL"); + +module_init(ltc4245_init); +module_exit(ltc4245_exit); diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c index fc12bd412e3a..dbfb30c588d8 100644 --- a/drivers/hwmon/w83781d.c +++ b/drivers/hwmon/w83781d.c @@ -58,7 +58,10 @@ static const unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, I2C_CLIENT_END }; /* Insmod parameters */ I2C_CLIENT_INSMOD_4(w83781d, w83782d, w83783s, as99127f); -I2C_CLIENT_MODULE_PARM(force_subclients, "List of subclient addresses: " + +static unsigned short force_subclients[4]; +module_param_array(force_subclients, short, NULL, 0); +MODULE_PARM_DESC(force_subclients, "List of subclient addresses: " "{bus, clientaddr, subclientaddr1, subclientaddr2}"); static int reset; diff --git a/drivers/hwmon/w83791d.c b/drivers/hwmon/w83791d.c index 5768def8a4f2..97851c5ba3a3 100644 --- a/drivers/hwmon/w83791d.c +++ b/drivers/hwmon/w83791d.c @@ -53,7 +53,10 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f, /* Insmod parameters */ I2C_CLIENT_INSMOD_1(w83791d); -I2C_CLIENT_MODULE_PARM(force_subclients, "List of subclient addresses: " + +static unsigned short force_subclients[4]; +module_param_array(force_subclients, short, NULL, 0); +MODULE_PARM_DESC(force_subclients, "List of subclient addresses: " "{bus, clientaddr, subclientaddr1, subclientaddr2}"); static int reset; diff --git a/drivers/hwmon/w83792d.c b/drivers/hwmon/w83792d.c index cf94c5b0c879..2be16194ddf3 100644 --- a/drivers/hwmon/w83792d.c +++ b/drivers/hwmon/w83792d.c @@ -51,7 +51,10 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f, /* Insmod parameters */ I2C_CLIENT_INSMOD_1(w83792d); -I2C_CLIENT_MODULE_PARM(force_subclients, "List of subclient addresses: " + +static unsigned short force_subclients[4]; +module_param_array(force_subclients, short, NULL, 0); +MODULE_PARM_DESC(force_subclients, "List of subclient addresses: " "{bus, clientaddr, subclientaddr1, subclientaddr2}"); static int init; diff --git a/drivers/hwmon/w83793.c b/drivers/hwmon/w83793.c index 0a739f1c69be..47dd398f7258 100644 --- a/drivers/hwmon/w83793.c +++ b/drivers/hwmon/w83793.c @@ -42,7 +42,10 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f, /* Insmod parameters */ I2C_CLIENT_INSMOD_1(w83793); -I2C_CLIENT_MODULE_PARM(force_subclients, "List of subclient addresses: " + +static unsigned short force_subclients[4]; +module_param_array(force_subclients, short, NULL, 0); +MODULE_PARM_DESC(force_subclients, "List of subclient addresses: " "{bus, clientaddr, subclientaddr1, subclientaddr2}"); static int reset; diff --git a/drivers/i2c/busses/i2c-ali1563.c b/drivers/i2c/busses/i2c-ali1563.c index fc3e5b026423..dd9e796fad69 100644 --- a/drivers/i2c/busses/i2c-ali1563.c +++ b/drivers/i2c/busses/i2c-ali1563.c @@ -399,8 +399,8 @@ static int __devinit ali1563_probe(struct pci_dev * dev, if ((error = ali1563_setup(dev))) goto exit; ali1563_adapter.dev.parent = &dev->dev; - sprintf(ali1563_adapter.name,"SMBus ALi 1563 Adapter @ %04x", - ali1563_smba); + snprintf(ali1563_adapter.name, sizeof(ali1563_adapter.name), + "SMBus ALi 1563 Adapter @ %04x", ali1563_smba); if ((error = i2c_add_adapter(&ali1563_adapter))) goto exit_shutdown; return 0; diff --git a/drivers/i2c/busses/i2c-amd756-s4882.c b/drivers/i2c/busses/i2c-amd756-s4882.c index 8ba2bcf727d3..378fcb5d5783 100644 --- a/drivers/i2c/busses/i2c-amd756-s4882.c +++ b/drivers/i2c/busses/i2c-amd756-s4882.c @@ -197,8 +197,8 @@ static int __init amd756_s4882_init(void) for (i = 1; i < 5; i++) { s4882_algo[i] = *(amd756_smbus.algo); s4882_adapter[i] = amd756_smbus; - sprintf(s4882_adapter[i].name, - "SMBus 8111 adapter (CPU%d)", i-1); + snprintf(s4882_adapter[i].name, sizeof(s4882_adapter[i].name), + "SMBus 8111 adapter (CPU%d)", i-1); s4882_adapter[i].algo = s4882_algo+i; s4882_adapter[i].dev.parent = amd756_smbus.dev.parent; } diff --git a/drivers/i2c/busses/i2c-amd756.c b/drivers/i2c/busses/i2c-amd756.c index 424dad6f18d8..36bee5b9c952 100644 --- a/drivers/i2c/busses/i2c-amd756.c +++ b/drivers/i2c/busses/i2c-amd756.c @@ -380,8 +380,9 @@ static int __devinit amd756_probe(struct pci_dev *pdev, /* set up the sysfs linkage to our parent device */ amd756_smbus.dev.parent = &pdev->dev; - sprintf(amd756_smbus.name, "SMBus %s adapter at %04x", - chipname[id->driver_data], amd756_ioport); + snprintf(amd756_smbus.name, sizeof(amd756_smbus.name), + "SMBus %s adapter at %04x", chipname[id->driver_data], + amd756_ioport); error = i2c_add_adapter(&amd756_smbus); if (error) { diff --git a/drivers/i2c/busses/i2c-at91.c b/drivers/i2c/busses/i2c-at91.c index 9efb02137254..67d9dc5b351b 100644 --- a/drivers/i2c/busses/i2c-at91.c +++ b/drivers/i2c/busses/i2c-at91.c @@ -222,7 +222,7 @@ static int __devinit at91_i2c_probe(struct platform_device *pdev) rc = -ENOMEM; goto fail2; } - sprintf(adapter->name, "AT91"); + snprintf(adapter->name, sizeof(adapter->name), "AT91"); adapter->algo = &at91_algorithm; adapter->class = I2C_CLASS_HWMON; adapter->dev.parent = &pdev->dev; diff --git a/drivers/i2c/busses/i2c-bfin-twi.c b/drivers/i2c/busses/i2c-bfin-twi.c index 3c855ff2992f..3fd2c417c1e0 100644 --- a/drivers/i2c/busses/i2c-bfin-twi.c +++ b/drivers/i2c/busses/i2c-bfin-twi.c @@ -656,7 +656,7 @@ static int i2c_bfin_twi_probe(struct platform_device *pdev) strlcpy(p_adap->name, pdev->name, sizeof(p_adap->name)); p_adap->algo = &bfin_twi_algorithm; p_adap->algo_data = iface; - p_adap->class = I2C_CLASS_ALL; + p_adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; p_adap->dev.parent = &pdev->dev; rc = peripheral_request_list(pin_req[pdev->id], "i2c-bfin-twi"); diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 5123eb69a971..526625eaa84b 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -64,7 +64,7 @@ #include <linux/init.h> #include <linux/i2c.h> #include <linux/acpi.h> -#include <asm/io.h> +#include <linux/io.h> /* I801 SMBus address offsets */ #define SMBHSTSTS (0 + i801_smba) @@ -583,6 +583,40 @@ static struct pci_device_id i801_ids[] = { MODULE_DEVICE_TABLE (pci, i801_ids); +#if defined CONFIG_INPUT_APANEL || defined CONFIG_INPUT_APANEL_MODULE +static unsigned char apanel_addr; + +/* Scan the system ROM for the signature "FJKEYINF" */ +static __init const void __iomem *bios_signature(const void __iomem *bios) +{ + ssize_t offset; + const unsigned char signature[] = "FJKEYINF"; + + for (offset = 0; offset < 0x10000; offset += 0x10) { + if (check_signature(bios + offset, signature, + sizeof(signature)-1)) + return bios + offset; + } + return NULL; +} + +static void __init input_apanel_init(void) +{ + void __iomem *bios; + const void __iomem *p; + + bios = ioremap(0xF0000, 0x10000); /* Can't fail */ + p = bios_signature(bios); + if (p) { + /* just use the first address */ + apanel_addr = readb(p + 8 + 3) >> 1; + } + iounmap(bios); +} +#else +static void __init input_apanel_init(void) {} +#endif + static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id *id) { unsigned char temp; @@ -667,6 +701,19 @@ static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id dev_err(&dev->dev, "Failed to add SMBus adapter\n"); goto exit_release; } + + /* Register optional slaves */ +#if defined CONFIG_INPUT_APANEL || defined CONFIG_INPUT_APANEL_MODULE + if (apanel_addr) { + struct i2c_board_info info; + + memset(&info, 0, sizeof(struct i2c_board_info)); + info.addr = apanel_addr; + strlcpy(info.type, "fujitsu_apanel", I2C_NAME_SIZE); + i2c_new_device(&i801_adapter, &info); + } +#endif + return 0; exit_release: @@ -717,6 +764,7 @@ static struct pci_driver i801_driver = { static int __init i2c_i801_init(void) { + input_apanel_init(); return pci_register_driver(&i801_driver); } diff --git a/drivers/i2c/busses/i2c-parport.c b/drivers/i2c/busses/i2c-parport.c index 59ba2086d2f9..a257cd5cd134 100644 --- a/drivers/i2c/busses/i2c-parport.c +++ b/drivers/i2c/busses/i2c-parport.c @@ -189,8 +189,6 @@ static void i2c_parport_attach (struct parport *port) if (adapter_parm[type].init.val) line_set(port, 1, &adapter_parm[type].init); - parport_release(adapter->pdev); - if (i2c_bit_add_bus(&adapter->adapter) < 0) { printk(KERN_ERR "i2c-parport: Unable to register with I2C\n"); goto ERROR1; @@ -202,6 +200,7 @@ static void i2c_parport_attach (struct parport *port) return; ERROR1: + parport_release(adapter->pdev); parport_unregister_device(adapter->pdev); ERROR0: kfree(adapter); @@ -221,6 +220,7 @@ static void i2c_parport_detach (struct parport *port) if (adapter_parm[type].init.val) line_set(port, 0, &adapter_parm[type].init); + parport_release(adapter->pdev); parport_unregister_device(adapter->pdev); if (prev) prev->next = adapter->next; diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c index 906f9b9d715d..6af68146c342 100644 --- a/drivers/i2c/busses/i2c-pxa.c +++ b/drivers/i2c/busses/i2c-pxa.c @@ -1016,7 +1016,7 @@ static int i2c_pxa_probe(struct platform_device *dev) snprintf(i2c->adap.name, sizeof(i2c->adap.name), "pxa_i2c-i2c.%u", i2c->adap.nr); - i2c->clk = clk_get(&dev->dev, "I2CCLK"); + i2c->clk = clk_get(&dev->dev, NULL); if (IS_ERR(i2c->clk)) { ret = PTR_ERR(i2c->clk); goto eclk; @@ -1076,10 +1076,10 @@ static int i2c_pxa_probe(struct platform_device *dev) #ifdef CONFIG_I2C_PXA_SLAVE printk(KERN_INFO "I2C: %s: PXA I2C adapter, slave address %d\n", - i2c->adap.dev.bus_id, i2c->slave_addr); + dev_name(&i2c->adap.dev), i2c->slave_addr); #else printk(KERN_INFO "I2C: %s: PXA I2C adapter\n", - i2c->adap.dev.bus_id); + dev_name(&i2c->adap.dev)); #endif return 0; diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index 1fac4e233133..54af2946de94 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -40,8 +40,8 @@ #include <asm/io.h> #include <mach/regs-gpio.h> -#include <asm/plat-s3c/regs-iic.h> -#include <asm/plat-s3c/iic.h> +#include <plat/regs-iic.h> +#include <plat/iic.h> /* i2c controller state */ @@ -936,7 +936,7 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev) platform_set_drvdata(pdev, i2c); - dev_info(&pdev->dev, "%s: S3C I2C adapter\n", i2c->adap.dev.bus_id); + dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev)); return 0; err_cpufreq: diff --git a/drivers/i2c/busses/i2c-sh7760.c b/drivers/i2c/busses/i2c-sh7760.c index 5e0e254976de..baa28b73ae42 100644 --- a/drivers/i2c/busses/i2c-sh7760.c +++ b/drivers/i2c/busses/i2c-sh7760.c @@ -475,7 +475,7 @@ static int __devinit sh7760_i2c_probe(struct platform_device *pdev) id->adap.nr = pdev->id; id->adap.algo = &sh7760_i2c_algo; - id->adap.class = I2C_CLASS_ALL; + id->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; id->adap.retries = 3; id->adap.algo_data = id; id->adap.dev.parent = &pdev->dev; diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index 640cbb237328..1c01083b01b5 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -160,9 +160,39 @@ struct sh_mobile_i2c_data { static void activate_ch(struct sh_mobile_i2c_data *pd) { + unsigned long i2c_clk; + u_int32_t num; + u_int32_t denom; + u_int32_t tmp; + /* Make sure the clock is enabled */ clk_enable(pd->clk); + /* Get clock rate after clock is enabled */ + i2c_clk = clk_get_rate(pd->clk); + + /* Calculate the value for iccl. From the data sheet: + * iccl = (p clock / transfer rate) * (L / (L + H)) + * where L and H are the SCL low/high ratio (5/4 in this case). + * We also round off the result. + */ + num = i2c_clk * 5; + denom = NORMAL_SPEED * 9; + tmp = num * 10 / denom; + if (tmp % 10 >= 5) + pd->iccl = (u_int8_t)((num/denom) + 1); + else + pd->iccl = (u_int8_t)(num/denom); + + /* Calculate the value for icch. From the data sheet: + icch = (p clock / transfer rate) * (H / (L + H)) */ + num = i2c_clk * 4; + tmp = num * 10 / denom; + if (tmp % 10 >= 5) + pd->icch = (u_int8_t)((num/denom) + 1); + else + pd->icch = (u_int8_t)(num/denom); + /* Enable channel and configure rx ack */ iowrite8(ioread8(ICCR(pd)) | ICCR_ICE, ICCR(pd)); @@ -318,7 +348,8 @@ static int sh_mobile_i2c_isr_rx(struct sh_mobile_i2c_data *pd) } else data = i2c_op(pd, OP_RX, 0); - pd->msg->buf[real_pos] = data; + if (real_pos >= 0) + pd->msg->buf[real_pos] = data; } while (0); pd->pos++; @@ -458,40 +489,6 @@ static struct i2c_algorithm sh_mobile_i2c_algorithm = { .master_xfer = sh_mobile_i2c_xfer, }; -static void sh_mobile_i2c_setup_channel(struct platform_device *dev) -{ - struct sh_mobile_i2c_data *pd = platform_get_drvdata(dev); - unsigned long peripheral_clk = clk_get_rate(pd->clk); - u_int32_t num; - u_int32_t denom; - u_int32_t tmp; - - spin_lock_init(&pd->lock); - init_waitqueue_head(&pd->wait); - - /* Calculate the value for iccl. From the data sheet: - * iccl = (p clock / transfer rate) * (L / (L + H)) - * where L and H are the SCL low/high ratio (5/4 in this case). - * We also round off the result. - */ - num = peripheral_clk * 5; - denom = NORMAL_SPEED * 9; - tmp = num * 10 / denom; - if (tmp % 10 >= 5) - pd->iccl = (u_int8_t)((num/denom) + 1); - else - pd->iccl = (u_int8_t)(num/denom); - - /* Calculate the value for icch. From the data sheet: - icch = (p clock / transfer rate) * (H / (L + H)) */ - num = peripheral_clk * 4; - tmp = num * 10 / denom; - if (tmp % 10 >= 5) - pd->icch = (u_int8_t)((num/denom) + 1); - else - pd->icch = (u_int8_t)(num/denom); -} - static int sh_mobile_i2c_hook_irqs(struct platform_device *dev, int hook) { struct resource *res; @@ -503,7 +500,7 @@ static int sh_mobile_i2c_hook_irqs(struct platform_device *dev, int hook) while ((res = platform_get_resource(dev, IORESOURCE_IRQ, k))) { for (n = res->start; hook && n <= res->end; n++) { if (request_irq(n, sh_mobile_i2c_isr, IRQF_DISABLED, - dev->dev.bus_id, dev)) + dev_name(&dev->dev), dev)) goto rollback; } k++; @@ -532,6 +529,7 @@ static int sh_mobile_i2c_probe(struct platform_device *dev) struct sh_mobile_i2c_data *pd; struct i2c_adapter *adap; struct resource *res; + char clk_name[8]; int size; int ret; @@ -541,9 +539,10 @@ static int sh_mobile_i2c_probe(struct platform_device *dev) return -ENOMEM; } - pd->clk = clk_get(&dev->dev, "peripheral_clk"); + snprintf(clk_name, sizeof(clk_name), "i2c%d", dev->id); + pd->clk = clk_get(&dev->dev, clk_name); if (IS_ERR(pd->clk)) { - dev_err(&dev->dev, "cannot get peripheral clock\n"); + dev_err(&dev->dev, "cannot get clock \"%s\"\n", clk_name); ret = PTR_ERR(pd->clk); goto err; } @@ -585,7 +584,8 @@ static int sh_mobile_i2c_probe(struct platform_device *dev) strlcpy(adap->name, dev->name, sizeof(adap->name)); - sh_mobile_i2c_setup_channel(dev); + spin_lock_init(&pd->lock); + init_waitqueue_head(&pd->wait); ret = i2c_add_numbered_adapter(adap); if (ret < 0) { diff --git a/drivers/i2c/busses/i2c-sis5595.c b/drivers/i2c/busses/i2c-sis5595.c index dfc2d5eb6a68..8ce2daff985c 100644 --- a/drivers/i2c/busses/i2c-sis5595.c +++ b/drivers/i2c/busses/i2c-sis5595.c @@ -389,8 +389,8 @@ static int __devinit sis5595_probe(struct pci_dev *dev, const struct pci_device_ /* set up the sysfs linkage to our parent device */ sis5595_adapter.dev.parent = &dev->dev; - sprintf(sis5595_adapter.name, "SMBus SIS5595 adapter at %04x", - sis5595_base + SMB_INDEX); + snprintf(sis5595_adapter.name, sizeof(sis5595_adapter.name), + "SMBus SIS5595 adapter at %04x", sis5595_base + SMB_INDEX); err = i2c_add_adapter(&sis5595_adapter); if (err) { release_region(sis5595_base + SMB_INDEX, 2); diff --git a/drivers/i2c/busses/i2c-sis630.c b/drivers/i2c/busses/i2c-sis630.c index e7c4b790da54..9c9c016ff2b5 100644 --- a/drivers/i2c/busses/i2c-sis630.c +++ b/drivers/i2c/busses/i2c-sis630.c @@ -487,8 +487,8 @@ static int __devinit sis630_probe(struct pci_dev *dev, const struct pci_device_i /* set up the sysfs linkage to our parent device */ sis630_adapter.dev.parent = &dev->dev; - sprintf(sis630_adapter.name, "SMBus SIS630 adapter at %04x", - acpi_base + SMB_STS); + snprintf(sis630_adapter.name, sizeof(sis630_adapter.name), + "SMBus SIS630 adapter at %04x", acpi_base + SMB_STS); return i2c_add_adapter(&sis630_adapter); } diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig index 4c35702830ce..b2ea73150b9a 100644 --- a/drivers/i2c/chips/Kconfig +++ b/drivers/i2c/chips/Kconfig @@ -114,18 +114,6 @@ config SENSORS_PCF8591 These devices are hard to detect and rarely found on mainstream hardware. If unsure, say N. -config ISP1301_OMAP - tristate "Philips ISP1301 with OMAP OTG" - depends on ARCH_OMAP_OTG - help - If you say yes here you get support for the Philips ISP1301 - USB-On-The-Go transceiver working with the OMAP OTG controller. - The ISP1301 is used in products including H2 and H3 development - boards for Texas Instruments OMAP processors. - - This driver can also be built as a module. If so, the module - will be called isp1301_omap. - config TPS65010 tristate "TPS6501x Power Management chips" depends on GPIOLIB diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile index 23d2a31b0a64..aa7552d5b6bf 100644 --- a/drivers/i2c/chips/Makefile +++ b/drivers/i2c/chips/Makefile @@ -18,7 +18,6 @@ obj-$(CONFIG_SENSORS_PCA9539) += pca9539.o obj-$(CONFIG_SENSORS_PCF8574) += pcf8574.o obj-$(CONFIG_PCF8575) += pcf8575.o obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o -obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o obj-$(CONFIG_TPS65010) += tps65010.o obj-$(CONFIG_MENELAUS) += menelaus.o obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 5a485c22660a..b1c9abe24c7b 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -459,7 +459,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap) pr_debug("I2C adapter driver [%s] forgot to specify " "physical device\n", adap->name); } - sprintf(adap->dev.bus_id, "i2c-%d", adap->nr); + dev_set_name(&adap->dev, "i2c-%d", adap->nr); adap->dev.release = &i2c_adapter_dev_release; adap->dev.class = &i2c_adapter_class; res = device_register(&adap->dev); @@ -631,7 +631,7 @@ int i2c_del_adapter(struct i2c_adapter *adap) /* detach any active clients. This must be done first, because * it can fail; in which case we give up. */ - list_for_each_entry_safe(client, _n, &adap->clients, list) { + list_for_each_entry_safe_reverse(client, _n, &adap->clients, list) { struct i2c_driver *driver; driver = client->driver; @@ -845,8 +845,8 @@ int i2c_attach_client(struct i2c_client *client) } else client->dev.release = i2c_client_dev_release; - snprintf(&client->dev.bus_id[0], sizeof(client->dev.bus_id), - "%d-%04x", i2c_adapter_id(adapter), client->addr); + dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adapter), + client->addr); res = device_register(&client->dev); if (res) goto out_err; @@ -856,7 +856,7 @@ int i2c_attach_client(struct i2c_client *client) mutex_unlock(&adapter->clist_lock); dev_dbg(&adapter->dev, "client [%s] registered with bus id %s\n", - client->name, client->dev.bus_id); + client->name, dev_name(&client->dev)); if (adapter->client_register) { if (adapter->client_register(client)) { diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig index 5f0de824fe7b..8d84fe440da1 100644 --- a/drivers/ide/Kconfig +++ b/drivers/ide/Kconfig @@ -735,7 +735,7 @@ config BLK_DEV_IDE_TX4939 config IDE_ARM tristate "ARM IDE support" - depends on ARM && (ARCH_CLPS7500 || ARCH_RPC || ARCH_SHARK) + depends on ARM && (ARCH_RPC || ARCH_SHARK) default y config BLK_DEV_IDE_ICSIDE diff --git a/drivers/ide/icside.c b/drivers/ide/icside.c index 2d848010499d..81f70caeb40f 100644 --- a/drivers/ide/icside.c +++ b/drivers/ide/icside.c @@ -419,7 +419,7 @@ static void icside_setup_ports(hw_regs_t *hw, void __iomem *base, hw->chipset = ide_acorn; } -static int __init +static int __devinit icside_register_v5(struct icside_state *state, struct expansion_card *ec) { void __iomem *base; @@ -473,7 +473,7 @@ static const struct ide_port_info icside_v6_port_info __initdata = { .swdma_mask = ATA_SWDMA2, }; -static int __init +static int __devinit icside_register_v6(struct icside_state *state, struct expansion_card *ec) { void __iomem *ioc_base, *easi_base; diff --git a/drivers/ide/ide-cs.c b/drivers/ide/ide-cs.c index cb199c815b53..f50210fe558f 100644 --- a/drivers/ide/ide-cs.c +++ b/drivers/ide/ide-cs.c @@ -444,6 +444,7 @@ static struct pcmcia_device_id ide_ids[] = { PCMCIA_DEVICE_PROD_ID1("STI Flash", 0xe4a13209), PCMCIA_DEVICE_PROD_ID12("STI", "Flash 5.0", 0xbf2df18d, 0x8cb57a0e), PCMCIA_MFC_DEVICE_PROD_ID12(1, "SanDisk", "ConnectPlus", 0x7a954bd9, 0x74be00c6), + PCMCIA_DEVICE_PROD_ID2("Flash Card", 0x5a362506), PCMCIA_DEVICE_NULL, }; MODULE_DEVICE_TABLE(pcmcia, ide_ids); diff --git a/drivers/ide/ide_arm.c b/drivers/ide/ide_arm.c index f728f2927b5a..bdcac94d7c1f 100644 --- a/drivers/ide/ide_arm.c +++ b/drivers/ide/ide_arm.c @@ -15,15 +15,8 @@ #define DRV_NAME "ide_arm" -#ifdef CONFIG_ARCH_CLPS7500 -# include <mach/hardware.h> -# -# define IDE_ARM_IO (ISASLOT_IO + 0x1f0) -# define IDE_ARM_IRQ IRQ_ISA_14 -#else -# define IDE_ARM_IO 0x1f0 -# define IDE_ARM_IRQ IRQ_HARDDISK -#endif +#define IDE_ARM_IO 0x1f0 +#define IDE_ARM_IRQ IRQ_HARDDISK static int __init ide_arm_init(void) { diff --git a/drivers/ide/macide.c b/drivers/ide/macide.c index 43f97cc1d30e..3c60064f1d4f 100644 --- a/drivers/ide/macide.c +++ b/drivers/ide/macide.c @@ -18,7 +18,6 @@ #include <linux/delay.h> #include <linux/ide.h> -#include <asm/machw.h> #include <asm/macintosh.h> #include <asm/macints.h> #include <asm/mac_baboon.h> diff --git a/drivers/idle/Kconfig b/drivers/idle/Kconfig index 108264de0ac9..f15e90a453d1 100644 --- a/drivers/idle/Kconfig +++ b/drivers/idle/Kconfig @@ -1,5 +1,6 @@ menu "Memory power savings" +depends on X86_64 config I7300_IDLE_IOAT_CHANNEL bool @@ -7,7 +8,7 @@ config I7300_IDLE_IOAT_CHANNEL config I7300_IDLE tristate "Intel chipset idle memory power saving driver" select I7300_IDLE_IOAT_CHANNEL - depends on X86_64 && EXPERIMENTAL + depends on EXPERIMENTAL help Enable memory power savings when idle with certain Intel server chipsets. The chipset must have I/O AT support, such as the diff --git a/drivers/idle/i7300_idle.c b/drivers/idle/i7300_idle.c index fb176f6ef9f8..17e8ddd01334 100644 --- a/drivers/idle/i7300_idle.c +++ b/drivers/idle/i7300_idle.c @@ -177,7 +177,7 @@ static int __init i7300_idle_ioat_selftest(u8 *ctl, } static struct device dummy_dma_dev = { - .bus_id = "fallback device", + .init_name = "fallback device", .coherent_dma_mask = DMA_64BIT_MASK, .dma_mask = &dummy_dma_dev.coherent_dma_mask, }; diff --git a/drivers/ieee1394/dv1394.c b/drivers/ieee1394/dv1394.c index 965cfdb84ebc..c19f23267157 100644 --- a/drivers/ieee1394/dv1394.c +++ b/drivers/ieee1394/dv1394.c @@ -1270,8 +1270,14 @@ static int dv1394_mmap(struct file *file, struct vm_area_struct *vma) struct video_card *video = file_to_video_card(file); int retval = -EINVAL; - /* serialize mmap */ - mutex_lock(&video->mtx); + /* + * We cannot use the blocking variant mutex_lock here because .mmap + * is called with mmap_sem held, while .ioctl, .read, .write acquire + * video->mtx and subsequently call copy_to/from_user which will + * grab mmap_sem in case of a page fault. + */ + if (!mutex_trylock(&video->mtx)) + return -EAGAIN; if ( ! video_card_initialized(video) ) { retval = do_dv1394_init_default(video); diff --git a/drivers/ieee1394/hosts.c b/drivers/ieee1394/hosts.c index 8dd09d850419..237d0c9d69c6 100644 --- a/drivers/ieee1394/hosts.c +++ b/drivers/ieee1394/hosts.c @@ -155,11 +155,11 @@ struct hpsb_host *hpsb_alloc_host(struct hpsb_host_driver *drv, size_t extra, memcpy(&h->device, &nodemgr_dev_template_host, sizeof(h->device)); h->device.parent = dev; set_dev_node(&h->device, dev_to_node(dev)); - snprintf(h->device.bus_id, BUS_ID_SIZE, "fw-host%d", h->id); + dev_set_name(&h->device, "fw-host%d", h->id); h->host_dev.parent = &h->device; h->host_dev.class = &hpsb_host_class; - snprintf(h->host_dev.bus_id, BUS_ID_SIZE, "fw-host%d", h->id); + dev_set_name(&h->host_dev, "fw-host%d", h->id); if (device_register(&h->device)) goto fail; diff --git a/drivers/ieee1394/nodemgr.c b/drivers/ieee1394/nodemgr.c index 2376b729e876..9e39f73282ee 100644 --- a/drivers/ieee1394/nodemgr.c +++ b/drivers/ieee1394/nodemgr.c @@ -826,13 +826,11 @@ static struct node_entry *nodemgr_create_node(octlet_t guid, memcpy(&ne->device, &nodemgr_dev_template_ne, sizeof(ne->device)); ne->device.parent = &host->device; - snprintf(ne->device.bus_id, BUS_ID_SIZE, "%016Lx", - (unsigned long long)(ne->guid)); + dev_set_name(&ne->device, "%016Lx", (unsigned long long)(ne->guid)); ne->node_dev.parent = &ne->device; ne->node_dev.class = &nodemgr_ne_class; - snprintf(ne->node_dev.bus_id, BUS_ID_SIZE, "%016Lx", - (unsigned long long)(ne->guid)); + dev_set_name(&ne->node_dev, "%016Lx", (unsigned long long)(ne->guid)); if (device_register(&ne->device)) goto fail_devreg; @@ -932,13 +930,11 @@ static void nodemgr_register_device(struct node_entry *ne, ud->device.parent = parent; - snprintf(ud->device.bus_id, BUS_ID_SIZE, "%s-%u", - ne->device.bus_id, ud->id); + dev_set_name(&ud->device, "%s-%u", dev_name(&ne->device), ud->id); ud->unit_dev.parent = &ud->device; ud->unit_dev.class = &nodemgr_ud_class; - snprintf(ud->unit_dev.bus_id, BUS_ID_SIZE, "%s-%u", - ne->device.bus_id, ud->id); + dev_set_name(&ud->unit_dev, "%s-%u", dev_name(&ne->device), ud->id); if (device_register(&ud->device)) goto fail_devreg; @@ -953,7 +949,7 @@ static void nodemgr_register_device(struct node_entry *ne, fail_classdevreg: device_unregister(&ud->device); fail_devreg: - HPSB_ERR("Failed to create unit %s", ud->device.bus_id); + HPSB_ERR("Failed to create unit %s", dev_name(&ud->device)); } diff --git a/drivers/ieee1394/raw1394.c b/drivers/ieee1394/raw1394.c index 9f19ac492106..bf7e761c12b1 100644 --- a/drivers/ieee1394/raw1394.c +++ b/drivers/ieee1394/raw1394.c @@ -2268,7 +2268,8 @@ static ssize_t raw1394_write(struct file *file, const char __user * buffer, return -EFAULT; } - mutex_lock(&fi->state_mutex); + if (!mutex_trylock(&fi->state_mutex)) + return -EAGAIN; switch (fi->state) { case opened: @@ -2548,7 +2549,8 @@ static int raw1394_mmap(struct file *file, struct vm_area_struct *vma) struct file_info *fi = file->private_data; int ret; - mutex_lock(&fi->state_mutex); + if (!mutex_trylock(&fi->state_mutex)) + return -EAGAIN; if (fi->iso_state == RAW1394_ISO_INACTIVE) ret = -EINVAL; @@ -2669,7 +2671,8 @@ static long raw1394_ioctl(struct file *file, unsigned int cmd, break; } - mutex_lock(&fi->state_mutex); + if (!mutex_trylock(&fi->state_mutex)) + return -EAGAIN; switch (fi->iso_state) { case RAW1394_ISO_INACTIVE: diff --git a/drivers/infiniband/core/sysfs.c b/drivers/infiniband/core/sysfs.c index 4d1042115598..6167649376a8 100644 --- a/drivers/infiniband/core/sysfs.c +++ b/drivers/infiniband/core/sysfs.c @@ -786,7 +786,7 @@ int ib_device_register_sysfs(struct ib_device *device) class_dev->class = &ib_class; class_dev->driver_data = device; class_dev->parent = device->dma_device; - strlcpy(class_dev->bus_id, device->name, BUS_ID_SIZE); + dev_set_name(class_dev, device->name); INIT_LIST_HEAD(&device->port_list); diff --git a/drivers/infiniband/core/ucm.c b/drivers/infiniband/core/ucm.c index e603736682bf..51bd9669cb1f 100644 --- a/drivers/infiniband/core/ucm.c +++ b/drivers/infiniband/core/ucm.c @@ -1266,8 +1266,7 @@ static void ib_ucm_add_one(struct ib_device *device) ucm_dev->dev.parent = device->dma_device; ucm_dev->dev.devt = ucm_dev->cdev.dev; ucm_dev->dev.release = ib_ucm_release_dev; - snprintf(ucm_dev->dev.bus_id, BUS_ID_SIZE, "ucm%d", - ucm_dev->devnum); + dev_set_name(&ucm_dev->dev, "ucm%d", ucm_dev->devnum); if (device_register(&ucm_dev->dev)) goto err_cdev; diff --git a/drivers/infiniband/hw/cxgb3/iwch_provider.c b/drivers/infiniband/hw/cxgb3/iwch_provider.c index ecff98043589..160ef482712d 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_provider.c +++ b/drivers/infiniband/hw/cxgb3/iwch_provider.c @@ -1102,9 +1102,7 @@ static u64 fw_vers_string_to_u64(struct iwch_dev *iwch_dev) char *cp, *next; unsigned fw_maj, fw_min, fw_mic; - rtnl_lock(); lldev->ethtool_ops->get_drvinfo(lldev, &info); - rtnl_unlock(); next = info.fw_version + 1; cp = strsep(&next, "."); @@ -1192,9 +1190,7 @@ static ssize_t show_fw_ver(struct device *dev, struct device_attribute *attr, ch struct net_device *lldev = iwch_dev->rdev.t3cdev_p->lldev; PDBG("%s dev 0x%p\n", __func__, dev); - rtnl_lock(); lldev->ethtool_ops->get_drvinfo(lldev, &info); - rtnl_unlock(); return sprintf(buf, "%s\n", info.fw_version); } @@ -1207,9 +1203,7 @@ static ssize_t show_hca(struct device *dev, struct device_attribute *attr, struct net_device *lldev = iwch_dev->rdev.t3cdev_p->lldev; PDBG("%s dev 0x%p\n", __func__, dev); - rtnl_lock(); lldev->ethtool_ops->get_drvinfo(lldev, &info); - rtnl_unlock(); return sprintf(buf, "%s\n", info.driver); } diff --git a/drivers/infiniband/hw/cxgb3/iwch_qp.c b/drivers/infiniband/hw/cxgb3/iwch_qp.c index 3e4585c2318a..19661b2f0406 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_qp.c +++ b/drivers/infiniband/hw/cxgb3/iwch_qp.c @@ -745,7 +745,6 @@ int iwch_post_zb_read(struct iwch_qp *qhp) wqe->read.rdmaop = T3_READ_REQ; wqe->read.reserved[0] = 0; wqe->read.reserved[1] = 0; - wqe->read.reserved[2] = 0; wqe->read.rem_stag = cpu_to_be32(1); wqe->read.rem_to = cpu_to_be64(1); wqe->read.local_stag = cpu_to_be32(1); diff --git a/drivers/infiniband/hw/ehca/ehca_irq.c b/drivers/infiniband/hw/ehca/ehca_irq.c index cb55be04442c..757035ea246f 100644 --- a/drivers/infiniband/hw/ehca/ehca_irq.c +++ b/drivers/infiniband/hw/ehca/ehca_irq.c @@ -359,36 +359,48 @@ static void notify_port_conf_change(struct ehca_shca *shca, int port_num) *old_attr = new_attr; } +/* replay modify_qp for sqps -- return 0 if all is well, 1 if AQP1 destroyed */ +static int replay_modify_qp(struct ehca_sport *sport) +{ + int aqp1_destroyed; + unsigned long flags; + + spin_lock_irqsave(&sport->mod_sqp_lock, flags); + + aqp1_destroyed = !sport->ibqp_sqp[IB_QPT_GSI]; + + if (sport->ibqp_sqp[IB_QPT_SMI]) + ehca_recover_sqp(sport->ibqp_sqp[IB_QPT_SMI]); + if (!aqp1_destroyed) + ehca_recover_sqp(sport->ibqp_sqp[IB_QPT_GSI]); + + spin_unlock_irqrestore(&sport->mod_sqp_lock, flags); + + return aqp1_destroyed; +} + static void parse_ec(struct ehca_shca *shca, u64 eqe) { u8 ec = EHCA_BMASK_GET(NEQE_EVENT_CODE, eqe); u8 port = EHCA_BMASK_GET(NEQE_PORT_NUMBER, eqe); u8 spec_event; struct ehca_sport *sport = &shca->sport[port - 1]; - unsigned long flags; switch (ec) { case 0x30: /* port availability change */ if (EHCA_BMASK_GET(NEQE_PORT_AVAILABILITY, eqe)) { - int suppress_event; - /* replay modify_qp for sqps */ - spin_lock_irqsave(&sport->mod_sqp_lock, flags); - suppress_event = !sport->ibqp_sqp[IB_QPT_GSI]; - if (sport->ibqp_sqp[IB_QPT_SMI]) - ehca_recover_sqp(sport->ibqp_sqp[IB_QPT_SMI]); - if (!suppress_event) - ehca_recover_sqp(sport->ibqp_sqp[IB_QPT_GSI]); - spin_unlock_irqrestore(&sport->mod_sqp_lock, flags); - - /* AQP1 was destroyed, ignore this event */ - if (suppress_event) - break; + /* only replay modify_qp calls in autodetect mode; + * if AQP1 was destroyed, the port is already down + * again and we can drop the event. + */ + if (ehca_nr_ports < 0) + if (replay_modify_qp(sport)) + break; sport->port_state = IB_PORT_ACTIVE; dispatch_port_event(shca, port, IB_EVENT_PORT_ACTIVE, "is active"); - ehca_query_sma_attr(shca, port, - &sport->saved_attr); + ehca_query_sma_attr(shca, port, &sport->saved_attr); } else { sport->port_state = IB_PORT_DOWN; dispatch_port_event(shca, port, IB_EVENT_PORT_ERR, diff --git a/drivers/infiniband/hw/ehca/ehca_qp.c b/drivers/infiniband/hw/ehca/ehca_qp.c index 4d54b9f64567..9e05ee2db39b 100644 --- a/drivers/infiniband/hw/ehca/ehca_qp.c +++ b/drivers/infiniband/hw/ehca/ehca_qp.c @@ -860,6 +860,11 @@ static struct ehca_qp *internal_create_qp( if (qp_type == IB_QPT_GSI) { h_ret = ehca_define_sqp(shca, my_qp, init_attr); if (h_ret != H_SUCCESS) { + kfree(my_qp->mod_qp_parm); + my_qp->mod_qp_parm = NULL; + /* the QP pointer is no longer valid */ + shca->sport[init_attr->port_num - 1].ibqp_sqp[qp_type] = + NULL; ret = ehca2ib_return_code(h_ret); goto create_qp_exit6; } diff --git a/drivers/infiniband/hw/ipath/ipath_ruc.c b/drivers/infiniband/hw/ipath/ipath_ruc.c index fc0f6d9e6030..2296832f94da 100644 --- a/drivers/infiniband/hw/ipath/ipath_ruc.c +++ b/drivers/infiniband/hw/ipath/ipath_ruc.c @@ -156,7 +156,7 @@ bail: /** * ipath_get_rwqe - copy the next RWQE into the QP's RWQE * @qp: the QP - * @wr_id_only: update wr_id only, not SGEs + * @wr_id_only: update qp->r_wr_id only, not qp->r_sge * * Return 0 if no RWQE is available, otherwise return 1. * @@ -173,8 +173,6 @@ int ipath_get_rwqe(struct ipath_qp *qp, int wr_id_only) u32 tail; int ret; - qp->r_sge.sg_list = qp->r_sg_list; - if (qp->ibqp.srq) { srq = to_isrq(qp->ibqp.srq); handler = srq->ibsrq.event_handler; @@ -206,8 +204,10 @@ int ipath_get_rwqe(struct ipath_qp *qp, int wr_id_only) wqe = get_rwqe_ptr(rq, tail); if (++tail >= rq->size) tail = 0; - } while (!wr_id_only && !ipath_init_sge(qp, wqe, &qp->r_len, - &qp->r_sge)); + if (wr_id_only) + break; + qp->r_sge.sg_list = qp->r_sg_list; + } while (!ipath_init_sge(qp, wqe, &qp->r_len, &qp->r_sge)); qp->r_wr_id = wqe->wr_id; wq->tail = tail; diff --git a/drivers/infiniband/hw/mlx4/mr.c b/drivers/infiniband/hw/mlx4/mr.c index 87f5c5a87b98..8e4d26d56a95 100644 --- a/drivers/infiniband/hw/mlx4/mr.c +++ b/drivers/infiniband/hw/mlx4/mr.c @@ -205,6 +205,7 @@ struct ib_mr *mlx4_ib_alloc_fast_reg_mr(struct ib_pd *pd, goto err_mr; mr->ibmr.rkey = mr->ibmr.lkey = mr->mmr.key; + mr->umem = NULL; return &mr->ibmr; diff --git a/drivers/infiniband/hw/nes/nes.c b/drivers/infiniband/hw/nes/nes.c index a2b04d62b1a4..aa1dc41f04c8 100644 --- a/drivers/infiniband/hw/nes/nes.c +++ b/drivers/infiniband/hw/nes/nes.c @@ -95,6 +95,10 @@ unsigned int wqm_quanta = 0x10000; module_param(wqm_quanta, int, 0644); MODULE_PARM_DESC(wqm_quanta, "WQM quanta"); +static unsigned int limit_maxrdreqsz; +module_param(limit_maxrdreqsz, bool, 0644); +MODULE_PARM_DESC(limit_maxrdreqsz, "Limit max read request size to 256 Bytes"); + LIST_HEAD(nes_adapter_list); static LIST_HEAD(nes_dev_list); @@ -588,6 +592,18 @@ static int __devinit nes_probe(struct pci_dev *pcidev, const struct pci_device_i nesdev->nesadapter->port_count; } + if ((limit_maxrdreqsz || + ((nesdev->nesadapter->phy_type[0] == NES_PHY_TYPE_GLADIUS) && + (hw_rev == NE020_REV1))) && + (pcie_get_readrq(pcidev) > 256)) { + if (pcie_set_readrq(pcidev, 256)) + printk(KERN_ERR PFX "Unable to set max read request" + " to 256 bytes\n"); + else + nes_debug(NES_DBG_INIT, "Max read request size set" + " to 256 bytes\n"); + } + tasklet_init(&nesdev->dpc_tasklet, nes_dpc, (unsigned long)nesdev); /* bring up the Control QP */ diff --git a/drivers/infiniband/hw/nes/nes_hw.h b/drivers/infiniband/hw/nes/nes_hw.h index 610b9d859597..bc0b4de04450 100644 --- a/drivers/infiniband/hw/nes/nes_hw.h +++ b/drivers/infiniband/hw/nes/nes_hw.h @@ -40,6 +40,7 @@ #define NES_PHY_TYPE_ARGUS 4 #define NES_PHY_TYPE_PUMA_1G 5 #define NES_PHY_TYPE_PUMA_10G 6 +#define NES_PHY_TYPE_GLADIUS 7 #define NES_MULTICAST_PF_MAX 8 diff --git a/drivers/infiniband/hw/nes/nes_verbs.c b/drivers/infiniband/hw/nes/nes_verbs.c index 932e56fcf774..d36c9a0bf1bb 100644 --- a/drivers/infiniband/hw/nes/nes_verbs.c +++ b/drivers/infiniband/hw/nes/nes_verbs.c @@ -220,14 +220,14 @@ static int nes_bind_mw(struct ib_qp *ibqp, struct ib_mw *ibmw, if (nesqp->ibqp_state > IB_QPS_RTS) return -EINVAL; - spin_lock_irqsave(&nesqp->lock, flags); + spin_lock_irqsave(&nesqp->lock, flags); head = nesqp->hwqp.sq_head; qsize = nesqp->hwqp.sq_tail; /* Check for SQ overflow */ if (((head + (2 * qsize) - nesqp->hwqp.sq_tail) % qsize) == (qsize - 1)) { - spin_unlock_irqrestore(&nesqp->lock, flags); + spin_unlock_irqrestore(&nesqp->lock, flags); return -EINVAL; } @@ -269,7 +269,7 @@ static int nes_bind_mw(struct ib_qp *ibqp, struct ib_mw *ibmw, nes_write32(nesdev->regs+NES_WQE_ALLOC, (1 << 24) | 0x00800000 | nesqp->hwqp.qp_id); - spin_unlock_irqrestore(&nesqp->lock, flags); + spin_unlock_irqrestore(&nesqp->lock, flags); return 0; } @@ -349,7 +349,7 @@ static struct ib_fmr *nes_alloc_fmr(struct ib_pd *ibpd, if (nesfmr->nesmr.pbls_used > nesadapter->free_4kpbl) { spin_unlock_irqrestore(&nesadapter->pbl_lock, flags); ret = -ENOMEM; - goto failed_vpbl_alloc; + goto failed_vpbl_avail; } else { nesadapter->free_4kpbl -= nesfmr->nesmr.pbls_used; } @@ -357,7 +357,7 @@ static struct ib_fmr *nes_alloc_fmr(struct ib_pd *ibpd, if (nesfmr->nesmr.pbls_used > nesadapter->free_256pbl) { spin_unlock_irqrestore(&nesadapter->pbl_lock, flags); ret = -ENOMEM; - goto failed_vpbl_alloc; + goto failed_vpbl_avail; } else { nesadapter->free_256pbl -= nesfmr->nesmr.pbls_used; } @@ -391,14 +391,14 @@ static struct ib_fmr *nes_alloc_fmr(struct ib_pd *ibpd, goto failed_vpbl_alloc; } - nesfmr->root_vpbl.leaf_vpbl = kzalloc(sizeof(*nesfmr->root_vpbl.leaf_vpbl)*1024, GFP_KERNEL); + nesfmr->leaf_pbl_cnt = nesfmr->nesmr.pbls_used-1; + nesfmr->root_vpbl.leaf_vpbl = kzalloc(sizeof(*nesfmr->root_vpbl.leaf_vpbl)*1024, GFP_ATOMIC); if (!nesfmr->root_vpbl.leaf_vpbl) { spin_unlock_irqrestore(&nesadapter->pbl_lock, flags); ret = -ENOMEM; goto failed_leaf_vpbl_alloc; } - nesfmr->leaf_pbl_cnt = nesfmr->nesmr.pbls_used-1; nes_debug(NES_DBG_MR, "two level pbl, root_vpbl.pbl_vbase=%p" " leaf_pbl_cnt=%d root_vpbl.leaf_vpbl=%p\n", nesfmr->root_vpbl.pbl_vbase, nesfmr->leaf_pbl_cnt, nesfmr->root_vpbl.leaf_vpbl); @@ -519,6 +519,16 @@ static struct ib_fmr *nes_alloc_fmr(struct ib_pd *ibpd, nesfmr->root_vpbl.pbl_pbase); failed_vpbl_alloc: + if (nesfmr->nesmr.pbls_used != 0) { + spin_lock_irqsave(&nesadapter->pbl_lock, flags); + if (nesfmr->nesmr.pbl_4k) + nesadapter->free_4kpbl += nesfmr->nesmr.pbls_used; + else + nesadapter->free_256pbl += nesfmr->nesmr.pbls_used; + spin_unlock_irqrestore(&nesadapter->pbl_lock, flags); + } + +failed_vpbl_avail: kfree(nesfmr); failed_fmr_alloc: @@ -534,18 +544,14 @@ static struct ib_fmr *nes_alloc_fmr(struct ib_pd *ibpd, */ static int nes_dealloc_fmr(struct ib_fmr *ibfmr) { + unsigned long flags; struct nes_mr *nesmr = to_nesmr_from_ibfmr(ibfmr); struct nes_fmr *nesfmr = to_nesfmr(nesmr); struct nes_vnic *nesvnic = to_nesvnic(ibfmr->device); struct nes_device *nesdev = nesvnic->nesdev; - struct nes_mr temp_nesmr = *nesmr; + struct nes_adapter *nesadapter = nesdev->nesadapter; int i = 0; - temp_nesmr.ibmw.device = ibfmr->device; - temp_nesmr.ibmw.pd = ibfmr->pd; - temp_nesmr.ibmw.rkey = ibfmr->rkey; - temp_nesmr.ibmw.uobject = NULL; - /* free the resources */ if (nesfmr->leaf_pbl_cnt == 0) { /* single PBL case */ @@ -561,8 +567,24 @@ static int nes_dealloc_fmr(struct ib_fmr *ibfmr) pci_free_consistent(nesdev->pcidev, 8192, nesfmr->root_vpbl.pbl_vbase, nesfmr->root_vpbl.pbl_pbase); } + nesmr->ibmw.device = ibfmr->device; + nesmr->ibmw.pd = ibfmr->pd; + nesmr->ibmw.rkey = ibfmr->rkey; + nesmr->ibmw.uobject = NULL; - return nes_dealloc_mw(&temp_nesmr.ibmw); + if (nesfmr->nesmr.pbls_used != 0) { + spin_lock_irqsave(&nesadapter->pbl_lock, flags); + if (nesfmr->nesmr.pbl_4k) { + nesadapter->free_4kpbl += nesfmr->nesmr.pbls_used; + WARN_ON(nesadapter->free_4kpbl > nesadapter->max_4kpbl); + } else { + nesadapter->free_256pbl += nesfmr->nesmr.pbls_used; + WARN_ON(nesadapter->free_256pbl > nesadapter->max_256pbl); + } + spin_unlock_irqrestore(&nesadapter->pbl_lock, flags); + } + + return nes_dealloc_mw(&nesmr->ibmw); } @@ -1595,7 +1617,7 @@ static struct ib_cq *nes_create_cq(struct ib_device *ibdev, int entries, nes_ucontext->mcrqf = req.mcrqf; if (nes_ucontext->mcrqf) { if (nes_ucontext->mcrqf & 0x80000000) - nescq->hw_cq.cq_number = nesvnic->nic.qp_id + 12 + (nes_ucontext->mcrqf & 0xf) - 1; + nescq->hw_cq.cq_number = nesvnic->nic.qp_id + 28 + 2 * ((nes_ucontext->mcrqf & 0xf) - 1); else if (nes_ucontext->mcrqf & 0x40000000) nescq->hw_cq.cq_number = nes_ucontext->mcrqf & 0xffff; else @@ -3212,7 +3234,7 @@ static int nes_post_send(struct ib_qp *ibqp, struct ib_send_wr *ib_wr, if (nesqp->ibqp_state > IB_QPS_RTS) return -EINVAL; - spin_lock_irqsave(&nesqp->lock, flags); + spin_lock_irqsave(&nesqp->lock, flags); head = nesqp->hwqp.sq_head; @@ -3337,7 +3359,7 @@ static int nes_post_send(struct ib_qp *ibqp, struct ib_send_wr *ib_wr, (counter << 24) | 0x00800000 | nesqp->hwqp.qp_id); } - spin_unlock_irqrestore(&nesqp->lock, flags); + spin_unlock_irqrestore(&nesqp->lock, flags); if (err) *bad_wr = ib_wr; @@ -3368,7 +3390,7 @@ static int nes_post_recv(struct ib_qp *ibqp, struct ib_recv_wr *ib_wr, if (nesqp->ibqp_state > IB_QPS_RTS) return -EINVAL; - spin_lock_irqsave(&nesqp->lock, flags); + spin_lock_irqsave(&nesqp->lock, flags); head = nesqp->hwqp.rq_head; @@ -3421,7 +3443,7 @@ static int nes_post_recv(struct ib_qp *ibqp, struct ib_recv_wr *ib_wr, nes_write32(nesdev->regs+NES_WQE_ALLOC, (counter<<24) | nesqp->hwqp.qp_id); } - spin_unlock_irqrestore(&nesqp->lock, flags); + spin_unlock_irqrestore(&nesqp->lock, flags); if (err) *bad_wr = ib_wr; @@ -3453,7 +3475,7 @@ static int nes_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *entry) nes_debug(NES_DBG_CQ, "\n"); - spin_lock_irqsave(&nescq->lock, flags); + spin_lock_irqsave(&nescq->lock, flags); head = nescq->hw_cq.cq_head; cq_size = nescq->hw_cq.cq_size; @@ -3562,7 +3584,7 @@ static int nes_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *entry) nes_debug(NES_DBG_CQ, "Reporting %u completions for CQ%u.\n", cqe_count, nescq->hw_cq.cq_number); - spin_unlock_irqrestore(&nescq->lock, flags); + spin_unlock_irqrestore(&nescq->lock, flags); return cqe_count; } diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c index fddded7900d1..85257f6b9576 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -106,12 +106,13 @@ int ipoib_open(struct net_device *dev) ipoib_dbg(priv, "bringing up interface\n"); - napi_enable(&priv->napi); set_bit(IPOIB_FLAG_ADMIN_UP, &priv->flags); if (ipoib_pkey_dev_delay_open(dev)) return 0; + napi_enable(&priv->napi); + if (ipoib_ib_dev_open(dev)) { napi_disable(&priv->napi); return -EINVAL; @@ -546,6 +547,7 @@ static int path_rec_start(struct net_device *dev, if (path->query_id < 0) { ipoib_warn(priv, "ib_sa_path_rec_get failed: %d\n", path->query_id); path->query = NULL; + complete(&path->done); return path->query_id; } @@ -662,7 +664,7 @@ static void unicast_arp_send(struct sk_buff *skb, struct net_device *dev, skb_push(skb, sizeof *phdr); __skb_queue_tail(&path->queue, skb); - if (path_rec_start(dev, path)) { + if (!path->query && path_rec_start(dev, path)) { spin_unlock_irqrestore(&priv->lock, flags); path_free(dev, path); return; diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c index 5b8b533f2908..e5ebcbea5907 100644 --- a/drivers/infiniband/ulp/srp/ib_srp.c +++ b/drivers/infiniband/ulp/srp/ib_srp.c @@ -1972,8 +1972,7 @@ static struct srp_host *srp_add_port(struct srp_device *device, u8 port) host->dev.class = &srp_class; host->dev.parent = device->dev->dma_device; - snprintf(host->dev.bus_id, BUS_ID_SIZE, "srp-%s-%d", - device->dev->name, port); + dev_set_name(&host->dev, "srp-%s-%d", device->dev->name, port); if (device_register(&host->dev)) goto free_host; diff --git a/drivers/input/ff-memless.c b/drivers/input/ff-memless.c index 6790e975a98c..bc4e40f3ede7 100644 --- a/drivers/input/ff-memless.c +++ b/drivers/input/ff-memless.c @@ -397,8 +397,9 @@ static int ml_ff_playback(struct input_dev *dev, int effect_id, int value) { struct ml_device *ml = dev->ff->private; struct ml_effect_state *state = &ml->states[effect_id]; + unsigned long flags; - spin_lock_bh(&ml->timer_lock); + spin_lock_irqsave(&ml->timer_lock, flags); if (value > 0) { debug("initiated play"); @@ -424,7 +425,7 @@ static int ml_ff_playback(struct input_dev *dev, int effect_id, int value) ml_play_effects(ml); } - spin_unlock_bh(&ml->timer_lock); + spin_unlock_irqrestore(&ml->timer_lock, flags); return 0; } diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index 22016ca15351..379b7ff354ec 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -824,7 +824,7 @@ static void atkbd_disconnect(struct serio *serio) atkbd_disable(atkbd); /* make sure we don't have a command in flight */ - flush_scheduled_work(); + cancel_delayed_work_sync(&atkbd->event_work); sysfs_remove_group(&serio->dev.kobj, &atkbd_attribute_group); input_unregister_device(atkbd->dev); @@ -868,6 +868,22 @@ static void atkbd_hp_keymap_fixup(struct atkbd *atkbd) } /* + * Inventec system with broken key release on volume keys + */ +static void atkbd_inventec_keymap_fixup(struct atkbd *atkbd) +{ + const unsigned int forced_release_keys[] = { + 0xae, 0xb0, + }; + int i; + + if (atkbd->set == 2) + for (i = 0; i < ARRAY_SIZE(forced_release_keys); i++) + __set_bit(forced_release_keys[i], + atkbd->force_release_mask); +} + +/* * atkbd_set_keycode_table() initializes keyboard's keycode table * according to the selected scancode set */ @@ -1468,6 +1484,15 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { .callback = atkbd_setup_fixup, .driver_data = atkbd_hp_keymap_fixup, }, + { + .ident = "Inventec Symphony", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "INVENTEC"), + DMI_MATCH(DMI_PRODUCT_NAME, "SYMPHONY 6.0/7.0"), + }, + .callback = atkbd_setup_fixup, + .driver_data = atkbd_inventec_keymap_fixup, + }, { } }; diff --git a/drivers/input/keyboard/pxa27x_keypad.c b/drivers/input/keyboard/pxa27x_keypad.c index 6d30c6d334c3..0d2fc64a5e1c 100644 --- a/drivers/input/keyboard/pxa27x_keypad.c +++ b/drivers/input/keyboard/pxa27x_keypad.c @@ -475,7 +475,7 @@ static int __devinit pxa27x_keypad_probe(struct platform_device *pdev) goto failed_free_mem; } - keypad->clk = clk_get(&pdev->dev, "KBDCLK"); + keypad->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(keypad->clk)) { dev_err(&pdev->dev, "failed to get keypad clock\n"); error = PTR_ERR(keypad->clk); diff --git a/drivers/input/keyboard/sh_keysc.c b/drivers/input/keyboard/sh_keysc.c index c600ab7f93e8..5c8a1bcf7ca7 100644 --- a/drivers/input/keyboard/sh_keysc.c +++ b/drivers/input/keyboard/sh_keysc.c @@ -18,6 +18,7 @@ #include <linux/delay.h> #include <linux/platform_device.h> #include <linux/input.h> +#include <linux/clk.h> #include <linux/io.h> #include <asm/sh_keysc.h> @@ -39,6 +40,7 @@ static const struct { struct sh_keysc_priv { void __iomem *iomem_base; + struct clk *clk; unsigned long last_keys; struct input_dev *input; struct sh_keysc_info pdata; @@ -125,6 +127,7 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev) struct sh_keysc_info *pdata; struct resource *res; struct input_dev *input; + char clk_name[8]; int i, k; int irq, error; @@ -165,11 +168,19 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev) goto err1; } + snprintf(clk_name, sizeof(clk_name), "keysc%d", pdev->id); + priv->clk = clk_get(&pdev->dev, clk_name); + if (IS_ERR(priv->clk)) { + dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name); + error = PTR_ERR(priv->clk); + goto err2; + } + priv->input = input_allocate_device(); if (!priv->input) { dev_err(&pdev->dev, "failed to allocate input device\n"); error = -ENOMEM; - goto err2; + goto err3; } input = priv->input; @@ -187,7 +198,7 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev) error = request_irq(irq, sh_keysc_isr, 0, pdev->name, pdev); if (error) { dev_err(&pdev->dev, "failed to request IRQ\n"); - goto err3; + goto err4; } for (i = 0; i < SH_KEYSC_MAXKEYS; i++) { @@ -199,18 +210,22 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev) error = input_register_device(input); if (error) { dev_err(&pdev->dev, "failed to register input device\n"); - goto err4; + goto err5; } + clk_enable(priv->clk); + iowrite16((sh_keysc_mode[pdata->mode].kymd << 8) | pdata->scan_timing, priv->iomem_base + KYCR1_OFFS); iowrite16(0, priv->iomem_base + KYOUTDR_OFFS); iowrite16(KYCR2_IRQ_LEVEL, priv->iomem_base + KYCR2_OFFS); return 0; - err4: + err5: free_irq(irq, pdev); - err3: + err4: input_free_device(input); + err3: + clk_put(priv->clk); err2: iounmap(priv->iomem_base); err1: @@ -230,6 +245,9 @@ static int __devexit sh_keysc_remove(struct platform_device *pdev) free_irq(platform_get_irq(pdev, 0), pdev); iounmap(priv->iomem_base); + clk_disable(priv->clk); + clk_put(priv->clk); + platform_set_drvdata(pdev, NULL); kfree(priv); return 0; diff --git a/drivers/input/misc/apanel.c b/drivers/input/misc/apanel.c index d82f7f727f7a..71b82434264d 100644 --- a/drivers/input/misc/apanel.c +++ b/drivers/input/misc/apanel.c @@ -57,7 +57,7 @@ static enum apanel_chip device_chip[APANEL_DEV_MAX]; struct apanel { struct input_polled_dev *ipdev; - struct i2c_client client; + struct i2c_client *client; unsigned short keymap[MAX_PANEL_KEYS]; u16 nkeys; u16 led_bits; @@ -66,16 +66,7 @@ struct apanel { }; -static int apanel_probe(struct i2c_adapter *, int, int); - -/* for now, we only support one address */ -static unsigned short normal_i2c[] = {0, I2C_CLIENT_END}; -static unsigned short ignore = I2C_CLIENT_END; -static struct i2c_client_address_data addr_data = { - .normal_i2c = normal_i2c, - .probe = &ignore, - .ignore = &ignore, -}; +static int apanel_probe(struct i2c_client *, const struct i2c_device_id *); static void report_key(struct input_dev *input, unsigned keycode) { @@ -103,12 +94,12 @@ static void apanel_poll(struct input_polled_dev *ipdev) s32 data; int i; - data = i2c_smbus_read_word_data(&ap->client, cmd); + data = i2c_smbus_read_word_data(ap->client, cmd); if (data < 0) return; /* ignore errors (due to ACPI??) */ /* write back to clear latch */ - i2c_smbus_write_word_data(&ap->client, cmd, 0); + i2c_smbus_write_word_data(ap->client, cmd, 0); if (!data) return; @@ -124,7 +115,7 @@ static void led_update(struct work_struct *work) { struct apanel *ap = container_of(work, struct apanel, led_work); - i2c_smbus_write_word_data(&ap->client, 0x10, ap->led_bits); + i2c_smbus_write_word_data(ap->client, 0x10, ap->led_bits); } static void mail_led_set(struct led_classdev *led, @@ -140,7 +131,7 @@ static void mail_led_set(struct led_classdev *led, schedule_work(&ap->led_work); } -static int apanel_detach_client(struct i2c_client *client) +static int apanel_remove(struct i2c_client *client) { struct apanel *ap = i2c_get_clientdata(client); @@ -148,43 +139,33 @@ static int apanel_detach_client(struct i2c_client *client) led_classdev_unregister(&ap->mail_led); input_unregister_polled_device(ap->ipdev); - i2c_detach_client(&ap->client); input_free_polled_device(ap->ipdev); return 0; } -/* Function is invoked for every i2c adapter. */ -static int apanel_attach_adapter(struct i2c_adapter *adap) -{ - dev_dbg(&adap->dev, APANEL ": attach adapter id=%d\n", adap->id); - - /* Our device is connected only to i801 on laptop */ - if (adap->id != I2C_HW_SMBUS_I801) - return -ENODEV; - - return i2c_probe(adap, &addr_data, apanel_probe); -} - static void apanel_shutdown(struct i2c_client *client) { - apanel_detach_client(client); + apanel_remove(client); } +static struct i2c_device_id apanel_id[] = { + { "fujitsu_apanel", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, apanel_id); + static struct i2c_driver apanel_driver = { .driver = { .name = APANEL, }, - .attach_adapter = &apanel_attach_adapter, - .detach_client = &apanel_detach_client, + .probe = &apanel_probe, + .remove = &apanel_remove, .shutdown = &apanel_shutdown, + .id_table = apanel_id, }; static struct apanel apanel = { - .client = { - .driver = &apanel_driver, - .name = APANEL, - }, .keymap = { [0] = KEY_MAIL, [1] = KEY_WWW, @@ -204,7 +185,8 @@ static struct apanel apanel = { }; /* NB: Only one panel on the i2c. */ -static int apanel_probe(struct i2c_adapter *bus, int address, int kind) +static int apanel_probe(struct i2c_client *client, + const struct i2c_device_id *id) { struct apanel *ap; struct input_polled_dev *ipdev; @@ -212,9 +194,6 @@ static int apanel_probe(struct i2c_adapter *bus, int address, int kind) u8 cmd = device_chip[APANEL_DEV_APPBTN] == CHIP_OZ992C ? 0 : 8; int i, err = -ENOMEM; - dev_dbg(&bus->dev, APANEL ": probe adapter %p addr %d kind %d\n", - bus, address, kind); - ap = &apanel; ipdev = input_allocate_polled_device(); @@ -222,18 +201,13 @@ static int apanel_probe(struct i2c_adapter *bus, int address, int kind) goto out1; ap->ipdev = ipdev; - ap->client.adapter = bus; - ap->client.addr = address; - - i2c_set_clientdata(&ap->client, ap); + ap->client = client; - err = i2c_attach_client(&ap->client); - if (err) - goto out2; + i2c_set_clientdata(client, ap); - err = i2c_smbus_write_word_data(&ap->client, cmd, 0); + err = i2c_smbus_write_word_data(client, cmd, 0); if (err) { - dev_warn(&ap->client.dev, APANEL ": smbus write error %d\n", + dev_warn(&client->dev, APANEL ": smbus write error %d\n", err); goto out3; } @@ -246,7 +220,7 @@ static int apanel_probe(struct i2c_adapter *bus, int address, int kind) idev->name = APANEL_NAME " buttons"; idev->phys = "apanel/input0"; idev->id.bustype = BUS_HOST; - idev->dev.parent = &ap->client.dev; + idev->dev.parent = &client->dev; set_bit(EV_KEY, idev->evbit); @@ -264,7 +238,7 @@ static int apanel_probe(struct i2c_adapter *bus, int address, int kind) INIT_WORK(&ap->led_work, led_update); if (device_chip[APANEL_DEV_LED] != CHIP_NONE) { - err = led_classdev_register(&ap->client.dev, &ap->mail_led); + err = led_classdev_register(&client->dev, &ap->mail_led); if (err) goto out4; } @@ -273,8 +247,6 @@ static int apanel_probe(struct i2c_adapter *bus, int address, int kind) out4: input_unregister_polled_device(ipdev); out3: - i2c_detach_client(&ap->client); -out2: input_free_polled_device(ipdev); out1: return err; @@ -301,6 +273,7 @@ static int __init apanel_init(void) void __iomem *bios; const void __iomem *p; u8 devno; + unsigned char i2c_addr; int found = 0; bios = ioremap(0xF0000, 0x10000); /* Can't fail */ @@ -313,7 +286,7 @@ static int __init apanel_init(void) /* just use the first address */ p += 8; - normal_i2c[0] = readb(p+3) >> 1; + i2c_addr = readb(p + 3) >> 1; for ( ; (devno = readb(p)) & 0x7f; p += 4) { unsigned char method, slave, chip; @@ -322,7 +295,7 @@ static int __init apanel_init(void) chip = readb(p + 2); slave = readb(p + 3) >> 1; - if (slave != normal_i2c[0]) { + if (slave != i2c_addr) { pr_notice(APANEL ": only one SMBus slave " "address supported, skiping device...\n"); continue; diff --git a/drivers/input/misc/cm109.c b/drivers/input/misc/cm109.c index bce160f4349b..86457feccfc4 100644 --- a/drivers/input/misc/cm109.c +++ b/drivers/input/misc/cm109.c @@ -42,7 +42,7 @@ static char *phone = "kip1000"; module_param(phone, charp, S_IRUSR); -MODULE_PARM_DESC(phone, "Phone name {kip1000, gtalk, usbph01}"); +MODULE_PARM_DESC(phone, "Phone name {kip1000, gtalk, usbph01, atcom}"); enum { /* HID Registers */ @@ -258,6 +258,37 @@ static unsigned short keymap_usbph01(int scancode) } } +/* + * Keymap for ATCom AU-100 + * http://www.atcom.cn/En_products_AU100.html + * http://www.packetizer.com/products/au100/ + * http://www.voip-info.org/wiki/view/AU-100 + * + * Contributed by daniel@gimpelevich.san-francisco.ca.us + */ +static unsigned short keymap_atcom(int scancode) +{ + switch (scancode) { /* phone key: */ + case 0x82: return KEY_NUMERIC_0; /* 0 */ + case 0x11: return KEY_NUMERIC_1; /* 1 */ + case 0x12: return KEY_NUMERIC_2; /* 2 */ + case 0x14: return KEY_NUMERIC_3; /* 3 */ + case 0x21: return KEY_NUMERIC_4; /* 4 */ + case 0x22: return KEY_NUMERIC_5; /* 5 */ + case 0x24: return KEY_NUMERIC_6; /* 6 */ + case 0x41: return KEY_NUMERIC_7; /* 7 */ + case 0x42: return KEY_NUMERIC_8; /* 8 */ + case 0x44: return KEY_NUMERIC_9; /* 9 */ + case 0x84: return KEY_NUMERIC_POUND; /* # */ + case 0x81: return KEY_NUMERIC_STAR; /* * */ + case 0x18: return KEY_ENTER; /* pickup */ + case 0x28: return KEY_ESC; /* hangup */ + case 0x48: return KEY_LEFT; /* left arrow */ + case 0x88: return KEY_RIGHT; /* right arrow */ + default: return special_keymap(scancode); + } +} + static unsigned short (*keymap)(int) = keymap_kip1000; /* @@ -840,6 +871,10 @@ static int __init cm109_select_keymap(void) keymap = keymap_usbph01; printk(KERN_INFO KBUILD_MODNAME ": " "Keymap for Allied-Telesis Corega USBPH01 phone loaded\n"); + } else if (!strcasecmp(phone, "atcom")) { + keymap = keymap_atcom; + printk(KERN_INFO KBUILD_MODNAME ": " + "Keymap for ATCom AU-100 phone loaded\n"); } else { printk(KERN_ERR KBUILD_MODNAME ": " "Unsupported phone: %s\n", phone); diff --git a/drivers/input/mouse/hgpk.c b/drivers/input/mouse/hgpk.c index e82d34201e97..88f04bf2ad6c 100644 --- a/drivers/input/mouse/hgpk.c +++ b/drivers/input/mouse/hgpk.c @@ -125,7 +125,7 @@ static void hgpk_spewing_hack(struct psmouse *psmouse, */ static int hgpk_validate_byte(unsigned char *packet) { - return (packet[0] & 0x0C) == 0x08; + return (packet[0] & 0x0C) != 0x08; } static void hgpk_process_packet(struct psmouse *psmouse) diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig index 27d70d326ff3..da3c3a5d2689 100644 --- a/drivers/input/serio/Kconfig +++ b/drivers/input/serio/Kconfig @@ -79,7 +79,7 @@ config SERIO_PARKBD config SERIO_RPCKBD tristate "Acorn RiscPC keyboard controller" - depends on ARCH_ACORN || ARCH_CLPS7500 + depends on ARCH_ACORN default y help Say Y here if you have the Acorn RiscPC and want to use an AT diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index eec375cd10e6..29e686388a2c 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -337,6 +337,20 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "2656"), }, }, + { + .ident = "Dell XPS M1530", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "XPS M1530"), + }, + }, + { + .ident = "Compal HEL80I", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "COMPAL"), + DMI_MATCH(DMI_PRODUCT_NAME, "HEL80I"), + }, + }, { } }; diff --git a/drivers/input/tablet/wacom.h b/drivers/input/tablet/wacom.h index ca62ec639f8f..677680e9f54f 100644 --- a/drivers/input/tablet/wacom.h +++ b/drivers/input/tablet/wacom.h @@ -66,6 +66,7 @@ * - Support Intuos3 4x6 * v1.47 (pc) - Added support for Bamboo * v1.48 (pc) - Added support for Bamboo1, BambooFun, and Cintiq 12WX + * v1.49 (pc) - Added support for USB Tablet PC (0x90, 0x93, and 0x9A) */ /* @@ -86,7 +87,7 @@ /* * Version Information */ -#define DRIVER_VERSION "v1.48" +#define DRIVER_VERSION "v1.49" #define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>" #define DRIVER_DESC "USB Wacom Graphire and Wacom Intuos tablet driver" #define DRIVER_LICENSE "GPL" @@ -103,15 +104,15 @@ struct wacom { struct usb_device *usbdev; struct usb_interface *intf; struct urb *irq; - struct wacom_wac * wacom_wac; + struct wacom_wac *wacom_wac; struct mutex lock; unsigned int open:1; char phys[32]; }; struct wacom_combo { - struct wacom * wacom; - struct urb * urb; + struct wacom *wacom; + struct urb *urb; }; extern int wacom_wac_irq(struct wacom_wac * wacom_wac, void * wcombo); @@ -132,7 +133,7 @@ extern void input_dev_mo(struct input_dev *input_dev, struct wacom_wac *wacom_wa extern void input_dev_bee(struct input_dev *input_dev, struct wacom_wac *wacom_wac); extern __u16 wacom_le16_to_cpu(unsigned char *data); extern __u16 wacom_be16_to_cpu(unsigned char *data); -extern struct wacom_features * get_wacom_feature(const struct usb_device_id *id); -extern const struct usb_device_id * get_device_table(void); +extern struct wacom_features *get_wacom_feature(const struct usb_device_id *id); +extern const struct usb_device_id *get_device_table(void); #endif diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c index 09e227aa0d49..484496daa0f3 100644 --- a/drivers/input/tablet/wacom_sys.c +++ b/drivers/input/tablet/wacom_sys.c @@ -14,8 +14,41 @@ #include "wacom.h" #include "wacom_wac.h" +/* defines to get HID report descriptor */ +#define HID_DEVICET_HID (USB_TYPE_CLASS | 0x01) +#define HID_DEVICET_REPORT (USB_TYPE_CLASS | 0x02) +#define HID_USAGE_UNDEFINED 0x00 +#define HID_USAGE_PAGE 0x05 +#define HID_USAGE_PAGE_DIGITIZER 0x0d +#define HID_USAGE_PAGE_DESKTOP 0x01 +#define HID_USAGE 0x09 +#define HID_USAGE_X 0x30 +#define HID_USAGE_Y 0x31 +#define HID_USAGE_X_TILT 0x3d +#define HID_USAGE_Y_TILT 0x3e +#define HID_USAGE_FINGER 0x22 +#define HID_USAGE_STYLUS 0x20 +#define HID_COLLECTION 0xc0 + +enum { + WCM_UNDEFINED = 0, + WCM_DESKTOP, + WCM_DIGITIZER, +}; + +struct hid_descriptor { + struct usb_descriptor_header header; + __le16 bcdHID; + u8 bCountryCode; + u8 bNumDescriptors; + u8 bDescriptorType; + __le16 wDescriptorLength; +} __attribute__ ((packed)); + +/* defines to get/set USB message */ #define USB_REQ_GET_REPORT 0x01 #define USB_REQ_SET_REPORT 0x09 +#define WAC_HID_FEATURE_REPORT 0x03 static int usb_get_report(struct usb_interface *intf, unsigned char type, unsigned char id, void *buf, int size) @@ -80,25 +113,21 @@ static void wacom_sys_irq(struct urb *urb) void wacom_report_key(void *wcombo, unsigned int key_type, int key_data) { input_report_key(get_input_dev((struct wacom_combo *)wcombo), key_type, key_data); - return; } void wacom_report_abs(void *wcombo, unsigned int abs_type, int abs_data) { input_report_abs(get_input_dev((struct wacom_combo *)wcombo), abs_type, abs_data); - return; } void wacom_report_rel(void *wcombo, unsigned int rel_type, int rel_data) { input_report_rel(get_input_dev((struct wacom_combo *)wcombo), rel_type, rel_data); - return; } void wacom_input_event(void *wcombo, unsigned int type, unsigned int code, int value) { input_event(get_input_dev((struct wacom_combo *)wcombo), type, code, value); - return; } __u16 wacom_be16_to_cpu(unsigned char *data) @@ -118,7 +147,6 @@ __u16 wacom_le16_to_cpu(unsigned char *data) void wacom_input_sync(void *wcombo) { input_sync(get_input_dev((struct wacom_combo *)wcombo)); - return; } static int wacom_open(struct input_dev *dev) @@ -160,7 +188,7 @@ static void wacom_close(struct input_dev *dev) void input_dev_mo(struct input_dev *input_dev, struct wacom_wac *wacom_wac) { - input_dev->keybit[BIT_WORD(BTN_LEFT)] |= BIT_MASK(BTN_1) | + input_dev->keybit[BIT_WORD(BTN_MISC)] |= BIT_MASK(BTN_1) | BIT_MASK(BTN_5); input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0); } @@ -170,7 +198,7 @@ void input_dev_g4(struct input_dev *input_dev, struct wacom_wac *wacom_wac) input_dev->evbit[0] |= BIT_MASK(EV_MSC); input_dev->mscbit[0] |= BIT_MASK(MSC_SERIAL); input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_FINGER); - input_dev->keybit[BIT_WORD(BTN_LEFT)] |= BIT_MASK(BTN_0) | + input_dev->keybit[BIT_WORD(BTN_MISC)] |= BIT_MASK(BTN_0) | BIT_MASK(BTN_4); } @@ -178,7 +206,7 @@ void input_dev_g(struct input_dev *input_dev, struct wacom_wac *wacom_wac) { input_dev->evbit[0] |= BIT_MASK(EV_REL); input_dev->relbit[0] |= BIT_MASK(REL_WHEEL); - input_dev->keybit[BIT_WORD(BTN_LEFT)] |= BIT_MASK(BTN_LEFT) | + input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE); input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_RUBBER) | BIT_MASK(BTN_TOOL_MOUSE) | BIT_MASK(BTN_STYLUS2); @@ -188,7 +216,7 @@ void input_dev_g(struct input_dev *input_dev, struct wacom_wac *wacom_wac) void input_dev_i3s(struct input_dev *input_dev, struct wacom_wac *wacom_wac) { input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_FINGER); - input_dev->keybit[BIT_WORD(BTN_LEFT)] |= BIT_MASK(BTN_0) | + input_dev->keybit[BIT_WORD(BTN_MISC)] |= BIT_MASK(BTN_0) | BIT_MASK(BTN_1) | BIT_MASK(BTN_2) | BIT_MASK(BTN_3); input_set_abs_params(input_dev, ABS_RX, 0, 4096, 0, 0); input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); @@ -196,14 +224,14 @@ void input_dev_i3s(struct input_dev *input_dev, struct wacom_wac *wacom_wac) void input_dev_i3(struct input_dev *input_dev, struct wacom_wac *wacom_wac) { - input_dev->keybit[BIT_WORD(BTN_LEFT)] |= BIT_MASK(BTN_4) | + input_dev->keybit[BIT_WORD(BTN_MISC)] |= BIT_MASK(BTN_4) | BIT_MASK(BTN_5) | BIT_MASK(BTN_6) | BIT_MASK(BTN_7); input_set_abs_params(input_dev, ABS_RY, 0, 4096, 0, 0); } void input_dev_bee(struct input_dev *input_dev, struct wacom_wac *wacom_wac) { - input_dev->keybit[BIT_WORD(BTN_LEFT)] |= BIT_MASK(BTN_8) | BIT_MASK(BTN_9); + input_dev->keybit[BIT_WORD(BTN_MISC)] |= BIT_MASK(BTN_8) | BIT_MASK(BTN_9); } void input_dev_i(struct input_dev *input_dev, struct wacom_wac *wacom_wac) @@ -211,7 +239,7 @@ void input_dev_i(struct input_dev *input_dev, struct wacom_wac *wacom_wac) input_dev->evbit[0] |= BIT_MASK(EV_MSC) | BIT_MASK(EV_REL); input_dev->mscbit[0] |= BIT_MASK(MSC_SERIAL); input_dev->relbit[0] |= BIT_MASK(REL_WHEEL); - input_dev->keybit[BIT_WORD(BTN_LEFT)] |= BIT_MASK(BTN_LEFT) | + input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_SIDE) | BIT_MASK(BTN_EXTRA); input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_RUBBER) | @@ -228,8 +256,7 @@ void input_dev_i(struct input_dev *input_dev, struct wacom_wac *wacom_wac) void input_dev_pl(struct input_dev *input_dev, struct wacom_wac *wacom_wac) { - input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_STYLUS2) | - BIT_MASK(BTN_TOOL_RUBBER); + input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_STYLUS2); } void input_dev_pt(struct input_dev *input_dev, struct wacom_wac *wacom_wac) @@ -237,15 +264,129 @@ void input_dev_pt(struct input_dev *input_dev, struct wacom_wac *wacom_wac) input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_RUBBER); } +static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hid_desc, + struct wacom_wac *wacom_wac) +{ + struct usb_device *dev = interface_to_usbdev(intf); + struct wacom_features *features = wacom_wac->features; + char limit = 0, result = 0; + int i = 0, usage = WCM_UNDEFINED, finger = 0, pen = 0; + unsigned char *report; + + report = kzalloc(hid_desc->wDescriptorLength, GFP_KERNEL); + if (!report) + return -ENOMEM; + + /* retrive report descriptors */ + do { + result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_DESCRIPTOR, + USB_RECIP_INTERFACE | USB_DIR_IN, + HID_DEVICET_REPORT << 8, + intf->altsetting[0].desc.bInterfaceNumber, /* interface */ + report, + hid_desc->wDescriptorLength, + 5000); /* 5 secs */ + } while (result < 0 && limit++ < 5); + + if (result < 0) + goto out; + + for (i = 0; i < hid_desc->wDescriptorLength; i++) { + + switch (report[i]) { + case HID_USAGE_PAGE: + switch (report[i + 1]) { + case HID_USAGE_PAGE_DIGITIZER: + usage = WCM_DIGITIZER; + i++; + break; + + case HID_USAGE_PAGE_DESKTOP: + usage = WCM_DESKTOP; + i++; + break; + } + break; + + case HID_USAGE: + switch (report[i + 1]) { + case HID_USAGE_X: + if (usage == WCM_DESKTOP) { + if (finger) { + features->touch_x_max = + features->touch_y_max = + wacom_le16_to_cpu(&report[i + 3]); + features->x_max = + wacom_le16_to_cpu(&report[i + 6]); + i += 7; + } else if (pen) { + features->x_max = + wacom_le16_to_cpu(&report[i + 3]); + i += 4; + } + } else if (usage == WCM_DIGITIZER) { + /* max pressure isn't reported + features->pressure_max = (unsigned short) + (report[i+4] << 8 | report[i + 3]); + */ + features->pressure_max = 255; + i += 4; + } + break; + + case HID_USAGE_Y: + if (usage == WCM_DESKTOP) + features->y_max = + wacom_le16_to_cpu(&report[i + 3]); + i += 4; + break; + + case HID_USAGE_FINGER: + finger = 1; + i++; + break; + + case HID_USAGE_STYLUS: + pen = 1; + i++; + break; + + case HID_USAGE_UNDEFINED: + if (usage == WCM_DESKTOP && finger) /* capacity */ + features->pressure_max = + wacom_le16_to_cpu(&report[i + 3]); + i += 4; + break; + } + break; + + case HID_COLLECTION: + /* reset UsagePage ans Finger */ + finger = usage = 0; + break; + } + } + + result = 0; + + out: + kfree(report); + return result; +} + static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_device *dev = interface_to_usbdev(intf); + struct usb_host_interface *interface = intf->cur_altsetting; struct usb_endpoint_descriptor *endpoint; struct wacom *wacom; struct wacom_wac *wacom_wac; + struct wacom_features *features; struct input_dev *input_dev; int error = -ENOMEM; char rep_data[2], limit = 0; + struct hid_descriptor *hid_desc; wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL); wacom_wac = kzalloc(sizeof(struct wacom_wac), GFP_KERNEL); @@ -268,8 +409,8 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i usb_make_path(dev, wacom->phys, sizeof(wacom->phys)); strlcat(wacom->phys, "/input0", sizeof(wacom->phys)); - wacom_wac->features = get_wacom_feature(id); - BUG_ON(wacom_wac->features->pktlen > 10); + wacom_wac->features = features = get_wacom_feature(id); + BUG_ON(features->pktlen > 10); input_dev->name = wacom_wac->features->name; wacom->wacom_wac = wacom_wac; @@ -282,18 +423,37 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i input_dev->open = wacom_open; input_dev->close = wacom_close; + endpoint = &intf->cur_altsetting->endpoint[0].desc; + + /* TabletPC need to retrieve the physical and logical maximum from report descriptor */ + if (wacom_wac->features->type == TABLETPC) { + if (usb_get_extra_descriptor(interface, HID_DEVICET_HID, &hid_desc)) { + if (usb_get_extra_descriptor(&interface->endpoint[0], + HID_DEVICET_REPORT, &hid_desc)) { + printk("wacom: can not retrive extra class descriptor\n"); + goto fail2; + } + } + error = wacom_parse_hid(intf, hid_desc, wacom_wac); + if (error) + goto fail2; + } + input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_PEN) | BIT_MASK(BTN_TOUCH) | BIT_MASK(BTN_STYLUS); - input_set_abs_params(input_dev, ABS_X, 0, wacom_wac->features->x_max, 4, 0); - input_set_abs_params(input_dev, ABS_Y, 0, wacom_wac->features->y_max, 4, 0); - input_set_abs_params(input_dev, ABS_PRESSURE, 0, wacom_wac->features->pressure_max, 0, 0); + input_set_abs_params(input_dev, ABS_X, 0, features->x_max, 4, 0); + input_set_abs_params(input_dev, ABS_Y, 0, features->y_max, 4, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, features->pressure_max, 0, 0); + if (features->type == TABLETPC) { + input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_DOUBLETAP); + input_set_abs_params(input_dev, ABS_RX, 0, features->touch_x_max, 4, 0); + input_set_abs_params(input_dev, ABS_RY, 0, features->touch_y_max, 4, 0); + } input_dev->absbit[BIT_WORD(ABS_MISC)] |= BIT_MASK(ABS_MISC); wacom_init_input_dev(input_dev, wacom_wac); - endpoint = &intf->cur_altsetting->endpoint[0].desc; - usb_fill_int_urb(wacom->irq, dev, usb_rcvintpipe(dev, endpoint->bEndpointAddress), wacom_wac->data, wacom_wac->features->pktlen, @@ -305,13 +465,22 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i if (error) goto fail3; - /* Ask the tablet to report tablet data. Repeat until it succeeds */ - do { - rep_data[0] = 2; - rep_data[1] = 2; - usb_set_report(intf, 3, 2, rep_data, 2); - usb_get_report(intf, 3, 2, rep_data, 2); - } while (rep_data[1] != 2 && limit++ < 5); + /* + * Ask the tablet to report tablet data if it is not a Tablet PC. + * Repeat until it succeeds + */ + if (wacom_wac->features->type != TABLETPC) { + do { + rep_data[0] = 2; + rep_data[1] = 2; + error = usb_set_report(intf, WAC_HID_FEATURE_REPORT, + 2, rep_data, 2); + if (error >= 0) + error = usb_get_report(intf, + WAC_HID_FEATURE_REPORT, 2, + rep_data, 2); + } while ((error < 0 || rep_data[1] != 2) && limit++ < 5); + } usb_set_intfdata(intf, wacom); return 0; @@ -333,7 +502,8 @@ static void wacom_disconnect(struct usb_interface *intf) usb_kill_urb(wacom->irq); input_unregister_device(wacom->dev); usb_free_urb(wacom->irq); - usb_buffer_free(interface_to_usbdev(intf), 10, wacom->wacom_wac->data, wacom->data_dma); + usb_buffer_free(interface_to_usbdev(intf), 10, + wacom->wacom_wac->data, wacom->data_dma); kfree(wacom->wacom_wac); kfree(wacom); } diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index bf3d9a8b2c1b..8dc8d1e59bea 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -535,31 +535,147 @@ static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo) return 1; } +int wacom_tpc_irq(struct wacom_wac *wacom, void *wcombo) +{ + char *data = wacom->data; + int prox = 0, pressure; + static int stylusInProx, touchInProx = 1, touchOut; + struct urb *urb = ((struct wacom_combo *)wcombo)->urb; + + dbg("wacom_tpc_irq: received report #%d", data[0]); + + if (urb->actual_length == 5 || data[0] == 6) { /* Touch data */ + if (urb->actual_length == 5) { /* with touch */ + prox = data[0] & 0x03; + } else { /* with capacity */ + prox = data[1] & 0x03; + } + + if (!stylusInProx) { /* stylus not in prox */ + if (prox) { + if (touchInProx) { + wacom->tool[1] = BTN_TOOL_DOUBLETAP; + wacom->id[0] = TOUCH_DEVICE_ID; + if (urb->actual_length != 5) { + wacom_report_abs(wcombo, ABS_X, wacom_le16_to_cpu(&data[2])); + wacom_report_abs(wcombo, ABS_Y, wacom_le16_to_cpu(&data[4])); + wacom_report_abs(wcombo, ABS_PRESSURE, wacom_le16_to_cpu(&data[6])); + wacom_report_key(wcombo, BTN_TOUCH, wacom_le16_to_cpu(&data[6])); + } else { + wacom_report_abs(wcombo, ABS_X, wacom_le16_to_cpu(&data[1])); + wacom_report_abs(wcombo, ABS_Y, wacom_le16_to_cpu(&data[3])); + wacom_report_key(wcombo, BTN_TOUCH, 1); + } + wacom_report_abs(wcombo, ABS_MISC, wacom->id[0]); + wacom_report_key(wcombo, wacom->tool[1], prox & 0x01); + touchOut = 1; + return 1; + } + } else { + wacom_report_abs(wcombo, ABS_MISC, wacom->id[0]); + wacom_report_key(wcombo, wacom->tool[1], prox & 0x01); + wacom_report_key(wcombo, BTN_TOUCH, 0); + touchOut = 0; + touchInProx = 1; + return 1; + } + } else if (touchOut || !prox) { /* force touch out-prox */ + wacom_report_abs(wcombo, ABS_MISC, TOUCH_DEVICE_ID); + wacom_report_key(wcombo, BTN_TOUCH, 0); + touchOut = 0; + touchInProx = 1; + return 1; + } + } else if (data[0] == 2) { /* Penabled */ + prox = data[1] & 0x20; + + touchInProx = 0; + + wacom->id[0] = ERASER_DEVICE_ID; + + /* + * if going from out of proximity into proximity select between the eraser + * and the pen based on the state of the stylus2 button, choose eraser if + * pressed else choose pen. if not a proximity change from out to in, send + * an out of proximity for previous tool then a in for new tool. + */ + if (prox) { /* in prox */ + if (!wacom->tool[0]) { + /* Going into proximity select tool */ + wacom->tool[1] = (data[1] & 0x08) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN; + if (wacom->tool[1] == BTN_TOOL_PEN) + wacom->id[0] = STYLUS_DEVICE_ID; + } else if (wacom->tool[1] == BTN_TOOL_RUBBER && !(data[1] & 0x08)) { + /* + * was entered with stylus2 pressed + * report out proximity for previous tool + */ + wacom_report_abs(wcombo, ABS_MISC, wacom->id[0]); + wacom_report_key(wcombo, wacom->tool[1], 0); + wacom_input_sync(wcombo); + + /* set new tool */ + wacom->tool[1] = BTN_TOOL_PEN; + wacom->id[0] = STYLUS_DEVICE_ID; + return 0; + } + if (wacom->tool[1] != BTN_TOOL_RUBBER) { + /* Unknown tool selected default to pen tool */ + wacom->tool[1] = BTN_TOOL_PEN; + wacom->id[0] = STYLUS_DEVICE_ID; + } + wacom_report_key(wcombo, BTN_STYLUS, data[1] & 0x02); + wacom_report_key(wcombo, BTN_STYLUS2, data[1] & 0x10); + wacom_report_abs(wcombo, ABS_X, wacom_le16_to_cpu(&data[2])); + wacom_report_abs(wcombo, ABS_Y, wacom_le16_to_cpu(&data[4])); + pressure = ((data[7] & 0x01) << 8) | data[6]; + if (pressure < 0) + pressure = wacom->features->pressure_max + pressure + 1; + wacom_report_abs(wcombo, ABS_PRESSURE, pressure); + wacom_report_key(wcombo, BTN_TOUCH, pressure); + } else { + wacom_report_abs(wcombo, ABS_PRESSURE, 0); + wacom_report_key(wcombo, BTN_STYLUS, 0); + wacom_report_key(wcombo, BTN_STYLUS2, 0); + wacom_report_key(wcombo, BTN_TOUCH, 0); + } + wacom_report_key(wcombo, wacom->tool[1], prox); + wacom_report_abs(wcombo, ABS_MISC, wacom->id[0]); + stylusInProx = prox; + wacom->tool[0] = prox; + return 1; + } + return 0; +} + int wacom_wac_irq(struct wacom_wac *wacom_wac, void *wcombo) { switch (wacom_wac->features->type) { case PENPARTNER: - return (wacom_penpartner_irq(wacom_wac, wcombo)); - break; + return wacom_penpartner_irq(wacom_wac, wcombo); + case PL: - return (wacom_pl_irq(wacom_wac, wcombo)); - break; + return wacom_pl_irq(wacom_wac, wcombo); + case WACOM_G4: case GRAPHIRE: case WACOM_MO: - return (wacom_graphire_irq(wacom_wac, wcombo)); - break; + return wacom_graphire_irq(wacom_wac, wcombo); + case PTU: - return (wacom_ptu_irq(wacom_wac, wcombo)); - break; + return wacom_ptu_irq(wacom_wac, wcombo); + case INTUOS: case INTUOS3S: case INTUOS3: case INTUOS3L: case CINTIQ: case WACOM_BEE: - return (wacom_intuos_irq(wacom_wac, wcombo)); - break; + return wacom_intuos_irq(wacom_wac, wcombo); + + case TABLETPC: + return wacom_tpc_irq(wacom_wac, wcombo); + default: return 0; } @@ -586,13 +702,15 @@ void wacom_init_input_dev(struct input_dev *input_dev, struct wacom_wac *wacom_w /* fall through */ case INTUOS3S: input_dev_i3s(input_dev, wacom_wac); + /* fall through */ case INTUOS: input_dev_i(input_dev, wacom_wac); break; case PL: case PTU: + case TABLETPC: input_dev_pl(input_dev, wacom_wac); - break; + /* fall through */ case PENPARTNER: input_dev_pt(input_dev, wacom_wac); break; @@ -611,6 +729,7 @@ static struct wacom_features wacom_features[] = { { "Wacom Graphire4 6x8", 8, 16704, 12064, 511, 63, WACOM_G4 }, { "Wacom BambooFun 4x5", 9, 14760, 9225, 511, 63, WACOM_MO }, { "Wacom BambooFun 6x8", 9, 21648, 13530, 511, 63, WACOM_MO }, + { "Wacom Bamboo1 Medium",8, 16704, 12064, 511, 63, GRAPHIRE }, { "Wacom Volito", 8, 5104, 3712, 511, 63, GRAPHIRE }, { "Wacom PenStation2", 8, 3250, 2320, 255, 63, GRAPHIRE }, { "Wacom Volito2 4x5", 8, 5104, 3712, 511, 63, GRAPHIRE }, @@ -650,6 +769,10 @@ static struct wacom_features wacom_features[] = { { "Wacom Cintiq 21UX", 10, 87200, 65600, 1023, 63, CINTIQ }, { "Wacom Cintiq 20WSX", 10, 86680, 54180, 1023, 63, WACOM_BEE }, { "Wacom Cintiq 12WX", 10, 53020, 33440, 1023, 63, WACOM_BEE }, + { "Wacom DTU1931", 8, 37832, 30305, 511, 0, PL }, + { "Wacom ISDv4 90", 8, 26202, 16325, 255, 0, TABLETPC }, + { "Wacom ISDv4 93", 8, 26202, 16325, 255, 0, TABLETPC }, + { "Wacom ISDv4 9A", 8, 26202, 16325, 255, 0, TABLETPC }, { "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 31, INTUOS }, { } }; @@ -665,6 +788,7 @@ static struct usb_device_id wacom_ids[] = { { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x16) }, { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x17) }, { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x18) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x19) }, { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x60) }, { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x61) }, { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x62) }, @@ -704,18 +828,26 @@ static struct usb_device_id wacom_ids[] = { { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x3F) }, { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xC5) }, { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xC6) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xC7) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x90) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x93) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x9A) }, { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x47) }, { } }; -const struct usb_device_id * get_device_table(void) { - const struct usb_device_id * id_table = wacom_ids; +const struct usb_device_id *get_device_table(void) +{ + const struct usb_device_id *id_table = wacom_ids; + return id_table; } -struct wacom_features * get_wacom_feature(const struct usb_device_id * id) { +struct wacom_features * get_wacom_feature(const struct usb_device_id *id) +{ int index = id - wacom_ids; struct wacom_features *wf = &wacom_features[index]; + return wf; } diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/input/tablet/wacom_wac.h index 3342bc05847d..f9c8b69673b7 100644 --- a/drivers/input/tablet/wacom_wac.h +++ b/drivers/input/tablet/wacom_wac.h @@ -10,6 +10,7 @@ #define WACOM_WAC_H #define STYLUS_DEVICE_ID 0x02 +#define TOUCH_DEVICE_ID 0x03 #define CURSOR_DEVICE_ID 0x06 #define ERASER_DEVICE_ID 0x0A #define PAD_DEVICE_ID 0x0F @@ -27,6 +28,7 @@ enum { CINTIQ, WACOM_BEE, WACOM_MO, + TABLETPC, MAX_TYPE }; @@ -38,6 +40,8 @@ struct wacom_features { int pressure_max; int distance_max; int type; + int touch_x_max; + int touch_y_max; }; struct wacom_wac { diff --git a/drivers/input/touchscreen/elo.c b/drivers/input/touchscreen/elo.c index d20689cdbd5d..8f38c5e55ce6 100644 --- a/drivers/input/touchscreen/elo.c +++ b/drivers/input/touchscreen/elo.c @@ -262,7 +262,7 @@ static int elo_setup_10(struct elo *elo) input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0); printk(KERN_INFO "elo: %sTouch touchscreen, fw: %02x.%02x, " - "features: %x02x, controller: 0x%02x\n", + "features: 0x%02x, controller: 0x%02x\n", elo_types[(packet[1] -'0') & 0x03], packet[5], packet[4], packet[3], packet[7]); diff --git a/drivers/input/touchscreen/mainstone-wm97xx.c b/drivers/input/touchscreen/mainstone-wm97xx.c index ba648750a8d9..1d11e2be9ef8 100644 --- a/drivers/input/touchscreen/mainstone-wm97xx.c +++ b/drivers/input/touchscreen/mainstone-wm97xx.c @@ -31,7 +31,7 @@ #include <linux/interrupt.h> #include <linux/wm97xx.h> #include <linux/io.h> -#include <mach/pxa-regs.h> +#include <mach/regs-ac97.h> #define VERSION "0.13" diff --git a/drivers/input/xen-kbdfront.c b/drivers/input/xen-kbdfront.c index 3ab6362f043c..928d2ed8865f 100644 --- a/drivers/input/xen-kbdfront.c +++ b/drivers/input/xen-kbdfront.c @@ -323,7 +323,7 @@ static struct xenbus_device_id xenkbd_ids[] = { { "" } }; -static struct xenbus_driver xenkbd = { +static struct xenbus_driver xenkbd_driver = { .name = "vkbd", .owner = THIS_MODULE, .ids = xenkbd_ids, @@ -342,12 +342,12 @@ static int __init xenkbd_init(void) if (xen_initial_domain()) return -ENODEV; - return xenbus_register_frontend(&xenkbd); + return xenbus_register_frontend(&xenkbd_driver); } static void __exit xenkbd_cleanup(void) { - xenbus_unregister_driver(&xenkbd); + xenbus_unregister_driver(&xenkbd_driver); } module_init(xenkbd_init); diff --git a/drivers/isdn/hisax/config.c b/drivers/isdn/hisax/config.c index 84d75a3f5d17..ded9d0baf607 100644 --- a/drivers/isdn/hisax/config.c +++ b/drivers/isdn/hisax/config.c @@ -1213,7 +1213,7 @@ static void HiSax_shiftcards(int idx) memcpy(&cards[i], &cards[i + 1], sizeof(cards[i])); } -static int HiSax_inithardware(int *busy_flag) +static int __init HiSax_inithardware(int *busy_flag) { int foundcards = 0; int i = 0; @@ -1542,7 +1542,9 @@ static void __exit HiSax_exit(void) printk(KERN_INFO "HiSax module removed\n"); } -int hisax_init_pcmcia(void *pcm_iob, int *busy_flag, struct IsdnCard *card) +#ifdef CONFIG_HOTPLUG + +int __devinit hisax_init_pcmcia(void *pcm_iob, int *busy_flag, struct IsdnCard *card) { u_char ids[16]; int ret = -1; @@ -1563,6 +1565,8 @@ error: } EXPORT_SYMBOL(hisax_init_pcmcia); +#endif + EXPORT_SYMBOL(HiSax_closecard); #include "hisax_if.h" @@ -1580,6 +1584,11 @@ static void hisax_bc_close(struct BCState *bcs); static void hisax_bh(struct work_struct *work); static void EChannel_proc_rcv(struct hisax_d_if *d_if); +static int hisax_setup_card_dynamic(struct IsdnCard *card) +{ + return 2; +} + int hisax_register(struct hisax_d_if *hisax_d_if, struct hisax_b_if *b_if[], char *name, int protocol) { @@ -1599,7 +1608,8 @@ int hisax_register(struct hisax_d_if *hisax_d_if, struct hisax_b_if *b_if[], cards[i].protocol = protocol; sprintf(id, "%s%d", name, i); nrcards++; - retval = checkcard(i, id, NULL, hisax_d_if->owner, hisax_cs_setup_card); + retval = checkcard(i, id, NULL, hisax_d_if->owner, + hisax_setup_card_dynamic); if (retval == 0) { // yuck cards[i].typ = 0; nrcards--; diff --git a/drivers/isdn/i4l/isdn_net.c b/drivers/isdn/i4l/isdn_net.c index bb904a0a98bd..1bfc55d7a26c 100644 --- a/drivers/isdn/i4l/isdn_net.c +++ b/drivers/isdn/i4l/isdn_net.c @@ -1641,8 +1641,10 @@ isdn_net_ciscohdlck_slarp_send_reply(isdn_net_local *lp) /* slarp reply, send own ip/netmask; if values are nonsense remote * should think we are unable to provide it with an address via SLARP */ p += put_u32(p, CISCO_SLARP_REPLY); - p += put_u32(p, addr); // address - p += put_u32(p, mask); // netmask + *(__be32 *)p = addr; // address + p += 4; + *(__be32 *)p = mask; // netmask + p += 4; p += put_u16(p, 0); // unused isdn_net_write_super(lp, skb); diff --git a/drivers/isdn/mISDN/dsp_pipeline.c b/drivers/isdn/mISDN/dsp_pipeline.c index 850260ab57d0..fdc800bda617 100644 --- a/drivers/isdn/mISDN/dsp_pipeline.c +++ b/drivers/isdn/mISDN/dsp_pipeline.c @@ -91,7 +91,7 @@ int mISDN_dsp_element_register(struct mISDN_dsp_element *elem) entry->dev.class = elements_class; dev_set_drvdata(&entry->dev, elem); - snprintf(entry->dev.bus_id, BUS_ID_SIZE, elem->name); + dev_set_name(&entry->dev, elem->name); ret = device_register(&entry->dev); if (ret) { printk(KERN_ERR "%s: failed to register %s\n", diff --git a/drivers/macintosh/macio_asic.c b/drivers/macintosh/macio_asic.c index ec9e5f32f0ae..6e149f4a1fff 100644 --- a/drivers/macintosh/macio_asic.c +++ b/drivers/macintosh/macio_asic.c @@ -33,7 +33,7 @@ #undef DEBUG -#define MAX_NODE_NAME_SIZE (BUS_ID_SIZE - 12) +#define MAX_NODE_NAME_SIZE (20 - 12) static struct macio_chip *macio_on_hold; @@ -240,7 +240,7 @@ static void macio_create_fixup_irq(struct macio_dev *dev, int index, if (irq != NO_IRQ) { dev->interrupt[index].start = irq; dev->interrupt[index].flags = IORESOURCE_IRQ; - dev->interrupt[index].name = dev->ofdev.dev.bus_id; + dev->interrupt[index].name = dev_name(&dev->ofdev.dev); } if (dev->n_interrupts <= index) dev->n_interrupts = index + 1; @@ -303,7 +303,7 @@ static void macio_setup_interrupts(struct macio_dev *dev) break; res->start = irq; res->flags = IORESOURCE_IRQ; - res->name = dev->ofdev.dev.bus_id; + res->name = dev_name(&dev->ofdev.dev); if (macio_resource_quirks(np, res, i - 1)) { memset(res, 0, sizeof(struct resource)); continue; @@ -325,7 +325,7 @@ static void macio_setup_resources(struct macio_dev *dev, if (index >= MACIO_DEV_COUNT_RESOURCES) break; *res = r; - res->name = dev->ofdev.dev.bus_id; + res->name = dev_name(&dev->ofdev.dev); if (macio_resource_quirks(np, res, index)) { memset(res, 0, sizeof(struct resource)); @@ -338,7 +338,7 @@ static void macio_setup_resources(struct macio_dev *dev, if (insert_resource(parent_res, res)) { printk(KERN_WARNING "Can't request resource " "%d for MacIO device %s\n", - index, dev->ofdev.dev.bus_id); + index, dev_name(&dev->ofdev.dev)); } } dev->n_resources = index; @@ -385,8 +385,8 @@ static struct macio_dev * macio_add_one_device(struct macio_chip *chip, /* MacIO itself has a different reg, we use it's PCI base */ if (np == chip->of_node) { - sprintf(dev->ofdev.dev.bus_id, "%1d.%08x:%.*s", - chip->lbus.index, + dev_set_name(&dev->ofdev.dev, "%1d.%08x:%.*s", + chip->lbus.index, #ifdef CONFIG_PCI (unsigned int)pci_resource_start(chip->lbus.pdev, 0), #else @@ -395,9 +395,9 @@ static struct macio_dev * macio_add_one_device(struct macio_chip *chip, MAX_NODE_NAME_SIZE, np->name); } else { reg = of_get_property(np, "reg", NULL); - sprintf(dev->ofdev.dev.bus_id, "%1d.%08x:%.*s", - chip->lbus.index, - reg ? *reg : 0, MAX_NODE_NAME_SIZE, np->name); + dev_set_name(&dev->ofdev.dev, "%1d.%08x:%.*s", + chip->lbus.index, + reg ? *reg : 0, MAX_NODE_NAME_SIZE, np->name); } /* Setup interrupts & resources */ @@ -408,7 +408,7 @@ static struct macio_dev * macio_add_one_device(struct macio_chip *chip, /* Register with core */ if (of_device_register(&dev->ofdev) != 0) { printk(KERN_DEBUG"macio: device registration error for %s!\n", - dev->ofdev.dev.bus_id); + dev_name(&dev->ofdev.dev)); kfree(dev); return NULL; } @@ -558,7 +558,7 @@ err_out: resource_no, macio_resource_len(dev, resource_no), macio_resource_start(dev, resource_no), - dev->ofdev.dev.bus_id); + dev_name(&dev->ofdev.dev)); return -EBUSY; } diff --git a/drivers/macintosh/rack-meter.c b/drivers/macintosh/rack-meter.c index 2c21d4f25cc8..a98ab72adf95 100644 --- a/drivers/macintosh/rack-meter.c +++ b/drivers/macintosh/rack-meter.c @@ -288,7 +288,7 @@ static void __devexit rackmeter_stop_cpu_sniffer(struct rackmeter *rm) cancel_rearming_delayed_work(&rm->cpu[1].sniffer); } -static int rackmeter_setup(struct rackmeter *rm) +static int __devinit rackmeter_setup(struct rackmeter *rm) { pr_debug("rackmeter: setting up i2s..\n"); rackmeter_setup_i2s(rm); @@ -582,12 +582,12 @@ static struct of_device_id rackmeter_match[] = { { } }; -static struct macio_driver rackmeter_drv = { +static struct macio_driver rackmeter_driver = { .name = "rackmeter", .owner = THIS_MODULE, .match_table = rackmeter_match, .probe = rackmeter_probe, - .remove = rackmeter_remove, + .remove = __devexit_p(rackmeter_remove), .shutdown = rackmeter_shutdown, }; @@ -596,14 +596,14 @@ static int __init rackmeter_init(void) { pr_debug("rackmeter_init()\n"); - return macio_register_driver(&rackmeter_drv); + return macio_register_driver(&rackmeter_driver); } static void __exit rackmeter_exit(void) { pr_debug("rackmeter_exit()\n"); - macio_unregister_driver(&rackmeter_drv); + macio_unregister_driver(&rackmeter_driver); } module_init(rackmeter_init); diff --git a/drivers/macintosh/via-cuda.c b/drivers/macintosh/via-cuda.c index 741a93a3eb61..62dd1fdafecf 100644 --- a/drivers/macintosh/via-cuda.c +++ b/drivers/macintosh/via-cuda.c @@ -23,7 +23,6 @@ #else #include <asm/macintosh.h> #include <asm/macints.h> -#include <asm/machw.h> #include <asm/mac_via.h> #endif #include <asm/io.h> diff --git a/drivers/macintosh/via-macii.c b/drivers/macintosh/via-macii.c index 6e6dd17ab572..817f37a875c9 100644 --- a/drivers/macintosh/via-macii.c +++ b/drivers/macintosh/via-macii.c @@ -33,7 +33,6 @@ #include <linux/init.h> #include <asm/macintosh.h> #include <asm/macints.h> -#include <asm/machw.h> #include <asm/mac_via.h> #include <asm/system.h> diff --git a/drivers/macintosh/via-maciisi.c b/drivers/macintosh/via-maciisi.c index 2dc788042707..4d686c0bdea0 100644 --- a/drivers/macintosh/via-maciisi.c +++ b/drivers/macintosh/via-maciisi.c @@ -24,7 +24,6 @@ #include <linux/interrupt.h> #include <asm/macintosh.h> #include <asm/macints.h> -#include <asm/machw.h> #include <asm/mac_via.h> static volatile unsigned char *via; diff --git a/drivers/macintosh/via-pmu68k.c b/drivers/macintosh/via-pmu68k.c index b64741c95ac4..fb9fa614a0e8 100644 --- a/drivers/macintosh/via-pmu68k.c +++ b/drivers/macintosh/via-pmu68k.c @@ -35,7 +35,6 @@ #include <asm/macintosh.h> #include <asm/macints.h> -#include <asm/machw.h> #include <asm/mac_via.h> #include <asm/pgtable.h> diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index ce26c84af064..dbc03c67fe98 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -1322,11 +1322,7 @@ static int __init dm_crypt_init(void) static void __exit dm_crypt_exit(void) { - int r = dm_unregister_target(&crypt_target); - - if (r < 0) - DMERR("unregister failed %d", r); - + dm_unregister_target(&crypt_target); kmem_cache_destroy(_crypt_io_pool); } diff --git a/drivers/md/dm-delay.c b/drivers/md/dm-delay.c index 848b381f1173..59ee1b015d2d 100644 --- a/drivers/md/dm-delay.c +++ b/drivers/md/dm-delay.c @@ -364,11 +364,7 @@ bad_queue: static void __exit dm_delay_exit(void) { - int r = dm_unregister_target(&delay_target); - - if (r < 0) - DMERR("unregister failed %d", r); - + dm_unregister_target(&delay_target); kmem_cache_destroy(delayed_cache); destroy_workqueue(kdelayd_wq); } diff --git a/drivers/md/dm-linear.c b/drivers/md/dm-linear.c index 44042becad8a..bfa107f59d96 100644 --- a/drivers/md/dm-linear.c +++ b/drivers/md/dm-linear.c @@ -142,6 +142,7 @@ static struct target_type linear_target = { .status = linear_status, .ioctl = linear_ioctl, .merge = linear_merge, + .features = DM_TARGET_SUPPORTS_BARRIERS, }; int __init dm_linear_init(void) @@ -156,8 +157,5 @@ int __init dm_linear_init(void) void dm_linear_exit(void) { - int r = dm_unregister_target(&linear_target); - - if (r < 0) - DMERR("unregister failed %d", r); + dm_unregister_target(&linear_target); } diff --git a/drivers/md/dm-log.c b/drivers/md/dm-log.c index a8c0fc79ca78..bc9bf01a5da4 100644 --- a/drivers/md/dm-log.c +++ b/drivers/md/dm-log.c @@ -326,8 +326,6 @@ static void header_from_disk(struct log_header *core, struct log_header *disk) static int rw_header(struct log_c *lc, int rw) { lc->io_req.bi_rw = rw; - lc->io_req.mem.ptr.vma = lc->disk_header; - lc->io_req.notify.fn = NULL; return dm_io(&lc->io_req, 1, &lc->header_location, NULL); } @@ -362,12 +360,6 @@ static int read_header(struct log_c *log) return 0; } -static inline int write_header(struct log_c *log) -{ - header_to_disk(&log->header, log->disk_header); - return rw_header(log, WRITE); -} - /*---------------------------------------------------------------- * core log constructor/destructor * @@ -454,7 +446,10 @@ static int create_log_context(struct dm_dirty_log *log, struct dm_target *ti, buf_size = dm_round_up((LOG_OFFSET << SECTOR_SHIFT) + bitset_size, ti->limits.hardsect_size); lc->header_location.count = buf_size >> SECTOR_SHIFT; + lc->io_req.mem.type = DM_IO_VMA; + lc->io_req.mem.ptr.vma = lc->disk_header; + lc->io_req.notify.fn = NULL; lc->io_req.client = dm_io_client_create(dm_div_up(buf_size, PAGE_SIZE)); if (IS_ERR(lc->io_req.client)) { @@ -467,6 +462,7 @@ static int create_log_context(struct dm_dirty_log *log, struct dm_target *ti, lc->disk_header = vmalloc(buf_size); if (!lc->disk_header) { DMWARN("couldn't allocate disk log buffer"); + dm_io_client_destroy(lc->io_req.client); kfree(lc); return -ENOMEM; } @@ -482,6 +478,8 @@ static int create_log_context(struct dm_dirty_log *log, struct dm_target *ti, DMWARN("couldn't allocate sync bitset"); if (!dev) vfree(lc->clean_bits); + else + dm_io_client_destroy(lc->io_req.client); vfree(lc->disk_header); kfree(lc); return -ENOMEM; @@ -495,6 +493,8 @@ static int create_log_context(struct dm_dirty_log *log, struct dm_target *ti, vfree(lc->sync_bits); if (!dev) vfree(lc->clean_bits); + else + dm_io_client_destroy(lc->io_req.client); vfree(lc->disk_header); kfree(lc); return -ENOMEM; @@ -631,8 +631,10 @@ static int disk_resume(struct dm_dirty_log *log) /* set the correct number of regions in the header */ lc->header.nr_regions = lc->region_count; + header_to_disk(&lc->header, lc->disk_header); + /* write the new header */ - r = write_header(lc); + r = rw_header(lc, WRITE); if (r) { DMWARN("%s: Failed to write header on dirty region log device", lc->log_dev->name); @@ -682,7 +684,7 @@ static int disk_flush(struct dm_dirty_log *log) if (!lc->touched) return 0; - r = write_header(lc); + r = rw_header(lc, WRITE); if (r) fail_log_device(lc); else diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index 4840733cd903..345a26047ae0 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c @@ -441,13 +441,13 @@ static void process_queued_ios(struct work_struct *work) __choose_pgpath(m); pgpath = m->current_pgpath; - m->pgpath_to_activate = m->current_pgpath; if ((pgpath && !m->queue_io) || (!pgpath && !m->queue_if_no_path)) must_queue = 0; - if (m->pg_init_required && !m->pg_init_in_progress) { + if (m->pg_init_required && !m->pg_init_in_progress && pgpath) { + m->pgpath_to_activate = pgpath; m->pg_init_count++; m->pg_init_required = 0; m->pg_init_in_progress = 1; @@ -708,6 +708,10 @@ static int parse_hw_handler(struct arg_set *as, struct multipath *m) m->hw_handler_name = NULL; return -EINVAL; } + + if (hw_argc > 1) + DMWARN("Ignoring user-specified arguments for " + "hardware handler \"%s\"", m->hw_handler_name); consume(as, hw_argc - 1); return 0; @@ -1491,14 +1495,10 @@ static int __init dm_multipath_init(void) static void __exit dm_multipath_exit(void) { - int r; - destroy_workqueue(kmpath_handlerd); destroy_workqueue(kmultipathd); - r = dm_unregister_target(&multipath_target); - if (r < 0) - DMERR("target unregister failed %d", r); + dm_unregister_target(&multipath_target); kmem_cache_destroy(_mpio_cache); } diff --git a/drivers/md/dm-raid1.c b/drivers/md/dm-raid1.c index 9d7b53ed75b2..a542ca4b1769 100644 --- a/drivers/md/dm-raid1.c +++ b/drivers/md/dm-raid1.c @@ -1032,6 +1032,7 @@ static void mirror_dtr(struct dm_target *ti) del_timer_sync(&ms->timer); flush_workqueue(ms->kmirrord_wq); + flush_scheduled_work(); dm_kcopyd_client_destroy(ms->kcopyd_client); destroy_workqueue(ms->kmirrord_wq); free_context(ms, ti, ms->nr_mirrors); @@ -1299,11 +1300,7 @@ static int __init dm_mirror_init(void) static void __exit dm_mirror_exit(void) { - int r; - - r = dm_unregister_target(&mirror_target); - if (r < 0) - DMERR("unregister failed %d", r); + dm_unregister_target(&mirror_target); } /* Module hooks */ diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c index 6c96db26b87c..a8005b43a06b 100644 --- a/drivers/md/dm-snap.c +++ b/drivers/md/dm-snap.c @@ -9,6 +9,7 @@ #include <linux/blkdev.h> #include <linux/ctype.h> #include <linux/device-mapper.h> +#include <linux/delay.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/kdev_t.h> @@ -735,7 +736,7 @@ static void snapshot_dtr(struct dm_target *ti) unregister_snapshot(s); while (atomic_read(&s->pending_exceptions_count)) - yield(); + msleep(1); /* * Ensure instructions in mempool_destroy aren't reordered * before atomic_read. @@ -888,10 +889,10 @@ static void pending_complete(struct dm_snap_pending_exception *pe, int success) /* * Check for conflicting reads. This is extremely improbable, - * so yield() is sufficient and there is no need for a wait queue. + * so msleep(1) is sufficient and there is no need for a wait queue. */ while (__chunk_is_tracked(s, pe->e.old_chunk)) - yield(); + msleep(1); /* * Add a proper exception, and remove the @@ -1469,17 +1470,10 @@ static int __init dm_snapshot_init(void) static void __exit dm_snapshot_exit(void) { - int r; - destroy_workqueue(ksnapd); - r = dm_unregister_target(&snapshot_target); - if (r) - DMERR("snapshot unregister failed %d", r); - - r = dm_unregister_target(&origin_target); - if (r) - DMERR("origin unregister failed %d", r); + dm_unregister_target(&snapshot_target); + dm_unregister_target(&origin_target); exit_origin_hash(); kmem_cache_destroy(pending_cache); diff --git a/drivers/md/dm-stripe.c b/drivers/md/dm-stripe.c index a2d068dbe9e2..41569bc60abc 100644 --- a/drivers/md/dm-stripe.c +++ b/drivers/md/dm-stripe.c @@ -320,8 +320,10 @@ int __init dm_stripe_init(void) int r; r = dm_register_target(&stripe_target); - if (r < 0) + if (r < 0) { DMWARN("target registration failed"); + return r; + } kstriped = create_singlethread_workqueue("kstriped"); if (!kstriped) { @@ -335,9 +337,7 @@ int __init dm_stripe_init(void) void dm_stripe_exit(void) { - if (dm_unregister_target(&stripe_target)) - DMWARN("target unregistration failed"); - + dm_unregister_target(&stripe_target); destroy_workqueue(kstriped); return; diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index a63161aec487..ea10c3d0d5d6 100644 --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c @@ -38,6 +38,8 @@ struct dm_table { sector_t *highs; struct dm_target *targets; + unsigned barriers_supported:1; + /* * Indicates the rw permissions for the new logical * device. This should be a combination of FMODE_READ @@ -227,6 +229,7 @@ int dm_table_create(struct dm_table **result, fmode_t mode, INIT_LIST_HEAD(&t->devices); atomic_set(&t->holders, 1); + t->barriers_supported = 1; if (!num_targets) num_targets = KEYS_PER_NODE; @@ -728,6 +731,10 @@ int dm_table_add_target(struct dm_table *t, const char *type, /* FIXME: the plan is to combine high here and then have * the merge fn apply the target level restrictions. */ combine_restrictions_low(&t->limits, &tgt->limits); + + if (!(tgt->type->features & DM_TARGET_SUPPORTS_BARRIERS)) + t->barriers_supported = 0; + return 0; bad: @@ -772,6 +779,12 @@ int dm_table_complete(struct dm_table *t) check_for_valid_limits(&t->limits); + /* + * We only support barriers if there is exactly one underlying device. + */ + if (!list_is_singular(&t->devices)) + t->barriers_supported = 0; + /* how many indexes will the btree have ? */ leaf_nodes = dm_div_up(t->num_targets, KEYS_PER_NODE); t->depth = 1 + int_log(leaf_nodes, CHILDREN_PER_NODE); @@ -986,6 +999,12 @@ struct mapped_device *dm_table_get_md(struct dm_table *t) return t->md; } +int dm_table_barrier_ok(struct dm_table *t) +{ + return t->barriers_supported; +} +EXPORT_SYMBOL(dm_table_barrier_ok); + EXPORT_SYMBOL(dm_vcalloc); EXPORT_SYMBOL(dm_get_device); EXPORT_SYMBOL(dm_put_device); diff --git a/drivers/md/dm-target.c b/drivers/md/dm-target.c index 835cf95b857f..7decf10006e4 100644 --- a/drivers/md/dm-target.c +++ b/drivers/md/dm-target.c @@ -130,26 +130,26 @@ int dm_register_target(struct target_type *t) return rv; } -int dm_unregister_target(struct target_type *t) +void dm_unregister_target(struct target_type *t) { struct tt_internal *ti; down_write(&_lock); if (!(ti = __find_target_type(t->name))) { - up_write(&_lock); - return -EINVAL; + DMCRIT("Unregistering unrecognised target: %s", t->name); + BUG(); } if (ti->use) { - up_write(&_lock); - return -ETXTBSY; + DMCRIT("Attempt to unregister target still in use: %s", + t->name); + BUG(); } list_del(&ti->list); kfree(ti); up_write(&_lock); - return 0; } /* @@ -187,8 +187,7 @@ int __init dm_target_init(void) void dm_target_exit(void) { - if (dm_unregister_target(&error_target)) - DMWARN("error target unregistration failed"); + dm_unregister_target(&error_target); } EXPORT_SYMBOL(dm_register_target); diff --git a/drivers/md/dm-zero.c b/drivers/md/dm-zero.c index cdbf126ec106..bbc97030c0c2 100644 --- a/drivers/md/dm-zero.c +++ b/drivers/md/dm-zero.c @@ -69,10 +69,7 @@ static int __init dm_zero_init(void) static void __exit dm_zero_exit(void) { - int r = dm_unregister_target(&zero_target); - - if (r < 0) - DMERR("unregister failed %d", r); + dm_unregister_target(&zero_target); } module_init(dm_zero_init) diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 6963ad148408..c2d1ba204b04 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -31,6 +31,7 @@ static unsigned int _major = 0; static DEFINE_SPINLOCK(_minor_lock); /* + * For bio-based dm. * One of these is allocated per bio. */ struct dm_io { @@ -42,6 +43,7 @@ struct dm_io { }; /* + * For bio-based dm. * One of these is allocated per target within a bio. Hopefully * this will be simplified out one day. */ @@ -51,6 +53,27 @@ struct dm_target_io { union map_info info; }; +/* + * For request-based dm. + * One of these is allocated per request. + */ +struct dm_rq_target_io { + struct mapped_device *md; + struct dm_target *ti; + struct request *orig, clone; + int error; + union map_info info; +}; + +/* + * For request-based dm. + * One of these is allocated per bio. + */ +struct dm_rq_clone_bio_info { + struct bio *orig; + struct request *rq; +}; + union map_info *dm_get_mapinfo(struct bio *bio) { if (bio && bio->bi_private) @@ -146,6 +169,8 @@ struct mapped_device { #define MIN_IOS 256 static struct kmem_cache *_io_cache; static struct kmem_cache *_tio_cache; +static struct kmem_cache *_rq_tio_cache; +static struct kmem_cache *_rq_bio_info_cache; static int __init local_init(void) { @@ -161,9 +186,17 @@ static int __init local_init(void) if (!_tio_cache) goto out_free_io_cache; + _rq_tio_cache = KMEM_CACHE(dm_rq_target_io, 0); + if (!_rq_tio_cache) + goto out_free_tio_cache; + + _rq_bio_info_cache = KMEM_CACHE(dm_rq_clone_bio_info, 0); + if (!_rq_bio_info_cache) + goto out_free_rq_tio_cache; + r = dm_uevent_init(); if (r) - goto out_free_tio_cache; + goto out_free_rq_bio_info_cache; _major = major; r = register_blkdev(_major, _name); @@ -177,6 +210,10 @@ static int __init local_init(void) out_uevent_exit: dm_uevent_exit(); +out_free_rq_bio_info_cache: + kmem_cache_destroy(_rq_bio_info_cache); +out_free_rq_tio_cache: + kmem_cache_destroy(_rq_tio_cache); out_free_tio_cache: kmem_cache_destroy(_tio_cache); out_free_io_cache: @@ -187,6 +224,8 @@ out_free_io_cache: static void local_exit(void) { + kmem_cache_destroy(_rq_bio_info_cache); + kmem_cache_destroy(_rq_tio_cache); kmem_cache_destroy(_tio_cache); kmem_cache_destroy(_io_cache); unregister_blkdev(_major, _name); @@ -375,7 +414,7 @@ static void start_io_acct(struct dm_io *io) dm_disk(md)->part0.in_flight = atomic_inc_return(&md->pending); } -static int end_io_acct(struct dm_io *io) +static void end_io_acct(struct dm_io *io) { struct mapped_device *md = io->md; struct bio *bio = io->bio; @@ -391,7 +430,9 @@ static int end_io_acct(struct dm_io *io) dm_disk(md)->part0.in_flight = pending = atomic_dec_return(&md->pending); - return !pending; + /* nudge anyone waiting on suspend queue */ + if (!pending) + wake_up(&md->wait); } /* @@ -499,9 +540,7 @@ static void dec_pending(struct dm_io *io, int error) spin_unlock_irqrestore(&io->md->pushback_lock, flags); } - if (end_io_acct(io)) - /* nudge anyone waiting on suspend queue */ - wake_up(&io->md->wait); + end_io_acct(io); if (io->error != DM_ENDIO_REQUEUE) { blk_add_trace_bio(io->md->queue, io->bio, @@ -794,7 +833,10 @@ static int __split_bio(struct mapped_device *md, struct bio *bio) ci.map = dm_get_table(md); if (unlikely(!ci.map)) return -EIO; - + if (unlikely(bio_barrier(bio) && !dm_table_barrier_ok(ci.map))) { + bio_endio(bio, -EOPNOTSUPP); + return 0; + } ci.md = md; ci.bio = bio; ci.io = alloc_io(md); @@ -878,15 +920,6 @@ static int dm_request(struct request_queue *q, struct bio *bio) struct mapped_device *md = q->queuedata; int cpu; - /* - * There is no use in forwarding any barrier request since we can't - * guarantee it is (or can be) handled by the targets correctly. - */ - if (unlikely(bio_barrier(bio))) { - bio_endio(bio, -EOPNOTSUPP); - return 0; - } - down_read(&md->io_lock); cpu = part_stat_lock(); @@ -937,16 +970,24 @@ static void dm_unplug_all(struct request_queue *q) static int dm_any_congested(void *congested_data, int bdi_bits) { - int r; - struct mapped_device *md = (struct mapped_device *) congested_data; - struct dm_table *map = dm_get_table(md); + int r = bdi_bits; + struct mapped_device *md = congested_data; + struct dm_table *map; - if (!map || test_bit(DMF_BLOCK_IO, &md->flags)) - r = bdi_bits; - else - r = dm_table_any_congested(map, bdi_bits); + atomic_inc(&md->pending); + + if (!test_bit(DMF_BLOCK_IO, &md->flags)) { + map = dm_get_table(md); + if (map) { + r = dm_table_any_congested(map, bdi_bits); + dm_table_put(map); + } + } + + if (!atomic_dec_return(&md->pending)) + /* nudge anyone waiting on suspend queue */ + wake_up(&md->wait); - dm_table_put(map); return r; } @@ -1320,8 +1361,8 @@ void dm_put(struct mapped_device *md) dm_table_presuspend_targets(map); dm_table_postsuspend_targets(map); } - __unbind(md); dm_table_put(map); + __unbind(md); free_dev(md); } } diff --git a/drivers/md/dm.h b/drivers/md/dm.h index 0ade60cdef42..5b5d08ba9e92 100644 --- a/drivers/md/dm.h +++ b/drivers/md/dm.h @@ -51,6 +51,7 @@ int dm_table_any_congested(struct dm_table *t, int bdi_bits); * To check the return value from dm_table_find_target(). */ #define dm_target_is_valid(t) ((t)->table) +int dm_table_barrier_ok(struct dm_table *t); /*----------------------------------------------------------------- * A registry of target types. diff --git a/drivers/md/linear.c b/drivers/md/linear.c index 190147c79e79..3b90c5c924ec 100644 --- a/drivers/md/linear.c +++ b/drivers/md/linear.c @@ -148,6 +148,8 @@ static linear_conf_t *linear_conf(mddev_t *mddev, int raid_disks) min_sectors = conf->array_sectors; sector_div(min_sectors, PAGE_SIZE/sizeof(struct dev_info *)); + if (min_sectors == 0) + min_sectors = 1; /* min_sectors is the minimum spacing that will fit the hash * table in one PAGE. This may be much smaller than needed. diff --git a/drivers/md/md.c b/drivers/md/md.c index 9abf6ed16535..1b1d32694f6f 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -3884,7 +3884,6 @@ static int do_md_stop(mddev_t * mddev, int mode, int is_open) if (mode == 0) { mdk_rdev_t *rdev; struct list_head *tmp; - struct block_device *bdev; printk(KERN_INFO "md: %s stopped.\n", mdname(mddev)); @@ -3941,11 +3940,6 @@ static int do_md_stop(mddev_t * mddev, int mode, int is_open) mddev->degraded = 0; mddev->barriers_work = 0; mddev->safemode = 0; - bdev = bdget_disk(mddev->gendisk, 0); - if (bdev) { - blkdev_ioctl(bdev, 0, BLKRRPART, 0); - bdput(bdev); - } kobject_uevent(&disk_to_dev(mddev->gendisk)->kobj, KOBJ_CHANGE); } else if (mddev->pers) diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index da5129a24b18..970a96ef9b18 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -1137,7 +1137,7 @@ static int raid10_add_disk(mddev_t *mddev, mdk_rdev_t *rdev) if (!enough(conf)) return -EINVAL; - if (rdev->raid_disk) + if (rdev->raid_disk >= 0) first = last = rdev->raid_disk; if (rdev->saved_raid_disk >= 0 && diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index 5689d1f1d444..8557bf12cfb4 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -223,6 +223,8 @@ static void dvb_frontend_init(struct dvb_frontend *fe) if (fe->ops.init) fe->ops.init(fe); if (fe->ops.tuner_ops.init) { + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); fe->ops.tuner_ops.init(fe); if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig index 3c13bcfa6385..62b68c291d99 100644 --- a/drivers/media/dvb/dvb-usb/Kconfig +++ b/drivers/media/dvb/dvb-usb/Kconfig @@ -261,7 +261,7 @@ config DVB_USB_DW2102 Say Y here to support the DvbWorld DVB-S/S2 USB2.0 receivers and the TeVii S650. -config DVB_USB_CINERGY_T2 +config DVB_USB_CINERGY_T2 tristate "Terratec CinergyT2/qanu USB 2.0 DVB-T receiver" depends on DVB_USB help @@ -283,6 +283,7 @@ config DVB_USB_ANYSEE config DVB_USB_DTV5100 tristate "AME DTV-5100 USB2.0 DVB-T support" depends on DVB_USB + select DVB_ZL10353 if !DVB_FE_CUSTOMISE select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE help Say Y here to support the AME DTV-5100 USB2.0 DVB-T receiver. diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c index cb0829c038ce..847d8fdd9ec4 100644 --- a/drivers/media/dvb/dvb-usb/af9015.c +++ b/drivers/media/dvb/dvb-usb/af9015.c @@ -31,13 +31,13 @@ #include "mc44s80x.h" #endif -int dvb_usb_af9015_debug; +static int dvb_usb_af9015_debug; module_param_named(debug, dvb_usb_af9015_debug, int, 0644); MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS); -int dvb_usb_af9015_remote; +static int dvb_usb_af9015_remote; module_param_named(remote, dvb_usb_af9015_remote, int, 0644); MODULE_PARM_DESC(remote, "select remote"); -int dvb_usb_af9015_dual_mode; +static int dvb_usb_af9015_dual_mode; module_param_named(dual_mode, dvb_usb_af9015_dual_mode, int, 0644); MODULE_PARM_DESC(dual_mode, "enable dual mode"); DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); @@ -46,7 +46,7 @@ static DEFINE_MUTEX(af9015_usb_mutex); static struct af9015_config af9015_config; static struct dvb_usb_device_properties af9015_properties[2]; -int af9015_properties_count = ARRAY_SIZE(af9015_properties); +static int af9015_properties_count = ARRAY_SIZE(af9015_properties); static struct af9013_config af9015_af9013_config[] = { { @@ -549,7 +549,7 @@ static int af9015_eeprom_dump(struct dvb_usb_device *d) return 0; } -int af9015_download_ir_table(struct dvb_usb_device *d) +static int af9015_download_ir_table(struct dvb_usb_device *d) { int i, packets = 0, ret; u16 addr = 0x9a56; /* ir-table start address */ @@ -999,7 +999,7 @@ static int af9015_rc_query(struct dvb_usb_device *d, u32 *event, int *state) } /* init 2nd I2C adapter */ -int af9015_i2c_init(struct dvb_usb_device *d) +static int af9015_i2c_init(struct dvb_usb_device *d) { int ret; struct af9015_state *state = d->priv; @@ -1419,7 +1419,7 @@ static int af9015_usb_probe(struct usb_interface *intf, return ret; } -void af9015_i2c_exit(struct dvb_usb_device *d) +static void af9015_i2c_exit(struct dvb_usb_device *d) { struct af9015_state *state = d->priv; deb_info("%s: \n", __func__); diff --git a/drivers/media/dvb/dvb-usb/af9015.h b/drivers/media/dvb/dvb-usb/af9015.h index 882e8a4b3681..6c3c97293316 100644 --- a/drivers/media/dvb/dvb-usb/af9015.h +++ b/drivers/media/dvb/dvb-usb/af9015.h @@ -27,7 +27,6 @@ #define DVB_USB_LOG_PREFIX "af9015" #include "dvb-usb.h" -extern int dvb_usb_af9015_debug; #define deb_info(args...) dprintk(dvb_usb_af9015_debug, 0x01, args) #define deb_rc(args...) dprintk(dvb_usb_af9015_debug, 0x02, args) #define deb_xfer(args...) dprintk(dvb_usb_af9015_debug, 0x04, args) diff --git a/drivers/media/dvb/dvb-usb/anysee.c b/drivers/media/dvb/dvb-usb/anysee.c index c786359fba03..cd2edbcaa097 100644 --- a/drivers/media/dvb/dvb-usb/anysee.c +++ b/drivers/media/dvb/dvb-usb/anysee.c @@ -46,7 +46,7 @@ module_param_named(delsys, dvb_usb_anysee_delsys, int, 0644); MODULE_PARM_DESC(delsys, "select delivery mode (0=DVB-C, 1=DVB-T)"); DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); -static struct mutex anysee_usb_mutex; +static DEFINE_MUTEX(anysee_usb_mutex); static int anysee_ctrl_msg(struct dvb_usb_device *d, u8 *sbuf, u8 slen, u8 *rbuf, u8 rlen) @@ -456,8 +456,6 @@ static int anysee_probe(struct usb_interface *intf, struct usb_host_interface *alt; int ret; - mutex_init(&anysee_usb_mutex); - /* There is one interface with two alternate settings. Alternate setting 0 is for bulk transfer. Alternate setting 1 is for isochronous transfer. diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-urb.c b/drivers/media/dvb/dvb-usb/dvb-usb-urb.c index 5cef12a07f72..6fe71c6745eb 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-urb.c +++ b/drivers/media/dvb/dvb-usb/dvb-usb-urb.c @@ -13,14 +13,14 @@ int dvb_usb_generic_rw(struct dvb_usb_device *d, u8 *wbuf, u16 wlen, u8 *rbuf, { int actlen,ret = -ENOMEM; + if (!d || wbuf == NULL || wlen == 0) + return -EINVAL; + if (d->props.generic_bulk_ctrl_endpoint == 0) { err("endpoint for generic control not specified."); return -EINVAL; } - if (wbuf == NULL || wlen == 0) - return -EINVAL; - if ((ret = mutex_lock_interruptible(&d->usb_mutex))) return ret; diff --git a/drivers/media/dvb/frontends/af9013.c b/drivers/media/dvb/frontends/af9013.c index 21c1060cf10e..692b68a9e73b 100644 --- a/drivers/media/dvb/frontends/af9013.c +++ b/drivers/media/dvb/frontends/af9013.c @@ -1187,7 +1187,7 @@ static int af9013_read_status(struct dvb_frontend *fe, fe_status_t *status) if (tmp) *status |= FE_HAS_SYNC | FE_HAS_LOCK; - if (!*status & FE_HAS_SIGNAL) { + if (!(*status & FE_HAS_SIGNAL)) { /* AGC lock */ ret = af9013_read_reg_bits(state, 0xd1a0, 6, 1, &tmp); if (ret) @@ -1196,7 +1196,7 @@ static int af9013_read_status(struct dvb_frontend *fe, fe_status_t *status) *status |= FE_HAS_SIGNAL; } - if (!*status & FE_HAS_CARRIER) { + if (!(*status & FE_HAS_CARRIER)) { /* CFO lock */ ret = af9013_read_reg_bits(state, 0xd333, 7, 1, &tmp); if (ret) @@ -1205,7 +1205,7 @@ static int af9013_read_status(struct dvb_frontend *fe, fe_status_t *status) *status |= FE_HAS_CARRIER; } - if (!*status & FE_HAS_CARRIER) { + if (!(*status & FE_HAS_CARRIER)) { /* SFOE lock */ ret = af9013_read_reg_bits(state, 0xd334, 6, 1, &tmp); if (ret) diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index 04cd7c04bdde..5189c4eb439f 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -355,6 +355,20 @@ config USB_SI470X tristate "Silicon Labs Si470x FM Radio Receiver support" depends on USB && VIDEO_V4L2 ---help--- + This is a driver for USB devices with the Silicon Labs SI470x + chip. Currently these devices are known to work: + - 10c4:818a: Silicon Labs USB FM Radio Reference Design + - 06e1:a155: ADS/Tech FM Radio Receiver (formerly Instant FM Music) + - 1b80:d700: KWorld USB FM Radio SnapMusic Mobile 700 (FM700) + + Sound is provided by the ALSA USB Audio/MIDI driver. Therefore + if you don't want to use the device solely for RDS receiving, + it is recommended to also select SND_USB_AUDIO. + + Please have a look at the documentation, especially on how + to redirect the audio stream from the radio to your sound device: + Documentation/video4linux/si470x.txt + Say Y here if you want to connect this type of radio to your computer's USB port. diff --git a/drivers/media/radio/radio-si470x.c b/drivers/media/radio/radio-si470x.c index 5920cd306975..3e1830293de5 100644 --- a/drivers/media/radio/radio-si470x.c +++ b/drivers/media/radio/radio-si470x.c @@ -4,6 +4,7 @@ * Driver for USB radios for the Silicon Labs Si470x FM Radio Receivers: * - Silicon Labs USB FM Radio Reference Design * - ADS/Tech FM Radio Receiver (formerly Instant FM Music) (RDX-155-EF) + * - KWorld USB FM Radio SnapMusic Mobile 700 (FM700) * * Copyright (c) 2008 Tobias Lorenz <tobias.lorenz@gmx.net> * @@ -24,19 +25,6 @@ /* - * User Notes: - * - USB Audio is provided by the alsa snd_usb_audio module. - * For listing you have to redirect the sound, for example using: - * arecord -D hw:1,0 -r96000 -c2 -f S16_LE | artsdsp aplay -B - - * - regarding module parameters in /sys/module/radio_si470x/parameters: - * the contents of read-only files (0444) are not updated, even if - * space, band and de are changed using private video controls - * - increase tune_timeout, if you often get -EIO errors - * - hw_freq_seek returns -EAGAIN, when timed out or band limit is reached - */ - - -/* * History: * 2008-01-12 Tobias Lorenz <tobias.lorenz@gmx.net> * Version 1.0.0 @@ -105,6 +93,9 @@ * - afc indication * - more safety checks, let si470x_get_freq return errno * - vidioc behavior corrected according to v4l2 spec + * 2008-10-20 Alexey Klimov <klimov.linux@gmail.com> + * - add support for KWorld USB FM Radio FM700 + * - blacklisted KWorld radio in hid-core.c and hid-ids.h * * ToDo: * - add firmware download/update support @@ -145,6 +136,8 @@ static struct usb_device_id si470x_usb_driver_id_table[] = { { USB_DEVICE_AND_INTERFACE_INFO(0x10c4, 0x818a, USB_CLASS_HID, 0, 0) }, /* ADS/Tech FM Radio Receiver (formerly Instant FM Music) */ { USB_DEVICE_AND_INTERFACE_INFO(0x06e1, 0xa155, USB_CLASS_HID, 0, 0) }, + /* KWorld USB FM Radio SnapMusic Mobile 700 (FM700) */ + { USB_DEVICE_AND_INTERFACE_INFO(0x1b80, 0xd700, USB_CLASS_HID, 0, 0) }, /* Terminating entry */ { } }; diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 47102c2c8250..057fd7e160c4 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -759,7 +759,7 @@ config VIDEO_PXA27x config VIDEO_SH_MOBILE_CEU tristate "SuperH Mobile CEU Interface driver" - depends on VIDEO_DEV && SOC_CAMERA && HAS_DMA + depends on VIDEO_DEV && SOC_CAMERA && HAS_DMA && HAVE_CLK select VIDEOBUF_DMA_CONTIG ---help--- This is a v4l2 driver for the SuperH Mobile CEU Interface diff --git a/drivers/media/video/cafe_ccic.c b/drivers/media/video/cafe_ccic.c index a8c068e1de1c..87eef4be2070 100644 --- a/drivers/media/video/cafe_ccic.c +++ b/drivers/media/video/cafe_ccic.c @@ -569,7 +569,6 @@ static int cafe_smbus_setup(struct cafe_camera *cam) cafe_smbus_enable_irq(cam); adap->id = I2C_HW_SMBUS_CAFE; - adap->class = I2C_CLASS_CAM_DIGITAL; adap->owner = THIS_MODULE; adap->client_register = cafe_smbus_attach; adap->client_unregister = cafe_smbus_detach; @@ -1476,12 +1475,9 @@ static int cafe_v4l_open(struct inode *inode, struct file *filp) { struct cafe_camera *cam; - lock_kernel(); cam = cafe_find_dev(iminor(inode)); - if (cam == NULL) { - unlock_kernel(); + if (cam == NULL) return -ENODEV; - } filp->private_data = cam; mutex_lock(&cam->s_mutex); @@ -1493,7 +1489,6 @@ static int cafe_v4l_open(struct inode *inode, struct file *filp) } (cam->users)++; mutex_unlock(&cam->s_mutex); - unlock_kernel(); return 0; } diff --git a/drivers/media/video/compat_ioctl32.c b/drivers/media/video/compat_ioctl32.c index bd5d9de5a008..e6ca4012b5f0 100644 --- a/drivers/media/video/compat_ioctl32.c +++ b/drivers/media/video/compat_ioctl32.c @@ -867,6 +867,7 @@ long v4l_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) case VIDIOC_STREAMON32: case VIDIOC_STREAMOFF32: case VIDIOC_G_PARM: + case VIDIOC_S_PARM: case VIDIOC_G_STD: case VIDIOC_S_STD: case VIDIOC_G_TUNER: @@ -885,6 +886,8 @@ long v4l_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) case VIDIOC_S_INPUT32: case VIDIOC_TRY_FMT32: case VIDIOC_S_HW_FREQ_SEEK: + case VIDIOC_ENUM_FRAMESIZES: + case VIDIOC_ENUM_FRAMEINTERVALS: ret = do_video_ioctl(file, cmd, arg); break; diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c index 7a1a7830a6b3..7874d9790a51 100644 --- a/drivers/media/video/cx18/cx18-driver.c +++ b/drivers/media/video/cx18/cx18-driver.c @@ -448,7 +448,14 @@ static int __devinit cx18_init_struct1(struct cx18 *cx) mutex_init(&cx->gpio_lock); spin_lock_init(&cx->lock); - spin_lock_init(&cx->dma_reg_lock); + + cx->work_queue = create_singlethread_workqueue(cx->name); + if (cx->work_queue == NULL) { + CX18_ERR("Could not create work queue\n"); + return -1; + } + + INIT_WORK(&cx->work, cx18_work_handler); /* start counting open_id at 1 */ cx->open_id = 1; @@ -581,10 +588,10 @@ static void cx18_load_and_init_modules(struct cx18 *cx) #ifdef MODULE /* load modules */ -#ifndef CONFIG_MEDIA_TUNER +#ifdef CONFIG_MEDIA_TUNER_MODULE hw = cx18_request_module(cx, hw, "tuner", CX18_HW_TUNER); #endif -#ifndef CONFIG_VIDEO_CS5345 +#ifdef CONFIG_VIDEO_CS5345_MODULE hw = cx18_request_module(cx, hw, "cs5345", CX18_HW_CS5345); #endif #endif @@ -832,6 +839,7 @@ free_map: free_mem: release_mem_region(cx->base_addr, CX18_MEM_SIZE); free_workqueue: + destroy_workqueue(cx->work_queue); err: if (retval == 0) retval = -ENODEV; @@ -932,6 +940,9 @@ static void cx18_remove(struct pci_dev *pci_dev) cx18_halt_firmware(cx); + flush_workqueue(cx->work_queue); + destroy_workqueue(cx->work_queue); + cx18_streams_cleanup(cx, 1); exit_cx18_i2c(cx); diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index a4b1708fafe7..bbdd5f25041d 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h @@ -199,12 +199,15 @@ struct cx18_options { #define CX18_F_S_APPL_IO 8 /* this stream is used read/written by an application */ /* per-cx18, i_flags */ -#define CX18_F_I_LOADED_FW 0 /* Loaded the firmware the first time */ -#define CX18_F_I_EOS 4 /* End of encoder stream reached */ -#define CX18_F_I_RADIO_USER 5 /* The radio tuner is selected */ -#define CX18_F_I_ENC_PAUSED 13 /* the encoder is paused */ -#define CX18_F_I_INITED 21 /* set after first open */ -#define CX18_F_I_FAILED 22 /* set if first open failed */ +#define CX18_F_I_LOADED_FW 0 /* Loaded firmware 1st time */ +#define CX18_F_I_EOS 4 /* End of encoder stream */ +#define CX18_F_I_RADIO_USER 5 /* radio tuner is selected */ +#define CX18_F_I_ENC_PAUSED 13 /* the encoder is paused */ +#define CX18_F_I_HAVE_WORK 15 /* there is work to be done */ +#define CX18_F_I_WORK_HANDLER_DVB 18 /* work to be done for DVB */ +#define CX18_F_I_INITED 21 /* set after first open */ +#define CX18_F_I_FAILED 22 /* set if first open failed */ +#define CX18_F_I_WORK_INITED 23 /* worker thread initialized */ /* These are the VBI types as they appear in the embedded VBI private packets. */ #define CX18_SLICED_TYPE_TELETEXT_B (1) @@ -402,8 +405,6 @@ struct cx18 { spinlock_t lock; /* lock access to this struct */ int search_pack_header; - spinlock_t dma_reg_lock; /* lock access to DMA engine registers */ - int open_id; /* incremented each time an open occurs, used as unique ID. Starts at 1, so 0 can be used as uninitialized value in the stream->id. */ @@ -433,6 +434,9 @@ struct cx18 { /* when the current DMA is finished this queue is woken up */ wait_queue_head_t dma_waitq; + struct workqueue_struct *work_queue; + struct work_struct work; + /* i2c */ struct i2c_adapter i2c_adap[2]; struct i2c_algo_bit_data i2c_algo[2]; diff --git a/drivers/media/video/cx18/cx18-dvb.c b/drivers/media/video/cx18/cx18-dvb.c index afc694e7bdb2..4542e2e5e3d7 100644 --- a/drivers/media/video/cx18/cx18-dvb.c +++ b/drivers/media/video/cx18/cx18-dvb.c @@ -23,6 +23,8 @@ #include "cx18-dvb.h" #include "cx18-io.h" #include "cx18-streams.h" +#include "cx18-queue.h" +#include "cx18-scb.h" #include "cx18-cards.h" #include "s5h1409.h" #include "mxl5005s.h" @@ -300,3 +302,24 @@ static int dvb_register(struct cx18_stream *stream) return ret; } + +void cx18_dvb_work_handler(struct cx18 *cx) +{ + struct cx18_buffer *buf; + struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_TS]; + + while ((buf = cx18_dequeue(s, &s->q_full)) != NULL) { + if (s->dvb.enabled) + dvb_dmx_swfilter(&s->dvb.demux, buf->buf, + buf->bytesused); + + cx18_enqueue(s, buf, &s->q_free); + cx18_buf_sync_for_device(s, buf); + if (s->handle == CX18_INVALID_TASK_HANDLE) /* FIXME: improve */ + continue; + + cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle, + (void __iomem *)&cx->scb->cpu_mdl[buf->id] - cx->enc_mem, + 1, buf->id, s->buf_size); + } +} diff --git a/drivers/media/video/cx18/cx18-dvb.h b/drivers/media/video/cx18/cx18-dvb.h index bf8d8f6f5455..bbdcefc87f28 100644 --- a/drivers/media/video/cx18/cx18-dvb.h +++ b/drivers/media/video/cx18/cx18-dvb.h @@ -23,3 +23,4 @@ int cx18_dvb_register(struct cx18_stream *stream); void cx18_dvb_unregister(struct cx18_stream *stream); +void cx18_dvb_work_handler(struct cx18 *cx); diff --git a/drivers/media/video/cx18/cx18-io.c b/drivers/media/video/cx18/cx18-io.c index 700ab9439c16..220fae8d4ad7 100644 --- a/drivers/media/video/cx18/cx18-io.c +++ b/drivers/media/video/cx18/cx18-io.c @@ -88,6 +88,19 @@ void cx18_writel_retry(struct cx18 *cx, u32 val, void __iomem *addr) cx18_log_write_retries(cx, i, addr); } +void _cx18_writel_expect(struct cx18 *cx, u32 val, void __iomem *addr, + u32 eval, u32 mask) +{ + int i; + eval &= mask; + for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) { + cx18_writel_noretry(cx, val, addr); + if (eval == (cx18_readl_noretry(cx, addr) & mask)) + break; + } + cx18_log_write_retries(cx, i, addr); +} + void cx18_writew_retry(struct cx18 *cx, u16 val, void __iomem *addr) { int i; @@ -218,7 +231,7 @@ void cx18_memset_io(struct cx18 *cx, void __iomem *addr, int val, size_t count) void cx18_sw1_irq_enable(struct cx18 *cx, u32 val) { u32 r; - cx18_write_reg(cx, val, SW1_INT_STATUS); + cx18_write_reg_expect(cx, val, SW1_INT_STATUS, ~val, val); r = cx18_read_reg(cx, SW1_INT_ENABLE_PCI); cx18_write_reg(cx, r | val, SW1_INT_ENABLE_PCI); } @@ -233,7 +246,7 @@ void cx18_sw1_irq_disable(struct cx18 *cx, u32 val) void cx18_sw2_irq_enable(struct cx18 *cx, u32 val) { u32 r; - cx18_write_reg(cx, val, SW2_INT_STATUS); + cx18_write_reg_expect(cx, val, SW2_INT_STATUS, ~val, val); r = cx18_read_reg(cx, SW2_INT_ENABLE_PCI); cx18_write_reg(cx, r | val, SW2_INT_ENABLE_PCI); } diff --git a/drivers/media/video/cx18/cx18-io.h b/drivers/media/video/cx18/cx18-io.h index 287a5e8bf67b..425244453ea7 100644 --- a/drivers/media/video/cx18/cx18-io.h +++ b/drivers/media/video/cx18/cx18-io.h @@ -133,6 +133,8 @@ static inline void cx18_writel(struct cx18 *cx, u32 val, void __iomem *addr) cx18_writel_noretry(cx, val, addr); } +void _cx18_writel_expect(struct cx18 *cx, u32 val, void __iomem *addr, + u32 eval, u32 mask); static inline void cx18_writew_noretry(struct cx18 *cx, u16 val, void __iomem *addr) @@ -271,6 +273,21 @@ static inline void cx18_write_reg(struct cx18 *cx, u32 val, u32 reg) cx18_write_reg_noretry(cx, val, reg); } +static inline void _cx18_write_reg_expect(struct cx18 *cx, u32 val, u32 reg, + u32 eval, u32 mask) +{ + _cx18_writel_expect(cx, val, cx->reg_mem + reg, eval, mask); +} + +static inline void cx18_write_reg_expect(struct cx18 *cx, u32 val, u32 reg, + u32 eval, u32 mask) +{ + if (cx18_retry_mmio) + _cx18_write_reg_expect(cx, val, reg, eval, mask); + else + cx18_write_reg_noretry(cx, val, reg); +} + static inline u32 cx18_read_reg_noretry(struct cx18 *cx, u32 reg) { diff --git a/drivers/media/video/cx18/cx18-irq.c b/drivers/media/video/cx18/cx18-irq.c index 360330f5463f..5fbfbd0f1493 100644 --- a/drivers/media/video/cx18/cx18-irq.c +++ b/drivers/media/video/cx18/cx18-irq.c @@ -29,8 +29,20 @@ #include "cx18-mailbox.h" #include "cx18-vbi.h" #include "cx18-scb.h" +#include "cx18-dvb.h" -#define DMA_MAGIC_COOKIE 0x000001fe +void cx18_work_handler(struct work_struct *work) +{ + struct cx18 *cx = container_of(work, struct cx18, work); + if (test_and_clear_bit(CX18_F_I_WORK_INITED, &cx->i_flags)) { + struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 }; + /* This thread must use the FIFO scheduler as it + * is realtime sensitive. */ + sched_setscheduler(current, SCHED_FIFO, ¶m); + } + if (test_and_clear_bit(CX18_F_I_WORK_HANDLER_DVB, &cx->i_flags)) + cx18_dvb_work_handler(cx); +} static void epu_dma_done(struct cx18 *cx, struct cx18_mailbox *mb) { @@ -67,17 +79,11 @@ static void epu_dma_done(struct cx18 *cx, struct cx18_mailbox *mb) if (buf) { cx18_buf_sync_for_cpu(s, buf); if (s->type == CX18_ENC_STREAM_TYPE_TS && s->dvb.enabled) { - /* process the buffer here */ - CX18_DEBUG_HI_DMA("TS recv and sent bytesused=%d\n", - buf->bytesused); - - dvb_dmx_swfilter(&s->dvb.demux, buf->buf, + CX18_DEBUG_HI_DMA("TS recv bytesused = %d\n", buf->bytesused); - cx18_buf_sync_for_device(s, buf); - cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle, - (void __iomem *)&cx->scb->cpu_mdl[buf->id] - cx->enc_mem, - 1, buf->id, s->buf_size); + set_bit(CX18_F_I_WORK_HANDLER_DVB, &cx->i_flags); + set_bit(CX18_F_I_HAVE_WORK, &cx->i_flags); } else set_bit(CX18_F_B_NEED_BUF_SWAP, &buf->b_flags); } else { @@ -109,7 +115,7 @@ static void epu_debug(struct cx18 *cx, struct cx18_mailbox *mb) CX18_INFO("FW version: %s\n", p - 1); } -static void hpu_cmd(struct cx18 *cx, u32 sw1) +static void epu_cmd(struct cx18 *cx, u32 sw1) { struct cx18_mailbox mb; @@ -125,12 +131,31 @@ static void hpu_cmd(struct cx18 *cx, u32 sw1) epu_debug(cx, &mb); break; default: - CX18_WARN("Unexpected mailbox command %08x\n", mb.cmd); + CX18_WARN("Unknown CPU_TO_EPU mailbox command %#08x\n", + mb.cmd); break; } } - if (sw1 & (IRQ_APU_TO_EPU | IRQ_HPU_TO_EPU)) - CX18_WARN("Unexpected interrupt %08x\n", sw1); + + if (sw1 & IRQ_APU_TO_EPU) { + cx18_memcpy_fromio(cx, &mb, &cx->scb->apu2epu_mb, sizeof(mb)); + CX18_WARN("Unknown APU_TO_EPU mailbox command %#08x\n", mb.cmd); + } + + if (sw1 & IRQ_HPU_TO_EPU) { + cx18_memcpy_fromio(cx, &mb, &cx->scb->hpu2epu_mb, sizeof(mb)); + CX18_WARN("Unknown HPU_TO_EPU mailbox command %#08x\n", mb.cmd); + } +} + +static void xpu_ack(struct cx18 *cx, u32 sw2) +{ + if (sw2 & IRQ_CPU_TO_EPU_ACK) + wake_up(&cx->mb_cpu_waitq); + if (sw2 & IRQ_APU_TO_EPU_ACK) + wake_up(&cx->mb_apu_waitq); + if (sw2 & IRQ_HPU_TO_EPU_ACK) + wake_up(&cx->mb_hpu_waitq); } irqreturn_t cx18_irq_handler(int irq, void *dev_id) @@ -140,43 +165,36 @@ irqreturn_t cx18_irq_handler(int irq, void *dev_id) u32 sw2, sw2_mask; u32 hw2, hw2_mask; - spin_lock(&cx->dma_reg_lock); - + sw1_mask = cx18_read_reg(cx, SW1_INT_ENABLE_PCI); + sw1 = cx18_read_reg(cx, SW1_INT_STATUS) & sw1_mask; + sw2_mask = cx18_read_reg(cx, SW2_INT_ENABLE_PCI); + sw2 = cx18_read_reg(cx, SW2_INT_STATUS) & sw2_mask; hw2_mask = cx18_read_reg(cx, HW2_INT_MASK5_PCI); hw2 = cx18_read_reg(cx, HW2_INT_CLR_STATUS) & hw2_mask; - sw2_mask = cx18_read_reg(cx, SW2_INT_ENABLE_PCI) | IRQ_EPU_TO_HPU_ACK; - sw2 = cx18_read_reg(cx, SW2_INT_STATUS) & sw2_mask; - sw1_mask = cx18_read_reg(cx, SW1_INT_ENABLE_PCI) | IRQ_EPU_TO_HPU; - sw1 = cx18_read_reg(cx, SW1_INT_STATUS) & sw1_mask; - cx18_write_reg(cx, sw2&sw2_mask, SW2_INT_STATUS); - cx18_write_reg(cx, sw1&sw1_mask, SW1_INT_STATUS); - cx18_write_reg(cx, hw2&hw2_mask, HW2_INT_CLR_STATUS); + if (sw1) + cx18_write_reg_expect(cx, sw1, SW1_INT_STATUS, ~sw1, sw1); + if (sw2) + cx18_write_reg_expect(cx, sw2, SW2_INT_STATUS, ~sw2, sw2); + if (hw2) + cx18_write_reg_expect(cx, hw2, HW2_INT_CLR_STATUS, ~hw2, hw2); if (sw1 || sw2 || hw2) CX18_DEBUG_HI_IRQ("SW1: %x SW2: %x HW2: %x\n", sw1, sw2, hw2); /* To do: interrupt-based I2C handling - if (hw2 & 0x00c00000) { + if (hw2 & (HW2_I2C1_INT|HW2_I2C2_INT)) { } */ - if (sw2) { - if (sw2 & (cx18_readl(cx, &cx->scb->cpu2hpu_irq_ack) | - cx18_readl(cx, &cx->scb->cpu2epu_irq_ack))) - wake_up(&cx->mb_cpu_waitq); - if (sw2 & (cx18_readl(cx, &cx->scb->apu2hpu_irq_ack) | - cx18_readl(cx, &cx->scb->apu2epu_irq_ack))) - wake_up(&cx->mb_apu_waitq); - if (sw2 & cx18_readl(cx, &cx->scb->epu2hpu_irq_ack)) - wake_up(&cx->mb_epu_waitq); - if (sw2 & cx18_readl(cx, &cx->scb->hpu2epu_irq_ack)) - wake_up(&cx->mb_hpu_waitq); - } + if (sw2) + xpu_ack(cx, sw2); if (sw1) - hpu_cmd(cx, sw1); - spin_unlock(&cx->dma_reg_lock); + epu_cmd(cx, sw1); + + if (test_and_clear_bit(CX18_F_I_HAVE_WORK, &cx->i_flags)) + queue_work(cx->work_queue, &cx->work); - return (hw2 | sw1 | sw2) ? IRQ_HANDLED : IRQ_NONE; + return (sw1 || sw2 || hw2) ? IRQ_HANDLED : IRQ_NONE; } diff --git a/drivers/media/video/cx18/cx18-irq.h b/drivers/media/video/cx18/cx18-irq.h index 379f704f5cba..6173ca3bc9e4 100644 --- a/drivers/media/video/cx18/cx18-irq.h +++ b/drivers/media/video/cx18/cx18-irq.h @@ -32,6 +32,4 @@ irqreturn_t cx18_irq_handler(int irq, void *dev_id); -void cx18_irq_work_handler(struct work_struct *work); -void cx18_dma_stream_dec_prepare(struct cx18_stream *s, u32 offset, int lock); -void cx18_unfinished_dma(unsigned long arg); +void cx18_work_handler(struct work_struct *work); diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c index 9d18dd22de76..acff7dfb60df 100644 --- a/drivers/media/video/cx18/cx18-mailbox.c +++ b/drivers/media/video/cx18/cx18-mailbox.c @@ -83,7 +83,7 @@ static const struct cx18_api_info api_info[] = { API_ENTRY(CPU, CX18_CPU_DE_SET_MDL_ACK, 0), API_ENTRY(CPU, CX18_CPU_DE_SET_MDL, API_FAST), API_ENTRY(CPU, CX18_APU_RESETAI, API_FAST), - API_ENTRY(CPU, CX18_CPU_DE_RELEASE_MDL, 0), + API_ENTRY(CPU, CX18_CPU_DE_RELEASE_MDL, API_SLOW), API_ENTRY(0, 0, 0), }; @@ -176,7 +176,7 @@ long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb) cx18_setup_page(cx, SCB_OFFSET); cx18_write_sync(cx, mb->request, &ack_mb->ack); - cx18_write_reg(cx, ack_irq, SW2_INT_SET); + cx18_write_reg_expect(cx, ack_irq, SW2_INT_SET, ack_irq, ack_irq); return 0; } @@ -225,7 +225,7 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) } if (info->flags & API_FAST) timeout /= 2; - cx18_write_reg(cx, irq, SW1_INT_SET); + cx18_write_reg_expect(cx, irq, SW1_INT_SET, irq, irq); while (!sig && cx18_readl(cx, &mb->ack) != cx18_readl(cx, &mb->request) && cnt < 660) { diff --git a/drivers/media/video/cx18/cx18-queue.c b/drivers/media/video/cx18/cx18-queue.c index a33ba04a2686..174682c2582f 100644 --- a/drivers/media/video/cx18/cx18-queue.c +++ b/drivers/media/video/cx18/cx18-queue.c @@ -88,15 +88,13 @@ struct cx18_buffer *cx18_queue_get_buf_irq(struct cx18_stream *s, u32 id, if (buf->id != id) continue; + buf->bytesused = bytesused; - /* the transport buffers are handled differently, - they are not moved to the full queue */ - if (s->type != CX18_ENC_STREAM_TYPE_TS) { - atomic_dec(&s->q_free.buffers); - atomic_inc(&s->q_full.buffers); - s->q_full.bytesused += buf->bytesused; - list_move_tail(&buf->list, &s->q_full.list); - } + atomic_dec(&s->q_free.buffers); + atomic_inc(&s->q_full.buffers); + s->q_full.bytesused += buf->bytesused; + list_move_tail(&buf->list, &s->q_full.list); + spin_unlock(&s->qlock); return buf; } diff --git a/drivers/media/video/cx18/cx18-scb.h b/drivers/media/video/cx18/cx18-scb.h index 86b4cb15d163..594713bbed68 100644 --- a/drivers/media/video/cx18/cx18-scb.h +++ b/drivers/media/video/cx18/cx18-scb.h @@ -128,22 +128,22 @@ struct cx18_scb { u32 apu2cpu_irq; /* Value to write to register SW2 register set (0xC7003140) after the command is cleared */ - u32 apu2cpu_irq_ack; + u32 cpu2apu_irq_ack; u32 reserved2[13]; u32 hpu2cpu_mb_offset; u32 hpu2cpu_irq; - u32 hpu2cpu_irq_ack; + u32 cpu2hpu_irq_ack; u32 reserved3[13]; u32 ppu2cpu_mb_offset; u32 ppu2cpu_irq; - u32 ppu2cpu_irq_ack; + u32 cpu2ppu_irq_ack; u32 reserved4[13]; u32 epu2cpu_mb_offset; u32 epu2cpu_irq; - u32 epu2cpu_irq_ack; + u32 cpu2epu_irq_ack; u32 reserved5[13]; u32 reserved6[8]; @@ -153,22 +153,22 @@ struct cx18_scb { u32 reserved11[7]; u32 cpu2apu_mb_offset; u32 cpu2apu_irq; - u32 cpu2apu_irq_ack; + u32 apu2cpu_irq_ack; u32 reserved12[13]; u32 hpu2apu_mb_offset; u32 hpu2apu_irq; - u32 hpu2apu_irq_ack; + u32 apu2hpu_irq_ack; u32 reserved13[13]; u32 ppu2apu_mb_offset; u32 ppu2apu_irq; - u32 ppu2apu_irq_ack; + u32 apu2ppu_irq_ack; u32 reserved14[13]; u32 epu2apu_mb_offset; u32 epu2apu_irq; - u32 epu2apu_irq_ack; + u32 apu2epu_irq_ack; u32 reserved15[13]; u32 reserved16[8]; @@ -178,22 +178,22 @@ struct cx18_scb { u32 reserved21[7]; u32 cpu2hpu_mb_offset; u32 cpu2hpu_irq; - u32 cpu2hpu_irq_ack; + u32 hpu2cpu_irq_ack; u32 reserved22[13]; u32 apu2hpu_mb_offset; u32 apu2hpu_irq; - u32 apu2hpu_irq_ack; + u32 hpu2apu_irq_ack; u32 reserved23[13]; u32 ppu2hpu_mb_offset; u32 ppu2hpu_irq; - u32 ppu2hpu_irq_ack; + u32 hpu2ppu_irq_ack; u32 reserved24[13]; u32 epu2hpu_mb_offset; u32 epu2hpu_irq; - u32 epu2hpu_irq_ack; + u32 hpu2epu_irq_ack; u32 reserved25[13]; u32 reserved26[8]; @@ -203,22 +203,22 @@ struct cx18_scb { u32 reserved31[7]; u32 cpu2ppu_mb_offset; u32 cpu2ppu_irq; - u32 cpu2ppu_irq_ack; + u32 ppu2cpu_irq_ack; u32 reserved32[13]; u32 apu2ppu_mb_offset; u32 apu2ppu_irq; - u32 apu2ppu_irq_ack; + u32 ppu2apu_irq_ack; u32 reserved33[13]; u32 hpu2ppu_mb_offset; u32 hpu2ppu_irq; - u32 hpu2ppu_irq_ack; + u32 ppu2hpu_irq_ack; u32 reserved34[13]; u32 epu2ppu_mb_offset; u32 epu2ppu_irq; - u32 epu2ppu_irq_ack; + u32 ppu2epu_irq_ack; u32 reserved35[13]; u32 reserved36[8]; @@ -228,22 +228,22 @@ struct cx18_scb { u32 reserved41[7]; u32 cpu2epu_mb_offset; u32 cpu2epu_irq; - u32 cpu2epu_irq_ack; + u32 epu2cpu_irq_ack; u32 reserved42[13]; u32 apu2epu_mb_offset; u32 apu2epu_irq; - u32 apu2epu_irq_ack; + u32 epu2apu_irq_ack; u32 reserved43[13]; u32 hpu2epu_mb_offset; u32 hpu2epu_irq; - u32 hpu2epu_irq_ack; + u32 epu2hpu_irq_ack; u32 reserved44[13]; u32 ppu2epu_mb_offset; u32 ppu2epu_irq; - u32 ppu2epu_irq_ack; + u32 epu2ppu_irq_ack; u32 reserved45[13]; u32 reserved46[8]; diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c index 078be6319556..d3ae5b4dfca7 100644 --- a/drivers/media/video/cx88/cx88-blackbird.c +++ b/drivers/media/video/cx88/cx88-blackbird.c @@ -1078,7 +1078,7 @@ static int mpeg_open(struct inode *inode, struct file *file) } } - if (blackbird_initialize_codec(dev) < 0) { + if (!atomic_read(&dev->core->mpeg_users) && blackbird_initialize_codec(dev) < 0) { if (drv) drv->request_release(drv); unlock_kernel(); @@ -1109,6 +1109,8 @@ static int mpeg_open(struct inode *inode, struct file *file) fh->mpegq.field); unlock_kernel(); + atomic_inc(&dev->core->mpeg_users); + return 0; } @@ -1118,7 +1120,7 @@ static int mpeg_release(struct inode *inode, struct file *file) struct cx8802_dev *dev = fh->dev; struct cx8802_driver *drv = NULL; - if (dev->mpeg_active) + if (dev->mpeg_active && atomic_read(&dev->core->mpeg_users) == 1) blackbird_stop_codec(dev); cx8802_cancel_buffers(fh->dev); @@ -1138,6 +1140,8 @@ static int mpeg_release(struct inode *inode, struct file *file) if (drv) drv->request_release(drv); + atomic_dec(&dev->core->mpeg_users); + return 0; } @@ -1158,6 +1162,10 @@ static unsigned int mpeg_poll(struct file *file, struct poll_table_struct *wait) { struct cx8802_fh *fh = file->private_data; + struct cx8802_dev *dev = fh->dev; + + if (!dev->mpeg_active) + blackbird_start_codec(file, fh); return videobuf_poll_stream(file, &fh->mpegq, wait); } diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c index cf6c30d4e545..309ca5e68063 100644 --- a/drivers/media/video/cx88/cx88-dvb.c +++ b/drivers/media/video/cx88/cx88-dvb.c @@ -598,6 +598,11 @@ static int dvb_register(struct cx8802_dev *dev) struct videobuf_dvb_frontend *fe0, *fe1 = NULL; int mfe_shared = 0; /* bus not shared by default */ + if (0 != core->i2c_rc) { + printk(KERN_ERR "%s/2: no i2c-bus available, cannot attach dvb drivers\n", core->name); + goto frontend_detach; + } + /* Get the first frontend */ fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1); if (!fe0) diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c index a1c435b4b1cd..3ebdcd1d83f8 100644 --- a/drivers/media/video/cx88/cx88-mpeg.c +++ b/drivers/media/video/cx88/cx88-mpeg.c @@ -769,10 +769,6 @@ static int __devinit cx8802_probe(struct pci_dev *pci_dev, struct cx8802_dev *dev; struct cx88_core *core; int err; -#if defined(CONFIG_VIDEO_CX88_DVB) || defined(CONFIG_VIDEO_CX88_DVB_MODULE) - struct videobuf_dvb_frontend *demod; - int i; -#endif /* general setup */ core = cx88_core_get(pci_dev); @@ -803,15 +799,21 @@ static int __devinit cx8802_probe(struct pci_dev *pci_dev, mutex_init(&dev->frontends.lock); INIT_LIST_HEAD(&dev->frontends.felist); - if (core->board.num_frontends) - printk(KERN_INFO "%s() allocating %d frontend(s)\n", __func__, core->board.num_frontends); - - for (i = 1; i <= core->board.num_frontends; i++) { - demod = videobuf_dvb_alloc_frontend(&dev->frontends, i); - if(demod == NULL) { - printk(KERN_ERR "%s() failed to alloc\n", __func__); - err = -ENOMEM; - goto fail_free; + if (core->board.num_frontends) { + struct videobuf_dvb_frontend *fe; + int i; + + printk(KERN_INFO "%s() allocating %d frontend(s)\n", __func__, + core->board.num_frontends); + for (i = 1; i <= core->board.num_frontends; i++) { + fe = videobuf_dvb_alloc_frontend(&dev->frontends, i); + if(fe == NULL) { + printk(KERN_ERR "%s() failed to alloc\n", + __func__); + videobuf_dvb_dealloc_frontends(&dev->frontends); + err = -ENOMEM; + goto fail_free; + } } } #endif diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c index 61265fd04d56..b96ce991d968 100644 --- a/drivers/media/video/cx88/cx88-video.c +++ b/drivers/media/video/cx88/cx88-video.c @@ -1216,8 +1216,12 @@ static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) struct cx8800_fh *fh = priv; struct cx8800_dev *dev = fh->dev; - if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) + /* We should remember that this driver also supports teletext, */ + /* so we have to test if the v4l2_buf_type is VBI capture data. */ + if (unlikely((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && + (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE))) return -EINVAL; + if (unlikely(i != fh->type)) return -EINVAL; @@ -1232,8 +1236,10 @@ static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) struct cx8800_dev *dev = fh->dev; int err, res; - if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + if ((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && + (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE)) return -EINVAL; + if (i != fh->type) return -EINVAL; diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h index 76207c2856b7..f4240965be32 100644 --- a/drivers/media/video/cx88/cx88.h +++ b/drivers/media/video/cx88/cx88.h @@ -352,6 +352,7 @@ struct cx88_core { /* various v4l controls */ u32 freq; atomic_t users; + atomic_t mpeg_users; /* cx88-video needs to access cx8802 for hybrid tuner pll access. */ struct cx8802_dev *dvbdev; diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig index 4d0817471c9f..6b557c057fac 100644 --- a/drivers/media/video/gspca/Kconfig +++ b/drivers/media/video/gspca/Kconfig @@ -3,16 +3,16 @@ menuconfig USB_GSPCA depends on VIDEO_V4L2 default m ---help--- - Say Y here if you want to enable selecting webcams based - on the GSPCA framework. + Say Y here if you want to enable selecting webcams based + on the GSPCA framework. - See <file:Documentation/video4linux/gspca.txt> for more info. + See <file:Documentation/video4linux/gspca.txt> for more info. - This driver uses the Video For Linux API. You must say Y or M to - "Video For Linux" to use this driver. + This driver uses the Video For Linux API. You must say Y or M to + "Video For Linux" to use this driver. - To compile this driver as modules, choose M here: the - modules will be called gspca_main. + To compile this driver as modules, choose M here: the + modules will be called gspca_main. if USB_GSPCA && VIDEO_V4L2 @@ -23,190 +23,190 @@ config USB_GSPCA_CONEX tristate "Conexant Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA help - Say Y here if you want support for cameras based on the Conexant chip. + Say Y here if you want support for cameras based on the Conexant chip. - To compile this driver as a module, choose M here: the - module will be called gspca_conex. + To compile this driver as a module, choose M here: the + module will be called gspca_conex. config USB_GSPCA_ETOMS tristate "Etoms USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA help - Say Y here if you want support for cameras based on the Etoms chip. + Say Y here if you want support for cameras based on the Etoms chip. - To compile this driver as a module, choose M here: the - module will be called gspca_etoms. + To compile this driver as a module, choose M here: the + module will be called gspca_etoms. config USB_GSPCA_FINEPIX tristate "Fujifilm FinePix USB V4L2 driver" depends on VIDEO_V4L2 && USB_GSPCA help - Say Y here if you want support for cameras based on the FinePix chip. + Say Y here if you want support for cameras based on the FinePix chip. - To compile this driver as a module, choose M here: the - module will be called gspca_finepix. + To compile this driver as a module, choose M here: the + module will be called gspca_finepix. config USB_GSPCA_MARS tristate "Mars USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA help - Say Y here if you want support for cameras based on the Mars chip. + Say Y here if you want support for cameras based on the Mars chip. - To compile this driver as a module, choose M here: the - module will be called gspca_mars. + To compile this driver as a module, choose M here: the + module will be called gspca_mars. config USB_GSPCA_OV519 tristate "OV519 USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA help - Say Y here if you want support for cameras based on the OV519 chip. + Say Y here if you want support for cameras based on the OV519 chip. - To compile this driver as a module, choose M here: the - module will be called gspca_ov519. + To compile this driver as a module, choose M here: the + module will be called gspca_ov519. config USB_GSPCA_PAC207 tristate "Pixart PAC207 USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA help - Say Y here if you want support for cameras based on the PAC207 chip. + Say Y here if you want support for cameras based on the PAC207 chip. - To compile this driver as a module, choose M here: the - module will be called gspca_pac207. + To compile this driver as a module, choose M here: the + module will be called gspca_pac207. config USB_GSPCA_PAC7311 tristate "Pixart PAC7311 USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA help - Say Y here if you want support for cameras based on the PAC7311 chip. + Say Y here if you want support for cameras based on the PAC7311 chip. - To compile this driver as a module, choose M here: the - module will be called gspca_pac7311. + To compile this driver as a module, choose M here: the + module will be called gspca_pac7311. config USB_GSPCA_SONIXB tristate "SN9C102 USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA help - Say Y here if you want support for cameras based on the SONIXB chip. + Say Y here if you want support for cameras based on the SONIXB chip. - To compile this driver as a module, choose M here: the - module will be called gspca_sonixb. + To compile this driver as a module, choose M here: the + module will be called gspca_sonixb. config USB_GSPCA_SONIXJ tristate "SONIX JPEG USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA help - Say Y here if you want support for cameras based on the SONIXJ chip. + Say Y here if you want support for cameras based on the SONIXJ chip. - To compile this driver as a module, choose M here: the - module will be called gspca_sonixj + To compile this driver as a module, choose M here: the + module will be called gspca_sonixj config USB_GSPCA_SPCA500 tristate "SPCA500 USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA help - Say Y here if you want support for cameras based on the SPCA500 chip. + Say Y here if you want support for cameras based on the SPCA500 chip. - To compile this driver as a module, choose M here: the - module will be called gspca_spca500. + To compile this driver as a module, choose M here: the + module will be called gspca_spca500. config USB_GSPCA_SPCA501 tristate "SPCA501 USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA help - Say Y here if you want support for cameras based on the SPCA501 chip. + Say Y here if you want support for cameras based on the SPCA501 chip. - To compile this driver as a module, choose M here: the - module will be called gspca_spca501. + To compile this driver as a module, choose M here: the + module will be called gspca_spca501. config USB_GSPCA_SPCA505 tristate "SPCA505 USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA help - Say Y here if you want support for cameras based on the SPCA505 chip. + Say Y here if you want support for cameras based on the SPCA505 chip. - To compile this driver as a module, choose M here: the - module will be called gspca_spca505. + To compile this driver as a module, choose M here: the + module will be called gspca_spca505. config USB_GSPCA_SPCA506 tristate "SPCA506 USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA help - Say Y here if you want support for cameras based on the SPCA506 chip. + Say Y here if you want support for cameras based on the SPCA506 chip. - To compile this driver as a module, choose M here: the - module will be called gspca_spca506. + To compile this driver as a module, choose M here: the + module will be called gspca_spca506. config USB_GSPCA_SPCA508 tristate "SPCA508 USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA help - Say Y here if you want support for cameras based on the SPCA508 chip. + Say Y here if you want support for cameras based on the SPCA508 chip. - To compile this driver as a module, choose M here: the - module will be called gspca_spca508. + To compile this driver as a module, choose M here: the + module will be called gspca_spca508. config USB_GSPCA_SPCA561 tristate "SPCA561 USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA help - Say Y here if you want support for cameras based on the SPCA561 chip. + Say Y here if you want support for cameras based on the SPCA561 chip. - To compile this driver as a module, choose M here: the - module will be called gspca_spca561. + To compile this driver as a module, choose M here: the + module will be called gspca_spca561. config USB_GSPCA_STK014 tristate "Syntek DV4000 (STK014) USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA help - Say Y here if you want support for cameras based on the STK014 chip. + Say Y here if you want support for cameras based on the STK014 chip. - To compile this driver as a module, choose M here: the - module will be called gspca_stk014. + To compile this driver as a module, choose M here: the + module will be called gspca_stk014. config USB_GSPCA_SUNPLUS tristate "SUNPLUS USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA help - Say Y here if you want support for cameras based on the Sunplus - SPCA504(abc) SPCA533 SPCA536 chips. + Say Y here if you want support for cameras based on the Sunplus + SPCA504(abc) SPCA533 SPCA536 chips. - To compile this driver as a module, choose M here: the - module will be called gspca_spca5xx. + To compile this driver as a module, choose M here: the + module will be called gspca_spca5xx. config USB_GSPCA_T613 tristate "T613 (JPEG Compliance) USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA help - Say Y here if you want support for cameras based on the T613 chip. + Say Y here if you want support for cameras based on the T613 chip. - To compile this driver as a module, choose M here: the - module will be called gspca_t613. + To compile this driver as a module, choose M here: the + module will be called gspca_t613. config USB_GSPCA_TV8532 tristate "TV8532 USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA help - Say Y here if you want support for cameras based on the TV8531 chip. + Say Y here if you want support for cameras based on the TV8531 chip. - To compile this driver as a module, choose M here: the - module will be called gspca_tv8532. + To compile this driver as a module, choose M here: the + module will be called gspca_tv8532. config USB_GSPCA_VC032X tristate "VC032X USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA help - Say Y here if you want support for cameras based on the VC032X chip. + Say Y here if you want support for cameras based on the VC032X chip. - To compile this driver as a module, choose M here: the - module will be called gspca_vc032x. + To compile this driver as a module, choose M here: the + module will be called gspca_vc032x. config USB_GSPCA_ZC3XX - tristate "VC3xx USB Camera Driver" + tristate "ZC3XX USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA help - Say Y here if you want support for cameras based on the ZC3XX chip. + Say Y here if you want support for cameras based on the ZC3XX chip. - To compile this driver as a module, choose M here: the - module will be called gspca_zc3xx. + To compile this driver as a module, choose M here: the + module will be called gspca_zc3xx. endif diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index d0a4451dc46f..8b3101d347c3 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -2266,7 +2266,7 @@ static const struct usb_action hdcs2020b_NoFliker[] = { {} }; -static const struct usb_action hv7131bxx_Initial[] = { +static const struct usb_action hv7131bxx_Initial[] = { /* 320x240 */ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT}, @@ -2290,7 +2290,7 @@ static const struct usb_action hv7131bxx_Initial[] = { {0xaa, 0x14, 0x0001}, {0xaa, 0x15, 0x00e8}, {0xaa, 0x16, 0x0002}, - {0xaa, 0x17, 0x0086}, + {0xaa, 0x17, 0x0086}, /* 00,17,88,aa */ {0xaa, 0x31, 0x0038}, {0xaa, 0x32, 0x0038}, {0xaa, 0x33, 0x0038}, @@ -2309,7 +2309,7 @@ static const struct usb_action hv7131bxx_Initial[] = { {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, - {0xaa, 0x02, 0x0080}, /* {0xaa, 0x02, 0x0090}; */ + {0xaa, 0x02, 0x0090}, /* 00,02,80,aa */ {0xa1, 0x01, 0x0002}, {0xa0, 0x00, ZC3XX_R092_I2CADDRESSSELECT}, {0xa0, 0x02, ZC3XX_R090_I2CCOMMAND}, @@ -2374,7 +2374,7 @@ static const struct usb_action hv7131bxx_Initial[] = { {} }; -static const struct usb_action hv7131bxx_InitialScale[] = { +static const struct usb_action hv7131bxx_InitialScale[] = { /* 640x480*/ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT}, @@ -6388,6 +6388,8 @@ static void setbrightness(struct gspca_dev *gspca_dev) /*fixme: is it really write to 011d and 018d for all other sensors? */ brightness = sd->brightness; reg_w(gspca_dev->dev, brightness, 0x011d); + if (sd->sensor == SENSOR_HV7131B) + return; if (brightness < 0x70) brightness += 0x10; else @@ -6529,6 +6531,7 @@ static void setquality(struct gspca_dev *gspca_dev) switch (sd->sensor) { case SENSOR_GC0305: + case SENSOR_HV7131B: case SENSOR_OV7620: case SENSOR_PO2030: return; @@ -7209,7 +7212,6 @@ static int sd_start(struct gspca_dev *gspca_dev) mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; zc3_init = init_tb[(int) sd->sensor][mode]; switch (sd->sensor) { - case SENSOR_HV7131B: case SENSOR_HV7131C: zcxx_probeSensor(gspca_dev); break; diff --git a/drivers/media/video/ivtv/Kconfig b/drivers/media/video/ivtv/Kconfig index 0069898bddab..c46bfb1569e3 100644 --- a/drivers/media/video/ivtv/Kconfig +++ b/drivers/media/video/ivtv/Kconfig @@ -1,6 +1,6 @@ config VIDEO_IVTV tristate "Conexant cx23416/cx23415 MPEG encoder/decoder support" - depends on VIDEO_V4L1 && VIDEO_V4L2 && PCI && I2C && EXPERIMENTAL + depends on VIDEO_V4L2 && PCI && I2C depends on INPUT # due to VIDEO_IR select I2C_ALGOBIT select VIDEO_IR @@ -12,7 +12,6 @@ config VIDEO_IVTV select VIDEO_SAA711X select VIDEO_SAA717X select VIDEO_SAA7127 - select VIDEO_TVAUDIO select VIDEO_CS53L32A select VIDEO_M52790 select VIDEO_WM8775 @@ -32,7 +31,7 @@ config VIDEO_IVTV config VIDEO_FB_IVTV tristate "Conexant cx23415 framebuffer support" - depends on VIDEO_IVTV && FB && EXPERIMENTAL + depends on VIDEO_IVTV && FB select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c index d36485023b68..b69cc1d55e5b 100644 --- a/drivers/media/video/ivtv/ivtv-driver.c +++ b/drivers/media/video/ivtv/ivtv-driver.c @@ -875,43 +875,43 @@ static void ivtv_load_and_init_modules(struct ivtv *itv) #ifdef MODULE /* load modules */ -#ifndef CONFIG_MEDIA_TUNER +#ifdef CONFIG_MEDIA_TUNER_MODULE hw = ivtv_request_module(itv, hw, "tuner", IVTV_HW_TUNER); #endif -#ifndef CONFIG_VIDEO_CX25840 +#ifdef CONFIG_VIDEO_CX25840_MODULE hw = ivtv_request_module(itv, hw, "cx25840", IVTV_HW_CX25840); #endif -#ifndef CONFIG_VIDEO_SAA711X +#ifdef CONFIG_VIDEO_SAA711X_MODULE hw = ivtv_request_module(itv, hw, "saa7115", IVTV_HW_SAA711X); #endif -#ifndef CONFIG_VIDEO_SAA7127 +#ifdef CONFIG_VIDEO_SAA7127_MODULE hw = ivtv_request_module(itv, hw, "saa7127", IVTV_HW_SAA7127); #endif -#ifndef CONFIG_VIDEO_SAA717X +#ifdef CONFIG_VIDEO_SAA717X_MODULE hw = ivtv_request_module(itv, hw, "saa717x", IVTV_HW_SAA717X); #endif -#ifndef CONFIG_VIDEO_UPD64031A +#ifdef CONFIG_VIDEO_UPD64031A_MODULE hw = ivtv_request_module(itv, hw, "upd64031a", IVTV_HW_UPD64031A); #endif -#ifndef CONFIG_VIDEO_UPD64083 +#ifdef CONFIG_VIDEO_UPD64083_MODULE hw = ivtv_request_module(itv, hw, "upd64083", IVTV_HW_UPD6408X); #endif -#ifndef CONFIG_VIDEO_MSP3400 +#ifdef CONFIG_VIDEO_MSP3400_MODULE hw = ivtv_request_module(itv, hw, "msp3400", IVTV_HW_MSP34XX); #endif -#ifndef CONFIG_VIDEO_VP27SMPX +#ifdef CONFIG_VIDEO_VP27SMPX_MODULE hw = ivtv_request_module(itv, hw, "vp27smpx", IVTV_HW_VP27SMPX); #endif -#ifndef CONFIG_VIDEO_WM8775 +#ifdef CONFIG_VIDEO_WM8775_MODULE hw = ivtv_request_module(itv, hw, "wm8775", IVTV_HW_WM8775); #endif -#ifndef CONFIG_VIDEO_WM8739 +#ifdef CONFIG_VIDEO_WM8739_MODULE hw = ivtv_request_module(itv, hw, "wm8739", IVTV_HW_WM8739); #endif -#ifndef CONFIG_VIDEO_CS53L32A +#ifdef CONFIG_VIDEO_CS53L32A_MODULE hw = ivtv_request_module(itv, hw, "cs53l32a", IVTV_HW_CS53L32A); #endif -#ifndef CONFIG_VIDEO_M52790 +#ifdef CONFIG_VIDEO_M52790_MODULE hw = ivtv_request_module(itv, hw, "m52790", IVTV_HW_M52790); #endif #endif diff --git a/drivers/media/video/ov7670.c b/drivers/media/video/ov7670.c index ea032f5f2f41..39bfe5d097c9 100644 --- a/drivers/media/video/ov7670.c +++ b/drivers/media/video/ov7670.c @@ -1347,7 +1347,6 @@ static struct i2c_driver ov7670_driver = { .name = "ov7670", }, .id = I2C_DRIVERID_OV7670, - .class = I2C_CLASS_CAM_DIGITAL, .attach_adapter = ov7670_attach, .detach_client = ov7670_detach, .command = ov7670_command, diff --git a/drivers/media/video/ovcamchip/ovcamchip_core.c b/drivers/media/video/ovcamchip/ovcamchip_core.c index 2c4acbf5a4fe..c841f4e4fbe4 100644 --- a/drivers/media/video/ovcamchip/ovcamchip_core.c +++ b/drivers/media/video/ovcamchip/ovcamchip_core.c @@ -405,7 +405,6 @@ static struct i2c_driver driver = { .name = "ovcamchip", }, .id = I2C_DRIVERID_OVCAMCHIP, - .class = I2C_CLASS_CAM_DIGITAL, .attach_adapter = ovcamchip_attach, .detach_client = ovcamchip_detach, .command = ovcamchip_command, diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 5b81ba469641..7b4bdadaa9cd 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -3655,7 +3655,7 @@ void pvr2_hdw_device_reset(struct pvr2_hdw *hdw) int ret; pvr2_trace(PVR2_TRACE_INIT,"Performing a device reset..."); ret = usb_lock_device_for_reset(hdw->usb_dev,NULL); - if (ret == 1) { + if (ret == 0) { ret = usb_reset_device(hdw->usb_dev); usb_unlock_device(hdw->usb_dev); } else { diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c index eb6be5802928..70a77625107d 100644 --- a/drivers/media/video/pxa_camera.c +++ b/drivers/media/video/pxa_camera.c @@ -39,6 +39,8 @@ #include <mach/pxa-regs.h> #include <mach/camera.h> +#include "pxa_camera.h" + #define PXA_CAM_VERSION_CODE KERNEL_VERSION(0, 0, 5) #define PXA_CAM_DRV_NAME "pxa27x-camera" @@ -1071,7 +1073,7 @@ static int pxa_camera_probe(struct platform_device *pdev) goto exit; } - pcdev->clk = clk_get(&pdev->dev, "CAMCLK"); + pcdev->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(pcdev->clk)) { err = PTR_ERR(pcdev->clk); goto exit_kfree; diff --git a/drivers/media/video/pxa_camera.h b/drivers/media/video/pxa_camera.h new file mode 100644 index 000000000000..89cbfc9a35c5 --- /dev/null +++ b/drivers/media/video/pxa_camera.h @@ -0,0 +1,95 @@ +/* Camera Interface */ +#define CICR0 __REG(0x50000000) +#define CICR1 __REG(0x50000004) +#define CICR2 __REG(0x50000008) +#define CICR3 __REG(0x5000000C) +#define CICR4 __REG(0x50000010) +#define CISR __REG(0x50000014) +#define CIFR __REG(0x50000018) +#define CITOR __REG(0x5000001C) +#define CIBR0 __REG(0x50000028) +#define CIBR1 __REG(0x50000030) +#define CIBR2 __REG(0x50000038) + +#define CICR0_DMAEN (1 << 31) /* DMA request enable */ +#define CICR0_PAR_EN (1 << 30) /* Parity enable */ +#define CICR0_SL_CAP_EN (1 << 29) /* Capture enable for slave mode */ +#define CICR0_ENB (1 << 28) /* Camera interface enable */ +#define CICR0_DIS (1 << 27) /* Camera interface disable */ +#define CICR0_SIM (0x7 << 24) /* Sensor interface mode mask */ +#define CICR0_TOM (1 << 9) /* Time-out mask */ +#define CICR0_RDAVM (1 << 8) /* Receive-data-available mask */ +#define CICR0_FEM (1 << 7) /* FIFO-empty mask */ +#define CICR0_EOLM (1 << 6) /* End-of-line mask */ +#define CICR0_PERRM (1 << 5) /* Parity-error mask */ +#define CICR0_QDM (1 << 4) /* Quick-disable mask */ +#define CICR0_CDM (1 << 3) /* Disable-done mask */ +#define CICR0_SOFM (1 << 2) /* Start-of-frame mask */ +#define CICR0_EOFM (1 << 1) /* End-of-frame mask */ +#define CICR0_FOM (1 << 0) /* FIFO-overrun mask */ + +#define CICR1_TBIT (1 << 31) /* Transparency bit */ +#define CICR1_RGBT_CONV (0x3 << 29) /* RGBT conversion mask */ +#define CICR1_PPL (0x7ff << 15) /* Pixels per line mask */ +#define CICR1_RGB_CONV (0x7 << 12) /* RGB conversion mask */ +#define CICR1_RGB_F (1 << 11) /* RGB format */ +#define CICR1_YCBCR_F (1 << 10) /* YCbCr format */ +#define CICR1_RGB_BPP (0x7 << 7) /* RGB bis per pixel mask */ +#define CICR1_RAW_BPP (0x3 << 5) /* Raw bis per pixel mask */ +#define CICR1_COLOR_SP (0x3 << 3) /* Color space mask */ +#define CICR1_DW (0x7 << 0) /* Data width mask */ + +#define CICR2_BLW (0xff << 24) /* Beginning-of-line pixel clock + wait count mask */ +#define CICR2_ELW (0xff << 16) /* End-of-line pixel clock + wait count mask */ +#define CICR2_HSW (0x3f << 10) /* Horizontal sync pulse width mask */ +#define CICR2_BFPW (0x3f << 3) /* Beginning-of-frame pixel clock + wait count mask */ +#define CICR2_FSW (0x7 << 0) /* Frame stabilization + wait count mask */ + +#define CICR3_BFW (0xff << 24) /* Beginning-of-frame line clock + wait count mask */ +#define CICR3_EFW (0xff << 16) /* End-of-frame line clock + wait count mask */ +#define CICR3_VSW (0x3f << 10) /* Vertical sync pulse width mask */ +#define CICR3_BFPW (0x3f << 3) /* Beginning-of-frame pixel clock + wait count mask */ +#define CICR3_LPF (0x7ff << 0) /* Lines per frame mask */ + +#define CICR4_MCLK_DLY (0x3 << 24) /* MCLK Data Capture Delay mask */ +#define CICR4_PCLK_EN (1 << 23) /* Pixel clock enable */ +#define CICR4_PCP (1 << 22) /* Pixel clock polarity */ +#define CICR4_HSP (1 << 21) /* Horizontal sync polarity */ +#define CICR4_VSP (1 << 20) /* Vertical sync polarity */ +#define CICR4_MCLK_EN (1 << 19) /* MCLK enable */ +#define CICR4_FR_RATE (0x7 << 8) /* Frame rate mask */ +#define CICR4_DIV (0xff << 0) /* Clock divisor mask */ + +#define CISR_FTO (1 << 15) /* FIFO time-out */ +#define CISR_RDAV_2 (1 << 14) /* Channel 2 receive data available */ +#define CISR_RDAV_1 (1 << 13) /* Channel 1 receive data available */ +#define CISR_RDAV_0 (1 << 12) /* Channel 0 receive data available */ +#define CISR_FEMPTY_2 (1 << 11) /* Channel 2 FIFO empty */ +#define CISR_FEMPTY_1 (1 << 10) /* Channel 1 FIFO empty */ +#define CISR_FEMPTY_0 (1 << 9) /* Channel 0 FIFO empty */ +#define CISR_EOL (1 << 8) /* End of line */ +#define CISR_PAR_ERR (1 << 7) /* Parity error */ +#define CISR_CQD (1 << 6) /* Camera interface quick disable */ +#define CISR_CDD (1 << 5) /* Camera interface disable done */ +#define CISR_SOF (1 << 4) /* Start of frame */ +#define CISR_EOF (1 << 3) /* End of frame */ +#define CISR_IFO_2 (1 << 2) /* FIFO overrun for Channel 2 */ +#define CISR_IFO_1 (1 << 1) /* FIFO overrun for Channel 1 */ +#define CISR_IFO_0 (1 << 0) /* FIFO overrun for Channel 0 */ + +#define CIFR_FLVL2 (0x7f << 23) /* FIFO 2 level mask */ +#define CIFR_FLVL1 (0x7f << 16) /* FIFO 1 level mask */ +#define CIFR_FLVL0 (0xff << 8) /* FIFO 0 level mask */ +#define CIFR_THL_0 (0x3 << 4) /* Threshold Level for Channel 0 FIFO */ +#define CIFR_RESET_F (1 << 3) /* Reset input FIFOs */ +#define CIFR_FEN2 (1 << 2) /* FIFO enable for channel 2 */ +#define CIFR_FEN1 (1 << 1) /* FIFO enable for channel 1 */ +#define CIFR_FEN0 (1 << 0) /* FIFO enable for channel 0 */ + diff --git a/drivers/media/video/saa7110.c b/drivers/media/video/saa7110.c index adf2ba79496a..37860698f782 100644 --- a/drivers/media/video/saa7110.c +++ b/drivers/media/video/saa7110.c @@ -47,7 +47,7 @@ module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Debug level (0-1)"); #define SAA7110_MAX_INPUT 9 /* 6 CVBS, 3 SVHS */ -#define SAA7110_MAX_OUTPUT 0 /* its a decoder only */ +#define SAA7110_MAX_OUTPUT 1 /* 1 YUV */ #define SAA7110_NR_REG 0x35 @@ -327,7 +327,7 @@ saa7110_command (struct i2c_client *client, case DECODER_SET_INPUT: v = *(int *) arg; - if (v < 0 || v > SAA7110_MAX_INPUT) { + if (v < 0 || v >= SAA7110_MAX_INPUT) { v4l_dbg(1, debug, client, "input=%d not available\n", v); return -EINVAL; } diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c index 249184452949..dfbe08a9ad9b 100644 --- a/drivers/media/video/saa7134/saa7134-core.c +++ b/drivers/media/video/saa7134/saa7134-core.c @@ -941,7 +941,8 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev, dev->name,(unsigned long long)pci_resource_start(pci_dev,0)); goto fail1; } - dev->lmmio = ioremap(pci_resource_start(pci_dev,0), 0x1000); + dev->lmmio = ioremap(pci_resource_start(pci_dev, 0), + pci_resource_len(pci_dev, 0)); dev->bmmio = (__u8 __iomem *)dev->lmmio; if (NULL == dev->lmmio) { err = -EIO; diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index 2407607f2eff..536b1a9b310c 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c @@ -31,6 +31,7 @@ #include <linux/platform_device.h> #include <linux/mutex.h> #include <linux/videodev2.h> +#include <linux/clk.h> #include <media/v4l2-common.h> #include <media/v4l2-dev.h> @@ -89,6 +90,7 @@ struct sh_mobile_ceu_dev { unsigned int irq; void __iomem *base; + struct clk *clk; unsigned long video_limit; /* lock used to protect videobuf */ @@ -309,6 +311,8 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd) if (ret) goto err; + clk_enable(pcdev->clk); + ceu_write(pcdev, CAPSR, 1 << 16); /* reset */ while (ceu_read(pcdev, CSTSR) & 1) msleep(1); @@ -342,6 +346,8 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd) } spin_unlock_irqrestore(&pcdev->lock, flags); + clk_disable(pcdev->clk); + icd->ops->release(icd); dev_info(&icd->dev, @@ -550,6 +556,7 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev) struct sh_mobile_ceu_dev *pcdev; struct resource *res; void __iomem *base; + char clk_name[8]; unsigned int irq; int err = 0; @@ -615,6 +622,14 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev) goto exit_release_mem; } + snprintf(clk_name, sizeof(clk_name), "ceu%d", pdev->id); + pcdev->clk = clk_get(&pdev->dev, clk_name); + if (IS_ERR(pcdev->clk)) { + dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name); + err = PTR_ERR(pcdev->clk); + goto exit_free_irq; + } + pcdev->ici.priv = pcdev; pcdev->ici.dev.parent = &pdev->dev; pcdev->ici.nr = pdev->id; @@ -623,10 +638,12 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev) err = soc_camera_host_register(&pcdev->ici); if (err) - goto exit_free_irq; + goto exit_free_clk; return 0; +exit_free_clk: + clk_put(pcdev->clk); exit_free_irq: free_irq(pcdev->irq, pcdev); exit_release_mem: @@ -645,6 +662,7 @@ static int sh_mobile_ceu_remove(struct platform_device *pdev) struct sh_mobile_ceu_dev *pcdev = platform_get_drvdata(pdev); soc_camera_host_unregister(&pcdev->ici); + clk_put(pcdev->clk); free_irq(pcdev->irq, pcdev); if (platform_get_resource(pdev, IORESOURCE_MEM, 1)) dma_release_declared_memory(&pdev->dev); diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c index b59e47272abf..3720f0e03a16 100644 --- a/drivers/media/video/tvaudio.c +++ b/drivers/media/video/tvaudio.c @@ -1,5 +1,5 @@ /* - * experimental driver for simple i2c audio chips. + * Driver for simple i2c audio chips. * * Copyright (c) 2000 Gerd Knorr * based on code by: @@ -7,6 +7,10 @@ * Steve VanDeBogart (vandebo@uclink.berkeley.edu) * Greg Alexander (galexand@acm.org) * + * Copyright(c) 2005-2008 Mauro Carvalho Chehab + * - Some cleanups, code fixes, etc + * - Convert it to V4L2 API + * * This code is placed under the terms of the GNU General Public License * * OPTIONS: @@ -30,6 +34,7 @@ #include <media/tvaudio.h> #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <media/v4l2-chip-ident.h> #include <media/v4l2-i2c-drv-legacy.h> @@ -58,7 +63,6 @@ typedef int (*checkit)(struct CHIPSTATE*); typedef int (*initialize)(struct CHIPSTATE*); typedef int (*getmode)(struct CHIPSTATE*); typedef void (*setmode)(struct CHIPSTATE*, int mode); -typedef void (*checkmode)(struct CHIPSTATE*); /* i2c command */ typedef struct AUDIOCMD { @@ -79,6 +83,7 @@ struct CHIPDESC { #define CHIP_HAS_VOLUME 1 #define CHIP_HAS_BASSTREBLE 2 #define CHIP_HAS_INPUTSEL 4 +#define CHIP_NEED_CHECKMODE 8 /* various i2c command sequences */ audiocmd init; @@ -96,23 +101,20 @@ struct CHIPDESC { getmode getmode; setmode setmode; - /* check / autoswitch audio after channel switches */ - checkmode checkmode; - /* input switch register + values for v4l inputs */ int inputreg; int inputmap[4]; int inputmute; int inputmask; }; -static struct CHIPDESC chiplist[]; /* current state of the chip */ struct CHIPSTATE { struct i2c_client *c; - /* index into CHIPDESC array */ - int type; + /* chip-specific description - should point to + an entry at CHIPDESC table */ + struct CHIPDESC *desc; /* shadow register set */ audiocmd shadow; @@ -152,7 +154,7 @@ static int chip_write(struct CHIPSTATE *chip, int subaddr, int val) { unsigned char buffer[2]; - if (-1 == subaddr) { + if (subaddr < 0) { v4l_dbg(1, debug, chip->c, "%s: chip_write: 0x%x\n", chip->c->name, val); chip->shadow.bytes[1] = val; @@ -163,6 +165,13 @@ static int chip_write(struct CHIPSTATE *chip, int subaddr, int val) return -1; } } else { + if (subaddr + 1 >= ARRAY_SIZE(chip->shadow.bytes)) { + v4l_info(chip->c, + "Tried to access a non-existent register: %d\n", + subaddr); + return -EINVAL; + } + v4l_dbg(1, debug, chip->c, "%s: chip_write: reg%d=0x%x\n", chip->c->name, subaddr, val); chip->shadow.bytes[subaddr+1] = val; @@ -177,12 +186,20 @@ static int chip_write(struct CHIPSTATE *chip, int subaddr, int val) return 0; } -static int chip_write_masked(struct CHIPSTATE *chip, int subaddr, int val, int mask) +static int chip_write_masked(struct CHIPSTATE *chip, + int subaddr, int val, int mask) { if (mask != 0) { - if (-1 == subaddr) { + if (subaddr < 0) { val = (chip->shadow.bytes[1] & ~mask) | (val & mask); } else { + if (subaddr + 1 >= ARRAY_SIZE(chip->shadow.bytes)) { + v4l_info(chip->c, + "Tried to access a non-existent register: %d\n", + subaddr); + return -EINVAL; + } + val = (chip->shadow.bytes[subaddr+1] & ~mask) | (val & mask); } } @@ -228,6 +245,15 @@ static int chip_cmd(struct CHIPSTATE *chip, char *name, audiocmd *cmd) if (0 == cmd->count) return 0; + if (cmd->count + cmd->bytes[0] - 1 >= ARRAY_SIZE(chip->shadow.bytes)) { + v4l_info(chip->c, + "Tried to access a non-existent register range: %d to %d\n", + cmd->bytes[0] + 1, cmd->bytes[0] + cmd->count - 1); + return -EINVAL; + } + + /* FIXME: it seems that the shadow bytes are wrong bellow !*/ + /* update our shadow register set; print bytes if (debug > 0) */ v4l_dbg(1, debug, chip->c, "%s: chip_cmd(%s): reg=%d, data:", chip->c->name, name,cmd->bytes[0]); @@ -263,7 +289,8 @@ static void chip_thread_wake(unsigned long data) static int chip_thread(void *data) { struct CHIPSTATE *chip = data; - struct CHIPDESC *desc = chiplist + chip->type; + struct CHIPDESC *desc = chip->desc; + int mode; v4l_dbg(1, debug, chip->c, "%s: thread started\n", chip->c->name); set_freezable(); @@ -282,7 +309,26 @@ static int chip_thread(void *data) continue; /* have a look what's going on */ - desc->checkmode(chip); + mode = desc->getmode(chip); + if (mode == chip->prevmode) + continue; + + /* chip detected a new audio mode - set it */ + v4l_dbg(1, debug, chip->c, "%s: thread checkmode\n", + chip->c->name); + + chip->prevmode = mode; + + if (mode & V4L2_TUNER_MODE_STEREO) + desc->setmode(chip, V4L2_TUNER_MODE_STEREO); + if (mode & V4L2_TUNER_MODE_LANG1_LANG2) + desc->setmode(chip, V4L2_TUNER_MODE_STEREO); + else if (mode & V4L2_TUNER_MODE_LANG1) + desc->setmode(chip, V4L2_TUNER_MODE_LANG1); + else if (mode & V4L2_TUNER_MODE_LANG2) + desc->setmode(chip, V4L2_TUNER_MODE_LANG2); + else + desc->setmode(chip, V4L2_TUNER_MODE_MONO); /* schedule next check */ mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000)); @@ -292,29 +338,6 @@ static int chip_thread(void *data) return 0; } -static void generic_checkmode(struct CHIPSTATE *chip) -{ - struct CHIPDESC *desc = chiplist + chip->type; - int mode = desc->getmode(chip); - - if (mode == chip->prevmode) - return; - - v4l_dbg(1, debug, chip->c, "%s: thread checkmode\n", chip->c->name); - chip->prevmode = mode; - - if (mode & V4L2_TUNER_MODE_STEREO) - desc->setmode(chip,V4L2_TUNER_MODE_STEREO); - if (mode & V4L2_TUNER_MODE_LANG1_LANG2) - desc->setmode(chip,V4L2_TUNER_MODE_STEREO); - else if (mode & V4L2_TUNER_MODE_LANG1) - desc->setmode(chip,V4L2_TUNER_MODE_LANG1); - else if (mode & V4L2_TUNER_MODE_LANG2) - desc->setmode(chip,V4L2_TUNER_MODE_LANG2); - else - desc->setmode(chip,V4L2_TUNER_MODE_MONO); -} - /* ---------------------------------------------------------------------- */ /* audio chip descriptions - defines+functions for tda9840 */ @@ -777,7 +800,7 @@ static struct tda9874a_MODES { char *name; audiocmd cmd; } tda9874a_modelist[9] = { - { "A2, B/G", + { "A2, B/G", /* default */ { 9, { TDA9874A_C1FRA, 0x72,0x95,0x55, 0x77,0xA0,0x00, 0x00,0x00 }} }, { "A2, M (Korea)", { 9, { TDA9874A_C1FRA, 0x5D,0xC0,0x00, 0x62,0x6A,0xAA, 0x20,0x22 }} }, @@ -791,7 +814,7 @@ static struct tda9874a_MODES { { 9, { TDA9874A_C1FRA, 0x7D,0x00,0x00, 0x88,0x8A,0xAA, 0x08,0x33 }} }, { "NICAM, B/G", { 9, { TDA9874A_C1FRA, 0x72,0x95,0x55, 0x79,0xEA,0xAA, 0x08,0x33 }} }, - { "NICAM, D/K", /* default */ + { "NICAM, D/K", { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x79,0xEA,0xAA, 0x08,0x33 }} }, { "NICAM, L", { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x79,0xEA,0xAA, 0x09,0x33 }} } @@ -981,7 +1004,7 @@ static int tda9874a_initialize(struct CHIPSTATE *chip) { if (tda9874a_SIF > 2) tda9874a_SIF = 1; - if (tda9874a_STD > 8) + if (tda9874a_STD >= ARRAY_SIZE(tda9874a_modelist)) tda9874a_STD = 0; if(tda9874a_AMSEL > 1) tda9874a_AMSEL = 0; @@ -1089,7 +1112,7 @@ static int tda8425_shift12(int val) { return (val >> 12) | 0xf0; } static int tda8425_initialize(struct CHIPSTATE *chip) { - struct CHIPDESC *desc = chiplist + chip->type; + struct CHIPDESC *desc = chip->desc; int inputmap[4] = { /* tuner */ TDA8425_S1_CH2, /* radio */ TDA8425_S1_CH1, /* extern */ TDA8425_S1_CH1, /* intern */ TDA8425_S1_OFF}; @@ -1259,27 +1282,28 @@ static struct CHIPDESC chiplist[] = { .addr_lo = I2C_ADDR_TDA9840 >> 1, .addr_hi = I2C_ADDR_TDA9840 >> 1, .registers = 5, + .flags = CHIP_NEED_CHECKMODE, + /* callbacks */ .checkit = tda9840_checkit, .getmode = tda9840_getmode, .setmode = tda9840_setmode, - .checkmode = generic_checkmode, .init = { 2, { TDA9840_TEST, TDA9840_TEST_INT1SN /* ,TDA9840_SW, TDA9840_MONO */} } }, { .name = "tda9873h", - .checkit = tda9873_checkit, .insmodopt = &tda9873, .addr_lo = I2C_ADDR_TDA985x_L >> 1, .addr_hi = I2C_ADDR_TDA985x_H >> 1, .registers = 3, - .flags = CHIP_HAS_INPUTSEL, + .flags = CHIP_HAS_INPUTSEL | CHIP_NEED_CHECKMODE, + /* callbacks */ + .checkit = tda9873_checkit, .getmode = tda9873_getmode, .setmode = tda9873_setmode, - .checkmode = generic_checkmode, .init = { 4, { TDA9873_SW, 0xa4, 0x06, 0x03 } }, .inputreg = TDA9873_SW, @@ -1290,15 +1314,16 @@ static struct CHIPDESC chiplist[] = { }, { .name = "tda9874h/a", - .checkit = tda9874a_checkit, - .initialize = tda9874a_initialize, .insmodopt = &tda9874a, .addr_lo = I2C_ADDR_TDA9874 >> 1, .addr_hi = I2C_ADDR_TDA9874 >> 1, + .flags = CHIP_NEED_CHECKMODE, + /* callbacks */ + .initialize = tda9874a_initialize, + .checkit = tda9874a_checkit, .getmode = tda9874a_getmode, .setmode = tda9874a_setmode, - .checkmode = generic_checkmode, }, { .name = "tda9850", @@ -1324,10 +1349,11 @@ static struct CHIPDESC chiplist[] = { .rightreg = TDA9855_VR, .bassreg = TDA9855_BA, .treblereg = TDA9855_TR, + + /* callbacks */ .volfunc = tda9855_volume, .bassfunc = tda9855_bass, .treblefunc = tda9855_treble, - .getmode = tda985x_getmode, .setmode = tda985x_setmode, @@ -1348,6 +1374,8 @@ static struct CHIPDESC chiplist[] = { .rightreg = TEA6300_VL, .bassreg = TEA6300_BA, .treblereg = TEA6300_TR, + + /* callbacks */ .volfunc = tea6300_shift10, .bassfunc = tea6300_shift12, .treblefunc = tea6300_shift12, @@ -1358,7 +1386,6 @@ static struct CHIPDESC chiplist[] = { }, { .name = "tea6320", - .initialize = tea6320_initialize, .insmodopt = &tea6320, .addr_lo = I2C_ADDR_TEA6300 >> 1, .addr_hi = I2C_ADDR_TEA6300 >> 1, @@ -1369,6 +1396,9 @@ static struct CHIPDESC chiplist[] = { .rightreg = TEA6320_V, .bassreg = TEA6320_BA, .treblereg = TEA6320_TR, + + /* callbacks */ + .initialize = tea6320_initialize, .volfunc = tea6320_volume, .bassfunc = tea6320_shift11, .treblefunc = tea6320_shift11, @@ -1401,16 +1431,18 @@ static struct CHIPDESC chiplist[] = { .rightreg = TDA8425_VR, .bassreg = TDA8425_BA, .treblereg = TDA8425_TR, + + /* callbacks */ + .initialize = tda8425_initialize, .volfunc = tda8425_shift10, .bassfunc = tda8425_shift12, .treblefunc = tda8425_shift12, + .setmode = tda8425_setmode, .inputreg = TDA8425_S1, .inputmap = { TDA8425_S1_CH1, TDA8425_S1_CH1, TDA8425_S1_CH1 }, .inputmute = TDA8425_S1_OFF, - .setmode = tda8425_setmode, - .initialize = tda8425_initialize, }, { .name = "pic16c54 (PV951)", @@ -1434,10 +1466,11 @@ static struct CHIPDESC chiplist[] = { .addr_lo = I2C_ADDR_TDA9840 >> 1, .addr_hi = I2C_ADDR_TDA9840 >> 1, .registers = 2, + .flags = CHIP_NEED_CHECKMODE, + /* callbacks */ .getmode = ta8874z_getmode, .setmode = ta8874z_setmode, - .checkmode = generic_checkmode, .init = {2, { TA8874Z_MONO_SET, TA8874Z_SEPARATION_DEFAULT}}, }, @@ -1481,6 +1514,7 @@ static int chip_probe(struct i2c_client *client, const struct i2c_device_id *id) } if (desc->name == NULL) { v4l_dbg(1, debug, client, "no matching chip description found\n"); + kfree(chip); return -EIO; } v4l_info(client, "%s found @ 0x%x (%s)\n", desc->name, client->addr<<1, client->adapter->name); @@ -1494,7 +1528,7 @@ static int chip_probe(struct i2c_client *client, const struct i2c_device_id *id) /* fill required data structures */ if (!id) strlcpy(client->name, desc->name, I2C_NAME_SIZE); - chip->type = desc-chiplist; + chip->desc = desc; chip->shadow.count = desc->registers+1; chip->prevmode = -1; chip->audmode = V4L2_TUNER_MODE_LANG1; @@ -1506,20 +1540,49 @@ static int chip_probe(struct i2c_client *client, const struct i2c_device_id *id) chip_cmd(chip,"init",&desc->init); if (desc->flags & CHIP_HAS_VOLUME) { - chip->left = desc->leftinit ? desc->leftinit : 65535; - chip->right = desc->rightinit ? desc->rightinit : 65535; - chip_write(chip,desc->leftreg,desc->volfunc(chip->left)); - chip_write(chip,desc->rightreg,desc->volfunc(chip->right)); + if (!desc->volfunc) { + /* This shouldn't be happen. Warn user, but keep working + without volume controls + */ + v4l_info(chip->c, "volume callback undefined!\n"); + desc->flags &= ~CHIP_HAS_VOLUME; + } else { + chip->left = desc->leftinit ? desc->leftinit : 65535; + chip->right = desc->rightinit ? desc->rightinit : 65535; + chip_write(chip, desc->leftreg, + desc->volfunc(chip->left)); + chip_write(chip, desc->rightreg, + desc->volfunc(chip->right)); + } } if (desc->flags & CHIP_HAS_BASSTREBLE) { - chip->treble = desc->trebleinit ? desc->trebleinit : 32768; - chip->bass = desc->bassinit ? desc->bassinit : 32768; - chip_write(chip,desc->bassreg,desc->bassfunc(chip->bass)); - chip_write(chip,desc->treblereg,desc->treblefunc(chip->treble)); + if (!desc->bassfunc || !desc->treblefunc) { + /* This shouldn't be happen. Warn user, but keep working + without bass/treble controls + */ + v4l_info(chip->c, "bass/treble callbacks undefined!\n"); + desc->flags &= ~CHIP_HAS_BASSTREBLE; + } else { + chip->treble = desc->trebleinit ? + desc->trebleinit : 32768; + chip->bass = desc->bassinit ? + desc->bassinit : 32768; + chip_write(chip, desc->bassreg, + desc->bassfunc(chip->bass)); + chip_write(chip, desc->treblereg, + desc->treblefunc(chip->treble)); + } } chip->thread = NULL; - if (desc->checkmode) { + if (desc->flags & CHIP_NEED_CHECKMODE) { + if (!desc->getmode || !desc->setmode) { + /* This shouldn't be happen. Warn user, but keep working + without kthread + */ + v4l_info(chip->c, "set/get mode callbacks undefined!\n"); + return 0; + } /* start async thread */ init_timer(&chip->wt); chip->wt.function = chip_thread_wake; @@ -1552,7 +1615,7 @@ static int chip_remove(struct i2c_client *client) static int tvaudio_get_ctrl(struct CHIPSTATE *chip, struct v4l2_control *ctrl) { - struct CHIPDESC *desc = chiplist + chip->type; + struct CHIPDESC *desc = chip->desc; switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: @@ -1576,13 +1639,13 @@ static int tvaudio_get_ctrl(struct CHIPSTATE *chip, return 0; } case V4L2_CID_AUDIO_BASS: - if (desc->flags & CHIP_HAS_BASSTREBLE) + if (!(desc->flags & CHIP_HAS_BASSTREBLE)) break; ctrl->value = chip->bass; return 0; case V4L2_CID_AUDIO_TREBLE: - if (desc->flags & CHIP_HAS_BASSTREBLE) - return -EINVAL; + if (!(desc->flags & CHIP_HAS_BASSTREBLE)) + break; ctrl->value = chip->treble; return 0; } @@ -1592,7 +1655,7 @@ static int tvaudio_get_ctrl(struct CHIPSTATE *chip, static int tvaudio_set_ctrl(struct CHIPSTATE *chip, struct v4l2_control *ctrl) { - struct CHIPDESC *desc = chiplist + chip->type; + struct CHIPDESC *desc = chip->desc; switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: @@ -1642,16 +1705,15 @@ static int tvaudio_set_ctrl(struct CHIPSTATE *chip, return 0; } case V4L2_CID_AUDIO_BASS: - if (desc->flags & CHIP_HAS_BASSTREBLE) + if (!(desc->flags & CHIP_HAS_BASSTREBLE)) break; chip->bass = ctrl->value; chip_write(chip,desc->bassreg,desc->bassfunc(chip->bass)); return 0; case V4L2_CID_AUDIO_TREBLE: - if (desc->flags & CHIP_HAS_BASSTREBLE) - return -EINVAL; - + if (!(desc->flags & CHIP_HAS_BASSTREBLE)) + break; chip->treble = ctrl->value; chip_write(chip,desc->treblereg,desc->treblefunc(chip->treble)); @@ -1668,9 +1730,12 @@ static int chip_command(struct i2c_client *client, unsigned int cmd, void *arg) { struct CHIPSTATE *chip = i2c_get_clientdata(client); - struct CHIPDESC *desc = chiplist + chip->type; + struct CHIPDESC *desc = chip->desc; - v4l_dbg(1, debug, chip->c, "%s: chip_command 0x%x\n", chip->c->name, cmd); + if (debug > 0) { + v4l_i2c_print_ioctl(chip->c, cmd); + printk("\n"); + } switch (cmd) { case AUDC_SET_RADIO: @@ -1695,7 +1760,7 @@ static int chip_command(struct i2c_client *client, break; case V4L2_CID_AUDIO_BASS: case V4L2_CID_AUDIO_TREBLE: - if (desc->flags & CHIP_HAS_BASSTREBLE) + if (!(desc->flags & CHIP_HAS_BASSTREBLE)) return -EINVAL; break; default: @@ -1792,12 +1857,20 @@ static int chip_command(struct i2c_client *client, break; case VIDIOC_S_FREQUENCY: chip->mode = 0; /* automatic */ - if (desc->checkmode && desc->setmode) { + + /* For chips that provide getmode and setmode, and doesn't + automatically follows the stereo carrier, a kthread is + created to set the audio standard. In this case, when then + the video channel is changed, tvaudio starts on MONO mode. + After waiting for 2 seconds, the kernel thread is called, + to follow whatever audio standard is pointed by the + audio carrier. + */ + if (chip->thread) { desc->setmode(chip,V4L2_TUNER_MODE_MONO); if (chip->prevmode != V4L2_TUNER_MODE_MONO) chip->prevmode = -1; /* reset previous mode */ mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000)); - /* the thread will call checkmode() later */ } break; @@ -1836,9 +1909,3 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = { .legacy_probe = chip_legacy_probe, .id_table = chip_id, }; - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/video/usbvideo/ibmcam.c b/drivers/media/video/usbvideo/ibmcam.c index 28421d386f1e..c710bcd1df48 100644 --- a/drivers/media/video/usbvideo/ibmcam.c +++ b/drivers/media/video/usbvideo/ibmcam.c @@ -3695,7 +3695,7 @@ static int ibmcam_probe(struct usb_interface *intf, const struct usb_device_id * unsigned char video_ep = 0; if (debug >= 1) - dev_info(&uvd->dev->dev, "ibmcam_probe(%p,%u.)\n", intf, ifnum); + dev_info(&dev->dev, "ibmcam_probe(%p,%u.)\n", intf, ifnum); /* We don't handle multi-config cameras */ if (dev->descriptor.bNumConfigurations != 1) @@ -3746,7 +3746,7 @@ static int ibmcam_probe(struct usb_interface *intf, const struct usb_device_id * brand = "IBM PC Camera"; /* a.k.a. Xirlink C-It */ break; } - dev_info(&uvd->dev->dev, + dev_info(&dev->dev, "%s USB camera found (model %d, rev. 0x%04x)\n", brand, model, le16_to_cpu(dev->descriptor.bcdDevice)); } while (0); @@ -3754,7 +3754,7 @@ static int ibmcam_probe(struct usb_interface *intf, const struct usb_device_id * /* Validate found interface: must have one ISO endpoint */ nas = intf->num_altsetting; if (debug > 0) - dev_info(&uvd->dev->dev, "Number of alternate settings=%d.\n", + dev_info(&dev->dev, "Number of alternate settings=%d.\n", nas); if (nas < 2) { err("Too few alternate settings for this camera!"); @@ -3799,7 +3799,7 @@ static int ibmcam_probe(struct usb_interface *intf, const struct usb_device_id * actInterface = i; maxPS = le16_to_cpu(endpoint->wMaxPacketSize); if (debug > 0) - dev_info(&uvd->dev->dev, + dev_info(&dev->dev, "Active setting=%d. " "maxPS=%d.\n", i, maxPS); } else @@ -3840,7 +3840,7 @@ static int ibmcam_probe(struct usb_interface *intf, const struct usb_device_id * RESTRICT_TO_RANGE(framerate, 0, 5); break; default: - dev_info(&uvd->dev->dev, "IBM camera: using 320x240\n"); + dev_info(&dev->dev, "IBM camera: using 320x240\n"); size = SIZE_320x240; /* No break here */ case SIZE_320x240: @@ -3869,7 +3869,7 @@ static int ibmcam_probe(struct usb_interface *intf, const struct usb_device_id * canvasY = 120; break; default: - dev_info(&uvd->dev->dev, "IBM NetCamera: using 176x144\n"); + dev_info(&dev->dev, "IBM NetCamera: using 176x144\n"); size = SIZE_176x144; /* No break here */ case SIZE_176x144: diff --git a/drivers/media/video/w9968cf.c b/drivers/media/video/w9968cf.c index 4dfb43bd1846..e4a6272bdbd5 100644 --- a/drivers/media/video/w9968cf.c +++ b/drivers/media/video/w9968cf.c @@ -1553,7 +1553,6 @@ static int w9968cf_i2c_init(struct w9968cf_device* cam) static struct i2c_adapter adap = { .id = I2C_HW_SMBUS_W9968CF, - .class = I2C_CLASS_CAM_DIGITAL, .owner = THIS_MODULE, .client_register = w9968cf_i2c_attach_inform, .client_unregister = w9968cf_i2c_detach_inform, diff --git a/drivers/memstick/core/memstick.c b/drivers/memstick/core/memstick.c index cea46906408e..a5b448ea4eab 100644 --- a/drivers/memstick/core/memstick.c +++ b/drivers/memstick/core/memstick.c @@ -385,8 +385,7 @@ static struct memstick_dev *memstick_alloc_card(struct memstick_host *host) if (card) { card->host = host; - snprintf(card->dev.bus_id, sizeof(card->dev.bus_id), - "%s", host->dev.bus_id); + dev_set_name(&card->dev, "%s", dev_name(&host->dev)); card->dev.parent = &host->dev; card->dev.bus = &memstick_bus_type; card->dev.release = memstick_free_card; @@ -519,7 +518,7 @@ int memstick_add_host(struct memstick_host *host) if (rc) return rc; - snprintf(host->dev.bus_id, BUS_ID_SIZE, "memstick%u", host->id); + dev_set_name(&host->dev, "memstick%u", host->id); rc = device_add(&host->dev); if (rc) { diff --git a/drivers/memstick/core/mspro_block.c b/drivers/memstick/core/mspro_block.c index 7911151e56a3..1f1e3982b1aa 100644 --- a/drivers/memstick/core/mspro_block.c +++ b/drivers/memstick/core/mspro_block.c @@ -887,14 +887,14 @@ try_again: if (rc) { printk(KERN_WARNING "%s: could not switch to 4-bit mode, error %d\n", - card->dev.bus_id, rc); + dev_name(&card->dev), rc); return 0; } msb->system = MEMSTICK_SYS_PAR4; host->set_param(host, MEMSTICK_INTERFACE, MEMSTICK_PAR4); printk(KERN_INFO "%s: switching to 4-bit parallel mode\n", - card->dev.bus_id); + dev_name(&card->dev)); if (msb->caps & MEMSTICK_CAP_PAR8) { rc = mspro_block_set_interface(card, MEMSTICK_SYS_PAR8); @@ -905,11 +905,11 @@ try_again: MEMSTICK_PAR8); printk(KERN_INFO "%s: switching to 8-bit parallel mode\n", - card->dev.bus_id); + dev_name(&card->dev)); } else printk(KERN_WARNING "%s: could not switch to 8-bit mode, error %d\n", - card->dev.bus_id, rc); + dev_name(&card->dev), rc); } card->next_request = h_mspro_block_req_init; @@ -922,7 +922,7 @@ try_again: if (rc) { printk(KERN_WARNING "%s: interface error, trying to fall back to serial\n", - card->dev.bus_id); + dev_name(&card->dev)); msb->system = MEMSTICK_SYS_SERIAL; host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_OFF); msleep(10); @@ -992,14 +992,14 @@ static int mspro_block_read_attributes(struct memstick_dev *card) if (be16_to_cpu(attr->signature) != MSPRO_BLOCK_SIGNATURE) { printk(KERN_ERR "%s: unrecognized device signature %x\n", - card->dev.bus_id, be16_to_cpu(attr->signature)); + dev_name(&card->dev), be16_to_cpu(attr->signature)); rc = -ENODEV; goto out_free_attr; } if (attr->count > MSPRO_BLOCK_MAX_ATTRIBUTES) { printk(KERN_WARNING "%s: way too many attribute entries\n", - card->dev.bus_id); + dev_name(&card->dev)); attr_count = MSPRO_BLOCK_MAX_ATTRIBUTES; } else attr_count = attr->count; diff --git a/drivers/memstick/host/tifm_ms.c b/drivers/memstick/host/tifm_ms.c index d32d6ad8f3fc..03f71a431c82 100644 --- a/drivers/memstick/host/tifm_ms.c +++ b/drivers/memstick/host/tifm_ms.c @@ -546,7 +546,7 @@ static void tifm_ms_abort(unsigned long data) printk(KERN_ERR "%s : card failed to respond for a long period of time " "(%x, %x)\n", - host->dev->dev.bus_id, host->req ? host->req->tpc : 0, + dev_name(&host->dev->dev), host->req ? host->req->tpc : 0, host->cmd_flags); tifm_eject(host->dev); @@ -561,7 +561,7 @@ static int tifm_ms_probe(struct tifm_dev *sock) if (!(TIFM_SOCK_STATE_OCCUPIED & readl(sock->addr + SOCK_PRESENT_STATE))) { printk(KERN_WARNING "%s : card gone, unexpectedly\n", - sock->dev.bus_id); + dev_name(&sock->dev)); return rc; } diff --git a/drivers/message/fusion/mptlan.c b/drivers/message/fusion/mptlan.c index a1abf95cf751..603ffd008c73 100644 --- a/drivers/message/fusion/mptlan.c +++ b/drivers/message/fusion/mptlan.c @@ -77,12 +77,6 @@ MODULE_VERSION(my_VERSION); * Fusion MPT LAN private structures */ -struct NAA_Hosed { - u16 NAA; - u8 ieee[FC_ALEN]; - struct NAA_Hosed *next; -}; - struct BufferControl { struct sk_buff *skb; dma_addr_t dma; @@ -159,11 +153,6 @@ static u8 LanCtx = MPT_MAX_PROTOCOL_DRIVERS; static u32 max_buckets_out = 127; static u32 tx_max_out_p = 127 - 16; -#ifdef QLOGIC_NAA_WORKAROUND -static struct NAA_Hosed *mpt_bad_naa = NULL; -DEFINE_RWLOCK(bad_naa_lock); -#endif - /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /** * lan_reply - Handle all data sent from the hardware. @@ -780,30 +769,6 @@ mpt_lan_sdu_send (struct sk_buff *skb, struct net_device *dev) // ctx, skb, skb->data)); mac = skb_mac_header(skb); -#ifdef QLOGIC_NAA_WORKAROUND -{ - struct NAA_Hosed *nh; - - /* Munge the NAA for Tx packets to QLogic boards, which don't follow - RFC 2625. The longer I look at this, the more my opinion of Qlogic - drops. */ - read_lock_irq(&bad_naa_lock); - for (nh = mpt_bad_naa; nh != NULL; nh=nh->next) { - if ((nh->ieee[0] == mac[0]) && - (nh->ieee[1] == mac[1]) && - (nh->ieee[2] == mac[2]) && - (nh->ieee[3] == mac[3]) && - (nh->ieee[4] == mac[4]) && - (nh->ieee[5] == mac[5])) { - cur_naa = nh->NAA; - dlprintk ((KERN_INFO "mptlan/sdu_send: using NAA value " - "= %04x.\n", cur_naa)); - break; - } - } - read_unlock_irq(&bad_naa_lock); -} -#endif pTrans->TransactionDetails[0] = cpu_to_le32((cur_naa << 16) | (mac[0] << 8) | @@ -1572,79 +1537,6 @@ mpt_lan_type_trans(struct sk_buff *skb, struct net_device *dev) fcllc = (struct fcllc *)skb->data; -#ifdef QLOGIC_NAA_WORKAROUND -{ - u16 source_naa = fch->stype, found = 0; - - /* Workaround for QLogic not following RFC 2625 in regards to the NAA - value. */ - - if ((source_naa & 0xF000) == 0) - source_naa = swab16(source_naa); - - if (fcllc->ethertype == htons(ETH_P_ARP)) - dlprintk ((KERN_INFO "mptlan/type_trans: got arp req/rep w/ naa of " - "%04x.\n", source_naa)); - - if ((fcllc->ethertype == htons(ETH_P_ARP)) && - ((source_naa >> 12) != MPT_LAN_NAA_RFC2625)){ - struct NAA_Hosed *nh, *prevnh; - int i; - - dlprintk ((KERN_INFO "mptlan/type_trans: ARP Req/Rep from " - "system with non-RFC 2625 NAA value (%04x).\n", - source_naa)); - - write_lock_irq(&bad_naa_lock); - for (prevnh = nh = mpt_bad_naa; nh != NULL; - prevnh=nh, nh=nh->next) { - if ((nh->ieee[0] == fch->saddr[0]) && - (nh->ieee[1] == fch->saddr[1]) && - (nh->ieee[2] == fch->saddr[2]) && - (nh->ieee[3] == fch->saddr[3]) && - (nh->ieee[4] == fch->saddr[4]) && - (nh->ieee[5] == fch->saddr[5])) { - found = 1; - dlprintk ((KERN_INFO "mptlan/type_trans: ARP Re" - "q/Rep w/ bad NAA from system already" - " in DB.\n")); - break; - } - } - - if ((!found) && (nh == NULL)) { - - nh = kmalloc(sizeof(struct NAA_Hosed), GFP_KERNEL); - dlprintk ((KERN_INFO "mptlan/type_trans: ARP Req/Rep w/" - " bad NAA from system not yet in DB.\n")); - - if (nh != NULL) { - nh->next = NULL; - if (!mpt_bad_naa) - mpt_bad_naa = nh; - if (prevnh) - prevnh->next = nh; - - nh->NAA = source_naa; /* Set the S_NAA value. */ - for (i = 0; i < FC_ALEN; i++) - nh->ieee[i] = fch->saddr[i]; - dlprintk ((KERN_INFO "Got ARP from %02x:%02x:%02x:%02x:" - "%02x:%02x with non-compliant S_NAA value.\n", - fch->saddr[0], fch->saddr[1], fch->saddr[2], - fch->saddr[3], fch->saddr[4],fch->saddr[5])); - } else { - printk (KERN_ERR "mptlan/type_trans: Unable to" - " kmalloc a NAA_Hosed struct.\n"); - } - } else if (!found) { - printk (KERN_ERR "mptlan/type_trans: found not" - " set, but nh isn't null. Evil " - "funkiness abounds.\n"); - } - write_unlock_irq(&bad_naa_lock); - } -} -#endif /* Strip the SNAP header from ARP packets since we don't * pass them through to the 802.2/SNAP layers. diff --git a/drivers/message/i2o/device.c b/drivers/message/i2o/device.c index 54c2e9ae23e5..a7dd03e8d332 100644 --- a/drivers/message/i2o/device.c +++ b/drivers/message/i2o/device.c @@ -134,7 +134,7 @@ static void i2o_device_release(struct device *dev) { struct i2o_device *i2o_dev = to_i2o_device(dev); - pr_debug("i2o: device %s released\n", dev->bus_id); + pr_debug("i2o: device %s released\n", dev_name(dev)); kfree(i2o_dev); } @@ -229,8 +229,8 @@ static int i2o_device_add(struct i2o_controller *c, i2o_lct_entry *entry) i2o_dev->lct_data = *entry; - snprintf(i2o_dev->device.bus_id, BUS_ID_SIZE, "%d:%03x", c->unit, - i2o_dev->lct_data.tid); + dev_set_name(&i2o_dev->device, "%d:%03x", c->unit, + i2o_dev->lct_data.tid); i2o_dev->iop = c; i2o_dev->device.parent = &c->device; @@ -281,7 +281,7 @@ static int i2o_device_add(struct i2o_controller *c, i2o_lct_entry *entry) i2o_driver_notify_device_add_all(i2o_dev); - pr_debug("i2o: device %s added\n", i2o_dev->device.bus_id); + pr_debug("i2o: device %s added\n", dev_name(&i2o_dev->device)); return 0; diff --git a/drivers/message/i2o/i2o_proc.c b/drivers/message/i2o/i2o_proc.c index 54a3016ff45d..9a36b5a7de57 100644 --- a/drivers/message/i2o/i2o_proc.c +++ b/drivers/message/i2o/i2o_proc.c @@ -1300,7 +1300,7 @@ static int i2o_seq_show_dev_name(struct seq_file *seq, void *v) { struct i2o_device *d = (struct i2o_device *)seq->private; - seq_printf(seq, "%s\n", d->device.bus_id); + seq_printf(seq, "%s\n", dev_name(&d->device)); return 0; } diff --git a/drivers/message/i2o/iop.c b/drivers/message/i2o/iop.c index be2b5926d26c..cc0eef1fd27f 100644 --- a/drivers/message/i2o/iop.c +++ b/drivers/message/i2o/iop.c @@ -1073,7 +1073,7 @@ struct i2o_controller *i2o_iop_alloc(void) c->device.release = &i2o_iop_release; - snprintf(c->device.bus_id, BUS_ID_SIZE, "iop%d", c->unit); + dev_set_name(&c->device, "iop%d", c->unit); #if BITS_PER_LONG == 64 spin_lock_init(&c->context_list_lock); diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c index e4c0db4dc7b1..9e485459f63b 100644 --- a/drivers/mfd/asic3.c +++ b/drivers/mfd/asic3.c @@ -474,9 +474,9 @@ static __init int asic3_gpio_probe(struct platform_device *pdev, u16 dir_reg[ASIC3_NUM_GPIO_BANKS]; int i; - memzero(alt_reg, ASIC3_NUM_GPIO_BANKS * sizeof(u16)); - memzero(out_reg, ASIC3_NUM_GPIO_BANKS * sizeof(u16)); - memzero(dir_reg, ASIC3_NUM_GPIO_BANKS * sizeof(u16)); + memset(alt_reg, 0, ASIC3_NUM_GPIO_BANKS * sizeof(u16)); + memset(out_reg, 0, ASIC3_NUM_GPIO_BANKS * sizeof(u16)); + memset(dir_reg, 0, ASIC3_NUM_GPIO_BANKS * sizeof(u16)); /* Enable all GPIOs */ asic3_write_register(asic, ASIC3_GPIO_OFFSET(A, MASK), 0xffff); diff --git a/drivers/mfd/da903x.c b/drivers/mfd/da903x.c index b57326ae464d..0b5bd85dfcec 100644 --- a/drivers/mfd/da903x.c +++ b/drivers/mfd/da903x.c @@ -267,7 +267,7 @@ static int da9030_mask_events(struct da903x_chip *chip, unsigned int events) { uint8_t v[3]; - chip->events_mask &= ~events; + chip->events_mask |= events; v[0] = (chip->events_mask & 0xff); v[1] = (chip->events_mask >> 8) & 0xff; diff --git a/drivers/mfd/mcp-core.c b/drivers/mfd/mcp-core.c index b4ed57e02729..6063dc2b52e8 100644 --- a/drivers/mfd/mcp-core.c +++ b/drivers/mfd/mcp-core.c @@ -18,7 +18,7 @@ #include <linux/slab.h> #include <linux/string.h> -#include <asm/dma.h> +#include <mach/dma.h> #include <asm/system.h> #include "mcp.h" diff --git a/drivers/mfd/mcp-sa11x0.c b/drivers/mfd/mcp-sa11x0.c index 28380b20bc70..62b32dabf629 100644 --- a/drivers/mfd/mcp-sa11x0.c +++ b/drivers/mfd/mcp-sa11x0.c @@ -20,7 +20,7 @@ #include <linux/slab.h> #include <linux/platform_device.h> -#include <asm/dma.h> +#include <mach/dma.h> #include <mach/hardware.h> #include <asm/mach-types.h> #include <asm/system.h> diff --git a/drivers/mfd/ucb1x00-assabet.c b/drivers/mfd/ucb1x00-assabet.c index 61aeaf79640d..86fed4870f93 100644 --- a/drivers/mfd/ucb1x00-assabet.c +++ b/drivers/mfd/ucb1x00-assabet.c @@ -15,7 +15,7 @@ #include <linux/proc_fs.h> #include <linux/device.h> -#include <asm/dma.h> +#include <mach/dma.h> #include "ucb1x00.h" diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c index a316f1b75933..6860c924f364 100644 --- a/drivers/mfd/ucb1x00-core.c +++ b/drivers/mfd/ucb1x00-core.c @@ -25,7 +25,7 @@ #include <linux/device.h> #include <linux/mutex.h> -#include <asm/dma.h> +#include <mach/dma.h> #include <mach/hardware.h> #include "ucb1x00.h" diff --git a/drivers/mfd/ucb1x00-ts.c b/drivers/mfd/ucb1x00-ts.c index 44762ca86a8d..61b7d3eb9a2f 100644 --- a/drivers/mfd/ucb1x00-ts.c +++ b/drivers/mfd/ucb1x00-ts.c @@ -31,7 +31,7 @@ #include <linux/slab.h> #include <linux/kthread.h> -#include <asm/dma.h> +#include <mach/dma.h> #include <mach/collie.h> #include <asm/mach-types.h> diff --git a/drivers/mfd/wm8350-i2c.c b/drivers/mfd/wm8350-i2c.c index 8dfe21bb3bd1..3e0ce0e50ea2 100644 --- a/drivers/mfd/wm8350-i2c.c +++ b/drivers/mfd/wm8350-i2c.c @@ -30,7 +30,12 @@ static int wm8350_i2c_read_device(struct wm8350 *wm8350, char reg, ret = i2c_master_send(wm8350->i2c_client, ®, 1); if (ret < 0) return ret; - return i2c_master_recv(wm8350->i2c_client, dest, bytes); + ret = i2c_master_recv(wm8350->i2c_client, dest, bytes); + if (ret < 0) + return ret; + if (ret != bytes) + return -EIO; + return 0; } static int wm8350_i2c_write_device(struct wm8350 *wm8350, char reg, @@ -38,13 +43,19 @@ static int wm8350_i2c_write_device(struct wm8350 *wm8350, char reg, { /* we add 1 byte for device register */ u8 msg[(WM8350_MAX_REGISTER << 1) + 1]; + int ret; if (bytes > ((WM8350_MAX_REGISTER << 1) + 1)) return -EINVAL; msg[0] = reg; memcpy(&msg[1], src, bytes); - return i2c_master_send(wm8350->i2c_client, msg, bytes + 1); + ret = i2c_master_send(wm8350->i2c_client, msg, bytes + 1); + if (ret < 0) + return ret; + if (ret != bytes + 1) + return -EIO; + return 0; } static int wm8350_i2c_probe(struct i2c_client *i2c, diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 9494400e8fd0..fee7304102af 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -227,10 +227,20 @@ config HP_WMI To compile this driver as a module, choose M here: the module will be called hp-wmi. +config ICS932S401 + tristate "Integrated Circuits ICS932S401" + depends on I2C && EXPERIMENTAL + help + If you say yes here you get support for the Integrated Circuits + ICS932S401 clock control chips. + + This driver can also be built as a module. If so, the module + will be called ics932s401. + config MSI_LAPTOP tristate "MSI Laptop Extras" depends on X86 - depends on ACPI_EC + depends on ACPI depends on BACKLIGHT_CLASS_DEVICE ---help--- This is a driver for laptops built by MSI (MICRO-STAR @@ -260,7 +270,7 @@ config PANASONIC_LAPTOP config COMPAL_LAPTOP tristate "Compal Laptop Extras" depends on X86 - depends on ACPI_EC + depends on ACPI depends on BACKLIGHT_CLASS_DEVICE ---help--- This is a driver for laptops built by Compal: @@ -488,4 +498,6 @@ config SGI_GRU_DEBUG This option enables addition debugging code for the SGI GRU driver. If you are unsure, say N. +source "drivers/misc/c2port/Kconfig" + endif # MISC_DEVICES diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 909e2468cdc9..817f7f5ab3bd 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_ATMEL_PWM) += atmel_pwm.o obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o obj-$(CONFIG_HP_WMI) += hp-wmi.o +obj-$(CONFIG_ICS932S401) += ics932s401.o obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o obj-$(CONFIG_LKDTM) += lkdtm.o obj-$(CONFIG_TIFM_CORE) += tifm_core.o @@ -31,3 +32,4 @@ obj-$(CONFIG_KGDB_TESTS) += kgdbts.o obj-$(CONFIG_SGI_XP) += sgi-xp/ obj-$(CONFIG_SGI_GRU) += sgi-gru/ obj-$(CONFIG_HP_ILO) += hpilo.o +obj-$(CONFIG_C2PORT) += c2port/ diff --git a/drivers/misc/acer-wmi.c b/drivers/misc/acer-wmi.c index 0532a2de2ce4..94c9f911824e 100644 --- a/drivers/misc/acer-wmi.c +++ b/drivers/misc/acer-wmi.c @@ -1297,6 +1297,12 @@ static int __init acer_wmi_init(void) set_quirks(); + if (!acpi_video_backlight_support() && has_cap(ACER_CAP_BRIGHTNESS)) { + interface->capability &= ~ACER_CAP_BRIGHTNESS; + printk(ACER_INFO "Brightness must be controlled by " + "generic video driver\n"); + } + if (platform_driver_register(&acer_platform_driver)) { printk(ACER_ERR "Unable to register platform driver.\n"); goto error_platform_register; diff --git a/drivers/misc/asus-laptop.c b/drivers/misc/asus-laptop.c index a9d5228724a6..8fb8b3591048 100644 --- a/drivers/misc/asus-laptop.c +++ b/drivers/misc/asus-laptop.c @@ -1208,9 +1208,13 @@ static int __init asus_laptop_init(void) dev = acpi_get_physical_device(hotk->device->handle); - result = asus_backlight_init(dev); - if (result) - goto fail_backlight; + if (!acpi_video_backlight_support()) { + result = asus_backlight_init(dev); + if (result) + goto fail_backlight; + } else + printk(ASUS_INFO "Brightness ignored, must be controlled by " + "ACPI video driver\n"); result = asus_led_init(dev); if (result) diff --git a/drivers/misc/c2port/Kconfig b/drivers/misc/c2port/Kconfig new file mode 100644 index 000000000000..e46af9a5810d --- /dev/null +++ b/drivers/misc/c2port/Kconfig @@ -0,0 +1,35 @@ +# +# C2 port devices +# + +menuconfig C2PORT + tristate "Silicon Labs C2 port support (EXPERIMENTAL)" + depends on EXPERIMENTAL + default no + help + This option enables support for Silicon Labs C2 port used to + program Silicon micro controller chips (and other 8051 compatible). + + If your board have no such micro controllers you don't need this + interface at all. + + To compile this driver as a module, choose M here: the module will + be called c2port_core. Note that you also need a client module + usually called c2port-*. + + If you are not sure, say N here. + +if C2PORT + +config C2PORT_DURAMAR_2150 + tristate "C2 port support for Eurotech's Duramar 2150 (EXPERIMENTAL)" + depends on X86 && C2PORT + default no + help + This option enables C2 support for the Eurotech's Duramar 2150 + on board micro controller. + + To compile this driver as a module, choose M here: the module will + be called c2port-duramar2150. + +endif # C2PORT diff --git a/drivers/misc/c2port/Makefile b/drivers/misc/c2port/Makefile new file mode 100644 index 000000000000..3b2cf43d60f5 --- /dev/null +++ b/drivers/misc/c2port/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_C2PORT) += core.o + +obj-$(CONFIG_C2PORT_DURAMAR_2150) += c2port-duramar2150.o diff --git a/drivers/misc/c2port/c2port-duramar2150.c b/drivers/misc/c2port/c2port-duramar2150.c new file mode 100644 index 000000000000..338dcc121507 --- /dev/null +++ b/drivers/misc/c2port/c2port-duramar2150.c @@ -0,0 +1,158 @@ +/* + * Silicon Labs C2 port Linux support for Eurotech Duramar 2150 + * + * Copyright (c) 2008 Rodolfo Giometti <giometti@linux.it> + * Copyright (c) 2008 Eurotech S.p.A. <info@eurotech.it> + * + * 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/errno.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/c2port.h> + +#define DATA_PORT 0x325 +#define DIR_PORT 0x326 +#define C2D (1 << 0) +#define C2CK (1 << 1) + +static DEFINE_MUTEX(update_lock); + +/* + * C2 port operations + */ + +static void duramar2150_c2port_access(struct c2port_device *dev, int status) +{ + u8 v; + + mutex_lock(&update_lock); + + v = inb(DIR_PORT); + + /* 0 = input, 1 = output */ + if (status) + outb(v | (C2D | C2CK), DIR_PORT); + else + /* When access is "off" is important that both lines are set + * as inputs or hi-impedence */ + outb(v & ~(C2D | C2CK), DIR_PORT); + + mutex_unlock(&update_lock); +} + +static void duramar2150_c2port_c2d_dir(struct c2port_device *dev, int dir) +{ + u8 v; + + mutex_lock(&update_lock); + + v = inb(DIR_PORT); + + if (dir) + outb(v & ~C2D, DIR_PORT); + else + outb(v | C2D, DIR_PORT); + + mutex_unlock(&update_lock); +} + +static int duramar2150_c2port_c2d_get(struct c2port_device *dev) +{ + return inb(DATA_PORT) & C2D; +} + +static void duramar2150_c2port_c2d_set(struct c2port_device *dev, int status) +{ + u8 v; + + mutex_lock(&update_lock); + + v = inb(DATA_PORT); + + if (status) + outb(v | C2D, DATA_PORT); + else + outb(v & ~C2D, DATA_PORT); + + mutex_unlock(&update_lock); +} + +static void duramar2150_c2port_c2ck_set(struct c2port_device *dev, int status) +{ + u8 v; + + mutex_lock(&update_lock); + + v = inb(DATA_PORT); + + if (status) + outb(v | C2CK, DATA_PORT); + else + outb(v & ~C2CK, DATA_PORT); + + mutex_unlock(&update_lock); +} + +static struct c2port_ops duramar2150_c2port_ops = { + .block_size = 512, /* bytes */ + .blocks_num = 30, /* total flash size: 15360 bytes */ + + .access = duramar2150_c2port_access, + .c2d_dir = duramar2150_c2port_c2d_dir, + .c2d_get = duramar2150_c2port_c2d_get, + .c2d_set = duramar2150_c2port_c2d_set, + .c2ck_set = duramar2150_c2port_c2ck_set, +}; + +static struct c2port_device *duramar2150_c2port_dev; + +/* + * Module stuff + */ + +static int __init duramar2150_c2port_init(void) +{ + struct resource *res; + int ret = 0; + + res = request_region(0x325, 2, "c2port"); + if (!res) + return -EBUSY; + + duramar2150_c2port_dev = c2port_device_register("uc", + &duramar2150_c2port_ops, NULL); + if (!duramar2150_c2port_dev) { + ret = -ENODEV; + goto free_region; + } + + return 0; + +free_region: + release_region(0x325, 2); + return ret; +} + +static void __exit duramar2150_c2port_exit(void) +{ + /* Setup the GPIOs as input by default (access = 0) */ + duramar2150_c2port_access(duramar2150_c2port_dev, 0); + + c2port_device_unregister(duramar2150_c2port_dev); + + release_region(0x325, 2); +} + +module_init(duramar2150_c2port_init); +module_exit(duramar2150_c2port_exit); + +MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>"); +MODULE_DESCRIPTION("Silicon Labs C2 port Linux support for Duramar 2150"); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/c2port/core.c b/drivers/misc/c2port/core.c new file mode 100644 index 000000000000..0207dd59090d --- /dev/null +++ b/drivers/misc/c2port/core.c @@ -0,0 +1,1003 @@ +/* + * Silicon Labs C2 port core Linux support + * + * Copyright (c) 2007 Rodolfo Giometti <giometti@linux.it> + * Copyright (c) 2007 Eurotech S.p.A. <info@eurotech.it> + * + * 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/init.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/ctype.h> +#include <linux/delay.h> +#include <linux/idr.h> +#include <linux/sched.h> + +#include <linux/c2port.h> + +#define DRIVER_NAME "c2port" +#define DRIVER_VERSION "0.51.0" + +static DEFINE_SPINLOCK(c2port_idr_lock); +static DEFINE_IDR(c2port_idr); + +/* + * Local variables + */ + +static struct class *c2port_class; + +/* + * C2 registers & commands defines + */ + +/* C2 registers */ +#define C2PORT_DEVICEID 0x00 +#define C2PORT_REVID 0x01 +#define C2PORT_FPCTL 0x02 +#define C2PORT_FPDAT 0xB4 + +/* C2 interface commands */ +#define C2PORT_GET_VERSION 0x01 +#define C2PORT_DEVICE_ERASE 0x03 +#define C2PORT_BLOCK_READ 0x06 +#define C2PORT_BLOCK_WRITE 0x07 +#define C2PORT_PAGE_ERASE 0x08 + +/* C2 status return codes */ +#define C2PORT_INVALID_COMMAND 0x00 +#define C2PORT_COMMAND_FAILED 0x02 +#define C2PORT_COMMAND_OK 0x0d + +/* + * C2 port low level signal managements + */ + +static void c2port_reset(struct c2port_device *dev) +{ + struct c2port_ops *ops = dev->ops; + + /* To reset the device we have to keep clock line low for at least + * 20us. + */ + local_irq_disable(); + ops->c2ck_set(dev, 0); + udelay(25); + ops->c2ck_set(dev, 1); + local_irq_enable(); + + udelay(1); +} + +static void c2port_strobe_ck(struct c2port_device *dev) +{ + struct c2port_ops *ops = dev->ops; + + /* During hi-low-hi transition we disable local IRQs to avoid + * interructions since C2 port specification says that it must be + * shorter than 5us, otherwise the microcontroller may consider + * it as a reset signal! + */ + local_irq_disable(); + ops->c2ck_set(dev, 0); + udelay(1); + ops->c2ck_set(dev, 1); + local_irq_enable(); + + udelay(1); +} + +/* + * C2 port basic functions + */ + +static void c2port_write_ar(struct c2port_device *dev, u8 addr) +{ + struct c2port_ops *ops = dev->ops; + int i; + + /* START field */ + c2port_strobe_ck(dev); + + /* INS field (11b, LSB first) */ + ops->c2d_dir(dev, 0); + ops->c2d_set(dev, 1); + c2port_strobe_ck(dev); + ops->c2d_set(dev, 1); + c2port_strobe_ck(dev); + + /* ADDRESS field */ + for (i = 0; i < 8; i++) { + ops->c2d_set(dev, addr & 0x01); + c2port_strobe_ck(dev); + + addr >>= 1; + } + + /* STOP field */ + ops->c2d_dir(dev, 1); + c2port_strobe_ck(dev); +} + +static int c2port_read_ar(struct c2port_device *dev, u8 *addr) +{ + struct c2port_ops *ops = dev->ops; + int i; + + /* START field */ + c2port_strobe_ck(dev); + + /* INS field (10b, LSB first) */ + ops->c2d_dir(dev, 0); + ops->c2d_set(dev, 0); + c2port_strobe_ck(dev); + ops->c2d_set(dev, 1); + c2port_strobe_ck(dev); + + /* ADDRESS field */ + ops->c2d_dir(dev, 1); + *addr = 0; + for (i = 0; i < 8; i++) { + *addr >>= 1; /* shift in 8-bit ADDRESS field LSB first */ + + c2port_strobe_ck(dev); + if (ops->c2d_get(dev)) + *addr |= 0x80; + } + + /* STOP field */ + c2port_strobe_ck(dev); + + return 0; +} + +static int c2port_write_dr(struct c2port_device *dev, u8 data) +{ + struct c2port_ops *ops = dev->ops; + int timeout, i; + + /* START field */ + c2port_strobe_ck(dev); + + /* INS field (01b, LSB first) */ + ops->c2d_dir(dev, 0); + ops->c2d_set(dev, 1); + c2port_strobe_ck(dev); + ops->c2d_set(dev, 0); + c2port_strobe_ck(dev); + + /* LENGTH field (00b, LSB first -> 1 byte) */ + ops->c2d_set(dev, 0); + c2port_strobe_ck(dev); + ops->c2d_set(dev, 0); + c2port_strobe_ck(dev); + + /* DATA field */ + for (i = 0; i < 8; i++) { + ops->c2d_set(dev, data & 0x01); + c2port_strobe_ck(dev); + + data >>= 1; + } + + /* WAIT field */ + ops->c2d_dir(dev, 1); + timeout = 20; + do { + c2port_strobe_ck(dev); + if (ops->c2d_get(dev)) + break; + + udelay(1); + } while (--timeout > 0); + if (timeout == 0) + return -EIO; + + /* STOP field */ + c2port_strobe_ck(dev); + + return 0; +} + +static int c2port_read_dr(struct c2port_device *dev, u8 *data) +{ + struct c2port_ops *ops = dev->ops; + int timeout, i; + + /* START field */ + c2port_strobe_ck(dev); + + /* INS field (00b, LSB first) */ + ops->c2d_dir(dev, 0); + ops->c2d_set(dev, 0); + c2port_strobe_ck(dev); + ops->c2d_set(dev, 0); + c2port_strobe_ck(dev); + + /* LENGTH field (00b, LSB first -> 1 byte) */ + ops->c2d_set(dev, 0); + c2port_strobe_ck(dev); + ops->c2d_set(dev, 0); + c2port_strobe_ck(dev); + + /* WAIT field */ + ops->c2d_dir(dev, 1); + timeout = 20; + do { + c2port_strobe_ck(dev); + if (ops->c2d_get(dev)) + break; + + udelay(1); + } while (--timeout > 0); + if (timeout == 0) + return -EIO; + + /* DATA field */ + *data = 0; + for (i = 0; i < 8; i++) { + *data >>= 1; /* shift in 8-bit DATA field LSB first */ + + c2port_strobe_ck(dev); + if (ops->c2d_get(dev)) + *data |= 0x80; + } + + /* STOP field */ + c2port_strobe_ck(dev); + + return 0; +} + +static int c2port_poll_in_busy(struct c2port_device *dev) +{ + u8 addr; + int ret, timeout = 20; + + do { + ret = (c2port_read_ar(dev, &addr)); + if (ret < 0) + return -EIO; + + if (!(addr & 0x02)) + break; + + udelay(1); + } while (--timeout > 0); + if (timeout == 0) + return -EIO; + + return 0; +} + +static int c2port_poll_out_ready(struct c2port_device *dev) +{ + u8 addr; + int ret, timeout = 10000; /* erase flash needs long time... */ + + do { + ret = (c2port_read_ar(dev, &addr)); + if (ret < 0) + return -EIO; + + if (addr & 0x01) + break; + + udelay(1); + } while (--timeout > 0); + if (timeout == 0) + return -EIO; + + return 0; +} + +/* + * sysfs methods + */ + +static ssize_t c2port_show_name(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct c2port_device *c2dev = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", c2dev->name); +} + +static ssize_t c2port_show_flash_blocks_num(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct c2port_device *c2dev = dev_get_drvdata(dev); + struct c2port_ops *ops = c2dev->ops; + + return sprintf(buf, "%d\n", ops->blocks_num); +} + +static ssize_t c2port_show_flash_block_size(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct c2port_device *c2dev = dev_get_drvdata(dev); + struct c2port_ops *ops = c2dev->ops; + + return sprintf(buf, "%d\n", ops->block_size); +} + +static ssize_t c2port_show_flash_size(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct c2port_device *c2dev = dev_get_drvdata(dev); + struct c2port_ops *ops = c2dev->ops; + + return sprintf(buf, "%d\n", ops->blocks_num * ops->block_size); +} + +static ssize_t c2port_show_access(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct c2port_device *c2dev = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", c2dev->access); +} + +static ssize_t c2port_store_access(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct c2port_device *c2dev = dev_get_drvdata(dev); + struct c2port_ops *ops = c2dev->ops; + int status, ret; + + ret = sscanf(buf, "%d", &status); + if (ret != 1) + return -EINVAL; + + mutex_lock(&c2dev->mutex); + + c2dev->access = !!status; + + /* If access is "on" clock should be HIGH _before_ setting the line + * as output and data line should be set as INPUT anyway */ + if (c2dev->access) + ops->c2ck_set(c2dev, 1); + ops->access(c2dev, c2dev->access); + if (c2dev->access) + ops->c2d_dir(c2dev, 1); + + mutex_unlock(&c2dev->mutex); + + return count; +} + +static ssize_t c2port_store_reset(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct c2port_device *c2dev = dev_get_drvdata(dev); + + /* Check the device access status */ + if (!c2dev->access) + return -EBUSY; + + mutex_lock(&c2dev->mutex); + + c2port_reset(c2dev); + c2dev->flash_access = 0; + + mutex_unlock(&c2dev->mutex); + + return count; +} + +static ssize_t __c2port_show_dev_id(struct c2port_device *dev, char *buf) +{ + u8 data; + int ret; + + /* Select DEVICEID register for C2 data register accesses */ + c2port_write_ar(dev, C2PORT_DEVICEID); + + /* Read and return the device ID register */ + ret = c2port_read_dr(dev, &data); + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", data); +} + +static ssize_t c2port_show_dev_id(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct c2port_device *c2dev = dev_get_drvdata(dev); + ssize_t ret; + + /* Check the device access status */ + if (!c2dev->access) + return -EBUSY; + + mutex_lock(&c2dev->mutex); + ret = __c2port_show_dev_id(c2dev, buf); + mutex_unlock(&c2dev->mutex); + + if (ret < 0) + dev_err(dev, "cannot read from %s\n", c2dev->name); + + return ret; +} + +static ssize_t __c2port_show_rev_id(struct c2port_device *dev, char *buf) +{ + u8 data; + int ret; + + /* Select REVID register for C2 data register accesses */ + c2port_write_ar(dev, C2PORT_REVID); + + /* Read and return the revision ID register */ + ret = c2port_read_dr(dev, &data); + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", data); +} + +static ssize_t c2port_show_rev_id(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct c2port_device *c2dev = dev_get_drvdata(dev); + ssize_t ret; + + /* Check the device access status */ + if (!c2dev->access) + return -EBUSY; + + mutex_lock(&c2dev->mutex); + ret = __c2port_show_rev_id(c2dev, buf); + mutex_unlock(&c2dev->mutex); + + if (ret < 0) + dev_err(c2dev->dev, "cannot read from %s\n", c2dev->name); + + return ret; +} + +static ssize_t c2port_show_flash_access(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct c2port_device *c2dev = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", c2dev->flash_access); +} + +static ssize_t __c2port_store_flash_access(struct c2port_device *dev, + int status) +{ + int ret; + + /* Check the device access status */ + if (!dev->access) + return -EBUSY; + + dev->flash_access = !!status; + + /* If flash_access is off we have nothing to do... */ + if (dev->flash_access == 0) + return 0; + + /* Target the C2 flash programming control register for C2 data + * register access */ + c2port_write_ar(dev, C2PORT_FPCTL); + + /* Write the first keycode to enable C2 Flash programming */ + ret = c2port_write_dr(dev, 0x02); + if (ret < 0) + return ret; + + /* Write the second keycode to enable C2 Flash programming */ + ret = c2port_write_dr(dev, 0x01); + if (ret < 0) + return ret; + + /* Delay for at least 20ms to ensure the target is ready for + * C2 flash programming */ + mdelay(25); + + return 0; +} + +static ssize_t c2port_store_flash_access(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct c2port_device *c2dev = dev_get_drvdata(dev); + int status; + ssize_t ret; + + ret = sscanf(buf, "%d", &status); + if (ret != 1) + return -EINVAL; + + mutex_lock(&c2dev->mutex); + ret = __c2port_store_flash_access(c2dev, status); + mutex_unlock(&c2dev->mutex); + + if (ret < 0) { + dev_err(c2dev->dev, "cannot enable %s flash programming\n", + c2dev->name); + return ret; + } + + return count; +} + +static ssize_t __c2port_write_flash_erase(struct c2port_device *dev) +{ + u8 status; + int ret; + + /* Target the C2 flash programming data register for C2 data register + * access. + */ + c2port_write_ar(dev, C2PORT_FPDAT); + + /* Send device erase command */ + c2port_write_dr(dev, C2PORT_DEVICE_ERASE); + + /* Wait for input acknowledge */ + ret = c2port_poll_in_busy(dev); + if (ret < 0) + return ret; + + /* Should check status before starting FLASH access sequence */ + + /* Wait for status information */ + ret = c2port_poll_out_ready(dev); + if (ret < 0) + return ret; + + /* Read flash programming interface status */ + ret = c2port_read_dr(dev, &status); + if (ret < 0) + return ret; + if (status != C2PORT_COMMAND_OK) + return -EBUSY; + + /* Send a three-byte arming sequence to enable the device erase. + * If the sequence is not received correctly, the command will be + * ignored. + * Sequence is: 0xde, 0xad, 0xa5. + */ + c2port_write_dr(dev, 0xde); + ret = c2port_poll_in_busy(dev); + if (ret < 0) + return ret; + c2port_write_dr(dev, 0xad); + ret = c2port_poll_in_busy(dev); + if (ret < 0) + return ret; + c2port_write_dr(dev, 0xa5); + ret = c2port_poll_in_busy(dev); + if (ret < 0) + return ret; + + ret = c2port_poll_out_ready(dev); + if (ret < 0) + return ret; + + return 0; +} + +static ssize_t c2port_store_flash_erase(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct c2port_device *c2dev = dev_get_drvdata(dev); + int ret; + + /* Check the device and flash access status */ + if (!c2dev->access || !c2dev->flash_access) + return -EBUSY; + + mutex_lock(&c2dev->mutex); + ret = __c2port_write_flash_erase(c2dev); + mutex_unlock(&c2dev->mutex); + + if (ret < 0) { + dev_err(c2dev->dev, "cannot erase %s flash\n", c2dev->name); + return ret; + } + + return count; +} + +static ssize_t __c2port_read_flash_data(struct c2port_device *dev, + char *buffer, loff_t offset, size_t count) +{ + struct c2port_ops *ops = dev->ops; + u8 status, nread = 128; + int i, ret; + + /* Check for flash end */ + if (offset >= ops->block_size * ops->blocks_num) + return 0; + + if (ops->block_size * ops->blocks_num - offset < nread) + nread = ops->block_size * ops->blocks_num - offset; + if (count < nread) + nread = count; + if (nread == 0) + return nread; + + /* Target the C2 flash programming data register for C2 data register + * access */ + c2port_write_ar(dev, C2PORT_FPDAT); + + /* Send flash block read command */ + c2port_write_dr(dev, C2PORT_BLOCK_READ); + + /* Wait for input acknowledge */ + ret = c2port_poll_in_busy(dev); + if (ret < 0) + return ret; + + /* Should check status before starting FLASH access sequence */ + + /* Wait for status information */ + ret = c2port_poll_out_ready(dev); + if (ret < 0) + return ret; + + /* Read flash programming interface status */ + ret = c2port_read_dr(dev, &status); + if (ret < 0) + return ret; + if (status != C2PORT_COMMAND_OK) + return -EBUSY; + + /* Send address high byte */ + c2port_write_dr(dev, offset >> 8); + ret = c2port_poll_in_busy(dev); + if (ret < 0) + return ret; + + /* Send address low byte */ + c2port_write_dr(dev, offset & 0x00ff); + ret = c2port_poll_in_busy(dev); + if (ret < 0) + return ret; + + /* Send address block size */ + c2port_write_dr(dev, nread); + ret = c2port_poll_in_busy(dev); + if (ret < 0) + return ret; + + /* Should check status before reading FLASH block */ + + /* Wait for status information */ + ret = c2port_poll_out_ready(dev); + if (ret < 0) + return ret; + + /* Read flash programming interface status */ + ret = c2port_read_dr(dev, &status); + if (ret < 0) + return ret; + if (status != C2PORT_COMMAND_OK) + return -EBUSY; + + /* Read flash block */ + for (i = 0; i < nread; i++) { + ret = c2port_poll_out_ready(dev); + if (ret < 0) + return ret; + + ret = c2port_read_dr(dev, buffer+i); + if (ret < 0) + return ret; + } + + return nread; +} + +static ssize_t c2port_read_flash_data(struct kobject *kobj, + struct bin_attribute *attr, + char *buffer, loff_t offset, size_t count) +{ + struct c2port_device *c2dev = + dev_get_drvdata(container_of(kobj, + struct device, kobj)); + ssize_t ret; + + /* Check the device and flash access status */ + if (!c2dev->access || !c2dev->flash_access) + return -EBUSY; + + mutex_lock(&c2dev->mutex); + ret = __c2port_read_flash_data(c2dev, buffer, offset, count); + mutex_unlock(&c2dev->mutex); + + if (ret < 0) + dev_err(c2dev->dev, "cannot read %s flash\n", c2dev->name); + + return ret; +} + +static ssize_t __c2port_write_flash_data(struct c2port_device *dev, + char *buffer, loff_t offset, size_t count) +{ + struct c2port_ops *ops = dev->ops; + u8 status, nwrite = 128; + int i, ret; + + if (nwrite > count) + nwrite = count; + if (ops->block_size * ops->blocks_num - offset < nwrite) + nwrite = ops->block_size * ops->blocks_num - offset; + + /* Check for flash end */ + if (offset >= ops->block_size * ops->blocks_num) + return -EINVAL; + + /* Target the C2 flash programming data register for C2 data register + * access */ + c2port_write_ar(dev, C2PORT_FPDAT); + + /* Send flash block write command */ + c2port_write_dr(dev, C2PORT_BLOCK_WRITE); + + /* Wait for input acknowledge */ + ret = c2port_poll_in_busy(dev); + if (ret < 0) + return ret; + + /* Should check status before starting FLASH access sequence */ + + /* Wait for status information */ + ret = c2port_poll_out_ready(dev); + if (ret < 0) + return ret; + + /* Read flash programming interface status */ + ret = c2port_read_dr(dev, &status); + if (ret < 0) + return ret; + if (status != C2PORT_COMMAND_OK) + return -EBUSY; + + /* Send address high byte */ + c2port_write_dr(dev, offset >> 8); + ret = c2port_poll_in_busy(dev); + if (ret < 0) + return ret; + + /* Send address low byte */ + c2port_write_dr(dev, offset & 0x00ff); + ret = c2port_poll_in_busy(dev); + if (ret < 0) + return ret; + + /* Send address block size */ + c2port_write_dr(dev, nwrite); + ret = c2port_poll_in_busy(dev); + if (ret < 0) + return ret; + + /* Should check status before writing FLASH block */ + + /* Wait for status information */ + ret = c2port_poll_out_ready(dev); + if (ret < 0) + return ret; + + /* Read flash programming interface status */ + ret = c2port_read_dr(dev, &status); + if (ret < 0) + return ret; + if (status != C2PORT_COMMAND_OK) + return -EBUSY; + + /* Write flash block */ + for (i = 0; i < nwrite; i++) { + ret = c2port_write_dr(dev, *(buffer+i)); + if (ret < 0) + return ret; + + ret = c2port_poll_in_busy(dev); + if (ret < 0) + return ret; + + } + + /* Wait for last flash write to complete */ + ret = c2port_poll_out_ready(dev); + if (ret < 0) + return ret; + + return nwrite; +} + +static ssize_t c2port_write_flash_data(struct kobject *kobj, + struct bin_attribute *attr, + char *buffer, loff_t offset, size_t count) +{ + struct c2port_device *c2dev = + dev_get_drvdata(container_of(kobj, + struct device, kobj)); + int ret; + + /* Check the device access status */ + if (!c2dev->access || !c2dev->flash_access) + return -EBUSY; + + mutex_lock(&c2dev->mutex); + ret = __c2port_write_flash_data(c2dev, buffer, offset, count); + mutex_unlock(&c2dev->mutex); + + if (ret < 0) + dev_err(c2dev->dev, "cannot write %s flash\n", c2dev->name); + + return ret; +} + +/* + * Class attributes + */ + +static struct device_attribute c2port_attrs[] = { + __ATTR(name, 0444, c2port_show_name, NULL), + __ATTR(flash_blocks_num, 0444, c2port_show_flash_blocks_num, NULL), + __ATTR(flash_block_size, 0444, c2port_show_flash_block_size, NULL), + __ATTR(flash_size, 0444, c2port_show_flash_size, NULL), + __ATTR(access, 0644, c2port_show_access, c2port_store_access), + __ATTR(reset, 0200, NULL, c2port_store_reset), + __ATTR(dev_id, 0444, c2port_show_dev_id, NULL), + __ATTR(rev_id, 0444, c2port_show_rev_id, NULL), + + __ATTR(flash_access, 0644, c2port_show_flash_access, + c2port_store_flash_access), + __ATTR(flash_erase, 0200, NULL, c2port_store_flash_erase), + __ATTR_NULL, +}; + +static struct bin_attribute c2port_bin_attrs = { + .attr = { + .name = "flash_data", + .mode = 0644 + }, + .read = c2port_read_flash_data, + .write = c2port_write_flash_data, + /* .size is computed at run-time */ +}; + +/* + * Exported functions + */ + +struct c2port_device *c2port_device_register(char *name, + struct c2port_ops *ops, void *devdata) +{ + struct c2port_device *c2dev; + int id, ret; + + if (unlikely(!ops) || unlikely(!ops->access) || \ + unlikely(!ops->c2d_dir) || unlikely(!ops->c2ck_set) || \ + unlikely(!ops->c2d_get) || unlikely(!ops->c2d_set)) + return ERR_PTR(-EINVAL); + + c2dev = kmalloc(sizeof(struct c2port_device), GFP_KERNEL); + if (unlikely(!c2dev)) + return ERR_PTR(-ENOMEM); + + ret = idr_pre_get(&c2port_idr, GFP_KERNEL); + if (!ret) { + ret = -ENOMEM; + goto error_idr_get_new; + } + + spin_lock_irq(&c2port_idr_lock); + ret = idr_get_new(&c2port_idr, c2dev, &id); + spin_unlock_irq(&c2port_idr_lock); + + if (ret < 0) + goto error_idr_get_new; + c2dev->id = id; + + c2dev->dev = device_create(c2port_class, NULL, 0, c2dev, + "c2port%d", id); + if (unlikely(!c2dev->dev)) { + ret = -ENOMEM; + goto error_device_create; + } + dev_set_drvdata(c2dev->dev, c2dev); + + strncpy(c2dev->name, name, C2PORT_NAME_LEN); + c2dev->ops = ops; + mutex_init(&c2dev->mutex); + + /* Create binary file */ + c2port_bin_attrs.size = ops->blocks_num * ops->block_size; + ret = device_create_bin_file(c2dev->dev, &c2port_bin_attrs); + if (unlikely(ret)) + goto error_device_create_bin_file; + + /* By default C2 port access is off */ + c2dev->access = c2dev->flash_access = 0; + ops->access(c2dev, 0); + + dev_info(c2dev->dev, "C2 port %s added\n", name); + dev_info(c2dev->dev, "%s flash has %d blocks x %d bytes " + "(%d bytes total)\n", + name, ops->blocks_num, ops->block_size, + ops->blocks_num * ops->block_size); + + return c2dev; + +error_device_create_bin_file: + device_destroy(c2port_class, 0); + +error_device_create: + spin_lock_irq(&c2port_idr_lock); + idr_remove(&c2port_idr, id); + spin_unlock_irq(&c2port_idr_lock); + +error_idr_get_new: + kfree(c2dev); + + return ERR_PTR(ret); +} +EXPORT_SYMBOL(c2port_device_register); + +void c2port_device_unregister(struct c2port_device *c2dev) +{ + if (!c2dev) + return; + + dev_info(c2dev->dev, "C2 port %s removed\n", c2dev->name); + + device_remove_bin_file(c2dev->dev, &c2port_bin_attrs); + spin_lock_irq(&c2port_idr_lock); + idr_remove(&c2port_idr, c2dev->id); + spin_unlock_irq(&c2port_idr_lock); + + device_destroy(c2port_class, c2dev->id); + + kfree(c2dev); +} +EXPORT_SYMBOL(c2port_device_unregister); + +/* + * Module stuff + */ + +static int __init c2port_init(void) +{ + printk(KERN_INFO "Silicon Labs C2 port support v. " DRIVER_VERSION + " - (C) 2007 Rodolfo Giometti\n"); + + c2port_class = class_create(THIS_MODULE, "c2port"); + if (!c2port_class) { + printk(KERN_ERR "c2port: failed to allocate class\n"); + return -ENOMEM; + } + c2port_class->dev_attrs = c2port_attrs; + + return 0; +} + +static void __exit c2port_exit(void) +{ + class_destroy(c2port_class); +} + +module_init(c2port_init); +module_exit(c2port_exit); + +MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>"); +MODULE_DESCRIPTION("Silicon Labs C2 port support v. " DRIVER_VERSION); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/compal-laptop.c b/drivers/misc/compal-laptop.c index 344b790a6253..11003bba10d3 100644 --- a/drivers/misc/compal-laptop.c +++ b/drivers/misc/compal-laptop.c @@ -326,12 +326,14 @@ static int __init compal_init(void) /* Register backlight stuff */ - compalbl_device = backlight_device_register("compal-laptop", NULL, NULL, - &compalbl_ops); - if (IS_ERR(compalbl_device)) - return PTR_ERR(compalbl_device); + if (!acpi_video_backlight_support()) { + compalbl_device = backlight_device_register("compal-laptop", NULL, NULL, + &compalbl_ops); + if (IS_ERR(compalbl_device)) + return PTR_ERR(compalbl_device); - compalbl_device->props.max_brightness = COMPAL_LCD_LEVEL_MAX-1; + compalbl_device->props.max_brightness = COMPAL_LCD_LEVEL_MAX-1; + } ret = platform_driver_register(&compal_driver); if (ret) diff --git a/drivers/misc/eeepc-laptop.c b/drivers/misc/eeepc-laptop.c index 9ef98b2d5039..02fe2b8b8939 100644 --- a/drivers/misc/eeepc-laptop.c +++ b/drivers/misc/eeepc-laptop.c @@ -825,9 +825,15 @@ static int __init eeepc_laptop_init(void) return -ENODEV; } dev = acpi_get_physical_device(ehotk->device->handle); - result = eeepc_backlight_init(dev); - if (result) - goto fail_backlight; + + if (!acpi_video_backlight_support()) { + result = eeepc_backlight_init(dev); + if (result) + goto fail_backlight; + } else + printk(EEEPC_INFO "Backlight controlled by ACPI video " + "driver\n"); + result = eeepc_hwmon_init(dev); if (result) goto fail_hwmon; diff --git a/drivers/misc/enclosure.c b/drivers/misc/enclosure.c index 0736cff9d97a..3cf61ece71d7 100644 --- a/drivers/misc/enclosure.c +++ b/drivers/misc/enclosure.c @@ -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; diff --git a/drivers/misc/fujitsu-laptop.c b/drivers/misc/fujitsu-laptop.c index d2cf0bfe3163..a7dd3e9fb79d 100644 --- a/drivers/misc/fujitsu-laptop.c +++ b/drivers/misc/fujitsu-laptop.c @@ -464,6 +464,14 @@ static int dmi_check_cb_s6410(const struct dmi_system_id *id) return 0; } +static int dmi_check_cb_s6420(const struct dmi_system_id *id) +{ + dmi_check_cb_common(id); + fujitsu->keycode1 = KEY_SCREENLOCK; /* "Lock" */ + fujitsu->keycode2 = KEY_HELP; /* "Mobility Center" */ + return 0; +} + static int dmi_check_cb_p8010(const struct dmi_system_id *id) { dmi_check_cb_common(id); @@ -473,7 +481,7 @@ static int dmi_check_cb_p8010(const struct dmi_system_id *id) return 0; } -static struct dmi_system_id __initdata fujitsu_dmi_table[] = { +static struct dmi_system_id fujitsu_dmi_table[] = { { .ident = "Fujitsu Siemens S6410", .matches = { @@ -482,6 +490,13 @@ static struct dmi_system_id __initdata fujitsu_dmi_table[] = { }, .callback = dmi_check_cb_s6410}, { + .ident = "Fujitsu Siemens S6420", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK S6420"), + }, + .callback = dmi_check_cb_s6420}, + { .ident = "Fujitsu LifeBook P8010", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), @@ -990,16 +1005,16 @@ static int __init fujitsu_init(void) /* Register backlight stuff */ - fujitsu->bl_device = - backlight_device_register("fujitsu-laptop", NULL, NULL, - &fujitsubl_ops); - if (IS_ERR(fujitsu->bl_device)) - return PTR_ERR(fujitsu->bl_device); - - max_brightness = fujitsu->max_brightness; - - fujitsu->bl_device->props.max_brightness = max_brightness - 1; - fujitsu->bl_device->props.brightness = fujitsu->brightness_level; + if (!acpi_video_backlight_support()) { + fujitsu->bl_device = + backlight_device_register("fujitsu-laptop", NULL, NULL, + &fujitsubl_ops); + if (IS_ERR(fujitsu->bl_device)) + return PTR_ERR(fujitsu->bl_device); + max_brightness = fujitsu->max_brightness; + fujitsu->bl_device->props.max_brightness = max_brightness - 1; + fujitsu->bl_device->props.brightness = fujitsu->brightness_level; + } ret = platform_driver_register(&fujitsupf_driver); if (ret) @@ -1035,7 +1050,8 @@ fail_hotkey: fail_backlight: - backlight_device_unregister(fujitsu->bl_device); + if (fujitsu->bl_device) + backlight_device_unregister(fujitsu->bl_device); fail_platform_device2: @@ -1062,7 +1078,8 @@ static void __exit fujitsu_cleanup(void) &fujitsupf_attribute_group); platform_device_unregister(fujitsu->pf_device); platform_driver_unregister(&fujitsupf_driver); - backlight_device_unregister(fujitsu->bl_device); + if (fujitsu->bl_device) + backlight_device_unregister(fujitsu->bl_device); acpi_bus_unregister_driver(&acpi_fujitsu_driver); diff --git a/drivers/misc/ics932s401.c b/drivers/misc/ics932s401.c new file mode 100644 index 000000000000..6e43ab4231ae --- /dev/null +++ b/drivers/misc/ics932s401.c @@ -0,0 +1,515 @@ +/* + * A driver for the Integrated Circuits ICS932S401 + * Copyright (C) 2008 IBM + * + * Author: Darrick J. Wong <djwong@us.ibm.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/module.h> +#include <linux/jiffies.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/mutex.h> +#include <linux/delay.h> +#include <linux/log2.h> + +/* Addresses to scan */ +static const unsigned short normal_i2c[] = { 0x69, I2C_CLIENT_END }; + +/* Insmod parameters */ +I2C_CLIENT_INSMOD_1(ics932s401); + +/* ICS932S401 registers */ +#define ICS932S401_REG_CFG2 0x01 +#define ICS932S401_CFG1_SPREAD 0x01 +#define ICS932S401_REG_CFG7 0x06 +#define ICS932S401_FS_MASK 0x07 +#define ICS932S401_REG_VENDOR_REV 0x07 +#define ICS932S401_VENDOR 1 +#define ICS932S401_VENDOR_MASK 0x0F +#define ICS932S401_REV 4 +#define ICS932S401_REV_SHIFT 4 +#define ICS932S401_REG_DEVICE 0x09 +#define ICS932S401_DEVICE 11 +#define ICS932S401_REG_CTRL 0x0A +#define ICS932S401_MN_ENABLED 0x80 +#define ICS932S401_CPU_ALT 0x04 +#define ICS932S401_SRC_ALT 0x08 +#define ICS932S401_REG_CPU_M_CTRL 0x0B +#define ICS932S401_M_MASK 0x3F +#define ICS932S401_REG_CPU_N_CTRL 0x0C +#define ICS932S401_REG_CPU_SPREAD1 0x0D +#define ICS932S401_REG_CPU_SPREAD2 0x0E +#define ICS932S401_SPREAD_MASK 0x7FFF +#define ICS932S401_REG_SRC_M_CTRL 0x0F +#define ICS932S401_REG_SRC_N_CTRL 0x10 +#define ICS932S401_REG_SRC_SPREAD1 0x11 +#define ICS932S401_REG_SRC_SPREAD2 0x12 +#define ICS932S401_REG_CPU_DIVISOR 0x13 +#define ICS932S401_CPU_DIVISOR_SHIFT 4 +#define ICS932S401_REG_PCISRC_DIVISOR 0x14 +#define ICS932S401_SRC_DIVISOR_MASK 0x0F +#define ICS932S401_PCI_DIVISOR_SHIFT 4 + +/* Base clock is 14.318MHz */ +#define BASE_CLOCK 14318 + +#define NUM_REGS 21 +#define NUM_MIRRORED_REGS 15 + +static int regs_to_copy[NUM_MIRRORED_REGS] = { + ICS932S401_REG_CFG2, + ICS932S401_REG_CFG7, + ICS932S401_REG_VENDOR_REV, + ICS932S401_REG_DEVICE, + ICS932S401_REG_CTRL, + ICS932S401_REG_CPU_M_CTRL, + ICS932S401_REG_CPU_N_CTRL, + ICS932S401_REG_CPU_SPREAD1, + ICS932S401_REG_CPU_SPREAD2, + ICS932S401_REG_SRC_M_CTRL, + ICS932S401_REG_SRC_N_CTRL, + ICS932S401_REG_SRC_SPREAD1, + ICS932S401_REG_SRC_SPREAD2, + ICS932S401_REG_CPU_DIVISOR, + ICS932S401_REG_PCISRC_DIVISOR, +}; + +/* How often do we reread sensors values? (In jiffies) */ +#define SENSOR_REFRESH_INTERVAL (2 * HZ) + +/* How often do we reread sensor limit values? (In jiffies) */ +#define LIMIT_REFRESH_INTERVAL (60 * HZ) + +struct ics932s401_data { + struct attribute_group attrs; + struct mutex lock; + char sensors_valid; + unsigned long sensors_last_updated; /* In jiffies */ + + u8 regs[NUM_REGS]; +}; + +static int ics932s401_probe(struct i2c_client *client, + const struct i2c_device_id *id); +static int ics932s401_detect(struct i2c_client *client, int kind, + struct i2c_board_info *info); +static int ics932s401_remove(struct i2c_client *client); + +static const struct i2c_device_id ics932s401_id[] = { + { "ics932s401", ics932s401 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ics932s401_id); + +static struct i2c_driver ics932s401_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "ics932s401", + }, + .probe = ics932s401_probe, + .remove = ics932s401_remove, + .id_table = ics932s401_id, + .detect = ics932s401_detect, + .address_data = &addr_data, +}; + +static struct ics932s401_data *ics932s401_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ics932s401_data *data = i2c_get_clientdata(client); + unsigned long local_jiffies = jiffies; + int i, temp; + + mutex_lock(&data->lock); + if (time_before(local_jiffies, data->sensors_last_updated + + SENSOR_REFRESH_INTERVAL) + && data->sensors_valid) + goto out; + + /* + * Each register must be read as a word and then right shifted 8 bits. + * Not really sure why this is; setting the "byte count programming" + * register to 1 does not fix this problem. + */ + for (i = 0; i < NUM_MIRRORED_REGS; i++) { + temp = i2c_smbus_read_word_data(client, regs_to_copy[i]); + data->regs[regs_to_copy[i]] = temp >> 8; + } + + data->sensors_last_updated = local_jiffies; + data->sensors_valid = 1; + +out: + mutex_unlock(&data->lock); + return data; +} + +static ssize_t show_spread_enabled(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct ics932s401_data *data = ics932s401_update_device(dev); + + if (data->regs[ICS932S401_REG_CFG2] & ICS932S401_CFG1_SPREAD) + return sprintf(buf, "1\n"); + + return sprintf(buf, "0\n"); +} + +/* bit to cpu khz map */ +static const int fs_speeds[] = { + 266666, + 133333, + 200000, + 166666, + 333333, + 100000, + 400000, + 0, +}; + +/* clock divisor map */ +static const int divisors[] = {2, 3, 5, 15, 4, 6, 10, 30, 8, 12, 20, 60, 16, + 24, 40, 120}; + +/* Calculate CPU frequency from the M/N registers. */ +static int calculate_cpu_freq(struct ics932s401_data *data) +{ + int m, n, freq; + + m = data->regs[ICS932S401_REG_CPU_M_CTRL] & ICS932S401_M_MASK; + n = data->regs[ICS932S401_REG_CPU_N_CTRL]; + + /* Pull in bits 8 & 9 from the M register */ + n |= ((int)data->regs[ICS932S401_REG_CPU_M_CTRL] & 0x80) << 1; + n |= ((int)data->regs[ICS932S401_REG_CPU_M_CTRL] & 0x40) << 3; + + freq = BASE_CLOCK * (n + 8) / (m + 2); + freq /= divisors[data->regs[ICS932S401_REG_CPU_DIVISOR] >> + ICS932S401_CPU_DIVISOR_SHIFT]; + + return freq; +} + +static ssize_t show_cpu_clock(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct ics932s401_data *data = ics932s401_update_device(dev); + + return sprintf(buf, "%d\n", calculate_cpu_freq(data)); +} + +static ssize_t show_cpu_clock_sel(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct ics932s401_data *data = ics932s401_update_device(dev); + int freq; + + if (data->regs[ICS932S401_REG_CTRL] & ICS932S401_MN_ENABLED) + freq = calculate_cpu_freq(data); + else { + /* Freq is neatly wrapped up for us */ + int fid = data->regs[ICS932S401_REG_CFG7] & ICS932S401_FS_MASK; + freq = fs_speeds[fid]; + if (data->regs[ICS932S401_REG_CTRL] & ICS932S401_CPU_ALT) { + switch (freq) { + case 166666: + freq = 160000; + break; + case 333333: + freq = 320000; + break; + } + } + } + + return sprintf(buf, "%d\n", freq); +} + +/* Calculate SRC frequency from the M/N registers. */ +static int calculate_src_freq(struct ics932s401_data *data) +{ + int m, n, freq; + + m = data->regs[ICS932S401_REG_SRC_M_CTRL] & ICS932S401_M_MASK; + n = data->regs[ICS932S401_REG_SRC_N_CTRL]; + + /* Pull in bits 8 & 9 from the M register */ + n |= ((int)data->regs[ICS932S401_REG_SRC_M_CTRL] & 0x80) << 1; + n |= ((int)data->regs[ICS932S401_REG_SRC_M_CTRL] & 0x40) << 3; + + freq = BASE_CLOCK * (n + 8) / (m + 2); + freq /= divisors[data->regs[ICS932S401_REG_PCISRC_DIVISOR] & + ICS932S401_SRC_DIVISOR_MASK]; + + return freq; +} + +static ssize_t show_src_clock(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct ics932s401_data *data = ics932s401_update_device(dev); + + return sprintf(buf, "%d\n", calculate_src_freq(data)); +} + +static ssize_t show_src_clock_sel(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct ics932s401_data *data = ics932s401_update_device(dev); + int freq; + + if (data->regs[ICS932S401_REG_CTRL] & ICS932S401_MN_ENABLED) + freq = calculate_src_freq(data); + else + /* Freq is neatly wrapped up for us */ + if (data->regs[ICS932S401_REG_CTRL] & ICS932S401_CPU_ALT && + data->regs[ICS932S401_REG_CTRL] & ICS932S401_SRC_ALT) + freq = 96000; + else + freq = 100000; + + return sprintf(buf, "%d\n", freq); +} + +/* Calculate PCI frequency from the SRC M/N registers. */ +static int calculate_pci_freq(struct ics932s401_data *data) +{ + int m, n, freq; + + m = data->regs[ICS932S401_REG_SRC_M_CTRL] & ICS932S401_M_MASK; + n = data->regs[ICS932S401_REG_SRC_N_CTRL]; + + /* Pull in bits 8 & 9 from the M register */ + n |= ((int)data->regs[ICS932S401_REG_SRC_M_CTRL] & 0x80) << 1; + n |= ((int)data->regs[ICS932S401_REG_SRC_M_CTRL] & 0x40) << 3; + + freq = BASE_CLOCK * (n + 8) / (m + 2); + freq /= divisors[data->regs[ICS932S401_REG_PCISRC_DIVISOR] >> + ICS932S401_PCI_DIVISOR_SHIFT]; + + return freq; +} + +static ssize_t show_pci_clock(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct ics932s401_data *data = ics932s401_update_device(dev); + + return sprintf(buf, "%d\n", calculate_pci_freq(data)); +} + +static ssize_t show_pci_clock_sel(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct ics932s401_data *data = ics932s401_update_device(dev); + int freq; + + if (data->regs[ICS932S401_REG_CTRL] & ICS932S401_MN_ENABLED) + freq = calculate_pci_freq(data); + else + freq = 33333; + + return sprintf(buf, "%d\n", freq); +} + +static ssize_t show_value(struct device *dev, + struct device_attribute *devattr, + char *buf); + +static ssize_t show_spread(struct device *dev, + struct device_attribute *devattr, + char *buf); + +static DEVICE_ATTR(spread_enabled, S_IRUGO, show_spread_enabled, NULL); +static DEVICE_ATTR(cpu_clock_selection, S_IRUGO, show_cpu_clock_sel, NULL); +static DEVICE_ATTR(cpu_clock, S_IRUGO, show_cpu_clock, NULL); +static DEVICE_ATTR(src_clock_selection, S_IRUGO, show_src_clock_sel, NULL); +static DEVICE_ATTR(src_clock, S_IRUGO, show_src_clock, NULL); +static DEVICE_ATTR(pci_clock_selection, S_IRUGO, show_pci_clock_sel, NULL); +static DEVICE_ATTR(pci_clock, S_IRUGO, show_pci_clock, NULL); +static DEVICE_ATTR(usb_clock, S_IRUGO, show_value, NULL); +static DEVICE_ATTR(ref_clock, S_IRUGO, show_value, NULL); +static DEVICE_ATTR(cpu_spread, S_IRUGO, show_spread, NULL); +static DEVICE_ATTR(src_spread, S_IRUGO, show_spread, NULL); + +static struct attribute *ics932s401_attr[] = +{ + &dev_attr_spread_enabled.attr, + &dev_attr_cpu_clock_selection.attr, + &dev_attr_cpu_clock.attr, + &dev_attr_src_clock_selection.attr, + &dev_attr_src_clock.attr, + &dev_attr_pci_clock_selection.attr, + &dev_attr_pci_clock.attr, + &dev_attr_usb_clock.attr, + &dev_attr_ref_clock.attr, + &dev_attr_cpu_spread.attr, + &dev_attr_src_spread.attr, + NULL +}; + +static ssize_t show_value(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + int x; + + if (devattr == &dev_attr_usb_clock) + x = 48000; + else if (devattr == &dev_attr_ref_clock) + x = BASE_CLOCK; + else + BUG(); + + return sprintf(buf, "%d\n", x); +} + +static ssize_t show_spread(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct ics932s401_data *data = ics932s401_update_device(dev); + int reg; + unsigned long val; + + if (!(data->regs[ICS932S401_REG_CFG2] & ICS932S401_CFG1_SPREAD)) + return sprintf(buf, "0%%\n"); + + if (devattr == &dev_attr_src_spread) + reg = ICS932S401_REG_SRC_SPREAD1; + else if (devattr == &dev_attr_cpu_spread) + reg = ICS932S401_REG_CPU_SPREAD1; + else + BUG(); + + val = data->regs[reg] | (data->regs[reg + 1] << 8); + val &= ICS932S401_SPREAD_MASK; + + /* Scale 0..2^14 to -0.5. */ + val = 500000 * val / 16384; + return sprintf(buf, "-0.%lu%%\n", val); +} + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int ics932s401_detect(struct i2c_client *client, int kind, + struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = client->adapter; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + if (kind <= 0) { + int vendor, device, revision; + + vendor = i2c_smbus_read_word_data(client, + ICS932S401_REG_VENDOR_REV); + vendor >>= 8; + revision = vendor >> ICS932S401_REV_SHIFT; + vendor &= ICS932S401_VENDOR_MASK; + if (vendor != ICS932S401_VENDOR) + return -ENODEV; + + device = i2c_smbus_read_word_data(client, + ICS932S401_REG_DEVICE); + device >>= 8; + if (device != ICS932S401_DEVICE) + return -ENODEV; + + if (revision != ICS932S401_REV) + dev_info(&adapter->dev, "Unknown revision %d\n", + revision); + } else + dev_dbg(&adapter->dev, "detection forced\n"); + + strlcpy(info->type, "ics932s401", I2C_NAME_SIZE); + + return 0; +} + +static int ics932s401_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ics932s401_data *data; + int err; + + data = kzalloc(sizeof(struct ics932s401_data), GFP_KERNEL); + if (!data) { + err = -ENOMEM; + goto exit; + } + + i2c_set_clientdata(client, data); + mutex_init(&data->lock); + + dev_info(&client->dev, "%s chip found\n", client->name); + + /* Register sysfs hooks */ + data->attrs.attrs = ics932s401_attr; + err = sysfs_create_group(&client->dev.kobj, &data->attrs); + if (err) + goto exit_free; + + return 0; + +exit_free: + kfree(data); +exit: + return err; +} + +static int ics932s401_remove(struct i2c_client *client) +{ + struct ics932s401_data *data = i2c_get_clientdata(client); + + sysfs_remove_group(&client->dev.kobj, &data->attrs); + kfree(data); + return 0; +} + +static int __init ics932s401_init(void) +{ + return i2c_add_driver(&ics932s401_driver); +} + +static void __exit ics932s401_exit(void) +{ + i2c_del_driver(&ics932s401_driver); +} + +MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>"); +MODULE_DESCRIPTION("ICS932S401 driver"); +MODULE_LICENSE("GPL"); + +module_init(ics932s401_init); +module_exit(ics932s401_exit); + +/* IBM IntelliStation Z30 */ +MODULE_ALIAS("dmi:bvnIBM:*:rn9228:*"); +MODULE_ALIAS("dmi:bvnIBM:*:rn9232:*"); + +/* IBM x3650/x3550 */ +MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3650*"); +MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3550*"); diff --git a/drivers/misc/intel_menlow.c b/drivers/misc/intel_menlow.c index e00a2756e97e..27b7662955bb 100644 --- a/drivers/misc/intel_menlow.c +++ b/drivers/misc/intel_menlow.c @@ -52,6 +52,11 @@ MODULE_LICENSE("GPL"); #define MEMORY_ARG_CUR_BANDWIDTH 1 #define MEMORY_ARG_MAX_BANDWIDTH 0 +/* + * GTHS returning 'n' would mean that [0,n-1] states are supported + * In that case max_cstate would be n-1 + * GTHS returning '0' would mean that no bandwidth control states are supported + */ static int memory_get_int_max_bandwidth(struct thermal_cooling_device *cdev, unsigned long *max_state) { @@ -71,6 +76,9 @@ static int memory_get_int_max_bandwidth(struct thermal_cooling_device *cdev, if (ACPI_FAILURE(status)) return -EFAULT; + if (!value) + return -EINVAL; + *max_state = value - 1; return 0; } @@ -121,7 +129,7 @@ static int memory_set_cur_bandwidth(struct thermal_cooling_device *cdev, if (memory_get_int_max_bandwidth(cdev, &max_state)) return -EFAULT; - if (max_state < 0 || state > max_state) + if (state > max_state) return -EINVAL; arg_list.count = 1; diff --git a/drivers/misc/msi-laptop.c b/drivers/misc/msi-laptop.c index de898c6938f3..759763d18e4c 100644 --- a/drivers/misc/msi-laptop.c +++ b/drivers/misc/msi-laptop.c @@ -347,12 +347,16 @@ static int __init msi_init(void) /* Register backlight stuff */ - msibl_device = backlight_device_register("msi-laptop-bl", NULL, NULL, - &msibl_ops); - if (IS_ERR(msibl_device)) - return PTR_ERR(msibl_device); - - msibl_device->props.max_brightness = MSI_LCD_LEVEL_MAX-1; + if (acpi_video_backlight_support()) { + printk(KERN_INFO "MSI: Brightness ignored, must be controlled " + "by ACPI video driver\n"); + } else { + msibl_device = backlight_device_register("msi-laptop-bl", NULL, + NULL, &msibl_ops); + if (IS_ERR(msibl_device)) + return PTR_ERR(msibl_device); + msibl_device->props.max_brightness = MSI_LCD_LEVEL_MAX-1; + } ret = platform_driver_register(&msipf_driver); if (ret) diff --git a/drivers/misc/sgi-gru/Makefile b/drivers/misc/sgi-gru/Makefile index d03597a521b0..9e9170b3599a 100644 --- a/drivers/misc/sgi-gru/Makefile +++ b/drivers/misc/sgi-gru/Makefile @@ -1,3 +1,7 @@ +ifdef CONFIG_SGI_GRU_DEBUG + EXTRA_CFLAGS += -DDEBUG +endif + obj-$(CONFIG_SGI_GRU) := gru.o gru-y := grufile.o grumain.o grufault.o grutlbpurge.o gruprocfs.o grukservices.o diff --git a/drivers/misc/sgi-gru/grumain.c b/drivers/misc/sgi-gru/grumain.c index e11e1ac50900..3d2fc216bae5 100644 --- a/drivers/misc/sgi-gru/grumain.c +++ b/drivers/misc/sgi-gru/grumain.c @@ -29,7 +29,7 @@ static struct device_driver gru_driver = { }; static struct device gru_device = { - .bus_id = {0}, + .init_name = "", .driver = &gru_driver, }; diff --git a/drivers/misc/sgi-gru/gruprocfs.c b/drivers/misc/sgi-gru/gruprocfs.c index 533923f83f1a..73b0ca061bb5 100644 --- a/drivers/misc/sgi-gru/gruprocfs.c +++ b/drivers/misc/sgi-gru/gruprocfs.c @@ -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++) diff --git a/drivers/misc/sgi-xp/xp.h b/drivers/misc/sgi-xp/xp.h index ed1722e50049..7b4cbd5e03e9 100644 --- a/drivers/misc/sgi-xp/xp.h +++ b/drivers/misc/sgi-xp/xp.h @@ -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 *); diff --git a/drivers/misc/sgi-xp/xp_main.c b/drivers/misc/sgi-xp/xp_main.c index 66a1d19e08ad..16f8dcab2da4 100644 --- a/drivers/misc/sgi-xp/xp_main.c +++ b/drivers/misc/sgi-xp/xp_main.c @@ -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. diff --git a/drivers/misc/sgi-xp/xp_sn2.c b/drivers/misc/sgi-xp/xp_sn2.c index 1440134caf31..fb3ec9d735a9 100644 --- a/drivers/misc/sgi-xp/xp_sn2.c +++ b/drivers/misc/sgi-xp/xp_sn2.c @@ -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(); } diff --git a/drivers/misc/sgi-xp/xp_uv.c b/drivers/misc/sgi-xp/xp_uv.c index d9f7ce2510bc..d238576b26fa 100644 --- a/drivers/misc/sgi-xp/xp_uv.c +++ b/drivers/misc/sgi-xp/xp_uv.c @@ -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; } diff --git a/drivers/misc/sgi-xp/xpc.h b/drivers/misc/sgi-xp/xpc.h index 619208d61862..a5bd658c2e83 100644 --- a/drivers/misc/sgi-xp/xpc.h +++ b/drivers/misc/sgi-xp/xpc.h @@ -181,6 +181,18 @@ struct xpc_vars_part_sn2 { 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. */ diff --git a/drivers/misc/sgi-xp/xpc_main.c b/drivers/misc/sgi-xp/xpc_main.c index e8d5cfbd32c2..89218f7cfaa7 100644 --- a/drivers/misc/sgi-xp/xpc_main.c +++ b/drivers/misc/sgi-xp/xpc_main.c @@ -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()) { /* diff --git a/drivers/misc/sgi-xp/xpc_sn2.c b/drivers/misc/sgi-xp/xpc_sn2.c index b4882ccf6344..73b7fb8de47a 100644 --- a/drivers/misc/sgi-xp/xpc_sn2.c +++ b/drivers/misc/sgi-xp/xpc_sn2.c @@ -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; } /* diff --git a/drivers/misc/sgi-xp/xpc_uv.c b/drivers/misc/sgi-xp/xpc_uv.c index 1ac694c01623..684b2dd17583 100644 --- a/drivers/misc/sgi-xp/xpc_uv.c +++ b/drivers/misc/sgi-xp/xpc_uv.c @@ -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_NOTIFY_MSG_SIZE_UV (2 * 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_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) +#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" -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, +static int +xpc_get_gru_mq_irq_uv(struct xpc_gru_mq_uv *mq, int cpu, char *irq_name) +{ +#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); + } + +#elif defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV + int mmr_pnode; + unsigned long mmr_value; + + 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; + + mmr_pnode = uv_blade_to_pnode(mq->mmr_blade); + mmr_value = (unsigned long)cpu_physical_id(cpu) << 32 | mq->irq; + + uv_write_global_mmr64(mmr_pnode, mq->mmr_offset, mmr_value); +#else + #error not a supported configuration +#endif + + return 0; +} + +static void +xpc_release_gru_mq_irq_uv(struct xpc_gru_mq_uv *mq) +{ +#if defined CONFIG_X86_64 + uv_teardown_irq(mq->irq, mq->mmr_blade, mq->mmr_offset); + +#elif defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV + int mmr_pnode; + unsigned long mmr_value; + + mmr_pnode = uv_blade_to_pnode(mq->mmr_blade); + mmr_value = 1UL << 16; + + 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, 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, 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 mq_order; + int pg_order; struct page *page; - void *mq; + 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(cpuid); - mq_order = get_order(mq_size); + nid = cpu_to_node(cpu); page = alloc_pages_node(nid, GFP_KERNEL | __GFP_ZERO | GFP_THISNODE, - mq_order); + 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); - return NULL; + ret = -ENOMEM; + goto out_2; } + mq->address = page_address(page); - mq = page_address(page); - ret = gru_create_message_queue(mq, mq_size); + 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); - free_pages((unsigned long)mq, mq_order); - return NULL; + ret = -EINVAL; + goto out_3; } - /* !!! Need to do some other things to set up IRQ */ + /* 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 = request_irq(irq, irq_handler, 0, "xpc", NULL); + 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", - irq, ret); - free_pages((unsigned long)mq, mq_order); - return NULL; + mq->irq, ret); + goto out_5; } - /* !!! enable generation of irq when GRU mq op occurs to this mq */ - - /* ??? allow other partitions to access GRU mq? */ + /* 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(void *mq, unsigned int mq_size, unsigned int irq) +xpc_destroy_gru_mq_uv(struct xpc_gru_mq_uv *mq) { - /* ??? disallow other partitions to access GRU 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); - /* !!! disable generation of irq when GRU mq op occurs to this mq */ + /* unregister irq handler and release mq irq/vector mapping */ + free_irq(mq->irq, NULL); + xpc_release_gru_mq_irq_uv(mq); - free_irq(irq, NULL); + /* disable generation of irq when GRU mq op occurs to this mq */ + xpc_gru_mq_watchlist_free_uv(mq); - free_pages((unsigned long)mq, get_order(mq_size)); + 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); } diff --git a/drivers/misc/sgi-xp/xpnet.c b/drivers/misc/sgi-xp/xpnet.c index 71513b3af708..234d7be6d812 100644 --- a/drivers/misc/sgi-xp/xpnet.c +++ b/drivers/misc/sgi-xp/xpnet.c @@ -138,7 +138,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 }; diff --git a/drivers/misc/sony-laptop.c b/drivers/misc/sony-laptop.c index 06f07e19dc70..571b211608d1 100644 --- a/drivers/misc/sony-laptop.c +++ b/drivers/misc/sony-laptop.c @@ -1038,7 +1038,11 @@ static int sony_nc_add(struct acpi_device *device) goto outinput; } - if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT", &handle))) { + if (acpi_video_backlight_support()) { + printk(KERN_INFO DRV_PFX "brightness ignored, must be " + "controlled by ACPI video driver\n"); + } else if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT", + &handle))) { sony_backlight_device = backlight_device_register("sony", NULL, NULL, &sony_backlight_ops); diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index 4db1cf9078d9..899766e16fa8 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c @@ -4932,16 +4932,25 @@ static int __init brightness_init(struct ibm_init_struct *iibm) */ b = tpacpi_check_std_acpi_brightness_support(); if (b > 0) { - if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO) { - printk(TPACPI_NOTICE - "Lenovo BIOS switched to ACPI backlight " - "control mode\n"); - } - if (brightness_enable > 1) { - printk(TPACPI_NOTICE - "standard ACPI backlight interface " - "available, not loading native one...\n"); - return 1; + + if (acpi_video_backlight_support()) { + if (brightness_enable > 1) { + printk(TPACPI_NOTICE + "Standard ACPI backlight interface " + "available, not loading native one.\n"); + return 1; + } else if (brightness_enable == 1) { + printk(TPACPI_NOTICE + "Backlight control force enabled, even if standard " + "ACPI backlight interface is available\n"); + } + } else { + if (brightness_enable > 1) { + printk(TPACPI_NOTICE + "Standard ACPI backlight interface not " + "available, thinkpad_acpi native " + "brightness control enabled\n"); + } } } @@ -5309,6 +5318,7 @@ static enum fan_control_commands fan_control_commands; static u8 fan_control_initial_status; static u8 fan_control_desired_level; +static u8 fan_control_resume_level; static int fan_watchdog_maxinterval; static struct mutex fan_mutex; @@ -5431,8 +5441,8 @@ static int fan_set_level(int level) case TPACPI_FAN_WR_ACPI_FANS: case TPACPI_FAN_WR_TPEC: - if ((level != TP_EC_FAN_AUTO) && - (level != TP_EC_FAN_FULLSPEED) && + if (!(level & TP_EC_FAN_AUTO) && + !(level & TP_EC_FAN_FULLSPEED) && ((level < 0) || (level > 7))) return -EINVAL; @@ -5996,38 +6006,67 @@ static void fan_exit(void) static void fan_suspend(pm_message_t state) { + int rc; + if (!fan_control_allowed) return; /* Store fan status in cache */ - fan_get_status_safe(NULL); + fan_control_resume_level = 0; + rc = fan_get_status_safe(&fan_control_resume_level); + if (rc < 0) + printk(TPACPI_NOTICE + "failed to read fan level for later " + "restore during resume: %d\n", rc); + + /* if it is undefined, don't attempt to restore it. + * KEEP THIS LAST */ if (tp_features.fan_ctrl_status_undef) - fan_control_desired_level = TP_EC_FAN_AUTO; + fan_control_resume_level = 0; } static void fan_resume(void) { - u8 saved_fan_level; u8 current_level = 7; bool do_set = false; + int rc; /* DSDT *always* updates status on resume */ tp_features.fan_ctrl_status_undef = 0; - saved_fan_level = fan_control_desired_level; if (!fan_control_allowed || + !fan_control_resume_level || (fan_get_status_safe(¤t_level) < 0)) return; switch (fan_control_access_mode) { case TPACPI_FAN_WR_ACPI_SFAN: - do_set = (saved_fan_level > current_level); + /* never decrease fan level */ + do_set = (fan_control_resume_level > current_level); break; case TPACPI_FAN_WR_ACPI_FANS: case TPACPI_FAN_WR_TPEC: - do_set = ((saved_fan_level & TP_EC_FAN_FULLSPEED) || - (saved_fan_level == 7 && - !(current_level & TP_EC_FAN_FULLSPEED))); + /* never decrease fan level, scale is: + * TP_EC_FAN_FULLSPEED > 7 >= TP_EC_FAN_AUTO + * + * We expect the firmware to set either 7 or AUTO, but we + * handle FULLSPEED out of paranoia. + * + * So, we can safely only restore FULLSPEED or 7, anything + * else could slow the fan. Restoring AUTO is useless, at + * best that's exactly what the DSDT already set (it is the + * slower it uses). + * + * Always keep in mind that the DSDT *will* have set the + * fans to what the vendor supposes is the best level. We + * muck with it only to speed the fan up. + */ + if (fan_control_resume_level != 7 && + !(fan_control_resume_level & TP_EC_FAN_FULLSPEED)) + return; + else + do_set = !(current_level & TP_EC_FAN_FULLSPEED) && + (current_level != fan_control_resume_level); break; default: return; @@ -6035,8 +6074,11 @@ static void fan_resume(void) if (do_set) { printk(TPACPI_NOTICE "restoring fan level to 0x%02x\n", - saved_fan_level); - fan_set_level_safe(saved_fan_level); + fan_control_resume_level); + rc = fan_set_level_safe(fan_control_resume_level); + if (rc < 0) + printk(TPACPI_NOTICE + "failed to restore fan level: %d\n", rc); } } diff --git a/drivers/misc/tifm_7xx1.c b/drivers/misc/tifm_7xx1.c index 67503ea71d21..e71eba31decb 100644 --- a/drivers/misc/tifm_7xx1.c +++ b/drivers/misc/tifm_7xx1.c @@ -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); diff --git a/drivers/misc/tifm_core.c b/drivers/misc/tifm_core.c index 82dc72a1484f..98bcba521da2 100644 --- a/drivers/misc/tifm_core.c +++ b/drivers/misc/tifm_core.c @@ -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); diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index 0d9b2d6f9ebf..f210a8ee6861 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -216,8 +216,7 @@ int mmc_add_card(struct mmc_card *card) int ret; const char *type; - snprintf(card->dev.bus_id, sizeof(card->dev.bus_id), - "%s:%04x", mmc_hostname(card->host), card->rca); + dev_set_name(&card->dev, "%s:%04x", mmc_hostname(card->host), card->rca); switch (card->type) { case MMC_TYPE_MMC: diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 044d84eeed7c..f7284b905eb3 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -280,7 +280,11 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card) (card->host->ios.clock / 1000); if (data->flags & MMC_DATA_WRITE) - limit_us = 250000; + /* + * The limit is really 250 ms, but that is + * insufficient for some crappy cards. + */ + limit_us = 300000; else limit_us = 100000; diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 6da80fd4d974..5e945e64ead7 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -73,8 +73,7 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) if (err) goto free; - snprintf(host->class_dev.bus_id, BUS_ID_SIZE, - "mmc%d", host->index); + dev_set_name(&host->class_dev, "mmc%d", host->index); host->parent = dev; host->class_dev.parent = dev; @@ -121,7 +120,7 @@ int mmc_add_host(struct mmc_host *host) WARN_ON((host->caps & MMC_CAP_SDIO_IRQ) && !host->ops->enable_sdio_irq); - led_trigger_register_simple(host->class_dev.bus_id, &host->led); + led_trigger_register_simple(dev_name(&host->class_dev), &host->led); err = device_add(&host->class_dev); if (err) diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c index 233d0f9b3c4b..46284b527397 100644 --- a/drivers/mmc/core/sdio_bus.c +++ b/drivers/mmc/core/sdio_bus.c @@ -239,8 +239,7 @@ int sdio_add_func(struct sdio_func *func) { int ret; - snprintf(func->dev.bus_id, sizeof(func->dev.bus_id), - "%s:%d", mmc_card_id(func->card), func->num); + dev_set_name(&func->dev, "%s:%d", mmc_card_id(func->card), func->num); ret = device_add(&func->dev); if (ret == 0) diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c index 07faf5412a1f..ad00e1632317 100644 --- a/drivers/mmc/host/mmc_spi.c +++ b/drivers/mmc/host/mmc_spi.c @@ -1348,7 +1348,7 @@ static int mmc_spi_probe(struct spi_device *spi) goto fail_add_host; dev_info(&spi->dev, "SD/MMC host %s%s%s%s%s\n", - mmc->class_dev.bus_id, + dev_name(&mmc->class_dev), host->dma_dev ? "" : ", no DMA", (host->pdata && host->pdata->get_ro) ? "" : ", no WP", diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 2fadf323c696..1bcbdd6763ac 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -500,7 +500,7 @@ static int mmci_probe(struct amba_device *dev, void *id) } host = mmc_priv(mmc); - host->clk = clk_get(&dev->dev, "MCLK"); + host->clk = clk_get(&dev->dev, NULL); if (IS_ERR(host->clk)) { ret = PTR_ERR(host->clk); host->clk = NULL; diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index ebfaa9960939..f88cc7406354 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -26,11 +26,12 @@ #include <linux/clk.h> #include <linux/err.h> #include <linux/mmc/host.h> +#include <linux/io.h> -#include <asm/dma.h> -#include <asm/io.h> #include <asm/sizes.h> +#include <mach/dma.h> +#include <mach/hardware.h> #include <mach/pxa-regs.h> #include <mach/mmc.h> @@ -533,7 +534,7 @@ static int pxamci_probe(struct platform_device *pdev) host->pdata = pdev->dev.platform_data; host->clkrt = CLKRT_OFF; - host->clk = clk_get(&pdev->dev, "MMCCLK"); + host->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(host->clk)) { ret = PTR_ERR(host->clk); host->clk = NULL; diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c index 3b2085b57769..fcc98a4cce3c 100644 --- a/drivers/mmc/host/s3cmci.c +++ b/drivers/mmc/host/s3cmci.c @@ -25,7 +25,7 @@ #include <mach/regs-sdi.h> #include <mach/regs-gpio.h> -#include <asm/plat-s3c24xx/mci.h> +#include <plat/mci.h> #include "s3cmci.h" diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 30f64b1f2354..4d010a984bed 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1733,7 +1733,7 @@ int sdhci_add_host(struct sdhci_host *host) mmc_add_host(mmc); printk(KERN_INFO "%s: SDHCI controller on %s [%s] using %s%s\n", - mmc_hostname(mmc), host->hw_name, mmc_dev(mmc)->bus_id, + mmc_hostname(mmc), host->hw_name, dev_name(mmc_dev(mmc)), (host->flags & SDHCI_USE_ADMA)?"A":"", (host->flags & SDHCI_USE_DMA)?"DMA":"PIO"); diff --git a/drivers/mmc/host/tifm_sd.c b/drivers/mmc/host/tifm_sd.c index 13844843e8de..82554ddec6b3 100644 --- a/drivers/mmc/host/tifm_sd.c +++ b/drivers/mmc/host/tifm_sd.c @@ -632,7 +632,7 @@ static void tifm_sd_request(struct mmc_host *mmc, struct mmc_request *mrq) if (host->req) { printk(KERN_ERR "%s : unfinished request detected\n", - sock->dev.bus_id); + dev_name(&sock->dev)); mrq->cmd->error = -ETIMEDOUT; goto err_out; } @@ -672,7 +672,7 @@ static void tifm_sd_request(struct mmc_host *mmc, struct mmc_request *mrq) ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE)) { printk(KERN_ERR "%s : scatterlist map failed\n", - sock->dev.bus_id); + dev_name(&sock->dev)); mrq->cmd->error = -ENOMEM; goto err_out; } @@ -684,7 +684,7 @@ static void tifm_sd_request(struct mmc_host *mmc, struct mmc_request *mrq) : PCI_DMA_FROMDEVICE); if (host->sg_len < 1) { printk(KERN_ERR "%s : scatterlist map failed\n", - sock->dev.bus_id); + dev_name(&sock->dev)); tifm_unmap_sg(sock, &host->bounce_buf, 1, r_data->flags & MMC_DATA_WRITE ? PCI_DMA_TODEVICE @@ -748,7 +748,7 @@ static void tifm_sd_end_cmd(unsigned long data) if (!mrq) { printk(KERN_ERR " %s : no request to complete?\n", - sock->dev.bus_id); + dev_name(&sock->dev)); spin_unlock_irqrestore(&sock->lock, flags); return; } @@ -789,7 +789,7 @@ static void tifm_sd_abort(unsigned long data) printk(KERN_ERR "%s : card failed to respond for a long period of time " "(%x, %x)\n", - host->dev->dev.bus_id, host->req->cmd->opcode, host->cmd_flags); + dev_name(&host->dev->dev), host->req->cmd->opcode, host->cmd_flags); tifm_eject(host->dev); } @@ -906,7 +906,7 @@ static int tifm_sd_initialize_host(struct tifm_sd *host) if (rc) { printk(KERN_ERR "%s : controller failed to reset\n", - sock->dev.bus_id); + dev_name(&sock->dev)); return -ENODEV; } @@ -933,7 +933,7 @@ static int tifm_sd_initialize_host(struct tifm_sd *host) if (rc) { printk(KERN_ERR "%s : card not ready - probe failed on initialization\n", - sock->dev.bus_id); + dev_name(&sock->dev)); return -ENODEV; } @@ -954,7 +954,7 @@ static int tifm_sd_probe(struct tifm_dev *sock) if (!(TIFM_SOCK_STATE_OCCUPIED & readl(sock->addr + SOCK_PRESENT_STATE))) { printk(KERN_WARNING "%s : card gone, unexpectedly\n", - sock->dev.bus_id); + dev_name(&sock->dev)); return rc; } diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 3e6f5d8609e8..d74ec46aa032 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -406,19 +406,6 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) /* Set the default CFI lock/unlock addresses */ cfi->addr_unlock1 = 0x555; cfi->addr_unlock2 = 0x2aa; - /* Modify the unlock address if we are in compatibility mode */ - if ( /* x16 in x8 mode */ - ((cfi->device_type == CFI_DEVICETYPE_X8) && - (cfi->cfiq->InterfaceDesc == - CFI_INTERFACE_X8_BY_X16_ASYNC)) || - /* x32 in x16 mode */ - ((cfi->device_type == CFI_DEVICETYPE_X16) && - (cfi->cfiq->InterfaceDesc == - CFI_INTERFACE_X16_BY_X32_ASYNC))) - { - cfi->addr_unlock1 = 0xaaa; - cfi->addr_unlock2 = 0x555; - } } /* CFI mode */ else if (cfi->cfi_mode == CFI_MODE_JEDEC) { diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c index f84ab6182148..2f3f2f719ba4 100644 --- a/drivers/mtd/chips/jedec_probe.c +++ b/drivers/mtd/chips/jedec_probe.c @@ -1808,9 +1808,7 @@ static inline u32 jedec_read_mfr(struct map_info *map, uint32_t base, * several first banks can contain 0x7f instead of actual ID */ do { - uint32_t ofs = cfi_build_cmd_addr(0 + (bank << 8), - cfi_interleave(cfi), - cfi->device_type); + uint32_t ofs = cfi_build_cmd_addr(0 + (bank << 8), map, cfi); mask = (1 << (cfi->device_type * 8)) - 1; result = map_read(map, base + ofs); bank++; @@ -1824,7 +1822,7 @@ static inline u32 jedec_read_id(struct map_info *map, uint32_t base, { map_word result; unsigned long mask; - u32 ofs = cfi_build_cmd_addr(1, cfi_interleave(cfi), cfi->device_type); + u32 ofs = cfi_build_cmd_addr(1, map, cfi); mask = (1 << (cfi->device_type * 8)) -1; result = map_read(map, base + ofs); return result.x[0] & mask; @@ -2067,8 +2065,8 @@ static int jedec_probe_chip(struct map_info *map, __u32 base, } /* Ensure the unlock addresses we try stay inside the map */ - probe_offset1 = cfi_build_cmd_addr(cfi->addr_unlock1, cfi_interleave(cfi), cfi->device_type); - probe_offset2 = cfi_build_cmd_addr(cfi->addr_unlock2, cfi_interleave(cfi), cfi->device_type); + probe_offset1 = cfi_build_cmd_addr(cfi->addr_unlock1, map, cfi); + probe_offset2 = cfi_build_cmd_addr(cfi->addr_unlock2, map, cfi); if ( ((base + probe_offset1 + map_bankwidth(map)) >= map->size) || ((base + probe_offset2 + map_bankwidth(map)) >= map->size)) goto retry; diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 76a76751da36..798ae4c8ef31 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -170,7 +170,7 @@ static int wait_till_ready(struct m25p *flash) static int erase_block(struct m25p *flash) { DEBUG(MTD_DEBUG_LEVEL3, "%s: %s %dKiB\n", - flash->spi->dev.bus_id, __func__, + dev_name(&flash->spi->dev), __func__, flash->mtd.size / 1024); /* Wait until finished previous write command. */ @@ -197,7 +197,7 @@ static int erase_block(struct m25p *flash) static int erase_sector(struct m25p *flash, u32 offset) { DEBUG(MTD_DEBUG_LEVEL3, "%s: %s %dKiB at 0x%08x\n", - flash->spi->dev.bus_id, __func__, + dev_name(&flash->spi->dev), __func__, flash->mtd.erasesize / 1024, offset); /* Wait until finished previous write command. */ @@ -234,7 +234,7 @@ static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr) u32 addr,len; DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %d\n", - flash->spi->dev.bus_id, __func__, "at", + dev_name(&flash->spi->dev), __func__, "at", (u32)instr->addr, instr->len); /* sanity checks */ @@ -292,7 +292,7 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, struct spi_message m; DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %zd\n", - flash->spi->dev.bus_id, __func__, "from", + dev_name(&flash->spi->dev), __func__, "from", (u32)from, len); /* sanity checks */ @@ -364,7 +364,7 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, struct spi_message m; DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %zd\n", - flash->spi->dev.bus_id, __func__, "to", + dev_name(&flash->spi->dev), __func__, "to", (u32)to, len); if (retlen) @@ -560,7 +560,7 @@ static struct flash_info *__devinit jedec_probe(struct spi_device *spi) tmp = spi_write_then_read(spi, &code, 1, id, 5); if (tmp < 0) { DEBUG(MTD_DEBUG_LEVEL0, "%s: error %d reading JEDEC ID\n", - spi->dev.bus_id, tmp); + dev_name(&spi->dev), tmp); return NULL; } jedec = id[0]; @@ -613,7 +613,7 @@ static int __devinit m25p_probe(struct spi_device *spi) /* unrecognized chip? */ if (i == ARRAY_SIZE(m25p_data)) { DEBUG(MTD_DEBUG_LEVEL0, "%s: unrecognized id %s\n", - spi->dev.bus_id, data->type); + dev_name(&spi->dev), data->type); info = NULL; /* recognized; is that chip really what's there? */ @@ -654,7 +654,7 @@ static int __devinit m25p_probe(struct spi_device *spi) if (data && data->name) flash->mtd.name = data->name; else - flash->mtd.name = spi->dev.bus_id; + flash->mtd.name = dev_name(&spi->dev); flash->mtd.type = MTD_NORFLASH; flash->mtd.writesize = 1; diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c index 6dd9aff8bb2d..65126cd668ff 100644 --- a/drivers/mtd/devices/mtd_dataflash.c +++ b/drivers/mtd/devices/mtd_dataflash.c @@ -128,7 +128,7 @@ static int dataflash_waitready(struct spi_device *spi) status = dataflash_status(spi); if (status < 0) { DEBUG(MTD_DEBUG_LEVEL1, "%s: status %d?\n", - spi->dev.bus_id, status); + dev_name(&spi->dev), status); status = 0; } @@ -154,7 +154,7 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr) uint8_t *command; DEBUG(MTD_DEBUG_LEVEL2, "%s: erase addr=0x%x len 0x%x\n", - spi->dev.bus_id, + dev_name(&spi->dev), instr->addr, instr->len); /* Sanity checks */ @@ -197,7 +197,7 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr) if (status < 0) { printk(KERN_ERR "%s: erase %x, err %d\n", - spi->dev.bus_id, pageaddr, status); + dev_name(&spi->dev), pageaddr, status); /* REVISIT: can retry instr->retries times; or * giveup and instr->fail_addr = instr->addr; */ @@ -239,7 +239,7 @@ static int dataflash_read(struct mtd_info *mtd, loff_t from, size_t len, int status; DEBUG(MTD_DEBUG_LEVEL2, "%s: read 0x%x..0x%x\n", - priv->spi->dev.bus_id, (unsigned)from, (unsigned)(from + len)); + dev_name(&priv->spi->dev), (unsigned)from, (unsigned)(from + len)); *retlen = 0; @@ -288,7 +288,7 @@ static int dataflash_read(struct mtd_info *mtd, loff_t from, size_t len, status = 0; } else DEBUG(MTD_DEBUG_LEVEL1, "%s: read %x..%x --> %d\n", - priv->spi->dev.bus_id, + dev_name(&priv->spi->dev), (unsigned)from, (unsigned)(from + len), status); return status; @@ -315,7 +315,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len, uint8_t *command; DEBUG(MTD_DEBUG_LEVEL2, "%s: write 0x%x..0x%x\n", - spi->dev.bus_id, (unsigned)to, (unsigned)(to + len)); + dev_name(&spi->dev), (unsigned)to, (unsigned)(to + len)); *retlen = 0; @@ -374,7 +374,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len, status = spi_sync(spi, &msg); if (status < 0) DEBUG(MTD_DEBUG_LEVEL1, "%s: xfer %u -> %d \n", - spi->dev.bus_id, addr, status); + dev_name(&spi->dev), addr, status); (void) dataflash_waitready(priv->spi); } @@ -396,7 +396,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len, spi_transfer_del(x + 1); if (status < 0) DEBUG(MTD_DEBUG_LEVEL1, "%s: pgm %u/%u -> %d \n", - spi->dev.bus_id, addr, writelen, status); + dev_name(&spi->dev), addr, writelen, status); (void) dataflash_waitready(priv->spi); @@ -416,14 +416,14 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len, status = spi_sync(spi, &msg); if (status < 0) DEBUG(MTD_DEBUG_LEVEL1, "%s: compare %u -> %d \n", - spi->dev.bus_id, addr, status); + dev_name(&spi->dev), addr, status); status = dataflash_waitready(priv->spi); /* Check result of the compare operation */ if (status & (1 << 6)) { printk(KERN_ERR "%s: compare page %u, err %d\n", - spi->dev.bus_id, pageaddr, status); + dev_name(&spi->dev), pageaddr, status); remaining = 0; status = -EIO; break; @@ -779,7 +779,7 @@ static struct flash_info *__devinit jedec_probe(struct spi_device *spi) tmp = spi_write_then_read(spi, &code, 1, id, 3); if (tmp < 0) { DEBUG(MTD_DEBUG_LEVEL0, "%s: error %d reading JEDEC ID\n", - spi->dev.bus_id, tmp); + dev_name(&spi->dev), tmp); return ERR_PTR(tmp); } if (id[0] != 0x1f) @@ -869,7 +869,7 @@ static int __devinit dataflash_probe(struct spi_device *spi) status = dataflash_status(spi); if (status <= 0 || status == 0xff) { DEBUG(MTD_DEBUG_LEVEL1, "%s: status error %d\n", - spi->dev.bus_id, status); + dev_name(&spi->dev), status); if (status == 0 || status == 0xff) status = -ENODEV; return status; @@ -905,13 +905,13 @@ static int __devinit dataflash_probe(struct spi_device *spi) /* obsolete AT45DB1282 not (yet?) supported */ default: DEBUG(MTD_DEBUG_LEVEL1, "%s: unsupported device (%x)\n", - spi->dev.bus_id, status & 0x3c); + dev_name(&spi->dev), status & 0x3c); status = -ENODEV; } if (status < 0) DEBUG(MTD_DEBUG_LEVEL1, "%s: add_dataflash --> %d\n", - spi->dev.bus_id, status); + dev_name(&spi->dev), status); return status; } @@ -921,7 +921,7 @@ static int __devexit dataflash_remove(struct spi_device *spi) struct dataflash *flash = dev_get_drvdata(&spi->dev); int status; - DEBUG(MTD_DEBUG_LEVEL1, "%s: remove\n", spi->dev.bus_id); + DEBUG(MTD_DEBUG_LEVEL1, "%s: remove\n", dev_name(&spi->dev)); if (mtd_has_partitions() && flash->partitioned) status = del_mtd_partitions(&flash->mtd); diff --git a/drivers/mtd/maps/cdb89712.c b/drivers/mtd/maps/cdb89712.c index e5059aa3c724..8d92d8db9a98 100644 --- a/drivers/mtd/maps/cdb89712.c +++ b/drivers/mtd/maps/cdb89712.c @@ -14,7 +14,18 @@ #include <linux/mtd/map.h> #include <linux/mtd/partitions.h> - +/* dynamic ioremap() areas */ +#define FLASH_START 0x00000000 +#define FLASH_SIZE 0x800000 +#define FLASH_WIDTH 4 + +#define SRAM_START 0x60000000 +#define SRAM_SIZE 0xc000 +#define SRAM_WIDTH 4 + +#define BOOTROM_START 0x70000000 +#define BOOTROM_SIZE 0x80 +#define BOOTROM_WIDTH 4 static struct mtd_info *flash_mtd; diff --git a/drivers/mtd/maps/h720x-flash.c b/drivers/mtd/maps/h720x-flash.c index 35fef655ccc4..3b959fad1c4e 100644 --- a/drivers/mtd/maps/h720x-flash.c +++ b/drivers/mtd/maps/h720x-flash.c @@ -24,8 +24,8 @@ static struct mtd_info *mymtd; static struct map_info h720x_map = { .name = "H720X", .bankwidth = 4, - .size = FLASH_SIZE, - .phys = FLASH_PHYS, + .size = H720X_FLASH_SIZE, + .phys = H720X_FLASH_PHYS, }; static struct mtd_partition h720x_partitions[] = { @@ -70,7 +70,7 @@ int __init h720x_mtd_init(void) char *part_type = NULL; - h720x_map.virt = ioremap(FLASH_PHYS, FLASH_SIZE); + h720x_map.virt = ioremap(h720x_map.phys, h720x_map.size); if (!h720x_map.virt) { printk(KERN_ERR "H720x-MTD: ioremap failed\n"); diff --git a/drivers/mtd/maps/integrator-flash.c b/drivers/mtd/maps/integrator-flash.c index 7100ee3c7b01..756d0de6637d 100644 --- a/drivers/mtd/maps/integrator-flash.c +++ b/drivers/mtd/maps/integrator-flash.c @@ -35,28 +35,34 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/map.h> #include <linux/mtd/partitions.h> +#include <linux/mtd/concat.h> #include <asm/mach/flash.h> #include <mach/hardware.h> #include <asm/io.h> #include <asm/system.h> -#ifdef CONFIG_ARCH_P720T -#define FLASH_BASE (0x04000000) -#define FLASH_SIZE (64*1024*1024) -#endif +#define SUBDEV_NAME_SIZE (BUS_ID_SIZE + 2) -struct armflash_info { +struct armflash_subdev_info { + char name[SUBDEV_NAME_SIZE]; + struct mtd_info *mtd; + struct map_info map; struct flash_platform_data *plat; +}; + +struct armflash_info { struct resource *res; struct mtd_partition *parts; struct mtd_info *mtd; - struct map_info map; + int nr_subdev; + struct armflash_subdev_info subdev[0]; }; static void armflash_set_vpp(struct map_info *map, int on) { - struct armflash_info *info = container_of(map, struct armflash_info, map); + struct armflash_subdev_info *info = + container_of(map, struct armflash_subdev_info, map); if (info->plat && info->plat->set_vpp) info->plat->set_vpp(on); @@ -64,32 +70,17 @@ static void armflash_set_vpp(struct map_info *map, int on) static const char *probes[] = { "cmdlinepart", "RedBoot", "afs", NULL }; -static int armflash_probe(struct platform_device *dev) +static int armflash_subdev_probe(struct armflash_subdev_info *subdev, + struct resource *res) { - struct flash_platform_data *plat = dev->dev.platform_data; - struct resource *res = dev->resource; - unsigned int size = res->end - res->start + 1; - struct armflash_info *info; - int err; + struct flash_platform_data *plat = subdev->plat; + resource_size_t size = res->end - res->start + 1; void __iomem *base; + int err = 0; - info = kzalloc(sizeof(struct armflash_info), GFP_KERNEL); - if (!info) { - err = -ENOMEM; - goto out; - } - - info->plat = plat; - if (plat && plat->init) { - err = plat->init(); - if (err) - goto no_resource; - } - - info->res = request_mem_region(res->start, size, "armflash"); - if (!info->res) { + if (!request_mem_region(res->start, size, subdev->name)) { err = -EBUSY; - goto no_resource; + goto out; } base = ioremap(res->start, size); @@ -101,27 +92,132 @@ static int armflash_probe(struct platform_device *dev) /* * look for CFI based flash parts fitted to this board */ - info->map.size = size; - info->map.bankwidth = plat->width; - info->map.phys = res->start; - info->map.virt = base; - info->map.name = dev->dev.bus_id; - info->map.set_vpp = armflash_set_vpp; + subdev->map.size = size; + subdev->map.bankwidth = plat->width; + subdev->map.phys = res->start; + subdev->map.virt = base; + subdev->map.name = subdev->name; + subdev->map.set_vpp = armflash_set_vpp; - simple_map_init(&info->map); + simple_map_init(&subdev->map); /* * Also, the CFI layer automatically works out what size * of chips we have, and does the necessary identification * for us automatically. */ - info->mtd = do_map_probe(plat->map_name, &info->map); - if (!info->mtd) { + subdev->mtd = do_map_probe(plat->map_name, &subdev->map); + if (!subdev->mtd) { err = -ENXIO; goto no_device; } - info->mtd->owner = THIS_MODULE; + subdev->mtd->owner = THIS_MODULE; + + /* Successful? */ + if (err == 0) + return err; + + if (subdev->mtd) + map_destroy(subdev->mtd); + no_device: + iounmap(base); + no_mem: + release_mem_region(res->start, size); + out: + return err; +} + +static void armflash_subdev_remove(struct armflash_subdev_info *subdev) +{ + if (subdev->mtd) + map_destroy(subdev->mtd); + if (subdev->map.virt) + iounmap(subdev->map.virt); + release_mem_region(subdev->map.phys, subdev->map.size); +} + +static int armflash_probe(struct platform_device *dev) +{ + struct flash_platform_data *plat = dev->dev.platform_data; + unsigned int size; + struct armflash_info *info; + int i, nr, err; + + /* Count the number of devices */ + for (nr = 0; ; nr++) + if (!platform_get_resource(dev, IORESOURCE_MEM, nr)) + break; + if (nr == 0) { + err = -ENODEV; + goto out; + } + + size = sizeof(struct armflash_info) + + sizeof(struct armflash_subdev_info) * nr; + info = kzalloc(size, GFP_KERNEL); + if (!info) { + err = -ENOMEM; + goto out; + } + + if (plat && plat->init) { + err = plat->init(); + if (err) + goto no_resource; + } + + for (i = 0; i < nr; i++) { + struct armflash_subdev_info *subdev = &info->subdev[i]; + struct resource *res; + + res = platform_get_resource(dev, IORESOURCE_MEM, i); + if (!res) + break; + + if (nr == 1) + /* No MTD concatenation, just use the default name */ + snprintf(subdev->name, SUBDEV_NAME_SIZE, "%s", + dev_name(&dev->dev)); + else + snprintf(subdev->name, SUBDEV_NAME_SIZE, "%s-%d", + dev_name(&dev->dev), i); + subdev->plat = plat; + + err = armflash_subdev_probe(subdev, res); + if (err) + break; + } + info->nr_subdev = i; + + if (err) + goto subdev_err; + + if (info->nr_subdev == 1) + info->mtd = info->subdev[0].mtd; + else if (info->nr_subdev > 1) { +#ifdef CONFIG_MTD_CONCAT + struct mtd_info *cdev[info->nr_subdev]; + + /* + * We detected multiple devices. Concatenate them together. + */ + for (i = 0; i < info->nr_subdev; i++) + cdev[i] = info->subdev[i].mtd; + + info->mtd = mtd_concat_create(cdev, info->nr_subdev, + dev_name(&dev->dev)); + if (info->mtd == NULL) + err = -ENXIO; +#else + printk(KERN_ERR "armflash: multiple devices found but " + "MTD concat support disabled.\n"); + err = -ENXIO; +#endif + } + + if (err < 0) + goto cleanup; err = parse_mtd_partitions(info->mtd, probes, &info->parts, 0); if (err > 0) { @@ -131,28 +227,30 @@ static int armflash_probe(struct platform_device *dev) "mtd partition registration failed: %d\n", err); } - if (err == 0) + if (err == 0) { platform_set_drvdata(dev, info); + return err; + } /* - * If we got an error, free all resources. + * We got an error, free all resources. */ - if (err < 0) { - if (info->mtd) { - del_mtd_partitions(info->mtd); - map_destroy(info->mtd); - } - kfree(info->parts); - - no_device: - iounmap(base); - no_mem: - release_mem_region(res->start, size); - no_resource: - if (plat && plat->exit) - plat->exit(); - kfree(info); + cleanup: + if (info->mtd) { + del_mtd_partitions(info->mtd); +#ifdef CONFIG_MTD_CONCAT + if (info->mtd != info->subdev[0].mtd) + mtd_concat_destroy(info->mtd); +#endif } + kfree(info->parts); + subdev_err: + for (i = info->nr_subdev - 1; i >= 0; i--) + armflash_subdev_remove(&info->subdev[i]); + no_resource: + if (plat && plat->exit) + plat->exit(); + kfree(info); out: return err; } @@ -160,22 +258,26 @@ static int armflash_probe(struct platform_device *dev) static int armflash_remove(struct platform_device *dev) { struct armflash_info *info = platform_get_drvdata(dev); + struct flash_platform_data *plat = dev->dev.platform_data; + int i; platform_set_drvdata(dev, NULL); if (info) { if (info->mtd) { del_mtd_partitions(info->mtd); - map_destroy(info->mtd); +#ifdef CONFIG_MTD_CONCAT + if (info->mtd != info->subdev[0].mtd) + mtd_concat_destroy(info->mtd); +#endif } kfree(info->parts); - iounmap(info->map.virt); - release_resource(info->res); - kfree(info->res); + for (i = info->nr_subdev - 1; i >= 0; i--) + armflash_subdev_remove(&info->subdev[i]); - if (info->plat && info->plat->exit) - info->plat->exit(); + if (plat && plat->exit) + plat->exit(); kfree(info); } diff --git a/drivers/mtd/maps/ixp2000.c b/drivers/mtd/maps/ixp2000.c index dcdb1f17577d..d4fb9a3ab4df 100644 --- a/drivers/mtd/maps/ixp2000.c +++ b/drivers/mtd/maps/ixp2000.c @@ -170,7 +170,7 @@ static int ixp2000_flash_probe(struct platform_device *dev) err = -ENOMEM; goto Error; } - memzero(info, sizeof(struct ixp2000_flash_info)); + memset(info, 0, sizeof(struct ixp2000_flash_info)); platform_set_drvdata(dev, info); @@ -188,7 +188,7 @@ static int ixp2000_flash_probe(struct platform_device *dev) */ info->map.map_priv_2 = (unsigned long) ixp_data->bank_setup; - info->map.name = dev->dev.bus_id; + info->map.name = dev_name(&dev->dev); info->map.read = ixp2000_flash_read8; info->map.write = ixp2000_flash_write8; info->map.copy_from = ixp2000_flash_copy_from; @@ -196,7 +196,7 @@ static int ixp2000_flash_probe(struct platform_device *dev) info->res = request_mem_region(dev->resource->start, dev->resource->end - dev->resource->start + 1, - dev->dev.bus_id); + dev_name(&dev->dev)); if (!info->res) { dev_err(&dev->dev, "Could not reserve memory region\n"); err = -ENOMEM; diff --git a/drivers/mtd/maps/ixp4xx.c b/drivers/mtd/maps/ixp4xx.c index 9c7a5fbd4e51..7214b876feba 100644 --- a/drivers/mtd/maps/ixp4xx.c +++ b/drivers/mtd/maps/ixp4xx.c @@ -201,7 +201,7 @@ static int ixp4xx_flash_probe(struct platform_device *dev) err = -ENOMEM; goto Error; } - memzero(info, sizeof(struct ixp4xx_flash_info)); + memset(info, 0, sizeof(struct ixp4xx_flash_info)); platform_set_drvdata(dev, info); @@ -218,7 +218,7 @@ static int ixp4xx_flash_probe(struct platform_device *dev) * handle that. */ info->map.bankwidth = 2; - info->map.name = dev->dev.bus_id; + info->map.name = dev_name(&dev->dev); info->map.read = ixp4xx_read16, info->map.write = ixp4xx_probe_write16, info->map.copy_from = ixp4xx_copy_from, diff --git a/drivers/mtd/maps/omap_nor.c b/drivers/mtd/maps/omap_nor.c index 05f276af15da..7e50e9b1b781 100644 --- a/drivers/mtd/maps/omap_nor.c +++ b/drivers/mtd/maps/omap_nor.c @@ -101,7 +101,7 @@ static int __init omapflash_probe(struct platform_device *pdev) err = -ENOMEM; goto out_release_mem_region; } - info->map.name = pdev->dev.bus_id; + info->map.name = dev_name(&pdev->dev); info->map.phys = res->start; info->map.size = size; info->map.bankwidth = pdata->width; diff --git a/drivers/mtd/maps/physmap.c b/drivers/mtd/maps/physmap.c index 42d844f8f6bf..8e2ee92c949c 100644 --- a/drivers/mtd/maps/physmap.c +++ b/drivers/mtd/maps/physmap.c @@ -116,14 +116,14 @@ static int physmap_flash_probe(struct platform_device *dev) info->res = request_mem_region(dev->resource[i].start, dev->resource[i].end - dev->resource[i].start + 1, - dev->dev.bus_id); + dev_name(&dev->dev)); if (info->res == NULL) { dev_err(&dev->dev, "Could not reserve memory region\n"); err = -ENOMEM; goto err_out; } - info->map[i].name = dev->dev.bus_id; + info->map[i].name = dev_name(&dev->dev); info->map[i].phys = dev->resource[i].start; info->map[i].size = dev->resource[i].end - dev->resource[i].start + 1; info->map[i].bankwidth = physmap_data->width; @@ -158,7 +158,7 @@ static int physmap_flash_probe(struct platform_device *dev) * We detected multiple devices. Concatenate them together. */ #ifdef CONFIG_MTD_CONCAT - info->cmtd = mtd_concat_create(info->mtd, devices_found, dev->dev.bus_id); + info->cmtd = mtd_concat_create(info->mtd, devices_found, dev_name(&dev->dev)); if (info->cmtd == NULL) err = -ENXIO; #else diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c index 5fcfec034a94..fbf0ca939d72 100644 --- a/drivers/mtd/maps/physmap_of.c +++ b/drivers/mtd/maps/physmap_of.c @@ -183,7 +183,7 @@ static int __devinit of_flash_probe(struct of_device *dev, err = -EBUSY; info->res = request_mem_region(res.start, res.end - res.start + 1, - dev->dev.bus_id); + dev_name(&dev->dev)); if (!info->res) goto err_out; @@ -194,7 +194,7 @@ static int __devinit of_flash_probe(struct of_device *dev, goto err_out; } - info->map.name = dev->dev.bus_id; + info->map.name = dev_name(&dev->dev); info->map.phys = res.start; info->map.size = res.end - res.start + 1; info->map.bankwidth = *width; diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index 789842d0e6f2..1a05cf37851e 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -691,7 +691,7 @@ static int concat_block_markbad(struct mtd_info *mtd, loff_t ofs) */ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to concatenate */ int num_devs, /* number of subdevices */ - char *name) + const char *name) { /* name for the new device */ int i; size_t size; diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c index 024e3fffd4bb..09c1fef5fd36 100644 --- a/drivers/mtd/nand/fsl_upm.c +++ b/drivers/mtd/nand/fsl_upm.c @@ -220,7 +220,7 @@ static int __devinit fun_probe(struct of_device *ofdev, fun->rnb_gpio = of_get_gpio(ofdev->node, 0); if (fun->rnb_gpio >= 0) { - ret = gpio_request(fun->rnb_gpio, ofdev->dev.bus_id); + ret = gpio_request(fun->rnb_gpio, dev_name(&ofdev->dev)); if (ret) { dev_err(&ofdev->dev, "can't request RNB gpio\n"); goto err2; diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c index f674c5427b17..75f9f4874ecf 100644 --- a/drivers/mtd/nand/plat_nand.c +++ b/drivers/mtd/nand/plat_nand.c @@ -54,7 +54,7 @@ static int __init plat_nand_probe(struct platform_device *pdev) data->chip.priv = &data; data->mtd.priv = &data->chip; data->mtd.owner = THIS_MODULE; - data->mtd.name = pdev->dev.bus_id; + data->mtd.name = dev_name(&pdev->dev); data->chip.IO_ADDR_R = data->io_base; data->chip.IO_ADDR_W = data->io_base; diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index c0fa9c9edf08..61c922a8356f 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -1079,7 +1079,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev) this = &info->nand_chip; mtd->priv = info; - info->clk = clk_get(&pdev->dev, "NANDCLK"); + info->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(info->clk)) { dev_err(&pdev->dev, "failed to get nand clock\n"); ret = PTR_ERR(info->clk); diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index 556139ed1fdf..8e375d5fe231 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -45,8 +45,8 @@ #include <asm/io.h> -#include <asm/plat-s3c/regs-nand.h> -#include <asm/plat-s3c/nand.h> +#include <plat/regs-nand.h> +#include <plat/nand.h> #ifdef CONFIG_MTD_NAND_S3C2410_HWECC static int hardware_ecc = 1; @@ -818,7 +818,7 @@ static int s3c24xx_nand_probe(struct platform_device *pdev, goto exit_error; } - memzero(info, sizeof(*info)); + memset(info, 0, sizeof(*info)); platform_set_drvdata(pdev, info); spin_lock_init(&info->controller.lock); @@ -883,7 +883,7 @@ static int s3c24xx_nand_probe(struct platform_device *pdev, goto exit_error; } - memzero(info->mtds, size); + memset(info->mtds, 0, size); /* initialise all possible chips */ diff --git a/drivers/mtd/nand/tmio_nand.c b/drivers/mtd/nand/tmio_nand.c index edb1e322113d..daa6a4c3b8ce 100644 --- a/drivers/mtd/nand/tmio_nand.c +++ b/drivers/mtd/nand/tmio_nand.c @@ -433,7 +433,7 @@ static int tmio_probe(struct platform_device *dev) nand_chip->chip_delay = 15; retval = request_irq(irq, &tmio_irq, - IRQF_DISABLED, dev->dev.bus_id, tmio); + IRQF_DISABLED, dev_name(&dev->dev), tmio); if (retval) { dev_err(&dev->dev, "request_irq error %d\n", retval); goto err_irq; diff --git a/drivers/mtd/onenand/generic.c b/drivers/mtd/onenand/generic.c index ad81ab8e95e2..5b69e7773c6c 100644 --- a/drivers/mtd/onenand/generic.c +++ b/drivers/mtd/onenand/generic.c @@ -63,7 +63,7 @@ static int __devinit generic_onenand_probe(struct device *dev) info->onenand.mmcontrol = pdata->mmcontrol; info->onenand.irq = platform_get_irq(pdev, 0); - info->mtd.name = pdev->dev.bus_id; + info->mtd.name = dev_name(&pdev->dev); info->mtd.priv = &info->onenand; info->mtd.owner = THIS_MODULE; diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c index 8387e05daae2..4e5a6bcbe0d4 100644 --- a/drivers/mtd/onenand/omap2.c +++ b/drivers/mtd/onenand/omap2.c @@ -38,7 +38,6 @@ #include <asm/arch/gpmc.h> #include <asm/arch/onenand.h> #include <asm/arch/gpio.h> -#include <asm/arch/gpmc.h> #include <asm/arch/pm.h> #include <linux/dma-mapping.h> @@ -670,7 +669,7 @@ static int __devinit omap2_onenand_probe(struct platform_device *pdev) c->onenand.base); c->pdev = pdev; - c->mtd.name = pdev->dev.bus_id; + c->mtd.name = dev_name(&pdev->dev); c->mtd.priv = &c->onenand; c->mtd.owner = THIS_MODULE; diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index c7630a228310..3a8a80cf2822 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -280,7 +280,7 @@ static int ubi_sysfs_init(struct ubi_device *ubi) ubi->dev.release = dev_release; ubi->dev.devt = ubi->cdev.dev; ubi->dev.class = ubi_class; - sprintf(&ubi->dev.bus_id[0], UBI_NAME_STR"%d", ubi->ubi_num); + dev_set_name(&ubi->dev, UBI_NAME_STR"%d", ubi->ubi_num); err = device_register(&ubi->dev); if (err) return err; diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c index e04bcf1dff87..d8966bae0e0b 100644 --- a/drivers/mtd/ubi/eba.c +++ b/drivers/mtd/ubi/eba.c @@ -1022,7 +1022,7 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, } /* - * OK, now the LEB is locked and we can safely start moving iy. Since + * OK, now the LEB is locked and we can safely start moving it. Since * this function utilizes thie @ubi->peb1_buf buffer which is shared * with some other functions, so lock the buffer by taking the * @ubi->buf_mutex. diff --git a/drivers/mtd/ubi/scan.c b/drivers/mtd/ubi/scan.c index 4f2daa5bbecf..41d47e1cf15c 100644 --- a/drivers/mtd/ubi/scan.c +++ b/drivers/mtd/ubi/scan.c @@ -320,7 +320,7 @@ static int compare_lebs(struct ubi_device *ubi, const struct ubi_scan_leb *seb, } err = ubi_io_read_data(ubi, buf, pnum, 0, len); - if (err && err != UBI_IO_BITFLIPS) + if (err && err != UBI_IO_BITFLIPS && err != -EBADMSG) goto out_free_buf; data_crc = be32_to_cpu(vid_hdr->data_crc); diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c index 3531ca9a1e24..22e1d7398fce 100644 --- a/drivers/mtd/ubi/vmt.c +++ b/drivers/mtd/ubi/vmt.c @@ -329,7 +329,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) vol->dev.devt = dev; vol->dev.class = ubi_class; - sprintf(&vol->dev.bus_id[0], "%s_%d", ubi->ubi_name, vol->vol_id); + dev_set_name(&vol->dev, "%s_%d", ubi->ubi_name, vol->vol_id); err = device_register(&vol->dev); if (err) { ubi_err("cannot register device"); @@ -678,7 +678,7 @@ int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol) vol->dev.parent = &ubi->dev; vol->dev.devt = dev; vol->dev.class = ubi_class; - sprintf(&vol->dev.bus_id[0], "%s_%d", ubi->ubi_name, vol->vol_id); + dev_set_name(&vol->dev, "%s_%d", ubi->ubi_name, vol->vol_id); err = device_register(&vol->dev); if (err) goto out_gluebi; diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index 05d70937b543..dcb6dac1dc54 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -1396,7 +1396,8 @@ int ubi_thread(void *u) ubi_msg("%s: %d consecutive failures", ubi->bgt_name, WL_MAX_FAILURES); ubi_ro_mode(ubi); - break; + ubi->thread_enabled = 0; + continue; } } else failures = 0; diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index f749b40f954e..231eeaf1d552 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -1825,9 +1825,10 @@ config FEC2 config FEC_MPC52xx tristate "MPC52xx FEC driver" - depends on PPC_MPC52xx && PPC_BESTCOMM_FEC + depends on PPC_MPC52xx && PPC_BESTCOMM select CRC32 select PHYLIB + select PPC_BESTCOMM_FEC ---help--- This option enables support for the MPC5200's on-chip Fast Ethernet Controller @@ -2010,9 +2011,13 @@ config IGB_LRO If in doubt, say N. config IGB_DCA - bool "Enable DCA" + bool "Direct Cache Access (DCA) Support" default y depends on IGB && DCA && !(IGB=y && DCA=m) + ---help--- + Say Y here if you want to use Direct Cache Access (DCA) in the + driver. DCA is a method for warming the CPU cache before data + is used, with the intent of lessening the impact of cache misses. source "drivers/net/ixp2000/Kconfig" @@ -2437,9 +2442,13 @@ config IXGBE will be called ixgbe. config IXGBE_DCA - bool + bool "Direct Cache Access (DCA) Support" default y depends on IXGBE && DCA && !(IXGBE=y && DCA=m) + ---help--- + Say Y here if you want to use Direct Cache Access (DCA) in the + driver. DCA is a method for warming the CPU cache before data + is used, with the intent of lessening the impact of cache misses. config IXGB tristate "Intel(R) PRO/10GbE support" @@ -2489,9 +2498,13 @@ config MYRI10GE will be called myri10ge. config MYRI10GE_DCA - bool + bool "Direct Cache Access (DCA) Support" default y depends on MYRI10GE && DCA && !(MYRI10GE=y && DCA=m) + ---help--- + Say Y here if you want to use Direct Cache Access (DCA) in the + driver. DCA is a method for warming the CPU cache before data + is used, with the intent of lessening the impact of cache misses. config NETXEN_NIC tristate "NetXen Multi port (1/10) Gigabit Ethernet NIC" diff --git a/drivers/net/Makefile b/drivers/net/Makefile index f19acf8b9220..017383ad5ec6 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -114,7 +114,7 @@ obj-$(CONFIG_EL2) += 3c503.o 8390p.o obj-$(CONFIG_NE2000) += ne.o 8390p.o obj-$(CONFIG_NE2_MCA) += ne2.o 8390p.o obj-$(CONFIG_HPLAN) += hp.o 8390p.o -obj-$(CONFIG_HPLAN_PLUS) += hp-plus.o 8390.o +obj-$(CONFIG_HPLAN_PLUS) += hp-plus.o 8390p.o obj-$(CONFIG_ULTRA) += smc-ultra.o 8390.o obj-$(CONFIG_ULTRAMCA) += smc-mca.o 8390.o obj-$(CONFIG_ULTRA32) += smc-ultra32.o 8390.o diff --git a/drivers/net/atl1e/atl1e.h b/drivers/net/atl1e/atl1e.h index b645fa0f3f64..c49550d507a0 100644 --- a/drivers/net/atl1e/atl1e.h +++ b/drivers/net/atl1e/atl1e.h @@ -46,7 +46,6 @@ #include <linux/vmalloc.h> #include <linux/pagemap.h> #include <linux/tcp.h> -#include <linux/mii.h> #include <linux/ethtool.h> #include <linux/if_vlan.h> #include <linux/workqueue.h> diff --git a/drivers/net/atl1e/atl1e_hw.c b/drivers/net/atl1e/atl1e_hw.c index 8cbc1b59bd62..4a7700620119 100644 --- a/drivers/net/atl1e/atl1e_hw.c +++ b/drivers/net/atl1e/atl1e_hw.c @@ -163,9 +163,6 @@ int atl1e_read_mac_addr(struct atl1e_hw *hw) * atl1e_hash_mc_addr * purpose * set hash value for a multicast address - * hash calcu processing : - * 1. calcu 32bit CRC for multicast address - * 2. reverse crc with MSB to LSB */ u32 atl1e_hash_mc_addr(struct atl1e_hw *hw, u8 *mc_addr) { @@ -174,7 +171,6 @@ u32 atl1e_hash_mc_addr(struct atl1e_hw *hw, u8 *mc_addr) int i; crc32 = ether_crc_le(6, mc_addr); - crc32 = ~crc32; for (i = 0; i < 32; i++) value |= (((crc32 >> i) & 1) << (31 - i)); diff --git a/drivers/net/atlx/atl1.c b/drivers/net/atlx/atl1.c index 246d92b42636..aef403d299ee 100644 --- a/drivers/net/atlx/atl1.c +++ b/drivers/net/atlx/atl1.c @@ -3404,14 +3404,8 @@ static void atl1_get_wol(struct net_device *netdev, { struct atl1_adapter *adapter = netdev_priv(netdev); - wol->supported = WAKE_UCAST | WAKE_MCAST | WAKE_BCAST | WAKE_MAGIC; + wol->supported = WAKE_MAGIC; wol->wolopts = 0; - if (adapter->wol & ATLX_WUFC_EX) - wol->wolopts |= WAKE_UCAST; - if (adapter->wol & ATLX_WUFC_MC) - wol->wolopts |= WAKE_MCAST; - if (adapter->wol & ATLX_WUFC_BC) - wol->wolopts |= WAKE_BCAST; if (adapter->wol & ATLX_WUFC_MAG) wol->wolopts |= WAKE_MAGIC; return; @@ -3422,15 +3416,10 @@ static int atl1_set_wol(struct net_device *netdev, { struct atl1_adapter *adapter = netdev_priv(netdev); - if (wol->wolopts & (WAKE_PHY | WAKE_ARP | WAKE_MAGICSECURE)) + if (wol->wolopts & (WAKE_PHY | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST | + WAKE_ARP | WAKE_MAGICSECURE)) return -EOPNOTSUPP; adapter->wol = 0; - if (wol->wolopts & WAKE_UCAST) - adapter->wol |= ATLX_WUFC_EX; - if (wol->wolopts & WAKE_MCAST) - adapter->wol |= ATLX_WUFC_MC; - if (wol->wolopts & WAKE_BCAST) - adapter->wol |= ATLX_WUFC_BC; if (wol->wolopts & WAKE_MAGIC) adapter->wol |= ATLX_WUFC_MAG; return 0; diff --git a/drivers/net/atlx/atl2.c b/drivers/net/atlx/atl2.c index f5bdc92c1a65..8571e8c0bc67 100644 --- a/drivers/net/atlx/atl2.c +++ b/drivers/net/atlx/atl2.c @@ -1690,9 +1690,11 @@ static int atl2_resume(struct pci_dev *pdev) ATL2_WRITE_REG(&adapter->hw, REG_WOL_CTRL, 0); - err = atl2_request_irq(adapter); - if (netif_running(netdev) && err) - return err; + if (netif_running(netdev)) { + err = atl2_request_irq(adapter); + if (err) + return err; + } atl2_reset_hw(&adapter->hw); diff --git a/drivers/net/bnx2.c b/drivers/net/bnx2.c index 430d430bce29..d07e3f148951 100644 --- a/drivers/net/bnx2.c +++ b/drivers/net/bnx2.c @@ -7204,10 +7204,13 @@ static void poll_bnx2(struct net_device *dev) { struct bnx2 *bp = netdev_priv(dev); + int i; - disable_irq(bp->pdev->irq); - bnx2_interrupt(bp->pdev->irq, dev); - enable_irq(bp->pdev->irq); + for (i = 0; i < bp->irq_nvecs; i++) { + disable_irq(bp->irq_tbl[i].vector); + bnx2_interrupt(bp->irq_tbl[i].vector, &bp->bnx2_napi[i]); + enable_irq(bp->irq_tbl[i].vector); + } } #endif diff --git a/drivers/net/bnx2x_init.h b/drivers/net/bnx2x_init.h index 130927cfc75b..a6c0b3abba29 100644 --- a/drivers/net/bnx2x_init.h +++ b/drivers/net/bnx2x_init.h @@ -564,14 +564,15 @@ static const struct arb_line write_arb_addr[NUM_WR_Q-1] = { static void bnx2x_init_pxp(struct bnx2x *bp) { + u16 devctl; int r_order, w_order; u32 val, i; pci_read_config_word(bp->pdev, - bp->pcie_cap + PCI_EXP_DEVCTL, (u16 *)&val); - DP(NETIF_MSG_HW, "read 0x%x from devctl\n", (u16)val); - w_order = ((val & PCI_EXP_DEVCTL_PAYLOAD) >> 5); - r_order = ((val & PCI_EXP_DEVCTL_READRQ) >> 12); + bp->pcie_cap + PCI_EXP_DEVCTL, &devctl); + DP(NETIF_MSG_HW, "read 0x%x from devctl\n", devctl); + w_order = ((devctl & PCI_EXP_DEVCTL_PAYLOAD) >> 5); + r_order = ((devctl & PCI_EXP_DEVCTL_READRQ) >> 12); if (r_order > MAX_RD_ORD) { DP(NETIF_MSG_HW, "read order of %d order adjusted to %d\n", diff --git a/drivers/net/bnx2x_main.c b/drivers/net/bnx2x_main.c index fce745148ff9..600210d7eff9 100644 --- a/drivers/net/bnx2x_main.c +++ b/drivers/net/bnx2x_main.c @@ -59,8 +59,8 @@ #include "bnx2x.h" #include "bnx2x_init.h" -#define DRV_MODULE_VERSION "1.45.22" -#define DRV_MODULE_RELDATE "2008/09/09" +#define DRV_MODULE_VERSION "1.45.23" +#define DRV_MODULE_RELDATE "2008/11/03" #define BNX2X_BC_VER 0x040200 /* Time in jiffies before concluding the transmitter is hung */ @@ -6481,6 +6481,7 @@ load_int_disable: bnx2x_free_irq(bp); load_error: bnx2x_free_mem(bp); + bp->port.pmf = 0; /* TBD we really need to reset the chip if we want to recover from this */ @@ -6791,6 +6792,7 @@ unload_error: /* Report UNLOAD_DONE to MCP */ if (!BP_NOMCP(bp)) bnx2x_fw_command(bp, DRV_MSG_CODE_UNLOAD_DONE); + bp->port.pmf = 0; /* Free SKBs, SGEs, TPA pool and driver internals */ bnx2x_free_skbs(bp); @@ -10204,8 +10206,6 @@ static int __devinit bnx2x_init_one(struct pci_dev *pdev, return -ENOMEM; } - netif_carrier_off(dev); - bp = netdev_priv(dev); bp->msglevel = debug; @@ -10229,6 +10229,8 @@ static int __devinit bnx2x_init_one(struct pci_dev *pdev, goto init_one_exit; } + netif_carrier_off(dev); + bp->common.name = board_info[ent->driver_data].name; printk(KERN_INFO "%s: %s (%c%d) PCI-E x%d %s found at mem %lx," " IRQ %d, ", dev->name, bp->common.name, diff --git a/drivers/net/chelsio/sge.c b/drivers/net/chelsio/sge.c index d6c7d2aa761b..7092df50ff78 100644 --- a/drivers/net/chelsio/sge.c +++ b/drivers/net/chelsio/sge.c @@ -1035,10 +1035,6 @@ MODULE_PARM_DESC(copybreak, "Receive copy threshold"); * @pdev: the PCI device that received the packet * @fl: the SGE free list holding the packet * @len: the actual packet length, excluding any SGE padding - * @dma_pad: padding at beginning of buffer left by SGE DMA - * @skb_pad: padding to be used if the packet is copied - * @copy_thres: length threshold under which a packet should be copied - * @drop_thres: # of remaining buffers before we start dropping packets * * Get the next packet from a free list and complete setup of the * sk_buff. If the packet is small we make a copy and recycle the diff --git a/drivers/net/cs89x0.c b/drivers/net/cs89x0.c index 7107620f615d..0b729f7d91fc 100644 --- a/drivers/net/cs89x0.c +++ b/drivers/net/cs89x0.c @@ -170,11 +170,7 @@ static char version[] __initdata = /* The cs8900 has 4 IRQ pins, software selectable. cs8900_irq_map maps them to system IRQ numbers. This mapping is card specific and is set to the configuration of the Cirrus Eval board for this chip. */ -#ifdef CONFIG_ARCH_CLPS7500 -static unsigned int netcard_portlist[] __used __initdata = - { 0x80090303, 0x300, 0x320, 0x340, 0x360, 0x200, 0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0}; -static unsigned int cs8900_irq_map[] = {12,0,0,0}; -#elif defined(CONFIG_SH_HICOSH4) +#if defined(CONFIG_SH_HICOSH4) static unsigned int netcard_portlist[] __used __initdata = { 0x0300, 0}; static unsigned int cs8900_irq_map[] = {1,0,0,0}; diff --git a/drivers/net/cxgb3/cxgb3_main.c b/drivers/net/cxgb3/cxgb3_main.c index 1ace41a13ac3..2c341f83d327 100644 --- a/drivers/net/cxgb3/cxgb3_main.c +++ b/drivers/net/cxgb3/cxgb3_main.c @@ -1307,8 +1307,10 @@ static void get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) u32 fw_vers = 0; u32 tp_vers = 0; + spin_lock(&adapter->stats_lock); t3_get_fw_version(adapter, &fw_vers); t3_get_tp_version(adapter, &tp_vers); + spin_unlock(&adapter->stats_lock); strcpy(info->driver, DRV_NAME); strcpy(info->version, DRV_VERSION); @@ -2699,7 +2701,7 @@ static void set_nqsets(struct adapter *adap) int hwports = adap->params.nports; int nqsets = SGE_QSETS; - if (adap->params.rev > 0) { + if (adap->params.rev > 0 && adap->flags & USING_MSIX) { if (hwports == 2 && (hwports * nqsets > SGE_QSETS || num_cpus >= nqsets / hwports)) diff --git a/drivers/net/cxgb3/t3_hw.c b/drivers/net/cxgb3/t3_hw.c index 968f64be3743..9a0898b0dbce 100644 --- a/drivers/net/cxgb3/t3_hw.c +++ b/drivers/net/cxgb3/t3_hw.c @@ -572,7 +572,7 @@ struct t3_vpd { u32 pad; /* for multiple-of-4 sizing and alignment */ }; -#define EEPROM_MAX_POLL 4 +#define EEPROM_MAX_POLL 40 #define EEPROM_STAT_ADDR 0x4000 #define VPD_BASE 0xc00 @@ -3690,6 +3690,12 @@ int t3_prep_adapter(struct adapter *adapter, const struct adapter_info *ai, ; pti = &port_types[adapter->params.vpd.port_type[j]]; + if (!pti->phy_prep) { + CH_ALERT(adapter, "Invalid port type index %d\n", + adapter->params.vpd.port_type[j]); + return -EINVAL; + } + ret = pti->phy_prep(&p->phy, adapter, ai->phy_base_addr + j, ai->mdio_ops); if (ret) diff --git a/drivers/net/e100.c b/drivers/net/e100.c index 3d69fae781cf..e8bfcce6b319 100644 --- a/drivers/net/e100.c +++ b/drivers/net/e100.c @@ -166,7 +166,7 @@ #define DRV_NAME "e100" #define DRV_EXT "-NAPI" -#define DRV_VERSION "3.5.23-k4"DRV_EXT +#define DRV_VERSION "3.5.23-k6"DRV_EXT #define DRV_DESCRIPTION "Intel(R) PRO/100 Network Driver" #define DRV_COPYRIGHT "Copyright(c) 1999-2006 Intel Corporation" #define PFX DRV_NAME ": " @@ -1804,7 +1804,7 @@ static int e100_rx_alloc_skb(struct nic *nic, struct rx *rx) struct rfd *prev_rfd = (struct rfd *)rx->prev->skb->data; put_unaligned_le32(rx->dma_addr, &prev_rfd->link); pci_dma_sync_single_for_device(nic->pdev, rx->prev->dma_addr, - sizeof(struct rfd), PCI_DMA_TODEVICE); + sizeof(struct rfd), PCI_DMA_BIDIRECTIONAL); } return 0; @@ -1823,7 +1823,7 @@ static int e100_rx_indicate(struct nic *nic, struct rx *rx, /* Need to sync before taking a peek at cb_complete bit */ pci_dma_sync_single_for_cpu(nic->pdev, rx->dma_addr, - sizeof(struct rfd), PCI_DMA_FROMDEVICE); + sizeof(struct rfd), PCI_DMA_BIDIRECTIONAL); rfd_status = le16_to_cpu(rfd->status); DPRINTK(RX_STATUS, DEBUG, "status=0x%04X\n", rfd_status); @@ -1850,7 +1850,7 @@ static int e100_rx_indicate(struct nic *nic, struct rx *rx, /* Get data */ pci_unmap_single(nic->pdev, rx->dma_addr, - RFD_BUF_LEN, PCI_DMA_FROMDEVICE); + RFD_BUF_LEN, PCI_DMA_BIDIRECTIONAL); /* If this buffer has the el bit, but we think the receiver * is still running, check to see if it really stopped while @@ -1943,7 +1943,7 @@ static void e100_rx_clean(struct nic *nic, unsigned int *work_done, new_before_last_rfd->command |= cpu_to_le16(cb_el); pci_dma_sync_single_for_device(nic->pdev, new_before_last_rx->dma_addr, sizeof(struct rfd), - PCI_DMA_TODEVICE); + PCI_DMA_BIDIRECTIONAL); /* Now that we have a new stopping point, we can clear the old * stopping point. We must sync twice to get the proper @@ -1951,11 +1951,11 @@ static void e100_rx_clean(struct nic *nic, unsigned int *work_done, old_before_last_rfd->command &= ~cpu_to_le16(cb_el); pci_dma_sync_single_for_device(nic->pdev, old_before_last_rx->dma_addr, sizeof(struct rfd), - PCI_DMA_TODEVICE); + PCI_DMA_BIDIRECTIONAL); old_before_last_rfd->size = cpu_to_le16(VLAN_ETH_FRAME_LEN); pci_dma_sync_single_for_device(nic->pdev, old_before_last_rx->dma_addr, sizeof(struct rfd), - PCI_DMA_TODEVICE); + PCI_DMA_BIDIRECTIONAL); } if(restart_required) { @@ -1978,7 +1978,7 @@ static void e100_rx_clean_list(struct nic *nic) for(rx = nic->rxs, i = 0; i < count; rx++, i++) { if(rx->skb) { pci_unmap_single(nic->pdev, rx->dma_addr, - RFD_BUF_LEN, PCI_DMA_FROMDEVICE); + RFD_BUF_LEN, PCI_DMA_BIDIRECTIONAL); dev_kfree_skb(rx->skb); } } @@ -2021,7 +2021,7 @@ static int e100_rx_alloc_list(struct nic *nic) before_last->command |= cpu_to_le16(cb_el); before_last->size = 0; pci_dma_sync_single_for_device(nic->pdev, rx->dma_addr, - sizeof(struct rfd), PCI_DMA_TODEVICE); + sizeof(struct rfd), PCI_DMA_BIDIRECTIONAL); nic->rx_to_use = nic->rx_to_clean = nic->rxs; nic->ru_running = RU_SUSPENDED; @@ -2222,7 +2222,7 @@ static int e100_loopback_test(struct nic *nic, enum loopback loopback_mode) msleep(10); pci_dma_sync_single_for_cpu(nic->pdev, nic->rx_to_clean->dma_addr, - RFD_BUF_LEN, PCI_DMA_FROMDEVICE); + RFD_BUF_LEN, PCI_DMA_BIDIRECTIONAL); if(memcmp(nic->rx_to_clean->skb->data + sizeof(struct rfd), skb->data, ETH_DATA_LEN)) diff --git a/drivers/net/e1000/e1000_ethtool.c b/drivers/net/e1000/e1000_ethtool.c index 6a3893acfe04..c854c96f5ab3 100644 --- a/drivers/net/e1000/e1000_ethtool.c +++ b/drivers/net/e1000/e1000_ethtool.c @@ -1774,7 +1774,8 @@ static void e1000_get_wol(struct net_device *netdev, /* this function will set ->supported = 0 and return 1 if wol is not * supported by this hardware */ - if (e1000_wol_exclusion(adapter, wol)) + if (e1000_wol_exclusion(adapter, wol) || + !device_can_wakeup(&adapter->pdev->dev)) return; /* apply any specific unsupported masks here */ @@ -1811,7 +1812,8 @@ static int e1000_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) if (wol->wolopts & (WAKE_PHY | WAKE_ARP | WAKE_MAGICSECURE)) return -EOPNOTSUPP; - if (e1000_wol_exclusion(adapter, wol)) + if (e1000_wol_exclusion(adapter, wol) || + !device_can_wakeup(&adapter->pdev->dev)) return wol->wolopts ? -EOPNOTSUPP : 0; switch (hw->device_id) { @@ -1838,6 +1840,8 @@ static int e1000_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) if (wol->wolopts & WAKE_MAGIC) adapter->wol |= E1000_WUFC_MAG; + device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol); + return 0; } diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c index fac82152e4c8..872799b746f5 100644 --- a/drivers/net/e1000/e1000_main.c +++ b/drivers/net/e1000/e1000_main.c @@ -1179,6 +1179,7 @@ static int __devinit e1000_probe(struct pci_dev *pdev, /* initialize the wol settings based on the eeprom settings */ adapter->wol = adapter->eeprom_wol; + device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol); /* print bus type/speed/width info */ DPRINTK(PROBE, INFO, "(PCI%s:%s:%s) ", diff --git a/drivers/net/e1000e/e1000.h b/drivers/net/e1000e/e1000.h index c55de1c027af..c55fd6fdb91c 100644 --- a/drivers/net/e1000e/e1000.h +++ b/drivers/net/e1000e/e1000.h @@ -299,6 +299,7 @@ struct e1000_adapter { unsigned long led_status; unsigned int flags; + unsigned int flags2; struct work_struct downshift_task; struct work_struct update_phy_task; }; @@ -306,6 +307,7 @@ struct e1000_adapter { struct e1000_info { enum e1000_mac_type mac; unsigned int flags; + unsigned int flags2; u32 pba; s32 (*get_variants)(struct e1000_adapter *); struct e1000_mac_operations *mac_ops; @@ -347,6 +349,9 @@ struct e1000_info { #define FLAG_RX_RESTART_NOW (1 << 30) #define FLAG_MSI_TEST_FAILED (1 << 31) +/* CRC Stripping defines */ +#define FLAG2_CRC_STRIPPING (1 << 0) + #define E1000_RX_DESC_PS(R, i) \ (&(((union e1000_rx_desc_packet_split *)((R).desc))[i])) #define E1000_GET_DESC(R, i, type) (&(((struct type *)((R).desc))[i])) diff --git a/drivers/net/e1000e/ethtool.c b/drivers/net/e1000e/ethtool.c index 70c11c811a08..62421ce96311 100644 --- a/drivers/net/e1000e/ethtool.c +++ b/drivers/net/e1000e/ethtool.c @@ -1713,7 +1713,8 @@ static void e1000_get_wol(struct net_device *netdev, wol->supported = 0; wol->wolopts = 0; - if (!(adapter->flags & FLAG_HAS_WOL)) + if (!(adapter->flags & FLAG_HAS_WOL) || + !device_can_wakeup(&adapter->pdev->dev)) return; wol->supported = WAKE_UCAST | WAKE_MCAST | @@ -1751,7 +1752,8 @@ static int e1000_set_wol(struct net_device *netdev, if (wol->wolopts & WAKE_MAGICSECURE) return -EOPNOTSUPP; - if (!(adapter->flags & FLAG_HAS_WOL)) + if (!(adapter->flags & FLAG_HAS_WOL) || + !device_can_wakeup(&adapter->pdev->dev)) return wol->wolopts ? -EOPNOTSUPP : 0; /* these settings will always override what we currently have */ @@ -1770,6 +1772,8 @@ static int e1000_set_wol(struct net_device *netdev, if (wol->wolopts & WAKE_ARP) adapter->wol |= E1000_WUFC_ARP; + device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol); + return 0; } diff --git a/drivers/net/e1000e/netdev.c b/drivers/net/e1000e/netdev.c index abd492b7336d..122539a0e1fe 100644 --- a/drivers/net/e1000e/netdev.c +++ b/drivers/net/e1000e/netdev.c @@ -345,7 +345,6 @@ no_buffers: /** * e1000_alloc_jumbo_rx_buffers - Replace used jumbo receive buffers * @adapter: address of board private structure - * @rx_ring: pointer to receive ring structure * @cleaned_count: number of buffers to allocate this pass **/ @@ -499,6 +498,10 @@ static bool e1000_clean_rx_irq(struct e1000_adapter *adapter, goto next_desc; } + /* adjust length to remove Ethernet CRC */ + if (!(adapter->flags2 & FLAG2_CRC_STRIPPING)) + length -= 4; + total_rx_bytes += length; total_rx_packets++; @@ -804,6 +807,10 @@ static bool e1000_clean_rx_irq_ps(struct e1000_adapter *adapter, pci_dma_sync_single_for_device(pdev, ps_page->dma, PAGE_SIZE, PCI_DMA_FROMDEVICE); + /* remove the CRC */ + if (!(adapter->flags2 & FLAG2_CRC_STRIPPING)) + l1 -= 4; + skb_put(skb, l1); goto copydone; } /* if */ @@ -825,6 +832,12 @@ static bool e1000_clean_rx_irq_ps(struct e1000_adapter *adapter, skb->truesize += length; } + /* strip the ethernet crc, problem is we're using pages now so + * this whole operation can get a little cpu intensive + */ + if (!(adapter->flags2 & FLAG2_CRC_STRIPPING)) + pskb_trim(skb, skb->len - 4); + copydone: total_rx_bytes += skb->len; total_rx_packets++; @@ -2301,8 +2314,12 @@ static void e1000_setup_rctl(struct e1000_adapter *adapter) else rctl |= E1000_RCTL_LPE; - /* Enable hardware CRC frame stripping */ - rctl |= E1000_RCTL_SECRC; + /* Some systems expect that the CRC is included in SMBUS traffic. The + * hardware strips the CRC before sending to both SMBUS (BMC) and to + * host memory when this is enabled + */ + if (adapter->flags2 & FLAG2_CRC_STRIPPING) + rctl |= E1000_RCTL_SECRC; /* Setup buffer sizes */ rctl &= ~E1000_RCTL_SZ_4096; @@ -4766,6 +4783,7 @@ static int __devinit e1000_probe(struct pci_dev *pdev, adapter->ei = ei; adapter->pba = ei->pba; adapter->flags = ei->flags; + adapter->flags2 = ei->flags2; adapter->hw.adapter = adapter; adapter->hw.mac.type = ei->mac; adapter->msg_enable = (1 << NETIF_MSG_DRV | NETIF_MSG_PROBE) - 1; @@ -4970,6 +4988,7 @@ static int __devinit e1000_probe(struct pci_dev *pdev, /* initialize the wol settings based on the eeprom settings */ adapter->wol = adapter->eeprom_wol; + device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol); /* reset the hardware with the new settings */ e1000e_reset(adapter); @@ -5008,6 +5027,7 @@ err_hw_init: err_sw_init: if (adapter->hw.flash_address) iounmap(adapter->hw.flash_address); + e1000e_reset_interrupt_capability(adapter); err_flashmap: iounmap(adapter->hw.hw_addr); err_ioremap: diff --git a/drivers/net/e1000e/param.c b/drivers/net/e1000e/param.c index 77a3d7207a5f..e909f96698e8 100644 --- a/drivers/net/e1000e/param.c +++ b/drivers/net/e1000e/param.c @@ -151,6 +151,16 @@ E1000_PARAM(KumeranLockLoss, "Enable Kumeran lock loss workaround"); */ E1000_PARAM(WriteProtectNVM, "Write-protect NVM [WARNING: disabling this can lead to corrupted NVM]"); +/* + * Enable CRC Stripping + * + * Valid Range: 0, 1 + * + * Default Value: 1 (enabled) + */ +E1000_PARAM(CrcStripping, "Enable CRC Stripping, disable if your BMC needs " \ + "the CRC"); + struct e1000_option { enum { enable_option, range_option, list_option } type; const char *name; @@ -404,6 +414,21 @@ void __devinit e1000e_check_options(struct e1000_adapter *adapter) adapter->flags |= FLAG_SMART_POWER_DOWN; } } + { /* CRC Stripping */ + const struct e1000_option opt = { + .type = enable_option, + .name = "CRC Stripping", + .err = "defaulting to enabled", + .def = OPTION_ENABLED + }; + + if (num_CrcStripping > bd) { + unsigned int crc_stripping = CrcStripping[bd]; + e1000_validate_option(&crc_stripping, &opt, adapter); + if (crc_stripping == OPTION_ENABLED) + adapter->flags2 |= FLAG2_CRC_STRIPPING; + } + } { /* Kumeran Lock Loss Workaround */ const struct e1000_option opt = { .type = enable_option, diff --git a/drivers/net/fs_enet/fs_enet-main.c b/drivers/net/fs_enet/fs_enet-main.c index cb51c1fb0338..a6f49d025787 100644 --- a/drivers/net/fs_enet/fs_enet-main.c +++ b/drivers/net/fs_enet/fs_enet-main.c @@ -1099,7 +1099,9 @@ static int __devinit fs_enet_probe(struct of_device *ofdev, ndev->stop = fs_enet_close; ndev->get_stats = fs_enet_get_stats; ndev->set_multicast_list = fs_set_multicast_list; - +#ifdef CONFIG_NET_POLL_CONTROLLER + ndev->poll_controller = fs_enet_netpoll; +#endif if (fpi->use_napi) netif_napi_add(ndev, &fep->napi, fs_enet_rx_napi, fpi->napi_weight); @@ -1209,7 +1211,7 @@ static void __exit fs_cleanup(void) static void fs_enet_netpoll(struct net_device *dev) { disable_irq(dev->irq); - fs_enet_interrupt(dev->irq, dev, NULL); + fs_enet_interrupt(dev->irq, dev); enable_irq(dev->irq); } #endif diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c index 83a5cb6aa23b..c4af949bf860 100644 --- a/drivers/net/gianfar.c +++ b/drivers/net/gianfar.c @@ -1407,6 +1407,10 @@ static int gfar_clean_tx_ring(struct net_device *dev) if (bdp->status & TXBD_DEF) dev->stats.collisions++; + /* Unmap the DMA memory */ + dma_unmap_single(&priv->dev->dev, bdp->bufPtr, + bdp->length, DMA_TO_DEVICE); + /* Free the sk buffer associated with this TxBD */ dev_kfree_skb_irq(priv->tx_skbuff[priv->skb_dirtytx]); @@ -1666,6 +1670,9 @@ int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit) skb = priv->rx_skbuff[priv->skb_currx]; + dma_unmap_single(&priv->dev->dev, bdp->bufPtr, + priv->rx_buffer_size, DMA_FROM_DEVICE); + /* We drop the frame if we failed to allocate a new buffer */ if (unlikely(!newskb || !(bdp->status & RXBD_LAST) || bdp->status & RXBD_ERR)) { @@ -1674,14 +1681,8 @@ int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit) if (unlikely(!newskb)) newskb = skb; - if (skb) { - dma_unmap_single(&priv->dev->dev, - bdp->bufPtr, - priv->rx_buffer_size, - DMA_FROM_DEVICE); - + if (skb) dev_kfree_skb_any(skb); - } } else { /* Increment the number of packets */ dev->stats.rx_packets++; diff --git a/drivers/net/hp-plus.c b/drivers/net/hp-plus.c index fbbd3e660c27..c01e290d09d2 100644 --- a/drivers/net/hp-plus.c +++ b/drivers/net/hp-plus.c @@ -230,7 +230,7 @@ static int __init hpp_probe1(struct net_device *dev, int ioaddr) dev->open = &hpp_open; dev->stop = &hpp_close; #ifdef CONFIG_NET_POLL_CONTROLLER - dev->poll_controller = ei_poll; + dev->poll_controller = eip_poll; #endif ei_status.name = name; diff --git a/drivers/net/igb/igb_ethtool.c b/drivers/net/igb/igb_ethtool.c index 58906c984be9..89964fa739a0 100644 --- a/drivers/net/igb/igb_ethtool.c +++ b/drivers/net/igb/igb_ethtool.c @@ -1776,7 +1776,8 @@ static void igb_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) /* this function will set ->supported = 0 and return 1 if wol is not * supported by this hardware */ - if (igb_wol_exclusion(adapter, wol)) + if (igb_wol_exclusion(adapter, wol) || + !device_can_wakeup(&adapter->pdev->dev)) return; /* apply any specific unsupported masks here */ @@ -1805,7 +1806,8 @@ static int igb_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) if (wol->wolopts & (WAKE_PHY | WAKE_ARP | WAKE_MAGICSECURE)) return -EOPNOTSUPP; - if (igb_wol_exclusion(adapter, wol)) + if (igb_wol_exclusion(adapter, wol) || + !device_can_wakeup(&adapter->pdev->dev)) return wol->wolopts ? -EOPNOTSUPP : 0; switch (hw->device_id) { @@ -1825,6 +1827,8 @@ static int igb_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) if (wol->wolopts & WAKE_MAGIC) adapter->wol |= E1000_WUFC_MAG; + device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol); + return 0; } diff --git a/drivers/net/igb/igb_main.c b/drivers/net/igb/igb_main.c index 1f397cd99414..20d27e622ec1 100644 --- a/drivers/net/igb/igb_main.c +++ b/drivers/net/igb/igb_main.c @@ -1019,10 +1019,9 @@ static int __devinit igb_probe(struct pci_dev *pdev, state &= ~PCIE_LINK_STATE_L0S; pci_write_config_word(us_dev, pos + PCI_EXP_LNKCTL, state); - printk(KERN_INFO "Disabling ASPM L0s upstream switch " - "port %x:%x.%x\n", us_dev->bus->number, - PCI_SLOT(us_dev->devfn), - PCI_FUNC(us_dev->devfn)); + dev_info(&pdev->dev, + "Disabling ASPM L0s upstream switch port %s\n", + pci_name(us_dev)); } default: break; @@ -1244,6 +1243,7 @@ static int __devinit igb_probe(struct pci_dev *pdev, /* initialize the wol settings based on the eeprom settings */ adapter->wol = adapter->eeprom_wol; + device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol); /* reset the hardware with the new settings */ igb_reset(adapter); @@ -1980,7 +1980,6 @@ static void igb_configure_rx(struct igb_adapter *adapter) /** * igb_free_tx_resources - Free Tx Resources per Queue - * @adapter: board private structure * @tx_ring: Tx descriptor ring for a specific queue * * Free all transmit software resources @@ -2033,7 +2032,6 @@ static void igb_unmap_and_free_tx_resource(struct igb_adapter *adapter, /** * igb_clean_tx_ring - Free Tx Buffers - * @adapter: board private structure * @tx_ring: ring to be cleaned **/ static void igb_clean_tx_ring(struct igb_ring *tx_ring) @@ -2080,7 +2078,6 @@ static void igb_clean_all_tx_rings(struct igb_adapter *adapter) /** * igb_free_rx_resources - Free Rx Resources - * @adapter: board private structure * @rx_ring: ring to clean the resources from * * Free all receive software resources @@ -2120,7 +2117,6 @@ static void igb_free_all_rx_resources(struct igb_adapter *adapter) /** * igb_clean_rx_ring - Free Rx Buffers per Queue - * @adapter: board private structure * @rx_ring: ring to free buffers from **/ static void igb_clean_rx_ring(struct igb_ring *rx_ring) diff --git a/drivers/net/ipg.c b/drivers/net/ipg.c index 7373dafbb3f7..059369885be1 100644 --- a/drivers/net/ipg.c +++ b/drivers/net/ipg.c @@ -1112,7 +1112,7 @@ static void ipg_nic_rx_free_skb(struct net_device *dev) struct ipg_rx *rxfd = sp->rxd + entry; pci_unmap_single(sp->pdev, - le64_to_cpu(rxfd->frag_info & ~IPG_RFI_FRAGLEN), + le64_to_cpu(rxfd->frag_info) & ~IPG_RFI_FRAGLEN, sp->rx_buf_sz, PCI_DMA_FROMDEVICE); dev_kfree_skb_irq(sp->rx_buff[entry]); sp->rx_buff[entry] = NULL; @@ -1179,7 +1179,7 @@ static int ipg_nic_rx_check_error(struct net_device *dev) */ if (sp->rx_buff[entry]) { pci_unmap_single(sp->pdev, - le64_to_cpu(rxfd->frag_info & ~IPG_RFI_FRAGLEN), + le64_to_cpu(rxfd->frag_info) & ~IPG_RFI_FRAGLEN, sp->rx_buf_sz, PCI_DMA_FROMDEVICE); dev_kfree_skb_irq(sp->rx_buff[entry]); @@ -1246,7 +1246,7 @@ static void ipg_nic_rx_with_start(struct net_device *dev, if (jumbo->found_start) dev_kfree_skb_irq(jumbo->skb); - pci_unmap_single(pdev, le64_to_cpu(rxfd->frag_info & ~IPG_RFI_FRAGLEN), + pci_unmap_single(pdev, le64_to_cpu(rxfd->frag_info) & ~IPG_RFI_FRAGLEN, sp->rx_buf_sz, PCI_DMA_FROMDEVICE); skb_put(skb, sp->rxfrag_size); @@ -1349,7 +1349,7 @@ static int ipg_nic_rx_jumbo(struct net_device *dev) unsigned int entry = curr % IPG_RFDLIST_LENGTH; struct ipg_rx *rxfd = sp->rxd + entry; - if (!(rxfd->rfs & le64_to_cpu(IPG_RFS_RFDDONE))) + if (!(rxfd->rfs & cpu_to_le64(IPG_RFS_RFDDONE))) break; switch (ipg_nic_rx_check_frame_type(dev)) { diff --git a/drivers/net/irda/pxaficp_ir.c b/drivers/net/irda/pxaficp_ir.c index c5b02b66f756..0e081292f4f7 100644 --- a/drivers/net/irda/pxaficp_ir.c +++ b/drivers/net/irda/pxaficp_ir.c @@ -22,9 +22,53 @@ #include <net/irda/wrapper.h> #include <net/irda/irda_device.h> -#include <asm/dma.h> +#include <mach/dma.h> #include <mach/irda.h> +#include <mach/hardware.h> #include <mach/pxa-regs.h> +#include <mach/regs-uart.h> + +#define FICP __REG(0x40800000) /* Start of FICP area */ +#define ICCR0 __REG(0x40800000) /* ICP Control Register 0 */ +#define ICCR1 __REG(0x40800004) /* ICP Control Register 1 */ +#define ICCR2 __REG(0x40800008) /* ICP Control Register 2 */ +#define ICDR __REG(0x4080000c) /* ICP Data Register */ +#define ICSR0 __REG(0x40800014) /* ICP Status Register 0 */ +#define ICSR1 __REG(0x40800018) /* ICP Status Register 1 */ + +#define ICCR0_AME (1 << 7) /* Address match enable */ +#define ICCR0_TIE (1 << 6) /* Transmit FIFO interrupt enable */ +#define ICCR0_RIE (1 << 5) /* Recieve FIFO interrupt enable */ +#define ICCR0_RXE (1 << 4) /* Receive enable */ +#define ICCR0_TXE (1 << 3) /* Transmit enable */ +#define ICCR0_TUS (1 << 2) /* Transmit FIFO underrun select */ +#define ICCR0_LBM (1 << 1) /* Loopback mode */ +#define ICCR0_ITR (1 << 0) /* IrDA transmission */ + +#define ICCR2_RXP (1 << 3) /* Receive Pin Polarity select */ +#define ICCR2_TXP (1 << 2) /* Transmit Pin Polarity select */ +#define ICCR2_TRIG (3 << 0) /* Receive FIFO Trigger threshold */ +#define ICCR2_TRIG_8 (0 << 0) /* >= 8 bytes */ +#define ICCR2_TRIG_16 (1 << 0) /* >= 16 bytes */ +#define ICCR2_TRIG_32 (2 << 0) /* >= 32 bytes */ + +#ifdef CONFIG_PXA27x +#define ICSR0_EOC (1 << 6) /* DMA End of Descriptor Chain */ +#endif +#define ICSR0_FRE (1 << 5) /* Framing error */ +#define ICSR0_RFS (1 << 4) /* Receive FIFO service request */ +#define ICSR0_TFS (1 << 3) /* Transnit FIFO service request */ +#define ICSR0_RAB (1 << 2) /* Receiver abort */ +#define ICSR0_TUR (1 << 1) /* Trunsmit FIFO underun */ +#define ICSR0_EIF (1 << 0) /* End/Error in FIFO */ + +#define ICSR1_ROR (1 << 6) /* Receiver FIFO underrun */ +#define ICSR1_CRE (1 << 5) /* CRC error */ +#define ICSR1_EOF (1 << 4) /* End of frame */ +#define ICSR1_TNF (1 << 3) /* Transmit FIFO not full */ +#define ICSR1_RNE (1 << 2) /* Receive FIFO not empty */ +#define ICSR1_TBY (1 << 1) /* Tramsmiter busy flag */ +#define ICSR1_RSY (1 << 0) /* Recevier synchronized flag */ #define IrSR_RXPL_NEG_IS_ZERO (1<<4) #define IrSR_RXPL_POS_IS_ZERO 0x0 diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c index 7548fb7360d9..5236f633ee36 100644 --- a/drivers/net/ixgbe/ixgbe_main.c +++ b/drivers/net/ixgbe/ixgbe_main.c @@ -1287,13 +1287,39 @@ static void ixgbe_set_itr(struct ixgbe_adapter *adapter) return; } -static inline void ixgbe_irq_enable(struct ixgbe_adapter *adapter); +/** + * ixgbe_irq_disable - Mask off interrupt generation on the NIC + * @adapter: board private structure + **/ +static inline void ixgbe_irq_disable(struct ixgbe_adapter *adapter) +{ + IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMC, ~0); + IXGBE_WRITE_FLUSH(&adapter->hw); + if (adapter->flags & IXGBE_FLAG_MSIX_ENABLED) { + int i; + for (i = 0; i < adapter->num_msix_vectors; i++) + synchronize_irq(adapter->msix_entries[i].vector); + } else { + synchronize_irq(adapter->pdev->irq); + } +} + +/** + * ixgbe_irq_enable - Enable default interrupt generation settings + * @adapter: board private structure + **/ +static inline void ixgbe_irq_enable(struct ixgbe_adapter *adapter) +{ + u32 mask; + mask = IXGBE_EIMS_ENABLE_MASK; + IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMS, mask); + IXGBE_WRITE_FLUSH(&adapter->hw); +} /** * ixgbe_intr - legacy mode Interrupt Handler * @irq: interrupt number * @data: pointer to a network interface device structure - * @pt_regs: CPU registers structure **/ static irqreturn_t ixgbe_intr(int irq, void *data) { @@ -1394,35 +1420,6 @@ static void ixgbe_free_irq(struct ixgbe_adapter *adapter) } /** - * ixgbe_irq_disable - Mask off interrupt generation on the NIC - * @adapter: board private structure - **/ -static inline void ixgbe_irq_disable(struct ixgbe_adapter *adapter) -{ - IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMC, ~0); - IXGBE_WRITE_FLUSH(&adapter->hw); - if (adapter->flags & IXGBE_FLAG_MSIX_ENABLED) { - int i; - for (i = 0; i < adapter->num_msix_vectors; i++) - synchronize_irq(adapter->msix_entries[i].vector); - } else { - synchronize_irq(adapter->pdev->irq); - } -} - -/** - * ixgbe_irq_enable - Enable default interrupt generation settings - * @adapter: board private structure - **/ -static inline void ixgbe_irq_enable(struct ixgbe_adapter *adapter) -{ - u32 mask; - mask = IXGBE_EIMS_ENABLE_MASK; - IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMS, mask); - IXGBE_WRITE_FLUSH(&adapter->hw); -} - -/** * ixgbe_configure_msi_and_legacy - Initialize PIN (INTA...) and MSI interrupts * **/ @@ -2332,7 +2329,7 @@ static void ixgbe_acquire_msix_vectors(struct ixgbe_adapter *adapter, * Once we know the feature-set enabled for the device, we'll cache * the register offset the descriptor ring is assigned to. **/ -static void __devinit ixgbe_cache_ring_register(struct ixgbe_adapter *adapter) +static void ixgbe_cache_ring_register(struct ixgbe_adapter *adapter) { int feature_mask = 0, rss_i; int i, txr_idx, rxr_idx; @@ -2369,7 +2366,7 @@ static void __devinit ixgbe_cache_ring_register(struct ixgbe_adapter *adapter) * number of queues at compile-time. The polling_netdev array is * intended for Multiqueue, but should work fine with a single queue. **/ -static int __devinit ixgbe_alloc_queues(struct ixgbe_adapter *adapter) +static int ixgbe_alloc_queues(struct ixgbe_adapter *adapter) { int i; @@ -2410,8 +2407,7 @@ err_tx_ring_allocation: * Attempt to configure the interrupts using the best available * capabilities of the hardware and the kernel. **/ -static int __devinit ixgbe_set_interrupt_capability(struct ixgbe_adapter - *adapter) +static int ixgbe_set_interrupt_capability(struct ixgbe_adapter *adapter) { int err = 0; int vector, v_budget; @@ -2503,7 +2499,7 @@ static void ixgbe_reset_interrupt_capability(struct ixgbe_adapter *adapter) * - Hardware queue count (num_*_queues) * - defined by miscellaneous hardware support/features (RSS, etc.) **/ -static int __devinit ixgbe_init_interrupt_scheme(struct ixgbe_adapter *adapter) +static int ixgbe_init_interrupt_scheme(struct ixgbe_adapter *adapter) { int err; diff --git a/drivers/net/jme.c b/drivers/net/jme.c index 81c6cdc3851f..665e70d620fc 100644 --- a/drivers/net/jme.c +++ b/drivers/net/jme.c @@ -912,23 +912,23 @@ jme_alloc_and_feed_skb(struct jme_adapter *jme, int idx) skb_put(skb, framesize); skb->protocol = eth_type_trans(skb, jme->dev); - if (jme_rxsum_ok(jme, rxdesc->descwb.flags)) + if (jme_rxsum_ok(jme, le16_to_cpu(rxdesc->descwb.flags))) skb->ip_summed = CHECKSUM_UNNECESSARY; else skb->ip_summed = CHECKSUM_NONE; - if (rxdesc->descwb.flags & RXWBFLAG_TAGON) { + if (rxdesc->descwb.flags & cpu_to_le16(RXWBFLAG_TAGON)) { if (jme->vlgrp) { jme->jme_vlan_rx(skb, jme->vlgrp, - le32_to_cpu(rxdesc->descwb.vlan)); + le16_to_cpu(rxdesc->descwb.vlan)); NET_STAT(jme).rx_bytes += 4; } } else { jme->jme_rx(skb); } - if ((le16_to_cpu(rxdesc->descwb.flags) & RXWBFLAG_DEST) == - RXWBFLAG_DEST_MUL) + if ((rxdesc->descwb.flags & cpu_to_le16(RXWBFLAG_DEST)) == + cpu_to_le16(RXWBFLAG_DEST_MUL)) ++(NET_STAT(jme).multicast); jme->dev->last_rx = jiffies; @@ -961,7 +961,7 @@ jme_process_receive(struct jme_adapter *jme, int limit) rxdesc = rxring->desc; rxdesc += i; - if ((rxdesc->descwb.flags & RXWBFLAG_OWN) || + if ((rxdesc->descwb.flags & cpu_to_le16(RXWBFLAG_OWN)) || !(rxdesc->descwb.desccnt & RXWBDCNT_WBCPL)) goto out; @@ -1763,10 +1763,9 @@ jme_expand_header(struct jme_adapter *jme, struct sk_buff *skb) } static int -jme_tx_tso(struct sk_buff *skb, - u16 *mss, u8 *flags) +jme_tx_tso(struct sk_buff *skb, __le16 *mss, u8 *flags) { - *mss = skb_shinfo(skb)->gso_size << TXDESC_MSS_SHIFT; + *mss = cpu_to_le16(skb_shinfo(skb)->gso_size << TXDESC_MSS_SHIFT); if (*mss) { *flags |= TXFLAG_LSEN; @@ -1826,11 +1825,11 @@ jme_tx_csum(struct jme_adapter *jme, struct sk_buff *skb, u8 *flags) } static inline void -jme_tx_vlan(struct sk_buff *skb, u16 *vlan, u8 *flags) +jme_tx_vlan(struct sk_buff *skb, __le16 *vlan, u8 *flags) { if (vlan_tx_tag_present(skb)) { *flags |= TXFLAG_TAGON; - *vlan = vlan_tx_tag_get(skb); + *vlan = cpu_to_le16(vlan_tx_tag_get(skb)); } } diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 42394505bb50..590039cbb146 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -70,6 +70,9 @@ static void macvlan_broadcast(struct sk_buff *skb, struct sk_buff *nskb; unsigned int i; + if (skb->protocol == htons(ETH_P_PAUSE)) + return; + for (i = 0; i < MACVLAN_HASH_SIZE; i++) { hlist_for_each_entry_rcu(vlan, n, &port->vlan_hash[i], hlist) { dev = vlan->dev; diff --git a/drivers/net/mlx4/en_netdev.c b/drivers/net/mlx4/en_netdev.c index a339afbeed38..96e709d6440a 100644 --- a/drivers/net/mlx4/en_netdev.c +++ b/drivers/net/mlx4/en_netdev.c @@ -656,10 +656,10 @@ static int mlx4_en_start_port(struct net_device *dev) /* Configure port */ err = mlx4_SET_PORT_general(mdev->dev, priv->port, priv->rx_skb_size + ETH_FCS_LEN, - mdev->profile.tx_pause, - mdev->profile.tx_ppp, - mdev->profile.rx_pause, - mdev->profile.rx_ppp); + priv->prof->tx_pause, + priv->prof->tx_ppp, + priv->prof->rx_pause, + priv->prof->rx_ppp); if (err) { mlx4_err(mdev, "Failed setting port general configurations" " for port %d, with error %d\n", priv->port, err); @@ -706,7 +706,7 @@ tx_err: mlx4_en_release_rss_steer(priv); rx_err: for (i = 0; i < priv->rx_ring_num; i++) - mlx4_en_deactivate_rx_ring(priv, &priv->rx_ring[rx_index]); + mlx4_en_deactivate_rx_ring(priv, &priv->rx_ring[i]); cq_err: while (rx_index--) mlx4_en_deactivate_cq(priv, &priv->rx_cq[rx_index]); diff --git a/drivers/net/mlx4/en_params.c b/drivers/net/mlx4/en_params.c index c2e69b1bcd0a..95706ee1c019 100644 --- a/drivers/net/mlx4/en_params.c +++ b/drivers/net/mlx4/en_params.c @@ -90,6 +90,7 @@ MLX4_EN_PARM_INT(rx_ring_size2, MLX4_EN_AUTO_CONF, "Rx ring size for port 2"); int mlx4_en_get_profile(struct mlx4_en_dev *mdev) { struct mlx4_en_profile *params = &mdev->profile; + int i; params->rx_moder_cnt = min_t(int, rx_moder_cnt, MLX4_EN_AUTO_CONF); params->rx_moder_time = min_t(int, rx_moder_time, MLX4_EN_AUTO_CONF); @@ -97,11 +98,13 @@ int mlx4_en_get_profile(struct mlx4_en_dev *mdev) params->rss_xor = (rss_xor != 0); params->rss_mask = rss_mask & 0x1f; params->num_lro = min_t(int, num_lro , MLX4_EN_MAX_LRO_DESCRIPTORS); - params->rx_pause = pprx; - params->rx_ppp = pfcrx; - params->tx_pause = pptx; - params->tx_ppp = pfctx; - if (params->rx_ppp || params->tx_ppp) { + for (i = 1; i <= MLX4_MAX_PORTS; i++) { + params->prof[i].rx_pause = pprx; + params->prof[i].rx_ppp = pfcrx; + params->prof[i].tx_pause = pptx; + params->prof[i].tx_ppp = pfctx; + } + if (pfcrx || pfctx) { params->prof[1].tx_ring_num = MLX4_EN_TX_RING_NUM; params->prof[2].tx_ring_num = MLX4_EN_TX_RING_NUM; } else { @@ -407,14 +410,14 @@ static int mlx4_en_set_pauseparam(struct net_device *dev, struct mlx4_en_dev *mdev = priv->mdev; int err; - mdev->profile.tx_pause = pause->tx_pause != 0; - mdev->profile.rx_pause = pause->rx_pause != 0; + priv->prof->tx_pause = pause->tx_pause != 0; + priv->prof->rx_pause = pause->rx_pause != 0; err = mlx4_SET_PORT_general(mdev->dev, priv->port, priv->rx_skb_size + ETH_FCS_LEN, - mdev->profile.tx_pause, - mdev->profile.tx_ppp, - mdev->profile.rx_pause, - mdev->profile.rx_ppp); + priv->prof->tx_pause, + priv->prof->tx_ppp, + priv->prof->rx_pause, + priv->prof->rx_ppp); if (err) mlx4_err(mdev, "Failed setting pause params to\n"); @@ -425,10 +428,9 @@ static void mlx4_en_get_pauseparam(struct net_device *dev, struct ethtool_pauseparam *pause) { struct mlx4_en_priv *priv = netdev_priv(dev); - struct mlx4_en_dev *mdev = priv->mdev; - pause->tx_pause = mdev->profile.tx_pause; - pause->rx_pause = mdev->profile.rx_pause; + pause->tx_pause = priv->prof->tx_pause; + pause->rx_pause = priv->prof->rx_pause; } static void mlx4_en_get_ringparam(struct net_device *dev, diff --git a/drivers/net/mlx4/mlx4.h b/drivers/net/mlx4/mlx4.h index fa431fad0eec..56a2e213fe62 100644 --- a/drivers/net/mlx4/mlx4.h +++ b/drivers/net/mlx4/mlx4.h @@ -87,6 +87,9 @@ enum { #ifdef CONFIG_MLX4_DEBUG extern int mlx4_debug_level; +#else /* CONFIG_MLX4_DEBUG */ +#define mlx4_debug_level (0) +#endif /* CONFIG_MLX4_DEBUG */ #define mlx4_dbg(mdev, format, arg...) \ do { \ @@ -94,12 +97,6 @@ extern int mlx4_debug_level; dev_printk(KERN_DEBUG, &mdev->pdev->dev, format, ## arg); \ } while (0) -#else /* CONFIG_MLX4_DEBUG */ - -#define mlx4_dbg(mdev, format, arg...) do { (void) mdev; } while (0) - -#endif /* CONFIG_MLX4_DEBUG */ - #define mlx4_err(mdev, format, arg...) \ dev_err(&mdev->pdev->dev, format, ## arg) #define mlx4_info(mdev, format, arg...) \ diff --git a/drivers/net/mlx4/mlx4_en.h b/drivers/net/mlx4/mlx4_en.h index 11fb17c6e97b..98ddc0811f93 100644 --- a/drivers/net/mlx4/mlx4_en.h +++ b/drivers/net/mlx4/mlx4_en.h @@ -322,6 +322,10 @@ struct mlx4_en_port_profile { u32 rx_ring_num; u32 tx_ring_size; u32 rx_ring_size; + u8 rx_pause; + u8 rx_ppp; + u8 tx_pause; + u8 tx_ppp; }; struct mlx4_en_profile { @@ -333,10 +337,6 @@ struct mlx4_en_profile { int rx_moder_cnt; int rx_moder_time; int auto_moder; - u8 rx_pause; - u8 rx_ppp; - u8 tx_pause; - u8 tx_ppp; u8 no_reset; struct mlx4_en_port_profile prof[MLX4_MAX_PORTS + 1]; }; diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c index a9c8c08044b1..e513f76f2a9f 100644 --- a/drivers/net/mv643xx_eth.c +++ b/drivers/net/mv643xx_eth.c @@ -899,7 +899,8 @@ static int txq_reclaim(struct tx_queue *txq, int budget, int force) if (skb != NULL) { if (skb_queue_len(&mp->rx_recycle) < mp->default_rx_ring_size && - skb_recycle_check(skb, mp->skb_size)) + skb_recycle_check(skb, mp->skb_size + + dma_get_cache_alignment() - 1)) __skb_queue_head(&mp->rx_recycle, skb); else dev_kfree_skb(skb); @@ -1066,9 +1067,12 @@ static int smi_wait_ready(struct mv643xx_eth_shared_private *msp) return 0; } - if (!wait_event_timeout(msp->smi_busy_wait, smi_is_done(msp), - msecs_to_jiffies(100))) - return -ETIMEDOUT; + if (!smi_is_done(msp)) { + wait_event_timeout(msp->smi_busy_wait, smi_is_done(msp), + msecs_to_jiffies(100)); + if (!smi_is_done(msp)) + return -ETIMEDOUT; + } return 0; } @@ -2432,8 +2436,8 @@ static int mv643xx_eth_shared_remove(struct platform_device *pdev) struct mv643xx_eth_shared_platform_data *pd = pdev->dev.platform_data; if (pd == NULL || pd->shared_smi == NULL) { - mdiobus_free(msp->smi_bus); mdiobus_unregister(msp->smi_bus); + mdiobus_free(msp->smi_bus); } if (msp->err_interrupt != NO_IRQ) free_irq(msp->err_interrupt, msp); diff --git a/drivers/net/myri10ge/myri10ge.c b/drivers/net/myri10ge/myri10ge.c index a5f428bcc0eb..b37867097308 100644 --- a/drivers/net/myri10ge/myri10ge.c +++ b/drivers/net/myri10ge/myri10ge.c @@ -75,7 +75,7 @@ #include "myri10ge_mcp.h" #include "myri10ge_mcp_gen_header.h" -#define MYRI10GE_VERSION_STR "1.4.3-1.375" +#define MYRI10GE_VERSION_STR "1.4.3-1.378" MODULE_DESCRIPTION("Myricom 10G driver (10GbE)"); MODULE_AUTHOR("Maintainer: help@myri.com"); @@ -1393,6 +1393,7 @@ myri10ge_tx_done(struct myri10ge_slice_state *ss, int mcp_index) if (tx->req == tx->done) { tx->queue_active = 0; put_be32(htonl(1), tx->send_stop); + mb(); mmiowb(); } __netif_tx_unlock(dev_queue); @@ -2865,6 +2866,7 @@ again: if ((mgp->dev->real_num_tx_queues > 1) && tx->queue_active == 0) { tx->queue_active = 1; put_be32(htonl(1), tx->send_go); + mb(); mmiowb(); } tx->pkt_start++; diff --git a/drivers/net/niu.c b/drivers/net/niu.c index ebc812702903..1b6f548c4411 100644 --- a/drivers/net/niu.c +++ b/drivers/net/niu.c @@ -33,8 +33,8 @@ #define DRV_MODULE_NAME "niu" #define PFX DRV_MODULE_NAME ": " -#define DRV_MODULE_VERSION "0.9" -#define DRV_MODULE_RELDATE "May 4, 2008" +#define DRV_MODULE_VERSION "1.0" +#define DRV_MODULE_RELDATE "Nov 14, 2008" static char version[] __devinitdata = DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; @@ -51,8 +51,7 @@ MODULE_VERSION(DRV_MODULE_VERSION); #ifndef readq static u64 readq(void __iomem *reg) { - return (((u64)readl(reg + 0x4UL) << 32) | - (u64)readl(reg)); + return ((u64) readl(reg)) | (((u64) readl(reg + 4UL)) << 32); } static void writeq(u64 val, void __iomem *reg) @@ -407,7 +406,7 @@ static int esr2_set_rx_cfg(struct niu *np, unsigned long channel, u32 val) } /* Mode is always 10G fiber. */ -static int serdes_init_niu(struct niu *np) +static int serdes_init_niu_10g_fiber(struct niu *np) { struct niu_link_config *lp = &np->link_config; u32 tx_cfg, rx_cfg; @@ -444,6 +443,223 @@ static int serdes_init_niu(struct niu *np) return 0; } +static int serdes_init_niu_1g_serdes(struct niu *np) +{ + struct niu_link_config *lp = &np->link_config; + u16 pll_cfg, pll_sts; + int max_retry = 100; + u64 sig, mask, val; + u32 tx_cfg, rx_cfg; + unsigned long i; + int err; + + tx_cfg = (PLL_TX_CFG_ENTX | PLL_TX_CFG_SWING_1375MV | + PLL_TX_CFG_RATE_HALF); + rx_cfg = (PLL_RX_CFG_ENRX | PLL_RX_CFG_TERM_0P8VDDT | + PLL_RX_CFG_ALIGN_ENA | PLL_RX_CFG_LOS_LTHRESH | + PLL_RX_CFG_RATE_HALF); + + if (np->port == 0) + rx_cfg |= PLL_RX_CFG_EQ_LP_ADAPTIVE; + + if (lp->loopback_mode == LOOPBACK_PHY) { + u16 test_cfg = PLL_TEST_CFG_LOOPBACK_CML_DIS; + + mdio_write(np, np->port, NIU_ESR2_DEV_ADDR, + ESR2_TI_PLL_TEST_CFG_L, test_cfg); + + tx_cfg |= PLL_TX_CFG_ENTEST; + rx_cfg |= PLL_RX_CFG_ENTEST; + } + + /* Initialize PLL for 1G */ + pll_cfg = (PLL_CFG_ENPLL | PLL_CFG_MPY_8X); + + err = mdio_write(np, np->port, NIU_ESR2_DEV_ADDR, + ESR2_TI_PLL_CFG_L, pll_cfg); + if (err) { + dev_err(np->device, PFX "NIU Port %d " + "serdes_init_niu_1g_serdes: " + "mdio write to ESR2_TI_PLL_CFG_L failed", np->port); + return err; + } + + pll_sts = PLL_CFG_ENPLL; + + err = mdio_write(np, np->port, NIU_ESR2_DEV_ADDR, + ESR2_TI_PLL_STS_L, pll_sts); + if (err) { + dev_err(np->device, PFX "NIU Port %d " + "serdes_init_niu_1g_serdes: " + "mdio write to ESR2_TI_PLL_STS_L failed", np->port); + return err; + } + + udelay(200); + + /* Initialize all 4 lanes of the SERDES. */ + for (i = 0; i < 4; i++) { + err = esr2_set_tx_cfg(np, i, tx_cfg); + if (err) + return err; + } + + for (i = 0; i < 4; i++) { + err = esr2_set_rx_cfg(np, i, rx_cfg); + if (err) + return err; + } + + switch (np->port) { + case 0: + val = (ESR_INT_SRDY0_P0 | ESR_INT_DET0_P0); + mask = val; + break; + + case 1: + val = (ESR_INT_SRDY0_P1 | ESR_INT_DET0_P1); + mask = val; + break; + + default: + return -EINVAL; + } + + while (max_retry--) { + sig = nr64(ESR_INT_SIGNALS); + if ((sig & mask) == val) + break; + + mdelay(500); + } + + if ((sig & mask) != val) { + dev_err(np->device, PFX "Port %u signal bits [%08x] are not " + "[%08x]\n", np->port, (int) (sig & mask), (int) val); + return -ENODEV; + } + + return 0; +} + +static int serdes_init_niu_10g_serdes(struct niu *np) +{ + struct niu_link_config *lp = &np->link_config; + u32 tx_cfg, rx_cfg, pll_cfg, pll_sts; + int max_retry = 100; + u64 sig, mask, val; + unsigned long i; + int err; + + tx_cfg = (PLL_TX_CFG_ENTX | PLL_TX_CFG_SWING_1375MV); + rx_cfg = (PLL_RX_CFG_ENRX | PLL_RX_CFG_TERM_0P8VDDT | + PLL_RX_CFG_ALIGN_ENA | PLL_RX_CFG_LOS_LTHRESH | + PLL_RX_CFG_EQ_LP_ADAPTIVE); + + if (lp->loopback_mode == LOOPBACK_PHY) { + u16 test_cfg = PLL_TEST_CFG_LOOPBACK_CML_DIS; + + mdio_write(np, np->port, NIU_ESR2_DEV_ADDR, + ESR2_TI_PLL_TEST_CFG_L, test_cfg); + + tx_cfg |= PLL_TX_CFG_ENTEST; + rx_cfg |= PLL_RX_CFG_ENTEST; + } + + /* Initialize PLL for 10G */ + pll_cfg = (PLL_CFG_ENPLL | PLL_CFG_MPY_10X); + + err = mdio_write(np, np->port, NIU_ESR2_DEV_ADDR, + ESR2_TI_PLL_CFG_L, pll_cfg & 0xffff); + if (err) { + dev_err(np->device, PFX "NIU Port %d " + "serdes_init_niu_10g_serdes: " + "mdio write to ESR2_TI_PLL_CFG_L failed", np->port); + return err; + } + + pll_sts = PLL_CFG_ENPLL; + + err = mdio_write(np, np->port, NIU_ESR2_DEV_ADDR, + ESR2_TI_PLL_STS_L, pll_sts & 0xffff); + if (err) { + dev_err(np->device, PFX "NIU Port %d " + "serdes_init_niu_10g_serdes: " + "mdio write to ESR2_TI_PLL_STS_L failed", np->port); + return err; + } + + udelay(200); + + /* Initialize all 4 lanes of the SERDES. */ + for (i = 0; i < 4; i++) { + err = esr2_set_tx_cfg(np, i, tx_cfg); + if (err) + return err; + } + + for (i = 0; i < 4; i++) { + err = esr2_set_rx_cfg(np, i, rx_cfg); + if (err) + return err; + } + + /* check if serdes is ready */ + + switch (np->port) { + case 0: + mask = ESR_INT_SIGNALS_P0_BITS; + val = (ESR_INT_SRDY0_P0 | + ESR_INT_DET0_P0 | + ESR_INT_XSRDY_P0 | + ESR_INT_XDP_P0_CH3 | + ESR_INT_XDP_P0_CH2 | + ESR_INT_XDP_P0_CH1 | + ESR_INT_XDP_P0_CH0); + break; + + case 1: + mask = ESR_INT_SIGNALS_P1_BITS; + val = (ESR_INT_SRDY0_P1 | + ESR_INT_DET0_P1 | + ESR_INT_XSRDY_P1 | + ESR_INT_XDP_P1_CH3 | + ESR_INT_XDP_P1_CH2 | + ESR_INT_XDP_P1_CH1 | + ESR_INT_XDP_P1_CH0); + break; + + default: + return -EINVAL; + } + + while (max_retry--) { + sig = nr64(ESR_INT_SIGNALS); + if ((sig & mask) == val) + break; + + mdelay(500); + } + + if ((sig & mask) != val) { + pr_info(PFX "NIU Port %u signal bits [%08x] are not " + "[%08x] for 10G...trying 1G\n", + np->port, (int) (sig & mask), (int) val); + + /* 10G failed, try initializing at 1G */ + err = serdes_init_niu_1g_serdes(np); + if (!err) { + np->flags &= ~NIU_FLAGS_10G; + np->mac_xcvr = MAC_XCVR_PCS; + } else { + dev_err(np->device, PFX "Port %u 10G/1G SERDES " + "Link Failed \n", np->port); + return -ENODEV; + } + } + return 0; +} + static int esr_read_rxtx_ctrl(struct niu *np, unsigned long chan, u32 *val) { int err; @@ -1955,13 +2171,23 @@ static const struct niu_phy_ops phy_ops_10g_serdes = { .link_status = link_status_10g_serdes, }; +static const struct niu_phy_ops phy_ops_10g_serdes_niu = { + .serdes_init = serdes_init_niu_10g_serdes, + .link_status = link_status_10g_serdes, +}; + +static const struct niu_phy_ops phy_ops_1g_serdes_niu = { + .serdes_init = serdes_init_niu_1g_serdes, + .link_status = link_status_1g_serdes, +}; + static const struct niu_phy_ops phy_ops_1g_rgmii = { .xcvr_init = xcvr_init_1g_rgmii, .link_status = link_status_1g_rgmii, }; static const struct niu_phy_ops phy_ops_10g_fiber_niu = { - .serdes_init = serdes_init_niu, + .serdes_init = serdes_init_niu_10g_fiber, .xcvr_init = xcvr_init_10g, .link_status = link_status_10g, }; @@ -1999,11 +2225,21 @@ struct niu_phy_template { u32 phy_addr_base; }; -static const struct niu_phy_template phy_template_niu = { +static const struct niu_phy_template phy_template_niu_10g_fiber = { .ops = &phy_ops_10g_fiber_niu, .phy_addr_base = 16, }; +static const struct niu_phy_template phy_template_niu_10g_serdes = { + .ops = &phy_ops_10g_serdes_niu, + .phy_addr_base = 0, +}; + +static const struct niu_phy_template phy_template_niu_1g_serdes = { + .ops = &phy_ops_1g_serdes_niu, + .phy_addr_base = 0, +}; + static const struct niu_phy_template phy_template_10g_fiber = { .ops = &phy_ops_10g_fiber, .phy_addr_base = 8, @@ -2183,8 +2419,25 @@ static int niu_determine_phy_disposition(struct niu *np) u32 phy_addr_off = 0; if (plat_type == PLAT_TYPE_NIU) { - tp = &phy_template_niu; - phy_addr_off += np->port; + switch (np->flags & + (NIU_FLAGS_10G | + NIU_FLAGS_FIBER | + NIU_FLAGS_XCVR_SERDES)) { + case NIU_FLAGS_10G | NIU_FLAGS_XCVR_SERDES: + /* 10G Serdes */ + tp = &phy_template_niu_10g_serdes; + break; + case NIU_FLAGS_XCVR_SERDES: + /* 1G Serdes */ + tp = &phy_template_niu_1g_serdes; + break; + case NIU_FLAGS_10G | NIU_FLAGS_FIBER: + /* 10G Fiber */ + default: + tp = &phy_template_niu_10g_fiber; + phy_addr_off += np->port; + break; + } } else { switch (np->flags & (NIU_FLAGS_10G | @@ -7214,6 +7467,12 @@ static int __devinit niu_phy_type_prop_decode(struct niu *np, np->flags |= NIU_FLAGS_10G; np->flags &= ~NIU_FLAGS_FIBER; np->mac_xcvr = MAC_XCVR_XPCS; + } else if (!strcmp(phy_prop, "xgsd") || !strcmp(phy_prop, "gsd")) { + /* 10G Serdes or 1G Serdes, default to 10G */ + np->flags |= NIU_FLAGS_10G; + np->flags &= ~NIU_FLAGS_FIBER; + np->flags |= NIU_FLAGS_XCVR_SERDES; + np->mac_xcvr = MAC_XCVR_XPCS; } else { return -EINVAL; } @@ -7742,6 +8001,8 @@ static int __devinit walk_phys(struct niu *np, struct niu_parent *parent) u32 val; int err; + num_10g = num_1g = 0; + if (!strcmp(np->vpd.model, NIU_ALONSO_MDL_STR) || !strcmp(np->vpd.model, NIU_KIMI_MDL_STR)) { num_10g = 0; @@ -7758,6 +8019,16 @@ static int __devinit walk_phys(struct niu *np, struct niu_parent *parent) parent->num_ports = 2; val = (phy_encode(PORT_TYPE_10G, 0) | phy_encode(PORT_TYPE_10G, 1)); + } else if ((np->flags & NIU_FLAGS_XCVR_SERDES) && + (parent->plat_type == PLAT_TYPE_NIU)) { + /* this is the Monza case */ + if (np->flags & NIU_FLAGS_10G) { + val = (phy_encode(PORT_TYPE_10G, 0) | + phy_encode(PORT_TYPE_10G, 1)); + } else { + val = (phy_encode(PORT_TYPE_1G, 0) | + phy_encode(PORT_TYPE_1G, 1)); + } } else { err = fill_phy_probe_info(np, parent, info); if (err) @@ -8657,7 +8928,9 @@ static void __devinit niu_device_announce(struct niu *np) dev->name, (np->flags & NIU_FLAGS_XMAC ? "XMAC" : "BMAC"), (np->flags & NIU_FLAGS_10G ? "10G" : "1G"), - (np->flags & NIU_FLAGS_FIBER ? "FIBER" : "COPPER"), + (np->flags & NIU_FLAGS_FIBER ? "FIBER" : + (np->flags & NIU_FLAGS_XCVR_SERDES ? "SERDES" : + "COPPER")), (np->mac_xcvr == MAC_XCVR_MII ? "MII" : (np->mac_xcvr == MAC_XCVR_PCS ? "PCS" : "XPCS")), np->vpd.phy_type); @@ -8667,7 +8940,6 @@ static void __devinit niu_device_announce(struct niu *np) static int __devinit niu_pci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { - unsigned long niureg_base, niureg_len; union niu_parent_id parent_id; struct net_device *dev; struct niu *np; @@ -8758,10 +9030,7 @@ static int __devinit niu_pci_init_one(struct pci_dev *pdev, dev->features |= (NETIF_F_SG | NETIF_F_HW_CSUM); - niureg_base = pci_resource_start(pdev, 0); - niureg_len = pci_resource_len(pdev, 0); - - np->regs = ioremap_nocache(niureg_base, niureg_len); + np->regs = pci_ioremap_bar(pdev, 0); if (!np->regs) { dev_err(&pdev->dev, PFX "Cannot map device registers, " "aborting.\n"); diff --git a/drivers/net/niu.h b/drivers/net/niu.h index c6fa883daa22..180ca8ae93de 100644 --- a/drivers/net/niu.h +++ b/drivers/net/niu.h @@ -1048,6 +1048,13 @@ #define PLL_CFG_LD_SHIFT 8 #define PLL_CFG_MPY 0x0000001e #define PLL_CFG_MPY_SHIFT 1 +#define PLL_CFG_MPY_4X 0x0 +#define PLL_CFG_MPY_5X 0x00000002 +#define PLL_CFG_MPY_6X 0x00000004 +#define PLL_CFG_MPY_8X 0x00000008 +#define PLL_CFG_MPY_10X 0x0000000a +#define PLL_CFG_MPY_12X 0x0000000c +#define PLL_CFG_MPY_12P5X 0x0000000e #define PLL_CFG_ENPLL 0x00000001 #define ESR2_TI_PLL_STS_L (ESR2_BASE + 0x002) @@ -1093,6 +1100,9 @@ #define PLL_TX_CFG_INVPAIR 0x00000080 #define PLL_TX_CFG_RATE 0x00000060 #define PLL_TX_CFG_RATE_SHIFT 5 +#define PLL_TX_CFG_RATE_FULL 0x0 +#define PLL_TX_CFG_RATE_HALF 0x20 +#define PLL_TX_CFG_RATE_QUAD 0x40 #define PLL_TX_CFG_BUSWIDTH 0x0000001c #define PLL_TX_CFG_BUSWIDTH_SHIFT 2 #define PLL_TX_CFG_ENTEST 0x00000002 @@ -1132,6 +1142,9 @@ #define PLL_RX_CFG_INVPAIR 0x00000080 #define PLL_RX_CFG_RATE 0x00000060 #define PLL_RX_CFG_RATE_SHIFT 5 +#define PLL_RX_CFG_RATE_FULL 0x0 +#define PLL_RX_CFG_RATE_HALF 0x20 +#define PLL_RX_CFG_RATE_QUAD 0x40 #define PLL_RX_CFG_BUSWIDTH 0x0000001c #define PLL_RX_CFG_BUSWIDTH_SHIFT 2 #define PLL_RX_CFG_ENTEST 0x00000002 diff --git a/drivers/net/pcmcia/axnet_cs.c b/drivers/net/pcmcia/axnet_cs.c index b37a498939ae..0418045166c3 100644 --- a/drivers/net/pcmcia/axnet_cs.c +++ b/drivers/net/pcmcia/axnet_cs.c @@ -779,6 +779,7 @@ static struct pcmcia_device_id axnet_ids[] = { PCMCIA_DEVICE_PROD_ID12("IO DATA", "ETXPCM", 0x547e66dc, 0x233adac2), PCMCIA_DEVICE_PROD_ID12("Linksys", "EtherFast 10/100 PC Card (PCMPC100 V3)", 0x0733cc81, 0x232019a8), PCMCIA_DEVICE_PROD_ID12("MELCO", "LPC3-TX", 0x481e0094, 0xf91af609), + PCMCIA_DEVICE_PROD_ID12("NETGEAR", "FA411", 0x9aa79dc3, 0x40fad875), PCMCIA_DEVICE_PROD_ID12("PCMCIA", "100BASE", 0x281f1c5d, 0x7c2add04), PCMCIA_DEVICE_PROD_ID12("PCMCIA", "FastEtherCard", 0x281f1c5d, 0x7ef26116), PCMCIA_DEVICE_PROD_ID12("PCMCIA", "FEP501", 0x281f1c5d, 0x2e272058), @@ -1174,7 +1175,6 @@ static int ei_start_xmit(struct sk_buff *skb, struct net_device *dev) * ax_interrupt - handle the interrupts from an 8390 * @irq: interrupt number * @dev_id: a pointer to the net_device - * @regs: unused * * Handle the ether interface interrupts. We pull packets from * the 8390 via the card specific functions and fire them at the networking diff --git a/drivers/net/pcmcia/ibmtr_cs.c b/drivers/net/pcmcia/ibmtr_cs.c index cf3cca4642f2..f51944b28cfa 100644 --- a/drivers/net/pcmcia/ibmtr_cs.c +++ b/drivers/net/pcmcia/ibmtr_cs.c @@ -349,7 +349,7 @@ static int ibmtr_suspend(struct pcmcia_device *link) return 0; } -static int ibmtr_resume(struct pcmcia_device *link) +static int __devinit ibmtr_resume(struct pcmcia_device *link) { ibmtr_dev_t *info = link->priv; struct net_device *dev = info->dev; diff --git a/drivers/net/pcmcia/pcnet_cs.c b/drivers/net/pcmcia/pcnet_cs.c index e40d6301aa7a..ce486f094492 100644 --- a/drivers/net/pcmcia/pcnet_cs.c +++ b/drivers/net/pcmcia/pcnet_cs.c @@ -1693,7 +1693,6 @@ static struct pcmcia_device_id pcnet_ids[] = { PCMCIA_DEVICE_PROD_ID12("National Semiconductor", "InfoMover NE4100", 0x36e1191f, 0xa6617ec8), PCMCIA_DEVICE_PROD_ID12("NEC", "PC-9801N-J12", 0x18df0ba0, 0xbc912d76), PCMCIA_DEVICE_PROD_ID12("NETGEAR", "FA410TX", 0x9aa79dc3, 0x60e5bc0e), - PCMCIA_DEVICE_PROD_ID12("NETGEAR", "FA411", 0x9aa79dc3, 0x40fad875), PCMCIA_DEVICE_PROD_ID12("Network Everywhere", "Fast Ethernet 10/100 PC Card", 0x820a67b6, 0x31ed1a5f), PCMCIA_DEVICE_PROD_ID12("NextCom K.K.", "Next Hawk", 0xaedaec74, 0xad050ef1), PCMCIA_DEVICE_PROD_ID12("PCMCIA", "10/100Mbps Ethernet Card", 0x281f1c5d, 0x6e41773b), diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index 4aa547947040..eb6411c4694f 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -227,6 +227,59 @@ static int m88e1111_config_init(struct phy_device *phydev) return 0; } +static int m88e1118_config_aneg(struct phy_device *phydev) +{ + int err; + + err = phy_write(phydev, MII_BMCR, BMCR_RESET); + if (err < 0) + return err; + + err = phy_write(phydev, MII_M1011_PHY_SCR, + MII_M1011_PHY_SCR_AUTO_CROSS); + if (err < 0) + return err; + + err = genphy_config_aneg(phydev); + return 0; +} + +static int m88e1118_config_init(struct phy_device *phydev) +{ + int err; + + /* Change address */ + err = phy_write(phydev, 0x16, 0x0002); + if (err < 0) + return err; + + /* Enable 1000 Mbit */ + err = phy_write(phydev, 0x15, 0x1070); + if (err < 0) + return err; + + /* Change address */ + err = phy_write(phydev, 0x16, 0x0003); + if (err < 0) + return err; + + /* Adjust LED Control */ + err = phy_write(phydev, 0x10, 0x021e); + if (err < 0) + return err; + + /* Reset address */ + err = phy_write(phydev, 0x16, 0x0); + if (err < 0) + return err; + + err = phy_write(phydev, MII_BMCR, BMCR_RESET); + if (err < 0) + return err; + + return 0; +} + static int m88e1145_config_init(struct phy_device *phydev) { int err; @@ -416,6 +469,19 @@ static struct phy_driver marvell_drivers[] = { .driver = { .owner = THIS_MODULE }, }, { + .phy_id = 0x01410e10, + .phy_id_mask = 0xfffffff0, + .name = "Marvell 88E1118", + .features = PHY_GBIT_FEATURES, + .flags = PHY_HAS_INTERRUPT, + .config_init = &m88e1118_config_init, + .config_aneg = &m88e1118_config_aneg, + .read_status = &genphy_read_status, + .ack_interrupt = &marvell_ack_interrupt, + .config_intr = &marvell_config_intr, + .driver = {.owner = THIS_MODULE,}, + }, + { .phy_id = 0x01410cd0, .phy_id_mask = 0xfffffff0, .name = "Marvell 88E1145", diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index d0ed1ef284a8..536bda1f428b 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -136,7 +136,7 @@ void mdiobus_unregister(struct mii_bus *bus) BUG_ON(bus->state != MDIOBUS_REGISTERED); bus->state = MDIOBUS_UNREGISTERED; - device_unregister(&bus->dev); + device_del(&bus->dev); for (i = 0; i < PHY_MAX_ADDR; i++) { if (bus->phy_map[i]) device_unregister(&bus->phy_map[i]->dev); diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index e11b03b2b25a..25acbbde4a60 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -227,8 +227,17 @@ struct phy_device * get_phy_device(struct mii_bus *bus, int addr) if (r) return ERR_PTR(r); - /* If the phy_id is all Fs, there is no device there */ - if (0xffffffff == phy_id) + /* If the phy_id is mostly Fs, there is no device there */ + if ((phy_id & 0x1fffffff) == 0x1fffffff) + return NULL; + + /* + * Broken hardware is sometimes missing the pull down resistor on the + * MDIO line, which results in reads to non-existent devices returning + * 0 rather than 0xffff. Catch this here and treat 0 as a non-existent + * device as well. + */ + if (phy_id == 0) return NULL; dev = phy_device_create(bus, addr, phy_id); @@ -564,20 +573,32 @@ EXPORT_SYMBOL(genphy_restart_aneg); */ int genphy_config_aneg(struct phy_device *phydev) { - int result = 0; + int result; - if (AUTONEG_ENABLE == phydev->autoneg) { - int result = genphy_config_advert(phydev); + if (AUTONEG_ENABLE != phydev->autoneg) + return genphy_setup_forced(phydev); - if (result < 0) /* error */ - return result; + result = genphy_config_advert(phydev); + + if (result < 0) /* error */ + return result; + + if (result == 0) { + /* Advertisment hasn't changed, but maybe aneg was never on to + * begin with? Or maybe phy was isolated? */ + int ctl = phy_read(phydev, MII_BMCR); + + if (ctl < 0) + return ctl; + + if (!(ctl & BMCR_ANENABLE) || (ctl & BMCR_ISOLATE)) + result = 1; /* do restart aneg */ + } - /* Only restart aneg if we are advertising something different - * than we were before. */ - if (result > 0) - result = genphy_restart_aneg(phydev); - } else - result = genphy_setup_forced(phydev); + /* Only restart aneg if we are advertising something different + * than we were before. */ + if (result > 0) + result = genphy_restart_aneg(phydev); return result; } diff --git a/drivers/net/phy/vitesse.c b/drivers/net/phy/vitesse.c index 8874497b6bbf..dd3b2447e85a 100644 --- a/drivers/net/phy/vitesse.c +++ b/drivers/net/phy/vitesse.c @@ -34,6 +34,8 @@ #define MII_VSC8244_IMASK_DUPLEX 0x1000 #define MII_VSC8244_IMASK_MASK 0xf000 +#define MII_VSC8221_IMASK_MASK 0xa000 + /* Vitesse Interrupt Status Register */ #define MII_VSC8244_ISTAT 0x1a #define MII_VSC8244_ISTAT_STATUS 0x8000 @@ -49,6 +51,12 @@ #define MII_VSC8244_AUXCONSTAT_GBIT 0x0010 #define MII_VSC8244_AUXCONSTAT_100 0x0008 +#define MII_VSC8221_AUXCONSTAT_INIT 0x0004 /* need to set this bit? */ +#define MII_VSC8221_AUXCONSTAT_RESERVED 0x0004 + +#define PHY_ID_VSC8244 0x000fc6c0 +#define PHY_ID_VSC8221 0x000fc550 + MODULE_DESCRIPTION("Vitesse PHY driver"); MODULE_AUTHOR("Kriston Carson"); MODULE_LICENSE("GPL"); @@ -95,13 +103,15 @@ static int vsc824x_ack_interrupt(struct phy_device *phydev) return (err < 0) ? err : 0; } -static int vsc824x_config_intr(struct phy_device *phydev) +static int vsc82xx_config_intr(struct phy_device *phydev) { int err; if (phydev->interrupts == PHY_INTERRUPT_ENABLED) err = phy_write(phydev, MII_VSC8244_IMASK, - MII_VSC8244_IMASK_MASK); + phydev->drv->phy_id == PHY_ID_VSC8244 ? + MII_VSC8244_IMASK_MASK : + MII_VSC8221_IMASK_MASK); else { /* * The Vitesse PHY cannot clear the interrupt @@ -120,7 +130,7 @@ static int vsc824x_config_intr(struct phy_device *phydev) /* Vitesse 824x */ static struct phy_driver vsc8244_driver = { - .phy_id = 0x000fc6c0, + .phy_id = PHY_ID_VSC8244, .name = "Vitesse VSC8244", .phy_id_mask = 0x000fffc0, .features = PHY_GBIT_FEATURES, @@ -129,19 +139,55 @@ static struct phy_driver vsc8244_driver = { .config_aneg = &genphy_config_aneg, .read_status = &genphy_read_status, .ack_interrupt = &vsc824x_ack_interrupt, - .config_intr = &vsc824x_config_intr, + .config_intr = &vsc82xx_config_intr, .driver = { .owner = THIS_MODULE,}, }; -static int __init vsc8244_init(void) +static int vsc8221_config_init(struct phy_device *phydev) { - return phy_driver_register(&vsc8244_driver); + int err; + + err = phy_write(phydev, MII_VSC8244_AUX_CONSTAT, + MII_VSC8221_AUXCONSTAT_INIT); + return err; + + /* Perhaps we should set EXT_CON1 based on the interface? + Options are 802.3Z SerDes or SGMII */ +} + +/* Vitesse 8221 */ +static struct phy_driver vsc8221_driver = { + .phy_id = PHY_ID_VSC8221, + .phy_id_mask = 0x000ffff0, + .name = "Vitesse VSC8221", + .features = PHY_GBIT_FEATURES, + .flags = PHY_HAS_INTERRUPT, + .config_init = &vsc8221_config_init, + .config_aneg = &genphy_config_aneg, + .read_status = &genphy_read_status, + .ack_interrupt = &vsc824x_ack_interrupt, + .config_intr = &vsc82xx_config_intr, + .driver = { .owner = THIS_MODULE,}, +}; + +static int __init vsc82xx_init(void) +{ + int err; + + err = phy_driver_register(&vsc8244_driver); + if (err < 0) + return err; + err = phy_driver_register(&vsc8221_driver); + if (err < 0) + phy_driver_unregister(&vsc8244_driver); + return err; } -static void __exit vsc8244_exit(void) +static void __exit vsc82xx_exit(void) { phy_driver_unregister(&vsc8244_driver); + phy_driver_unregister(&vsc8221_driver); } -module_init(vsc8244_init); -module_exit(vsc8244_exit); +module_init(vsc82xx_init); +module_exit(vsc82xx_exit); diff --git a/drivers/net/pppol2tp.c b/drivers/net/pppol2tp.c index 185b1dff10a8..e98d9773158d 100644 --- a/drivers/net/pppol2tp.c +++ b/drivers/net/pppol2tp.c @@ -1353,6 +1353,7 @@ static int pppol2tp_release(struct socket *sock) kfree_skb(skb); sock_put(sk); } + sock_put(sk); } release_sock(sk); diff --git a/drivers/net/qla3xxx.c b/drivers/net/qla3xxx.c index 3cdd07c45b6d..508452c02151 100644 --- a/drivers/net/qla3xxx.c +++ b/drivers/net/qla3xxx.c @@ -1515,9 +1515,6 @@ static u32 ql_get_link_state(struct ql3_adapter *qdev) linkState = LS_UP; } else { linkState = LS_DOWN; - if (netif_msg_link(qdev)) - printk(KERN_WARNING PFX - "%s: Link is down.\n", qdev->ndev->name); } return linkState; } @@ -1581,10 +1578,6 @@ static int ql_finish_auto_neg(struct ql3_adapter *qdev) ql_mac_enable(qdev, 1); } - if (netif_msg_link(qdev)) - printk(KERN_DEBUG PFX - "%s: Change port_link_state LS_DOWN to LS_UP.\n", - qdev->ndev->name); qdev->port_link_state = LS_UP; netif_start_queue(qdev->ndev); netif_carrier_on(qdev->ndev); @@ -1655,14 +1648,9 @@ static void ql_link_state_machine_work(struct work_struct *work) /* Fall Through */ case LS_DOWN: - if (netif_msg_link(qdev)) - printk(KERN_DEBUG PFX - "%s: port_link_state = LS_DOWN.\n", - qdev->ndev->name); if (curr_link_state == LS_UP) { if (netif_msg_link(qdev)) - printk(KERN_DEBUG PFX - "%s: curr_link_state = LS_UP.\n", + printk(KERN_INFO PFX "%s: Link is up.\n", qdev->ndev->name); if (ql_is_auto_neg_complete(qdev)) ql_finish_auto_neg(qdev); @@ -1670,6 +1658,7 @@ static void ql_link_state_machine_work(struct work_struct *work) if (qdev->port_link_state == LS_UP) ql_link_down_detect_clear(qdev); + qdev->port_link_state = LS_UP; } break; @@ -1678,12 +1667,14 @@ static void ql_link_state_machine_work(struct work_struct *work) * See if the link is currently down or went down and came * back up */ - if ((curr_link_state == LS_DOWN) || ql_link_down_detect(qdev)) { + if (curr_link_state == LS_DOWN) { if (netif_msg_link(qdev)) printk(KERN_INFO PFX "%s: Link is down.\n", qdev->ndev->name); qdev->port_link_state = LS_DOWN; } + if (ql_link_down_detect(qdev)) + qdev->port_link_state = LS_DOWN; break; } spin_unlock_irqrestore(&qdev->hw_lock, hw_flags); diff --git a/drivers/net/sfc/ethtool.c b/drivers/net/sfc/ethtool.c index fa98af58223e..cd0d0873d978 100644 --- a/drivers/net/sfc/ethtool.c +++ b/drivers/net/sfc/ethtool.c @@ -174,8 +174,8 @@ static struct efx_ethtool_stat efx_ethtool_stats[] = { /* EEPROM range with gPXE configuration */ #define EFX_ETHTOOL_EEPROM_MAGIC 0xEFAB -#define EFX_ETHTOOL_EEPROM_MIN 0x100U -#define EFX_ETHTOOL_EEPROM_MAX 0x400U +#define EFX_ETHTOOL_EEPROM_MIN 0x800U +#define EFX_ETHTOOL_EEPROM_MAX 0x1800U /************************************************************************** * diff --git a/drivers/net/sh_eth.c b/drivers/net/sh_eth.c index a24bb68887ab..59f242a67714 100644 --- a/drivers/net/sh_eth.c +++ b/drivers/net/sh_eth.c @@ -927,7 +927,7 @@ static int sh_eth_start_xmit(struct sk_buff *skb, struct net_device *ndev) struct sh_eth_private *mdp = netdev_priv(ndev); struct sh_eth_txdesc *txdesc; u32 entry; - int flags; + unsigned long flags; spin_lock_irqsave(&mdp->lock, flags); if ((mdp->cur_tx - mdp->dirty_tx) >= (TX_RING_SIZE - 4)) { @@ -1141,7 +1141,7 @@ static int sh_mdio_init(struct net_device *ndev, int id) /* Hook up MII support for ethtool */ mdp->mii_bus->name = "sh_mii"; mdp->mii_bus->parent = &ndev->dev; - mdp->mii_bus->id[0] = id; + snprintf(mdp->mii_bus->id, MII_BUS_ID_SIZE, "%x", id); /* PHY IRQ */ mdp->mii_bus->irq = kmalloc(sizeof(int)*PHY_MAX_ADDR, GFP_KERNEL); diff --git a/drivers/net/sis900.c b/drivers/net/sis900.c index fa3a460f8e2f..8e8337e8b072 100644 --- a/drivers/net/sis900.c +++ b/drivers/net/sis900.c @@ -1630,7 +1630,6 @@ sis900_start_xmit(struct sk_buff *skb, struct net_device *net_dev) * sis900_interrupt - sis900 interrupt handler * @irq: the irq number * @dev_instance: the client data object - * @regs: snapshot of processor context * * The interrupt handler does all of the Rx thread work, * and cleans up after the Tx thread diff --git a/drivers/net/smc911x.c b/drivers/net/smc911x.c index f59c7772f344..9a16a79b67d0 100644 --- a/drivers/net/smc911x.c +++ b/drivers/net/smc911x.c @@ -499,7 +499,7 @@ static void smc911x_hardware_send_pkt(struct net_device *dev) #else SMC_PUSH_DATA(lp, buf, len); dev->trans_start = jiffies; - dev_kfree_skb(skb); + dev_kfree_skb_irq(skb); #endif if (!lp->tx_throttle) { netif_wake_queue(dev); @@ -1735,7 +1735,7 @@ static const struct ethtool_ops smc911x_ethtool_ops = { * This routine has a simple purpose -- make the SMC chip generate an * interrupt, so an auto-detect routine can detect it, and find the IRQ, */ -static int __init smc911x_findirq(struct net_device *dev) +static int __devinit smc911x_findirq(struct net_device *dev) { struct smc911x_local *lp = netdev_priv(dev); int timeout = 20; @@ -1799,7 +1799,7 @@ static int __init smc911x_findirq(struct net_device *dev) * o actually GRAB the irq. * o GRAB the region */ -static int __init smc911x_probe(struct net_device *dev) +static int __devinit smc911x_probe(struct net_device *dev) { struct smc911x_local *lp = netdev_priv(dev); int i, retval; @@ -1813,7 +1813,7 @@ static int __init smc911x_probe(struct net_device *dev) val = SMC_GET_BYTE_TEST(lp); DBG(SMC_DEBUG_MISC, "%s: endian probe returned 0x%04x\n", CARDNAME, val); if (val != 0x87654321) { - printk(KERN_ERR "Invalid chip endian 0x08%x\n",val); + printk(KERN_ERR "Invalid chip endian 0x%08x\n",val); retval = -ENODEV; goto err_out; } @@ -2048,9 +2048,11 @@ err_out: * 0 --> there is a device * anything else, error */ -static int smc911x_drv_probe(struct platform_device *pdev) +static int __devinit smc911x_drv_probe(struct platform_device *pdev) { +#ifdef SMC_DYNAMIC_BUS_CONFIG struct smc911x_platdata *pd = pdev->dev.platform_data; +#endif struct net_device *ndev; struct resource *res; struct smc911x_local *lp; @@ -2122,7 +2124,7 @@ out: return ret; } -static int smc911x_drv_remove(struct platform_device *pdev) +static int __devexit smc911x_drv_remove(struct platform_device *pdev) { struct net_device *ndev = platform_get_drvdata(pdev); struct smc911x_local *lp = netdev_priv(ndev); @@ -2182,9 +2184,9 @@ static int smc911x_drv_resume(struct platform_device *dev) if (netif_running(ndev)) { smc911x_reset(ndev); - smc911x_enable(ndev); if (lp->phy_type != 0) smc911x_phy_configure(&lp->phy_configure); + smc911x_enable(ndev); netif_device_attach(ndev); } } @@ -2193,7 +2195,7 @@ static int smc911x_drv_resume(struct platform_device *dev) static struct platform_driver smc911x_driver = { .probe = smc911x_drv_probe, - .remove = smc911x_drv_remove, + .remove = __devexit_p(smc911x_drv_remove), .suspend = smc911x_drv_suspend, .resume = smc911x_drv_resume, .driver = { diff --git a/drivers/net/smc91x.c b/drivers/net/smc91x.c index 6f9895d4e5bd..35c56abf4113 100644 --- a/drivers/net/smc91x.c +++ b/drivers/net/smc91x.c @@ -1696,7 +1696,7 @@ static const struct ethtool_ops smc_ethtool_ops = { * I just deleted auto_irq.c, since it was never built... * --jgarzik */ -static int __init smc_findirq(struct smc_local *lp) +static int __devinit smc_findirq(struct smc_local *lp) { void __iomem *ioaddr = lp->base; int timeout = 20; @@ -1770,7 +1770,7 @@ static int __init smc_findirq(struct smc_local *lp) * o actually GRAB the irq. * o GRAB the region */ -static int __init smc_probe(struct net_device *dev, void __iomem *ioaddr, +static int __devinit smc_probe(struct net_device *dev, void __iomem *ioaddr, unsigned long irq_flags) { struct smc_local *lp = netdev_priv(dev); @@ -2060,6 +2060,7 @@ static int smc_request_attrib(struct platform_device *pdev, struct net_device *ndev) { struct resource * res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-attrib"); + struct smc_local *lp __maybe_unused = netdev_priv(ndev); if (!res) return 0; @@ -2074,6 +2075,7 @@ static void smc_release_attrib(struct platform_device *pdev, struct net_device *ndev) { struct resource * res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-attrib"); + struct smc_local *lp __maybe_unused = netdev_priv(ndev); if (res) release_mem_region(res->start, ATTRIB_SIZE); @@ -2124,7 +2126,7 @@ static void smc_release_datacs(struct platform_device *pdev, struct net_device * * 0 --> there is a device * anything else, error */ -static int smc_drv_probe(struct platform_device *pdev) +static int __devinit smc_drv_probe(struct platform_device *pdev) { struct smc91x_platdata *pd = pdev->dev.platform_data; struct smc_local *lp; @@ -2238,7 +2240,7 @@ static int smc_drv_probe(struct platform_device *pdev) return ret; } -static int smc_drv_remove(struct platform_device *pdev) +static int __devexit smc_drv_remove(struct platform_device *pdev) { struct net_device *ndev = platform_get_drvdata(pdev); struct smc_local *lp = netdev_priv(ndev); @@ -2303,7 +2305,7 @@ static int smc_drv_resume(struct platform_device *dev) static struct platform_driver smc_driver = { .probe = smc_drv_probe, - .remove = smc_drv_remove, + .remove = __devexit_p(smc_drv_remove), .suspend = smc_drv_suspend, .resume = smc_drv_resume, .driver = { diff --git a/drivers/net/smc91x.h b/drivers/net/smc91x.h index a07cc9351c6b..37e2cb4f4f88 100644 --- a/drivers/net/smc91x.h +++ b/drivers/net/smc91x.h @@ -527,7 +527,8 @@ struct smc_local { * as RX which can overrun memory and lose packets. */ #include <linux/dma-mapping.h> -#include <asm/dma.h> +#include <mach/dma.h> +#include <mach/hardware.h> #include <mach/pxa-regs.h> #ifdef SMC_insl diff --git a/drivers/net/spider_net.c b/drivers/net/spider_net.c index b6435d0d71f9..07599b492359 100644 --- a/drivers/net/spider_net.c +++ b/drivers/net/spider_net.c @@ -672,7 +672,6 @@ write_hash: /** * spider_net_prepare_tx_descr - fill tx descriptor with skb data * @card: card structure - * @descr: descriptor structure to fill out * @skb: packet to use * * returns 0 on success, <0 on failure. @@ -867,7 +866,6 @@ spider_net_release_tx_chain(struct spider_net_card *card, int brutal) /** * spider_net_kick_tx_dma - enables TX DMA processing * @card: card structure - * @descr: descriptor address to enable TX processing at * * This routine will start the transmit DMA running if * it is not already running. This routine ned only be @@ -1637,7 +1635,6 @@ spider_net_handle_error_irq(struct spider_net_card *card, u32 status_reg, * spider_net_interrupt - interrupt handler for spider_net * @irq: interrupt number * @ptr: pointer to net_device - * @regs: PU registers * * returns IRQ_HANDLED, if interrupt was for driver, or IRQ_NONE, if no * interrupt found raised by card. @@ -2419,7 +2416,6 @@ spider_net_undo_pci_setup(struct spider_net_card *card) /** * spider_net_setup_pci_dev - sets up the device in terms of PCI operations - * @card: card structure * @pdev: PCI device * * Returns the card structure or NULL if any errors occur diff --git a/drivers/net/sungem.c b/drivers/net/sungem.c index 4291458955ef..1349e419673c 100644 --- a/drivers/net/sungem.c +++ b/drivers/net/sungem.c @@ -1714,7 +1714,7 @@ static void gem_init_phy(struct gem *gp) /* Reset PCS unit. */ val = readl(gp->regs + PCS_MIICTRL); val |= PCS_MIICTRL_RST; - writeb(val, gp->regs + PCS_MIICTRL); + writel(val, gp->regs + PCS_MIICTRL); limit = 32; while (readl(gp->regs + PCS_MIICTRL) & PCS_MIICTRL_RST) { diff --git a/drivers/net/ucc_geth_ethtool.c b/drivers/net/ucc_geth_ethtool.c index cfbbfee55836..68a7f5414133 100644 --- a/drivers/net/ucc_geth_ethtool.c +++ b/drivers/net/ucc_geth_ethtool.c @@ -37,7 +37,6 @@ #include <asm/irq.h> #include <asm/uaccess.h> #include <asm/types.h> -#include <asm/uaccess.h> #include "ucc_geth.h" #include "ucc_geth_mii.h" @@ -324,17 +323,17 @@ static void uec_get_ethtool_stats(struct net_device *netdev, if (stats_mode & UCC_GETH_STATISTICS_GATHERING_MODE_HARDWARE) { base = (u32 __iomem *)&ugeth->ug_regs->tx64; for (i = 0; i < UEC_HW_STATS_LEN; i++) - data[j++] = (u64)in_be32(&base[i]); + data[j++] = in_be32(&base[i]); } if (stats_mode & UCC_GETH_STATISTICS_GATHERING_MODE_FIRMWARE_TX) { base = (u32 __iomem *)ugeth->p_tx_fw_statistics_pram; for (i = 0; i < UEC_TX_FW_STATS_LEN; i++) - data[j++] = (u64)in_be32(&base[i]); + data[j++] = base ? in_be32(&base[i]) : 0; } if (stats_mode & UCC_GETH_STATISTICS_GATHERING_MODE_FIRMWARE_RX) { base = (u32 __iomem *)ugeth->p_rx_fw_statistics_pram; for (i = 0; i < UEC_RX_FW_STATS_LEN; i++) - data[j++] = (u64)in_be32(&base[i]); + data[j++] = base ? in_be32(&base[i]) : 0; } } diff --git a/drivers/net/usb/asix.c b/drivers/net/usb/asix.c index 37ecf845edfe..de57490103fc 100644 --- a/drivers/net/usb/asix.c +++ b/drivers/net/usb/asix.c @@ -1102,12 +1102,14 @@ static int ax88178_link_reset(struct usbnet *dev) mode = AX88178_MEDIUM_DEFAULT; if (ecmd.speed == SPEED_1000) - mode |= AX_MEDIUM_GM | AX_MEDIUM_ENCK; + mode |= AX_MEDIUM_GM; else if (ecmd.speed == SPEED_100) mode |= AX_MEDIUM_PS; else mode &= ~(AX_MEDIUM_PS | AX_MEDIUM_GM); + mode |= AX_MEDIUM_ENCK; + if (ecmd.duplex == DUPLEX_FULL) mode |= AX_MEDIUM_FD; else @@ -1444,6 +1446,10 @@ static const struct usb_device_id products [] = { // Apple USB Ethernet Adapter USB_DEVICE(0x05ac, 0x1402), .driver_info = (unsigned long) &ax88772_info, +}, { + // Cables-to-Go USB Ethernet Adapter + USB_DEVICE(0x0b95, 0x772a), + .driver_info = (unsigned long) &ax88772_info, }, { }, // END }; diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index 1164c52e2c0a..8e90891f0e42 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -2184,19 +2184,20 @@ static void hso_create_rfkill(struct hso_device *hso_dev, struct usb_interface *interface) { struct hso_net *hso_net = dev2net(hso_dev); - struct device *dev = hso_dev->dev; + struct device *dev = &hso_net->net->dev; char *rfkn; hso_net->rfkill = rfkill_allocate(&interface_to_usbdev(interface)->dev, - RFKILL_TYPE_WLAN); + RFKILL_TYPE_WWAN); if (!hso_net->rfkill) { - dev_err(dev, "%s - Out of memory", __func__); + dev_err(dev, "%s - Out of memory\n", __func__); return; } rfkn = kzalloc(20, GFP_KERNEL); if (!rfkn) { rfkill_free(hso_net->rfkill); - dev_err(dev, "%s - Out of memory", __func__); + hso_net->rfkill = NULL; + dev_err(dev, "%s - Out of memory\n", __func__); return; } snprintf(rfkn, 20, "hso-%d", @@ -2209,7 +2210,8 @@ static void hso_create_rfkill(struct hso_device *hso_dev, kfree(rfkn); hso_net->rfkill->name = NULL; rfkill_free(hso_net->rfkill); - dev_err(dev, "%s - Failed to register rfkill", __func__); + hso_net->rfkill = NULL; + dev_err(dev, "%s - Failed to register rfkill\n", __func__); return; } } diff --git a/drivers/net/usb/kaweth.c b/drivers/net/usb/kaweth.c index fdbf3be24fda..92a0aee89a85 100644 --- a/drivers/net/usb/kaweth.c +++ b/drivers/net/usb/kaweth.c @@ -283,9 +283,9 @@ static int kaweth_control(struct kaweth_device *kaweth, dr->bRequestType= requesttype; dr->bRequest = request; - dr->wValue = cpu_to_le16p(&value); - dr->wIndex = cpu_to_le16p(&index); - dr->wLength = cpu_to_le16p(&size); + dr->wValue = cpu_to_le16(value); + dr->wIndex = cpu_to_le16(index); + dr->wLength = cpu_to_le16(size); return kaweth_internal_control_msg(kaweth->dev, pipe, diff --git a/drivers/net/usb/pegasus.c b/drivers/net/usb/pegasus.c index 7914867110ed..32cc6617cc71 100644 --- a/drivers/net/usb/pegasus.c +++ b/drivers/net/usb/pegasus.c @@ -149,8 +149,8 @@ static int get_registers(pegasus_t * pegasus, __u16 indx, __u16 size, pegasus->dr.bRequestType = PEGASUS_REQT_READ; pegasus->dr.bRequest = PEGASUS_REQ_GET_REGS; pegasus->dr.wValue = cpu_to_le16(0); - pegasus->dr.wIndex = cpu_to_le16p(&indx); - pegasus->dr.wLength = cpu_to_le16p(&size); + pegasus->dr.wIndex = cpu_to_le16(indx); + pegasus->dr.wLength = cpu_to_le16(size); pegasus->ctrl_urb->transfer_buffer_length = size; usb_fill_control_urb(pegasus->ctrl_urb, pegasus->usb, @@ -207,8 +207,8 @@ static int set_registers(pegasus_t * pegasus, __u16 indx, __u16 size, pegasus->dr.bRequestType = PEGASUS_REQT_WRITE; pegasus->dr.bRequest = PEGASUS_REQ_SET_REGS; pegasus->dr.wValue = cpu_to_le16(0); - pegasus->dr.wIndex = cpu_to_le16p(&indx); - pegasus->dr.wLength = cpu_to_le16p(&size); + pegasus->dr.wIndex = cpu_to_le16(indx); + pegasus->dr.wLength = cpu_to_le16(size); pegasus->ctrl_urb->transfer_buffer_length = size; usb_fill_control_urb(pegasus->ctrl_urb, pegasus->usb, @@ -260,7 +260,7 @@ static int set_register(pegasus_t * pegasus, __u16 indx, __u8 data) pegasus->dr.bRequestType = PEGASUS_REQT_WRITE; pegasus->dr.bRequest = PEGASUS_REQ_SET_REG; pegasus->dr.wValue = cpu_to_le16(data); - pegasus->dr.wIndex = cpu_to_le16p(&indx); + pegasus->dr.wIndex = cpu_to_le16(indx); pegasus->dr.wLength = cpu_to_le16(1); pegasus->ctrl_urb->transfer_buffer_length = 1; @@ -475,7 +475,7 @@ static inline void get_node_id(pegasus_t * pegasus, __u8 * id) for (i = 0; i < 3; i++) { read_eprom_word(pegasus, i, &w16); - ((__le16 *) id)[i] = cpu_to_le16p(&w16); + ((__le16 *) id)[i] = cpu_to_le16(w16); } } diff --git a/drivers/net/via-velocity.c b/drivers/net/via-velocity.c index 3590ea5a902d..11cb3e504e1c 100644 --- a/drivers/net/via-velocity.c +++ b/drivers/net/via-velocity.c @@ -2296,7 +2296,7 @@ static void velocity_set_multi(struct net_device *dev) } mac_set_cam_mask(regs, vptr->mCAMmask); - rx_mode = (RCR_AM | RCR_AB); + rx_mode = RCR_AM | RCR_AB | RCR_AP; } if (dev->mtu > 1500) rx_mode |= RCR_AL; diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index 9e47d727e220..2d14255eb103 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -240,6 +240,10 @@ static u64 ath5k_get_tsf(struct ieee80211_hw *hw); static void ath5k_reset_tsf(struct ieee80211_hw *hw); static int ath5k_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb); +static void ath5k_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf, + u32 changes); static struct ieee80211_ops ath5k_hw_ops = { .tx = ath5k_tx, @@ -256,6 +260,7 @@ static struct ieee80211_ops ath5k_hw_ops = { .get_tx_stats = ath5k_get_tx_stats, .get_tsf = ath5k_get_tsf, .reset_tsf = ath5k_reset_tsf, + .bss_info_changed = ath5k_bss_info_changed, }; /* @@ -2942,8 +2947,10 @@ static void ath5k_configure_filter(struct ieee80211_hw *hw, sc->opmode != NL80211_IFTYPE_MESH_POINT && test_bit(ATH_STAT_PROMISC, sc->status)) rfilt |= AR5K_RX_FILTER_PROM; - if (sc->opmode == NL80211_IFTYPE_ADHOC) + if ((sc->opmode == NL80211_IFTYPE_STATION && sc->assoc) || + sc->opmode == NL80211_IFTYPE_ADHOC) { rfilt |= AR5K_RX_FILTER_BEACON; + } /* Set filters */ ath5k_hw_set_rx_filter(ah,rfilt); @@ -3081,4 +3088,32 @@ ath5k_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb) end: return ret; } +static void +set_beacon_filter(struct ieee80211_hw *hw, bool enable) +{ + struct ath5k_softc *sc = hw->priv; + struct ath5k_hw *ah = sc->ah; + u32 rfilt; + rfilt = ath5k_hw_get_rx_filter(ah); + if (enable) + rfilt |= AR5K_RX_FILTER_BEACON; + else + rfilt &= ~AR5K_RX_FILTER_BEACON; + ath5k_hw_set_rx_filter(ah, rfilt); + sc->filter_flags = rfilt; +} +static void ath5k_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf, + u32 changes) +{ + struct ath5k_softc *sc = hw->priv; + if (changes & BSS_CHANGED_ASSOC) { + mutex_lock(&sc->lock); + sc->assoc = bss_conf->assoc; + if (sc->opmode == NL80211_IFTYPE_STATION) + set_beacon_filter(hw, sc->assoc); + mutex_unlock(&sc->lock); + } +} diff --git a/drivers/net/wireless/ath5k/base.h b/drivers/net/wireless/ath5k/base.h index 06d1054ca94b..facc60ddada2 100644 --- a/drivers/net/wireless/ath5k/base.h +++ b/drivers/net/wireless/ath5k/base.h @@ -179,6 +179,7 @@ struct ath5k_softc { struct timer_list calib_tim; /* calibration timer */ int power_level; /* Requested tx power in dbm */ + bool assoc; /* assocate state */ }; #define ath5k_hw_hasbssidmask(_ah) \ diff --git a/drivers/net/wireless/ath5k/debug.c b/drivers/net/wireless/ath5k/debug.c index 19980cbd5d5f..ccaeb5c219d2 100644 --- a/drivers/net/wireless/ath5k/debug.c +++ b/drivers/net/wireless/ath5k/debug.c @@ -417,19 +417,19 @@ ath5k_debug_init_device(struct ath5k_softc *sc) sc->debug.debugfs_phydir = debugfs_create_dir(wiphy_name(sc->hw->wiphy), ath5k_global_debugfs); - sc->debug.debugfs_debug = debugfs_create_file("debug", 0666, + sc->debug.debugfs_debug = debugfs_create_file("debug", S_IWUSR | S_IRUGO, sc->debug.debugfs_phydir, sc, &fops_debug); - sc->debug.debugfs_registers = debugfs_create_file("registers", 0444, + sc->debug.debugfs_registers = debugfs_create_file("registers", S_IRUGO, sc->debug.debugfs_phydir, sc, &fops_registers); - sc->debug.debugfs_tsf = debugfs_create_file("tsf", 0666, + sc->debug.debugfs_tsf = debugfs_create_file("tsf", S_IWUSR | S_IRUGO, sc->debug.debugfs_phydir, sc, &fops_tsf); - sc->debug.debugfs_beacon = debugfs_create_file("beacon", 0666, + sc->debug.debugfs_beacon = debugfs_create_file("beacon", S_IWUSR | S_IRUGO, sc->debug.debugfs_phydir, sc, &fops_beacon); - sc->debug.debugfs_reset = debugfs_create_file("reset", 0222, + sc->debug.debugfs_reset = debugfs_create_file("reset", S_IWUSR, sc->debug.debugfs_phydir, sc, &fops_reset); } diff --git a/drivers/net/wireless/ath5k/desc.c b/drivers/net/wireless/ath5k/desc.c index dd1374052ba9..5e362a7a3620 100644 --- a/drivers/net/wireless/ath5k/desc.c +++ b/drivers/net/wireless/ath5k/desc.c @@ -531,10 +531,10 @@ static int ath5k_hw_proc_5210_rx_status(struct ath5k_hw *ah, AR5K_5210_RX_DESC_STATUS0_RECEIVE_SIGNAL); rs->rs_rate = AR5K_REG_MS(rx_status->rx_status_0, AR5K_5210_RX_DESC_STATUS0_RECEIVE_RATE); - rs->rs_antenna = rx_status->rx_status_0 & - AR5K_5210_RX_DESC_STATUS0_RECEIVE_ANTENNA; - rs->rs_more = rx_status->rx_status_0 & - AR5K_5210_RX_DESC_STATUS0_MORE; + rs->rs_antenna = AR5K_REG_MS(rx_status->rx_status_0, + AR5K_5210_RX_DESC_STATUS0_RECEIVE_ANTENNA); + rs->rs_more = !!(rx_status->rx_status_0 & + AR5K_5210_RX_DESC_STATUS0_MORE); /* TODO: this timestamp is 13 bit, later on we assume 15 bit */ rs->rs_tstamp = AR5K_REG_MS(rx_status->rx_status_1, AR5K_5210_RX_DESC_STATUS1_RECEIVE_TIMESTAMP); @@ -607,10 +607,10 @@ static int ath5k_hw_proc_5212_rx_status(struct ath5k_hw *ah, AR5K_5212_RX_DESC_STATUS0_RECEIVE_SIGNAL); rs->rs_rate = AR5K_REG_MS(rx_status->rx_status_0, AR5K_5212_RX_DESC_STATUS0_RECEIVE_RATE); - rs->rs_antenna = rx_status->rx_status_0 & - AR5K_5212_RX_DESC_STATUS0_RECEIVE_ANTENNA; - rs->rs_more = rx_status->rx_status_0 & - AR5K_5212_RX_DESC_STATUS0_MORE; + rs->rs_antenna = AR5K_REG_MS(rx_status->rx_status_0, + AR5K_5212_RX_DESC_STATUS0_RECEIVE_ANTENNA); + rs->rs_more = !!(rx_status->rx_status_0 & + AR5K_5212_RX_DESC_STATUS0_MORE); rs->rs_tstamp = AR5K_REG_MS(rx_status->rx_status_1, AR5K_5212_RX_DESC_STATUS1_RECEIVE_TIMESTAMP); rs->rs_status = 0; diff --git a/drivers/net/wireless/ath9k/beacon.c b/drivers/net/wireless/ath9k/beacon.c index 9e15c30bbc06..4dd1c1bda0fb 100644 --- a/drivers/net/wireless/ath9k/beacon.c +++ b/drivers/net/wireless/ath9k/beacon.c @@ -170,7 +170,7 @@ static struct ath_buf *ath_beacon_generate(struct ath_softc *sc, int if_id) skb = (struct sk_buff *)bf->bf_mpdu; if (skb) { pci_unmap_single(sc->pdev, bf->bf_dmacontext, - skb_end_pointer(skb) - skb->head, + skb->len, PCI_DMA_TODEVICE); } @@ -193,7 +193,7 @@ static struct ath_buf *ath_beacon_generate(struct ath_softc *sc, int if_id) bf->bf_buf_addr = bf->bf_dmacontext = pci_map_single(sc->pdev, skb->data, - skb_end_pointer(skb) - skb->head, + skb->len, PCI_DMA_TODEVICE); skb = ieee80211_get_buffered_bc(sc->hw, avp->av_if_data); @@ -352,7 +352,7 @@ int ath_beacon_alloc(struct ath_softc *sc, int if_id) if (bf->bf_mpdu != NULL) { skb = (struct sk_buff *)bf->bf_mpdu; pci_unmap_single(sc->pdev, bf->bf_dmacontext, - skb_end_pointer(skb) - skb->head, + skb->len, PCI_DMA_TODEVICE); dev_kfree_skb_any(skb); bf->bf_mpdu = NULL; @@ -412,7 +412,7 @@ int ath_beacon_alloc(struct ath_softc *sc, int if_id) bf->bf_buf_addr = bf->bf_dmacontext = pci_map_single(sc->pdev, skb->data, - skb_end_pointer(skb) - skb->head, + skb->len, PCI_DMA_TODEVICE); bf->bf_mpdu = skb; @@ -439,7 +439,7 @@ void ath_beacon_return(struct ath_softc *sc, struct ath_vap *avp) if (bf->bf_mpdu != NULL) { struct sk_buff *skb = (struct sk_buff *)bf->bf_mpdu; pci_unmap_single(sc->pdev, bf->bf_dmacontext, - skb_end_pointer(skb) - skb->head, + skb->len, PCI_DMA_TODEVICE); dev_kfree_skb_any(skb); bf->bf_mpdu = NULL; diff --git a/drivers/net/wireless/ath9k/recv.c b/drivers/net/wireless/ath9k/recv.c index 4983402af559..504a0444d89f 100644 --- a/drivers/net/wireless/ath9k/recv.c +++ b/drivers/net/wireless/ath9k/recv.c @@ -49,10 +49,12 @@ static void ath_rx_buf_link(struct ath_softc *sc, struct ath_buf *bf) ASSERT(skb != NULL); ds->ds_vdata = skb->data; - /* setup rx descriptors */ + /* setup rx descriptors. The sc_rxbufsize here tells the harware + * how much data it can DMA to us and that we are prepared + * to process */ ath9k_hw_setuprxdesc(ah, ds, - skb_tailroom(skb), /* buffer size */ + sc->sc_rxbufsize, 0); if (sc->sc_rxlink == NULL) @@ -398,6 +400,13 @@ static struct sk_buff *ath_rxbuf_alloc(struct ath_softc *sc, * in rx'd frames. */ + /* Note: the kernel can allocate a value greater than + * what we ask it to give us. We really only need 4 KB as that + * is this hardware supports and in fact we need at least 3849 + * as that is the MAX AMSDU size this hardware supports. + * Unfortunately this means we may get 8 KB here from the + * kernel... and that is actually what is observed on some + * systems :( */ skb = dev_alloc_skb(len + sc->sc_cachelsz - 1); if (skb != NULL) { off = ((unsigned long) skb->data) % sc->sc_cachelsz; @@ -456,7 +465,7 @@ static int ath_rx_indicate(struct ath_softc *sc, if (nskb != NULL) { bf->bf_mpdu = nskb; bf->bf_buf_addr = pci_map_single(sc->pdev, nskb->data, - skb_end_pointer(nskb) - nskb->head, + sc->sc_rxbufsize, PCI_DMA_FROMDEVICE); bf->bf_dmacontext = bf->bf_buf_addr; ATH_RX_CONTEXT(nskb)->ctx_rxbuf = bf; @@ -542,7 +551,7 @@ int ath_rx_init(struct ath_softc *sc, int nbufs) bf->bf_mpdu = skb; bf->bf_buf_addr = pci_map_single(sc->pdev, skb->data, - skb_end_pointer(skb) - skb->head, + sc->sc_rxbufsize, PCI_DMA_FROMDEVICE); bf->bf_dmacontext = bf->bf_buf_addr; ATH_RX_CONTEXT(skb)->ctx_rxbuf = bf; @@ -1007,7 +1016,7 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush) pci_dma_sync_single_for_cpu(sc->pdev, bf->bf_buf_addr, - skb_tailroom(skb), + sc->sc_rxbufsize, PCI_DMA_FROMDEVICE); pci_unmap_single(sc->pdev, bf->bf_buf_addr, diff --git a/drivers/net/wireless/hostap/hostap_wlan.h b/drivers/net/wireless/hostap/hostap_wlan.h index ffdf4876121b..a68f97c39359 100644 --- a/drivers/net/wireless/hostap/hostap_wlan.h +++ b/drivers/net/wireless/hostap/hostap_wlan.h @@ -918,9 +918,12 @@ struct hostap_interface { /* * TX meta data - stored in skb->cb buffer, so this must not be increased over - * the 40-byte limit + * the 48-byte limit. + * THE PADDING THIS STARTS WITH IS A HORRIBLE HACK THAT SHOULD NOT LIVE + * TO SEE THE DAY. */ struct hostap_skb_tx_data { + unsigned int __padding_for_default_qdiscs; u32 magic; /* HOSTAP_SKB_TX_DATA_MAGIC */ u8 rate; /* transmit rate */ #define HOSTAP_TX_FLAGS_WDS BIT(0) diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 321dbc8c034a..c4c0371c763b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -1384,9 +1384,11 @@ void iwl_rx_handle(struct iwl_priv *priv) rxq->queue[i] = NULL; - pci_dma_sync_single_for_cpu(priv->pci_dev, rxb->dma_addr, - priv->hw_params.rx_buf_size, - PCI_DMA_FROMDEVICE); + dma_sync_single_range_for_cpu( + &priv->pci_dev->dev, rxb->real_dma_addr, + rxb->aligned_dma_addr - rxb->real_dma_addr, + priv->hw_params.rx_buf_size, + PCI_DMA_FROMDEVICE); pkt = (struct iwl_rx_packet *)rxb->skb->data; /* Reclaim a command buffer only if this packet is a response @@ -1436,8 +1438,8 @@ void iwl_rx_handle(struct iwl_priv *priv) rxb->skb = NULL; } - pci_unmap_single(priv->pci_dev, rxb->dma_addr, - priv->hw_params.rx_buf_size, + pci_unmap_single(priv->pci_dev, rxb->real_dma_addr, + priv->hw_params.rx_buf_size + 256, PCI_DMA_FROMDEVICE); spin_lock_irqsave(&rxq->lock, flags); list_add_tail(&rxb->list, &priv->rxq.rx_used); @@ -2341,7 +2343,6 @@ static void iwl_bg_alive_start(struct work_struct *data) mutex_lock(&priv->mutex); iwl_alive_start(priv); mutex_unlock(&priv->mutex); - ieee80211_notify_mac(priv->hw, IEEE80211_NOTIFY_RE_ASSOC); } static void iwl4965_bg_rf_kill(struct work_struct *work) @@ -3252,7 +3253,11 @@ static void iwl4965_mac_update_tkip_key(struct ieee80211_hw *hw, return; } - iwl_scan_cancel_timeout(priv, 100); + if (iwl_scan_cancel(priv)) { + /* cancel scan failed, just live w/ bad key and rely + briefly on SW decryption */ + return; + } key_flags |= (STA_KEY_FLG_TKIP | STA_KEY_FLG_MAP_KEY_MSK); key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS); diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h index c018121085e9..9966d4e384ce 100644 --- a/drivers/net/wireless/iwlwifi/iwl-dev.h +++ b/drivers/net/wireless/iwlwifi/iwl-dev.h @@ -89,7 +89,8 @@ extern struct iwl_cfg iwl5100_abg_cfg; #define DEFAULT_LONG_RETRY_LIMIT 4U struct iwl_rx_mem_buffer { - dma_addr_t dma_addr; + dma_addr_t real_dma_addr; + dma_addr_t aligned_dma_addr; struct sk_buff *skb; struct list_head list; }; diff --git a/drivers/net/wireless/iwlwifi/iwl-rx.c b/drivers/net/wireless/iwlwifi/iwl-rx.c index 7cde9d76ff5d..0509c16dbe75 100644 --- a/drivers/net/wireless/iwlwifi/iwl-rx.c +++ b/drivers/net/wireless/iwlwifi/iwl-rx.c @@ -204,7 +204,7 @@ int iwl_rx_queue_restock(struct iwl_priv *priv) list_del(element); /* Point to Rx buffer via next RBD in circular buffer */ - rxq->bd[rxq->write] = iwl_dma_addr2rbd_ptr(priv, rxb->dma_addr); + rxq->bd[rxq->write] = iwl_dma_addr2rbd_ptr(priv, rxb->aligned_dma_addr); rxq->queue[rxq->write] = rxb; rxq->write = (rxq->write + 1) & RX_QUEUE_MASK; rxq->free_count--; @@ -251,7 +251,7 @@ void iwl_rx_allocate(struct iwl_priv *priv) rxb = list_entry(element, struct iwl_rx_mem_buffer, list); /* Alloc a new receive buffer */ - rxb->skb = alloc_skb(priv->hw_params.rx_buf_size, + rxb->skb = alloc_skb(priv->hw_params.rx_buf_size + 256, __GFP_NOWARN | GFP_ATOMIC); if (!rxb->skb) { if (net_ratelimit()) @@ -266,9 +266,17 @@ void iwl_rx_allocate(struct iwl_priv *priv) list_del(element); /* Get physical address of RB/SKB */ - rxb->dma_addr = - pci_map_single(priv->pci_dev, rxb->skb->data, - priv->hw_params.rx_buf_size, PCI_DMA_FROMDEVICE); + rxb->real_dma_addr = pci_map_single( + priv->pci_dev, + rxb->skb->data, + priv->hw_params.rx_buf_size + 256, + PCI_DMA_FROMDEVICE); + /* dma address must be no more than 36 bits */ + BUG_ON(rxb->real_dma_addr & ~DMA_BIT_MASK(36)); + /* and also 256 byte aligned! */ + rxb->aligned_dma_addr = ALIGN(rxb->real_dma_addr, 256); + skb_reserve(rxb->skb, rxb->aligned_dma_addr - rxb->real_dma_addr); + list_add_tail(&rxb->list, &rxq->rx_free); rxq->free_count++; } @@ -300,8 +308,8 @@ void iwl_rx_queue_free(struct iwl_priv *priv, struct iwl_rx_queue *rxq) for (i = 0; i < RX_QUEUE_SIZE + RX_FREE_BUFFERS; i++) { if (rxq->pool[i].skb != NULL) { pci_unmap_single(priv->pci_dev, - rxq->pool[i].dma_addr, - priv->hw_params.rx_buf_size, + rxq->pool[i].real_dma_addr, + priv->hw_params.rx_buf_size + 256, PCI_DMA_FROMDEVICE); dev_kfree_skb(rxq->pool[i].skb); } @@ -354,8 +362,8 @@ void iwl_rx_queue_reset(struct iwl_priv *priv, struct iwl_rx_queue *rxq) * to an SKB, so we need to unmap and free potential storage */ if (rxq->pool[i].skb != NULL) { pci_unmap_single(priv->pci_dev, - rxq->pool[i].dma_addr, - priv->hw_params.rx_buf_size, + rxq->pool[i].real_dma_addr, + priv->hw_params.rx_buf_size + 256, PCI_DMA_FROMDEVICE); priv->alloc_rxb_skb--; dev_kfree_skb(rxq->pool[i].skb); diff --git a/drivers/net/wireless/iwlwifi/iwl-scan.c b/drivers/net/wireless/iwlwifi/iwl-scan.c index 3b0bee331a33..c89365e2ca58 100644 --- a/drivers/net/wireless/iwlwifi/iwl-scan.c +++ b/drivers/net/wireless/iwlwifi/iwl-scan.c @@ -896,6 +896,13 @@ static void iwl_bg_request_scan(struct work_struct *data) return; done: + /* Cannot perform scan. Make sure we clear scanning + * bits from status so next scan request can be performed. + * If we don't clear scanning status bit here all next scan + * will fail + */ + clear_bit(STATUS_SCAN_HW, &priv->status); + clear_bit(STATUS_SCANNING, &priv->status); /* inform mac80211 scan aborted */ queue_work(priv->workqueue, &priv->scan_completed); mutex_unlock(&priv->mutex); diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index d15a2c997954..45a6b0c35695 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -5768,7 +5768,6 @@ static void iwl3945_alive_start(struct iwl3945_priv *priv) if (priv->error_recovering) iwl3945_error_recovery(priv); - ieee80211_notify_mac(priv->hw, IEEE80211_NOTIFY_RE_ASSOC); return; restart: @@ -6256,6 +6255,11 @@ static void iwl3945_bg_request_scan(struct work_struct *data) n_probes, (void *)&scan->data[le16_to_cpu(scan->tx_cmd.len)]); + if (scan->channel_count == 0) { + IWL_DEBUG_SCAN("channel count %d\n", scan->channel_count); + goto done; + } + cmd.len += le16_to_cpu(scan->tx_cmd.len) + scan->channel_count * sizeof(struct iwl3945_scan_channel); cmd.data = scan; @@ -6273,6 +6277,14 @@ static void iwl3945_bg_request_scan(struct work_struct *data) return; done: + /* can not perform scan make sure we clear scanning + * bits from status so next scan request can be performed. + * if we dont clear scanning status bit here all next scan + * will fail + */ + clear_bit(STATUS_SCAN_HW, &priv->status); + clear_bit(STATUS_SCANNING, &priv->status); + /* inform mac80211 scan aborted */ queue_work(priv->workqueue, &priv->scan_completed); mutex_unlock(&priv->mutex); diff --git a/drivers/net/wireless/libertas_tf/if_usb.c b/drivers/net/wireless/libertas_tf/if_usb.c index 1cc03a8dd67a..59634c33b1f9 100644 --- a/drivers/net/wireless/libertas_tf/if_usb.c +++ b/drivers/net/wireless/libertas_tf/if_usb.c @@ -331,7 +331,7 @@ static int __if_usb_submit_rx_urb(struct if_usb_card *cardp, /* Fill the receive configuration URB and initialise the Rx call back */ usb_fill_bulk_urb(cardp->rx_urb, cardp->udev, usb_rcvbulkpipe(cardp->udev, cardp->ep_in), - (void *) (skb->tail), + skb_tail_pointer(skb), MRVDRV_ETH_RX_PACKET_BUFFER_SIZE, callbackfn, cardp); cardp->rx_urb->transfer_flags |= URB_ZERO_PACKET; diff --git a/drivers/net/wireless/rtl8187_dev.c b/drivers/net/wireless/rtl8187_dev.c index 431e3c78bf27..69eb0132593b 100644 --- a/drivers/net/wireless/rtl8187_dev.c +++ b/drivers/net/wireless/rtl8187_dev.c @@ -48,6 +48,9 @@ static struct usb_device_id rtl8187_table[] __devinitdata = { {USB_DEVICE(0x03f0, 0xca02), .driver_info = DEVICE_RTL8187}, /* Sitecom */ {USB_DEVICE(0x0df6, 0x000d), .driver_info = DEVICE_RTL8187}, + {USB_DEVICE(0x0df6, 0x0028), .driver_info = DEVICE_RTL8187B}, + /* Abocom */ + {USB_DEVICE(0x13d1, 0xabe6), .driver_info = DEVICE_RTL8187}, {} }; diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c index a60ae86bd5c9..a3ccd8c1c716 100644 --- a/drivers/net/wireless/zd1211rw/zd_usb.c +++ b/drivers/net/wireless/zd1211rw/zd_usb.c @@ -61,6 +61,7 @@ static struct usb_device_id usb_ids[] = { { USB_DEVICE(0x0105, 0x145f), .driver_info = DEVICE_ZD1211 }, /* ZD1211B */ { USB_DEVICE(0x0ace, 0x1215), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0ace, 0xb215), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x157e, 0x300d), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x079b, 0x0062), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x1582, 0x6003), .driver_info = DEVICE_ZD1211B }, @@ -82,6 +83,7 @@ static struct usb_device_id usb_ids[] = { { USB_DEVICE(0x0cde, 0x001a), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x0586, 0x340a), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x0471, 0x1237), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x07fa, 0x1196), .driver_info = DEVICE_ZD1211B }, /* "Driverless" devices that need ejecting */ { USB_DEVICE(0x0ace, 0x2011), .driver_info = DEVICE_INSTALLER }, { USB_DEVICE(0x0ace, 0x20ff), .driver_info = DEVICE_INSTALLER }, diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index c6948d8f53f6..6d017adc914a 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -1785,7 +1785,7 @@ static int __devexit xennet_remove(struct xenbus_device *dev) return 0; } -static struct xenbus_driver netfront = { +static struct xenbus_driver netfront_driver = { .name = "vif", .owner = THIS_MODULE, .ids = netfront_ids, @@ -1805,7 +1805,7 @@ static int __init netif_init(void) printk(KERN_INFO "Initialising Xen virtual ethernet driver.\n"); - return xenbus_register_frontend(&netfront); + return xenbus_register_frontend(&netfront_driver); } module_init(netif_init); @@ -1815,7 +1815,7 @@ static void __exit netif_exit(void) if (xen_initial_domain()) return; - xenbus_unregister_driver(&netfront); + xenbus_unregister_driver(&netfront_driver); } module_exit(netif_exit); diff --git a/drivers/of/base.c b/drivers/of/base.c index 7c79e94a35ea..4f884a358a7b 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -329,6 +329,41 @@ struct device_node *of_find_compatible_node(struct device_node *from, EXPORT_SYMBOL(of_find_compatible_node); /** + * of_find_node_with_property - Find a node which has a property with + * the given name. + * @from: The node to start searching from or NULL, the node + * you pass will not be searched, only the next one + * will; typically, you pass what the previous call + * returned. of_node_put() will be called on it + * @prop_name: The name of the property to look for. + * + * Returns a node pointer with refcount incremented, use + * of_node_put() on it when done. + */ +struct device_node *of_find_node_with_property(struct device_node *from, + const char *prop_name) +{ + struct device_node *np; + struct property *pp; + + read_lock(&devtree_lock); + np = from ? from->allnext : allnodes; + for (; np; np = np->allnext) { + for (pp = np->properties; pp != 0; pp = pp->next) { + if (of_prop_cmp(pp->name, prop_name) == 0) { + of_node_get(np); + goto out; + } + } + } +out: + of_node_put(from); + read_unlock(&devtree_lock); + return np; +} +EXPORT_SYMBOL(of_find_node_with_property); + +/** * of_match_node - Tell if an device_node has a matching of_match structure * @matches: array of of device match structures to search in * @node: the of device structure to match against diff --git a/drivers/oprofile/event_buffer.c b/drivers/oprofile/event_buffer.c index d962ba0dd87a..191a3202cecc 100644 --- a/drivers/oprofile/event_buffer.c +++ b/drivers/oprofile/event_buffer.c @@ -105,7 +105,7 @@ static int event_buffer_open(struct inode *inode, struct file *file) if (!capable(CAP_SYS_ADMIN)) return -EPERM; - if (test_and_set_bit(0, &buffer_opened)) + if (test_and_set_bit_lock(0, &buffer_opened)) return -EBUSY; /* Register as a user of dcookies @@ -129,7 +129,7 @@ static int event_buffer_open(struct inode *inode, struct file *file) fail: dcookie_unregister(file->private_data); out: - clear_bit(0, &buffer_opened); + __clear_bit_unlock(0, &buffer_opened); return err; } @@ -141,7 +141,7 @@ static int event_buffer_release(struct inode *inode, struct file *file) dcookie_unregister(file->private_data); buffer_pos = 0; atomic_set(&buffer_ready, 0); - clear_bit(0, &buffer_opened); + __clear_bit_unlock(0, &buffer_opened); return 0; } diff --git a/drivers/parport/Kconfig b/drivers/parport/Kconfig index 209b4a464bcf..855f389eea40 100644 --- a/drivers/parport/Kconfig +++ b/drivers/parport/Kconfig @@ -36,7 +36,7 @@ if PARPORT config PARPORT_PC tristate "PC-style hardware" depends on (!SPARC64 || PCI) && !SPARC32 && !M32R && !FRV && \ - (!M68K || ISA) && !MN10300 && !AVR32 + (!M68K || ISA) && !MN10300 && !AVR32 && !BLACKFIN ---help--- You should say Y here if you have a PC-style parallel port. All IBM PC compatible computers and some Alphas have PC-style diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 999cc4088b59..3e1c135b174a 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -158,6 +158,10 @@ void pci_bus_add_devices(struct pci_bus *bus) dev_err(&dev->dev, "Error creating cpulistaffinity" " file, continuing...\n"); + + /* Create legacy_io and legacy_mem files for this bus */ + pci_create_legacy_files(child_bus); + } } } diff --git a/drivers/pci/hotplug/acpiphp_ibm.c b/drivers/pci/hotplug/acpiphp_ibm.c index 881fdd2b7313..5befa7e379b7 100644 --- a/drivers/pci/hotplug/acpiphp_ibm.c +++ b/drivers/pci/hotplug/acpiphp_ibm.c @@ -271,7 +271,7 @@ static void ibm_handle_events(acpi_handle handle, u32 event, void *context) dbg("%s: generationg bus event\n", __func__); acpi_bus_generate_proc_event(note->device, note->event, detail); acpi_bus_generate_netlink_event(note->device->pnp.device_class, - note->device->dev.bus_id, + dev_name(¬e->device->dev), note->event, detail); } else note->event = event; diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index fead63c6b49e..ff4034502d24 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c @@ -178,15 +178,14 @@ static void set_slot_off(struct controller *ctrl, struct slot * pslot) "Issue of Slot Power Off command failed\n"); return; } + /* + * After turning power off, we must wait for at least 1 second + * before taking any action that relies on power having been + * removed from the slot/adapter. + */ + msleep(1000); } - /* - * After turning power off, we must wait for at least 1 second - * before taking any action that relies on power having been - * removed from the slot/adapter. - */ - msleep(1000); - if (PWR_LED(ctrl)) pslot->hpc_ops->green_led_off(pslot); @@ -286,15 +285,14 @@ static int remove_board(struct slot *p_slot) "Issue of Slot Disable command failed\n"); return retval; } + /* + * After turning power off, we must wait for at least 1 second + * before taking any action that relies on power having been + * removed from the slot/adapter. + */ + msleep(1000); } - /* - * After turning power off, we must wait for at least 1 second - * before taking any action that relies on power having been - * removed from the slot/adapter. - */ - msleep(1000); - if (PWR_LED(ctrl)) /* turn off Green LED */ p_slot->hpc_ops->green_led_off(p_slot); diff --git a/drivers/pci/hotplug/rpadlpar_core.c b/drivers/pci/hotplug/rpadlpar_core.c index 9c2a22fed18b..4e3e0382c16e 100644 --- a/drivers/pci/hotplug/rpadlpar_core.c +++ b/drivers/pci/hotplug/rpadlpar_core.c @@ -14,6 +14,9 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ + +#undef DEBUG + #include <linux/init.h> #include <linux/pci.h> #include <linux/string.h> @@ -151,20 +154,20 @@ static void dlpar_pci_add_bus(struct device_node *dn) return; } + /* Scan below the new bridge */ if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) of_scan_pci_bridge(dn, dev); - pcibios_fixup_new_pci_devices(dev->subordinate); - - /* Claim new bus resources */ - pcibios_claim_one_bus(dev->bus); - /* Map IO space for child bus, which may or may not succeed */ pcibios_map_io_space(dev->subordinate); - /* Add new devices to global lists. Register in proc, sysfs. */ - pci_bus_add_devices(phb->bus); + /* Finish adding it : resource allocation, adding devices, etc... + * Note that we need to perform the finish pass on the -parent- + * bus of the EADS bridge so the bridge device itself gets + * properly added + */ + pcibios_finish_adding_to_bus(phb->bus); } static int dlpar_add_pci_slot(char *drc_name, struct device_node *dn) @@ -203,27 +206,6 @@ static int dlpar_add_pci_slot(char *drc_name, struct device_node *dn) return 0; } -static int dlpar_remove_root_bus(struct pci_controller *phb) -{ - struct pci_bus *phb_bus; - int rc; - - phb_bus = phb->bus; - if (!(list_empty(&phb_bus->children) && - list_empty(&phb_bus->devices))) { - return -EBUSY; - } - - rc = pcibios_remove_root_bus(phb); - if (rc) - return -EIO; - - device_unregister(phb_bus->bridge); - pci_remove_bus(phb_bus); - - return 0; -} - static int dlpar_remove_phb(char *drc_name, struct device_node *dn) { struct slot *slot; @@ -235,18 +217,15 @@ static int dlpar_remove_phb(char *drc_name, struct device_node *dn) /* If pci slot is hotplugable, use hotplug to remove it */ slot = find_php_slot(dn); - if (slot) { - if (rpaphp_deregister_slot(slot)) { - printk(KERN_ERR - "%s: unable to remove hotplug slot %s\n", - __func__, drc_name); - return -EIO; - } + if (slot && rpaphp_deregister_slot(slot)) { + printk(KERN_ERR "%s: unable to remove hotplug slot %s\n", + __func__, drc_name); + return -EIO; } pdn = dn->data; BUG_ON(!pdn || !pdn->phb); - rc = dlpar_remove_root_bus(pdn->phb); + rc = remove_phb_dynamic(pdn->phb); if (rc < 0) return rc; @@ -378,26 +357,38 @@ int dlpar_remove_pci_slot(char *drc_name, struct device_node *dn) if (!bus) return -EINVAL; - /* If pci slot is hotplugable, use hotplug to remove it */ + pr_debug("PCI: Removing PCI slot below EADS bridge %s\n", + bus->self ? pci_name(bus->self) : "<!PHB!>"); + slot = find_php_slot(dn); if (slot) { + pr_debug("PCI: Removing hotplug slot for %04x:%02x...\n", + pci_domain_nr(bus), bus->number); + if (rpaphp_deregister_slot(slot)) { printk(KERN_ERR "%s: unable to remove hotplug slot %s\n", __func__, drc_name); return -EIO; } - } else - pcibios_remove_pci_devices(bus); + } + + /* Remove all devices below slot */ + pcibios_remove_pci_devices(bus); + /* Unmap PCI IO space */ if (pcibios_unmap_io_space(bus)) { printk(KERN_ERR "%s: failed to unmap bus range\n", __func__); return -ERANGE; } + /* Remove the EADS bridge device itself */ BUG_ON(!bus->self); + pr_debug("PCI: Now removing bridge device %s\n", pci_name(bus->self)); + eeh_remove_bus_device(bus->self); pci_remove_bus_device(bus->self); + return 0; } diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index a2692724b68f..5c8baa43ac9c 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -1655,12 +1655,14 @@ int __init init_dmars(void) iommu->flush.flush_context = __iommu_flush_context; iommu->flush.flush_iotlb = __iommu_flush_iotlb; printk(KERN_INFO "IOMMU 0x%Lx: using Register based " - "invalidation\n", drhd->reg_base_addr); + "invalidation\n", + (unsigned long long)drhd->reg_base_addr); } else { iommu->flush.flush_context = qi_flush_context; iommu->flush.flush_iotlb = qi_flush_iotlb; printk(KERN_INFO "IOMMU 0x%Lx: using Queued " - "invalidation\n", drhd->reg_base_addr); + "invalidation\n", + (unsigned long long)drhd->reg_base_addr); } } diff --git a/drivers/pci/irq.c b/drivers/pci/irq.c index 6441dfa969a3..de01174aff06 100644 --- a/drivers/pci/irq.c +++ b/drivers/pci/irq.c @@ -15,7 +15,7 @@ static void pci_note_irq_problem(struct pci_dev *pdev, const char *reason) dev_printk(KERN_ERR, &pdev->dev, "Potentially misrouted IRQ (Bridge %s %04x:%04x)\n", - parent->dev.bus_id, parent->vendor, parent->device); + dev_name(&parent->dev), parent->vendor, parent->device); dev_printk(KERN_ERR, &pdev->dev, "%s\n", reason); dev_printk(KERN_ERR, &pdev->dev, "Please report to linux-kernel@vger.kernel.org\n"); WARN_ON(1); diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 74801f7df9c9..0e8dae10d6a7 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -755,28 +755,19 @@ void pci_no_msi(void) pci_msi_enable = 0; } -void pci_msi_init_pci_dev(struct pci_dev *dev) -{ - INIT_LIST_HEAD(&dev->msi_list); -} - -#ifdef CONFIG_ACPI -#include <linux/acpi.h> -#include <linux/pci-acpi.h> -static void __devinit msi_acpi_init(void) +/** + * pci_msi_enabled - is MSI enabled? + * + * Returns true if MSI has not been disabled by the command-line option + * pci=nomsi. + **/ +int pci_msi_enabled(void) { - if (acpi_pci_disabled) - return; - pci_osc_support_set(OSC_MSI_SUPPORT); - pcie_osc_support_set(OSC_MSI_SUPPORT); + return pci_msi_enable; } -#else -static inline void msi_acpi_init(void) { } -#endif /* CONFIG_ACPI */ +EXPORT_SYMBOL(pci_msi_enabled); -void __devinit msi_init(void) +void pci_msi_init_pci_dev(struct pci_dev *dev) { - if (!pci_msi_enable) - return; - msi_acpi_init(); + INIT_LIST_HEAD(&dev->msi_list); } diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index b3a63edb6901..8f923ee5177f 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -63,7 +63,7 @@ static acpi_status acpi_run_osc(acpi_handle handle, union acpi_object in_params[4]; struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; union acpi_object *out_obj; - u32 osc_dw0, flags = osc_args->capbuf[OSC_QUERY_TYPE]; + u32 errors, flags = osc_args->capbuf[OSC_QUERY_TYPE]; /* Setting up input parameters */ input.count = 4; @@ -92,15 +92,16 @@ static acpi_status acpi_run_osc(acpi_handle handle, status = AE_TYPE; goto out_kfree; } - osc_dw0 = *((u32 *)out_obj->buffer.pointer); - if (osc_dw0) { - if (osc_dw0 & OSC_REQUEST_ERROR) + /* Need to ignore the bit0 in result code */ + errors = *((u32 *)out_obj->buffer.pointer) & ~(1 << 0); + if (errors) { + if (errors & OSC_REQUEST_ERROR) printk(KERN_DEBUG "_OSC request fails\n"); - if (osc_dw0 & OSC_INVALID_UUID_ERROR) + if (errors & OSC_INVALID_UUID_ERROR) printk(KERN_DEBUG "_OSC invalid UUID\n"); - if (osc_dw0 & OSC_INVALID_REVISION_ERROR) + if (errors & OSC_INVALID_REVISION_ERROR) printk(KERN_DEBUG "_OSC invalid revision\n"); - if (osc_dw0 & OSC_CAPABILITIES_MASK_ERROR) { + if (errors & OSC_CAPABILITIES_MASK_ERROR) { if (flags & OSC_QUERY_ENABLE) goto out_success; printk(KERN_DEBUG "_OSC FW not grant req. control\n"); @@ -142,47 +143,36 @@ static acpi_status __acpi_query_osc(u32 flags, struct acpi_osc_data *osc_data, return status; } -static acpi_status acpi_query_osc(acpi_handle handle, - u32 level, void *context, void **retval) +/* + * pci_acpi_osc_support: Invoke _OSC indicating support for the given feature + * @flags: Bitmask of flags to support + * + * See the ACPI spec for the definition of the flags + */ +int pci_acpi_osc_support(acpi_handle handle, u32 flags) { + u32 dummy; acpi_status status; - struct acpi_osc_data *osc_data; - u32 flags = (unsigned long)context, dummy; acpi_handle tmp; + struct acpi_osc_data *osc_data; + int rc = 0; status = acpi_get_handle(handle, "_OSC", &tmp); if (ACPI_FAILURE(status)) - return AE_OK; + return -ENOTTY; mutex_lock(&pci_acpi_lock); osc_data = acpi_get_osc_data(handle); if (!osc_data) { printk(KERN_ERR "acpi osc data array is full\n"); + rc = -ENOMEM; goto out; } __acpi_query_osc(flags, osc_data, &dummy); out: mutex_unlock(&pci_acpi_lock); - return AE_OK; -} - -/** - * __pci_osc_support_set - register OS support to Firmware - * @flags: OS support bits - * @hid: hardware ID - * - * Update OS support fields and doing a _OSC Query to obtain an update - * from Firmware on supported control bits. - **/ -acpi_status __pci_osc_support_set(u32 flags, const char *hid) -{ - if (!(flags & OSC_SUPPORT_MASKS)) - return AE_TYPE; - - acpi_get_devices(hid, acpi_query_osc, - (void *)(unsigned long)flags, NULL); - return AE_OK; + return rc; } /** @@ -374,7 +364,7 @@ static int acpi_pci_find_root_bridge(struct device *dev, acpi_handle *handle) * The string should be the same as root bridge's name * Please look at 'pci_scan_bus_parented' */ - num = sscanf(dev->bus_id, "pci%04x:%02x", &seg, &bus); + num = sscanf(dev_name(dev), "pci%04x:%02x", &seg, &bus); if (num != 2) return -ENODEV; *handle = acpi_get_pci_rootbridge_handle(seg, bus); diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index b4cdd690ae71..4042d211c3e5 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -433,8 +433,7 @@ static int pci_pm_suspend(struct device *dev) static int pci_pm_suspend_noirq(struct device *dev) { - struct pci_dev *pci_dev = to_pci_dev(dev); - struct pci_driver *drv = pci_dev->driver; + struct device_driver *drv = dev->driver; int error = 0; if (drv && drv->pm) { @@ -469,11 +468,10 @@ static int pci_pm_resume(struct device *dev) static int pci_pm_resume_noirq(struct device *dev) { - struct pci_dev *pci_dev = to_pci_dev(dev); - struct pci_driver *drv = pci_dev->driver; + struct device_driver *drv = dev->driver; int error = 0; - pci_fixup_device(pci_fixup_resume_early, pci_dev); + pci_fixup_device(pci_fixup_resume_early, to_pci_dev(dev)); if (drv && drv->pm) { if (drv->pm->resume_noirq) @@ -519,8 +517,7 @@ static int pci_pm_freeze(struct device *dev) static int pci_pm_freeze_noirq(struct device *dev) { - struct pci_dev *pci_dev = to_pci_dev(dev); - struct pci_driver *drv = pci_dev->driver; + struct device_driver *drv = dev->driver; int error = 0; if (drv && drv->pm) { @@ -553,15 +550,14 @@ static int pci_pm_thaw(struct device *dev) static int pci_pm_thaw_noirq(struct device *dev) { - struct pci_dev *pci_dev = to_pci_dev(dev); - struct pci_driver *drv = pci_dev->driver; + struct device_driver *drv = dev->driver; int error = 0; if (drv && drv->pm) { if (drv->pm->thaw_noirq) error = drv->pm->thaw_noirq(dev); } else { - pci_fixup_device(pci_fixup_resume_early, pci_dev); + pci_fixup_device(pci_fixup_resume_early, to_pci_dev(dev)); error = pci_legacy_resume_early(dev); } @@ -589,8 +585,7 @@ static int pci_pm_poweroff(struct device *dev) static int pci_pm_poweroff_noirq(struct device *dev) { - struct pci_dev *pci_dev = to_pci_dev(dev); - struct pci_driver *drv = pci_dev->driver; + struct device_driver *drv = dev->driver; int error = 0; if (drv && drv->pm) { @@ -625,7 +620,7 @@ static int pci_pm_restore(struct device *dev) static int pci_pm_restore_noirq(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); - struct pci_driver *drv = pci_dev->driver; + struct device_driver *drv = dev->driver; int error = 0; pci_fixup_device(pci_fixup_resume, pci_dev); @@ -654,17 +649,15 @@ static int pci_pm_restore_noirq(struct device *dev) #endif /* !CONFIG_HIBERNATION */ -struct pm_ext_ops pci_pm_ops = { - .base = { - .prepare = pci_pm_prepare, - .complete = pci_pm_complete, - .suspend = pci_pm_suspend, - .resume = pci_pm_resume, - .freeze = pci_pm_freeze, - .thaw = pci_pm_thaw, - .poweroff = pci_pm_poweroff, - .restore = pci_pm_restore, - }, +struct dev_pm_ops pci_dev_pm_ops = { + .prepare = pci_pm_prepare, + .complete = pci_pm_complete, + .suspend = pci_pm_suspend, + .resume = pci_pm_resume, + .freeze = pci_pm_freeze, + .thaw = pci_pm_thaw, + .poweroff = pci_pm_poweroff, + .restore = pci_pm_restore, .suspend_noirq = pci_pm_suspend_noirq, .resume_noirq = pci_pm_resume_noirq, .freeze_noirq = pci_pm_freeze_noirq, @@ -673,7 +666,7 @@ struct pm_ext_ops pci_pm_ops = { .restore_noirq = pci_pm_restore_noirq, }; -#define PCI_PM_OPS_PTR &pci_pm_ops +#define PCI_PM_OPS_PTR (&pci_dev_pm_ops) #else /* !CONFIG_PM_SLEEP */ @@ -703,9 +696,6 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner, drv->driver.owner = owner; drv->driver.mod_name = mod_name; - if (drv->pm) - drv->driver.pm = &drv->pm->base; - spin_lock_init(&drv->dynids.lock); INIT_LIST_HEAD(&drv->dynids.list); diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 110022d78689..e286dc2bacba 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -569,13 +569,13 @@ void pci_remove_legacy_files(struct pci_bus *b) #ifdef HAVE_PCI_MMAP -static int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vma) +int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vma) { unsigned long nr, start, size; nr = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; start = vma->vm_pgoff; - size = pci_resource_len(pdev, resno) >> PAGE_SHIFT; + size = ((pci_resource_len(pdev, resno) - 1) >> PAGE_SHIFT) + 1; if (start < size && size - start >= nr) return 1; WARN(1, "process \"%s\" tried to map 0x%08lx-0x%08lx on %s BAR %d (size 0x%08lx)\n", diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 21f2ac639cab..2cfa41e367a7 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1751,24 +1751,7 @@ int pci_set_dma_seg_boundary(struct pci_dev *dev, unsigned long mask) EXPORT_SYMBOL(pci_set_dma_seg_boundary); #endif -/** - * pci_execute_reset_function() - Reset a PCI device function - * @dev: Device function to reset - * - * Some devices allow an individual function to be reset without affecting - * other functions in the same device. The PCI device must be responsive - * to PCI config space in order to use this function. - * - * The device function is presumed to be unused when this function is called. - * Resetting the device will make the contents of PCI configuration space - * random, so any caller of this must be prepared to reinitialise the - * device including MSI, bus mastering, BARs, decoding IO and memory spaces, - * etc. - * - * Returns 0 if the device function was successfully reset or -ENOTTY if the - * device doesn't support resetting a single function. - */ -int pci_execute_reset_function(struct pci_dev *dev) +static int __pcie_flr(struct pci_dev *dev, int probe) { u16 status; u32 cap; @@ -1780,6 +1763,9 @@ int pci_execute_reset_function(struct pci_dev *dev) if (!(cap & PCI_EXP_DEVCAP_FLR)) return -ENOTTY; + if (probe) + return 0; + pci_block_user_cfg_access(dev); /* Wait for Transaction Pending bit clean */ @@ -1802,6 +1788,80 @@ int pci_execute_reset_function(struct pci_dev *dev) pci_unblock_user_cfg_access(dev); return 0; } + +static int __pci_af_flr(struct pci_dev *dev, int probe) +{ + int cappos = pci_find_capability(dev, PCI_CAP_ID_AF); + u8 status; + u8 cap; + + if (!cappos) + return -ENOTTY; + pci_read_config_byte(dev, cappos + PCI_AF_CAP, &cap); + if (!(cap & PCI_AF_CAP_TP) || !(cap & PCI_AF_CAP_FLR)) + return -ENOTTY; + + if (probe) + return 0; + + pci_block_user_cfg_access(dev); + + /* Wait for Transaction Pending bit clean */ + msleep(100); + pci_read_config_byte(dev, cappos + PCI_AF_STATUS, &status); + if (status & PCI_AF_STATUS_TP) { + dev_info(&dev->dev, "Busy after 100ms while trying to" + " reset; sleeping for 1 second\n"); + ssleep(1); + pci_read_config_byte(dev, + cappos + PCI_AF_STATUS, &status); + if (status & PCI_AF_STATUS_TP) + dev_info(&dev->dev, "Still busy after 1s; " + "proceeding with reset anyway\n"); + } + pci_write_config_byte(dev, cappos + PCI_AF_CTRL, PCI_AF_CTRL_FLR); + mdelay(100); + + pci_unblock_user_cfg_access(dev); + return 0; +} + +static int __pci_reset_function(struct pci_dev *pdev, int probe) +{ + int res; + + res = __pcie_flr(pdev, probe); + if (res != -ENOTTY) + return res; + + res = __pci_af_flr(pdev, probe); + if (res != -ENOTTY) + return res; + + return res; +} + +/** + * pci_execute_reset_function() - Reset a PCI device function + * @dev: Device function to reset + * + * Some devices allow an individual function to be reset without affecting + * other functions in the same device. The PCI device must be responsive + * to PCI config space in order to use this function. + * + * The device function is presumed to be unused when this function is called. + * Resetting the device will make the contents of PCI configuration space + * random, so any caller of this must be prepared to reinitialise the + * device including MSI, bus mastering, BARs, decoding IO and memory spaces, + * etc. + * + * Returns 0 if the device function was successfully reset or -ENOTTY if the + * device doesn't support resetting a single function. + */ +int pci_execute_reset_function(struct pci_dev *dev) +{ + return __pci_reset_function(dev, 0); +} EXPORT_SYMBOL_GPL(pci_execute_reset_function); /** @@ -1822,17 +1882,12 @@ EXPORT_SYMBOL_GPL(pci_execute_reset_function); */ int pci_reset_function(struct pci_dev *dev) { - u32 cap; - int exppos = pci_find_capability(dev, PCI_CAP_ID_EXP); - int r; + int r = __pci_reset_function(dev, 1); - if (!exppos) - return -ENOTTY; - pci_read_config_dword(dev, exppos + PCI_EXP_DEVCAP, &cap); - if (!(cap & PCI_EXP_DEVCAP_FLR)) - return -ENOTTY; + if (r < 0) + return r; - if (!dev->msi_enabled && !dev->msix_enabled) + if (!dev->msi_enabled && !dev->msix_enabled && dev->irq != 0) disable_irq(dev->irq); pci_save_state(dev); @@ -1841,7 +1896,7 @@ int pci_reset_function(struct pci_dev *dev) r = pci_execute_reset_function(dev); pci_restore_state(dev); - if (!dev->msi_enabled && !dev->msix_enabled) + if (!dev->msi_enabled && !dev->msix_enabled && dev->irq != 0) enable_irq(dev->irq); return r; @@ -2029,6 +2084,19 @@ static void __devinit pci_no_domains(void) #endif } +/** + * pci_ext_cfg_enabled - can we access extended PCI config space? + * @dev: The PCI device of the root bridge. + * + * Returns 1 if we can access PCI extended config space (offsets + * greater than 0xff). This is the default implementation. Architecture + * implementations can override this. + */ +int __attribute__ ((weak)) pci_ext_cfg_avail(struct pci_dev *dev) +{ + return 1; +} + static int __devinit pci_init(void) { struct pci_dev *dev = NULL; @@ -2037,12 +2105,10 @@ static int __devinit pci_init(void) pci_fixup_device(pci_fixup_final, dev); } - msi_init(); - return 0; } -static int __devinit pci_setup(char *str) +static int __init pci_setup(char *str) { while (str) { char *k = strchr(str, ','); diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 9de87e9f98f5..9162e242b99e 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -10,6 +10,10 @@ extern int pci_uevent(struct device *dev, struct kobj_uevent_env *env); extern int pci_create_sysfs_dev_files(struct pci_dev *pdev); extern void pci_remove_sysfs_dev_files(struct pci_dev *pdev); extern void pci_cleanup_rom(struct pci_dev *dev); +#ifdef HAVE_PCI_MMAP +extern int pci_mmap_fits(struct pci_dev *pdev, int resno, + struct vm_area_struct *vma); +#endif /** * Firmware PM callbacks @@ -98,11 +102,9 @@ extern unsigned int pci_pm_d3_delay; #ifdef CONFIG_PCI_MSI void pci_no_msi(void); extern void pci_msi_init_pci_dev(struct pci_dev *dev); -extern void __devinit msi_init(void); #else static inline void pci_no_msi(void) { } static inline void pci_msi_init_pci_dev(struct pci_dev *dev) { } -static inline void msi_init(void) { } #endif #ifdef CONFIG_PCIEAER diff --git a/drivers/pci/pcie/aer/aerdrv_acpi.c b/drivers/pci/pcie/aer/aerdrv_acpi.c index 6dd7b13e9808..ebce26c37049 100644 --- a/drivers/pci/pcie/aer/aerdrv_acpi.c +++ b/drivers/pci/pcie/aer/aerdrv_acpi.c @@ -38,7 +38,6 @@ int aer_osc_setup(struct pcie_device *pciedev) handle = acpi_find_root_bridge_handle(pdev); if (handle) { - pcie_osc_support_set(OSC_EXT_PCI_CONFIG_SUPPORT); status = pci_osc_control_set(handle, OSC_PCI_EXPRESS_AER_CONTROL | OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL); diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 8f63f4c6b85f..1089f8653d5a 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -834,24 +834,15 @@ void pcie_no_aspm(void) aspm_disabled = 1; } -#ifdef CONFIG_ACPI -#include <acpi/acpi_bus.h> -#include <linux/pci-acpi.h> -static void pcie_aspm_platform_init(void) -{ - pcie_osc_support_set(OSC_ACTIVE_STATE_PWR_SUPPORT| - OSC_CLOCK_PWR_CAPABILITY_SUPPORT); -} -#else -static inline void pcie_aspm_platform_init(void) { } -#endif - -static int __init pcie_aspm_init(void) +/** + * pcie_aspm_enabled - is PCIe ASPM enabled? + * + * Returns true if ASPM has not been disabled by the command-line option + * pcie_aspm=off. + **/ +int pcie_aspm_enabled(void) { - if (aspm_disabled) - return 0; - pcie_aspm_platform_init(); - return 0; + return !aspm_disabled; } +EXPORT_SYMBOL(pcie_aspm_enabled); -fs_initcall(pcie_aspm_init); diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 2e091e014829..75f501ab6468 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -224,7 +224,7 @@ static void pcie_device_init(struct pci_dev *parent, struct pcie_device *dev, device->driver = NULL; device->driver_data = NULL; device->release = release_pcie_device; /* callback to free pcie dev */ - snprintf(device->bus_id, sizeof(device->bus_id), "%s:pcie%02x", + dev_set_name(device, "%s:pcie%02x", pci_name(parent), get_descriptor_id(port_type, service_type)); device->parent = &parent->dev; } diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 003a9b3c293f..73040c0cfb1a 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -412,7 +412,7 @@ static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent, * registered later in pci_bus_add_devices() */ child->dev.class = &pcibus_class; - sprintf(child->dev.bus_id, "%04x:%02x", pci_domain_nr(child), busnr); + dev_set_name(&child->dev, "%04x:%02x", pci_domain_nr(child), busnr); /* * Set up the primary, secondary and subordinate @@ -1130,7 +1130,7 @@ struct pci_bus * pci_create_bus(struct device *parent, memset(dev, 0, sizeof(*dev)); dev->parent = parent; dev->release = pci_release_bus_bridge_dev; - sprintf(dev->bus_id, "pci%04x:%02x", pci_domain_nr(b), bus); + dev_set_name(dev, "pci%04x:%02x", pci_domain_nr(b), bus); error = device_register(dev); if (error) goto dev_reg_err; @@ -1141,7 +1141,7 @@ struct pci_bus * pci_create_bus(struct device *parent, b->dev.class = &pcibus_class; b->dev.parent = b->bridge; - sprintf(b->dev.bus_id, "%04x:%02x", pci_domain_nr(b), bus); + dev_set_name(&b->dev, "%04x:%02x", pci_domain_nr(b), bus); error = device_register(&b->dev); if (error) goto class_dev_reg_err; diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c index e1098c302c45..7fb086d39617 100644 --- a/drivers/pci/proc.c +++ b/drivers/pci/proc.c @@ -252,11 +252,20 @@ static int proc_bus_pci_mmap(struct file *file, struct vm_area_struct *vma) const struct proc_dir_entry *dp = PDE(inode); struct pci_dev *dev = dp->data; struct pci_filp_private *fpriv = file->private_data; - int ret; + int i, ret; if (!capable(CAP_SYS_RAWIO)) return -EPERM; + /* Make sure the caller is mapping a real resource for this device */ + for (i = 0; i < PCI_ROM_RESOURCE; i++) { + if (pci_mmap_fits(dev, i, vma)) + break; + } + + if (i >= PCI_ROM_RESOURCE) + return -ENODEV; + ret = pci_mmap_page_range(dev, vma, fpriv->mmap_state, fpriv->write_combine); diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index bbf66ea8fd87..e915a17b36c3 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -22,6 +22,7 @@ #include <linux/delay.h> #include <linux/acpi.h> #include <linux/kallsyms.h> +#include <linux/dmi.h> #include "pci.h" int isa_dma_bridge_buggy; @@ -605,27 +606,6 @@ static void __init quirk_ioapic_rmw(struct pci_dev *dev) sis_apic_bug = 1; } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SI, PCI_ANY_ID, quirk_ioapic_rmw); - -#define AMD8131_revA0 0x01 -#define AMD8131_revB0 0x11 -#define AMD8131_MISC 0x40 -#define AMD8131_NIOAMODE_BIT 0 -static void quirk_amd_8131_ioapic(struct pci_dev *dev) -{ - unsigned char tmp; - - if (nr_ioapics == 0) - return; - - if (dev->revision == AMD8131_revA0 || dev->revision == AMD8131_revB0) { - dev_info(&dev->dev, "Fixing up AMD8131 IOAPIC mode\n"); - pci_read_config_byte( dev, AMD8131_MISC, &tmp); - tmp &= ~(1 << AMD8131_NIOAMODE_BIT); - pci_write_config_byte( dev, AMD8131_MISC, tmp); - } -} -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE, quirk_amd_8131_ioapic); -DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE, quirk_amd_8131_ioapic); #endif /* CONFIG_X86_IO_APIC */ /* @@ -1422,6 +1402,155 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x2609, quirk_intel_pcie_pm); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x260a, quirk_intel_pcie_pm); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x260b, quirk_intel_pcie_pm); +#ifdef CONFIG_X86_IO_APIC +/* + * Boot interrupts on some chipsets cannot be turned off. For these chipsets, + * remap the original interrupt in the linux kernel to the boot interrupt, so + * that a PCI device's interrupt handler is installed on the boot interrupt + * line instead. + */ +static void quirk_reroute_to_boot_interrupts_intel(struct pci_dev *dev) +{ + if (noioapicquirk || noioapicreroute) + return; + + dev->irq_reroute_variant = INTEL_IRQ_REROUTE_VARIANT; + + printk(KERN_INFO "PCI quirk: reroute interrupts for 0x%04x:0x%04x\n", + dev->vendor, dev->device); + return; +} +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80333_0, quirk_reroute_to_boot_interrupts_intel); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80333_1, quirk_reroute_to_boot_interrupts_intel); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB2_0, quirk_reroute_to_boot_interrupts_intel); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PXH_0, quirk_reroute_to_boot_interrupts_intel); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PXH_1, quirk_reroute_to_boot_interrupts_intel); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PXHV, quirk_reroute_to_boot_interrupts_intel); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80332_0, quirk_reroute_to_boot_interrupts_intel); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80332_1, quirk_reroute_to_boot_interrupts_intel); +DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80333_0, quirk_reroute_to_boot_interrupts_intel); +DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80333_1, quirk_reroute_to_boot_interrupts_intel); +DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB2_0, quirk_reroute_to_boot_interrupts_intel); +DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PXH_0, quirk_reroute_to_boot_interrupts_intel); +DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PXH_1, quirk_reroute_to_boot_interrupts_intel); +DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PXHV, quirk_reroute_to_boot_interrupts_intel); +DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80332_0, quirk_reroute_to_boot_interrupts_intel); +DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80332_1, quirk_reroute_to_boot_interrupts_intel); + +/* + * On some chipsets we can disable the generation of legacy INTx boot + * interrupts. + */ + +/* + * IO-APIC1 on 6300ESB generates boot interrupts, see intel order no + * 300641-004US, section 5.7.3. + */ +#define INTEL_6300_IOAPIC_ABAR 0x40 +#define INTEL_6300_DISABLE_BOOT_IRQ (1<<14) + +static void quirk_disable_intel_boot_interrupt(struct pci_dev *dev) +{ + u16 pci_config_word; + + if (noioapicquirk) + return; + + pci_read_config_word(dev, INTEL_6300_IOAPIC_ABAR, &pci_config_word); + pci_config_word |= INTEL_6300_DISABLE_BOOT_IRQ; + pci_write_config_word(dev, INTEL_6300_IOAPIC_ABAR, pci_config_word); + + printk(KERN_INFO "disabled boot interrupt on device 0x%04x:0x%04x\n", + dev->vendor, dev->device); +} +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_10, quirk_disable_intel_boot_interrupt); +DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_10, quirk_disable_intel_boot_interrupt); + +/* + * disable boot interrupts on HT-1000 + */ +#define BC_HT1000_FEATURE_REG 0x64 +#define BC_HT1000_PIC_REGS_ENABLE (1<<0) +#define BC_HT1000_MAP_IDX 0xC00 +#define BC_HT1000_MAP_DATA 0xC01 + +static void quirk_disable_broadcom_boot_interrupt(struct pci_dev *dev) +{ + u32 pci_config_dword; + u8 irq; + + if (noioapicquirk) + return; + + pci_read_config_dword(dev, BC_HT1000_FEATURE_REG, &pci_config_dword); + pci_write_config_dword(dev, BC_HT1000_FEATURE_REG, pci_config_dword | + BC_HT1000_PIC_REGS_ENABLE); + + for (irq = 0x10; irq < 0x10 + 32; irq++) { + outb(irq, BC_HT1000_MAP_IDX); + outb(0x00, BC_HT1000_MAP_DATA); + } + + pci_write_config_dword(dev, BC_HT1000_FEATURE_REG, pci_config_dword); + + printk(KERN_INFO "disabled boot interrupts on PCI device" + "0x%04x:0x%04x\n", dev->vendor, dev->device); +} +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_HT1000SB, quirk_disable_broadcom_boot_interrupt); +DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_HT1000SB, quirk_disable_broadcom_boot_interrupt); + +/* + * disable boot interrupts on AMD and ATI chipsets + */ +/* + * NOIOAMODE needs to be disabled to disable "boot interrupts". For AMD 8131 + * rev. A0 and B0, NOIOAMODE needs to be disabled anyway to fix IO-APIC mode + * (due to an erratum). + */ +#define AMD_813X_MISC 0x40 +#define AMD_813X_NOIOAMODE (1<<0) + +static void quirk_disable_amd_813x_boot_interrupt(struct pci_dev *dev) +{ + u32 pci_config_dword; + + if (noioapicquirk) + return; + + pci_read_config_dword(dev, AMD_813X_MISC, &pci_config_dword); + pci_config_dword &= ~AMD_813X_NOIOAMODE; + pci_write_config_dword(dev, AMD_813X_MISC, pci_config_dword); + + printk(KERN_INFO "disabled boot interrupts on PCI device " + "0x%04x:0x%04x\n", dev->vendor, dev->device); +} +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE, quirk_disable_amd_813x_boot_interrupt); +DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8132_BRIDGE, quirk_disable_amd_813x_boot_interrupt); + +#define AMD_8111_PCI_IRQ_ROUTING 0x56 + +static void quirk_disable_amd_8111_boot_interrupt(struct pci_dev *dev) +{ + u16 pci_config_word; + + if (noioapicquirk) + return; + + pci_read_config_word(dev, AMD_8111_PCI_IRQ_ROUTING, &pci_config_word); + if (!pci_config_word) { + printk(KERN_INFO "boot interrupts on PCI device 0x%04x:0x%04x " + "already disabled\n", + dev->vendor, dev->device); + return; + } + pci_write_config_word(dev, AMD_8111_PCI_IRQ_ROUTING, 0); + printk(KERN_INFO "disabled boot interrupts on PCI device " + "0x%04x:0x%04x\n", dev->vendor, dev->device); +} +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_SMBUS, quirk_disable_amd_8111_boot_interrupt); +DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_SMBUS, quirk_disable_amd_8111_boot_interrupt); +#endif /* CONFIG_X86_IO_APIC */ + /* * Toshiba TC86C001 IDE controller reports the standard 8-byte BAR0 size * but the PIO transfers won't work if BAR0 falls at the odd 8 bytes. @@ -1692,24 +1821,24 @@ static void __devinit quirk_brcm_570x_limit_vpd(struct pci_dev *dev) } } -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM, - PCI_DEVICE_ID_NX2_5706, - quirk_brcm_570x_limit_vpd); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM, - PCI_DEVICE_ID_NX2_5706S, - quirk_brcm_570x_limit_vpd); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM, - PCI_DEVICE_ID_NX2_5708, - quirk_brcm_570x_limit_vpd); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM, - PCI_DEVICE_ID_NX2_5708S, - quirk_brcm_570x_limit_vpd); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM, - PCI_DEVICE_ID_NX2_5709, - quirk_brcm_570x_limit_vpd); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM, - PCI_DEVICE_ID_NX2_5709S, - quirk_brcm_570x_limit_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM, + PCI_DEVICE_ID_NX2_5706, + quirk_brcm_570x_limit_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM, + PCI_DEVICE_ID_NX2_5706S, + quirk_brcm_570x_limit_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM, + PCI_DEVICE_ID_NX2_5708, + quirk_brcm_570x_limit_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM, + PCI_DEVICE_ID_NX2_5708S, + quirk_brcm_570x_limit_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM, + PCI_DEVICE_ID_NX2_5709, + quirk_brcm_570x_limit_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM, + PCI_DEVICE_ID_NX2_5709S, + quirk_brcm_570x_limit_vpd); #ifdef CONFIG_PCI_MSI /* Some chipsets do not support MSI. We cannot easily rely on setting @@ -1828,6 +1957,22 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_HT1000_PXB, ht_enable_msi_mapping); +/* The P5N32-SLI Premium motherboard from Asus has a problem with msi + * for the MCP55 NIC. It is not yet determined whether the msi problem + * also affects other devices. As for now, turn off msi for this device. + */ +static void __devinit nvenet_msi_disable(struct pci_dev *dev) +{ + if (dmi_name_in_vendors("P5N32-SLI PREMIUM")) { + dev_info(&dev->dev, + "Disabling msi for MCP55 NIC on P5N32-SLI Premium\n"); + dev->no_msi = 1; + } +} +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, + PCI_DEVICE_ID_NVIDIA_NVENET_15, + nvenet_msi_disable); + static void __devinit nv_msi_ht_cap_quirk(struct pci_dev *dev) { struct pci_dev *host_bridge; @@ -1929,11 +2074,12 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x4375, #endif /* CONFIG_PCI_MSI */ -static void pci_do_fixups(struct pci_dev *dev, struct pci_fixup *f, struct pci_fixup *end) +static void pci_do_fixups(struct pci_dev *dev, struct pci_fixup *f, + struct pci_fixup *end) { while (f < end) { if ((f->vendor == dev->vendor || f->vendor == (u16) PCI_ANY_ID) && - (f->device == dev->device || f->device == (u16) PCI_ANY_ID)) { + (f->device == dev->device || f->device == (u16) PCI_ANY_ID)) { dev_dbg(&dev->dev, "calling %pF\n", f->hook); f->hook(dev); } diff --git a/drivers/pci/rom.c b/drivers/pci/rom.c index 1f5f6143f35c..132a78159b60 100644 --- a/drivers/pci/rom.c +++ b/drivers/pci/rom.c @@ -100,7 +100,8 @@ size_t pci_get_rom_size(void __iomem *rom, size_t size) * pci_map_rom - map a PCI ROM to kernel space * @pdev: pointer to pci device struct * @size: pointer to receive size of pci window over ROM - * @return: kernel virtual pointer to image of ROM + * + * Return: kernel virtual pointer to image of ROM * * Map a PCI ROM into kernel space. If ROM is boot video ROM, * the shadow BIOS copy will be returned instead of the @@ -167,7 +168,8 @@ void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size) * pci_map_rom_copy - map a PCI ROM to kernel space, create a copy * @pdev: pointer to pci device struct * @size: pointer to receive size of pci window over ROM - * @return: kernel virtual pointer to image of ROM + * + * Return: kernel virtual pointer to image of ROM * * Map a PCI ROM into kernel space. If ROM is boot video ROM, * the shadow BIOS copy will be returned instead of the diff --git a/drivers/pcmcia/cistpl.c b/drivers/pcmcia/cistpl.c index dcce9f5d8465..4a110b7b2673 100644 --- a/drivers/pcmcia/cistpl.c +++ b/drivers/pcmcia/cistpl.c @@ -351,10 +351,11 @@ int verify_cis_cache(struct pcmcia_socket *s) char *buf; buf = kmalloc(256, GFP_KERNEL); - if (buf == NULL) + if (buf == NULL) { dev_printk(KERN_WARNING, &s->dev, "no memory for verifying CIS\n"); return -ENOMEM; + } list_for_each_entry(cis, &s->cis_cache, node) { int len = cis->len; diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index c68c5d338285..0660ad182589 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -186,12 +186,6 @@ int pcmcia_register_socket(struct pcmcia_socket *socket) spin_lock_init(&socket->lock); - if (socket->resource_ops->init) { - ret = socket->resource_ops->init(socket); - if (ret) - return (ret); - } - /* try to obtain a socket number [yes, it gets ugly if we * register more than 2^sizeof(unsigned int) pcmcia * sockets... but the socket number is deprecated @@ -226,7 +220,7 @@ int pcmcia_register_socket(struct pcmcia_socket *socket) /* set proper values in socket->dev */ dev_set_drvdata(&socket->dev, socket); socket->dev.class = &pcmcia_socket_class; - snprintf(socket->dev.bus_id, BUS_ID_SIZE, "pcmcia_socket%u", socket->sock); + dev_set_name(&socket->dev, "pcmcia_socket%u", socket->sock); /* base address = 0, map = 0 */ socket->cis_mem.flags = 0; @@ -239,6 +233,12 @@ int pcmcia_register_socket(struct pcmcia_socket *socket) mutex_init(&socket->skt_mutex); spin_lock_init(&socket->thread_lock); + if (socket->resource_ops->init) { + ret = socket->resource_ops->init(socket); + if (ret) + goto err; + } + tsk = kthread_run(pccardd, socket, "pccardd"); if (IS_ERR(tsk)) { ret = PTR_ERR(tsk); diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 795660255490..47cab31ff6e4 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c @@ -622,7 +622,6 @@ struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int f { struct pcmcia_device *p_dev, *tmp_dev; unsigned long flags; - int bus_id_len; s = pcmcia_get_socket(s); if (!s) @@ -650,12 +649,12 @@ struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int f /* by default don't allow DMA */ p_dev->dma_mask = DMA_MASK_NONE; p_dev->dev.dma_mask = &p_dev->dma_mask; - bus_id_len = sprintf (p_dev->dev.bus_id, "%d.%d", p_dev->socket->sock, p_dev->device_no); - - p_dev->devname = kmalloc(6 + bus_id_len + 1, GFP_KERNEL); + dev_set_name(&p_dev->dev, "%d.%d", p_dev->socket->sock, p_dev->device_no); + if (!dev_name(&p_dev->dev)) + goto err_free; + p_dev->devname = kasprintf(GFP_KERNEL, "pcmcia%s", dev_name(&p_dev->dev)); if (!p_dev->devname) goto err_free; - sprintf (p_dev->devname, "pcmcia%s", p_dev->dev.bus_id); ds_dev_dbg(3, &p_dev->dev, "devname is %s\n", p_dev->devname); spin_lock_irqsave(&pcmcia_dev_list_lock, flags); @@ -668,6 +667,8 @@ struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int f list_for_each_entry(tmp_dev, &s->devices_list, socket_device_list) if (p_dev->func == tmp_dev->func) { p_dev->function_config = tmp_dev->function_config; + p_dev->io = tmp_dev->io; + p_dev->irq = tmp_dev->irq; kref_get(&p_dev->function_config->ref); } diff --git a/drivers/pcmcia/pcmcia_resource.c b/drivers/pcmcia/pcmcia_resource.c index 76d4a98f0955..f5d0ba8e22d5 100644 --- a/drivers/pcmcia/pcmcia_resource.c +++ b/drivers/pcmcia/pcmcia_resource.c @@ -302,9 +302,10 @@ int pcmcia_modify_configuration(struct pcmcia_device *p_dev, /* We only allow changing Vpp1 and Vpp2 to the same value */ if ((mod->Attributes & CONF_VPP1_CHANGE_VALID) && (mod->Attributes & CONF_VPP2_CHANGE_VALID)) { - if (mod->Vpp1 != mod->Vpp2) + if (mod->Vpp1 != mod->Vpp2) { ds_dbg(s, 0, "Vpp1 and Vpp2 must be the same\n"); return -EINVAL; + } s->socket.Vpp = mod->Vpp1; if (s->ops->set_socket(s, &s->socket)) { dev_printk(KERN_WARNING, &s->dev, diff --git a/drivers/pcmcia/rsrc_nonstatic.c b/drivers/pcmcia/rsrc_nonstatic.c index 17f4ecf1c0c5..9ca22c7aafb2 100644 --- a/drivers/pcmcia/rsrc_nonstatic.c +++ b/drivers/pcmcia/rsrc_nonstatic.c @@ -71,7 +71,7 @@ static DEFINE_MUTEX(rsrc_mutex); ======================================================================*/ static struct resource * -make_resource(resource_size_t b, resource_size_t n, int flags, char *name) +make_resource(resource_size_t b, resource_size_t n, int flags, const char *name) { struct resource *res = kzalloc(sizeof(*res), GFP_KERNEL); @@ -624,7 +624,7 @@ static int nonstatic_adjust_io_region(struct resource *res, unsigned long r_star static struct resource *nonstatic_find_io_region(unsigned long base, int num, unsigned long align, struct pcmcia_socket *s) { - struct resource *res = make_resource(0, num, IORESOURCE_IO, s->dev.bus_id); + struct resource *res = make_resource(0, num, IORESOURCE_IO, dev_name(&s->dev)); struct socket_data *s_data = s->resource_data; struct pcmcia_align_data data; unsigned long min = base; @@ -658,7 +658,7 @@ static struct resource *nonstatic_find_io_region(unsigned long base, int num, static struct resource * nonstatic_find_mem_region(u_long base, u_long num, u_long align, int low, struct pcmcia_socket *s) { - struct resource *res = make_resource(0, num, IORESOURCE_MEM, s->dev.bus_id); + struct resource *res = make_resource(0, num, IORESOURCE_MEM, dev_name(&s->dev)); struct socket_data *s_data = s->resource_data; struct pcmcia_align_data data; unsigned long min, max; diff --git a/drivers/pnp/card.c b/drivers/pnp/card.c index e75b060daa95..efea128f02da 100644 --- a/drivers/pnp/card.c +++ b/drivers/pnp/card.c @@ -165,8 +165,7 @@ struct pnp_card *pnp_alloc_card(struct pnp_protocol *protocol, int id, char *pnp card->number = id; card->dev.parent = &card->protocol->dev; - sprintf(card->dev.bus_id, "%02x:%02x", card->protocol->number, - card->number); + dev_set_name(&card->dev, "%02x:%02x", card->protocol->number, card->number); card->dev.coherent_dma_mask = DMA_24BIT_MASK; card->dev.dma_mask = &card->dev.coherent_dma_mask; @@ -295,8 +294,8 @@ int pnp_add_card_device(struct pnp_card *card, struct pnp_dev *dev) { dev->dev.parent = &card->dev; dev->card_link = NULL; - snprintf(dev->dev.bus_id, BUS_ID_SIZE, "%02x:%02x.%02x", - dev->protocol->number, card->number, dev->number); + dev_set_name(&dev->dev, "%02x:%02x.%02x", + dev->protocol->number, card->number, dev->number); spin_lock(&pnp_lock); dev->card = card; list_add_tail(&dev->card_list, &card->devices); diff --git a/drivers/pnp/core.c b/drivers/pnp/core.c index 16c01c6fa7c5..14814f231739 100644 --- a/drivers/pnp/core.c +++ b/drivers/pnp/core.c @@ -70,7 +70,7 @@ int pnp_register_protocol(struct pnp_protocol *protocol) spin_unlock(&pnp_lock); protocol->number = nodenum; - sprintf(protocol->dev.bus_id, "pnp%d", nodenum); + dev_set_name(&protocol->dev, "pnp%d", nodenum); return device_register(&protocol->dev); } @@ -145,8 +145,7 @@ struct pnp_dev *pnp_alloc_dev(struct pnp_protocol *protocol, int id, char *pnpid dev->dev.coherent_dma_mask = dev->dma_mask; dev->dev.release = &pnp_release_device; - sprintf(dev->dev.bus_id, "%02x:%02x", dev->protocol->number, - dev->number); + dev_set_name(&dev->dev, "%02x:%02x", dev->protocol->number, dev->number); dev_id = pnp_add_id(dev, pnpid); if (!dev_id) { diff --git a/drivers/pnp/interface.c b/drivers/pnp/interface.c index 478a4a739c00..c3f1c8e9d254 100644 --- a/drivers/pnp/interface.c +++ b/drivers/pnp/interface.c @@ -12,7 +12,6 @@ #include <linux/errno.h> #include <linux/list.h> #include <linux/types.h> -#include <linux/pnp.h> #include <linux/stat.h> #include <linux/ctype.h> #include <linux/slab.h> diff --git a/drivers/pnp/system.c b/drivers/pnp/system.c index 764f3a310685..59b90922da8c 100644 --- a/drivers/pnp/system.c +++ b/drivers/pnp/system.c @@ -26,7 +26,7 @@ static void reserve_range(struct pnp_dev *dev, resource_size_t start, resource_size_t end, int port) { char *regionid; - const char *pnpid = dev->dev.bus_id; + const char *pnpid = dev_name(&dev->dev); struct resource *res; regionid = kmalloc(16, GFP_KERNEL); diff --git a/drivers/power/ds2760_battery.c b/drivers/power/ds2760_battery.c index 308ddb201b66..1d768928e0bb 100644 --- a/drivers/power/ds2760_battery.c +++ b/drivers/power/ds2760_battery.c @@ -354,7 +354,7 @@ static int ds2760_battery_probe(struct platform_device *pdev) pdata = pdev->dev.platform_data; di->dev = &pdev->dev; di->w1_dev = pdev->dev.parent; - di->bat.name = pdev->dev.bus_id; + di->bat.name = dev_name(&pdev->dev); di->bat.type = POWER_SUPPLY_TYPE_BATTERY; di->bat.properties = ds2760_battery_props; di->bat.num_properties = ARRAY_SIZE(ds2760_battery_props); @@ -371,7 +371,7 @@ static int ds2760_battery_probe(struct platform_device *pdev) } INIT_DELAYED_WORK(&di->monitor_work, ds2760_battery_work); - di->monitor_wqueue = create_singlethread_workqueue(pdev->dev.bus_id); + di->monitor_wqueue = create_singlethread_workqueue(dev_name(&pdev->dev)); if (!di->monitor_wqueue) { retval = -ESRCH; goto workqueue_failed; diff --git a/drivers/ps3/ps3-lpm.c b/drivers/ps3/ps3-lpm.c index 85edf945ab86..204158cf7a55 100644 --- a/drivers/ps3/ps3-lpm.c +++ b/drivers/ps3/ps3-lpm.c @@ -22,6 +22,7 @@ #include <linux/module.h> #include <linux/interrupt.h> #include <linux/uaccess.h> +#include <asm/smp.h> #include <asm/time.h> #include <asm/ps3.h> #include <asm/lv1call.h> diff --git a/drivers/ps3/ps3av.c b/drivers/ps3/ps3av.c index 06848b254d57..5324978b73fb 100644 --- a/drivers/ps3/ps3av.c +++ b/drivers/ps3/ps3av.c @@ -59,8 +59,6 @@ static struct ps3av { struct ps3av_reply_hdr reply_hdr; u8 raw[PS3AV_BUF_SIZE]; } recv_buf; - void (*flip_ctl)(int on, void *data); - void *flip_data; } *ps3av; /* color space */ @@ -939,24 +937,6 @@ int ps3av_audio_mute(int mute) EXPORT_SYMBOL_GPL(ps3av_audio_mute); -void ps3av_register_flip_ctl(void (*flip_ctl)(int on, void *data), - void *flip_data) -{ - mutex_lock(&ps3av->mutex); - ps3av->flip_ctl = flip_ctl; - ps3av->flip_data = flip_data; - mutex_unlock(&ps3av->mutex); -} -EXPORT_SYMBOL_GPL(ps3av_register_flip_ctl); - -void ps3av_flip_ctl(int on) -{ - mutex_lock(&ps3av->mutex); - if (ps3av->flip_ctl) - ps3av->flip_ctl(on, ps3av->flip_data); - mutex_unlock(&ps3av->mutex); -} - static int ps3av_probe(struct ps3_system_bus_device *dev) { int res; diff --git a/drivers/ps3/ps3av_cmd.c b/drivers/ps3/ps3av_cmd.c index 11eb50318fec..716596e8e5b0 100644 --- a/drivers/ps3/ps3av_cmd.c +++ b/drivers/ps3/ps3av_cmd.c @@ -864,7 +864,7 @@ int ps3av_cmd_avb_param(struct ps3av_pkt_avb_param *avb, u32 send_len) { int res; - ps3av_flip_ctl(0); /* flip off */ + mutex_lock(&ps3_gpu_mutex); /* avb packet */ res = ps3av_do_pkt(PS3AV_CID_AVB_PARAM, send_len, sizeof(*avb), @@ -878,7 +878,7 @@ int ps3av_cmd_avb_param(struct ps3av_pkt_avb_param *avb, u32 send_len) res); out: - ps3av_flip_ctl(1); /* flip on */ + mutex_unlock(&ps3_gpu_mutex); return res; } diff --git a/drivers/rapidio/rio-scan.c b/drivers/rapidio/rio-scan.c index a926c896475e..5c13f61bfb1b 100644 --- a/drivers/rapidio/rio-scan.c +++ b/drivers/rapidio/rio-scan.c @@ -365,15 +365,15 @@ static struct rio_dev *rio_setup_device(struct rio_net *net, rdid++) rswitch->route_table[rdid] = RIO_INVALID_ROUTE; rdev->rswitch = rswitch; - sprintf(rio_name(rdev), "%02x:s:%04x", rdev->net->id, - rdev->rswitch->switchid); + dev_set_name(&rdev->dev, "%02x:s:%04x", rdev->net->id, + rdev->rswitch->switchid); rio_route_set_ops(rdev); list_add_tail(&rswitch->node, &rio_switches); } else - sprintf(rio_name(rdev), "%02x:e:%04x", rdev->net->id, - rdev->destid); + dev_set_name(&rdev->dev, "%02x:e:%04x", rdev->net->id, + rdev->destid); rdev->dev.bus = &rio_bus_type; @@ -879,7 +879,7 @@ static void rio_update_route_tables(struct rio_mport *port) * link, then start recursive peer enumeration. Returns %0 if * enumeration succeeds or %-EBUSY if enumeration fails. */ -int rio_enum_mport(struct rio_mport *mport) +int __devinit rio_enum_mport(struct rio_mport *mport) { struct rio_net *net = NULL; int rc = 0; @@ -972,7 +972,7 @@ static void rio_enum_timeout(unsigned long data) * peer discovery. Returns %0 if discovery succeeds or %-EBUSY * on failure. */ -int rio_disc_mport(struct rio_mport *mport) +int __devinit rio_disc_mport(struct rio_mport *mport) { struct rio_net *net = NULL; int enum_timeout_flag = 0; diff --git a/drivers/rapidio/rio.c b/drivers/rapidio/rio.c index 680661abbc4b..6395c780008b 100644 --- a/drivers/rapidio/rio.c +++ b/drivers/rapidio/rio.c @@ -467,7 +467,7 @@ static int __devinit rio_init(void) device_initcall(rio_init); -int rio_init_mports(void) +int __devinit rio_init_mports(void) { int rc = 0; struct rio_mport *port; diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 4dada6ee1119..39360e2a4540 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -1,6 +1,4 @@ -menu "Voltage and Current regulators" - -config REGULATOR +menuconfig REGULATOR bool "Voltage and Current Regulator Support" default n help @@ -23,21 +21,20 @@ config REGULATOR If unsure, say no. +if REGULATOR + config REGULATOR_DEBUG bool "Regulator debug support" - depends on REGULATOR help Say yes here to enable debugging support. config REGULATOR_FIXED_VOLTAGE tristate default n - select REGULATOR config REGULATOR_VIRTUAL_CONSUMER tristate "Virtual regulator consumer support" default n - select REGULATOR help This driver provides a virtual consumer for the voltage and current regulator API which provides sysfs controls for @@ -49,7 +46,6 @@ config REGULATOR_VIRTUAL_CONSUMER config REGULATOR_BQ24022 tristate "TI bq24022 Dual Input 1-Cell Li-Ion Charger IC" default n - select REGULATOR help This driver controls a TI bq24022 Charger attached via GPIOs. The provided current regulator can enable/disable @@ -59,7 +55,6 @@ config REGULATOR_BQ24022 config REGULATOR_WM8350 tristate "Wolfson Microelectroncis WM8350 AudioPlus PMIC" depends on MFD_WM8350 - select REGULATOR help This driver provides support for the voltage and current regulators of the WM8350 AudioPlus PMIC. @@ -67,7 +62,6 @@ config REGULATOR_WM8350 config REGULATOR_WM8400 tristate "Wolfson Microelectroncis WM8400 AudioPlus PMIC" depends on MFD_WM8400 - select REGULATOR help This driver provides support for the voltage regulators of the WM8400 AudioPlus PMIC. @@ -75,9 +69,8 @@ config REGULATOR_WM8400 config REGULATOR_DA903X tristate "Support regulators on Dialog Semiconductor DA9030/DA9034 PMIC" depends on PMIC_DA903X - select REGULATOR help Say y here to support the BUCKs and LDOs regulators found on Dialog Semiconductor DA9030/DA9034 PMIC. -endmenu +endif diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 8abbb2020af9..123092d8a984 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -277,6 +277,14 @@ config RTC_DRV_FM3130 This driver can also be built as a module. If so the module will be called rtc-fm3130. +config RTC_DRV_RX8581 + tristate "Epson RX-8581" + help + If you say yes here you will get support for the Epson RX-8581. + + This driver can also be built as a module. If so the module + will be called rtc-rx8581. + endif # I2C comment "SPI RTC drivers" @@ -302,6 +310,17 @@ config RTC_DRV_DS1305 This driver can also be built as a module. If so, the module will be called rtc-ds1305. +config RTC_DRV_DS1390 + tristate "Dallas/Maxim DS1390/93/94" + help + If you say yes here you get support for the DS1390/93/94 chips. + + This driver only supports the RTC feature, and not other chip + features such as alarms and trickle charging. + + This driver can also be built as a module. If so, the module + will be called rtc-ds1390. + config RTC_DRV_MAX6902 tristate "Maxim MAX6902" help @@ -468,6 +487,16 @@ config RTC_DRV_V3020 This driver can also be built as a module. If so, the module will be called rtc-v3020. +config RTC_DRV_WM8350 + tristate "Wolfson Microelectronics WM8350 RTC" + depends on MFD_WM8350 + help + If you say yes here you will get support for the RTC subsystem + of the Wolfson Microelectronics WM8350. + + This driver can also be built as a module. If so, the module + will be called "rtc-wm8350". + comment "on-CPU RTC drivers" config RTC_DRV_OMAP diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index e9e8474cc8fe..6e79c912bf9e 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_RTC_DRV_DS1302) += rtc-ds1302.o obj-$(CONFIG_RTC_DRV_DS1305) += rtc-ds1305.o obj-$(CONFIG_RTC_DRV_DS1307) += rtc-ds1307.o obj-$(CONFIG_RTC_DRV_DS1374) += rtc-ds1374.o +obj-$(CONFIG_RTC_DRV_DS1390) += rtc-ds1390.o obj-$(CONFIG_RTC_DRV_DS1511) += rtc-ds1511.o obj-$(CONFIG_RTC_DRV_DS1553) += rtc-ds1553.o obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o @@ -57,6 +58,7 @@ obj-$(CONFIG_RTC_DRV_R9701) += rtc-r9701.o obj-$(CONFIG_RTC_DRV_RS5C313) += rtc-rs5c313.o obj-$(CONFIG_RTC_DRV_RS5C348) += rtc-rs5c348.o obj-$(CONFIG_RTC_DRV_RS5C372) += rtc-rs5c372.o +obj-$(CONFIG_RTC_DRV_RX8581) += rtc-rx8581.o obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o obj-$(CONFIG_RTC_DRV_SA1100) += rtc-sa1100.o @@ -66,4 +68,5 @@ obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o obj-$(CONFIG_RTC_DRV_TWL4030) += rtc-twl4030.o obj-$(CONFIG_RTC_DRV_V3020) += rtc-v3020.o obj-$(CONFIG_RTC_DRV_VR41XX) += rtc-vr41xx.o +obj-$(CONFIG_RTC_DRV_WM8350) += rtc-wm8350.o obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index 7af60b98d8a4..a04c1b6b1575 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -271,7 +271,7 @@ int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) dev_dbg(&rtc->dev, "alarm rollover: %s\n", "year"); do { alarm->time.tm_year++; - } while (!rtc_valid_tm(&alarm->time)); + } while (rtc_valid_tm(&alarm->time) != 0); break; default: diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index 5549231179a2..6cf8e282338f 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -794,7 +794,7 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) goto cleanup2; } - pr_info("%s: alarms up to one %s%s, %zd bytes nvram, %s irqs\n", + pr_info("%s: alarms up to one %s%s, %zd bytes nvram%s\n", cmos_rtc.rtc->dev.bus_id, is_valid_irq(rtc_irq) ? (cmos_rtc.mon_alrm diff --git a/drivers/rtc/rtc-ds1390.c b/drivers/rtc/rtc-ds1390.c new file mode 100644 index 000000000000..599e976bf014 --- /dev/null +++ b/drivers/rtc/rtc-ds1390.c @@ -0,0 +1,220 @@ +/* + * rtc-ds1390.c -- driver for DS1390/93/94 + * + * Copyright (C) 2008 Mercury IMC Ltd + * Written by Mark Jackson <mpfj@mimc.co.uk> + * + * 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. + * + * NOTE : Currently this driver only supports the bare minimum for read + * and write the RTC. The extra features provided by the chip family + * (alarms, trickle charger, different control registers) are unavailable. + */ + +#include <linux/platform_device.h> +#include <linux/rtc.h> +#include <linux/spi/spi.h> +#include <linux/bcd.h> + +#define DS1390_REG_100THS 0x00 +#define DS1390_REG_SECONDS 0x01 +#define DS1390_REG_MINUTES 0x02 +#define DS1390_REG_HOURS 0x03 +#define DS1390_REG_DAY 0x04 +#define DS1390_REG_DATE 0x05 +#define DS1390_REG_MONTH_CENT 0x06 +#define DS1390_REG_YEAR 0x07 + +#define DS1390_REG_ALARM_100THS 0x08 +#define DS1390_REG_ALARM_SECONDS 0x09 +#define DS1390_REG_ALARM_MINUTES 0x0A +#define DS1390_REG_ALARM_HOURS 0x0B +#define DS1390_REG_ALARM_DAY_DATE 0x0C + +#define DS1390_REG_CONTROL 0x0D +#define DS1390_REG_STATUS 0x0E +#define DS1390_REG_TRICKLE 0x0F + +struct ds1390 { + struct rtc_device *rtc; + u8 txrx_buf[9]; /* cmd + 8 registers */ +}; + +static void ds1390_set_reg(struct device *dev, unsigned char address, + unsigned char data) +{ + struct spi_device *spi = to_spi_device(dev); + struct ds1390 *chip = dev_get_drvdata(dev); + + /* Set MSB to indicate write */ + chip->txrx_buf[0] = address | 0x80; + chip->txrx_buf[1] = data; + + /* do the i/o */ + spi_write_then_read(spi, chip->txrx_buf, 2, NULL, 0); +} + +static int ds1390_get_reg(struct device *dev, unsigned char address, + unsigned char *data) +{ + struct spi_device *spi = to_spi_device(dev); + struct ds1390 *chip = dev_get_drvdata(dev); + int status; + + if (!data) + return -EINVAL; + + /* Clear MSB to indicate read */ + chip->txrx_buf[0] = address & 0x7f; + /* do the i/o */ + status = spi_write_then_read(spi, chip->txrx_buf, 1, chip->txrx_buf, 1); + if (status != 0) + return status; + + *data = chip->txrx_buf[1]; + + return 0; +} + +static int ds1390_get_datetime(struct device *dev, struct rtc_time *dt) +{ + struct spi_device *spi = to_spi_device(dev); + struct ds1390 *chip = dev_get_drvdata(dev); + int status; + + /* build the message */ + chip->txrx_buf[0] = DS1390_REG_SECONDS; + + /* do the i/o */ + status = spi_write_then_read(spi, chip->txrx_buf, 1, chip->txrx_buf, 8); + if (status != 0) + return status; + + /* The chip sends data in this order: + * Seconds, Minutes, Hours, Day, Date, Month / Century, Year */ + dt->tm_sec = bcd2bin(chip->txrx_buf[0]); + dt->tm_min = bcd2bin(chip->txrx_buf[1]); + dt->tm_hour = bcd2bin(chip->txrx_buf[2]); + dt->tm_wday = bcd2bin(chip->txrx_buf[3]); + dt->tm_mday = bcd2bin(chip->txrx_buf[4]); + /* mask off century bit */ + dt->tm_mon = bcd2bin(chip->txrx_buf[5] & 0x7f) - 1; + /* adjust for century bit */ + dt->tm_year = bcd2bin(chip->txrx_buf[6]) + ((chip->txrx_buf[5] & 0x80) ? 100 : 0); + + return rtc_valid_tm(dt); +} + +static int ds1390_set_datetime(struct device *dev, struct rtc_time *dt) +{ + struct spi_device *spi = to_spi_device(dev); + struct ds1390 *chip = dev_get_drvdata(dev); + + /* build the message */ + chip->txrx_buf[0] = DS1390_REG_SECONDS | 0x80; + chip->txrx_buf[1] = bin2bcd(dt->tm_sec); + chip->txrx_buf[2] = bin2bcd(dt->tm_min); + chip->txrx_buf[3] = bin2bcd(dt->tm_hour); + chip->txrx_buf[4] = bin2bcd(dt->tm_wday); + chip->txrx_buf[5] = bin2bcd(dt->tm_mday); + chip->txrx_buf[6] = bin2bcd(dt->tm_mon + 1) | + ((dt->tm_year > 99) ? 0x80 : 0x00); + chip->txrx_buf[7] = bin2bcd(dt->tm_year % 100); + + /* do the i/o */ + return spi_write_then_read(spi, chip->txrx_buf, 8, NULL, 0); +} + +static int ds1390_read_time(struct device *dev, struct rtc_time *tm) +{ + return ds1390_get_datetime(dev, tm); +} + +static int ds1390_set_time(struct device *dev, struct rtc_time *tm) +{ + return ds1390_set_datetime(dev, tm); +} + +static const struct rtc_class_ops ds1390_rtc_ops = { + .read_time = ds1390_read_time, + .set_time = ds1390_set_time, +}; + +static int __devinit ds1390_probe(struct spi_device *spi) +{ + struct rtc_device *rtc; + unsigned char tmp; + struct ds1390 *chip; + int res; + + printk(KERN_DEBUG "DS1390 SPI RTC driver\n"); + + rtc = rtc_device_register("ds1390", + &spi->dev, &ds1390_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc)) { + printk(KERN_ALERT "RTC : unable to register device\n"); + return PTR_ERR(rtc); + } + + spi->mode = SPI_MODE_3; + spi->bits_per_word = 8; + spi_setup(spi); + + chip = kzalloc(sizeof *chip, GFP_KERNEL); + if (!chip) { + printk(KERN_ALERT "RTC : unable to allocate device memory\n"); + rtc_device_unregister(rtc); + return -ENOMEM; + } + chip->rtc = rtc; + dev_set_drvdata(&spi->dev, chip); + + res = ds1390_get_reg(&spi->dev, DS1390_REG_SECONDS, &tmp); + if (res) { + printk(KERN_ALERT "RTC : unable to read device\n"); + rtc_device_unregister(rtc); + return res; + } + + return 0; +} + +static int __devexit ds1390_remove(struct spi_device *spi) +{ + struct ds1390 *chip = platform_get_drvdata(spi); + struct rtc_device *rtc = chip->rtc; + + if (rtc) + rtc_device_unregister(rtc); + + kfree(chip); + + return 0; +} + +static struct spi_driver ds1390_driver = { + .driver = { + .name = "rtc-ds1390", + .owner = THIS_MODULE, + }, + .probe = ds1390_probe, + .remove = __devexit_p(ds1390_remove), +}; + +static __init int ds1390_init(void) +{ + return spi_register_driver(&ds1390_driver); +} +module_init(ds1390_init); + +static __exit void ds1390_exit(void) +{ + spi_unregister_driver(&ds1390_driver); +} +module_exit(ds1390_exit); + +MODULE_DESCRIPTION("DS1390/93/94 SPI RTC driver"); +MODULE_AUTHOR("Mark Jackson <mpfj@mimc.co.uk>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-rx8581.c b/drivers/rtc/rtc-rx8581.c new file mode 100644 index 000000000000..c9522f3bc21c --- /dev/null +++ b/drivers/rtc/rtc-rx8581.c @@ -0,0 +1,281 @@ +/* + * An I2C driver for the Epson RX8581 RTC + * + * Author: Martyn Welch <martyn.welch@gefanuc.com> + * Copyright 2008 GE Fanuc Intelligent Platforms Embedded Systems, 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. + * + * Based on: rtc-pcf8563.c (An I2C driver for the Philips PCF8563 RTC) + * Copyright 2005-06 Tower Technologies + */ + +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/bcd.h> +#include <linux/rtc.h> +#include <linux/log2.h> + +#define DRV_VERSION "0.1" + +#define RX8581_REG_SC 0x00 /* Second in BCD */ +#define RX8581_REG_MN 0x01 /* Minute in BCD */ +#define RX8581_REG_HR 0x02 /* Hour in BCD */ +#define RX8581_REG_DW 0x03 /* Day of Week */ +#define RX8581_REG_DM 0x04 /* Day of Month in BCD */ +#define RX8581_REG_MO 0x05 /* Month in BCD */ +#define RX8581_REG_YR 0x06 /* Year in BCD */ +#define RX8581_REG_RAM 0x07 /* RAM */ +#define RX8581_REG_AMN 0x08 /* Alarm Min in BCD*/ +#define RX8581_REG_AHR 0x09 /* Alarm Hour in BCD */ +#define RX8581_REG_ADM 0x0A +#define RX8581_REG_ADW 0x0A +#define RX8581_REG_TMR0 0x0B +#define RX8581_REG_TMR1 0x0C +#define RX8581_REG_EXT 0x0D /* Extension Register */ +#define RX8581_REG_FLAG 0x0E /* Flag Register */ +#define RX8581_REG_CTRL 0x0F /* Control Register */ + + +/* Flag Register bit definitions */ +#define RX8581_FLAG_UF 0x20 /* Update */ +#define RX8581_FLAG_TF 0x10 /* Timer */ +#define RX8581_FLAG_AF 0x08 /* Alarm */ +#define RX8581_FLAG_VLF 0x02 /* Voltage Low */ + +/* Control Register bit definitions */ +#define RX8581_CTRL_UIE 0x20 /* Update Interrupt Enable */ +#define RX8581_CTRL_TIE 0x10 /* Timer Interrupt Enable */ +#define RX8581_CTRL_AIE 0x08 /* Alarm Interrupt Enable */ +#define RX8581_CTRL_STOP 0x02 /* STOP bit */ +#define RX8581_CTRL_RESET 0x01 /* RESET bit */ + +static struct i2c_driver rx8581_driver; + +/* + * In the routines that deal directly with the rx8581 hardware, we use + * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch. + */ +static int rx8581_get_datetime(struct i2c_client *client, struct rtc_time *tm) +{ + unsigned char date[7]; + int data, err; + + /* First we ensure that the "update flag" is not set, we read the + * time and date then re-read the "update flag". If the update flag + * has been set, we know that the time has changed during the read so + * we repeat the whole process again. + */ + data = i2c_smbus_read_byte_data(client, RX8581_REG_FLAG); + if (data < 0) { + dev_err(&client->dev, "Unable to read device flags\n"); + return -EIO; + } + + do { + /* If update flag set, clear it */ + if (data & RX8581_FLAG_UF) { + err = i2c_smbus_write_byte_data(client, + RX8581_REG_FLAG, (data & ~RX8581_FLAG_UF)); + if (err != 0) { + dev_err(&client->dev, "Unable to write device " + "flags\n"); + return -EIO; + } + } + + /* Now read time and date */ + err = i2c_smbus_read_i2c_block_data(client, RX8581_REG_SC, + 7, date); + if (err < 0) { + dev_err(&client->dev, "Unable to read date\n"); + return -EIO; + } + + /* Check flag register */ + data = i2c_smbus_read_byte_data(client, RX8581_REG_FLAG); + if (data < 0) { + dev_err(&client->dev, "Unable to read device flags\n"); + return -EIO; + } + } while (data & RX8581_FLAG_UF); + + if (data & RX8581_FLAG_VLF) + dev_info(&client->dev, + "low voltage detected, date/time is not reliable.\n"); + + dev_dbg(&client->dev, + "%s: raw data is sec=%02x, min=%02x, hr=%02x, " + "wday=%02x, mday=%02x, mon=%02x, year=%02x\n", + __func__, + date[0], date[1], date[2], date[3], date[4], date[5], date[6]); + + tm->tm_sec = bcd2bin(date[RX8581_REG_SC] & 0x7F); + tm->tm_min = bcd2bin(date[RX8581_REG_MN] & 0x7F); + tm->tm_hour = bcd2bin(date[RX8581_REG_HR] & 0x3F); /* rtc hr 0-23 */ + tm->tm_wday = ilog2(date[RX8581_REG_DW] & 0x7F); + tm->tm_mday = bcd2bin(date[RX8581_REG_DM] & 0x3F); + tm->tm_mon = bcd2bin(date[RX8581_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */ + tm->tm_year = bcd2bin(date[RX8581_REG_YR]); + if (tm->tm_year < 70) + tm->tm_year += 100; /* assume we are in 1970...2069 */ + + + dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, " + "mday=%d, mon=%d, year=%d, wday=%d\n", + __func__, + tm->tm_sec, tm->tm_min, tm->tm_hour, + tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); + + err = rtc_valid_tm(tm); + if (err < 0) + dev_err(&client->dev, "retrieved date/time is not valid.\n"); + + return err; +} + +static int rx8581_set_datetime(struct i2c_client *client, struct rtc_time *tm) +{ + int data, err; + unsigned char buf[7]; + + dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, " + "mday=%d, mon=%d, year=%d, wday=%d\n", + __func__, + tm->tm_sec, tm->tm_min, tm->tm_hour, + tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); + + /* hours, minutes and seconds */ + buf[RX8581_REG_SC] = bin2bcd(tm->tm_sec); + buf[RX8581_REG_MN] = bin2bcd(tm->tm_min); + buf[RX8581_REG_HR] = bin2bcd(tm->tm_hour); + + buf[RX8581_REG_DM] = bin2bcd(tm->tm_mday); + + /* month, 1 - 12 */ + buf[RX8581_REG_MO] = bin2bcd(tm->tm_mon + 1); + + /* year and century */ + buf[RX8581_REG_YR] = bin2bcd(tm->tm_year % 100); + buf[RX8581_REG_DW] = (0x1 << tm->tm_wday); + + /* Stop the clock */ + data = i2c_smbus_read_byte_data(client, RX8581_REG_CTRL); + if (data < 0) { + dev_err(&client->dev, "Unable to read control register\n"); + return -EIO; + } + + err = i2c_smbus_write_byte_data(client, RX8581_REG_FLAG, + (data | RX8581_CTRL_STOP)); + if (err < 0) { + dev_err(&client->dev, "Unable to write control register\n"); + return -EIO; + } + + /* write register's data */ + err = i2c_smbus_write_i2c_block_data(client, RX8581_REG_SC, 7, buf); + if (err < 0) { + dev_err(&client->dev, "Unable to write to date registers\n"); + return -EIO; + } + + /* Restart the clock */ + data = i2c_smbus_read_byte_data(client, RX8581_REG_CTRL); + if (data < 0) { + dev_err(&client->dev, "Unable to read control register\n"); + return -EIO; + } + + err = i2c_smbus_write_byte_data(client, RX8581_REG_FLAG, + (data | ~(RX8581_CTRL_STOP))); + if (err != 0) { + dev_err(&client->dev, "Unable to write control register\n"); + return -EIO; + } + + return 0; +} + +static int rx8581_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + return rx8581_get_datetime(to_i2c_client(dev), tm); +} + +static int rx8581_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + return rx8581_set_datetime(to_i2c_client(dev), tm); +} + +static const struct rtc_class_ops rx8581_rtc_ops = { + .read_time = rx8581_rtc_read_time, + .set_time = rx8581_rtc_set_time, +}; + +static int __devinit rx8581_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct rtc_device *rtc; + + dev_dbg(&client->dev, "%s\n", __func__); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n"); + + rtc = rtc_device_register(rx8581_driver.driver.name, + &client->dev, &rx8581_rtc_ops, THIS_MODULE); + + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + i2c_set_clientdata(client, rtc); + + return 0; +} + +static int __devexit rx8581_remove(struct i2c_client *client) +{ + struct rtc_device *rtc = i2c_get_clientdata(client); + + rtc_device_unregister(rtc); + + return 0; +} + +static const struct i2c_device_id rx8581_id[] = { + { "rx8581", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rx8581_id); + +static struct i2c_driver rx8581_driver = { + .driver = { + .name = "rtc-rx8581", + .owner = THIS_MODULE, + }, + .probe = rx8581_probe, + .remove = __devexit_p(rx8581_remove), + .id_table = rx8581_id, +}; + +static int __init rx8581_init(void) +{ + return i2c_add_driver(&rx8581_driver); +} + +static void __exit rx8581_exit(void) +{ + i2c_del_driver(&rx8581_driver); +} + +MODULE_AUTHOR("Martyn Welch <martyn.welch@gefanuc.com>"); +MODULE_DESCRIPTION("Epson RX-8581 RTC driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + +module_init(rx8581_init); +module_exit(rx8581_exit); diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index f59277bbedaa..7a568beba3f0 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c @@ -26,7 +26,7 @@ #include <asm/uaccess.h> #include <asm/io.h> #include <asm/irq.h> -#include <asm/plat-s3c/regs-rtc.h> +#include <plat/regs-rtc.h> /* I have yet to find an S3C implementation with more than one * of these rtc blocks in */ diff --git a/drivers/rtc/rtc-sun4v.c b/drivers/rtc/rtc-sun4v.c index 2012ccbb4a53..5b2261052a65 100644 --- a/drivers/rtc/rtc-sun4v.c +++ b/drivers/rtc/rtc-sun4v.c @@ -1,4 +1,4 @@ -/* rtc-sun4c.c: Hypervisor based RTC for SUN4V systems. +/* rtc-sun4v.c: Hypervisor based RTC for SUN4V systems. * * Copyright (C) 2008 David S. Miller <davem@davemloft.net> */ @@ -7,21 +7,11 @@ #include <linux/module.h> #include <linux/delay.h> #include <linux/init.h> -#include <linux/time.h> #include <linux/rtc.h> #include <linux/platform_device.h> #include <asm/hypervisor.h> -MODULE_AUTHOR("David S. Miller <davem@davemloft.net>"); -MODULE_DESCRIPTION("SUN4V RTC driver"); -MODULE_LICENSE("GPL"); - -struct sun4v_rtc { - struct rtc_device *rtc; - spinlock_t lock; -}; - static unsigned long hypervisor_get_time(void) { unsigned long ret, time; @@ -45,15 +35,7 @@ retry: static int sun4v_read_time(struct device *dev, struct rtc_time *tm) { - struct sun4v_rtc *p = dev_get_drvdata(dev); - unsigned long flags, secs; - - spin_lock_irqsave(&p->lock, flags); - secs = hypervisor_get_time(); - spin_unlock_irqrestore(&p->lock, flags); - - rtc_time_to_tm(secs, tm); - + rtc_time_to_tm(hypervisor_get_time(), tm); return 0; } @@ -80,19 +62,14 @@ retry: static int sun4v_set_time(struct device *dev, struct rtc_time *tm) { - struct sun4v_rtc *p = dev_get_drvdata(dev); - unsigned long flags, secs; + unsigned long secs; int err; err = rtc_tm_to_time(tm, &secs); if (err) return err; - spin_lock_irqsave(&p->lock, flags); - err = hypervisor_set_time(secs); - spin_unlock_irqrestore(&p->lock, flags); - - return err; + return hypervisor_set_time(secs); } static const struct rtc_class_ops sun4v_rtc_ops = { @@ -100,33 +77,22 @@ static const struct rtc_class_ops sun4v_rtc_ops = { .set_time = sun4v_set_time, }; -static int __devinit sun4v_rtc_probe(struct platform_device *pdev) +static int __init sun4v_rtc_probe(struct platform_device *pdev) { - struct sun4v_rtc *p = kzalloc(sizeof(*p), GFP_KERNEL); - - if (!p) - return -ENOMEM; - - spin_lock_init(&p->lock); - - p->rtc = rtc_device_register("sun4v", &pdev->dev, + struct rtc_device *rtc = rtc_device_register("sun4v", &pdev->dev, &sun4v_rtc_ops, THIS_MODULE); - if (IS_ERR(p->rtc)) { - int err = PTR_ERR(p->rtc); - kfree(p); - return err; - } - platform_set_drvdata(pdev, p); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + platform_set_drvdata(pdev, rtc); return 0; } -static int __devexit sun4v_rtc_remove(struct platform_device *pdev) +static int __exit sun4v_rtc_remove(struct platform_device *pdev) { - struct sun4v_rtc *p = platform_get_drvdata(pdev); - - rtc_device_unregister(p->rtc); - kfree(p); + struct rtc_device *rtc = platform_get_drvdata(pdev); + rtc_device_unregister(rtc); return 0; } @@ -135,13 +101,12 @@ static struct platform_driver sun4v_rtc_driver = { .name = "rtc-sun4v", .owner = THIS_MODULE, }, - .probe = sun4v_rtc_probe, - .remove = __devexit_p(sun4v_rtc_remove), + .remove = __exit_p(sun4v_rtc_remove), }; static int __init sun4v_rtc_init(void) { - return platform_driver_register(&sun4v_rtc_driver); + return platform_driver_probe(&sun4v_rtc_driver, sun4v_rtc_probe); } static void __exit sun4v_rtc_exit(void) @@ -151,3 +116,7 @@ static void __exit sun4v_rtc_exit(void) module_init(sun4v_rtc_init); module_exit(sun4v_rtc_exit); + +MODULE_AUTHOR("David S. Miller <davem@davemloft.net>"); +MODULE_DESCRIPTION("SUN4V RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-wm8350.c b/drivers/rtc/rtc-wm8350.c new file mode 100644 index 000000000000..5c5e3aa91385 --- /dev/null +++ b/drivers/rtc/rtc-wm8350.c @@ -0,0 +1,514 @@ +/* + * Real Time Clock driver for Wolfson Microelectronics WM8350 + * + * Copyright (C) 2007, 2008 Wolfson Microelectronics PLC. + * + * Author: Liam Girdwood + * linux@wolfsonmicro.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/time.h> +#include <linux/rtc.h> +#include <linux/bcd.h> +#include <linux/interrupt.h> +#include <linux/ioctl.h> +#include <linux/completion.h> +#include <linux/mfd/wm8350/rtc.h> +#include <linux/mfd/wm8350/core.h> +#include <linux/delay.h> +#include <linux/platform_device.h> + +#define WM8350_SET_ALM_RETRIES 5 +#define WM8350_SET_TIME_RETRIES 5 +#define WM8350_GET_TIME_RETRIES 5 + +#define to_wm8350_from_rtc_dev(d) container_of(d, struct wm8350, rtc.pdev.dev) + +/* + * Read current time and date in RTC + */ +static int wm8350_rtc_readtime(struct device *dev, struct rtc_time *tm) +{ + struct wm8350 *wm8350 = dev_get_drvdata(dev); + u16 time1[4], time2[4]; + int retries = WM8350_GET_TIME_RETRIES, ret; + + /* + * Read the time twice and compare. + * If time1 == time2, then time is valid else retry. + */ + do { + ret = wm8350_block_read(wm8350, WM8350_RTC_SECONDS_MINUTES, + 4, time1); + if (ret < 0) + return ret; + ret = wm8350_block_read(wm8350, WM8350_RTC_SECONDS_MINUTES, + 4, time2); + if (ret < 0) + return ret; + + if (memcmp(time1, time2, sizeof(time1)) == 0) { + tm->tm_sec = time1[0] & WM8350_RTC_SECS_MASK; + + tm->tm_min = (time1[0] & WM8350_RTC_MINS_MASK) + >> WM8350_RTC_MINS_SHIFT; + + tm->tm_hour = time1[1] & WM8350_RTC_HRS_MASK; + + tm->tm_wday = ((time1[1] >> WM8350_RTC_DAY_SHIFT) + & 0x7) - 1; + + tm->tm_mon = ((time1[2] & WM8350_RTC_MTH_MASK) + >> WM8350_RTC_MTH_SHIFT) - 1; + + tm->tm_mday = (time1[2] & WM8350_RTC_DATE_MASK); + + tm->tm_year = ((time1[3] & WM8350_RTC_YHUNDREDS_MASK) + >> WM8350_RTC_YHUNDREDS_SHIFT) * 100; + tm->tm_year += time1[3] & WM8350_RTC_YUNITS_MASK; + + tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, + tm->tm_year); + tm->tm_year -= 1900; + + dev_dbg(dev, "Read (%d left): %04x %04x %04x %04x\n", + retries, + time1[0], time1[1], time1[2], time1[3]); + + return 0; + } + } while (retries--); + + dev_err(dev, "timed out reading RTC time\n"); + return -EIO; +} + +/* + * Set current time and date in RTC + */ +static int wm8350_rtc_settime(struct device *dev, struct rtc_time *tm) +{ + struct wm8350 *wm8350 = dev_get_drvdata(dev); + u16 time[4]; + u16 rtc_ctrl; + int ret, retries = WM8350_SET_TIME_RETRIES; + + time[0] = tm->tm_sec; + time[0] |= tm->tm_min << WM8350_RTC_MINS_SHIFT; + time[1] = tm->tm_hour; + time[1] |= (tm->tm_wday + 1) << WM8350_RTC_DAY_SHIFT; + time[2] = tm->tm_mday; + time[2] |= (tm->tm_mon + 1) << WM8350_RTC_MTH_SHIFT; + time[3] = ((tm->tm_year + 1900) / 100) << WM8350_RTC_YHUNDREDS_SHIFT; + time[3] |= (tm->tm_year + 1900) % 100; + + dev_dbg(dev, "Setting: %04x %04x %04x %04x\n", + time[0], time[1], time[2], time[3]); + + /* Set RTC_SET to stop the clock */ + ret = wm8350_set_bits(wm8350, WM8350_RTC_TIME_CONTROL, WM8350_RTC_SET); + if (ret < 0) + return ret; + + /* Wait until confirmation of stopping */ + do { + rtc_ctrl = wm8350_reg_read(wm8350, WM8350_RTC_TIME_CONTROL); + schedule_timeout_uninterruptible(msecs_to_jiffies(1)); + } while (retries-- && !(rtc_ctrl & WM8350_RTC_STS)); + + if (!retries) { + dev_err(dev, "timed out on set confirmation\n"); + return -EIO; + } + + /* Write time to RTC */ + ret = wm8350_block_write(wm8350, WM8350_RTC_SECONDS_MINUTES, 4, time); + if (ret < 0) + return ret; + + /* Clear RTC_SET to start the clock */ + ret = wm8350_clear_bits(wm8350, WM8350_RTC_TIME_CONTROL, + WM8350_RTC_SET); + return ret; +} + +/* + * Read alarm time and date in RTC + */ +static int wm8350_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct wm8350 *wm8350 = dev_get_drvdata(dev); + struct rtc_time *tm = &alrm->time; + u16 time[4]; + int ret; + + ret = wm8350_block_read(wm8350, WM8350_ALARM_SECONDS_MINUTES, 4, time); + if (ret < 0) + return ret; + + tm->tm_sec = time[0] & WM8350_RTC_ALMSECS_MASK; + if (tm->tm_sec == WM8350_RTC_ALMSECS_MASK) + tm->tm_sec = -1; + + tm->tm_min = time[0] & WM8350_RTC_ALMMINS_MASK; + if (tm->tm_min == WM8350_RTC_ALMMINS_MASK) + tm->tm_min = -1; + else + tm->tm_min >>= WM8350_RTC_ALMMINS_SHIFT; + + tm->tm_hour = time[1] & WM8350_RTC_ALMHRS_MASK; + if (tm->tm_hour == WM8350_RTC_ALMHRS_MASK) + tm->tm_hour = -1; + + tm->tm_wday = ((time[1] >> WM8350_RTC_ALMDAY_SHIFT) & 0x7) - 1; + if (tm->tm_wday > 7) + tm->tm_wday = -1; + + tm->tm_mon = time[2] & WM8350_RTC_ALMMTH_MASK; + if (tm->tm_mon == WM8350_RTC_ALMMTH_MASK) + tm->tm_mon = -1; + else + tm->tm_mon = (tm->tm_mon >> WM8350_RTC_ALMMTH_SHIFT) - 1; + + tm->tm_mday = (time[2] & WM8350_RTC_ALMDATE_MASK); + if (tm->tm_mday == WM8350_RTC_ALMDATE_MASK) + tm->tm_mday = -1; + + tm->tm_year = -1; + + alrm->enabled = !(time[3] & WM8350_RTC_ALMSTS); + + return 0; +} + +static int wm8350_rtc_stop_alarm(struct wm8350 *wm8350) +{ + int retries = WM8350_SET_ALM_RETRIES; + u16 rtc_ctrl; + int ret; + + /* Set RTC_SET to stop the clock */ + ret = wm8350_set_bits(wm8350, WM8350_RTC_TIME_CONTROL, + WM8350_RTC_ALMSET); + if (ret < 0) + return ret; + + /* Wait until confirmation of stopping */ + do { + rtc_ctrl = wm8350_reg_read(wm8350, WM8350_RTC_TIME_CONTROL); + schedule_timeout_uninterruptible(msecs_to_jiffies(1)); + } while (retries-- && !(rtc_ctrl & WM8350_RTC_ALMSTS)); + + if (!(rtc_ctrl & WM8350_RTC_ALMSTS)) + return -ETIMEDOUT; + + return 0; +} + +static int wm8350_rtc_start_alarm(struct wm8350 *wm8350) +{ + int ret; + int retries = WM8350_SET_ALM_RETRIES; + u16 rtc_ctrl; + + ret = wm8350_clear_bits(wm8350, WM8350_RTC_TIME_CONTROL, + WM8350_RTC_ALMSET); + if (ret < 0) + return ret; + + /* Wait until confirmation */ + do { + rtc_ctrl = wm8350_reg_read(wm8350, WM8350_RTC_TIME_CONTROL); + schedule_timeout_uninterruptible(msecs_to_jiffies(1)); + } while (retries-- && rtc_ctrl & WM8350_RTC_ALMSTS); + + if (rtc_ctrl & WM8350_RTC_ALMSTS) + return -ETIMEDOUT; + + return 0; +} + +static int wm8350_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct wm8350 *wm8350 = dev_get_drvdata(dev); + struct rtc_time *tm = &alrm->time; + u16 time[3]; + int ret; + + memset(time, 0, sizeof(time)); + + if (tm->tm_sec != -1) + time[0] |= tm->tm_sec; + else + time[0] |= WM8350_RTC_ALMSECS_MASK; + + if (tm->tm_min != -1) + time[0] |= tm->tm_min << WM8350_RTC_ALMMINS_SHIFT; + else + time[0] |= WM8350_RTC_ALMMINS_MASK; + + if (tm->tm_hour != -1) + time[1] |= tm->tm_hour; + else + time[1] |= WM8350_RTC_ALMHRS_MASK; + + if (tm->tm_wday != -1) + time[1] |= (tm->tm_wday + 1) << WM8350_RTC_ALMDAY_SHIFT; + else + time[1] |= WM8350_RTC_ALMDAY_MASK; + + if (tm->tm_mday != -1) + time[2] |= tm->tm_mday; + else + time[2] |= WM8350_RTC_ALMDATE_MASK; + + if (tm->tm_mon != -1) + time[2] |= (tm->tm_mon + 1) << WM8350_RTC_ALMMTH_SHIFT; + else + time[2] |= WM8350_RTC_ALMMTH_MASK; + + ret = wm8350_rtc_stop_alarm(wm8350); + if (ret < 0) + return ret; + + /* Write time to RTC */ + ret = wm8350_block_write(wm8350, WM8350_ALARM_SECONDS_MINUTES, + 3, time); + if (ret < 0) + return ret; + + if (alrm->enabled) + ret = wm8350_rtc_start_alarm(wm8350); + + return ret; +} + +/* + * Handle commands from user-space + */ +static int wm8350_rtc_ioctl(struct device *dev, unsigned int cmd, + unsigned long arg) +{ + struct wm8350 *wm8350 = dev_get_drvdata(dev); + + switch (cmd) { + case RTC_AIE_OFF: + return wm8350_rtc_stop_alarm(wm8350); + case RTC_AIE_ON: + return wm8350_rtc_start_alarm(wm8350); + + case RTC_UIE_OFF: + wm8350_mask_irq(wm8350, WM8350_IRQ_RTC_SEC); + break; + case RTC_UIE_ON: + wm8350_unmask_irq(wm8350, WM8350_IRQ_RTC_SEC); + break; + + default: + return -ENOIOCTLCMD; + } + + return 0; +} + +static void wm8350_rtc_alarm_handler(struct wm8350 *wm8350, int irq, + void *data) +{ + struct rtc_device *rtc = wm8350->rtc.rtc; + int ret; + + rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF); + + /* Make it one shot */ + ret = wm8350_set_bits(wm8350, WM8350_RTC_TIME_CONTROL, + WM8350_RTC_ALMSET); + if (ret != 0) { + dev_err(&(wm8350->rtc.pdev->dev), + "Failed to disable alarm: %d\n", ret); + } +} + +static void wm8350_rtc_update_handler(struct wm8350 *wm8350, int irq, + void *data) +{ + struct rtc_device *rtc = wm8350->rtc.rtc; + + rtc_update_irq(rtc, 1, RTC_IRQF | RTC_UF); +} + +static const struct rtc_class_ops wm8350_rtc_ops = { + .ioctl = wm8350_rtc_ioctl, + .read_time = wm8350_rtc_readtime, + .set_time = wm8350_rtc_settime, + .read_alarm = wm8350_rtc_readalarm, + .set_alarm = wm8350_rtc_setalarm, +}; + +#ifdef CONFIG_PM +static int wm8350_rtc_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct wm8350 *wm8350 = dev_get_drvdata(&pdev->dev); + int ret = 0; + u16 reg; + + reg = wm8350_reg_read(wm8350, WM8350_RTC_TIME_CONTROL); + + if (device_may_wakeup(&wm8350->rtc.pdev->dev) && + reg & WM8350_RTC_ALMSTS) { + ret = wm8350_rtc_stop_alarm(wm8350); + if (ret != 0) + dev_err(&pdev->dev, "Failed to stop RTC alarm: %d\n", + ret); + } + + return ret; +} + +static int wm8350_rtc_resume(struct platform_device *pdev) +{ + struct wm8350 *wm8350 = dev_get_drvdata(&pdev->dev); + int ret; + + if (wm8350->rtc.alarm_enabled) { + ret = wm8350_rtc_start_alarm(wm8350); + if (ret != 0) + dev_err(&pdev->dev, + "Failed to restart RTC alarm: %d\n", ret); + } + + return 0; +} + +#else +#define wm8350_rtc_suspend NULL +#define wm8350_rtc_resume NULL +#endif + +static int wm8350_rtc_probe(struct platform_device *pdev) +{ + struct wm8350 *wm8350 = platform_get_drvdata(pdev); + struct wm8350_rtc *wm_rtc = &wm8350->rtc; + int ret = 0; + u16 timectl, power5; + + timectl = wm8350_reg_read(wm8350, WM8350_RTC_TIME_CONTROL); + if (timectl & WM8350_RTC_BCD) { + dev_err(&pdev->dev, "RTC BCD mode not supported\n"); + return -EINVAL; + } + if (timectl & WM8350_RTC_12HR) { + dev_err(&pdev->dev, "RTC 12 hour mode not supported\n"); + return -EINVAL; + } + + /* enable the RTC if it's not already enabled */ + power5 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_5); + if (!(power5 & WM8350_RTC_TICK_ENA)) { + dev_info(wm8350->dev, "Starting RTC\n"); + + wm8350_reg_unlock(wm8350); + + ret = wm8350_set_bits(wm8350, WM8350_POWER_MGMT_5, + WM8350_RTC_TICK_ENA); + if (ret < 0) { + dev_err(&pdev->dev, "failed to enable RTC: %d\n", ret); + return ret; + } + + wm8350_reg_lock(wm8350); + } + + if (timectl & WM8350_RTC_STS) { + int retries; + + ret = wm8350_clear_bits(wm8350, WM8350_RTC_TIME_CONTROL, + WM8350_RTC_SET); + if (ret < 0) { + dev_err(&pdev->dev, "failed to start: %d\n", ret); + return ret; + } + + retries = WM8350_SET_TIME_RETRIES; + do { + timectl = wm8350_reg_read(wm8350, + WM8350_RTC_TIME_CONTROL); + } while (timectl & WM8350_RTC_STS && retries--); + + if (retries == 0) { + dev_err(&pdev->dev, "failed to start: timeout\n"); + return -ENODEV; + } + } + + device_init_wakeup(&pdev->dev, 1); + + wm_rtc->rtc = rtc_device_register("wm8350", &pdev->dev, + &wm8350_rtc_ops, THIS_MODULE); + if (IS_ERR(wm_rtc->rtc)) { + ret = PTR_ERR(wm_rtc->rtc); + dev_err(&pdev->dev, "failed to register RTC: %d\n", ret); + return ret; + } + + wm8350_mask_irq(wm8350, WM8350_IRQ_RTC_SEC); + wm8350_mask_irq(wm8350, WM8350_IRQ_RTC_PER); + + wm8350_register_irq(wm8350, WM8350_IRQ_RTC_SEC, + wm8350_rtc_update_handler, NULL); + + wm8350_register_irq(wm8350, WM8350_IRQ_RTC_ALM, + wm8350_rtc_alarm_handler, NULL); + wm8350_unmask_irq(wm8350, WM8350_IRQ_RTC_ALM); + + return 0; +} + +static int __devexit wm8350_rtc_remove(struct platform_device *pdev) +{ + struct wm8350 *wm8350 = platform_get_drvdata(pdev); + struct wm8350_rtc *wm_rtc = &wm8350->rtc; + + wm8350_mask_irq(wm8350, WM8350_IRQ_RTC_SEC); + + wm8350_free_irq(wm8350, WM8350_IRQ_RTC_SEC); + wm8350_free_irq(wm8350, WM8350_IRQ_RTC_ALM); + + rtc_device_unregister(wm_rtc->rtc); + + return 0; +} + +static struct platform_driver wm8350_rtc_driver = { + .probe = wm8350_rtc_probe, + .remove = __devexit_p(wm8350_rtc_remove), + .suspend = wm8350_rtc_suspend, + .resume = wm8350_rtc_resume, + .driver = { + .name = "wm8350-rtc", + }, +}; + +static int __init wm8350_rtc_init(void) +{ + return platform_driver_register(&wm8350_rtc_driver); +} +module_init(wm8350_rtc_init); + +static void __exit wm8350_rtc_exit(void) +{ + platform_driver_unregister(&wm8350_rtc_driver); +} +module_exit(wm8350_rtc_exit); + +MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); +MODULE_DESCRIPTION("RTC driver for the WM8350"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:wm8350-rtc"); diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 4b76fca64a6f..363bd1303d21 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -1746,6 +1746,11 @@ restart: goto restart; } + /* log sense for fatal error */ + if (cqr->status == DASD_CQR_FAILED) { + dasd_log_sense(cqr, &cqr->irb); + } + /* First of all call extended error reporting. */ if (dasd_eer_enabled(base) && cqr->status == DASD_CQR_FAILED) { diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c index 921443b01d16..2ef25731d197 100644 --- a/drivers/s390/block/dasd_devmap.c +++ b/drivers/s390/block/dasd_devmap.c @@ -23,6 +23,7 @@ /* This is ugly... */ #define PRINTK_HEADER "dasd_devmap:" +#define DASD_BUS_ID_SIZE 20 #include "dasd_int.h" @@ -41,7 +42,7 @@ EXPORT_SYMBOL_GPL(dasd_page_cache); */ struct dasd_devmap { struct list_head list; - char bus_id[BUS_ID_SIZE]; + char bus_id[DASD_BUS_ID_SIZE]; unsigned int devindex; unsigned short features; struct dasd_device *device; @@ -94,7 +95,7 @@ dasd_hash_busid(const char *bus_id) int hash, i; hash = 0; - for (i = 0; (i < BUS_ID_SIZE) && *bus_id; i++, bus_id++) + for (i = 0; (i < DASD_BUS_ID_SIZE) && *bus_id; i++, bus_id++) hash += *bus_id; return hash & 0xff; } @@ -301,7 +302,7 @@ dasd_parse_range( char *parsestring ) { int from, from_id0, from_id1; int to, to_id0, to_id1; int features, rc; - char bus_id[BUS_ID_SIZE+1], *str; + char bus_id[DASD_BUS_ID_SIZE+1], *str; str = parsestring; rc = dasd_busid(&str, &from_id0, &from_id1, &from); @@ -407,14 +408,14 @@ dasd_add_busid(const char *bus_id, int features) devmap = NULL; hash = dasd_hash_busid(bus_id); list_for_each_entry(tmp, &dasd_hashlists[hash], list) - if (strncmp(tmp->bus_id, bus_id, BUS_ID_SIZE) == 0) { + if (strncmp(tmp->bus_id, bus_id, DASD_BUS_ID_SIZE) == 0) { devmap = tmp; break; } if (!devmap) { /* This bus_id is new. */ new->devindex = dasd_max_devindex++; - strncpy(new->bus_id, bus_id, BUS_ID_SIZE); + strncpy(new->bus_id, bus_id, DASD_BUS_ID_SIZE); new->features = features; new->device = NULL; list_add(&new->list, &dasd_hashlists[hash]); @@ -439,7 +440,7 @@ dasd_find_busid(const char *bus_id) devmap = ERR_PTR(-ENODEV); hash = dasd_hash_busid(bus_id); list_for_each_entry(tmp, &dasd_hashlists[hash], list) { - if (strncmp(tmp->bus_id, bus_id, BUS_ID_SIZE) == 0) { + if (strncmp(tmp->bus_id, bus_id, DASD_BUS_ID_SIZE) == 0) { devmap = tmp; break; } @@ -561,7 +562,7 @@ dasd_create_device(struct ccw_device *cdev) } spin_lock_irqsave(get_ccwdev_lock(cdev), flags); - cdev->dev.driver_data = device; + dev_set_drvdata(&cdev->dev, device); spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); return device; @@ -597,7 +598,7 @@ dasd_delete_device(struct dasd_device *device) /* Disconnect dasd_device structure from ccw_device structure. */ spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); - device->cdev->dev.driver_data = NULL; + dev_set_drvdata(&device->cdev->dev, NULL); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); /* @@ -638,7 +639,7 @@ dasd_put_device_wake(struct dasd_device *device) struct dasd_device * dasd_device_from_cdev_locked(struct ccw_device *cdev) { - struct dasd_device *device = cdev->dev.driver_data; + struct dasd_device *device = dev_get_drvdata(&cdev->dev); if (!device) return ERR_PTR(-ENODEV); diff --git a/drivers/s390/block/dasd_proc.c b/drivers/s390/block/dasd_proc.c index 9088de84b45d..bf6fd348f20e 100644 --- a/drivers/s390/block/dasd_proc.c +++ b/drivers/s390/block/dasd_proc.c @@ -180,12 +180,12 @@ dasd_calc_metrics(char *page, char **start, off_t off, #ifdef CONFIG_DASD_PROFILE static char * -dasd_statistics_array(char *str, unsigned int *array, int shift) +dasd_statistics_array(char *str, unsigned int *array, int factor) { int i; for (i = 0; i < 32; i++) { - str += sprintf(str, "%7d ", array[i] >> shift); + str += sprintf(str, "%7d ", array[i] / factor); if (i == 15) str += sprintf(str, "\n"); } @@ -202,7 +202,7 @@ dasd_statistics_read(char *page, char **start, off_t off, #ifdef CONFIG_DASD_PROFILE struct dasd_profile_info_t *prof; char *str; - int shift; + int factor; /* check for active profiling */ if (dasd_profile_level == DASD_PROFILE_OFF) { @@ -214,12 +214,14 @@ dasd_statistics_read(char *page, char **start, off_t off, prof = &dasd_global_profile; /* prevent couter 'overflow' on output */ - for (shift = 0; (prof->dasd_io_reqs >> shift) > 9999999; shift++); + for (factor = 1; (prof->dasd_io_reqs / factor) > 9999999; + factor *= 10); str = page; str += sprintf(str, "%d dasd I/O requests\n", prof->dasd_io_reqs); - str += sprintf(str, "with %d sectors(512B each)\n", + str += sprintf(str, "with %u sectors(512B each)\n", prof->dasd_io_sects); + str += sprintf(str, "Scale Factor is %d\n", factor); str += sprintf(str, " __<4 ___8 __16 __32 __64 _128 " " _256 _512 __1k __2k __4k __8k " @@ -230,22 +232,22 @@ dasd_statistics_read(char *page, char **start, off_t off, " __1G __2G __4G " " _>4G\n"); str += sprintf(str, "Histogram of sizes (512B secs)\n"); - str = dasd_statistics_array(str, prof->dasd_io_secs, shift); + str = dasd_statistics_array(str, prof->dasd_io_secs, factor); str += sprintf(str, "Histogram of I/O times (microseconds)\n"); - str = dasd_statistics_array(str, prof->dasd_io_times, shift); + str = dasd_statistics_array(str, prof->dasd_io_times, factor); str += sprintf(str, "Histogram of I/O times per sector\n"); - str = dasd_statistics_array(str, prof->dasd_io_timps, shift); + str = dasd_statistics_array(str, prof->dasd_io_timps, factor); str += sprintf(str, "Histogram of I/O time till ssch\n"); - str = dasd_statistics_array(str, prof->dasd_io_time1, shift); + str = dasd_statistics_array(str, prof->dasd_io_time1, factor); str += sprintf(str, "Histogram of I/O time between ssch and irq\n"); - str = dasd_statistics_array(str, prof->dasd_io_time2, shift); + str = dasd_statistics_array(str, prof->dasd_io_time2, factor); str += sprintf(str, "Histogram of I/O time between ssch " "and irq per sector\n"); - str = dasd_statistics_array(str, prof->dasd_io_time2ps, shift); + str = dasd_statistics_array(str, prof->dasd_io_time2ps, factor); str += sprintf(str, "Histogram of I/O time between irq and end\n"); - str = dasd_statistics_array(str, prof->dasd_io_time3, shift); + str = dasd_statistics_array(str, prof->dasd_io_time3, factor); str += sprintf(str, "# of req in chanq at enqueuing (1..32) \n"); - str = dasd_statistics_array(str, prof->dasd_io_nr_req, shift); + str = dasd_statistics_array(str, prof->dasd_io_nr_req, factor); len = str - page; #else len = sprintf(page, "Statistics are not activated in this kernel\n"); diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c index 63f26a135fe5..26ffc6ab441d 100644 --- a/drivers/s390/block/dcssblk.c +++ b/drivers/s390/block/dcssblk.c @@ -4,6 +4,9 @@ * Authors: Carsten Otte, Stefan Weinhuber, Gerald Schaefer */ +#define KMSG_COMPONENT "dcssblk" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/ctype.h> @@ -17,19 +20,10 @@ #include <linux/interrupt.h> #include <asm/s390_rdev.h> -//#define DCSSBLK_DEBUG /* Debug messages on/off */ #define DCSSBLK_NAME "dcssblk" #define DCSSBLK_MINORS_PER_DISK 1 #define DCSSBLK_PARM_LEN 400 - -#ifdef DCSSBLK_DEBUG -#define PRINT_DEBUG(x...) printk(KERN_DEBUG DCSSBLK_NAME " debug: " x) -#else -#define PRINT_DEBUG(x...) do {} while (0) -#endif -#define PRINT_INFO(x...) printk(KERN_INFO DCSSBLK_NAME " info: " x) -#define PRINT_WARN(x...) printk(KERN_WARNING DCSSBLK_NAME " warning: " x) -#define PRINT_ERR(x...) printk(KERN_ERR DCSSBLK_NAME " error: " x) +#define DCSS_BUS_ID_SIZE 20 static int dcssblk_open(struct block_device *bdev, fmode_t mode); static int dcssblk_release(struct gendisk *disk, fmode_t mode); @@ -50,7 +44,7 @@ static struct block_device_operations dcssblk_devops = { struct dcssblk_dev_info { struct list_head lh; struct device dev; - char segment_name[BUS_ID_SIZE]; + char segment_name[DCSS_BUS_ID_SIZE]; atomic_t use_count; struct gendisk *gd; unsigned long start; @@ -65,7 +59,7 @@ struct dcssblk_dev_info { struct segment_info { struct list_head lh; - char segment_name[BUS_ID_SIZE]; + char segment_name[DCSS_BUS_ID_SIZE]; unsigned long start; unsigned long end; int segment_type; @@ -261,10 +255,9 @@ dcssblk_is_continuous(struct dcssblk_dev_info *dev_info) /* check continuity */ for (i = 0; i < dev_info->num_of_segments - 1; i++) { if ((sort_list[i].end + 1) != sort_list[i+1].start) { - PRINT_ERR("Segment %s is not contiguous with " - "segment %s\n", - sort_list[i].segment_name, - sort_list[i+1].segment_name); + pr_err("Adjacent DCSSs %s and %s are not " + "contiguous\n", sort_list[i].segment_name, + sort_list[i+1].segment_name); rc = -EINVAL; goto out; } @@ -275,10 +268,10 @@ dcssblk_is_continuous(struct dcssblk_dev_info *dev_info) !(sort_list[i+1].segment_type & SEGMENT_EXCLUSIVE) || (sort_list[i+1].segment_type == SEG_TYPE_ER)) { - PRINT_ERR("Segment %s has different type from " - "segment %s\n", - sort_list[i].segment_name, - sort_list[i+1].segment_name); + pr_err("DCSS %s and DCSS %s have " + "incompatible types\n", + sort_list[i].segment_name, + sort_list[i+1].segment_name); rc = -EINVAL; goto out; } @@ -380,8 +373,9 @@ dcssblk_shared_store(struct device *dev, struct device_attribute *attr, const ch } else if (inbuf[0] == '0') { /* reload segments in exclusive mode */ if (dev_info->segment_type == SEG_TYPE_SC) { - PRINT_ERR("Segment type SC (%s) cannot be loaded in " - "non-shared mode\n", dev_info->segment_name); + pr_err("DCSS %s is of type SC and cannot be " + "loaded as exclusive-writable\n", + dev_info->segment_name); rc = -EINVAL; goto out; } @@ -404,9 +398,8 @@ dcssblk_shared_store(struct device *dev, struct device_attribute *attr, const ch goto out; removeseg: - PRINT_ERR("Could not reload segment(s) of the device %s, removing " - "segment(s) now!\n", - dev_info->segment_name); + pr_err("DCSS device %s is removed after a failed access mode " + "change\n", dev_info->segment_name); temp = entry; list_for_each_entry(entry, &dev_info->seg_list, lh) { if (entry != temp) @@ -454,17 +447,17 @@ dcssblk_save_store(struct device *dev, struct device_attribute *attr, const char if (inbuf[0] == '1') { if (atomic_read(&dev_info->use_count) == 0) { // device is idle => we save immediately - PRINT_INFO("Saving segment(s) of the device %s\n", - dev_info->segment_name); + pr_info("All DCSSs that map to device %s are " + "saved\n", dev_info->segment_name); list_for_each_entry(entry, &dev_info->seg_list, lh) { segment_save(entry->segment_name); } } else { // device is busy => we save it when it becomes // idle in dcssblk_release - PRINT_INFO("Device %s is currently busy, segment(s) " - "will be saved when it becomes idle...\n", - dev_info->segment_name); + pr_info("Device %s is in use, its DCSSs will be " + "saved when it becomes idle\n", + dev_info->segment_name); dev_info->save_pending = 1; } } else if (inbuf[0] == '0') { @@ -472,9 +465,9 @@ dcssblk_save_store(struct device *dev, struct device_attribute *attr, const char // device is busy & the user wants to undo his save // request dev_info->save_pending = 0; - PRINT_INFO("Pending save for segment(s) of the device " - "%s deactivated\n", - dev_info->segment_name); + pr_info("A pending save request for device %s " + "has been canceled\n", + dev_info->segment_name); } } else { up_write(&dcssblk_devices_sem); @@ -614,9 +607,8 @@ dcssblk_add_store(struct device *dev, struct device_attribute *attr, const char seg_byte_size = (dev_info->end - dev_info->start + 1); set_capacity(dev_info->gd, seg_byte_size >> 9); // size in sectors - PRINT_INFO("Loaded segment(s) %s, size = %lu Byte, " - "capacity = %lu (512 Byte) sectors\n", local_buf, - seg_byte_size, seg_byte_size >> 9); + pr_info("Loaded %s with total size %lu bytes and capacity %lu " + "sectors\n", local_buf, seg_byte_size, seg_byte_size >> 9); dev_info->save_pending = 0; dev_info->is_shared = 1; @@ -744,13 +736,15 @@ dcssblk_remove_store(struct device *dev, struct device_attribute *attr, const ch dev_info = dcssblk_get_device_by_name(local_buf); if (dev_info == NULL) { up_write(&dcssblk_devices_sem); - PRINT_WARN("Device %s is not loaded!\n", local_buf); + pr_warning("Device %s cannot be removed because it is not a " + "known device\n", local_buf); rc = -ENODEV; goto out_buf; } if (atomic_read(&dev_info->use_count) != 0) { up_write(&dcssblk_devices_sem); - PRINT_WARN("Device %s is in use!\n", local_buf); + pr_warning("Device %s cannot be removed while it is in " + "use\n", local_buf); rc = -EBUSY; goto out_buf; } @@ -807,8 +801,8 @@ dcssblk_release(struct gendisk *disk, fmode_t mode) down_write(&dcssblk_devices_sem); if (atomic_dec_and_test(&dev_info->use_count) && (dev_info->save_pending)) { - PRINT_INFO("Device %s became idle and is being saved now\n", - dev_info->segment_name); + pr_info("Device %s has become idle and is being saved " + "now\n", dev_info->segment_name); list_for_each_entry(entry, &dev_info->seg_list, lh) { segment_save(entry->segment_name); } @@ -851,7 +845,8 @@ dcssblk_make_request(struct request_queue *q, struct bio *bio) case SEG_TYPE_SC: /* cannot write to these segments */ if (bio_data_dir(bio) == WRITE) { - PRINT_WARN("rejecting write to ro device %s\n", + pr_warning("Writing to %s failed because it " + "is a read-only device\n", dev_name(&dev_info->dev)); goto fail; } diff --git a/drivers/s390/block/xpram.c b/drivers/s390/block/xpram.c index 03916989ed2d..76814f3e898a 100644 --- a/drivers/s390/block/xpram.c +++ b/drivers/s390/block/xpram.c @@ -25,6 +25,9 @@ * generic hard disk support to replace ad-hoc partitioning */ +#define KMSG_COMPONENT "xpram" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/ctype.h> /* isdigit, isxdigit */ @@ -42,12 +45,6 @@ #define XPRAM_DEVS 1 /* one partition */ #define XPRAM_MAX_DEVS 32 /* maximal number of devices (partitions) */ -#define PRINT_DEBUG(x...) printk(KERN_DEBUG XPRAM_NAME " debug:" x) -#define PRINT_INFO(x...) printk(KERN_INFO XPRAM_NAME " info:" x) -#define PRINT_WARN(x...) printk(KERN_WARNING XPRAM_NAME " warning:" x) -#define PRINT_ERR(x...) printk(KERN_ERR XPRAM_NAME " error:" x) - - typedef struct { unsigned int size; /* size of xpram segment in pages */ unsigned int offset; /* start page of xpram segment */ @@ -264,7 +261,7 @@ static int __init xpram_setup_sizes(unsigned long pages) /* Check number of devices. */ if (devs <= 0 || devs > XPRAM_MAX_DEVS) { - PRINT_ERR("invalid number %d of devices\n",devs); + pr_err("%d is not a valid number of XPRAM devices\n",devs); return -EINVAL; } xpram_devs = devs; @@ -295,22 +292,22 @@ static int __init xpram_setup_sizes(unsigned long pages) mem_auto_no++; } - PRINT_INFO(" number of devices (partitions): %d \n", xpram_devs); + pr_info(" number of devices (partitions): %d \n", xpram_devs); for (i = 0; i < xpram_devs; i++) { if (xpram_sizes[i]) - PRINT_INFO(" size of partition %d: %u kB\n", - i, xpram_sizes[i]); + pr_info(" size of partition %d: %u kB\n", + i, xpram_sizes[i]); else - PRINT_INFO(" size of partition %d to be set " - "automatically\n",i); + pr_info(" size of partition %d to be set " + "automatically\n",i); } - PRINT_DEBUG(" memory needed (for sized partitions): %lu kB\n", - mem_needed); - PRINT_DEBUG(" partitions to be sized automatically: %d\n", - mem_auto_no); + pr_info(" memory needed (for sized partitions): %lu kB\n", + mem_needed); + pr_info(" partitions to be sized automatically: %d\n", + mem_auto_no); if (mem_needed > pages * 4) { - PRINT_ERR("Not enough expanded memory available\n"); + pr_err("Not enough expanded memory available\n"); return -EINVAL; } @@ -322,8 +319,8 @@ static int __init xpram_setup_sizes(unsigned long pages) */ if (mem_auto_no) { mem_auto = ((pages - mem_needed / 4) / mem_auto_no) * 4; - PRINT_INFO(" automatically determined " - "partition size: %lu kB\n", mem_auto); + pr_info(" automatically determined " + "partition size: %lu kB\n", mem_auto); for (i = 0; i < xpram_devs; i++) if (xpram_sizes[i] == 0) xpram_sizes[i] = mem_auto; @@ -405,12 +402,12 @@ static int __init xpram_init(void) /* Find out size of expanded memory. */ if (xpram_present() != 0) { - PRINT_WARN("No expanded memory available\n"); + pr_err("No expanded memory available\n"); return -ENODEV; } xpram_pages = xpram_highest_page_index() + 1; - PRINT_INFO(" %u pages expanded memory found (%lu KB).\n", - xpram_pages, (unsigned long) xpram_pages*4); + pr_info(" %u pages expanded memory found (%lu KB).\n", + xpram_pages, (unsigned long) xpram_pages*4); rc = xpram_setup_sizes(xpram_pages); if (rc) return rc; diff --git a/drivers/s390/char/monreader.c b/drivers/s390/char/monreader.c index 35fd8dfcaaa6..97e63cf46944 100644 --- a/drivers/s390/char/monreader.c +++ b/drivers/s390/char/monreader.c @@ -7,6 +7,9 @@ * Author: Gerald Schaefer <gerald.schaefer@de.ibm.com> */ +#define KMSG_COMPONENT "monreader" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> @@ -24,19 +27,6 @@ #include <asm/ebcdic.h> #include <asm/extmem.h> -//#define MON_DEBUG /* Debug messages on/off */ - -#define MON_NAME "monreader" - -#define P_INFO(x...) printk(KERN_INFO MON_NAME " info: " x) -#define P_ERROR(x...) printk(KERN_ERR MON_NAME " error: " x) -#define P_WARNING(x...) printk(KERN_WARNING MON_NAME " warning: " x) - -#ifdef MON_DEBUG -#define P_DEBUG(x...) printk(KERN_DEBUG MON_NAME " debug: " x) -#else -#define P_DEBUG(x...) do {} while (0) -#endif #define MON_COLLECT_SAMPLE 0x80 #define MON_COLLECT_EVENT 0x40 @@ -172,7 +162,7 @@ static int mon_send_reply(struct mon_msg *monmsg, } else monmsg->replied_msglim = 1; if (rc) { - P_ERROR("read, IUCV reply failed with rc = %i\n\n", rc); + pr_err("Reading monitor data failed with rc=%i\n", rc); return -EIO; } return 0; @@ -251,7 +241,8 @@ static void mon_iucv_path_severed(struct iucv_path *path, u8 ipuser[16]) { struct mon_private *monpriv = path->private; - P_ERROR("IUCV connection severed with rc = 0x%X\n", ipuser[0]); + pr_err("z/VM *MONITOR system service disconnected with rc=%i\n", + ipuser[0]); iucv_path_sever(path, NULL); atomic_set(&monpriv->iucv_severed, 1); wake_up(&mon_conn_wait_queue); @@ -266,8 +257,7 @@ static void mon_iucv_message_pending(struct iucv_path *path, memcpy(&monpriv->msg_array[monpriv->write_index]->msg, msg, sizeof(*msg)); if (atomic_inc_return(&monpriv->msglim_count) == MON_MSGLIM) { - P_WARNING("IUCV message pending, message limit (%i) reached\n", - MON_MSGLIM); + pr_warning("The read queue for monitor data is full\n"); monpriv->msg_array[monpriv->write_index]->msglim_reached = 1; } monpriv->write_index = (monpriv->write_index + 1) % MON_MSGLIM; @@ -311,8 +301,8 @@ static int mon_open(struct inode *inode, struct file *filp) rc = iucv_path_connect(monpriv->path, &monreader_iucv_handler, MON_SERVICE, NULL, user_data_connect, monpriv); if (rc) { - P_ERROR("iucv connection to *MONITOR failed with " - "IPUSER SEVER code = %i\n", rc); + pr_err("Connecting to the z/VM *MONITOR system service " + "failed with rc=%i\n", rc); rc = -EIO; goto out_path; } @@ -353,7 +343,8 @@ static int mon_close(struct inode *inode, struct file *filp) */ rc = iucv_path_sever(monpriv->path, user_data_sever); if (rc) - P_ERROR("close, iucv_sever failed with rc = %i\n", rc); + pr_warning("Disconnecting the z/VM *MONITOR system service " + "failed with rc=%i\n", rc); atomic_set(&monpriv->iucv_severed, 0); atomic_set(&monpriv->iucv_connected, 0); @@ -469,7 +460,8 @@ static int __init mon_init(void) int rc; if (!MACHINE_IS_VM) { - P_ERROR("not running under z/VM, driver not loaded\n"); + pr_err("The z/VM *MONITOR record device driver cannot be " + "loaded without z/VM\n"); return -ENODEV; } @@ -478,7 +470,8 @@ static int __init mon_init(void) */ rc = iucv_register(&monreader_iucv_handler, 1); if (rc) { - P_ERROR("failed to register with iucv driver\n"); + pr_err("The z/VM *MONITOR record device driver failed to " + "register with IUCV\n"); return rc; } @@ -488,8 +481,8 @@ static int __init mon_init(void) goto out_iucv; } if (rc != SEG_TYPE_SC) { - P_ERROR("segment %s has unsupported type, should be SC\n", - mon_dcss_name); + pr_err("The specified *MONITOR DCSS %s does not have the " + "required type SC\n", mon_dcss_name); rc = -EINVAL; goto out_iucv; } diff --git a/drivers/s390/char/monwriter.c b/drivers/s390/char/monwriter.c index 4d71aa8c1a79..c7d7483bab9a 100644 --- a/drivers/s390/char/monwriter.c +++ b/drivers/s390/char/monwriter.c @@ -8,6 +8,9 @@ * Author(s): Melissa Howland <Melissa.Howland@us.ibm.com> */ +#define KMSG_COMPONENT "monwriter" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> @@ -64,9 +67,9 @@ static int monwrite_diag(struct monwrite_hdr *myhdr, char *buffer, int fcn) rc = appldata_asm(&id, fcn, (void *) buffer, myhdr->datalen); if (rc <= 0) return rc; + pr_err("Writing monitor data failed with rc=%i\n", rc); if (rc == 5) return -EPERM; - printk("DIAG X'DC' error with return code: %i\n", rc); return -EINVAL; } diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c index eb5f1b8bc57f..506390496416 100644 --- a/drivers/s390/char/sclp_cmd.c +++ b/drivers/s390/char/sclp_cmd.c @@ -6,6 +6,9 @@ * Peter Oberparleiter <peter.oberparleiter@de.ibm.com> */ +#define KMSG_COMPONENT "sclp_cmd" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include <linux/completion.h> #include <linux/init.h> #include <linux/errno.h> @@ -16,9 +19,8 @@ #include <linux/memory.h> #include <asm/chpid.h> #include <asm/sclp.h> -#include "sclp.h" -#define TAG "sclp_cmd: " +#include "sclp.h" #define SCLP_CMDW_READ_SCP_INFO 0x00020001 #define SCLP_CMDW_READ_SCP_INFO_FORCED 0x00120001 @@ -169,8 +171,8 @@ static int do_sync_request(sclp_cmdw_t cmd, void *sccb) /* Check response. */ if (request->status != SCLP_REQ_DONE) { - printk(KERN_WARNING TAG "sync request failed " - "(cmd=0x%08x, status=0x%02x)\n", cmd, request->status); + pr_warning("sync request failed (cmd=0x%08x, " + "status=0x%02x)\n", cmd, request->status); rc = -EIO; } out: @@ -224,8 +226,8 @@ int sclp_get_cpu_info(struct sclp_cpu_info *info) if (rc) goto out; if (sccb->header.response_code != 0x0010) { - printk(KERN_WARNING TAG "readcpuinfo failed " - "(response=0x%04x)\n", sccb->header.response_code); + pr_warning("readcpuinfo failed (response=0x%04x)\n", + sccb->header.response_code); rc = -EIO; goto out; } @@ -262,8 +264,9 @@ static int do_cpu_configure(sclp_cmdw_t cmd) case 0x0120: break; default: - printk(KERN_WARNING TAG "configure cpu failed (cmd=0x%08x, " - "response=0x%04x)\n", cmd, sccb->header.response_code); + pr_warning("configure cpu failed (cmd=0x%08x, " + "response=0x%04x)\n", cmd, + sccb->header.response_code); rc = -EIO; break; } @@ -324,6 +327,9 @@ static int do_assign_storage(sclp_cmdw_t cmd, u16 rn) case 0x0120: break; default: + pr_warning("assign storage failed (cmd=0x%08x, " + "response=0x%04x, rn=0x%04x)\n", cmd, + sccb->header.response_code, rn); rc = -EIO; break; } @@ -623,9 +629,9 @@ static int do_chp_configure(sclp_cmdw_t cmd) case 0x0450: break; default: - printk(KERN_WARNING TAG "configure channel-path failed " - "(cmd=0x%08x, response=0x%04x)\n", cmd, - sccb->header.response_code); + pr_warning("configure channel-path failed " + "(cmd=0x%08x, response=0x%04x)\n", cmd, + sccb->header.response_code); rc = -EIO; break; } @@ -692,8 +698,8 @@ int sclp_chp_read_info(struct sclp_chp_info *info) if (rc) goto out; if (sccb->header.response_code != 0x0010) { - printk(KERN_WARNING TAG "read channel-path info failed " - "(response=0x%04x)\n", sccb->header.response_code); + pr_warning("read channel-path info failed " + "(response=0x%04x)\n", sccb->header.response_code); rc = -EIO; goto out; } diff --git a/drivers/s390/char/sclp_config.c b/drivers/s390/char/sclp_config.c index 4cebd6ee6d27..b497afe061cc 100644 --- a/drivers/s390/char/sclp_config.c +++ b/drivers/s390/char/sclp_config.c @@ -5,15 +5,17 @@ * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com> */ +#define KMSG_COMPONENT "sclp_config" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include <linux/init.h> #include <linux/errno.h> #include <linux/cpu.h> #include <linux/sysdev.h> #include <linux/workqueue.h> #include <asm/smp.h> -#include "sclp.h" -#define TAG "sclp_config: " +#include "sclp.h" struct conf_mgm_data { u8 reserved; @@ -31,7 +33,7 @@ static void sclp_cpu_capability_notify(struct work_struct *work) int cpu; struct sys_device *sysdev; - printk(KERN_WARNING TAG "cpu capability changed.\n"); + pr_warning("cpu capability changed.\n"); get_online_cpus(); for_each_online_cpu(cpu) { sysdev = get_cpu_sysdev(cpu); @@ -78,7 +80,7 @@ static int __init sclp_conf_init(void) return rc; if (!(sclp_conf_register.sclp_send_mask & EVTYP_CONFMGMDATA_MASK)) { - printk(KERN_WARNING TAG "no configuration management.\n"); + pr_warning("no configuration management.\n"); sclp_unregister(&sclp_conf_register); rc = -ENOSYS; } diff --git a/drivers/s390/char/sclp_cpi_sys.c b/drivers/s390/char/sclp_cpi_sys.c index d887bd261d28..62c2647f37f4 100644 --- a/drivers/s390/char/sclp_cpi_sys.c +++ b/drivers/s390/char/sclp_cpi_sys.c @@ -7,6 +7,9 @@ * Michael Ernst <mernst@de.ibm.com> */ +#define KMSG_COMPONENT "sclp_cpi" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include <linux/kernel.h> #include <linux/init.h> #include <linux/stat.h> @@ -20,6 +23,7 @@ #include <linux/completion.h> #include <asm/ebcdic.h> #include <asm/sclp.h> + #include "sclp.h" #include "sclp_rw.h" #include "sclp_cpi_sys.h" @@ -150,16 +154,16 @@ static int cpi_req(void) wait_for_completion(&completion); if (req->status != SCLP_REQ_DONE) { - printk(KERN_WARNING "cpi: request failed (status=0x%02x)\n", - req->status); + pr_warning("request failed (status=0x%02x)\n", + req->status); rc = -EIO; goto out_free_req; } response = ((struct cpi_sccb *) req->sccb)->header.response_code; if (response != 0x0020) { - printk(KERN_WARNING "cpi: failed with " - "response code 0x%x\n", response); + pr_warning("request failed with response code 0x%x\n", + response); rc = -EIO; } diff --git a/drivers/s390/char/sclp_sdias.c b/drivers/s390/char/sclp_sdias.c index 8b854857ba07..6a1c58dc61a7 100644 --- a/drivers/s390/char/sclp_sdias.c +++ b/drivers/s390/char/sclp_sdias.c @@ -5,15 +5,18 @@ * Author(s): Michael Holzheu */ +#define KMSG_COMPONENT "sclp_sdias" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include <linux/sched.h> #include <asm/sclp.h> #include <asm/debug.h> #include <asm/ipl.h> + #include "sclp.h" #include "sclp_rw.h" #define TRACE(x...) debug_sprintf_event(sdias_dbf, 1, x) -#define ERROR_MSG(x...) printk ( KERN_ALERT "SDIAS: " x ) #define SDIAS_RETRIES 300 #define SDIAS_SLEEP_TICKS 50 @@ -131,7 +134,7 @@ int sclp_sdias_blk_count(void) rc = sdias_sclp_send(&request); if (rc) { - ERROR_MSG("sclp_send failed for get_nr_blocks\n"); + pr_err("sclp_send failed for get_nr_blocks\n"); goto out; } if (sccb.hdr.response_code != 0x0020) { @@ -145,7 +148,8 @@ int sclp_sdias_blk_count(void) rc = sccb.evbuf.blk_cnt; break; default: - ERROR_MSG("SCLP error: %x\n", sccb.evbuf.event_status); + pr_err("SCLP error: %x\n", + sccb.evbuf.event_status); rc = -EIO; goto out; } @@ -201,7 +205,7 @@ int sclp_sdias_copy(void *dest, int start_blk, int nr_blks) rc = sdias_sclp_send(&request); if (rc) { - ERROR_MSG("sclp_send failed: %x\n", rc); + pr_err("sclp_send failed: %x\n", rc); goto out; } if (sccb.hdr.response_code != 0x0020) { @@ -219,9 +223,9 @@ int sclp_sdias_copy(void *dest, int start_blk, int nr_blks) case EVSTATE_NO_DATA: TRACE("no data\n"); default: - ERROR_MSG("Error from SCLP while copying hsa. " - "Event status = %x\n", - sccb.evbuf.event_status); + pr_err("Error from SCLP while copying hsa. " + "Event status = %x\n", + sccb.evbuf.event_status); rc = -EIO; } out: diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c index 9854f19f5e62..a839aa531d7c 100644 --- a/drivers/s390/char/sclp_vt220.c +++ b/drivers/s390/char/sclp_vt220.c @@ -583,23 +583,6 @@ sclp_vt220_chars_in_buffer(struct tty_struct *tty) return count; } -static void -__sclp_vt220_flush_buffer(void) -{ - unsigned long flags; - - sclp_vt220_emit_current(); - spin_lock_irqsave(&sclp_vt220_lock, flags); - if (timer_pending(&sclp_vt220_timer)) - del_timer(&sclp_vt220_timer); - while (sclp_vt220_outqueue_count > 0) { - spin_unlock_irqrestore(&sclp_vt220_lock, flags); - sclp_sync_wait(); - spin_lock_irqsave(&sclp_vt220_lock, flags); - } - spin_unlock_irqrestore(&sclp_vt220_lock, flags); -} - /* * Pass on all buffers to the hardware. Return only when there are no more * buffers pending. @@ -745,6 +728,22 @@ sclp_vt220_con_device(struct console *c, int *index) return sclp_vt220_driver; } +static void __sclp_vt220_flush_buffer(void) +{ + unsigned long flags; + + sclp_vt220_emit_current(); + spin_lock_irqsave(&sclp_vt220_lock, flags); + if (timer_pending(&sclp_vt220_timer)) + del_timer(&sclp_vt220_timer); + while (sclp_vt220_outqueue_count > 0) { + spin_unlock_irqrestore(&sclp_vt220_lock, flags); + sclp_sync_wait(); + spin_lock_irqsave(&sclp_vt220_lock, flags); + } + spin_unlock_irqrestore(&sclp_vt220_lock, flags); +} + static int sclp_vt220_notify(struct notifier_block *self, unsigned long event, void *data) diff --git a/drivers/s390/char/tape.h b/drivers/s390/char/tape.h index d0d565a05dfe..c07809c8016a 100644 --- a/drivers/s390/char/tape.h +++ b/drivers/s390/char/tape.h @@ -324,8 +324,6 @@ static inline void tape_proc_cleanup (void) {;} #endif /* a function for dumping device sense info */ -extern void tape_dump_sense(struct tape_device *, struct tape_request *, - struct irb *); extern void tape_dump_sense_dbf(struct tape_device *, struct tape_request *, struct irb *); diff --git a/drivers/s390/char/tape_34xx.c b/drivers/s390/char/tape_34xx.c index 22ca34361ed7..6d0d4abdb3e2 100644 --- a/drivers/s390/char/tape_34xx.c +++ b/drivers/s390/char/tape_34xx.c @@ -8,6 +8,8 @@ * Martin Schwidefsky <schwidefsky@de.ibm.com> */ +#define KMSG_COMPONENT "tape" + #include <linux/module.h> #include <linux/init.h> #include <linux/bio.h> @@ -18,8 +20,6 @@ #include "tape.h" #include "tape_std.h" -#define PRINTK_HEADER "TAPE_34XX: " - /* * Pointer to debug area. */ @@ -203,8 +203,7 @@ tape_34xx_unsolicited_irq(struct tape_device *device, struct irb *irb) tape_34xx_schedule_work(device, TO_MSEN); } else { DBF_EVENT(3, "unsol.irq! dev end: %08x\n", device->cdev_id); - PRINT_WARN("Unsolicited IRQ (Device End) caught.\n"); - tape_dump_sense(device, NULL, irb); + tape_dump_sense_dbf(device, NULL, irb); } return TAPE_IO_SUCCESS; } @@ -226,9 +225,7 @@ tape_34xx_erp_read_opposite(struct tape_device *device, tape_std_read_backward(device, request); return tape_34xx_erp_retry(request); } - if (request->op != TO_RBA) - PRINT_ERR("read_opposite called with state:%s\n", - tape_op_verbose[request->op]); + /* * We tried to read forward and backward, but hat no * success -> failed. @@ -241,13 +238,10 @@ tape_34xx_erp_bug(struct tape_device *device, struct tape_request *request, struct irb *irb, int no) { if (request->op != TO_ASSIGN) { - PRINT_WARN("An unexpected condition #%d was caught in " - "tape error recovery.\n", no); - PRINT_WARN("Please report this incident.\n"); - if (request) - PRINT_WARN("Operation of tape:%s\n", - tape_op_verbose[request->op]); - tape_dump_sense(device, request, irb); + dev_err(&device->cdev->dev, "An unexpected condition %d was " + "caught in tape error recovery. Please report this " + "incident", no); + tape_dump_sense_dbf(device, request, irb); } return tape_34xx_erp_failed(request, -EIO); } @@ -261,9 +255,9 @@ tape_34xx_erp_overrun(struct tape_device *device, struct tape_request *request, struct irb *irb) { if (irb->ecw[3] == 0x40) { - PRINT_WARN ("Data overrun error between control-unit " - "and drive. Use a faster channel connection, " - "if possible! \n"); + dev_warn (&device->cdev->dev, "Data overrun error between " + "control-unit and drive. Use a faster channel " + "connection, if possible!"); return tape_34xx_erp_failed(request, -EIO); } return tape_34xx_erp_bug(device, request, irb, -1); @@ -280,7 +274,8 @@ tape_34xx_erp_sequence(struct tape_device *device, /* * cu detected incorrect block-id sequence on tape. */ - PRINT_WARN("Illegal block-id sequence found!\n"); + dev_warn (&device->cdev->dev, "Illegal block-id sequence " + "found!"); return tape_34xx_erp_failed(request, -EIO); } /* @@ -393,8 +388,6 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, /* Writing at physical end of volume */ return tape_34xx_erp_failed(request, -ENOSPC); default: - PRINT_ERR("Invalid op in %s:%i\n", - __func__, __LINE__); return tape_34xx_erp_failed(request, 0); } } @@ -420,7 +413,7 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, irb, -4); /* data check is permanent, CU recovery has failed */ - PRINT_WARN("Permanent read error\n"); + dev_warn (&device->cdev->dev, "Permanent read error"); return tape_34xx_erp_failed(request, -EIO); case 0x25: // a write data check occurred @@ -433,22 +426,25 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, irb, -5); // data check is permanent, cu-recovery has failed - PRINT_WARN("Permanent write error\n"); + dev_warn (&device->cdev->dev, "Permanent write error"); return tape_34xx_erp_failed(request, -EIO); case 0x26: /* Data Check (read opposite) occurred. */ return tape_34xx_erp_read_opposite(device, request); case 0x28: /* ID-Mark at tape start couldn't be written */ - PRINT_WARN("ID-Mark could not be written.\n"); + dev_warn (&device->cdev->dev, "ID-Mark could not be " + "written."); return tape_34xx_erp_failed(request, -EIO); case 0x31: /* Tape void. Tried to read beyond end of device. */ - PRINT_WARN("Read beyond end of recorded area.\n"); + dev_warn (&device->cdev->dev, "Read beyond end of " + "recorded area."); return tape_34xx_erp_failed(request, -ENOSPC); case 0x41: /* Record sequence error. */ - PRINT_WARN("Invalid block-id sequence found.\n"); + dev_warn (&device->cdev->dev, "Invalid block-id " + "sequence found."); return tape_34xx_erp_failed(request, -EIO); default: /* all data checks for 3480 should result in one of @@ -470,16 +466,12 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, switch (sense[3]) { case 0x00: /* Unit check with erpa code 0. Report and ignore. */ - PRINT_WARN("Non-error sense was found. " - "Unit-check will be ignored.\n"); return TAPE_IO_SUCCESS; case 0x21: /* * Data streaming not operational. CU will switch to * interlock mode. Reissue the command. */ - PRINT_WARN("Data streaming not operational. " - "Switching to interlock-mode.\n"); return tape_34xx_erp_retry(request); case 0x22: /* @@ -487,11 +479,7 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, * error on the lower interface, internal path not usable, * or error during cartridge load. */ - PRINT_WARN("A path equipment check occurred. One of the " - "following conditions occurred:\n"); - PRINT_WARN("drive adapter error, buffer error on the lower " - "interface, internal path not usable, error " - "during cartridge load.\n"); + dev_warn (&device->cdev->dev, "path equipment check occurred."); return tape_34xx_erp_failed(request, -EIO); case 0x24: /* @@ -514,7 +502,6 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, * but the hardware isn't capable to do idrc, or a perform * subsystem func is issued and the CU is not on-line. */ - PRINT_WARN ("Function incompatible. Try to switch off idrc\n"); return tape_34xx_erp_failed(request, -EIO); case 0x2a: /* @@ -552,23 +539,24 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, * reading the format id mark or that that format specified * is not supported by the drive. */ - PRINT_WARN("Drive not capable processing the tape format!\n"); + dev_warn (&device->cdev->dev, "Drive not capable processing " + "the tape format!"); return tape_34xx_erp_failed(request, -EMEDIUMTYPE); case 0x30: /* The medium is write protected. */ - PRINT_WARN("Medium is write protected!\n"); + dev_warn (&device->cdev->dev, "Medium is write protected!"); return tape_34xx_erp_failed(request, -EACCES); case 0x32: // Tension loss. We cannot recover this, it's an I/O error. - PRINT_WARN("The drive lost tape tension.\n"); + dev_warn (&device->cdev->dev, "The drive lost tape tension."); return tape_34xx_erp_failed(request, -EIO); case 0x33: /* * Load Failure. The cartridge was not inserted correctly or * the tape is not threaded correctly. */ - PRINT_WARN("Cartridge load failure. Reload the cartridge " - "and try again.\n"); + dev_warn (&device->cdev->dev, "Cartridge load failure. Reload " + "the cartridge and try again."); tape_34xx_delete_sbid_from(device, 0); return tape_34xx_erp_failed(request, -EIO); case 0x34: @@ -576,8 +564,8 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, * Unload failure. The drive cannot maintain tape tension * and control tape movement during an unload operation. */ - PRINT_WARN("Failure during cartridge unload. " - "Please try manually.\n"); + dev_warn (&device->cdev->dev, "Failure during cartridge " + "unload, try manually."); if (request->op == TO_RUN) return tape_34xx_erp_failed(request, -EIO); return tape_34xx_erp_bug(device, request, irb, sense[3]); @@ -589,8 +577,8 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, * - the cartridge loader does not respond correctly * - a failure occurs during an index, load, or unload cycle */ - PRINT_WARN("Equipment check! Please check the drive and " - "the cartridge loader.\n"); + dev_warn (&device->cdev->dev, "Equipment check! Please check " + "the drive and the cartridge loader."); return tape_34xx_erp_failed(request, -EIO); case 0x36: if (device->cdev->id.driver_info == tape_3490) @@ -603,7 +591,7 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, * Tape length error. The tape is shorter than reported in * the beginning-of-tape data. */ - PRINT_WARN("Tape length error.\n"); + dev_warn (&device->cdev->dev, "Tape length error."); return tape_34xx_erp_failed(request, -EIO); case 0x38: /* @@ -620,12 +608,14 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, return tape_34xx_erp_failed(request, -EIO); case 0x3a: /* Drive switched to not ready. */ - PRINT_WARN("Drive not ready. Turn the ready/not ready switch " - "to ready position and try again.\n"); + dev_warn (&device->cdev->dev, "Drive not ready. Turn the " + "ready/not ready switch to ready position and try " + "again."); return tape_34xx_erp_failed(request, -EIO); case 0x3b: /* Manual rewind or unload. This causes an I/O error. */ - PRINT_WARN("Medium was rewound or unloaded manually.\n"); + dev_warn (&device->cdev->dev, "Medium was rewound or unloaded" + " manually."); tape_34xx_delete_sbid_from(device, 0); return tape_34xx_erp_failed(request, -EIO); case 0x42: @@ -633,7 +623,8 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, * Degraded mode. A condition that can cause degraded * performance is detected. */ - PRINT_WARN("Subsystem is running in degraded mode.\n"); + dev_warn (&device->cdev->dev, "Subsystem is running in " + "degraded mode."); return tape_34xx_erp_retry(request); case 0x43: /* Drive not ready. */ @@ -652,7 +643,6 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, break; } } - PRINT_WARN("The drive is not ready.\n"); return tape_34xx_erp_failed(request, -ENOMEDIUM); case 0x44: /* Locate Block unsuccessful. */ @@ -663,7 +653,8 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, return tape_34xx_erp_failed(request, -EIO); case 0x45: /* The drive is assigned to a different channel path. */ - PRINT_WARN("The drive is assigned elsewhere.\n"); + dev_warn (&device->cdev->dev, "The drive is assigned " + "elsewhere."); return tape_34xx_erp_failed(request, -EIO); case 0x46: /* @@ -671,11 +662,12 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, * the power supply may be switched off or * the drive address may not be set correctly. */ - PRINT_WARN("The drive is not on-line."); + dev_warn (&device->cdev->dev, "The drive is not on-line."); return tape_34xx_erp_failed(request, -EIO); case 0x47: /* Volume fenced. CU reports volume integrity is lost. */ - PRINT_WARN("Volume fenced. The volume integrity is lost.\n"); + dev_warn (&device->cdev->dev, "Volume fenced. The volume " + "integrity is lost."); tape_34xx_delete_sbid_from(device, 0); return tape_34xx_erp_failed(request, -EIO); case 0x48: @@ -683,20 +675,21 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, return tape_34xx_erp_retry(request); case 0x49: /* Bus out check. A parity check error on the bus was found. */ - PRINT_WARN("Bus out check. A data transfer over the bus " - "has been corrupted.\n"); + dev_warn (&device->cdev->dev, "Bus out check. A data transfer" + "over the bus has been corrupted."); return tape_34xx_erp_failed(request, -EIO); case 0x4a: /* Control unit erp failed. */ - PRINT_WARN("The control unit I/O error recovery failed.\n"); + dev_warn (&device->cdev->dev, "The control unit I/O error " + "recovery failed."); return tape_34xx_erp_failed(request, -EIO); case 0x4b: /* * CU and drive incompatible. The drive requests micro-program * patches, which are not available on the CU. */ - PRINT_WARN("The drive needs microprogram patches from the " - "control unit, which are not available.\n"); + dev_warn (&device->cdev->dev, "The drive needs microprogram " + "patches from the control unit, which are not available."); return tape_34xx_erp_failed(request, -EIO); case 0x4c: /* @@ -721,8 +714,8 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, * the block to be written is larger than allowed for * buffered mode. */ - PRINT_WARN("Maximum block size for buffered " - "mode exceeded.\n"); + dev_warn (&device->cdev->dev, "Maximum block size for" + " buffered mode exceeded."); return tape_34xx_erp_failed(request, -ENOBUFS); } /* This erpa is reserved for 3480. */ @@ -759,22 +752,20 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, return tape_34xx_erp_retry(request); case 0x55: /* Channel interface recovery (permanent). */ - PRINT_WARN("A permanent channel interface error occurred.\n"); + dev_warn (&device->cdev->dev, "A permanent channel interface " + "error occurred."); return tape_34xx_erp_failed(request, -EIO); case 0x56: /* Channel protocol error. */ - PRINT_WARN("A channel protocol error occurred.\n"); + dev_warn (&device->cdev->dev, "A channel protocol error " + "occurred."); return tape_34xx_erp_failed(request, -EIO); case 0x57: if (device->cdev->id.driver_info == tape_3480) { /* Attention intercept. */ - PRINT_WARN("An attention intercept occurred, " - "which will be recovered.\n"); return tape_34xx_erp_retry(request); } else { /* Global status intercept. */ - PRINT_WARN("An global status intercept was received, " - "which will be recovered.\n"); return tape_34xx_erp_retry(request); } case 0x5a: @@ -782,42 +773,27 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request, * Tape length incompatible. The tape inserted is too long, * which could cause damage to the tape or the drive. */ - PRINT_WARN("Tape Length Incompatible\n"); - PRINT_WARN("Tape length exceeds IBM enhanced capacity " - "cartdridge length or a medium\n"); - PRINT_WARN("with EC-CST identification mark has been mounted " - "in a device that writes\n"); - PRINT_WARN("3480 or 3480 XF format.\n"); + dev_warn (&device->cdev->dev, "Tape Length Incompatible"); return tape_34xx_erp_failed(request, -EIO); case 0x5b: /* Format 3480 XF incompatible */ if (sense[1] & SENSE_BEGINNING_OF_TAPE) /* The tape will get overwritten. */ return tape_34xx_erp_retry(request); - PRINT_WARN("Format 3480 XF Incompatible\n"); - PRINT_WARN("Medium has been created in 3480 format. " - "To change the format writes\n"); - PRINT_WARN("must be issued at BOT.\n"); + dev_warn (&device->cdev->dev, "Format 3480 XF Incompatible"); return tape_34xx_erp_failed(request, -EIO); case 0x5c: /* Format 3480-2 XF incompatible */ - PRINT_WARN("Format 3480-2 XF Incompatible\n"); - PRINT_WARN("Device can only read 3480 or 3480 XF format.\n"); + dev_warn (&device->cdev->dev, "Format 3480-2 XF Incompatible"); return tape_34xx_erp_failed(request, -EIO); case 0x5d: /* Tape length violation. */ - PRINT_WARN("Tape Length Violation\n"); - PRINT_WARN("The mounted tape exceeds IBM Enhanced Capacity " - "Cartdridge System Tape length.\n"); - PRINT_WARN("This may cause damage to the drive or tape when " - "processing to the EOV\n"); + dev_warn (&device->cdev->dev, "Tape Length Violation"); return tape_34xx_erp_failed(request, -EMEDIUMTYPE); case 0x5e: /* Compaction algorithm incompatible. */ - PRINT_WARN("Compaction Algorithm Incompatible\n"); - PRINT_WARN("The volume is recorded using an incompatible " - "compaction algorithm,\n"); - PRINT_WARN("which is not supported by the device.\n"); + dev_warn (&device->cdev->dev, "Compaction Algorithm " + "Incompatible"); return tape_34xx_erp_failed(request, -EMEDIUMTYPE); /* The following erpas should have been covered earlier. */ @@ -848,7 +824,6 @@ tape_34xx_irq(struct tape_device *device, struct tape_request *request, (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) && (request->op == TO_WRI)) { /* Write at end of volume */ - PRINT_INFO("End of volume\n"); /* XXX */ return tape_34xx_erp_failed(request, -ENOSPC); } @@ -869,9 +844,7 @@ tape_34xx_irq(struct tape_device *device, struct tape_request *request, } DBF_EVENT(6, "xunknownirq\n"); - PRINT_ERR("Unexpected interrupt.\n"); - PRINT_ERR("Current op is: %s", tape_op_verbose[request->op]); - tape_dump_sense(device, request, irb); + tape_dump_sense_dbf(device, request, irb); return TAPE_IO_STOP; } diff --git a/drivers/s390/char/tape_3590.c b/drivers/s390/char/tape_3590.c index 4005c44a404c..77e9e430a1b8 100644 --- a/drivers/s390/char/tape_3590.c +++ b/drivers/s390/char/tape_3590.c @@ -8,12 +8,15 @@ * Martin Schwidefsky <schwidefsky@de.ibm.com> */ +#define KMSG_COMPONENT "tape" + #include <linux/module.h> #include <linux/init.h> #include <linux/bio.h> #include <asm/ebcdic.h> #define TAPE_DBF_AREA tape_3590_dbf +#define BUFSIZE 512 /* size of buffers for dynamic generated messages */ #include "tape.h" #include "tape_std.h" @@ -36,7 +39,7 @@ EXPORT_SYMBOL(TAPE_DBF_AREA); * - Read Alternate: implemented *******************************************************************/ -#define PRINTK_HEADER "TAPE_3590: " +#define KMSG_COMPONENT "tape" static const char *tape_3590_msg[TAPE_3590_MAX_MSG] = { [0x00] = "", @@ -726,7 +729,7 @@ static void tape_3590_med_state_set(struct tape_device *device, } c_info->medium_status |= TAPE390_MEDIUM_LOADED_MASK; if (sense->flags & MSENSE_CRYPT_MASK) { - PRINT_INFO("Medium is encrypted (%04x)\n", sense->flags); + DBF_EVENT(6, "Medium is encrypted (%04x)\n", sense->flags); c_info->medium_status |= TAPE390_MEDIUM_ENCRYPTED_MASK; } else { DBF_EVENT(6, "Medium is not encrypted %04x\n", sense->flags); @@ -847,8 +850,7 @@ tape_3590_unsolicited_irq(struct tape_device *device, struct irb *irb) tape_3590_schedule_work(device, TO_READ_ATTMSG); } else { DBF_EVENT(3, "unsol.irq! dev end: %08x\n", device->cdev_id); - PRINT_WARN("Unsolicited IRQ (Device End) caught.\n"); - tape_dump_sense(device, NULL, irb); + tape_dump_sense_dbf(device, NULL, irb); } /* check medium state */ tape_3590_schedule_work(device, TO_MSEN); @@ -876,8 +878,6 @@ tape_3590_erp_basic(struct tape_device *device, struct tape_request *request, case SENSE_BRA_DRE: return tape_3590_erp_failed(device, request, irb, rc); default: - PRINT_ERR("Unknown BRA %x - This should not happen!\n", - sense->bra); BUG(); return TAPE_IO_STOP; } @@ -910,7 +910,7 @@ tape_3590_erp_swap(struct tape_device *device, struct tape_request *request, * should proceed with the new tape... this * should probably be done in user space! */ - PRINT_WARN("(%s): Swap Tape Device!\n", dev_name(&device->cdev->dev)); + dev_warn (&device->cdev->dev, "Swap Tape Device!"); return tape_3590_erp_basic(device, request, irb, -EIO); } @@ -985,8 +985,6 @@ tape_3590_erp_read_opposite(struct tape_device *device, return tape_3590_erp_failed(device, request, irb, -EIO); break; default: - PRINT_WARN("read_opposite_recovery_called_with_op: %s\n", - tape_op_verbose[request->op]); return tape_3590_erp_failed(device, request, irb, -EIO); } } @@ -998,50 +996,61 @@ static void tape_3590_print_mim_msg_f0(struct tape_device *device, struct irb *irb) { struct tape_3590_sense *sense; + char *exception, *service; + + exception = kmalloc(BUFSIZE, GFP_ATOMIC); + service = kmalloc(BUFSIZE, GFP_ATOMIC); + + if (!exception || !service) + goto out_nomem; sense = (struct tape_3590_sense *) irb->ecw; /* Exception Message */ switch (sense->fmt.f70.emc) { case 0x02: - PRINT_WARN("(%s): Data degraded\n", - dev_name(&device->cdev->dev)); + snprintf(exception, BUFSIZE, "Data degraded"); break; case 0x03: - PRINT_WARN("(%s): Data degraded in partion %i\n", - dev_name(&device->cdev->dev), sense->fmt.f70.mp); + snprintf(exception, BUFSIZE, "Data degraded in partion %i", + sense->fmt.f70.mp); break; case 0x04: - PRINT_WARN("(%s): Medium degraded\n", - dev_name(&device->cdev->dev)); + snprintf(exception, BUFSIZE, "Medium degraded"); break; case 0x05: - PRINT_WARN("(%s): Medium degraded in partition %i\n", - dev_name(&device->cdev->dev), sense->fmt.f70.mp); + snprintf(exception, BUFSIZE, "Medium degraded in partition %i", + sense->fmt.f70.mp); break; case 0x06: - PRINT_WARN("(%s): Block 0 Error\n", - dev_name(&device->cdev->dev)); + snprintf(exception, BUFSIZE, "Block 0 Error"); break; case 0x07: - PRINT_WARN("(%s): Medium Exception 0x%02x\n", - dev_name(&device->cdev->dev), sense->fmt.f70.md); + snprintf(exception, BUFSIZE, "Medium Exception 0x%02x", + sense->fmt.f70.md); break; default: - PRINT_WARN("(%s): MIM ExMsg: 0x%02x\n", - dev_name(&device->cdev->dev), sense->fmt.f70.emc); + snprintf(exception, BUFSIZE, "0x%02x", + sense->fmt.f70.emc); break; } /* Service Message */ switch (sense->fmt.f70.smc) { case 0x02: - PRINT_WARN("(%s): Reference Media maintenance procedure %i\n", - dev_name(&device->cdev->dev), sense->fmt.f70.md); + snprintf(service, BUFSIZE, "Reference Media maintenance " + "procedure %i", sense->fmt.f70.md); break; default: - PRINT_WARN("(%s): MIM ServiceMsg: 0x%02x\n", - dev_name(&device->cdev->dev), sense->fmt.f70.smc); + snprintf(service, BUFSIZE, "0x%02x", + sense->fmt.f70.smc); break; } + + dev_warn (&device->cdev->dev, "Media information: exception %s, " + "service %s", exception, service); + +out_nomem: + kfree(exception); + kfree(service); } /* @@ -1051,108 +1060,108 @@ static void tape_3590_print_io_sim_msg_f1(struct tape_device *device, struct irb *irb) { struct tape_3590_sense *sense; + char *exception, *service; + + exception = kmalloc(BUFSIZE, GFP_ATOMIC); + service = kmalloc(BUFSIZE, GFP_ATOMIC); + + if (!exception || !service) + goto out_nomem; sense = (struct tape_3590_sense *) irb->ecw; /* Exception Message */ switch (sense->fmt.f71.emc) { case 0x01: - PRINT_WARN("(%s): Effect of failure is unknown\n", - dev_name(&device->cdev->dev)); + snprintf(exception, BUFSIZE, "Effect of failure is unknown"); break; case 0x02: - PRINT_WARN("(%s): CU Exception - no performance impact\n", - dev_name(&device->cdev->dev)); + snprintf(exception, BUFSIZE, "CU Exception - no performance " + "impact"); break; case 0x03: - PRINT_WARN("(%s): CU Exception on channel interface 0x%02x\n", - dev_name(&device->cdev->dev), sense->fmt.f71.md[0]); + snprintf(exception, BUFSIZE, "CU Exception on channel " + "interface 0x%02x", sense->fmt.f71.md[0]); break; case 0x04: - PRINT_WARN("(%s): CU Exception on device path 0x%02x\n", - dev_name(&device->cdev->dev), sense->fmt.f71.md[0]); + snprintf(exception, BUFSIZE, "CU Exception on device path " + "0x%02x", sense->fmt.f71.md[0]); break; case 0x05: - PRINT_WARN("(%s): CU Exception on library path 0x%02x\n", - dev_name(&device->cdev->dev), sense->fmt.f71.md[0]); + snprintf(exception, BUFSIZE, "CU Exception on library path " + "0x%02x", sense->fmt.f71.md[0]); break; case 0x06: - PRINT_WARN("(%s): CU Exception on node 0x%02x\n", - dev_name(&device->cdev->dev), sense->fmt.f71.md[0]); + snprintf(exception, BUFSIZE, "CU Exception on node 0x%02x", + sense->fmt.f71.md[0]); break; case 0x07: - PRINT_WARN("(%s): CU Exception on partition 0x%02x\n", - dev_name(&device->cdev->dev), sense->fmt.f71.md[0]); + snprintf(exception, BUFSIZE, "CU Exception on partition " + "0x%02x", sense->fmt.f71.md[0]); break; default: - PRINT_WARN("(%s): SIM ExMsg: 0x%02x\n", - dev_name(&device->cdev->dev), sense->fmt.f71.emc); + snprintf(exception, BUFSIZE, "0x%02x", + sense->fmt.f71.emc); } /* Service Message */ switch (sense->fmt.f71.smc) { case 0x01: - PRINT_WARN("(%s): Repair impact is unknown\n", - dev_name(&device->cdev->dev)); + snprintf(service, BUFSIZE, "Repair impact is unknown"); break; case 0x02: - PRINT_WARN("(%s): Repair will not impact cu performance\n", - dev_name(&device->cdev->dev)); + snprintf(service, BUFSIZE, "Repair will not impact cu " + "performance"); break; case 0x03: if (sense->fmt.f71.mdf == 0) - PRINT_WARN("(%s): Repair will disable node " - "0x%x on CU\n", - dev_name(&device->cdev->dev), - sense->fmt.f71.md[1]); + snprintf(service, BUFSIZE, "Repair will disable node " + "0x%x on CU", sense->fmt.f71.md[1]); else - PRINT_WARN("(%s): Repair will disable nodes " - "(0x%x-0x%x) on CU\n", - dev_name(&device->cdev->dev), - sense->fmt.f71.md[1], sense->fmt.f71.md[2]); + snprintf(service, BUFSIZE, "Repair will disable " + "nodes (0x%x-0x%x) on CU", sense->fmt.f71.md[1], + sense->fmt.f71.md[2]); break; case 0x04: if (sense->fmt.f71.mdf == 0) - PRINT_WARN("(%s): Repair will disable cannel path " - "0x%x on CU\n", - dev_name(&device->cdev->dev), - sense->fmt.f71.md[1]); + snprintf(service, BUFSIZE, "Repair will disable " + "channel path 0x%x on CU", + sense->fmt.f71.md[1]); else - PRINT_WARN("(%s): Repair will disable cannel paths " - "(0x%x-0x%x) on CU\n", - dev_name(&device->cdev->dev), - sense->fmt.f71.md[1], sense->fmt.f71.md[2]); + snprintf(service, BUFSIZE, "Repair will disable cannel" + " paths (0x%x-0x%x) on CU", + sense->fmt.f71.md[1], sense->fmt.f71.md[2]); break; case 0x05: if (sense->fmt.f71.mdf == 0) - PRINT_WARN("(%s): Repair will disable device path " - "0x%x on CU\n", - dev_name(&device->cdev->dev), - sense->fmt.f71.md[1]); + snprintf(service, BUFSIZE, "Repair will disable device" + " path 0x%x on CU", sense->fmt.f71.md[1]); else - PRINT_WARN("(%s): Repair will disable device paths " - "(0x%x-0x%x) on CU\n", - dev_name(&device->cdev->dev), - sense->fmt.f71.md[1], sense->fmt.f71.md[2]); + snprintf(service, BUFSIZE, "Repair will disable device" + " paths (0x%x-0x%x) on CU", + sense->fmt.f71.md[1], sense->fmt.f71.md[2]); break; case 0x06: if (sense->fmt.f71.mdf == 0) - PRINT_WARN("(%s): Repair will disable library path " - "0x%x on CU\n", - dev_name(&device->cdev->dev), - sense->fmt.f71.md[1]); + snprintf(service, BUFSIZE, "Repair will disable " + "library path 0x%x on CU", + sense->fmt.f71.md[1]); else - PRINT_WARN("(%s): Repair will disable library paths " - "(0x%x-0x%x) on CU\n", - dev_name(&device->cdev->dev), - sense->fmt.f71.md[1], sense->fmt.f71.md[2]); + snprintf(service, BUFSIZE, "Repair will disable " + "library paths (0x%x-0x%x) on CU", + sense->fmt.f71.md[1], sense->fmt.f71.md[2]); break; case 0x07: - PRINT_WARN("(%s): Repair will disable access to CU\n", - dev_name(&device->cdev->dev)); + snprintf(service, BUFSIZE, "Repair will disable access to CU"); break; default: - PRINT_WARN("(%s): SIM ServiceMsg: 0x%02x\n", - dev_name(&device->cdev->dev), sense->fmt.f71.smc); + snprintf(service, BUFSIZE, "0x%02x", + sense->fmt.f71.smc); } + + dev_warn (&device->cdev->dev, "I/O subsystem information: exception" + " %s, service %s", exception, service); +out_nomem: + kfree(exception); + kfree(service); } /* @@ -1162,111 +1171,109 @@ static void tape_3590_print_dev_sim_msg_f2(struct tape_device *device, struct irb *irb) { struct tape_3590_sense *sense; + char *exception, *service; + + exception = kmalloc(BUFSIZE, GFP_ATOMIC); + service = kmalloc(BUFSIZE, GFP_ATOMIC); + + if (!exception || !service) + goto out_nomem; sense = (struct tape_3590_sense *) irb->ecw; /* Exception Message */ switch (sense->fmt.f71.emc) { case 0x01: - PRINT_WARN("(%s): Effect of failure is unknown\n", - dev_name(&device->cdev->dev)); + snprintf(exception, BUFSIZE, "Effect of failure is unknown"); break; case 0x02: - PRINT_WARN("(%s): DV Exception - no performance impact\n", - dev_name(&device->cdev->dev)); + snprintf(exception, BUFSIZE, "DV Exception - no performance" + " impact"); break; case 0x03: - PRINT_WARN("(%s): DV Exception on channel interface 0x%02x\n", - dev_name(&device->cdev->dev), sense->fmt.f71.md[0]); + snprintf(exception, BUFSIZE, "DV Exception on channel " + "interface 0x%02x", sense->fmt.f71.md[0]); break; case 0x04: - PRINT_WARN("(%s): DV Exception on loader 0x%02x\n", - dev_name(&device->cdev->dev), sense->fmt.f71.md[0]); + snprintf(exception, BUFSIZE, "DV Exception on loader 0x%02x", + sense->fmt.f71.md[0]); break; case 0x05: - PRINT_WARN("(%s): DV Exception on message display 0x%02x\n", - dev_name(&device->cdev->dev), sense->fmt.f71.md[0]); + snprintf(exception, BUFSIZE, "DV Exception on message display" + " 0x%02x", sense->fmt.f71.md[0]); break; case 0x06: - PRINT_WARN("(%s): DV Exception in tape path\n", - dev_name(&device->cdev->dev)); + snprintf(exception, BUFSIZE, "DV Exception in tape path"); break; case 0x07: - PRINT_WARN("(%s): DV Exception in drive\n", - dev_name(&device->cdev->dev)); + snprintf(exception, BUFSIZE, "DV Exception in drive"); break; default: - PRINT_WARN("(%s): DSIM ExMsg: 0x%02x\n", - dev_name(&device->cdev->dev), sense->fmt.f71.emc); + snprintf(exception, BUFSIZE, "0x%02x", + sense->fmt.f71.emc); } /* Service Message */ switch (sense->fmt.f71.smc) { case 0x01: - PRINT_WARN("(%s): Repair impact is unknown\n", - dev_name(&device->cdev->dev)); + snprintf(service, BUFSIZE, "Repair impact is unknown"); break; case 0x02: - PRINT_WARN("(%s): Repair will not impact device performance\n", - dev_name(&device->cdev->dev)); + snprintf(service, BUFSIZE, "Repair will not impact device " + "performance"); break; case 0x03: if (sense->fmt.f71.mdf == 0) - PRINT_WARN("(%s): Repair will disable channel path " - "0x%x on DV\n", - dev_name(&device->cdev->dev), - sense->fmt.f71.md[1]); + snprintf(service, BUFSIZE, "Repair will disable " + "channel path 0x%x on DV", + sense->fmt.f71.md[1]); else - PRINT_WARN("(%s): Repair will disable channel path " - "(0x%x-0x%x) on DV\n", - dev_name(&device->cdev->dev), - sense->fmt.f71.md[1], sense->fmt.f71.md[2]); + snprintf(service, BUFSIZE, "Repair will disable " + "channel path (0x%x-0x%x) on DV", + sense->fmt.f71.md[1], sense->fmt.f71.md[2]); break; case 0x04: if (sense->fmt.f71.mdf == 0) - PRINT_WARN("(%s): Repair will disable interface 0x%x " - "on DV\n", - dev_name(&device->cdev->dev), - sense->fmt.f71.md[1]); + snprintf(service, BUFSIZE, "Repair will disable " + "interface 0x%x on DV", sense->fmt.f71.md[1]); else - PRINT_WARN("(%s): Repair will disable interfaces " - "(0x%x-0x%x) on DV\n", - dev_name(&device->cdev->dev), - sense->fmt.f71.md[1], sense->fmt.f71.md[2]); + snprintf(service, BUFSIZE, "Repair will disable " + "interfaces (0x%x-0x%x) on DV", + sense->fmt.f71.md[1], sense->fmt.f71.md[2]); break; case 0x05: if (sense->fmt.f71.mdf == 0) - PRINT_WARN("(%s): Repair will disable loader 0x%x " - "on DV\n", - dev_name(&device->cdev->dev), - sense->fmt.f71.md[1]); + snprintf(service, BUFSIZE, "Repair will disable loader" + " 0x%x on DV", sense->fmt.f71.md[1]); else - PRINT_WARN("(%s): Repair will disable loader " - "(0x%x-0x%x) on DV\n", - dev_name(&device->cdev->dev), - sense->fmt.f71.md[1], sense->fmt.f71.md[2]); + snprintf(service, BUFSIZE, "Repair will disable loader" + " (0x%x-0x%x) on DV", + sense->fmt.f71.md[1], sense->fmt.f71.md[2]); break; case 0x07: - PRINT_WARN("(%s): Repair will disable access to DV\n", - dev_name(&device->cdev->dev)); + snprintf(service, BUFSIZE, "Repair will disable access to DV"); break; case 0x08: if (sense->fmt.f71.mdf == 0) - PRINT_WARN("(%s): Repair will disable message " - "display 0x%x on DV\n", - dev_name(&device->cdev->dev), - sense->fmt.f71.md[1]); + snprintf(service, BUFSIZE, "Repair will disable " + "message display 0x%x on DV", + sense->fmt.f71.md[1]); else - PRINT_WARN("(%s): Repair will disable message " - "displays (0x%x-0x%x) on DV\n", - dev_name(&device->cdev->dev), - sense->fmt.f71.md[1], sense->fmt.f71.md[2]); + snprintf(service, BUFSIZE, "Repair will disable " + "message displays (0x%x-0x%x) on DV", + sense->fmt.f71.md[1], sense->fmt.f71.md[2]); break; case 0x09: - PRINT_WARN("(%s): Clean DV\n", dev_name(&device->cdev->dev)); + snprintf(service, BUFSIZE, "Clean DV"); break; default: - PRINT_WARN("(%s): DSIM ServiceMsg: 0x%02x\n", - dev_name(&device->cdev->dev), sense->fmt.f71.smc); + snprintf(service, BUFSIZE, "0x%02x", + sense->fmt.f71.smc); } + + dev_warn (&device->cdev->dev, "Device subsystem information: exception" + " %s, service %s", exception, service); +out_nomem: + kfree(exception); + kfree(service); } /* @@ -1282,46 +1289,42 @@ tape_3590_print_era_msg(struct tape_device *device, struct irb *irb) return; if ((sense->mc > 0) && (sense->mc < TAPE_3590_MAX_MSG)) { if (tape_3590_msg[sense->mc] != NULL) - PRINT_WARN("(%s): %s\n", dev_name(&device->cdev->dev), - tape_3590_msg[sense->mc]); - else { - PRINT_WARN("(%s): Message Code 0x%x\n", - dev_name(&device->cdev->dev), sense->mc); - } + dev_warn (&device->cdev->dev, "Tape sense message: %s", + tape_3590_msg[sense->mc]); + else + dev_warn (&device->cdev->dev, "Tape sense message code" + " 0x%x", sense->mc); return; } if (sense->mc == 0xf0) { /* Standard Media Information Message */ - PRINT_WARN("(%s): MIM SEV=%i, MC=%02x, ES=%x/%x, " - "RC=%02x-%04x-%02x\n", dev_name(&device->cdev->dev), - sense->fmt.f70.sev, sense->mc, - sense->fmt.f70.emc, sense->fmt.f70.smc, - sense->fmt.f70.refcode, sense->fmt.f70.mid, - sense->fmt.f70.fid); + dev_warn (&device->cdev->dev, "MIM SEV=%i, MC=%02x, ES=%x/%x, " + "RC=%02x-%04x-%02x", sense->fmt.f70.sev, sense->mc, + sense->fmt.f70.emc, sense->fmt.f70.smc, + sense->fmt.f70.refcode, sense->fmt.f70.mid, + sense->fmt.f70.fid); tape_3590_print_mim_msg_f0(device, irb); return; } if (sense->mc == 0xf1) { /* Standard I/O Subsystem Service Information Message */ - PRINT_WARN("(%s): IOSIM SEV=%i, DEVTYPE=3590/%02x, " - "MC=%02x, ES=%x/%x, REF=0x%04x-0x%04x-0x%04x\n", - dev_name(&device->cdev->dev), sense->fmt.f71.sev, - device->cdev->id.dev_model, - sense->mc, sense->fmt.f71.emc, - sense->fmt.f71.smc, sense->fmt.f71.refcode1, - sense->fmt.f71.refcode2, sense->fmt.f71.refcode3); + dev_warn (&device->cdev->dev, "IOSIM SEV=%i, DEVTYPE=3590/%02x," + " MC=%02x, ES=%x/%x, REF=0x%04x-0x%04x-0x%04x", + sense->fmt.f71.sev, device->cdev->id.dev_model, + sense->mc, sense->fmt.f71.emc, sense->fmt.f71.smc, + sense->fmt.f71.refcode1, sense->fmt.f71.refcode2, + sense->fmt.f71.refcode3); tape_3590_print_io_sim_msg_f1(device, irb); return; } if (sense->mc == 0xf2) { /* Standard Device Service Information Message */ - PRINT_WARN("(%s): DEVSIM SEV=%i, DEVTYPE=3590/%02x, " - "MC=%02x, ES=%x/%x, REF=0x%04x-0x%04x-0x%04x\n", - dev_name(&device->cdev->dev), sense->fmt.f71.sev, - device->cdev->id.dev_model, - sense->mc, sense->fmt.f71.emc, - sense->fmt.f71.smc, sense->fmt.f71.refcode1, - sense->fmt.f71.refcode2, sense->fmt.f71.refcode3); + dev_warn (&device->cdev->dev, "DEVSIM SEV=%i, DEVTYPE=3590/%02x" + ", MC=%02x, ES=%x/%x, REF=0x%04x-0x%04x-0x%04x", + sense->fmt.f71.sev, device->cdev->id.dev_model, + sense->mc, sense->fmt.f71.emc, sense->fmt.f71.smc, + sense->fmt.f71.refcode1, sense->fmt.f71.refcode2, + sense->fmt.f71.refcode3); tape_3590_print_dev_sim_msg_f2(device, irb); return; } @@ -1329,8 +1332,7 @@ tape_3590_print_era_msg(struct tape_device *device, struct irb *irb) /* Standard Library Service Information Message */ return; } - PRINT_WARN("(%s): Device Message(%x)\n", - dev_name(&device->cdev->dev), sense->mc); + dev_warn (&device->cdev->dev, "Device Message(%x)", sense->mc); } static int tape_3590_crypt_error(struct tape_device *device, @@ -1355,9 +1357,7 @@ static int tape_3590_crypt_error(struct tape_device *device, /* No connection to EKM */ return tape_3590_erp_basic(device, request, irb, -ENOTCONN); - PRINT_ERR("(%s): Unable to get encryption key from EKM\n", bus_id); - PRINT_ERR("(%s): CU=%02X DRIVE=%06X EKM=%02X:%04X\n", bus_id, cu_rc, - drv_rc, ekm_rc1, ekm_rc2); + dev_err (&device->cdev->dev, "Unable to get encryption key from EKM"); return tape_3590_erp_basic(device, request, irb, -ENOKEY); } @@ -1443,8 +1443,6 @@ tape_3590_unit_check(struct tape_device *device, struct tape_request *request, * print additional msg since default msg * "device intervention" is not very meaningfull */ - PRINT_WARN("(%s): Tape operation when medium not loaded\n", - dev_name(&device->cdev->dev)); tape_med_state_set(device, MS_UNLOADED); tape_3590_schedule_work(device, TO_CRYPT_OFF); return tape_3590_erp_basic(device, request, irb, -ENOMEDIUM); @@ -1490,19 +1488,14 @@ tape_3590_unit_check(struct tape_device *device, struct tape_request *request, return tape_3590_erp_basic(device, request, irb, -ENOMEDIUM); case 0x6020: - PRINT_WARN("(%s): Cartridge of wrong type ?\n", - dev_name(&device->cdev->dev)); return tape_3590_erp_basic(device, request, irb, -EMEDIUMTYPE); case 0x8011: - PRINT_WARN("(%s): Another host has reserved the tape device\n", - dev_name(&device->cdev->dev)); return tape_3590_erp_basic(device, request, irb, -EPERM); case 0x8013: - PRINT_WARN("(%s): Another host has privileged access to the " - "tape device\n", dev_name(&device->cdev->dev)); - PRINT_WARN("(%s): To solve the problem unload the current " - "cartridge!\n", dev_name(&device->cdev->dev)); + dev_warn (&device->cdev->dev, "Another host has privileged " + "access to the tape device, To solve the problem " + "unload the current cartridge!"); return tape_3590_erp_basic(device, request, irb, -EPERM); default: return tape_3590_erp_basic(device, request, irb, -EIO); @@ -1552,9 +1545,7 @@ tape_3590_irq(struct tape_device *device, struct tape_request *request, } DBF_EVENT(6, "xunknownirq\n"); - PRINT_ERR("Unexpected interrupt.\n"); - PRINT_ERR("Current op is: %s", tape_op_verbose[request->op]); - tape_dump_sense(device, request, irb); + tape_dump_sense_dbf(device, request, irb); return TAPE_IO_STOP; } @@ -1609,7 +1600,6 @@ tape_3590_setup_device(struct tape_device *device) if (rc) goto fail_rdc_data; if (rdc_data->data[31] == 0x13) { - PRINT_INFO("Device has crypto support\n"); data->crypt_info.capability |= TAPE390_CRYPT_SUPPORTED_MASK; tape_3592_disable_crypt(device); } else { diff --git a/drivers/s390/char/tape_block.c b/drivers/s390/char/tape_block.c index ae18baf59f06..feb23be5ce53 100644 --- a/drivers/s390/char/tape_block.c +++ b/drivers/s390/char/tape_block.c @@ -10,6 +10,8 @@ * Stefan Bader <shbader@de.ibm.com> */ +#define KMSG_COMPONENT "tape_block" + #include <linux/fs.h> #include <linux/module.h> #include <linux/blkdev.h> @@ -23,8 +25,6 @@ #include "tape.h" -#define PRINTK_HEADER "TAPE_BLOCK: " - #define TAPEBLOCK_MAX_SEC 100 #define TAPEBLOCK_MIN_REQUEUE 3 @@ -279,8 +279,6 @@ tapeblock_cleanup_device(struct tape_device *device) tape_put_device(device); if (!device->blk_data.disk) { - PRINT_ERR("(%s): No gendisk to clean up!\n", - dev_name(&device->cdev->dev)); goto cleanup_queue; } @@ -314,7 +312,7 @@ tapeblock_revalidate_disk(struct gendisk *disk) if (!device->blk_data.medium_changed) return 0; - PRINT_INFO("Detecting media size...\n"); + dev_info(&device->cdev->dev, "Detecting media size..."); rc = tape_mtop(device, MTFSFM, 1); if (rc) return rc; @@ -341,7 +339,8 @@ tapeblock_revalidate_disk(struct gendisk *disk) device->bof = rc; nr_of_blks -= rc; - PRINT_INFO("Found %i blocks on media\n", nr_of_blks); + dev_info(&device->cdev->dev, "Found %i blocks on media", + nr_of_blks); set_capacity(device->blk_data.disk, nr_of_blks*(TAPEBLOCK_HSEC_SIZE/512)); @@ -376,8 +375,8 @@ tapeblock_open(struct block_device *bdev, fmode_t mode) if (device->required_tapemarks) { DBF_EVENT(2, "TBLOCK: missing tapemarks\n"); - PRINT_ERR("TBLOCK: Refusing to open tape with missing" - " end of file marks.\n"); + dev_warn(&device->cdev->dev, "Refusing to open tape with" + " missing end of file marks."); rc = -EPERM; goto put_device; } @@ -452,7 +451,6 @@ tapeblock_ioctl( rc = -EINVAL; break; default: - PRINT_WARN("invalid ioctl 0x%x\n", command); rc = -EINVAL; } @@ -474,7 +472,6 @@ tapeblock_init(void) if (tapeblock_major == 0) tapeblock_major = rc; - PRINT_INFO("tape gets major %d for block device\n", tapeblock_major); return 0; } diff --git a/drivers/s390/char/tape_char.c b/drivers/s390/char/tape_char.c index be0ce2215c8d..31566c55adfe 100644 --- a/drivers/s390/char/tape_char.c +++ b/drivers/s390/char/tape_char.c @@ -24,8 +24,6 @@ #include "tape_std.h" #include "tape_class.h" -#define PRINTK_HEADER "TAPE_CHAR: " - #define TAPECHAR_MAJOR 0 /* get dynamic major */ /* @@ -102,8 +100,6 @@ tapechar_check_idalbuffer(struct tape_device *device, size_t block_size) if (block_size > MAX_BLOCKSIZE) { DBF_EVENT(3, "Invalid blocksize (%zd > %d)\n", block_size, MAX_BLOCKSIZE); - PRINT_ERR("Invalid blocksize (%zd> %d)\n", - block_size, MAX_BLOCKSIZE); return -EINVAL; } @@ -485,7 +481,6 @@ tapechar_init (void) return -1; tapechar_major = MAJOR(dev); - PRINT_INFO("tape gets major %d for character devices\n", MAJOR(dev)); return 0; } @@ -496,7 +491,5 @@ tapechar_init (void) void tapechar_exit(void) { - PRINT_INFO("tape releases major %d for character devices\n", - tapechar_major); unregister_chrdev_region(MKDEV(tapechar_major, 0), 256); } diff --git a/drivers/s390/char/tape_core.c b/drivers/s390/char/tape_core.c index f9bb51fa7f5b..e00a6a61bba8 100644 --- a/drivers/s390/char/tape_core.c +++ b/drivers/s390/char/tape_core.c @@ -11,6 +11,7 @@ * Stefan Bader <shbader@de.ibm.com> */ +#define KMSG_COMPONENT "tape" #include <linux/module.h> #include <linux/init.h> // for kernel parameters #include <linux/kmod.h> // for requesting modules @@ -25,7 +26,6 @@ #include "tape.h" #include "tape_std.h" -#define PRINTK_HEADER "TAPE_CORE: " #define LONG_BUSY_TIMEOUT 180 /* seconds */ static void __tape_do_irq (struct ccw_device *, unsigned long, struct irb *); @@ -214,13 +214,11 @@ tape_med_state_set(struct tape_device *device, enum tape_medium_state newstate) switch(newstate){ case MS_UNLOADED: device->tape_generic_status |= GMT_DR_OPEN(~0); - PRINT_INFO("(%s): Tape is unloaded\n", - dev_name(&device->cdev->dev)); + dev_info(&device->cdev->dev, "Tape is unloaded"); break; case MS_LOADED: device->tape_generic_status &= ~GMT_DR_OPEN(~0); - PRINT_INFO("(%s): Tape has been mounted\n", - dev_name(&device->cdev->dev)); + dev_info(&device->cdev->dev, "Tape has been mounted"); break; default: // print nothing @@ -333,7 +331,6 @@ tape_generic_online(struct tape_device *device, /* Let the discipline have a go at the device. */ device->discipline = discipline; if (!try_module_get(discipline->owner)) { - PRINT_ERR("Cannot get module. Module gone.\n"); return -EINVAL; } @@ -391,7 +388,6 @@ int tape_generic_offline(struct tape_device *device) { if (!device) { - PRINT_ERR("tape_generic_offline: no such device\n"); return -ENODEV; } @@ -413,9 +409,6 @@ tape_generic_offline(struct tape_device *device) DBF_EVENT(3, "(%08x): Set offline failed " "- drive in use.\n", device->cdev_id); - PRINT_WARN("(%s): Set offline failed " - "- drive in use.\n", - dev_name(&device->cdev->dev)); spin_unlock_irq(get_ccwdev_lock(device->cdev)); return -EBUSY; } @@ -435,14 +428,11 @@ tape_alloc_device(void) device = kzalloc(sizeof(struct tape_device), GFP_KERNEL); if (device == NULL) { DBF_EXCEPTION(2, "ti:no mem\n"); - PRINT_INFO ("can't allocate memory for " - "tape info structure\n"); return ERR_PTR(-ENOMEM); } device->modeset_byte = kmalloc(1, GFP_KERNEL | GFP_DMA); if (device->modeset_byte == NULL) { DBF_EXCEPTION(2, "ti:no mem\n"); - PRINT_INFO("can't allocate memory for modeset byte\n"); kfree(device); return ERR_PTR(-ENOMEM); } @@ -490,7 +480,6 @@ tape_put_device(struct tape_device *device) } else { if (remain < 0) { DBF_EVENT(4, "put device without reference\n"); - PRINT_ERR("put device without reference\n"); } else { DBF_EVENT(4, "tape_free_device(%p)\n", device); kfree(device->modeset_byte); @@ -538,8 +527,6 @@ tape_generic_probe(struct ccw_device *cdev) ret = sysfs_create_group(&cdev->dev.kobj, &tape_attr_group); if (ret) { tape_put_device(device); - PRINT_ERR("probe failed for tape device %s\n", - dev_name(&cdev->dev)); return ret; } cdev->dev.driver_data = device; @@ -547,7 +534,6 @@ tape_generic_probe(struct ccw_device *cdev) device->cdev = cdev; ccw_device_get_id(cdev, &dev_id); device->cdev_id = devid_to_int(&dev_id); - PRINT_INFO("tape device %s found\n", dev_name(&cdev->dev)); return ret; } @@ -584,7 +570,6 @@ tape_generic_remove(struct ccw_device *cdev) device = cdev->dev.driver_data; if (!device) { - PRINT_ERR("No device pointer in tape_generic_remove!\n"); return; } DBF_LH(3, "(%08x): tape_generic_remove(%p)\n", device->cdev_id, cdev); @@ -615,10 +600,8 @@ tape_generic_remove(struct ccw_device *cdev) */ DBF_EVENT(3, "(%08x): Drive in use vanished!\n", device->cdev_id); - PRINT_WARN("(%s): Drive in use vanished - " - "expect trouble!\n", - dev_name(&device->cdev->dev)); - PRINT_WARN("State was %i\n", device->tape_state); + dev_warn(&device->cdev->dev, "Drive in use vanished - " + "expect trouble!"); tape_state_set(device, TS_NOT_OPER); __tape_discard_requests(device); spin_unlock_irq(get_ccwdev_lock(device->cdev)); @@ -830,30 +813,6 @@ __tape_end_request( } /* - * Write sense data to console/dbf - */ -void -tape_dump_sense(struct tape_device* device, struct tape_request *request, - struct irb *irb) -{ - unsigned int *sptr; - - PRINT_INFO("-------------------------------------------------\n"); - PRINT_INFO("DSTAT : %02x CSTAT: %02x CPA: %04x\n", - irb->scsw.cmd.dstat, irb->scsw.cmd.cstat, irb->scsw.cmd.cpa); - PRINT_INFO("DEVICE: %s\n", dev_name(&device->cdev->dev)); - if (request != NULL) - PRINT_INFO("OP : %s\n", tape_op_verbose[request->op]); - - sptr = (unsigned int *) irb->ecw; - PRINT_INFO("Sense data: %08X %08X %08X %08X \n", - sptr[0], sptr[1], sptr[2], sptr[3]); - PRINT_INFO("Sense data: %08X %08X %08X %08X \n", - sptr[4], sptr[5], sptr[6], sptr[7]); - PRINT_INFO("--------------------------------------------------\n"); -} - -/* * Write sense data to dbf */ void @@ -1051,8 +1010,6 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) device = (struct tape_device *) cdev->dev.driver_data; if (device == NULL) { - PRINT_ERR("could not get device structure for %s " - "in interrupt\n", dev_name(&cdev->dev)); return; } request = (struct tape_request *) intparm; @@ -1064,13 +1021,13 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) /* FIXME: What to do with the request? */ switch (PTR_ERR(irb)) { case -ETIMEDOUT: - PRINT_WARN("(%s): Request timed out\n", + DBF_LH(1, "(%s): Request timed out\n", dev_name(&cdev->dev)); case -EIO: __tape_end_request(device, request, -EIO); break; default: - PRINT_ERR("(%s): Unexpected i/o error %li\n", + DBF_LH(1, "(%s): Unexpected i/o error %li\n", dev_name(&cdev->dev), PTR_ERR(irb)); } @@ -1182,8 +1139,6 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) default: if (rc > 0) { DBF_EVENT(6, "xunknownrc\n"); - PRINT_ERR("Invalid return code from discipline " - "interrupt function.\n"); __tape_end_request(device, request, -EIO); } else { __tape_end_request(device, request, rc); @@ -1323,7 +1278,6 @@ EXPORT_SYMBOL(tape_state_set); EXPORT_SYMBOL(tape_med_state_set); EXPORT_SYMBOL(tape_alloc_request); EXPORT_SYMBOL(tape_free_request); -EXPORT_SYMBOL(tape_dump_sense); EXPORT_SYMBOL(tape_dump_sense_dbf); EXPORT_SYMBOL(tape_do_io); EXPORT_SYMBOL(tape_do_io_async); diff --git a/drivers/s390/char/tape_proc.c b/drivers/s390/char/tape_proc.c index 8a376af926a7..202f42132939 100644 --- a/drivers/s390/char/tape_proc.c +++ b/drivers/s390/char/tape_proc.c @@ -20,8 +20,6 @@ #include "tape.h" -#define PRINTK_HEADER "TAPE_PROC: " - static const char *tape_med_st_verbose[MS_SIZE] = { [MS_UNKNOWN] = "UNKNOWN ", @@ -128,7 +126,6 @@ tape_proc_init(void) proc_create("tapedevices", S_IFREG | S_IRUGO | S_IWUSR, NULL, &tape_proc_ops); if (tape_proc_devices == NULL) { - PRINT_WARN("tape: Cannot register procfs entry tapedevices\n"); return; } } diff --git a/drivers/s390/char/tape_std.c b/drivers/s390/char/tape_std.c index 5bd573d144d6..44dd0f80c847 100644 --- a/drivers/s390/char/tape_std.c +++ b/drivers/s390/char/tape_std.c @@ -26,8 +26,6 @@ #include "tape.h" #include "tape_std.h" -#define PRINTK_HEADER "TAPE_STD: " - /* * tape_std_assign */ @@ -46,9 +44,8 @@ tape_std_assign_timeout(unsigned long data) device->cdev_id); rc = tape_cancel_io(device, request); if(rc) - PRINT_ERR("(%s): Assign timeout: Cancel failed with rc = %i\n", + DBF_EVENT(3, "(%s): Assign timeout: Cancel failed with rc = %i\n", dev_name(&device->cdev->dev), rc); - } int @@ -82,8 +79,6 @@ tape_std_assign(struct tape_device *device) del_timer(&timeout); if (rc != 0) { - PRINT_WARN("%s: assign failed - device might be busy\n", - dev_name(&device->cdev->dev)); DBF_EVENT(3, "%08x: assign failed - device might be busy\n", device->cdev_id); } else { @@ -105,8 +100,6 @@ tape_std_unassign (struct tape_device *device) if (device->tape_state == TS_NOT_OPER) { DBF_EVENT(3, "(%08x): Can't unassign device\n", device->cdev_id); - PRINT_WARN("(%s): Can't unassign device - device gone\n", - dev_name(&device->cdev->dev)); return -EIO; } @@ -120,8 +113,6 @@ tape_std_unassign (struct tape_device *device) if ((rc = tape_do_io(device, request)) != 0) { DBF_EVENT(3, "%08x: Unassign failed\n", device->cdev_id); - PRINT_WARN("%s: Unassign failed\n", - dev_name(&device->cdev->dev)); } else { DBF_EVENT(3, "%08x: Tape unassigned\n", device->cdev_id); } @@ -242,8 +233,6 @@ tape_std_mtsetblk(struct tape_device *device, int count) if (count > MAX_BLOCKSIZE) { DBF_EVENT(3, "Invalid block size (%d > %d) given.\n", count, MAX_BLOCKSIZE); - PRINT_ERR("Invalid block size (%d > %d) given.\n", - count, MAX_BLOCKSIZE); return -EINVAL; } @@ -633,14 +622,6 @@ tape_std_mtcompression(struct tape_device *device, int mt_count) if (mt_count < 0 || mt_count > 1) { DBF_EXCEPTION(6, "xcom parm\n"); - if (*device->modeset_byte & 0x08) - PRINT_INFO("(%s) Compression is currently on\n", - dev_name(&device->cdev->dev)); - else - PRINT_INFO("(%s) Compression is currently off\n", - dev_name(&device->cdev->dev)); - PRINT_INFO("Use 1 to switch compression on, 0 to " - "switch it off\n"); return -EINVAL; } request = tape_alloc_request(2, 0); diff --git a/drivers/s390/char/vmcp.c b/drivers/s390/char/vmcp.c index 09e7d9bf438b..a6087cec55b4 100644 --- a/drivers/s390/char/vmcp.c +++ b/drivers/s390/char/vmcp.c @@ -11,12 +11,14 @@ * The idea of this driver is based on cpint from Neale Ferguson and #CP in CMS */ +#define KMSG_COMPONENT "vmcp" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include <linux/fs.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/miscdevice.h> #include <linux/module.h> -#include <linux/smp_lock.h> #include <asm/cpcmd.h> #include <asm/debug.h> #include <asm/uaccess.h> @@ -26,8 +28,6 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Christian Borntraeger <borntraeger@de.ibm.com>"); MODULE_DESCRIPTION("z/VM CP interface"); -#define PRINTK_HEADER "vmcp: " - static debug_info_t *vmcp_debug; static int vmcp_open(struct inode *inode, struct file *file) @@ -41,13 +41,11 @@ static int vmcp_open(struct inode *inode, struct file *file) if (!session) return -ENOMEM; - lock_kernel(); session->bufsize = PAGE_SIZE; session->response = NULL; session->resp_size = 0; mutex_init(&session->mutex); file->private_data = session; - unlock_kernel(); return nonseekable_open(inode, file); } @@ -193,7 +191,8 @@ static int __init vmcp_init(void) int ret; if (!MACHINE_IS_VM) { - PRINT_WARN("z/VM CP interface is only available under z/VM\n"); + pr_warning("The z/VM CP interface device driver cannot be " + "loaded without z/VM\n"); return -ENODEV; } diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c index 24762727bc27..aabbeb909cc6 100644 --- a/drivers/s390/char/vmlogrdr.c +++ b/drivers/s390/char/vmlogrdr.c @@ -10,6 +10,10 @@ * Stefan Weinhuber <wein@de.ibm.com> * */ + +#define KMSG_COMPONENT "vmlogrdr" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include <linux/module.h> #include <linux/init.h> #include <linux/errno.h> @@ -28,8 +32,6 @@ #include <linux/smp_lock.h> #include <linux/string.h> - - MODULE_AUTHOR ("(C) 2004 IBM Corporation by Xenia Tkatschow (xenia@us.ibm.com)\n" " Stefan Weinhuber (wein@de.ibm.com)"); @@ -174,8 +176,7 @@ static void vmlogrdr_iucv_path_severed(struct iucv_path *path, u8 ipuser[16]) struct vmlogrdr_priv_t * logptr = path->private; u8 reason = (u8) ipuser[8]; - printk (KERN_ERR "vmlogrdr: connection severed with" - " reason %i\n", reason); + pr_err("vmlogrdr: connection severed with reason %i\n", reason); iucv_path_sever(path, NULL); kfree(path); @@ -333,8 +334,8 @@ static int vmlogrdr_open (struct inode *inode, struct file *filp) if (logptr->autorecording) { ret = vmlogrdr_recording(logptr,1,logptr->autopurge); if (ret) - printk (KERN_WARNING "vmlogrdr: failed to start " - "recording automatically\n"); + pr_warning("vmlogrdr: failed to start " + "recording automatically\n"); } /* create connection to the system service */ @@ -345,9 +346,9 @@ static int vmlogrdr_open (struct inode *inode, struct file *filp) logptr->system_service, NULL, NULL, logptr); if (connect_rc) { - printk (KERN_ERR "vmlogrdr: iucv connection to %s " - "failed with rc %i \n", logptr->system_service, - connect_rc); + pr_err("vmlogrdr: iucv connection to %s " + "failed with rc %i \n", + logptr->system_service, connect_rc); goto out_path; } @@ -388,8 +389,8 @@ static int vmlogrdr_release (struct inode *inode, struct file *filp) if (logptr->autorecording) { ret = vmlogrdr_recording(logptr,0,logptr->autopurge); if (ret) - printk (KERN_WARNING "vmlogrdr: failed to stop " - "recording automatically\n"); + pr_warning("vmlogrdr: failed to stop " + "recording automatically\n"); } logptr->dev_in_use = 0; @@ -823,8 +824,7 @@ static int __init vmlogrdr_init(void) dev_t dev; if (! MACHINE_IS_VM) { - printk (KERN_ERR "vmlogrdr: not running under VM, " - "driver not loaded.\n"); + pr_err("not running under VM, driver not loaded.\n"); return -ENODEV; } diff --git a/drivers/s390/char/vmur.c b/drivers/s390/char/vmur.c index 9020eba620ee..5dcef81fc9d9 100644 --- a/drivers/s390/char/vmur.c +++ b/drivers/s390/char/vmur.c @@ -8,6 +8,9 @@ * Frank Munzert <munzert@de.ibm.com> */ +#define KMSG_COMPONENT "vmur" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include <linux/cdev.h> #include <linux/smp_lock.h> @@ -40,8 +43,6 @@ MODULE_AUTHOR("IBM Corporation"); MODULE_DESCRIPTION("s390 z/VM virtual unit record device driver"); MODULE_LICENSE("GPL"); -#define PRINTK_HEADER "vmur: " - static dev_t ur_first_dev_maj_min; static struct class *vmur_class; static struct debug_info *vmur_dbf; @@ -987,7 +988,8 @@ static int __init ur_init(void) dev_t dev; if (!MACHINE_IS_VM) { - PRINT_ERR("%s is only available under z/VM.\n", ur_banner); + pr_err("The %s cannot be loaded without z/VM\n", + ur_banner); return -ENODEV; } @@ -1006,7 +1008,8 @@ static int __init ur_init(void) rc = alloc_chrdev_region(&dev, 0, NUM_MINORS, "vmur"); if (rc) { - PRINT_ERR("alloc_chrdev_region failed: err = %d\n", rc); + pr_err("Kernel function alloc_chrdev_region failed with " + "error code %d\n", rc); goto fail_unregister_driver; } ur_first_dev_maj_min = MKDEV(MAJOR(dev), 0); @@ -1016,7 +1019,7 @@ static int __init ur_init(void) rc = PTR_ERR(vmur_class); goto fail_unregister_region; } - PRINT_INFO("%s loaded.\n", ur_banner); + pr_info("%s loaded.\n", ur_banner); return 0; fail_unregister_region: @@ -1034,7 +1037,7 @@ static void __exit ur_exit(void) unregister_chrdev_region(ur_first_dev_maj_min, NUM_MINORS); ccw_driver_unregister(&ur_driver); debug_unregister(vmur_dbf); - PRINT_INFO("%s unloaded.\n", ur_banner); + pr_info("%s unloaded.\n", ur_banner); } module_init(ur_init); diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c index 7fd84be11931..eefc6611412e 100644 --- a/drivers/s390/char/zcore.c +++ b/drivers/s390/char/zcore.c @@ -9,6 +9,9 @@ * Author(s): Michael Holzheu */ +#define KMSG_COMPONENT "zdump" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include <linux/init.h> #include <linux/miscdevice.h> #include <linux/utsname.h> @@ -24,8 +27,6 @@ #include "sclp.h" #define TRACE(x...) debug_sprintf_event(zcore_dbf, 1, x) -#define MSG(x...) printk( KERN_ALERT x ) -#define ERROR_MSG(x...) printk ( KERN_ALERT "DUMP: " x ) #define TO_USER 0 #define TO_KERNEL 1 @@ -563,19 +564,19 @@ static int __init sys_info_init(enum arch_id arch) switch (arch) { case ARCH_S390X: - MSG("DETECTED 'S390X (64 bit) OS'\n"); + pr_alert("DETECTED 'S390X (64 bit) OS'\n"); sys_info.sa_base = SAVE_AREA_BASE_S390X; sys_info.sa_size = sizeof(struct save_area_s390x); set_s390x_lc_mask(&sys_info.lc_mask); break; case ARCH_S390: - MSG("DETECTED 'S390 (32 bit) OS'\n"); + pr_alert("DETECTED 'S390 (32 bit) OS'\n"); sys_info.sa_base = SAVE_AREA_BASE_S390; sys_info.sa_size = sizeof(struct save_area_s390); set_s390_lc_mask(&sys_info.lc_mask); break; default: - ERROR_MSG("unknown architecture 0x%x.\n",arch); + pr_alert("0x%x is an unknown architecture.\n",arch); return -EINVAL; } sys_info.arch = arch; @@ -674,7 +675,8 @@ static int __init zcore_init(void) #ifndef __s390x__ if (arch == ARCH_S390X) { - ERROR_MSG("32 bit dumper can't dump 64 bit system!\n"); + pr_alert("The 32-bit dump tool cannot be used for a " + "64-bit system\n"); rc = -EINVAL; goto fail; } diff --git a/drivers/s390/cio/blacklist.c b/drivers/s390/cio/blacklist.c index 2f547b840ef0..fe00be3675cd 100644 --- a/drivers/s390/cio/blacklist.c +++ b/drivers/s390/cio/blacklist.c @@ -9,6 +9,9 @@ * Arnd Bergmann (arndb@de.ibm.com) */ +#define KMSG_COMPONENT "cio" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include <linux/init.h> #include <linux/vmalloc.h> #include <linux/slab.h> @@ -50,9 +53,10 @@ static int blacklist_range(range_action action, unsigned int from_ssid, { if ((from_ssid > to_ssid) || ((from_ssid == to_ssid) && (from > to))) { if (msgtrigger) - printk(KERN_WARNING "cio: Invalid cio_ignore range " - "0.%x.%04x-0.%x.%04x\n", from_ssid, from, - to_ssid, to); + pr_warning("0.%x.%04x to 0.%x.%04x is not a valid " + "range for cio_ignore\n", from_ssid, from, + to_ssid, to); + return 1; } @@ -140,8 +144,8 @@ static int parse_busid(char *str, unsigned int *cssid, unsigned int *ssid, rc = 0; out: if (rc && msgtrigger) - printk(KERN_WARNING "cio: Invalid cio_ignore device '%s'\n", - str); + pr_warning("%s is not a valid device for the cio_ignore " + "kernel parameter\n", str); return rc; } diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c index 3ac2c2019f5e..17fa009d9959 100644 --- a/drivers/s390/cio/ccwgroup.c +++ b/drivers/s390/cio/ccwgroup.c @@ -19,6 +19,8 @@ #include <asm/ccwdev.h> #include <asm/ccwgroup.h> +#define CCW_BUS_ID_SIZE 20 + /* In Linux 2.4, we had a channel device layer called "chandev" * that did all sorts of obscure stuff for networking devices. * This is another driver that serves as a replacement for just @@ -172,7 +174,7 @@ static int __get_next_bus_id(const char **buf, char *bus_id) len = end - start + 1; end++; } - if (len < BUS_ID_SIZE) { + if (len < CCW_BUS_ID_SIZE) { strlcpy(bus_id, start, len); rc = 0; } else @@ -181,7 +183,7 @@ static int __get_next_bus_id(const char **buf, char *bus_id) return rc; } -static int __is_valid_bus_id(char bus_id[BUS_ID_SIZE]) +static int __is_valid_bus_id(char bus_id[CCW_BUS_ID_SIZE]) { int cssid, ssid, devno; @@ -213,7 +215,7 @@ int ccwgroup_create_from_string(struct device *root, unsigned int creator_id, { struct ccwgroup_device *gdev; int rc, i; - char tmp_bus_id[BUS_ID_SIZE]; + char tmp_bus_id[CCW_BUS_ID_SIZE]; const char *curr_buf; gdev = kzalloc(sizeof(*gdev) + num_devices * sizeof(gdev->cdev[0]), diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 29826fdd47b8..ebab6ea4659b 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -8,6 +8,9 @@ * Arnd Bergmann (arndb@de.ibm.com) */ +#define KMSG_COMPONENT "cio" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include <linux/module.h> #include <linux/slab.h> #include <linux/init.h> @@ -333,6 +336,7 @@ static void chsc_process_sei_chp_config(struct chsc_sei_area *sei_area) struct chp_config_data *data; struct chp_id chpid; int num; + char *events[3] = {"configure", "deconfigure", "cancel deconfigure"}; CIO_CRW_EVENT(4, "chsc: channel-path-configuration notification\n"); if (sei_area->rs != 0) @@ -343,8 +347,8 @@ static void chsc_process_sei_chp_config(struct chsc_sei_area *sei_area) if (!chp_test_bit(data->map, num)) continue; chpid.id = num; - printk(KERN_WARNING "cio: processing configure event %d for " - "chpid %x.%02x\n", data->op, chpid.cssid, chpid.id); + pr_notice("Processing %s for channel path %x.%02x\n", + events[data->op], chpid.cssid, chpid.id); switch (data->op) { case 0: chp_cfg_schedule(chpid, 1); diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index 3db2c386546f..36af2636d910 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -9,6 +9,9 @@ * Martin Schwidefsky (schwidefsky@de.ibm.com) */ +#define KMSG_COMPONENT "cio" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include <linux/module.h> #include <linux/init.h> #include <linux/slab.h> @@ -104,44 +107,6 @@ cio_get_options (struct subchannel *sch) return flags; } -/* - * Use tpi to get a pending interrupt, call the interrupt handler and - * return a pointer to the subchannel structure. - */ -static int -cio_tpi(void) -{ - struct tpi_info *tpi_info; - struct subchannel *sch; - struct irb *irb; - int irq_context; - - tpi_info = (struct tpi_info *) __LC_SUBCHANNEL_ID; - if (tpi (NULL) != 1) - return 0; - irb = (struct irb *) __LC_IRB; - /* Store interrupt response block to lowcore. */ - if (tsch (tpi_info->schid, irb) != 0) - /* Not status pending or not operational. */ - return 1; - sch = (struct subchannel *)(unsigned long)tpi_info->intparm; - if (!sch) - return 1; - irq_context = in_interrupt(); - if (!irq_context) - local_bh_disable(); - irq_enter (); - spin_lock(sch->lock); - memcpy(&sch->schib.scsw, &irb->scsw, sizeof(union scsw)); - if (sch->driver && sch->driver->irq) - sch->driver->irq(sch); - spin_unlock(sch->lock); - irq_exit (); - if (!irq_context) - _local_bh_enable(); - return 1; -} - static int cio_start_handle_notoper(struct subchannel *sch, __u8 lpm) { @@ -687,6 +652,43 @@ static char console_sch_name[10] = "0.x.xxxx"; static struct io_subchannel_private console_priv; static int console_subchannel_in_use; +/* + * Use tpi to get a pending interrupt, call the interrupt handler and + * return a pointer to the subchannel structure. + */ +static int cio_tpi(void) +{ + struct tpi_info *tpi_info; + struct subchannel *sch; + struct irb *irb; + int irq_context; + + tpi_info = (struct tpi_info *) __LC_SUBCHANNEL_ID; + if (tpi(NULL) != 1) + return 0; + irb = (struct irb *) __LC_IRB; + /* Store interrupt response block to lowcore. */ + if (tsch(tpi_info->schid, irb) != 0) + /* Not status pending or not operational. */ + return 1; + sch = (struct subchannel *)(unsigned long)tpi_info->intparm; + if (!sch) + return 1; + irq_context = in_interrupt(); + if (!irq_context) + local_bh_disable(); + irq_enter(); + spin_lock(sch->lock); + memcpy(&sch->schib.scsw, &irb->scsw, sizeof(union scsw)); + if (sch->driver && sch->driver->irq) + sch->driver->irq(sch); + spin_unlock(sch->lock); + irq_exit(); + if (!irq_context) + _local_bh_enable(); + return 1; +} + void *cio_get_console_priv(void) { return &console_priv; @@ -780,7 +782,7 @@ cio_probe_console(void) sch_no = cio_get_console_sch_no(); if (sch_no == -1) { console_subchannel_in_use = 0; - printk(KERN_WARNING "cio: No ccw console found!\n"); + pr_warning("No CCW console was found\n"); return ERR_PTR(-ENODEV); } memset(&console_subchannel, 0, sizeof(struct subchannel)); diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c index a90b28c0be57..12643a815848 100644 --- a/drivers/s390/cio/cmf.c +++ b/drivers/s390/cio/cmf.c @@ -25,6 +25,9 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#define KMSG_COMPONENT "cio" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include <linux/bootmem.h> #include <linux/device.h> #include <linux/init.h> @@ -1359,9 +1362,8 @@ static int __init init_cmf(void) default: return 1; } - - printk(KERN_INFO "cio: Channel measurement facility using %s " - "format (%s)\n", format_string, detect_string); + pr_info("Channel measurement facility initialized using format " + "%s (mode %s)\n", format_string, detect_string); return 0; } diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 76bbb1e74c29..d744aa1981db 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -6,6 +6,10 @@ * Author(s): Arnd Bergmann (arndb@de.ibm.com) * Cornelia Huck (cornelia.huck@de.ibm.com) */ + +#define KMSG_COMPONENT "cio" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include <linux/module.h> #include <linux/init.h> #include <linux/device.h> @@ -844,8 +848,8 @@ out: s390_unregister_crw_handler(CRW_RSC_CSS); chsc_free_sei_area(); kfree(slow_subchannel_set); - printk(KERN_WARNING"cio: failed to initialize css driver (%d)!\n", - ret); + pr_alert("The CSS device driver initialization failed with " + "errno=%d\n", ret); return ret; } diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 4e78c82194b4..fc0c0abd1271 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -874,11 +874,15 @@ void ccw_device_move_to_orphanage(struct work_struct *work) replacing_cdev = get_disc_ccwdev_by_dev_id(&dev_id, cdev); if (replacing_cdev) { sch_attach_disconnected_device(sch, replacing_cdev); + /* Release reference from get_disc_ccwdev_by_dev_id() */ + put_device(&cdev->dev); return; } replacing_cdev = get_orphaned_ccwdev_by_dev_id(css, &dev_id); if (replacing_cdev) { sch_attach_orphaned_device(sch, replacing_cdev); + /* Release reference from get_orphaned_ccwdev_by_dev_id() */ + put_device(&cdev->dev); return; } sch_create_and_recog_new_device(sch); @@ -1175,12 +1179,6 @@ static int io_subchannel_probe(struct subchannel *sch) return 0; } io_subchannel_init_fields(sch); - /* - * First check if a fitting device may be found amongst the - * disconnected devices or in the orphanage. - */ - dev_id.devno = sch->schib.pmcw.dev; - dev_id.ssid = sch->schid.ssid; rc = sysfs_create_group(&sch->dev.kobj, &io_subchannel_attr_group); if (rc) @@ -1192,6 +1190,12 @@ static int io_subchannel_probe(struct subchannel *sch) rc = -ENOMEM; goto out_err; } + /* + * First check if a fitting device may be found amongst the + * disconnected devices or in the orphanage. + */ + dev_id.devno = sch->schib.pmcw.dev; + dev_id.ssid = sch->schid.ssid; cdev = get_disc_ccwdev_by_dev_id(&dev_id, NULL); if (!cdev) cdev = get_orphaned_ccwdev_by_dev_id(to_css(sch->dev.parent), @@ -1719,7 +1723,7 @@ __ccwdev_check_busid(struct device *dev, void *id) bus_id = id; - return (strncmp(bus_id, dev_name(dev), BUS_ID_SIZE) == 0); + return (strcmp(bus_id, dev_name(dev)) == 0); } diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h index e3ea1d5f2810..c60f2566d28c 100644 --- a/drivers/s390/cio/qdio.h +++ b/drivers/s390/cio/qdio.h @@ -10,6 +10,7 @@ #include <asm/page.h> #include <asm/schid.h> +#include <asm/debug.h> #include "chsc.h" #define QDIO_BUSY_BIT_PATIENCE 100 /* 100 microseconds */ @@ -111,12 +112,12 @@ static inline int do_sqbs(u64 token, unsigned char state, int queue, } static inline int do_eqbs(u64 token, unsigned char *state, int queue, - int *start, int *count) + int *start, int *count, int ack) { register unsigned long _ccq asm ("0") = *count; register unsigned long _token asm ("1") = token; unsigned long _queuestart = ((unsigned long)queue << 32) | *start; - unsigned long _state = 0; + unsigned long _state = (unsigned long)ack << 63; asm volatile( " .insn rrf,0xB99c0000,%1,%2,0,0" @@ -133,7 +134,7 @@ static inline int do_eqbs(u64 token, unsigned char *state, int queue, static inline int do_sqbs(u64 token, unsigned char state, int queue, int *start, int *count) { return 0; } static inline int do_eqbs(u64 token, unsigned char *state, int queue, - int *start, int *count) { return 0; } + int *start, int *count, int ack) { return 0; } #endif /* CONFIG_64BIT */ struct qdio_irq; @@ -186,11 +187,11 @@ struct qdio_input_q { /* input buffer acknowledgement flag */ int polling; + /* how much sbals are acknowledged with qebsm */ + int ack_count; + /* last time of noticing incoming data */ u64 timestamp; - - /* lock for clearing the acknowledgement */ - spinlock_t lock; }; struct qdio_output_q { @@ -300,11 +301,13 @@ struct qdio_irq { struct qdio_q *input_qs[QDIO_MAX_QUEUES_PER_IRQ]; struct qdio_q *output_qs[QDIO_MAX_QUEUES_PER_IRQ]; + debug_info_t *debug_area; struct mutex setup_mutex; }; /* helper functions */ #define queue_type(q) q->irq_ptr->qib.qfmt +#define SCH_NO(q) (q->irq_ptr->schid.sch_no) #define is_thinint_irq(irq) \ (irq->qib.qfmt == QDIO_IQDIO_QFMT || \ @@ -348,10 +351,13 @@ static inline unsigned long long get_usecs(void) ((bufnr + 1) & QDIO_MAX_BUFFERS_MASK) #define add_buf(bufnr, inc) \ ((bufnr + inc) & QDIO_MAX_BUFFERS_MASK) +#define sub_buf(bufnr, dec) \ + ((bufnr - dec) & QDIO_MAX_BUFFERS_MASK) /* prototypes for thin interrupt */ void qdio_sync_after_thinint(struct qdio_q *q); -int get_buf_state(struct qdio_q *q, unsigned int bufnr, unsigned char *state); +int get_buf_state(struct qdio_q *q, unsigned int bufnr, unsigned char *state, + int auto_ack); void qdio_check_outbound_after_thinint(struct qdio_q *q); int qdio_inbound_q_moved(struct qdio_q *q); void qdio_kick_inbound_handler(struct qdio_q *q); @@ -378,10 +384,15 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm, int qdio_allocate_qs(struct qdio_irq *irq_ptr, int nr_input_qs, int nr_output_qs); void qdio_setup_ssqd_info(struct qdio_irq *irq_ptr); +int qdio_setup_get_ssqd(struct qdio_irq *irq_ptr, + struct subchannel_id *schid, + struct qdio_ssqd_desc *data); int qdio_setup_irq(struct qdio_initialize *init_data); void qdio_print_subchannel_info(struct qdio_irq *irq_ptr, struct ccw_device *cdev); void qdio_release_memory(struct qdio_irq *irq_ptr); +int qdio_setup_create_sysfs(struct ccw_device *cdev); +void qdio_setup_destroy_sysfs(struct ccw_device *cdev); int qdio_setup_init(void); void qdio_setup_exit(void); diff --git a/drivers/s390/cio/qdio_debug.c b/drivers/s390/cio/qdio_debug.c index f05590355be8..f8a3b6967f69 100644 --- a/drivers/s390/cio/qdio_debug.c +++ b/drivers/s390/cio/qdio_debug.c @@ -14,7 +14,7 @@ #include "qdio.h" debug_info_t *qdio_dbf_setup; -debug_info_t *qdio_dbf_trace; +debug_info_t *qdio_dbf_error; static struct dentry *debugfs_root; #define MAX_DEBUGFS_QUEUES 32 @@ -22,59 +22,33 @@ static struct dentry *debugfs_queues[MAX_DEBUGFS_QUEUES] = { NULL }; static DEFINE_MUTEX(debugfs_mutex); #define QDIO_DEBUGFS_NAME_LEN 40 -void qdio_allocate_do_dbf(struct qdio_initialize *init_data) +void qdio_allocate_dbf(struct qdio_initialize *init_data, + struct qdio_irq *irq_ptr) { - char dbf_text[20]; - - sprintf(dbf_text, "qfmt:%x", init_data->q_format); - QDIO_DBF_TEXT0(0, setup, dbf_text); - QDIO_DBF_HEX0(0, setup, init_data->adapter_name, 8); - sprintf(dbf_text, "qpff%4x", init_data->qib_param_field_format); - QDIO_DBF_TEXT0(0, setup, dbf_text); - QDIO_DBF_HEX0(0, setup, &init_data->qib_param_field, sizeof(void *)); - QDIO_DBF_HEX0(0, setup, &init_data->input_slib_elements, sizeof(void *)); - QDIO_DBF_HEX0(0, setup, &init_data->output_slib_elements, sizeof(void *)); - sprintf(dbf_text, "niq:%4x", init_data->no_input_qs); - QDIO_DBF_TEXT0(0, setup, dbf_text); - sprintf(dbf_text, "noq:%4x", init_data->no_output_qs); - QDIO_DBF_TEXT0(0, setup, dbf_text); - QDIO_DBF_HEX0(0, setup, &init_data->input_handler, sizeof(void *)); - QDIO_DBF_HEX0(0, setup, &init_data->output_handler, sizeof(void *)); - QDIO_DBF_HEX0(0, setup, &init_data->int_parm, sizeof(long)); - QDIO_DBF_HEX0(0, setup, &init_data->flags, sizeof(long)); - QDIO_DBF_HEX0(0, setup, &init_data->input_sbal_addr_array, sizeof(void *)); - QDIO_DBF_HEX0(0, setup, &init_data->output_sbal_addr_array, sizeof(void *)); -} - -static void qdio_unregister_dbf_views(void) -{ - if (qdio_dbf_setup) - debug_unregister(qdio_dbf_setup); - if (qdio_dbf_trace) - debug_unregister(qdio_dbf_trace); -} - -static int qdio_register_dbf_views(void) -{ - qdio_dbf_setup = debug_register("qdio_setup", QDIO_DBF_SETUP_PAGES, - QDIO_DBF_SETUP_NR_AREAS, - QDIO_DBF_SETUP_LEN); - if (!qdio_dbf_setup) - goto oom; - debug_register_view(qdio_dbf_setup, &debug_hex_ascii_view); - debug_set_level(qdio_dbf_setup, QDIO_DBF_SETUP_LEVEL); - - qdio_dbf_trace = debug_register("qdio_trace", QDIO_DBF_TRACE_PAGES, - QDIO_DBF_TRACE_NR_AREAS, - QDIO_DBF_TRACE_LEN); - if (!qdio_dbf_trace) - goto oom; - debug_register_view(qdio_dbf_trace, &debug_hex_ascii_view); - debug_set_level(qdio_dbf_trace, QDIO_DBF_TRACE_LEVEL); - return 0; -oom: - qdio_unregister_dbf_views(); - return -ENOMEM; + char text[20]; + + DBF_EVENT("qfmt:%1d", init_data->q_format); + DBF_HEX(init_data->adapter_name, 8); + DBF_EVENT("qpff%4x", init_data->qib_param_field_format); + DBF_HEX(&init_data->qib_param_field, sizeof(void *)); + DBF_HEX(&init_data->input_slib_elements, sizeof(void *)); + DBF_HEX(&init_data->output_slib_elements, sizeof(void *)); + DBF_EVENT("niq:%1d noq:%1d", init_data->no_input_qs, + init_data->no_output_qs); + DBF_HEX(&init_data->input_handler, sizeof(void *)); + DBF_HEX(&init_data->output_handler, sizeof(void *)); + DBF_HEX(&init_data->int_parm, sizeof(long)); + DBF_HEX(&init_data->flags, sizeof(long)); + DBF_HEX(&init_data->input_sbal_addr_array, sizeof(void *)); + DBF_HEX(&init_data->output_sbal_addr_array, sizeof(void *)); + DBF_EVENT("irq:%8lx", (unsigned long)irq_ptr); + + /* allocate trace view for the interface */ + snprintf(text, 20, "qdio_%s", dev_name(&init_data->cdev->dev)); + irq_ptr->debug_area = debug_register(text, 2, 1, 16); + debug_register_view(irq_ptr->debug_area, &debug_hex_ascii_view); + debug_set_level(irq_ptr->debug_area, DBF_WARN); + DBF_DEV_EVENT(DBF_ERR, irq_ptr, "dbf created"); } static int qstat_show(struct seq_file *m, void *v) @@ -86,16 +60,18 @@ static int qstat_show(struct seq_file *m, void *v) if (!q) return 0; - seq_printf(m, "device state indicator: %d\n", *q->irq_ptr->dsci); + seq_printf(m, "device state indicator: %d\n", *(u32 *)q->irq_ptr->dsci); seq_printf(m, "nr_used: %d\n", atomic_read(&q->nr_buf_used)); seq_printf(m, "ftc: %d\n", q->first_to_check); seq_printf(m, "last_move_ftc: %d\n", q->last_move_ftc); seq_printf(m, "polling: %d\n", q->u.in.polling); + seq_printf(m, "ack count: %d\n", q->u.in.ack_count); seq_printf(m, "slsb buffer states:\n"); + seq_printf(m, "|0 |8 |16 |24 |32 |40 |48 |56 63|\n"); qdio_siga_sync_q(q); for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; i++) { - get_buf_state(q, i, &state); + get_buf_state(q, i, &state, 0); switch (state) { case SLSB_P_INPUT_NOT_INIT: case SLSB_P_OUTPUT_NOT_INIT: @@ -127,6 +103,7 @@ static int qstat_show(struct seq_file *m, void *v) seq_printf(m, "\n"); } seq_printf(m, "\n"); + seq_printf(m, "|64 |72 |80 |88 |96 |104 |112 | 127|\n"); return 0; } @@ -223,11 +200,24 @@ void qdio_shutdown_debug_entries(struct qdio_irq *irq_ptr, struct ccw_device *cd int __init qdio_debug_init(void) { debugfs_root = debugfs_create_dir("qdio_queues", NULL); - return qdio_register_dbf_views(); + + qdio_dbf_setup = debug_register("qdio_setup", 16, 1, 16); + debug_register_view(qdio_dbf_setup, &debug_hex_ascii_view); + debug_set_level(qdio_dbf_setup, DBF_INFO); + DBF_EVENT("dbf created\n"); + + qdio_dbf_error = debug_register("qdio_error", 4, 1, 16); + debug_register_view(qdio_dbf_error, &debug_hex_ascii_view); + debug_set_level(qdio_dbf_error, DBF_INFO); + DBF_ERROR("dbf created\n"); + return 0; } void qdio_debug_exit(void) { debugfs_remove(debugfs_root); - qdio_unregister_dbf_views(); + if (qdio_dbf_setup) + debug_unregister(qdio_dbf_setup); + if (qdio_dbf_error) + debug_unregister(qdio_dbf_error); } diff --git a/drivers/s390/cio/qdio_debug.h b/drivers/s390/cio/qdio_debug.h index 5a4d85b829ad..5d70bd162ae9 100644 --- a/drivers/s390/cio/qdio_debug.h +++ b/drivers/s390/cio/qdio_debug.h @@ -12,80 +12,72 @@ #include <asm/qdio.h> #include "qdio.h" -#define QDIO_DBF_HEX(ex, name, level, addr, len) \ +/* that gives us 15 characters in the text event views */ +#define QDIO_DBF_LEN 16 + +extern debug_info_t *qdio_dbf_setup; +extern debug_info_t *qdio_dbf_error; + +/* sort out low debug levels early to avoid wasted sprints */ +static inline int qdio_dbf_passes(debug_info_t *dbf_grp, int level) +{ + return (level <= dbf_grp->level); +} + +#define DBF_ERR 3 /* error conditions */ +#define DBF_WARN 4 /* warning conditions */ +#define DBF_INFO 6 /* informational */ + +#undef DBF_EVENT +#undef DBF_ERROR +#undef DBF_DEV_EVENT + +#define DBF_EVENT(text...) \ do { \ - if (ex) \ - debug_exception(qdio_dbf_##name, level, (void *)(addr), len); \ - else \ - debug_event(qdio_dbf_##name, level, (void *)(addr), len); \ + char debug_buffer[QDIO_DBF_LEN]; \ + snprintf(debug_buffer, QDIO_DBF_LEN, text); \ + debug_text_event(qdio_dbf_setup, DBF_ERR, debug_buffer); \ } while (0) -#define QDIO_DBF_TEXT(ex, name, level, text) \ + +#define DBF_HEX(addr, len) \ do { \ - if (ex) \ - debug_text_exception(qdio_dbf_##name, level, text); \ - else \ - debug_text_event(qdio_dbf_##name, level, text); \ + debug_event(qdio_dbf_setup, DBF_ERR, (void*)(addr), len); \ } while (0) -#define QDIO_DBF_HEX0(ex, name, addr, len) QDIO_DBF_HEX(ex, name, 0, addr, len) -#define QDIO_DBF_HEX1(ex, name, addr, len) QDIO_DBF_HEX(ex, name, 1, addr, len) -#define QDIO_DBF_HEX2(ex, name, addr, len) QDIO_DBF_HEX(ex, name, 2, addr, len) - -#ifdef CONFIG_QDIO_DEBUG -#define QDIO_DBF_HEX3(ex, name, addr, len) QDIO_DBF_HEX(ex, name, 3, addr, len) -#define QDIO_DBF_HEX4(ex, name, addr, len) QDIO_DBF_HEX(ex, name, 4, addr, len) -#define QDIO_DBF_HEX5(ex, name, addr, len) QDIO_DBF_HEX(ex, name, 5, addr, len) -#define QDIO_DBF_HEX6(ex, name, addr, len) QDIO_DBF_HEX(ex, name, 6, addr, len) -#else -#define QDIO_DBF_HEX3(ex, name, addr, len) do {} while (0) -#define QDIO_DBF_HEX4(ex, name, addr, len) do {} while (0) -#define QDIO_DBF_HEX5(ex, name, addr, len) do {} while (0) -#define QDIO_DBF_HEX6(ex, name, addr, len) do {} while (0) -#endif /* CONFIG_QDIO_DEBUG */ - -#define QDIO_DBF_TEXT0(ex, name, text) QDIO_DBF_TEXT(ex, name, 0, text) -#define QDIO_DBF_TEXT1(ex, name, text) QDIO_DBF_TEXT(ex, name, 1, text) -#define QDIO_DBF_TEXT2(ex, name, text) QDIO_DBF_TEXT(ex, name, 2, text) - -#ifdef CONFIG_QDIO_DEBUG -#define QDIO_DBF_TEXT3(ex, name, text) QDIO_DBF_TEXT(ex, name, 3, text) -#define QDIO_DBF_TEXT4(ex, name, text) QDIO_DBF_TEXT(ex, name, 4, text) -#define QDIO_DBF_TEXT5(ex, name, text) QDIO_DBF_TEXT(ex, name, 5, text) -#define QDIO_DBF_TEXT6(ex, name, text) QDIO_DBF_TEXT(ex, name, 6, text) -#else -#define QDIO_DBF_TEXT3(ex, name, text) do {} while (0) -#define QDIO_DBF_TEXT4(ex, name, text) do {} while (0) -#define QDIO_DBF_TEXT5(ex, name, text) do {} while (0) -#define QDIO_DBF_TEXT6(ex, name, text) do {} while (0) -#endif /* CONFIG_QDIO_DEBUG */ +#define DBF_ERROR(text...) \ + do { \ + char debug_buffer[QDIO_DBF_LEN]; \ + snprintf(debug_buffer, QDIO_DBF_LEN, text); \ + debug_text_event(qdio_dbf_error, DBF_ERR, debug_buffer); \ + } while (0) -/* s390dbf views */ -#define QDIO_DBF_SETUP_LEN 8 -#define QDIO_DBF_SETUP_PAGES 8 -#define QDIO_DBF_SETUP_NR_AREAS 1 +#define DBF_ERROR_HEX(addr, len) \ + do { \ + debug_event(qdio_dbf_error, DBF_ERR, (void*)(addr), len); \ + } while (0) -#define QDIO_DBF_TRACE_LEN 8 -#define QDIO_DBF_TRACE_NR_AREAS 2 -#ifdef CONFIG_QDIO_DEBUG -#define QDIO_DBF_TRACE_PAGES 32 -#define QDIO_DBF_SETUP_LEVEL 6 -#define QDIO_DBF_TRACE_LEVEL 4 -#else /* !CONFIG_QDIO_DEBUG */ -#define QDIO_DBF_TRACE_PAGES 8 -#define QDIO_DBF_SETUP_LEVEL 2 -#define QDIO_DBF_TRACE_LEVEL 2 -#endif /* CONFIG_QDIO_DEBUG */ +#define DBF_DEV_EVENT(level, device, text...) \ + do { \ + char debug_buffer[QDIO_DBF_LEN]; \ + if (qdio_dbf_passes(device->debug_area, level)) { \ + snprintf(debug_buffer, QDIO_DBF_LEN, text); \ + debug_text_event(device->debug_area, level, debug_buffer); \ + } \ + } while (0) -extern debug_info_t *qdio_dbf_setup; -extern debug_info_t *qdio_dbf_trace; +#define DBF_DEV_HEX(level, device, addr, len) \ + do { \ + debug_event(device->debug_area, level, (void*)(addr), len); \ + } while (0) -void qdio_allocate_do_dbf(struct qdio_initialize *init_data); -void debug_print_bstat(struct qdio_q *q); +void qdio_allocate_dbf(struct qdio_initialize *init_data, + struct qdio_irq *irq_ptr); void qdio_setup_debug_entries(struct qdio_irq *irq_ptr, struct ccw_device *cdev); void qdio_shutdown_debug_entries(struct qdio_irq *irq_ptr, struct ccw_device *cdev); int qdio_debug_init(void); void qdio_debug_exit(void); + #endif diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 7c8659151993..584f8498e3b3 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -95,8 +95,6 @@ static inline int do_siga_output(unsigned long schid, unsigned long mask, static inline int qdio_check_ccq(struct qdio_q *q, unsigned int ccq) { - char dbf_text[15]; - /* all done or next buffer state different */ if (ccq == 0 || ccq == 32) return 0; @@ -104,8 +102,7 @@ static inline int qdio_check_ccq(struct qdio_q *q, unsigned int ccq) if (ccq == 96 || ccq == 97) return 1; /* notify devices immediately */ - sprintf(dbf_text, "%d", ccq); - QDIO_DBF_TEXT2(1, trace, dbf_text); + DBF_ERROR("%4x ccq:%3d", SCH_NO(q), ccq); return -EIO; } @@ -115,41 +112,45 @@ static inline int qdio_check_ccq(struct qdio_q *q, unsigned int ccq) * @state: state of the extracted buffers * @start: buffer number to start at * @count: count of buffers to examine + * @auto_ack: automatically acknowledge buffers * * Returns the number of successfull extracted equal buffer states. * Stops processing if a state is different from the last buffers state. */ static int qdio_do_eqbs(struct qdio_q *q, unsigned char *state, - int start, int count) + int start, int count, int auto_ack) { unsigned int ccq = 0; int tmp_count = count, tmp_start = start; int nr = q->nr; int rc; - char dbf_text[15]; BUG_ON(!q->irq_ptr->sch_token); + qdio_perf_stat_inc(&perf_stats.debug_eqbs_all); if (!q->is_input_q) nr += q->irq_ptr->nr_input_qs; again: - ccq = do_eqbs(q->irq_ptr->sch_token, state, nr, &tmp_start, &tmp_count); + ccq = do_eqbs(q->irq_ptr->sch_token, state, nr, &tmp_start, &tmp_count, + auto_ack); rc = qdio_check_ccq(q, ccq); /* At least one buffer was processed, return and extract the remaining * buffers later. */ - if ((ccq == 96) && (count != tmp_count)) + if ((ccq == 96) && (count != tmp_count)) { + qdio_perf_stat_inc(&perf_stats.debug_eqbs_incomplete); return (count - tmp_count); + } + if (rc == 1) { - QDIO_DBF_TEXT5(1, trace, "eqAGAIN"); + DBF_DEV_EVENT(DBF_WARN, q->irq_ptr, "EQBS again:%2d", ccq); goto again; } if (rc < 0) { - QDIO_DBF_TEXT2(1, trace, "eqberr"); - sprintf(dbf_text, "%2x,%2x,%d,%d", count, tmp_count, ccq, nr); - QDIO_DBF_TEXT2(1, trace, dbf_text); + DBF_ERROR("%4x EQBS ERROR", SCH_NO(q)); + DBF_ERROR("%3d%3d%2d", count, tmp_count, nr); q->handler(q->irq_ptr->cdev, QDIO_ERROR_ACTIVATE_CHECK_CONDITION, 0, -1, -1, q->irq_ptr->int_parm); @@ -176,9 +177,12 @@ static int qdio_do_sqbs(struct qdio_q *q, unsigned char state, int start, int tmp_count = count, tmp_start = start; int nr = q->nr; int rc; - char dbf_text[15]; + + if (!count) + return 0; BUG_ON(!q->irq_ptr->sch_token); + qdio_perf_stat_inc(&perf_stats.debug_sqbs_all); if (!q->is_input_q) nr += q->irq_ptr->nr_input_qs; @@ -186,16 +190,13 @@ again: ccq = do_sqbs(q->irq_ptr->sch_token, state, nr, &tmp_start, &tmp_count); rc = qdio_check_ccq(q, ccq); if (rc == 1) { - QDIO_DBF_TEXT5(1, trace, "sqAGAIN"); + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "SQBS again:%2d", ccq); + qdio_perf_stat_inc(&perf_stats.debug_sqbs_incomplete); goto again; } if (rc < 0) { - QDIO_DBF_TEXT3(1, trace, "sqberr"); - sprintf(dbf_text, "%2x,%2x", count, tmp_count); - QDIO_DBF_TEXT3(1, trace, dbf_text); - sprintf(dbf_text, "%d,%d", ccq, nr); - QDIO_DBF_TEXT3(1, trace, dbf_text); - + DBF_ERROR("%4x SQBS ERROR", SCH_NO(q)); + DBF_ERROR("%3d%3d%2d", count, tmp_count, nr); q->handler(q->irq_ptr->cdev, QDIO_ERROR_ACTIVATE_CHECK_CONDITION, 0, -1, -1, q->irq_ptr->int_parm); @@ -207,7 +208,8 @@ again: /* returns number of examined buffers and their common state in *state */ static inline int get_buf_states(struct qdio_q *q, unsigned int bufnr, - unsigned char *state, unsigned int count) + unsigned char *state, unsigned int count, + int auto_ack) { unsigned char __state = 0; int i; @@ -216,7 +218,7 @@ static inline int get_buf_states(struct qdio_q *q, unsigned int bufnr, BUG_ON(count > QDIO_MAX_BUFFERS_PER_Q); if (is_qebsm(q)) - return qdio_do_eqbs(q, state, bufnr, count); + return qdio_do_eqbs(q, state, bufnr, count, auto_ack); for (i = 0; i < count; i++) { if (!__state) @@ -230,9 +232,9 @@ static inline int get_buf_states(struct qdio_q *q, unsigned int bufnr, } inline int get_buf_state(struct qdio_q *q, unsigned int bufnr, - unsigned char *state) + unsigned char *state, int auto_ack) { - return get_buf_states(q, bufnr, state, 1); + return get_buf_states(q, bufnr, state, 1, auto_ack); } /* wrap-around safe setting of slsb states, returns number of changed buffers */ @@ -282,14 +284,13 @@ static int qdio_siga_sync(struct qdio_q *q, unsigned int output, if (!need_siga_sync(q)) return 0; + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-s:"); + DBF_DEV_HEX(DBF_INFO, q->irq_ptr, q, sizeof(void *)); qdio_perf_stat_inc(&perf_stats.siga_sync); cc = do_siga_sync(q->irq_ptr->schid, output, input); - if (cc) { - QDIO_DBF_TEXT4(0, trace, "sigasync"); - QDIO_DBF_HEX4(0, trace, &q, sizeof(void *)); - QDIO_DBF_HEX3(0, trace, &cc, sizeof(int *)); - } + if (cc) + DBF_ERROR("%4x SIGA-S:%2d", SCH_NO(q), cc); return cc; } @@ -333,17 +334,13 @@ static int qdio_siga_output(struct qdio_q *q) int cc; u32 busy_bit; u64 start_time = 0; - char dbf_text[15]; - - QDIO_DBF_TEXT5(0, trace, "sigaout"); - QDIO_DBF_HEX5(0, trace, &q, sizeof(void *)); + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w:%1d", q->nr); qdio_perf_stat_inc(&perf_stats.siga_out); again: cc = qdio_do_siga_output(q, &busy_bit); if (queue_type(q) == QDIO_IQDIO_QFMT && cc == 2 && busy_bit) { - sprintf(dbf_text, "bb%4x%2x", q->irq_ptr->schid.sch_no, q->nr); - QDIO_DBF_TEXT3(0, trace, dbf_text); + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w bb:%2d", q->nr); if (!start_time) start_time = get_usecs(); @@ -354,7 +351,7 @@ again: if (cc == 2 && busy_bit) cc |= QDIO_ERROR_SIGA_BUSY; if (cc) - QDIO_DBF_HEX3(0, trace, &cc, sizeof(int *)); + DBF_ERROR("%4x SIGA-W:%2d", SCH_NO(q), cc); return cc; } @@ -362,14 +359,12 @@ static inline int qdio_siga_input(struct qdio_q *q) { int cc; - QDIO_DBF_TEXT4(0, trace, "sigain"); - QDIO_DBF_HEX4(0, trace, &q, sizeof(void *)); - + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-r:%1d", q->nr); qdio_perf_stat_inc(&perf_stats.siga_in); cc = do_siga_input(q->irq_ptr->schid, q->mask); if (cc) - QDIO_DBF_HEX3(0, trace, &cc, sizeof(int *)); + DBF_ERROR("%4x SIGA-R:%2d", SCH_NO(q), cc); return cc; } @@ -387,35 +382,91 @@ void qdio_sync_after_thinint(struct qdio_q *q) inline void qdio_stop_polling(struct qdio_q *q) { - spin_lock_bh(&q->u.in.lock); - if (!q->u.in.polling) { - spin_unlock_bh(&q->u.in.lock); + if (!q->u.in.polling) return; - } + q->u.in.polling = 0; qdio_perf_stat_inc(&perf_stats.debug_stop_polling); /* show the card that we are not polling anymore */ - set_buf_state(q, q->last_move_ftc, SLSB_P_INPUT_NOT_INIT); - spin_unlock_bh(&q->u.in.lock); + if (is_qebsm(q)) { + set_buf_states(q, q->last_move_ftc, SLSB_P_INPUT_NOT_INIT, + q->u.in.ack_count); + q->u.in.ack_count = 0; + } else + set_buf_state(q, q->last_move_ftc, SLSB_P_INPUT_NOT_INIT); } -static void announce_buffer_error(struct qdio_q *q) +static void announce_buffer_error(struct qdio_q *q, int count) { - char dbf_text[15]; + q->qdio_error = QDIO_ERROR_SLSB_STATE; - if (q->is_input_q) - QDIO_DBF_TEXT3(1, trace, "inperr"); - else - QDIO_DBF_TEXT3(0, trace, "outperr"); + /* special handling for no target buffer empty */ + if ((!q->is_input_q && + (q->sbal[q->first_to_check]->element[15].flags & 0xff) == 0x10)) { + qdio_perf_stat_inc(&perf_stats.outbound_target_full); + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "OUTFULL FTC:%3d", + q->first_to_check); + return; + } - sprintf(dbf_text, "%x-%x-%x", q->first_to_check, - q->sbal[q->first_to_check]->element[14].flags, - q->sbal[q->first_to_check]->element[15].flags); - QDIO_DBF_TEXT3(1, trace, dbf_text); - QDIO_DBF_HEX2(1, trace, q->sbal[q->first_to_check], 256); + DBF_ERROR("%4x BUF ERROR", SCH_NO(q)); + DBF_ERROR((q->is_input_q) ? "IN:%2d" : "OUT:%2d", q->nr); + DBF_ERROR("FTC:%3d C:%3d", q->first_to_check, count); + DBF_ERROR("F14:%2x F15:%2x", + q->sbal[q->first_to_check]->element[14].flags & 0xff, + q->sbal[q->first_to_check]->element[15].flags & 0xff); +} - q->qdio_error = QDIO_ERROR_SLSB_STATE; +static inline void inbound_primed(struct qdio_q *q, int count) +{ + int new; + + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in prim: %3d", count); + + /* for QEBSM the ACK was already set by EQBS */ + if (is_qebsm(q)) { + if (!q->u.in.polling) { + q->u.in.polling = 1; + q->u.in.ack_count = count; + q->last_move_ftc = q->first_to_check; + return; + } + + /* delete the previous ACK's */ + set_buf_states(q, q->last_move_ftc, SLSB_P_INPUT_NOT_INIT, + q->u.in.ack_count); + q->u.in.ack_count = count; + q->last_move_ftc = q->first_to_check; + return; + } + + /* + * ACK the newest buffer. The ACK will be removed in qdio_stop_polling + * or by the next inbound run. + */ + new = add_buf(q->first_to_check, count - 1); + if (q->u.in.polling) { + /* reset the previous ACK but first set the new one */ + set_buf_state(q, new, SLSB_P_INPUT_ACK); + set_buf_state(q, q->last_move_ftc, SLSB_P_INPUT_NOT_INIT); + } + else { + q->u.in.polling = 1; + set_buf_state(q, q->first_to_check, SLSB_P_INPUT_ACK); + } + + q->last_move_ftc = new; + count--; + if (!count) + return; + + /* + * Need to change all PRIMED buffers to NOT_INIT, otherwise + * we're loosing initiative in the thinint code. + */ + set_buf_states(q, next_buf(q->first_to_check), SLSB_P_INPUT_NOT_INIT, + count); } static int get_inbound_buffer_frontier(struct qdio_q *q) @@ -424,13 +475,6 @@ static int get_inbound_buffer_frontier(struct qdio_q *q) unsigned char state; /* - * If we still poll don't update last_move_ftc, keep the - * previously ACK buffer there. - */ - if (!q->u.in.polling) - q->last_move_ftc = q->first_to_check; - - /* * Don't check 128 buffers, as otherwise qdio_inbound_q_moved * would return 0. */ @@ -450,34 +494,13 @@ check_next: if (q->first_to_check == stop) goto out; - count = get_buf_states(q, q->first_to_check, &state, count); + count = get_buf_states(q, q->first_to_check, &state, count, 1); if (!count) goto out; switch (state) { case SLSB_P_INPUT_PRIMED: - QDIO_DBF_TEXT5(0, trace, "inptprim"); - - /* - * Only ACK the first buffer. The ACK will be removed in - * qdio_stop_polling. - */ - if (q->u.in.polling) - state = SLSB_P_INPUT_NOT_INIT; - else { - q->u.in.polling = 1; - state = SLSB_P_INPUT_ACK; - } - set_buf_state(q, q->first_to_check, state); - - /* - * Need to change all PRIMED buffers to NOT_INIT, otherwise - * we're loosing initiative in the thinint code. - */ - if (count > 1) - set_buf_states(q, next_buf(q->first_to_check), - SLSB_P_INPUT_NOT_INIT, count - 1); - + inbound_primed(q, count); /* * No siga-sync needed for non-qebsm here, as the inbound queue * will be synced on the next siga-r, resp. @@ -487,21 +510,23 @@ check_next: atomic_sub(count, &q->nr_buf_used); goto check_next; case SLSB_P_INPUT_ERROR: - announce_buffer_error(q); + announce_buffer_error(q, count); /* process the buffer, the upper layer will take care of it */ q->first_to_check = add_buf(q->first_to_check, count); atomic_sub(count, &q->nr_buf_used); break; case SLSB_CU_INPUT_EMPTY: + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in nop"); + break; case SLSB_P_INPUT_NOT_INIT: case SLSB_P_INPUT_ACK: - QDIO_DBF_TEXT5(0, trace, "inpnipro"); + WARN_ON(1); + q->first_to_check++; break; default: BUG(); } out: - QDIO_DBF_HEX4(0, trace, &q->first_to_check, sizeof(int)); return q->first_to_check; } @@ -515,8 +540,7 @@ int qdio_inbound_q_moved(struct qdio_q *q) if (!need_siga_sync(q) && !pci_out_supported(q)) q->u.in.timestamp = get_usecs(); - QDIO_DBF_TEXT4(0, trace, "inhasmvd"); - QDIO_DBF_HEX4(0, trace, &q, sizeof(void *)); + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in moved"); return 1; } else return 0; @@ -524,10 +548,7 @@ int qdio_inbound_q_moved(struct qdio_q *q) static int qdio_inbound_q_done(struct qdio_q *q) { - unsigned char state; -#ifdef CONFIG_QDIO_DEBUG - char dbf_text[15]; -#endif + unsigned char state = 0; if (!atomic_read(&q->nr_buf_used)) return 1; @@ -538,7 +559,7 @@ static int qdio_inbound_q_done(struct qdio_q *q) */ qdio_siga_sync_q(q); - get_buf_state(q, q->first_to_check, &state); + get_buf_state(q, q->first_to_check, &state, 0); if (state == SLSB_P_INPUT_PRIMED) /* we got something to do */ return 0; @@ -552,20 +573,12 @@ static int qdio_inbound_q_done(struct qdio_q *q) * has (probably) not moved (see qdio_inbound_processing). */ if (get_usecs() > q->u.in.timestamp + QDIO_INPUT_THRESHOLD) { -#ifdef CONFIG_QDIO_DEBUG - QDIO_DBF_TEXT4(0, trace, "inqisdon"); - QDIO_DBF_HEX4(0, trace, &q, sizeof(void *)); - sprintf(dbf_text, "pf%02x", q->first_to_check); - QDIO_DBF_TEXT4(0, trace, dbf_text); -#endif /* CONFIG_QDIO_DEBUG */ + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in done:%3d", + q->first_to_check); return 1; } else { -#ifdef CONFIG_QDIO_DEBUG - QDIO_DBF_TEXT4(0, trace, "inqisntd"); - QDIO_DBF_HEX4(0, trace, &q, sizeof(void *)); - sprintf(dbf_text, "pf%02x", q->first_to_check); - QDIO_DBF_TEXT4(0, trace, dbf_text); -#endif /* CONFIG_QDIO_DEBUG */ + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in notd:%3d", + q->first_to_check); return 0; } } @@ -573,9 +586,6 @@ static int qdio_inbound_q_done(struct qdio_q *q) void qdio_kick_inbound_handler(struct qdio_q *q) { int count, start, end; -#ifdef CONFIG_QDIO_DEBUG - char dbf_text[15]; -#endif qdio_perf_stat_inc(&perf_stats.inbound_handler); @@ -586,10 +596,7 @@ void qdio_kick_inbound_handler(struct qdio_q *q) else count = end + QDIO_MAX_BUFFERS_PER_Q - start; -#ifdef CONFIG_QDIO_DEBUG - sprintf(dbf_text, "s=%2xc=%2x", start, count); - QDIO_DBF_TEXT4(0, trace, dbf_text); -#endif /* CONFIG_QDIO_DEBUG */ + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "kih s:%3d c:%3d", start, count); if (unlikely(q->irq_ptr->state != QDIO_IRQ_STATE_ACTIVE)) return; @@ -655,14 +662,14 @@ check_next: if (q->first_to_check == stop) return q->first_to_check; - count = get_buf_states(q, q->first_to_check, &state, count); + count = get_buf_states(q, q->first_to_check, &state, count, 0); if (!count) return q->first_to_check; switch (state) { case SLSB_P_OUTPUT_EMPTY: /* the adapter got it */ - QDIO_DBF_TEXT5(0, trace, "outpempt"); + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "out empty:%1d %3d", q->nr, count); atomic_sub(count, &q->nr_buf_used); q->first_to_check = add_buf(q->first_to_check, count); @@ -674,16 +681,19 @@ check_next: break; goto check_next; case SLSB_P_OUTPUT_ERROR: - announce_buffer_error(q); + announce_buffer_error(q, count); /* process the buffer, the upper layer will take care of it */ q->first_to_check = add_buf(q->first_to_check, count); atomic_sub(count, &q->nr_buf_used); break; case SLSB_CU_OUTPUT_PRIMED: /* the adapter has not fetched the output yet */ - QDIO_DBF_TEXT5(0, trace, "outpprim"); + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "out primed:%1d", q->nr); break; case SLSB_P_OUTPUT_NOT_INIT: + WARN_ON(1); + q->first_to_check++; + break; case SLSB_P_OUTPUT_HALTED: break; default: @@ -706,8 +716,7 @@ static inline int qdio_outbound_q_moved(struct qdio_q *q) if ((bufnr != q->last_move_ftc) || q->qdio_error) { q->last_move_ftc = bufnr; - QDIO_DBF_TEXT4(0, trace, "oqhasmvd"); - QDIO_DBF_HEX4(0, trace, &q, sizeof(void *)); + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "out moved:%1d", q->nr); return 1; } else return 0; @@ -742,12 +751,8 @@ static inline int qdio_outbound_q_moved(struct qdio_q *q) static void qdio_kick_outbound_q(struct qdio_q *q) { int rc; -#ifdef CONFIG_QDIO_DEBUG - char dbf_text[15]; - QDIO_DBF_TEXT5(0, trace, "kickoutq"); - QDIO_DBF_HEX5(0, trace, &q, sizeof(void *)); -#endif /* CONFIG_QDIO_DEBUG */ + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "kickoutq:%1d", q->nr); if (!need_siga_out(q)) return; @@ -756,15 +761,9 @@ static void qdio_kick_outbound_q(struct qdio_q *q) switch (rc) { case 0: /* TODO: improve error handling for CC=0 case */ -#ifdef CONFIG_QDIO_DEBUG - if (q->u.out.timestamp) { - QDIO_DBF_TEXT3(0, trace, "cc2reslv"); - sprintf(dbf_text, "%4x%2x%2x", q->irq_ptr->schid.sch_no, - q->nr, - atomic_read(&q->u.out.busy_siga_counter)); - QDIO_DBF_TEXT3(0, trace, dbf_text); - } -#endif /* CONFIG_QDIO_DEBUG */ + if (q->u.out.timestamp) + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "cc2 rslv:%4x", + atomic_read(&q->u.out.busy_siga_counter)); /* went smooth this time, reset timestamp */ q->u.out.timestamp = 0; break; @@ -781,12 +780,7 @@ static void qdio_kick_outbound_q(struct qdio_q *q) tasklet_schedule(&q->tasklet); break; } - QDIO_DBF_TEXT2(0, trace, "cc2REPRT"); -#ifdef CONFIG_QDIO_DEBUG - sprintf(dbf_text, "%4x%2x%2x", q->irq_ptr->schid.sch_no, q->nr, - atomic_read(&q->u.out.busy_siga_counter)); - QDIO_DBF_TEXT3(0, trace, dbf_text); -#endif /* CONFIG_QDIO_DEBUG */ + DBF_ERROR("%4x cc2 REP:%1d", SCH_NO(q), q->nr); default: /* for plain cc=1, 2 or 3 */ q->qdio_error = rc; @@ -796,9 +790,6 @@ static void qdio_kick_outbound_q(struct qdio_q *q) static void qdio_kick_outbound_handler(struct qdio_q *q) { int start, end, count; -#ifdef CONFIG_QDIO_DEBUG - char dbf_text[15]; -#endif start = q->first_to_kick; end = q->last_move_ftc; @@ -807,13 +798,8 @@ static void qdio_kick_outbound_handler(struct qdio_q *q) else count = end + QDIO_MAX_BUFFERS_PER_Q - start; -#ifdef CONFIG_QDIO_DEBUG - QDIO_DBF_TEXT4(0, trace, "kickouth"); - QDIO_DBF_HEX4(0, trace, &q, sizeof(void *)); - - sprintf(dbf_text, "s=%2xc=%2x", start, count); - QDIO_DBF_TEXT4(0, trace, dbf_text); -#endif /* CONFIG_QDIO_DEBUG */ + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "kickouth: %1d", q->nr); + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "s:%3d c:%3d", start, count); if (unlikely(q->irq_ptr->state != QDIO_IRQ_STATE_ACTIVE)) return; @@ -908,27 +894,18 @@ void qdio_check_outbound_after_thinint(struct qdio_q *q) static inline void qdio_set_state(struct qdio_irq *irq_ptr, enum qdio_irq_states state) { -#ifdef CONFIG_QDIO_DEBUG - char dbf_text[15]; - - QDIO_DBF_TEXT5(0, trace, "newstate"); - sprintf(dbf_text, "%4x%4x", irq_ptr->schid.sch_no, state); - QDIO_DBF_TEXT5(0, trace, dbf_text); -#endif /* CONFIG_QDIO_DEBUG */ + DBF_DEV_EVENT(DBF_INFO, irq_ptr, "newstate: %1d", state); irq_ptr->state = state; mb(); } -static void qdio_irq_check_sense(struct subchannel_id schid, struct irb *irb) +static void qdio_irq_check_sense(struct qdio_irq *irq_ptr, struct irb *irb) { - char dbf_text[15]; - if (irb->esw.esw0.erw.cons) { - sprintf(dbf_text, "sens%4x", schid.sch_no); - QDIO_DBF_TEXT2(1, trace, dbf_text); - QDIO_DBF_HEX0(0, trace, irb, 64); - QDIO_DBF_HEX0(0, trace, irb->ecw, 64); + DBF_ERROR("%4x sense:", irq_ptr->schid.sch_no); + DBF_ERROR_HEX(irb, 64); + DBF_ERROR_HEX(irb->ecw, 64); } } @@ -962,14 +939,10 @@ static void qdio_handle_activate_check(struct ccw_device *cdev, { struct qdio_irq *irq_ptr = cdev->private->qdio_data; struct qdio_q *q; - char dbf_text[15]; - QDIO_DBF_TEXT2(1, trace, "ick2"); - sprintf(dbf_text, "%s", dev_name(&cdev->dev)); - QDIO_DBF_TEXT2(1, trace, dbf_text); - QDIO_DBF_HEX2(0, trace, &intparm, sizeof(int)); - QDIO_DBF_HEX2(0, trace, &dstat, sizeof(int)); - QDIO_DBF_HEX2(0, trace, &cstat, sizeof(int)); + DBF_ERROR("%4x ACT CHECK", irq_ptr->schid.sch_no); + DBF_ERROR("intp :%lx", intparm); + DBF_ERROR("ds: %2x cs:%2x", dstat, cstat); if (irq_ptr->nr_input_qs) { q = irq_ptr->input_qs[0]; @@ -1022,28 +995,29 @@ static void qdio_int_error(struct ccw_device *cdev) } static int qdio_establish_check_errors(struct ccw_device *cdev, int cstat, - int dstat) + int dstat) { struct qdio_irq *irq_ptr = cdev->private->qdio_data; if (cstat || (dstat & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END))) { - QDIO_DBF_TEXT2(1, setup, "eq:ckcon"); + DBF_ERROR("EQ:ck con"); goto error; } if (!(dstat & DEV_STAT_DEV_END)) { - QDIO_DBF_TEXT2(1, setup, "eq:no de"); + DBF_ERROR("EQ:no dev"); goto error; } if (dstat & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END)) { - QDIO_DBF_TEXT2(1, setup, "eq:badio"); + DBF_ERROR("EQ: bad io"); goto error; } return 0; error: - QDIO_DBF_HEX2(0, trace, &cstat, sizeof(int)); - QDIO_DBF_HEX2(0, trace, &dstat, sizeof(int)); + DBF_ERROR("%4x EQ:error", irq_ptr->schid.sch_no); + DBF_ERROR("ds: %2x cs:%2x", dstat, cstat); + qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR); return 1; } @@ -1052,12 +1026,8 @@ static void qdio_establish_handle_irq(struct ccw_device *cdev, int cstat, int dstat) { struct qdio_irq *irq_ptr = cdev->private->qdio_data; - char dbf_text[15]; - - sprintf(dbf_text, "qehi%4x", cdev->private->schid.sch_no); - QDIO_DBF_TEXT0(0, setup, dbf_text); - QDIO_DBF_TEXT0(0, trace, dbf_text); + DBF_DEV_EVENT(DBF_INFO, irq_ptr, "qest irq"); if (!qdio_establish_check_errors(cdev, cstat, dstat)) qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ESTABLISHED); } @@ -1068,25 +1038,21 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm, { struct qdio_irq *irq_ptr = cdev->private->qdio_data; int cstat, dstat; - char dbf_text[15]; qdio_perf_stat_inc(&perf_stats.qdio_int); if (!intparm || !irq_ptr) { - sprintf(dbf_text, "qihd%4x", cdev->private->schid.sch_no); - QDIO_DBF_TEXT2(1, setup, dbf_text); + DBF_ERROR("qint:%4x", cdev->private->schid.sch_no); return; } if (IS_ERR(irb)) { switch (PTR_ERR(irb)) { case -EIO: - sprintf(dbf_text, "ierr%4x", irq_ptr->schid.sch_no); - QDIO_DBF_TEXT2(1, setup, dbf_text); + DBF_ERROR("%4x IO error", irq_ptr->schid.sch_no); return; case -ETIMEDOUT: - sprintf(dbf_text, "qtoh%4x", irq_ptr->schid.sch_no); - QDIO_DBF_TEXT2(1, setup, dbf_text); + DBF_ERROR("%4x IO timeout", irq_ptr->schid.sch_no); qdio_int_error(cdev); return; default: @@ -1094,7 +1060,7 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm, return; } } - qdio_irq_check_sense(irq_ptr->schid, irb); + qdio_irq_check_sense(irq_ptr, irb); cstat = irb->scsw.cmd.cstat; dstat = irb->scsw.cmd.dstat; @@ -1129,23 +1095,20 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm, /** * qdio_get_ssqd_desc - get qdio subchannel description * @cdev: ccw device to get description for + * @data: where to store the ssqd * - * Returns a pointer to the saved qdio subchannel description, - * or NULL for not setup qdio devices. + * Returns 0 or an error code. The results of the chsc are stored in the + * specified structure. */ -struct qdio_ssqd_desc *qdio_get_ssqd_desc(struct ccw_device *cdev) +int qdio_get_ssqd_desc(struct ccw_device *cdev, + struct qdio_ssqd_desc *data) { - struct qdio_irq *irq_ptr; - char dbf_text[15]; - sprintf(dbf_text, "qssq%4x", cdev->private->schid.sch_no); - QDIO_DBF_TEXT0(0, setup, dbf_text); - - irq_ptr = cdev->private->qdio_data; - if (!irq_ptr) - return NULL; + if (!cdev || !cdev->private) + return -EINVAL; - return &irq_ptr->ssqd_desc; + DBF_EVENT("get ssqd:%4x", cdev->private->schid.sch_no); + return qdio_setup_get_ssqd(NULL, &cdev->private->schid, data); } EXPORT_SYMBOL_GPL(qdio_get_ssqd_desc); @@ -1159,14 +1122,9 @@ EXPORT_SYMBOL_GPL(qdio_get_ssqd_desc); */ int qdio_cleanup(struct ccw_device *cdev, int how) { - struct qdio_irq *irq_ptr; - char dbf_text[15]; + struct qdio_irq *irq_ptr = cdev->private->qdio_data; int rc; - sprintf(dbf_text, "qcln%4x", cdev->private->schid.sch_no); - QDIO_DBF_TEXT0(0, setup, dbf_text); - - irq_ptr = cdev->private->qdio_data; if (!irq_ptr) return -ENODEV; @@ -1199,18 +1157,15 @@ static void qdio_shutdown_queues(struct ccw_device *cdev) */ int qdio_shutdown(struct ccw_device *cdev, int how) { - struct qdio_irq *irq_ptr; + struct qdio_irq *irq_ptr = cdev->private->qdio_data; int rc; unsigned long flags; - char dbf_text[15]; - - sprintf(dbf_text, "qshu%4x", cdev->private->schid.sch_no); - QDIO_DBF_TEXT0(0, setup, dbf_text); - irq_ptr = cdev->private->qdio_data; if (!irq_ptr) return -ENODEV; + DBF_EVENT("qshutdown:%4x", cdev->private->schid.sch_no); + mutex_lock(&irq_ptr->setup_mutex); /* * Subchannel was already shot down. We cannot prevent being called @@ -1234,10 +1189,8 @@ int qdio_shutdown(struct ccw_device *cdev, int how) /* default behaviour is halt */ rc = ccw_device_halt(cdev, QDIO_DOING_CLEANUP); if (rc) { - sprintf(dbf_text, "sher%4x", irq_ptr->schid.sch_no); - QDIO_DBF_TEXT0(0, setup, dbf_text); - sprintf(dbf_text, "rc=%d", rc); - QDIO_DBF_TEXT0(0, setup, dbf_text); + DBF_ERROR("%4x SHUTD ERR", irq_ptr->schid.sch_no); + DBF_ERROR("rc:%4d", rc); goto no_cleanup; } @@ -1271,17 +1224,18 @@ EXPORT_SYMBOL_GPL(qdio_shutdown); */ int qdio_free(struct ccw_device *cdev) { - struct qdio_irq *irq_ptr; - char dbf_text[15]; - - sprintf(dbf_text, "qfre%4x", cdev->private->schid.sch_no); - QDIO_DBF_TEXT0(0, setup, dbf_text); + struct qdio_irq *irq_ptr = cdev->private->qdio_data; - irq_ptr = cdev->private->qdio_data; if (!irq_ptr) return -ENODEV; + DBF_EVENT("qfree:%4x", cdev->private->schid.sch_no); mutex_lock(&irq_ptr->setup_mutex); + + if (irq_ptr->debug_area != NULL) { + debug_unregister(irq_ptr->debug_area); + irq_ptr->debug_area = NULL; + } cdev->private->qdio_data = NULL; mutex_unlock(&irq_ptr->setup_mutex); @@ -1300,10 +1254,6 @@ EXPORT_SYMBOL_GPL(qdio_free); int qdio_initialize(struct qdio_initialize *init_data) { int rc; - char dbf_text[15]; - - sprintf(dbf_text, "qini%4x", init_data->cdev->private->schid.sch_no); - QDIO_DBF_TEXT0(0, setup, dbf_text); rc = qdio_allocate(init_data); if (rc) @@ -1323,10 +1273,8 @@ EXPORT_SYMBOL_GPL(qdio_initialize); int qdio_allocate(struct qdio_initialize *init_data) { struct qdio_irq *irq_ptr; - char dbf_text[15]; - sprintf(dbf_text, "qalc%4x", init_data->cdev->private->schid.sch_no); - QDIO_DBF_TEXT0(0, setup, dbf_text); + DBF_EVENT("qallocate:%4x", init_data->cdev->private->schid.sch_no); if ((init_data->no_input_qs && !init_data->input_handler) || (init_data->no_output_qs && !init_data->output_handler)) @@ -1340,16 +1288,13 @@ int qdio_allocate(struct qdio_initialize *init_data) (!init_data->output_sbal_addr_array)) return -EINVAL; - qdio_allocate_do_dbf(init_data); - /* irq_ptr must be in GFP_DMA since it contains ccw1.cda */ irq_ptr = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); if (!irq_ptr) goto out_err; - QDIO_DBF_TEXT0(0, setup, "irq_ptr:"); - QDIO_DBF_HEX0(0, setup, &irq_ptr, sizeof(void *)); mutex_init(&irq_ptr->setup_mutex); + qdio_allocate_dbf(init_data, irq_ptr); /* * Allocate a page for the chsc calls in qdio_establish. @@ -1367,9 +1312,6 @@ int qdio_allocate(struct qdio_initialize *init_data) goto out_rel; WARN_ON((unsigned long)irq_ptr->qdr & 0xfff); - QDIO_DBF_TEXT0(0, setup, "qdr:"); - QDIO_DBF_HEX0(0, setup, &irq_ptr->qdr, sizeof(void *)); - if (qdio_allocate_qs(irq_ptr, init_data->no_input_qs, init_data->no_output_qs)) goto out_rel; @@ -1390,14 +1332,12 @@ EXPORT_SYMBOL_GPL(qdio_allocate); */ int qdio_establish(struct qdio_initialize *init_data) { - char dbf_text[20]; struct qdio_irq *irq_ptr; struct ccw_device *cdev = init_data->cdev; unsigned long saveflags; int rc; - sprintf(dbf_text, "qest%4x", cdev->private->schid.sch_no); - QDIO_DBF_TEXT0(0, setup, dbf_text); + DBF_EVENT("qestablish:%4x", cdev->private->schid.sch_no); irq_ptr = cdev->private->qdio_data; if (!irq_ptr) @@ -1427,10 +1367,8 @@ int qdio_establish(struct qdio_initialize *init_data) rc = ccw_device_start(cdev, &irq_ptr->ccw, QDIO_DOING_ESTABLISH, 0, 0); if (rc) { - sprintf(dbf_text, "eq:io%4x", irq_ptr->schid.sch_no); - QDIO_DBF_TEXT2(1, setup, dbf_text); - sprintf(dbf_text, "eq:rc%4x", rc); - QDIO_DBF_TEXT2(1, setup, dbf_text); + DBF_ERROR("%4x est IO ERR", irq_ptr->schid.sch_no); + DBF_ERROR("rc:%4x", rc); } spin_unlock_irqrestore(get_ccwdev_lock(cdev), saveflags); @@ -1451,10 +1389,8 @@ int qdio_establish(struct qdio_initialize *init_data) } qdio_setup_ssqd_info(irq_ptr); - sprintf(dbf_text, "qDmmwc%2x", irq_ptr->ssqd_desc.mmwc); - QDIO_DBF_TEXT2(0, setup, dbf_text); - sprintf(dbf_text, "qib ac%2x", irq_ptr->qib.ac); - QDIO_DBF_TEXT2(0, setup, dbf_text); + DBF_EVENT("qDmmwc:%2x", irq_ptr->ssqd_desc.mmwc); + DBF_EVENT("qib ac:%4x", irq_ptr->qib.ac); /* qebsm is now setup if available, initialize buffer states */ qdio_init_buf_states(irq_ptr); @@ -1475,10 +1411,8 @@ int qdio_activate(struct ccw_device *cdev) struct qdio_irq *irq_ptr; int rc; unsigned long saveflags; - char dbf_text[20]; - sprintf(dbf_text, "qact%4x", cdev->private->schid.sch_no); - QDIO_DBF_TEXT0(0, setup, dbf_text); + DBF_EVENT("qactivate:%4x", cdev->private->schid.sch_no); irq_ptr = cdev->private->qdio_data; if (!irq_ptr) @@ -1504,10 +1438,8 @@ int qdio_activate(struct ccw_device *cdev) rc = ccw_device_start(cdev, &irq_ptr->ccw, QDIO_DOING_ACTIVATE, 0, DOIO_DENY_PREFETCH); if (rc) { - sprintf(dbf_text, "aq:io%4x", irq_ptr->schid.sch_no); - QDIO_DBF_TEXT2(1, setup, dbf_text); - sprintf(dbf_text, "aq:rc%4x", rc); - QDIO_DBF_TEXT2(1, setup, dbf_text); + DBF_ERROR("%4x act IO ERR", irq_ptr->schid.sch_no); + DBF_ERROR("rc:%4x", rc); } spin_unlock_irqrestore(get_ccwdev_lock(cdev), saveflags); @@ -1565,23 +1497,38 @@ static inline int buf_in_between(int bufnr, int start, int count) static void handle_inbound(struct qdio_q *q, unsigned int callflags, int bufnr, int count) { - unsigned long flags; - int used, rc; + int used, rc, diff; - /* - * do_QDIO could run in parallel with the queue tasklet so the - * upper-layer programm could empty the ACK'ed buffer here. - * If that happens we must clear the polling flag, otherwise - * qdio_stop_polling() could set the buffer to NOT_INIT after - * it was set to EMPTY which would kill us. - */ - spin_lock_irqsave(&q->u.in.lock, flags); - if (q->u.in.polling) - if (buf_in_between(q->last_move_ftc, bufnr, count)) + if (!q->u.in.polling) + goto set; + + /* protect against stop polling setting an ACK for an emptied slsb */ + if (count == QDIO_MAX_BUFFERS_PER_Q) { + /* overwriting everything, just delete polling status */ + q->u.in.polling = 0; + q->u.in.ack_count = 0; + goto set; + } else if (buf_in_between(q->last_move_ftc, bufnr, count)) { + if (is_qebsm(q)) { + /* partial overwrite, just update last_move_ftc */ + diff = add_buf(bufnr, count); + diff = sub_buf(diff, q->last_move_ftc); + q->u.in.ack_count -= diff; + if (q->u.in.ack_count <= 0) { + q->u.in.polling = 0; + q->u.in.ack_count = 0; + /* TODO: must we set last_move_ftc to something meaningful? */ + goto set; + } + q->last_move_ftc = add_buf(q->last_move_ftc, diff); + } + else + /* the only ACK will be deleted, so stop polling */ q->u.in.polling = 0; + } +set: count = set_buf_states(q, bufnr, SLSB_CU_INPUT_EMPTY, count); - spin_unlock_irqrestore(&q->u.in.lock, flags); used = atomic_add_return(count, &q->nr_buf_used) - count; BUG_ON(used + count > QDIO_MAX_BUFFERS_PER_Q); @@ -1649,11 +1596,11 @@ static void handle_outbound(struct qdio_q *q, unsigned int callflags, } /* try to fast requeue buffers */ - get_buf_state(q, prev_buf(bufnr), &state); + get_buf_state(q, prev_buf(bufnr), &state, 0); if (state != SLSB_CU_OUTPUT_PRIMED) qdio_kick_outbound_q(q); else { - QDIO_DBF_TEXT5(0, trace, "fast-req"); + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "fast-req"); qdio_perf_stat_inc(&perf_stats.fast_requeue); } out: @@ -1673,12 +1620,6 @@ int do_QDIO(struct ccw_device *cdev, unsigned int callflags, int q_nr, int bufnr, int count) { struct qdio_irq *irq_ptr; -#ifdef CONFIG_QDIO_DEBUG - char dbf_text[20]; - - sprintf(dbf_text, "doQD%4x", cdev->private->schid.sch_no); - QDIO_DBF_TEXT3(0, trace, dbf_text); -#endif /* CONFIG_QDIO_DEBUG */ if ((bufnr > QDIO_MAX_BUFFERS_PER_Q) || (count > QDIO_MAX_BUFFERS_PER_Q) || @@ -1692,33 +1633,24 @@ int do_QDIO(struct ccw_device *cdev, unsigned int callflags, if (!irq_ptr) return -ENODEV; -#ifdef CONFIG_QDIO_DEBUG if (callflags & QDIO_FLAG_SYNC_INPUT) - QDIO_DBF_HEX3(0, trace, &irq_ptr->input_qs[q_nr], - sizeof(void *)); + DBF_DEV_EVENT(DBF_INFO, irq_ptr, "doQDIO input"); else - QDIO_DBF_HEX3(0, trace, &irq_ptr->output_qs[q_nr], - sizeof(void *)); - - sprintf(dbf_text, "flag%04x", callflags); - QDIO_DBF_TEXT3(0, trace, dbf_text); - sprintf(dbf_text, "qi%02xct%02x", bufnr, count); - QDIO_DBF_TEXT3(0, trace, dbf_text); -#endif /* CONFIG_QDIO_DEBUG */ + DBF_DEV_EVENT(DBF_INFO, irq_ptr, "doQDIO output"); + DBF_DEV_EVENT(DBF_INFO, irq_ptr, "q:%1d flag:%4x", q_nr, callflags); + DBF_DEV_EVENT(DBF_INFO, irq_ptr, "buf:%2d cnt:%3d", bufnr, count); if (irq_ptr->state != QDIO_IRQ_STATE_ACTIVE) return -EBUSY; if (callflags & QDIO_FLAG_SYNC_INPUT) - handle_inbound(irq_ptr->input_qs[q_nr], - callflags, bufnr, count); + handle_inbound(irq_ptr->input_qs[q_nr], callflags, bufnr, + count); else if (callflags & QDIO_FLAG_SYNC_OUTPUT) - handle_outbound(irq_ptr->output_qs[q_nr], - callflags, bufnr, count); - else { - QDIO_DBF_TEXT3(1, trace, "doQD:inv"); + handle_outbound(irq_ptr->output_qs[q_nr], callflags, bufnr, + count); + else return -EINVAL; - } return 0; } EXPORT_SYMBOL_GPL(do_QDIO); diff --git a/drivers/s390/cio/qdio_perf.c b/drivers/s390/cio/qdio_perf.c index ec5c4a414235..136d0f0b1e93 100644 --- a/drivers/s390/cio/qdio_perf.c +++ b/drivers/s390/cio/qdio_perf.c @@ -74,12 +74,20 @@ static int qdio_perf_proc_show(struct seq_file *m, void *v) seq_printf(m, "\n"); seq_printf(m, "Number of fast requeues (outg. SBAL w/o SIGA)\t: %li\n", (long)atomic_long_read(&perf_stats.fast_requeue)); + seq_printf(m, "Number of outbound target full condition\t: %li\n", + (long)atomic_long_read(&perf_stats.outbound_target_full)); seq_printf(m, "Number of outbound tasklet mod_timer calls\t: %li\n", (long)atomic_long_read(&perf_stats.debug_tl_out_timer)); seq_printf(m, "Number of stop polling calls\t\t\t: %li\n", (long)atomic_long_read(&perf_stats.debug_stop_polling)); seq_printf(m, "AI inbound tasklet loops after stop polling\t: %li\n", (long)atomic_long_read(&perf_stats.thinint_inbound_loop2)); + seq_printf(m, "QEBSM EQBS total/incomplete\t\t\t: %li/%li\n", + (long)atomic_long_read(&perf_stats.debug_eqbs_all), + (long)atomic_long_read(&perf_stats.debug_eqbs_incomplete)); + seq_printf(m, "QEBSM SQBS total/incomplete\t\t\t: %li/%li\n", + (long)atomic_long_read(&perf_stats.debug_sqbs_all), + (long)atomic_long_read(&perf_stats.debug_sqbs_incomplete)); seq_printf(m, "\n"); return 0; } diff --git a/drivers/s390/cio/qdio_perf.h b/drivers/s390/cio/qdio_perf.h index 5c406a8b7387..7821ac4fa517 100644 --- a/drivers/s390/cio/qdio_perf.h +++ b/drivers/s390/cio/qdio_perf.h @@ -36,10 +36,15 @@ struct qdio_perf_stats { atomic_long_t inbound_handler; atomic_long_t outbound_handler; atomic_long_t fast_requeue; + atomic_long_t outbound_target_full; /* for debugging */ atomic_long_t debug_tl_out_timer; atomic_long_t debug_stop_polling; + atomic_long_t debug_eqbs_all; + atomic_long_t debug_eqbs_incomplete; + atomic_long_t debug_sqbs_all; + atomic_long_t debug_sqbs_incomplete; }; extern struct qdio_perf_stats perf_stats; diff --git a/drivers/s390/cio/qdio_setup.c b/drivers/s390/cio/qdio_setup.c index a0b6b46e7466..18d54fc21ce9 100644 --- a/drivers/s390/cio/qdio_setup.c +++ b/drivers/s390/cio/qdio_setup.c @@ -120,14 +120,12 @@ static void setup_queues_misc(struct qdio_q *q, struct qdio_irq *irq_ptr, } static void setup_storage_lists(struct qdio_q *q, struct qdio_irq *irq_ptr, - void **sbals_array, char *dbf_text, int i) + void **sbals_array, int i) { struct qdio_q *prev; int j; - QDIO_DBF_TEXT0(0, setup, dbf_text); - QDIO_DBF_HEX0(0, setup, &q, sizeof(void *)); - + DBF_HEX(&q, sizeof(void *)); q->sl = (struct sl *)((char *)q->slib + PAGE_SIZE / 2); /* fill in sbal */ @@ -150,31 +148,26 @@ static void setup_storage_lists(struct qdio_q *q, struct qdio_irq *irq_ptr, for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; j++) q->sl->element[j].sbal = (unsigned long)q->sbal[j]; - QDIO_DBF_TEXT2(0, setup, "sl-sb-b0"); - QDIO_DBF_HEX2(0, setup, q->sl, sizeof(void *)); - QDIO_DBF_HEX2(0, setup, &q->slsb, sizeof(void *)); - QDIO_DBF_HEX2(0, setup, q->sbal, sizeof(void *)); + DBF_EVENT("sl-slsb-sbal"); + DBF_HEX(q->sl, sizeof(void *)); + DBF_HEX(&q->slsb, sizeof(void *)); + DBF_HEX(q->sbal, sizeof(void *)); } static void setup_queues(struct qdio_irq *irq_ptr, struct qdio_initialize *qdio_init) { - char dbf_text[20]; struct qdio_q *q; void **input_sbal_array = qdio_init->input_sbal_addr_array; void **output_sbal_array = qdio_init->output_sbal_addr_array; int i; - sprintf(dbf_text, "qset%4x", qdio_init->cdev->private->schid.sch_no); - QDIO_DBF_TEXT0(0, setup, dbf_text); - for_each_input_queue(irq_ptr, q, i) { - sprintf(dbf_text, "in-q%4x", i); + DBF_EVENT("in-q:%1d", i); setup_queues_misc(q, irq_ptr, qdio_init->input_handler, i); q->is_input_q = 1; - spin_lock_init(&q->u.in.lock); - setup_storage_lists(q, irq_ptr, input_sbal_array, dbf_text, i); + setup_storage_lists(q, irq_ptr, input_sbal_array, i); input_sbal_array += QDIO_MAX_BUFFERS_PER_Q; if (is_thinint_irq(irq_ptr)) @@ -186,12 +179,11 @@ static void setup_queues(struct qdio_irq *irq_ptr, } for_each_output_queue(irq_ptr, q, i) { - sprintf(dbf_text, "outq%4x", i); + DBF_EVENT("outq:%1d", i); setup_queues_misc(q, irq_ptr, qdio_init->output_handler, i); q->is_input_q = 0; - setup_storage_lists(q, irq_ptr, output_sbal_array, - dbf_text, i); + setup_storage_lists(q, irq_ptr, output_sbal_array, i); output_sbal_array += QDIO_MAX_BUFFERS_PER_Q; tasklet_init(&q->tasklet, qdio_outbound_processing, @@ -222,8 +214,6 @@ static void process_ac_flags(struct qdio_irq *irq_ptr, unsigned char qdioac) static void check_and_setup_qebsm(struct qdio_irq *irq_ptr, unsigned char qdioac, unsigned long token) { - char dbf_text[15]; - if (!(irq_ptr->qib.rflags & QIB_RFLAGS_ENABLE_QEBSM)) goto no_qebsm; if (!(qdioac & AC1_SC_QEBSM_AVAILABLE) || @@ -232,33 +222,41 @@ static void check_and_setup_qebsm(struct qdio_irq *irq_ptr, irq_ptr->sch_token = token; - QDIO_DBF_TEXT0(0, setup, "V=V:1"); - sprintf(dbf_text, "%8lx", irq_ptr->sch_token); - QDIO_DBF_TEXT0(0, setup, dbf_text); + DBF_EVENT("V=V:1"); + DBF_EVENT("%8lx", irq_ptr->sch_token); return; no_qebsm: irq_ptr->sch_token = 0; irq_ptr->qib.rflags &= ~QIB_RFLAGS_ENABLE_QEBSM; - QDIO_DBF_TEXT0(0, setup, "noV=V"); + DBF_EVENT("noV=V"); } -static int __get_ssqd_info(struct qdio_irq *irq_ptr) +/* + * If there is a qdio_irq we use the chsc_page and store the information + * in the qdio_irq, otherwise we copy it to the specified structure. + */ +int qdio_setup_get_ssqd(struct qdio_irq *irq_ptr, + struct subchannel_id *schid, + struct qdio_ssqd_desc *data) { struct chsc_ssqd_area *ssqd; int rc; - QDIO_DBF_TEXT0(0, setup, "getssqd"); - ssqd = (struct chsc_ssqd_area *)irq_ptr->chsc_page; + DBF_EVENT("getssqd:%4x", schid->sch_no); + if (irq_ptr != NULL) + ssqd = (struct chsc_ssqd_area *)irq_ptr->chsc_page; + else + ssqd = (struct chsc_ssqd_area *)__get_free_page(GFP_KERNEL); memset(ssqd, 0, PAGE_SIZE); ssqd->request = (struct chsc_header) { .length = 0x0010, .code = 0x0024, }; - ssqd->first_sch = irq_ptr->schid.sch_no; - ssqd->last_sch = irq_ptr->schid.sch_no; - ssqd->ssid = irq_ptr->schid.ssid; + ssqd->first_sch = schid->sch_no; + ssqd->last_sch = schid->sch_no; + ssqd->ssid = schid->ssid; if (chsc(ssqd)) return -EIO; @@ -268,27 +266,29 @@ static int __get_ssqd_info(struct qdio_irq *irq_ptr) if (!(ssqd->qdio_ssqd.flags & CHSC_FLAG_QDIO_CAPABILITY) || !(ssqd->qdio_ssqd.flags & CHSC_FLAG_VALIDITY) || - (ssqd->qdio_ssqd.sch != irq_ptr->schid.sch_no)) + (ssqd->qdio_ssqd.sch != schid->sch_no)) return -EINVAL; - memcpy(&irq_ptr->ssqd_desc, &ssqd->qdio_ssqd, - sizeof(struct qdio_ssqd_desc)); + if (irq_ptr != NULL) + memcpy(&irq_ptr->ssqd_desc, &ssqd->qdio_ssqd, + sizeof(struct qdio_ssqd_desc)); + else { + memcpy(data, &ssqd->qdio_ssqd, + sizeof(struct qdio_ssqd_desc)); + free_page((unsigned long)ssqd); + } return 0; } void qdio_setup_ssqd_info(struct qdio_irq *irq_ptr) { unsigned char qdioac; - char dbf_text[15]; int rc; - rc = __get_ssqd_info(irq_ptr); + rc = qdio_setup_get_ssqd(irq_ptr, &irq_ptr->schid, NULL); if (rc) { - QDIO_DBF_TEXT2(0, setup, "ssqdasig"); - sprintf(dbf_text, "schn%4x", irq_ptr->schid.sch_no); - QDIO_DBF_TEXT2(0, setup, dbf_text); - sprintf(dbf_text, "rc:%d", rc); - QDIO_DBF_TEXT2(0, setup, dbf_text); + DBF_ERROR("%4x ssqd ERR", irq_ptr->schid.sch_no); + DBF_ERROR("rc:%x", rc); /* all flags set, worst case */ qdioac = AC1_SIGA_INPUT_NEEDED | AC1_SIGA_OUTPUT_NEEDED | AC1_SIGA_SYNC_NEEDED; @@ -297,9 +297,7 @@ void qdio_setup_ssqd_info(struct qdio_irq *irq_ptr) check_and_setup_qebsm(irq_ptr, qdioac, irq_ptr->ssqd_desc.sch_token); process_ac_flags(irq_ptr, qdioac); - - sprintf(dbf_text, "qdioac%2x", qdioac); - QDIO_DBF_TEXT2(0, setup, dbf_text); + DBF_EVENT("qdioac:%4x", qdioac); } void qdio_release_memory(struct qdio_irq *irq_ptr) @@ -419,7 +417,7 @@ int qdio_setup_irq(struct qdio_initialize *init_data) /* get qdio commands */ ciw = ccw_device_get_ciw(init_data->cdev, CIW_TYPE_EQUEUE); if (!ciw) { - QDIO_DBF_TEXT2(1, setup, "no eq"); + DBF_ERROR("%4x NO EQ", irq_ptr->schid.sch_no); rc = -EINVAL; goto out_err; } @@ -427,7 +425,7 @@ int qdio_setup_irq(struct qdio_initialize *init_data) ciw = ccw_device_get_ciw(init_data->cdev, CIW_TYPE_AQUEUE); if (!ciw) { - QDIO_DBF_TEXT2(1, setup, "no aq"); + DBF_ERROR("%4x NO AQ", irq_ptr->schid.sch_no); rc = -EINVAL; goto out_err; } @@ -447,56 +445,38 @@ void qdio_print_subchannel_info(struct qdio_irq *irq_ptr, { char s[80]; - sprintf(s, "qdio: %s ", dev_name(&cdev->dev)); - switch (irq_ptr->qib.qfmt) { - case QDIO_QETH_QFMT: - sprintf(s + strlen(s), "OSA "); - break; - case QDIO_ZFCP_QFMT: - sprintf(s + strlen(s), "ZFCP "); - break; - case QDIO_IQDIO_QFMT: - sprintf(s + strlen(s), "HS "); - break; - } - sprintf(s + strlen(s), "on SC %x using ", irq_ptr->schid.sch_no); - sprintf(s + strlen(s), "AI:%d ", is_thinint_irq(irq_ptr)); - sprintf(s + strlen(s), "QEBSM:%d ", (irq_ptr->sch_token) ? 1 : 0); - sprintf(s + strlen(s), "PCI:%d ", - (irq_ptr->qib.ac & QIB_AC_OUTBOUND_PCI_SUPPORTED) ? 1 : 0); - sprintf(s + strlen(s), "TDD:%d ", css_general_characteristics.aif_tdd); - sprintf(s + strlen(s), "SIGA:"); - sprintf(s + strlen(s), "%s", (irq_ptr->siga_flag.input) ? "R" : " "); - sprintf(s + strlen(s), "%s", (irq_ptr->siga_flag.output) ? "W" : " "); - sprintf(s + strlen(s), "%s", (irq_ptr->siga_flag.sync) ? "S" : " "); - sprintf(s + strlen(s), "%s", - (!irq_ptr->siga_flag.no_sync_ti) ? "A" : " "); - sprintf(s + strlen(s), "%s", - (!irq_ptr->siga_flag.no_sync_out_ti) ? "O" : " "); - sprintf(s + strlen(s), "%s", - (!irq_ptr->siga_flag.no_sync_out_pci) ? "P" : " "); - sprintf(s + strlen(s), "\n"); + snprintf(s, 80, "qdio: %s %s on SC %x using " + "AI:%d QEBSM:%d PCI:%d TDD:%d SIGA:%s%s%s%s%s%s\n", + dev_name(&cdev->dev), + (irq_ptr->qib.qfmt == QDIO_QETH_QFMT) ? "OSA" : + ((irq_ptr->qib.qfmt == QDIO_ZFCP_QFMT) ? "ZFCP" : "HS"), + irq_ptr->schid.sch_no, + is_thinint_irq(irq_ptr), + (irq_ptr->sch_token) ? 1 : 0, + (irq_ptr->qib.ac & QIB_AC_OUTBOUND_PCI_SUPPORTED) ? 1 : 0, + css_general_characteristics.aif_tdd, + (irq_ptr->siga_flag.input) ? "R" : " ", + (irq_ptr->siga_flag.output) ? "W" : " ", + (irq_ptr->siga_flag.sync) ? "S" : " ", + (!irq_ptr->siga_flag.no_sync_ti) ? "A" : " ", + (!irq_ptr->siga_flag.no_sync_out_ti) ? "O" : " ", + (!irq_ptr->siga_flag.no_sync_out_pci) ? "P" : " "); printk(KERN_INFO "%s", s); } int __init qdio_setup_init(void) { - char dbf_text[15]; - qdio_q_cache = kmem_cache_create("qdio_q", sizeof(struct qdio_q), 256, 0, NULL); if (!qdio_q_cache) return -ENOMEM; /* Check for OSA/FCP thin interrupts (bit 67). */ - sprintf(dbf_text, "thini%1x", - (css_general_characteristics.aif_osa) ? 1 : 0); - QDIO_DBF_TEXT0(0, setup, dbf_text); + DBF_EVENT("thinint:%1d", + (css_general_characteristics.aif_osa) ? 1 : 0); /* Check for QEBSM support in general (bit 58). */ - sprintf(dbf_text, "cssQBS:%1x", - (qebsm_possible()) ? 1 : 0); - QDIO_DBF_TEXT0(0, setup, dbf_text); + DBF_EVENT("cssQEBSM:%1d", (qebsm_possible()) ? 1 : 0); return 0; } diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c index ea7f61400267..8e90e147b746 100644 --- a/drivers/s390/cio/qdio_thinint.c +++ b/drivers/s390/cio/qdio_thinint.c @@ -125,13 +125,13 @@ void tiqdio_remove_input_queues(struct qdio_irq *irq_ptr) static inline int tiqdio_inbound_q_done(struct qdio_q *q) { - unsigned char state; + unsigned char state = 0; if (!atomic_read(&q->nr_buf_used)) return 1; qdio_siga_sync_q(q); - get_buf_state(q, q->first_to_check, &state); + get_buf_state(q, q->first_to_check, &state, 0); if (state == SLSB_P_INPUT_PRIMED) /* more work coming */ @@ -258,8 +258,6 @@ static void tiqdio_thinint_handler(void *ind, void *drv_data) static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset) { struct scssc_area *scssc_area; - char dbf_text[15]; - void *ptr; int rc; scssc_area = (struct scssc_area *)irq_ptr->chsc_page; @@ -294,19 +292,15 @@ static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset) rc = chsc_error_from_response(scssc_area->response.code); if (rc) { - sprintf(dbf_text, "sidR%4x", scssc_area->response.code); - QDIO_DBF_TEXT1(0, trace, dbf_text); - QDIO_DBF_TEXT1(0, setup, dbf_text); - ptr = &scssc_area->response; - QDIO_DBF_HEX2(1, setup, &ptr, QDIO_DBF_SETUP_LEN); + DBF_ERROR("%4x SSI r:%4x", irq_ptr->schid.sch_no, + scssc_area->response.code); + DBF_ERROR_HEX(&scssc_area->response, sizeof(void *)); return rc; } - QDIO_DBF_TEXT2(0, setup, "setscind"); - QDIO_DBF_HEX2(0, setup, &scssc_area->summary_indicator_addr, - sizeof(unsigned long)); - QDIO_DBF_HEX2(0, setup, &scssc_area->subchannel_indicator_addr, - sizeof(unsigned long)); + DBF_EVENT("setscind"); + DBF_HEX(&scssc_area->summary_indicator_addr, sizeof(unsigned long)); + DBF_HEX(&scssc_area->subchannel_indicator_addr, sizeof(unsigned long)); return 0; } @@ -327,14 +321,11 @@ void tiqdio_free_memory(void) int __init tiqdio_register_thinints(void) { - char dbf_text[20]; - isc_register(QDIO_AIRQ_ISC); tiqdio_alsi = s390_register_adapter_interrupt(&tiqdio_thinint_handler, NULL, QDIO_AIRQ_ISC); if (IS_ERR(tiqdio_alsi)) { - sprintf(dbf_text, "regthn%lx", PTR_ERR(tiqdio_alsi)); - QDIO_DBF_TEXT0(0, setup, dbf_text); + DBF_EVENT("RTI:%lx", PTR_ERR(tiqdio_alsi)); tiqdio_alsi = NULL; isc_unregister(QDIO_AIRQ_ISC); return -ENOMEM; @@ -360,7 +351,7 @@ void qdio_setup_thinint(struct qdio_irq *irq_ptr) if (!is_thinint_irq(irq_ptr)) return; irq_ptr->dsci = get_indicator(); - QDIO_DBF_HEX1(0, setup, &irq_ptr->dsci, sizeof(void *)); + DBF_HEX(&irq_ptr->dsci, sizeof(void *)); } void qdio_shutdown_thinint(struct qdio_irq *irq_ptr) diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index e3fe6838293a..1f5f5d2d87d9 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -5,6 +5,7 @@ * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> * Martin Schwidefsky <schwidefsky@de.ibm.com> * Ralph Wuerthner <rwuerthn@de.ibm.com> + * Felix Beck <felix.beck@de.ibm.com> * * Adjunct processor bus. * @@ -23,6 +24,9 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#define KMSG_COMPONENT "ap" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include <linux/module.h> #include <linux/init.h> #include <linux/delay.h> @@ -34,6 +38,10 @@ #include <linux/mutex.h> #include <asm/s390_rdev.h> #include <asm/reset.h> +#include <asm/airq.h> +#include <asm/atomic.h> +#include <asm/system.h> +#include <asm/isc.h> #include <linux/hrtimer.h> #include <linux/ktime.h> @@ -46,6 +54,7 @@ static enum hrtimer_restart ap_poll_timeout(struct hrtimer *); static int ap_poll_thread_start(void); static void ap_poll_thread_stop(void); static void ap_request_timeout(unsigned long); +static inline void ap_schedule_poll_timer(void); /* * Module description. @@ -68,7 +77,7 @@ module_param_named(poll_thread, ap_thread_flag, int, 0000); MODULE_PARM_DESC(poll_thread, "Turn on/off poll thread, default is 0 (off)."); static struct device *ap_root_device = NULL; -static DEFINE_SPINLOCK(ap_device_lock); +static DEFINE_SPINLOCK(ap_device_list_lock); static LIST_HEAD(ap_device_list); /* @@ -80,19 +89,29 @@ static int ap_config_time = AP_CONFIG_TIME; static DECLARE_WORK(ap_config_work, ap_scan_bus); /* - * Tasklet & timer for AP request polling. + * Tasklet & timer for AP request polling and interrupts */ static DECLARE_TASKLET(ap_tasklet, ap_poll_all, 0); static atomic_t ap_poll_requests = ATOMIC_INIT(0); static DECLARE_WAIT_QUEUE_HEAD(ap_poll_wait); static struct task_struct *ap_poll_kthread = NULL; static DEFINE_MUTEX(ap_poll_thread_mutex); +static void *ap_interrupt_indicator; static struct hrtimer ap_poll_timer; /* In LPAR poll with 4kHz frequency. Poll every 250000 nanoseconds. * If z/VM change to 1500000 nanoseconds to adjust to z/VM polling.*/ static unsigned long long poll_timeout = 250000; /** + * ap_using_interrupts() - Returns non-zero if interrupt support is + * available. + */ +static inline int ap_using_interrupts(void) +{ + return ap_interrupt_indicator != NULL; +} + +/** * ap_intructions_available() - Test if AP instructions are available. * * Returns 0 if the AP instructions are installed. @@ -113,6 +132,23 @@ static inline int ap_instructions_available(void) } /** + * ap_interrupts_available(): Test if AP interrupts are available. + * + * Returns 1 if AP interrupts are available. + */ +static int ap_interrupts_available(void) +{ + unsigned long long facility_bits[2]; + + if (stfle(facility_bits, 2) <= 1) + return 0; + if (!(facility_bits[0] & (1ULL << 61)) || + !(facility_bits[1] & (1ULL << 62))) + return 0; + return 1; +} + +/** * ap_test_queue(): Test adjunct processor queue. * @qid: The AP queue number * @queue_depth: Pointer to queue depth value @@ -152,6 +188,80 @@ static inline struct ap_queue_status ap_reset_queue(ap_qid_t qid) return reg1; } +#ifdef CONFIG_64BIT +/** + * ap_queue_interruption_control(): Enable interruption for a specific AP. + * @qid: The AP queue number + * @ind: The notification indicator byte + * + * Returns AP queue status. + */ +static inline struct ap_queue_status +ap_queue_interruption_control(ap_qid_t qid, void *ind) +{ + register unsigned long reg0 asm ("0") = qid | 0x03000000UL; + register unsigned long reg1_in asm ("1") = 0x0000800000000000UL | AP_ISC; + register struct ap_queue_status reg1_out asm ("1"); + register void *reg2 asm ("2") = ind; + asm volatile( + ".long 0xb2af0000" /* PQAP(RAPQ) */ + : "+d" (reg0), "+d" (reg1_in), "=d" (reg1_out), "+d" (reg2) + : + : "cc" ); + return reg1_out; +} +#endif + +/** + * ap_queue_enable_interruption(): Enable interruption on an AP. + * @qid: The AP queue number + * @ind: the notification indicator byte + * + * Enables interruption on AP queue via ap_queue_interruption_control(). Based + * on the return value it waits a while and tests the AP queue if interrupts + * have been switched on using ap_test_queue(). + */ +static int ap_queue_enable_interruption(ap_qid_t qid, void *ind) +{ +#ifdef CONFIG_64BIT + struct ap_queue_status status; + int t_depth, t_device_type, rc, i; + + rc = -EBUSY; + status = ap_queue_interruption_control(qid, ind); + + for (i = 0; i < AP_MAX_RESET; i++) { + switch (status.response_code) { + case AP_RESPONSE_NORMAL: + if (status.int_enabled) + return 0; + break; + case AP_RESPONSE_RESET_IN_PROGRESS: + case AP_RESPONSE_BUSY: + break; + case AP_RESPONSE_Q_NOT_AVAIL: + case AP_RESPONSE_DECONFIGURED: + case AP_RESPONSE_CHECKSTOPPED: + case AP_RESPONSE_INVALID_ADDRESS: + return -ENODEV; + case AP_RESPONSE_OTHERWISE_CHANGED: + if (status.int_enabled) + return 0; + break; + default: + break; + } + if (i < AP_MAX_RESET - 1) { + udelay(5); + status = ap_test_queue(qid, &t_depth, &t_device_type); + } + } + return rc; +#else + return -EINVAL; +#endif +} + /** * __ap_send(): Send message to adjunct processor queue. * @qid: The AP queue number @@ -295,6 +405,11 @@ static int ap_query_queue(ap_qid_t qid, int *queue_depth, int *device_type) case AP_RESPONSE_CHECKSTOPPED: rc = -ENODEV; break; + case AP_RESPONSE_INVALID_ADDRESS: + rc = -ENODEV; + break; + case AP_RESPONSE_OTHERWISE_CHANGED: + break; case AP_RESPONSE_BUSY: break; default: @@ -345,6 +460,15 @@ static int ap_init_queue(ap_qid_t qid) status = ap_test_queue(qid, &dummy, &dummy); } } + if (rc == 0 && ap_using_interrupts()) { + rc = ap_queue_enable_interruption(qid, ap_interrupt_indicator); + /* If interruption mode is supported by the machine, + * but an AP can not be enabled for interruption then + * the AP will be discarded. */ + if (rc) + pr_err("Registering adapter interrupts for " + "AP %d failed\n", AP_QID_DEVICE(qid)); + } return rc; } @@ -397,16 +521,16 @@ static ssize_t ap_hwtype_show(struct device *dev, struct ap_device *ap_dev = to_ap_dev(dev); return snprintf(buf, PAGE_SIZE, "%d\n", ap_dev->device_type); } -static DEVICE_ATTR(hwtype, 0444, ap_hwtype_show, NULL); +static DEVICE_ATTR(hwtype, 0444, ap_hwtype_show, NULL); static ssize_t ap_depth_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ap_device *ap_dev = to_ap_dev(dev); return snprintf(buf, PAGE_SIZE, "%d\n", ap_dev->queue_depth); } -static DEVICE_ATTR(depth, 0444, ap_depth_show, NULL); +static DEVICE_ATTR(depth, 0444, ap_depth_show, NULL); static ssize_t ap_request_count_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -509,9 +633,9 @@ static int ap_device_probe(struct device *dev) ap_dev->drv = ap_drv; rc = ap_drv->probe ? ap_drv->probe(ap_dev) : -ENODEV; if (!rc) { - spin_lock_bh(&ap_device_lock); + spin_lock_bh(&ap_device_list_lock); list_add(&ap_dev->list, &ap_device_list); - spin_unlock_bh(&ap_device_lock); + spin_unlock_bh(&ap_device_list_lock); } return rc; } @@ -553,9 +677,9 @@ static int ap_device_remove(struct device *dev) ap_flush_queue(ap_dev); del_timer_sync(&ap_dev->timeout); - spin_lock_bh(&ap_device_lock); + spin_lock_bh(&ap_device_list_lock); list_del_init(&ap_dev->list); - spin_unlock_bh(&ap_device_lock); + spin_unlock_bh(&ap_device_list_lock); if (ap_drv->remove) ap_drv->remove(ap_dev); spin_lock_bh(&ap_dev->lock); @@ -599,6 +723,14 @@ static ssize_t ap_config_time_show(struct bus_type *bus, char *buf) return snprintf(buf, PAGE_SIZE, "%d\n", ap_config_time); } +static ssize_t ap_interrupts_show(struct bus_type *bus, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", + ap_using_interrupts() ? 1 : 0); +} + +static BUS_ATTR(ap_interrupts, 0444, ap_interrupts_show, NULL); + static ssize_t ap_config_time_store(struct bus_type *bus, const char *buf, size_t count) { @@ -653,7 +785,8 @@ static ssize_t poll_timeout_store(struct bus_type *bus, const char *buf, ktime_t hr_time; /* 120 seconds = maximum poll interval */ - if (sscanf(buf, "%llu\n", &time) != 1 || time < 1 || time > 120000000000) + if (sscanf(buf, "%llu\n", &time) != 1 || time < 1 || + time > 120000000000ULL) return -EINVAL; poll_timeout = time; hr_time = ktime_set(0, poll_timeout); @@ -672,6 +805,7 @@ static struct bus_attribute *const ap_bus_attrs[] = { &bus_attr_ap_domain, &bus_attr_config_time, &bus_attr_poll_thread, + &bus_attr_ap_interrupts, &bus_attr_poll_timeout, NULL, }; @@ -814,6 +948,11 @@ out: return rc; } +static void ap_interrupt_handler(void *unused1, void *unused2) +{ + tasklet_schedule(&ap_tasklet); +} + /** * __ap_scan_bus(): Scan the AP bus. * @dev: Pointer to device @@ -928,6 +1067,8 @@ ap_config_timeout(unsigned long ptr) */ static inline void ap_schedule_poll_timer(void) { + if (ap_using_interrupts()) + return; if (hrtimer_is_queued(&ap_poll_timer)) return; hrtimer_start(&ap_poll_timer, ktime_set(0, poll_timeout), @@ -1181,7 +1322,7 @@ static void ap_reset(struct ap_device *ap_dev) ap_dev->unregistered = 1; } -static int __ap_poll_all(struct ap_device *ap_dev, unsigned long *flags) +static int __ap_poll_device(struct ap_device *ap_dev, unsigned long *flags) { spin_lock(&ap_dev->lock); if (!ap_dev->unregistered) { @@ -1207,13 +1348,19 @@ static void ap_poll_all(unsigned long dummy) unsigned long flags; struct ap_device *ap_dev; + /* Reset the indicator if interrupts are used. Thus new interrupts can + * be received. Doing it in the beginning of the tasklet is therefor + * important that no requests on any AP get lost. + */ + if (ap_using_interrupts()) + xchg((u8 *)ap_interrupt_indicator, 0); do { flags = 0; - spin_lock(&ap_device_lock); + spin_lock(&ap_device_list_lock); list_for_each_entry(ap_dev, &ap_device_list, list) { - __ap_poll_all(ap_dev, &flags); + __ap_poll_device(ap_dev, &flags); } - spin_unlock(&ap_device_lock); + spin_unlock(&ap_device_list_lock); } while (flags & 1); if (flags & 2) ap_schedule_poll_timer(); @@ -1253,11 +1400,11 @@ static int ap_poll_thread(void *data) remove_wait_queue(&ap_poll_wait, &wait); flags = 0; - spin_lock_bh(&ap_device_lock); + spin_lock_bh(&ap_device_list_lock); list_for_each_entry(ap_dev, &ap_device_list, list) { - __ap_poll_all(ap_dev, &flags); + __ap_poll_device(ap_dev, &flags); } - spin_unlock_bh(&ap_device_lock); + spin_unlock_bh(&ap_device_list_lock); } set_current_state(TASK_RUNNING); remove_wait_queue(&ap_poll_wait, &wait); @@ -1268,6 +1415,8 @@ static int ap_poll_thread_start(void) { int rc; + if (ap_using_interrupts()) + return 0; mutex_lock(&ap_poll_thread_mutex); if (!ap_poll_kthread) { ap_poll_kthread = kthread_run(ap_poll_thread, NULL, "appoll"); @@ -1301,8 +1450,12 @@ static void ap_request_timeout(unsigned long data) { struct ap_device *ap_dev = (struct ap_device *) data; - if (ap_dev->reset == AP_RESET_ARMED) + if (ap_dev->reset == AP_RESET_ARMED) { ap_dev->reset = AP_RESET_DO; + + if (ap_using_interrupts()) + tasklet_schedule(&ap_tasklet); + } } static void ap_reset_domain(void) @@ -1337,14 +1490,25 @@ int __init ap_module_init(void) int rc, i; if (ap_domain_index < -1 || ap_domain_index >= AP_DOMAINS) { - printk(KERN_WARNING "Invalid param: domain = %d. " - " Not loading.\n", ap_domain_index); + pr_warning("%d is not a valid cryptographic domain\n", + ap_domain_index); return -EINVAL; } if (ap_instructions_available() != 0) { - printk(KERN_WARNING "AP instructions not installed.\n"); + pr_warning("The hardware system does not support " + "AP instructions\n"); return -ENODEV; } + if (ap_interrupts_available()) { + isc_register(AP_ISC); + ap_interrupt_indicator = s390_register_adapter_interrupt( + &ap_interrupt_handler, NULL, AP_ISC); + if (IS_ERR(ap_interrupt_indicator)) { + ap_interrupt_indicator = NULL; + isc_unregister(AP_ISC); + } + } + register_reset_call(&ap_reset_call); /* Create /sys/bus/ap. */ @@ -1408,6 +1572,10 @@ out_bus: bus_unregister(&ap_bus_type); out: unregister_reset_call(&ap_reset_call); + if (ap_using_interrupts()) { + s390_unregister_adapter_interrupt(ap_interrupt_indicator, AP_ISC); + isc_unregister(AP_ISC); + } return rc; } @@ -1443,6 +1611,10 @@ void ap_module_exit(void) bus_remove_file(&ap_bus_type, ap_bus_attrs[i]); bus_unregister(&ap_bus_type); unregister_reset_call(&ap_reset_call); + if (ap_using_interrupts()) { + s390_unregister_adapter_interrupt(ap_interrupt_indicator, AP_ISC); + isc_unregister(AP_ISC); + } } #ifndef CONFIG_ZCRYPT_MONOLITHIC diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h index 446378b308fc..a35362241805 100644 --- a/drivers/s390/crypto/ap_bus.h +++ b/drivers/s390/crypto/ap_bus.h @@ -5,6 +5,7 @@ * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> * Martin Schwidefsky <schwidefsky@de.ibm.com> * Ralph Wuerthner <rwuerthn@de.ibm.com> + * Felix Beck <felix.beck@de.ibm.com> * * Adjunct processor bus header file. * @@ -67,7 +68,8 @@ struct ap_queue_status { unsigned int queue_empty : 1; unsigned int replies_waiting : 1; unsigned int queue_full : 1; - unsigned int pad1 : 5; + unsigned int pad1 : 4; + unsigned int int_enabled : 1; unsigned int response_code : 8; unsigned int pad2 : 16; }; @@ -78,6 +80,8 @@ struct ap_queue_status { #define AP_RESPONSE_DECONFIGURED 0x03 #define AP_RESPONSE_CHECKSTOPPED 0x04 #define AP_RESPONSE_BUSY 0x05 +#define AP_RESPONSE_INVALID_ADDRESS 0x06 +#define AP_RESPONSE_OTHERWISE_CHANGED 0x07 #define AP_RESPONSE_Q_FULL 0x10 #define AP_RESPONSE_NO_PENDING_REPLY 0x10 #define AP_RESPONSE_INDEX_TOO_BIG 0x11 diff --git a/drivers/s390/kvm/kvm_virtio.c b/drivers/s390/kvm/kvm_virtio.c index ff4a6931bb8e..3d442444c618 100644 --- a/drivers/s390/kvm/kvm_virtio.c +++ b/drivers/s390/kvm/kvm_virtio.c @@ -322,13 +322,13 @@ static int __init kvm_devices_init(void) return rc; } - rc = vmem_add_mapping(PFN_PHYS(max_pfn), PAGE_SIZE); + rc = vmem_add_mapping(real_memory_size, PAGE_SIZE); if (rc) { s390_root_dev_unregister(kvm_root); return rc; } - kvm_devices = (void *) PFN_PHYS(max_pfn); + kvm_devices = (void *) real_memory_size; ctl_set_bit(0, 9); register_external_interrupt(0x2603, kvm_extint_handler); diff --git a/drivers/s390/net/ctcm_fsms.c b/drivers/s390/net/ctcm_fsms.c index 42776550acfd..f29c7086fc19 100644 --- a/drivers/s390/net/ctcm_fsms.c +++ b/drivers/s390/net/ctcm_fsms.c @@ -13,6 +13,9 @@ #undef DEBUGDATA #undef DEBUGCCW +#define KMSG_COMPONENT "ctcm" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include <linux/module.h> #include <linux/init.h> #include <linux/kernel.h> @@ -190,21 +193,22 @@ static void ctcmpc_chx_send_sweep(fsm_instance *fsm, int event, void *arg); void ctcm_ccw_check_rc(struct channel *ch, int rc, char *msg) { CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR, - "%s(%s): %s: %04x\n", - CTCM_FUNTAIL, ch->id, msg, rc); + "%s(%s): %s: %04x\n", + CTCM_FUNTAIL, ch->id, msg, rc); switch (rc) { case -EBUSY: - ctcm_pr_warn("%s (%s): Busy !\n", ch->id, msg); + pr_info("%s: The communication peer is busy\n", + ch->id); fsm_event(ch->fsm, CTC_EVENT_IO_EBUSY, ch); break; case -ENODEV: - ctcm_pr_emerg("%s (%s): Invalid device called for IO\n", - ch->id, msg); + pr_err("%s: The specified target device is not valid\n", + ch->id); fsm_event(ch->fsm, CTC_EVENT_IO_ENODEV, ch); break; default: - ctcm_pr_emerg("%s (%s): Unknown error in do_IO %04x\n", - ch->id, msg, rc); + pr_err("An I/O operation resulted in error %04x\n", + rc); fsm_event(ch->fsm, CTC_EVENT_IO_UNKNOWN, ch); } } @@ -886,8 +890,15 @@ static void ctcm_chx_rxiniterr(fsm_instance *fi, int event, void *arg) fsm_newstate(fi, CTC_STATE_RXERR); fsm_event(priv->fsm, DEV_EVENT_RXDOWN, dev); } - } else - ctcm_pr_warn("%s: Error during RX init handshake\n", dev->name); + } else { + CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR, + "%s(%s): %s in %s", CTCM_FUNTAIL, ch->id, + ctc_ch_event_names[event], fsm_getstate_str(fi)); + + dev_warn(&dev->dev, + "Initialization failed with RX/TX init handshake " + "error %s\n", ctc_ch_event_names[event]); + } } /** @@ -969,7 +980,9 @@ static void ctcm_chx_txiniterr(fsm_instance *fi, int event, void *arg) "%s(%s): %s in %s", CTCM_FUNTAIL, ch->id, ctc_ch_event_names[event], fsm_getstate_str(fi)); - ctcm_pr_warn("%s: Error during TX init handshake\n", dev->name); + dev_warn(&dev->dev, + "Initialization failed with RX/TX init handshake " + "error %s\n", ctc_ch_event_names[event]); } } @@ -2101,14 +2114,11 @@ static void dev_action_restart(fsm_instance *fi, int event, void *arg) CTCMY_DBF_DEV_NAME(TRACE, dev, ""); if (IS_MPC(priv)) { - ctcm_pr_info("ctcm: %s Restarting Device and " - "MPC Group in 5 seconds\n", - dev->name); restart_timer = CTCM_TIME_1_SEC; } else { - ctcm_pr_info("%s: Restarting\n", dev->name); restart_timer = CTCM_TIME_5_SEC; } + dev_info(&dev->dev, "Restarting device\n"); dev_action_stop(fi, event, arg); fsm_event(priv->fsm, DEV_EVENT_STOP, dev); @@ -2150,16 +2160,16 @@ static void dev_action_chup(fsm_instance *fi, int event, void *arg) case DEV_STATE_STARTWAIT_RX: if (event == DEV_EVENT_RXUP) { fsm_newstate(fi, DEV_STATE_RUNNING); - ctcm_pr_info("%s: connected with remote side\n", - dev->name); + dev_info(&dev->dev, + "Connected with remote side\n"); ctcm_clear_busy(dev); } break; case DEV_STATE_STARTWAIT_TX: if (event == DEV_EVENT_TXUP) { fsm_newstate(fi, DEV_STATE_RUNNING); - ctcm_pr_info("%s: connected with remote side\n", - dev->name); + dev_info(&dev->dev, + "Connected with remote side\n"); ctcm_clear_busy(dev); } break; diff --git a/drivers/s390/net/ctcm_main.c b/drivers/s390/net/ctcm_main.c index a4e29836a2aa..2678573becec 100644 --- a/drivers/s390/net/ctcm_main.c +++ b/drivers/s390/net/ctcm_main.c @@ -21,6 +21,9 @@ #undef DEBUGDATA #undef DEBUGCCW +#define KMSG_COMPONENT "ctcm" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include <linux/module.h> #include <linux/init.h> #include <linux/kernel.h> @@ -281,14 +284,16 @@ static long ctcm_check_irb_error(struct ccw_device *cdev, struct irb *irb) switch (PTR_ERR(irb)) { case -EIO: - ctcm_pr_warn("i/o-error on device %s\n", dev_name(&cdev->dev)); + dev_err(&cdev->dev, + "An I/O-error occurred on the CTCM device\n"); break; case -ETIMEDOUT: - ctcm_pr_warn("timeout on device %s\n", dev_name(&cdev->dev)); + dev_err(&cdev->dev, + "An adapter hardware operation timed out\n"); break; default: - ctcm_pr_warn("unknown error %ld on device %s\n", - PTR_ERR(irb), dev_name(&cdev->dev)); + dev_err(&cdev->dev, + "An error occurred on the adapter hardware\n"); } return PTR_ERR(irb); } @@ -309,15 +314,17 @@ static inline void ccw_unit_check(struct channel *ch, __u8 sense) if (sense & SNS0_INTERVENTION_REQ) { if (sense & 0x01) { if (ch->sense_rc != 0x01) { - ctcm_pr_debug("%s: Interface disc. or Sel. " - "reset (remote)\n", ch->id); + pr_notice( + "%s: The communication peer has " + "disconnected\n", ch->id); ch->sense_rc = 0x01; } fsm_event(ch->fsm, CTC_EVENT_UC_RCRESET, ch); } else { if (ch->sense_rc != SNS0_INTERVENTION_REQ) { - ctcm_pr_debug("%s: System reset (remote)\n", - ch->id); + pr_notice( + "%s: The remote operating system is " + "not available\n", ch->id); ch->sense_rc = SNS0_INTERVENTION_REQ; } fsm_event(ch->fsm, CTC_EVENT_UC_RSRESET, ch); @@ -1194,8 +1201,11 @@ static void ctcm_irq_handler(struct ccw_device *cdev, /* Check for unsolicited interrupts. */ if (cgdev == NULL) { - ctcm_pr_warn("ctcm: Got unsolicited irq: c-%02x d-%02x\n", - cstat, dstat); + CTCM_DBF_TEXT_(TRACE, CTC_DBF_ERROR, + "%s(%s) unsolicited irq: c-%02x d-%02x\n", + CTCM_FUNTAIL, dev_name(&cdev->dev), cstat, dstat); + dev_warn(&cdev->dev, + "The adapter received a non-specific IRQ\n"); return; } @@ -1207,31 +1217,34 @@ static void ctcm_irq_handler(struct ccw_device *cdev, else if (priv->channel[WRITE]->cdev == cdev) ch = priv->channel[WRITE]; else { - ctcm_pr_err("ctcm: Can't determine channel for interrupt, " - "device %s\n", dev_name(&cdev->dev)); + dev_err(&cdev->dev, + "%s: Internal error: Can't determine channel for " + "interrupt device %s\n", + __func__, dev_name(&cdev->dev)); + /* Explain: inconsistent internal structures */ return; } dev = ch->netdev; if (dev == NULL) { - ctcm_pr_crit("ctcm: %s dev=NULL bus_id=%s, ch=0x%p\n", - __func__, dev_name(&cdev->dev), ch); + dev_err(&cdev->dev, + "%s Internal error: net_device is NULL, ch = 0x%p\n", + __func__, ch); + /* Explain: inconsistent internal structures */ return; } - CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG, - "%s(%s): int. for %s: cstat=%02x dstat=%02x", - CTCM_FUNTAIL, dev->name, ch->id, cstat, dstat); - /* Copy interruption response block. */ memcpy(ch->irb, irb, sizeof(struct irb)); + /* Issue error message and return on subchannel error code */ if (irb->scsw.cmd.cstat) { - /* Check for good subchannel return code, otherwise error message */ fsm_event(ch->fsm, CTC_EVENT_SC_UNKNOWN, ch); - ctcm_pr_warn("%s: subchannel check for dev: %s - %02x %02x\n", - dev->name, ch->id, irb->scsw.cmd.cstat, - irb->scsw.cmd.dstat); + CTCM_DBF_TEXT_(TRACE, CTC_DBF_WARN, + "%s(%s): sub-ch check %s: cs=%02x ds=%02x", + CTCM_FUNTAIL, dev->name, ch->id, cstat, dstat); + dev_warn(&cdev->dev, + "A check occurred on the subchannel\n"); return; } @@ -1239,7 +1252,7 @@ static void ctcm_irq_handler(struct ccw_device *cdev, if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) { if ((irb->ecw[0] & ch->sense_rc) == 0) /* print it only once */ - CTCM_DBF_TEXT_(TRACE, CTC_DBF_INFO, + CTCM_DBF_TEXT_(TRACE, CTC_DBF_WARN, "%s(%s): sense=%02x, ds=%02x", CTCM_FUNTAIL, ch->id, irb->ecw[0], dstat); ccw_unit_check(ch, irb->ecw[0]); @@ -1574,6 +1587,11 @@ static int ctcm_new_device(struct ccwgroup_device *cgdev) strlcpy(priv->fsm->name, dev->name, sizeof(priv->fsm->name)); + dev_info(&dev->dev, + "setup OK : r/w = %s/%s, protocol : %d\n", + priv->channel[READ]->id, + priv->channel[WRITE]->id, priv->protocol); + CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO, "setup(%s) OK : r/w = %s/%s, protocol : %d", dev->name, priv->channel[READ]->id, @@ -1687,7 +1705,7 @@ static void __exit ctcm_exit(void) { unregister_cu3088_discipline(&ctcm_group_driver); ctcm_unregister_dbf_views(); - ctcm_pr_info("CTCM driver unloaded\n"); + pr_info("CTCM driver unloaded\n"); } /* @@ -1695,7 +1713,7 @@ static void __exit ctcm_exit(void) */ static void print_banner(void) { - printk(KERN_INFO "CTCM driver initialized\n"); + pr_info("CTCM driver initialized\n"); } /** @@ -1717,8 +1735,8 @@ static int __init ctcm_init(void) ret = register_cu3088_discipline(&ctcm_group_driver); if (ret) { ctcm_unregister_dbf_views(); - ctcm_pr_crit("ctcm_init failed with register_cu3088_discipline " - "(rc = %d)\n", ret); + pr_err("%s / register_cu3088_discipline failed, ret = %d\n", + __func__, ret); return ret; } print_banner(); diff --git a/drivers/s390/net/ctcm_main.h b/drivers/s390/net/ctcm_main.h index d77cce3fe4d4..d925e732b7d8 100644 --- a/drivers/s390/net/ctcm_main.h +++ b/drivers/s390/net/ctcm_main.h @@ -41,12 +41,6 @@ #define LOG_FLAG_NOMEM 8 #define ctcm_pr_debug(fmt, arg...) printk(KERN_DEBUG fmt, ##arg) -#define ctcm_pr_info(fmt, arg...) printk(KERN_INFO fmt, ##arg) -#define ctcm_pr_notice(fmt, arg...) printk(KERN_NOTICE fmt, ##arg) -#define ctcm_pr_warn(fmt, arg...) printk(KERN_WARNING fmt, ##arg) -#define ctcm_pr_emerg(fmt, arg...) printk(KERN_EMERG fmt, ##arg) -#define ctcm_pr_err(fmt, arg...) printk(KERN_ERR fmt, ##arg) -#define ctcm_pr_crit(fmt, arg...) printk(KERN_CRIT fmt, ##arg) #define CTCM_PR_DEBUG(fmt, arg...) \ do { \ diff --git a/drivers/s390/net/ctcm_mpc.c b/drivers/s390/net/ctcm_mpc.c index 19f5d5ed85e0..3db5f846bbf6 100644 --- a/drivers/s390/net/ctcm_mpc.c +++ b/drivers/s390/net/ctcm_mpc.c @@ -19,6 +19,9 @@ #undef DEBUGDATA #undef DEBUGCCW +#define KMSG_COMPONENT "ctcm" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include <linux/module.h> #include <linux/init.h> #include <linux/kernel.h> @@ -386,7 +389,7 @@ int ctc_mpc_alloc_channel(int port_num, void (*callback)(int, int)) if (grp->allocchan_callback_retries < 4) { if (grp->allochanfunc) grp->allochanfunc(grp->port_num, - grp->group_max_buflen); + grp->group_max_buflen); } else { /* there are problems...bail out */ /* there may be a state mismatch so restart */ @@ -1232,8 +1235,9 @@ done: dev_kfree_skb_any(pskb); if (sendrc == NET_RX_DROP) { - printk(KERN_WARNING "%s %s() NETWORK BACKLOG EXCEEDED" - " - PACKET DROPPED\n", dev->name, __func__); + dev_warn(&dev->dev, + "The network backlog for %s is exceeded, " + "package dropped\n", __func__); fsm_event(grp->fsm, MPCG_EVENT_INOP, dev); } @@ -1670,10 +1674,11 @@ static int mpc_validate_xid(struct mpcg_info *mpcginfo) CTCM_FUNTAIL, ch->id); } } - done: if (rc) { - ctcm_pr_info("ctcmpc : %s() failed\n", __func__); + dev_warn(&dev->dev, + "The XID used in the MPC protocol is not valid, " + "rc = %d\n", rc); priv->xid->xid2_flag2 = 0x40; grp->saved_xid2->xid2_flag2 = 0x40; } diff --git a/drivers/s390/net/ctcm_sysfs.c b/drivers/s390/net/ctcm_sysfs.c index bb2d13721d34..8452bb052d68 100644 --- a/drivers/s390/net/ctcm_sysfs.c +++ b/drivers/s390/net/ctcm_sysfs.c @@ -10,6 +10,9 @@ #undef DEBUGDATA #undef DEBUGCCW +#define KMSG_COMPONENT "ctcm" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include <linux/sysfs.h> #include "ctcm_main.h" diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c index 0825be87e5a0..afa8337734d1 100644 --- a/drivers/s390/net/lcs.c +++ b/drivers/s390/net/lcs.c @@ -26,6 +26,9 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#define KMSG_COMPONENT "lcs" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include <linux/module.h> #include <linux/if.h> #include <linux/netdevice.h> @@ -54,8 +57,6 @@ #error Cannot compile lcs.c without some net devices switched on. #endif -#define PRINTK_HEADER " lcs: " - /** * initialization string for output */ @@ -96,7 +97,7 @@ lcs_register_debug_facility(void) lcs_dbf_setup = debug_register("lcs_setup", 2, 1, 8); lcs_dbf_trace = debug_register("lcs_trace", 4, 1, 8); if (lcs_dbf_setup == NULL || lcs_dbf_trace == NULL) { - PRINT_ERR("Not enough memory for debug facility.\n"); + pr_err("Not enough memory for debug facility.\n"); lcs_unregister_debug_facility(); return -ENOMEM; } @@ -503,7 +504,9 @@ lcs_start_channel(struct lcs_channel *channel) if (rc) { LCS_DBF_TEXT_(4,trace,"essh%s", dev_name(&channel->ccwdev->dev)); - PRINT_ERR("Error in starting channel, rc=%d!\n", rc); + dev_err(&channel->ccwdev->dev, + "Starting an LCS device resulted in an error," + " rc=%d!\n", rc); } return rc; } @@ -640,7 +643,9 @@ __lcs_resume_channel(struct lcs_channel *channel) if (rc) { LCS_DBF_TEXT_(4, trace, "ersc%s", dev_name(&channel->ccwdev->dev)); - PRINT_ERR("Error in lcs_resume_channel: rc=%d\n",rc); + dev_warn(&channel->ccwdev->dev, + "Sending data from the LCS device to the LAN failed" + " with rc=%d\n",rc); } else channel->state = LCS_CH_STATE_RUNNING; return rc; @@ -1086,7 +1091,7 @@ lcs_check_multicast_support(struct lcs_card *card) cmd->cmd.lcs_qipassist.num_ip_pairs = 1; rc = lcs_send_lancmd(card, buffer, __lcs_check_multicast_cb); if (rc != 0) { - PRINT_ERR("Query IPAssist failed. Assuming unsupported!\n"); + pr_err("Query IPAssist failed. Assuming unsupported!\n"); return -EOPNOTSUPP; } if (card->ip_assists_supported & LCS_IPASS_MULTICAST_SUPPORT) @@ -1119,8 +1124,8 @@ list_modified: rc = lcs_send_setipm(card, ipm); spin_lock_irqsave(&card->ipm_lock, flags); if (rc) { - PRINT_INFO("Adding multicast address failed. " - "Table possibly full!\n"); + pr_info("Adding multicast address failed." + " Table possibly full!\n"); /* store ipm in failed list -> will be added * to ipm_list again, so a retry will be done * during the next call of this function */ @@ -1231,8 +1236,8 @@ lcs_set_mc_addresses(struct lcs_card *card, struct in_device *in4_dev) ipm = (struct lcs_ipm_list *) kzalloc(sizeof(struct lcs_ipm_list), GFP_ATOMIC); if (ipm == NULL) { - PRINT_INFO("Not enough memory to add " - "new multicast entry!\n"); + pr_info("Not enough memory to add" + " new multicast entry!\n"); break; } memcpy(&ipm->ipm.mac_addr, buf, LCS_MAC_LENGTH); @@ -1306,18 +1311,21 @@ lcs_check_irb_error(struct ccw_device *cdev, struct irb *irb) switch (PTR_ERR(irb)) { case -EIO: - PRINT_WARN("i/o-error on device %s\n", dev_name(&cdev->dev)); + dev_warn(&cdev->dev, + "An I/O-error occurred on the LCS device\n"); LCS_DBF_TEXT(2, trace, "ckirberr"); LCS_DBF_TEXT_(2, trace, " rc%d", -EIO); break; case -ETIMEDOUT: - PRINT_WARN("timeout on device %s\n", dev_name(&cdev->dev)); + dev_warn(&cdev->dev, + "A command timed out on the LCS device\n"); LCS_DBF_TEXT(2, trace, "ckirberr"); LCS_DBF_TEXT_(2, trace, " rc%d", -ETIMEDOUT); break; default: - PRINT_WARN("unknown error %ld on device %s\n", PTR_ERR(irb), - dev_name(&cdev->dev)); + dev_warn(&cdev->dev, + "An error occurred on the LCS device, rc=%ld\n", + PTR_ERR(irb)); LCS_DBF_TEXT(2, trace, "ckirberr"); LCS_DBF_TEXT(2, trace, " rc???"); } @@ -1403,8 +1411,10 @@ lcs_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb) /* Check for channel and device errors presented */ rc = lcs_get_problem(cdev, irb); if (rc || (dstat & DEV_STAT_UNIT_EXCEP)) { - PRINT_WARN("check on device %s, dstat=0x%X, cstat=0x%X \n", - dev_name(&cdev->dev), dstat, cstat); + dev_warn(&cdev->dev, + "The LCS device stopped because of an error," + " dstat=0x%X, cstat=0x%X \n", + dstat, cstat); if (rc) { channel->state = LCS_CH_STATE_ERROR; } @@ -1761,8 +1771,8 @@ lcs_get_control(struct lcs_card *card, struct lcs_cmd *cmd) lcs_schedule_recovery(card); break; case LCS_CMD_STOPLAN: - PRINT_WARN("Stoplan for %s initiated by LGW.\n", - card->dev->name); + pr_warning("Stoplan for %s initiated by LGW.\n", + card->dev->name); if (card->dev) netif_carrier_off(card->dev); break; @@ -1790,7 +1800,8 @@ lcs_get_skb(struct lcs_card *card, char *skb_data, unsigned int skb_len) skb = dev_alloc_skb(skb_len); if (skb == NULL) { - PRINT_ERR("LCS: alloc_skb failed for device=%s\n", + dev_err(&card->dev->dev, + " Allocating a socket buffer to interface %s failed\n", card->dev->name); card->stats.rx_dropped++; return; @@ -1886,7 +1897,8 @@ lcs_stop_device(struct net_device *dev) (card->write.state != LCS_CH_STATE_RUNNING)); rc = lcs_stopcard(card); if (rc) - PRINT_ERR("Try it again!\n "); + dev_err(&card->dev->dev, + " Shutting down the LCS device failed\n "); return rc; } @@ -1905,7 +1917,7 @@ lcs_open_device(struct net_device *dev) /* initialize statistics */ rc = lcs_detect(card); if (rc) { - PRINT_ERR("LCS:Error in opening device!\n"); + pr_err("Error in opening device!\n"); } else { dev->flags |= IFF_UP; @@ -2113,8 +2125,9 @@ lcs_new_device(struct ccwgroup_device *ccwgdev) rc = lcs_detect(card); if (rc) { LCS_DBF_TEXT(2, setup, "dtctfail"); - PRINT_WARN("Detection of LCS card failed with return code " - "%d (0x%x)\n", rc, rc); + dev_warn(&card->dev->dev, + "Detecting a network adapter for LCS devices" + " failed with rc=%d (0x%x)\n", rc, rc); lcs_stopcard(card); goto out; } @@ -2144,7 +2157,7 @@ lcs_new_device(struct ccwgroup_device *ccwgdev) #endif default: LCS_DBF_TEXT(3, setup, "errinit"); - PRINT_ERR("LCS: Initialization failed\n"); + pr_err(" Initialization failed\n"); goto out; } if (!dev) @@ -2176,13 +2189,13 @@ netdev_out: goto out; /* Print out supported assists: IPv6 */ - PRINT_INFO("LCS device %s %s IPv6 support\n", card->dev->name, - (card->ip_assists_supported & LCS_IPASS_IPV6_SUPPORT) ? - "with" : "without"); + pr_info("LCS device %s %s IPv6 support\n", card->dev->name, + (card->ip_assists_supported & LCS_IPASS_IPV6_SUPPORT) ? + "with" : "without"); /* Print out supported assist: Multicast */ - PRINT_INFO("LCS device %s %s Multicast support\n", card->dev->name, - (card->ip_assists_supported & LCS_IPASS_MULTICAST_SUPPORT) ? - "with" : "without"); + pr_info("LCS device %s %s Multicast support\n", card->dev->name, + (card->ip_assists_supported & LCS_IPASS_MULTICAST_SUPPORT) ? + "with" : "without"); return 0; out: @@ -2248,15 +2261,16 @@ lcs_recovery(void *ptr) return 0; LCS_DBF_TEXT(4, trace, "recover2"); gdev = card->gdev; - PRINT_WARN("Recovery of device %s started...\n", dev_name(&gdev->dev)); + dev_warn(&gdev->dev, + "A recovery process has been started for the LCS device\n"); rc = __lcs_shutdown_device(gdev, 1); rc = lcs_new_device(gdev); if (!rc) - PRINT_INFO("Device %s successfully recovered!\n", - card->dev->name); + pr_info("Device %s successfully recovered!\n", + card->dev->name); else - PRINT_INFO("Device %s could not be recovered!\n", - card->dev->name); + pr_info("Device %s could not be recovered!\n", + card->dev->name); lcs_clear_thread_running_bit(card, LCS_RECOVERY_THREAD); return 0; } @@ -2308,17 +2322,17 @@ __init lcs_init_module(void) { int rc; - PRINT_INFO("Loading %s\n",version); + pr_info("Loading %s\n", version); rc = lcs_register_debug_facility(); LCS_DBF_TEXT(0, setup, "lcsinit"); if (rc) { - PRINT_ERR("Initialization failed\n"); + pr_err("Initialization failed\n"); return rc; } rc = register_cu3088_discipline(&lcs_group_driver); if (rc) { - PRINT_ERR("Initialization failed\n"); + pr_err("Initialization failed\n"); return rc; } return 0; @@ -2331,7 +2345,7 @@ __init lcs_init_module(void) static void __exit lcs_cleanup_module(void) { - PRINT_INFO("Terminating lcs module.\n"); + pr_info("Terminating lcs module.\n"); LCS_DBF_TEXT(0, trace, "cleanup"); unregister_cu3088_discipline(&lcs_group_driver); lcs_unregister_debug_facility(); diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c index 0fea51e34b57..930e2fc2a011 100644 --- a/drivers/s390/net/netiucv.c +++ b/drivers/s390/net/netiucv.c @@ -31,6 +31,9 @@ * */ +#define KMSG_COMPONENT "netiucv" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #undef DEBUG #include <linux/module.h> @@ -846,7 +849,8 @@ static void conn_action_connsever(fsm_instance *fi, int event, void *arg) fsm_deltimer(&conn->timer); iucv_path_sever(conn->path, NULL); - PRINT_INFO("%s: Remote dropped connection\n", netdev->name); + dev_info(privptr->dev, "The peer interface of the IUCV device" + " has closed the connection\n"); IUCV_DBF_TEXT(data, 2, "conn_action_connsever: Remote dropped connection\n"); fsm_newstate(fi, CONN_STATE_STARTWAIT); @@ -856,13 +860,15 @@ static void conn_action_connsever(fsm_instance *fi, int event, void *arg) static void conn_action_start(fsm_instance *fi, int event, void *arg) { struct iucv_connection *conn = arg; + struct net_device *netdev = conn->netdev; + struct netiucv_priv *privptr = netdev_priv(netdev); int rc; IUCV_DBF_TEXT(trace, 3, __func__); fsm_newstate(fi, CONN_STATE_STARTWAIT); IUCV_DBF_TEXT_(setup, 2, "%s('%s'): connecting ...\n", - conn->netdev->name, conn->userid); + netdev->name, conn->userid); /* * We must set the state before calling iucv_connect because the @@ -876,41 +882,45 @@ static void conn_action_start(fsm_instance *fi, int event, void *arg) NULL, iucvMagic, conn); switch (rc) { case 0: - conn->netdev->tx_queue_len = conn->path->msglim; + netdev->tx_queue_len = conn->path->msglim; fsm_addtimer(&conn->timer, NETIUCV_TIMEOUT_5SEC, CONN_EVENT_TIMER, conn); return; case 11: - PRINT_INFO("%s: User %s is currently not available.\n", - conn->netdev->name, - netiucv_printname(conn->userid)); + dev_warn(privptr->dev, + "The IUCV device failed to connect to z/VM guest %s\n", + netiucv_printname(conn->userid)); fsm_newstate(fi, CONN_STATE_STARTWAIT); break; case 12: - PRINT_INFO("%s: User %s is currently not ready.\n", - conn->netdev->name, - netiucv_printname(conn->userid)); + dev_warn(privptr->dev, + "The IUCV device failed to connect to the peer on z/VM" + " guest %s\n", netiucv_printname(conn->userid)); fsm_newstate(fi, CONN_STATE_STARTWAIT); break; case 13: - PRINT_WARN("%s: Too many IUCV connections.\n", - conn->netdev->name); + dev_err(privptr->dev, + "Connecting the IUCV device would exceed the maximum" + " number of IUCV connections\n"); fsm_newstate(fi, CONN_STATE_CONNERR); break; case 14: - PRINT_WARN("%s: User %s has too many IUCV connections.\n", - conn->netdev->name, - netiucv_printname(conn->userid)); + dev_err(privptr->dev, + "z/VM guest %s has too many IUCV connections" + " to connect with the IUCV device\n", + netiucv_printname(conn->userid)); fsm_newstate(fi, CONN_STATE_CONNERR); break; case 15: - PRINT_WARN("%s: No IUCV authorization in CP directory.\n", - conn->netdev->name); + dev_err(privptr->dev, + "The IUCV device cannot connect to a z/VM guest with no" + " IUCV authorization\n"); fsm_newstate(fi, CONN_STATE_CONNERR); break; default: - PRINT_WARN("%s: iucv_connect returned error %d\n", - conn->netdev->name, rc); + dev_err(privptr->dev, + "Connecting the IUCV device failed with error %d\n", + rc); fsm_newstate(fi, CONN_STATE_CONNERR); break; } @@ -1059,8 +1069,9 @@ dev_action_connup(fsm_instance *fi, int event, void *arg) switch (fsm_getstate(fi)) { case DEV_STATE_STARTWAIT: fsm_newstate(fi, DEV_STATE_RUNNING); - PRINT_INFO("%s: connected with remote side %s\n", - dev->name, privptr->conn->userid); + dev_info(privptr->dev, + "The IUCV device has been connected" + " successfully to %s\n", privptr->conn->userid); IUCV_DBF_TEXT(setup, 3, "connection is up and running\n"); break; @@ -1982,6 +1993,8 @@ static ssize_t conn_write(struct device_driver *drv, if (rc) goto out_unreg; + dev_info(priv->dev, "The IUCV interface to %s has been" + " established successfully\n", netiucv_printname(username)); return count; @@ -2027,10 +2040,9 @@ static ssize_t remove_write (struct device_driver *drv, continue; read_unlock_bh(&iucv_connection_rwlock); if (ndev->flags & (IFF_UP | IFF_RUNNING)) { - PRINT_WARN("netiucv: net device %s active with peer " - "%s\n", ndev->name, priv->conn->userid); - PRINT_WARN("netiucv: %s cannot be removed\n", - ndev->name); + dev_warn(dev, "The IUCV device is connected" + " to %s and cannot be removed\n", + priv->conn->userid); IUCV_DBF_TEXT(data, 2, "remove_write: still active\n"); return -EPERM; } @@ -2062,7 +2074,7 @@ static struct attribute_group *netiucv_drv_attr_groups[] = { static void netiucv_banner(void) { - PRINT_INFO("NETIUCV driver initialized\n"); + pr_info("driver initialized\n"); } static void __exit netiucv_exit(void) @@ -2088,7 +2100,7 @@ static void __exit netiucv_exit(void) iucv_unregister(&netiucv_handler, 1); iucv_unregister_dbf_views(); - PRINT_INFO("NETIUCV driver unloaded\n"); + pr_info("driver unloaded\n"); return; } diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index af6d60458513..d5ccce1643e4 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -31,11 +31,10 @@ #include <asm/qdio.h> #include <asm/ccwdev.h> #include <asm/ccwgroup.h> +#include <asm/sysinfo.h> #include "qeth_core_mpc.h" -#define KMSG_COMPONENT "qeth" - /** * Debug Facility stuff */ @@ -74,11 +73,6 @@ struct qeth_dbf_info { #define QETH_DBF_TEXT_(name, level, text...) \ qeth_dbf_longtext(QETH_DBF_##name, level, text) -/** - * some more debug stuff - */ -#define PRINTK_HEADER "qeth: " - #define SENSE_COMMAND_REJECT_BYTE 0 #define SENSE_COMMAND_REJECT_FLAG 0x80 #define SENSE_RESETTING_EVENT_BYTE 1 @@ -733,6 +727,7 @@ struct qeth_card { struct qeth_osn_info osn_info; struct qeth_discipline discipline; atomic_t force_alloc_skb; + struct service_level qeth_service_level; }; struct qeth_card_list_struct { diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 52d26592c72c..bbd6788e8270 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -8,6 +8,9 @@ * Frank Blaschka <frank.blaschka@de.ibm.com> */ +#define KMSG_COMPONENT "qeth" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/string.h> @@ -319,7 +322,10 @@ static int qeth_issue_next_read(struct qeth_card *card) return -EIO; iob = qeth_get_buffer(&card->read); if (!iob) { - PRINT_WARN("issue_next_read failed: no iob available!\n"); + dev_warn(&card->gdev->dev, "The qeth device driver " + "failed to recover an error on the device\n"); + QETH_DBF_MESSAGE(2, "%s issue_next_read failed: no iob " + "available\n", dev_name(&card->gdev->dev)); return -ENOMEM; } qeth_setup_ccw(&card->read, iob->data, QETH_BUFSIZE); @@ -327,7 +333,8 @@ static int qeth_issue_next_read(struct qeth_card *card) rc = ccw_device_start(card->read.ccwdev, &card->read.ccw, (addr_t) iob, 0, 0); if (rc) { - PRINT_ERR("Error in starting next read ccw! rc=%i\n", rc); + QETH_DBF_MESSAGE(2, "%s error in starting next read ccw! " + "rc=%i\n", dev_name(&card->gdev->dev), rc); atomic_set(&card->read.irq_pending, 0); qeth_schedule_recovery(card); wake_up(&card->wait_q); @@ -393,10 +400,9 @@ static struct qeth_ipa_cmd *qeth_check_ipa_data(struct qeth_card *card, } else { switch (cmd->hdr.command) { case IPA_CMD_STOPLAN: - PRINT_WARN("Link failure on %s (CHPID 0x%X) - " - "there is a network problem or " - "someone pulled the cable or " - "disabled the port.\n", + dev_warn(&card->gdev->dev, + "The link for interface %s on CHPID" + " 0x%X failed\n", QETH_CARD_IFNAME(card), card->info.chpid); card->lan_online = 0; @@ -404,9 +410,9 @@ static struct qeth_ipa_cmd *qeth_check_ipa_data(struct qeth_card *card, netif_carrier_off(card->dev); return NULL; case IPA_CMD_STARTLAN: - PRINT_INFO("Link reestablished on %s " - "(CHPID 0x%X). Scheduling " - "IP address reset.\n", + dev_info(&card->gdev->dev, + "The link for %s on CHPID 0x%X has" + " been restored\n", QETH_CARD_IFNAME(card), card->info.chpid); netif_carrier_on(card->dev); @@ -458,7 +464,7 @@ static int qeth_check_idx_response(unsigned char *buffer) QETH_DBF_HEX(CTRL, 2, buffer, QETH_DBF_CTRL_LEN); if ((buffer[2] & 0xc0) == 0xc0) { - PRINT_WARN("received an IDX TERMINATE " + QETH_DBF_MESSAGE(2, "received an IDX TERMINATE " "with cause code 0x%02x%s\n", buffer[4], ((buffer[4] == 0x22) ? @@ -744,8 +750,10 @@ static int qeth_get_problem(struct ccw_device *cdev, struct irb *irb) SCHN_STAT_CHN_DATA_CHK | SCHN_STAT_CHAIN_CHECK | SCHN_STAT_PROT_CHECK | SCHN_STAT_PROG_CHECK)) { QETH_DBF_TEXT(TRACE, 2, "CGENCHK"); - PRINT_WARN("check on device %s, dstat=x%x, cstat=x%x ", - dev_name(&cdev->dev), dstat, cstat); + dev_warn(&cdev->dev, "The qeth device driver " + "failed to recover an error on the device\n"); + QETH_DBF_MESSAGE(2, "%s check on device dstat=x%x, cstat=x%x ", + dev_name(&cdev->dev), dstat, cstat); print_hex_dump(KERN_WARNING, "qeth: irb ", DUMP_PREFIX_OFFSET, 16, 1, irb, 64, 1); return 1; @@ -784,12 +792,14 @@ static long __qeth_check_irb_error(struct ccw_device *cdev, switch (PTR_ERR(irb)) { case -EIO: - PRINT_WARN("i/o-error on device %s\n", dev_name(&cdev->dev)); + QETH_DBF_MESSAGE(2, "%s i/o-error on device\n", + dev_name(&cdev->dev)); QETH_DBF_TEXT(TRACE, 2, "ckirberr"); QETH_DBF_TEXT_(TRACE, 2, " rc%d", -EIO); break; case -ETIMEDOUT: - PRINT_WARN("timeout on device %s\n", dev_name(&cdev->dev)); + dev_warn(&cdev->dev, "A hardware operation timed out" + " on the device\n"); QETH_DBF_TEXT(TRACE, 2, "ckirberr"); QETH_DBF_TEXT_(TRACE, 2, " rc%d", -ETIMEDOUT); if (intparm == QETH_RCD_PARM) { @@ -802,8 +812,8 @@ static long __qeth_check_irb_error(struct ccw_device *cdev, } break; default: - PRINT_WARN("unknown error %ld on device %s\n", PTR_ERR(irb), - dev_name(&cdev->dev)); + QETH_DBF_MESSAGE(2, "%s unknown error %ld on device\n", + dev_name(&cdev->dev), PTR_ERR(irb)); QETH_DBF_TEXT(TRACE, 2, "ckirberr"); QETH_DBF_TEXT(TRACE, 2, " rc???"); } @@ -869,10 +879,12 @@ static void qeth_irq(struct ccw_device *cdev, unsigned long intparm, (dstat & DEV_STAT_UNIT_CHECK) || (cstat)) { if (irb->esw.esw0.erw.cons) { - /* TODO: we should make this s390dbf */ - PRINT_WARN("sense data available on channel %s.\n", - CHANNEL_ID(channel)); - PRINT_WARN(" cstat 0x%X\n dstat 0x%X\n", cstat, dstat); + dev_warn(&channel->ccwdev->dev, + "The qeth device driver failed to recover " + "an error on the device\n"); + QETH_DBF_MESSAGE(2, "%s sense data available. cstat " + "0x%X dstat 0x%X\n", + dev_name(&channel->ccwdev->dev), cstat, dstat); print_hex_dump(KERN_WARNING, "qeth: irb ", DUMP_PREFIX_OFFSET, 16, 1, irb, 32, 1); print_hex_dump(KERN_WARNING, "qeth: sense data ", @@ -1138,6 +1150,14 @@ static int qeth_setup_card(struct qeth_card *card) return 0; } +static void qeth_core_sl_print(struct seq_file *m, struct service_level *slr) +{ + struct qeth_card *card = container_of(slr, struct qeth_card, + qeth_service_level); + seq_printf(m, "qeth: %s firmware level %s\n", CARD_BUS_ID(card), + card->info.mcl_level); +} + static struct qeth_card *qeth_alloc_card(void) { struct qeth_card *card; @@ -1157,6 +1177,8 @@ static struct qeth_card *qeth_alloc_card(void) return NULL; } card->options.layer2 = -1; + card->qeth_service_level.seq_print = qeth_core_sl_print; + register_service_level(&card->qeth_service_level); return card; } @@ -1175,8 +1197,8 @@ static int qeth_determine_card_type(struct qeth_card *card) card->qdio.no_out_queues = known_devices[i][8]; card->info.is_multicast_different = known_devices[i][9]; if (qeth_is_1920_device(card)) { - PRINT_INFO("Priority Queueing not able " - "due to hardware limitations!\n"); + dev_info(&card->gdev->dev, + "Priority Queueing not supported\n"); card->qdio.no_out_queues = 1; card->qdio.default_out_queue = 0; } @@ -1185,7 +1207,8 @@ static int qeth_determine_card_type(struct qeth_card *card) i++; } card->info.type = QETH_CARD_TYPE_UNKNOWN; - PRINT_ERR("unknown card type on device %s\n", CARD_BUS_ID(card)); + dev_err(&card->gdev->dev, "The adapter hardware is of an " + "unknown type\n"); return -ENOENT; } @@ -1368,8 +1391,8 @@ static int qeth_get_unitaddr(struct qeth_card *card) QETH_DBF_TEXT(SETUP, 2, "getunit"); rc = qeth_read_conf_data(card, (void **) &prcd, &length); if (rc) { - PRINT_ERR("qeth_read_conf_data for device %s returned %i\n", - CARD_DDEV_ID(card), rc); + QETH_DBF_MESSAGE(2, "%s qeth_read_conf_data returned %i\n", + dev_name(&card->gdev->dev), rc); return rc; } card->info.chpid = prcd[30]; @@ -1519,7 +1542,10 @@ static int qeth_idx_activate_channel(struct qeth_channel *channel, if (rc == -ERESTARTSYS) return rc; if (channel->state != CH_STATE_ACTIVATING) { - PRINT_WARN("IDX activate timed out!\n"); + dev_warn(&channel->ccwdev->dev, "The qeth device driver" + " failed to recover an error on the device\n"); + QETH_DBF_MESSAGE(2, "%s IDX activate timed out\n", + dev_name(&channel->ccwdev->dev)); QETH_DBF_TEXT_(SETUP, 2, "2err%d", -ETIME); qeth_clear_cmd_buffers(channel); return -ETIME; @@ -1552,20 +1578,21 @@ static void qeth_idx_write_cb(struct qeth_channel *channel, if (!(QETH_IS_IDX_ACT_POS_REPLY(iob->data))) { if (QETH_IDX_ACT_CAUSE_CODE(iob->data) == 0x19) - PRINT_ERR("IDX_ACTIVATE on write channel device %s: " - "adapter exclusively used by another host\n", - CARD_WDEV_ID(card)); + dev_err(&card->write.ccwdev->dev, + "The adapter is used exclusively by another " + "host\n"); else - PRINT_ERR("IDX_ACTIVATE on write channel device %s: " - "negative reply\n", CARD_WDEV_ID(card)); + QETH_DBF_MESSAGE(2, "%s IDX_ACTIVATE on write channel:" + " negative reply\n", + dev_name(&card->write.ccwdev->dev)); goto out; } memcpy(&temp, QETH_IDX_ACT_FUNC_LEVEL(iob->data), 2); if ((temp & ~0x0100) != qeth_peer_func_level(card->info.func_level)) { - PRINT_WARN("IDX_ACTIVATE on write channel device %s: " - "function level mismatch " - "(sent: 0x%x, received: 0x%x)\n", - CARD_WDEV_ID(card), card->info.func_level, temp); + QETH_DBF_MESSAGE(2, "%s IDX_ACTIVATE on write channel: " + "function level mismatch (sent: 0x%x, received: " + "0x%x)\n", dev_name(&card->write.ccwdev->dev), + card->info.func_level, temp); goto out; } channel->state = CH_STATE_UP; @@ -1591,12 +1618,13 @@ static void qeth_idx_read_cb(struct qeth_channel *channel, if (!(QETH_IS_IDX_ACT_POS_REPLY(iob->data))) { if (QETH_IDX_ACT_CAUSE_CODE(iob->data) == 0x19) - PRINT_ERR("IDX_ACTIVATE on read channel device %s: " - "adapter exclusively used by another host\n", - CARD_RDEV_ID(card)); + dev_err(&card->write.ccwdev->dev, + "The adapter is used exclusively by another " + "host\n"); else - PRINT_ERR("IDX_ACTIVATE on read channel device %s: " - "negative reply\n", CARD_RDEV_ID(card)); + QETH_DBF_MESSAGE(2, "%s IDX_ACTIVATE on read channel:" + " negative reply\n", + dev_name(&card->read.ccwdev->dev)); goto out; } @@ -1610,9 +1638,10 @@ static void qeth_idx_read_cb(struct qeth_channel *channel, memcpy(&temp, QETH_IDX_ACT_FUNC_LEVEL(iob->data), 2); if (temp != qeth_peer_func_level(card->info.func_level)) { - PRINT_WARN("IDX_ACTIVATE on read channel device %s: function " - "level mismatch (sent: 0x%x, received: 0x%x)\n", - CARD_RDEV_ID(card), card->info.func_level, temp); + QETH_DBF_MESSAGE(2, "%s IDX_ACTIVATE on read channel: function " + "level mismatch (sent: 0x%x, received: 0x%x)\n", + dev_name(&card->read.ccwdev->dev), + card->info.func_level, temp); goto out; } memcpy(&card->token.issuer_rm_r, @@ -1686,8 +1715,9 @@ int qeth_send_control_data(struct qeth_card *card, int len, (addr_t) iob, 0, 0); spin_unlock_irqrestore(get_ccwdev_lock(card->write.ccwdev), flags); if (rc) { - PRINT_WARN("qeth_send_control_data: " - "ccw_device_start rc = %i\n", rc); + QETH_DBF_MESSAGE(2, "%s qeth_send_control_data: " + "ccw_device_start rc = %i\n", + dev_name(&card->write.ccwdev->dev), rc); QETH_DBF_TEXT_(TRACE, 2, " err%d", rc); spin_lock_irqsave(&card->lock, flags); list_del_init(&reply->list); @@ -2170,11 +2200,8 @@ static void qeth_print_status_with_portname(struct qeth_card *card) dbf_text[i] = (char) _ebcasc[(__u8) dbf_text[i]]; dbf_text[8] = 0; - PRINT_INFO("Device %s/%s/%s is a%s card%s%s%s\n" + dev_info(&card->gdev->dev, "Device is a%s card%s%s%s\n" "with link type %s (portname: %s)\n", - CARD_RDEV_ID(card), - CARD_WDEV_ID(card), - CARD_DDEV_ID(card), qeth_get_cardname(card), (card->info.mcl_level[0]) ? " (level: " : "", (card->info.mcl_level[0]) ? card->info.mcl_level : "", @@ -2187,23 +2214,17 @@ static void qeth_print_status_with_portname(struct qeth_card *card) static void qeth_print_status_no_portname(struct qeth_card *card) { if (card->info.portname[0]) - PRINT_INFO("Device %s/%s/%s is a%s " + dev_info(&card->gdev->dev, "Device is a%s " "card%s%s%s\nwith link type %s " "(no portname needed by interface).\n", - CARD_RDEV_ID(card), - CARD_WDEV_ID(card), - CARD_DDEV_ID(card), qeth_get_cardname(card), (card->info.mcl_level[0]) ? " (level: " : "", (card->info.mcl_level[0]) ? card->info.mcl_level : "", (card->info.mcl_level[0]) ? ")" : "", qeth_get_cardname_short(card)); else - PRINT_INFO("Device %s/%s/%s is a%s " + dev_info(&card->gdev->dev, "Device is a%s " "card%s%s%s\nwith link type %s.\n", - CARD_RDEV_ID(card), - CARD_WDEV_ID(card), - CARD_DDEV_ID(card), qeth_get_cardname(card), (card->info.mcl_level[0]) ? " (level: " : "", (card->info.mcl_level[0]) ? card->info.mcl_level : "", @@ -2630,9 +2651,8 @@ void qeth_queue_input_buffer(struct qeth_card *card, int index) qeth_get_micros() - card->perf_stats.inbound_do_qdio_start_time; if (rc) { - PRINT_WARN("qeth_queue_input_buffer's do_QDIO " - "return %i (device %s).\n", - rc, CARD_DDEV_ID(card)); + dev_warn(&card->gdev->dev, + "QDIO reported an error, rc=%i\n", rc); QETH_DBF_TEXT(TRACE, 2, "qinberr"); QETH_DBF_TEXT_(TRACE, 2, "%s", CARD_BUS_ID(card)); } @@ -3730,6 +3750,7 @@ static void qeth_core_free_card(struct qeth_card *card) free_netdev(card->dev); kfree(card->ip_tbd_list); qeth_free_qdio_buffers(card); + unregister_service_level(&card->qeth_service_level); kfree(card); } @@ -3757,7 +3778,7 @@ static int qeth_core_driver_group(const char *buf, struct device *root_dev, int qeth_core_hardsetup_card(struct qeth_card *card) { - struct qdio_ssqd_desc *qdio_ssqd; + struct qdio_ssqd_desc *ssqd; int retries = 3; int mpno = 0; int rc; @@ -3766,7 +3787,8 @@ int qeth_core_hardsetup_card(struct qeth_card *card) atomic_set(&card->force_alloc_skb, 0); retry: if (retries < 3) { - PRINT_WARN("Retrying to do IDX activates.\n"); + QETH_DBF_MESSAGE(2, "%s Retrying to do IDX activates.\n", + dev_name(&card->gdev->dev)); ccw_device_set_offline(CARD_DDEV(card)); ccw_device_set_offline(CARD_WDEV(card)); ccw_device_set_offline(CARD_RDEV(card)); @@ -3792,9 +3814,16 @@ retry: return rc; } - qdio_ssqd = qdio_get_ssqd_desc(CARD_DDEV(card)); - if (qdio_ssqd) - mpno = qdio_ssqd->pcnt; + ssqd = kmalloc(sizeof(struct qdio_ssqd_desc), GFP_KERNEL); + if (!ssqd) { + rc = -ENOMEM; + goto out; + } + rc = qdio_get_ssqd_desc(CARD_DDEV(card), ssqd); + if (rc == 0) + mpno = ssqd->pcnt; + kfree(ssqd); + if (mpno) mpno = min(mpno - 1, QETH_MAX_PORTNO); if (card->info.portno > mpno) { @@ -3834,7 +3863,10 @@ retry: } return 0; out: - PRINT_ERR("Initialization in hardsetup failed! rc=%d\n", rc); + dev_warn(&card->gdev->dev, "The qeth device driver failed to recover " + "an error on the device\n"); + QETH_DBF_MESSAGE(2, "%s Initialization in hardsetup failed! rc=%d\n", + dev_name(&card->gdev->dev), rc); return rc; } EXPORT_SYMBOL_GPL(qeth_core_hardsetup_card); @@ -4054,8 +4086,8 @@ int qeth_core_load_discipline(struct qeth_card *card, break; } if (!card->discipline.ccwgdriver) { - PRINT_ERR("Support for discipline %d not present\n", - discipline); + dev_err(&card->gdev->dev, "There is no kernel module to " + "support discipline %d\n", discipline); rc = -EINVAL; } return rc; @@ -4448,7 +4480,7 @@ static int __init qeth_core_init(void) { int rc; - PRINT_INFO("loading core functions\n"); + pr_info("loading core functions\n"); INIT_LIST_HEAD(&qeth_core_card_list.list); rwlock_init(&qeth_core_card_list.rwlock); @@ -4488,9 +4520,10 @@ driver_err: ccwgroup_err: ccw_driver_unregister(&qeth_ccw_driver); ccw_err: + QETH_DBF_MESSAGE(2, "Initialization failed with code %d\n", rc); qeth_unregister_dbf_views(); out_err: - PRINT_ERR("Initialization failed with code %d\n", rc); + pr_err("Initializing the qeth device driver failed\n"); return rc; } @@ -4503,7 +4536,7 @@ static void __exit qeth_core_exit(void) ccw_driver_unregister(&qeth_ccw_driver); kmem_cache_destroy(qeth_core_header_cache); qeth_unregister_dbf_views(); - PRINT_INFO("core functions removed\n"); + pr_info("core functions removed\n"); } module_init(qeth_core_init); diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index 1b1e80336d2c..af15bc648ba1 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -8,6 +8,9 @@ * Frank Blaschka <frank.blaschka@de.ibm.com> */ +#define KMSG_COMPONENT "qeth" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/string.h> @@ -503,12 +506,13 @@ static int qeth_l2_send_setmac_cb(struct qeth_card *card, card->info.mac_bits |= QETH_LAYER2_MAC_REGISTERED; memcpy(card->dev->dev_addr, cmd->data.setdelmac.mac, OSA_ADDR_LEN); - PRINT_INFO("MAC address %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x " - "successfully registered on device %s\n", - card->dev->dev_addr[0], card->dev->dev_addr[1], - card->dev->dev_addr[2], card->dev->dev_addr[3], - card->dev->dev_addr[4], card->dev->dev_addr[5], - card->dev->name); + dev_info(&card->gdev->dev, + "MAC address %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x " + "successfully registered on device %s\n", + card->dev->dev_addr[0], card->dev->dev_addr[1], + card->dev->dev_addr[2], card->dev->dev_addr[3], + card->dev->dev_addr[4], card->dev->dev_addr[5], + card->dev->name); } return 0; } @@ -1015,9 +1019,8 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode) if (rc) { QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); if (rc == 0xe080) { - PRINT_WARN("LAN on card %s if offline! " - "Waiting for STARTLAN from card.\n", - CARD_BUS_ID(card)); + dev_warn(&card->gdev->dev, + "The LAN is offline\n"); card->lan_online = 0; } return rc; @@ -1117,8 +1120,8 @@ static int qeth_l2_recover(void *ptr) if (!qeth_do_run_thread(card, QETH_RECOVER_THREAD)) return 0; QETH_DBF_TEXT(TRACE, 2, "recover2"); - PRINT_WARN("Recovery of device %s started ...\n", - CARD_BUS_ID(card)); + dev_warn(&card->gdev->dev, + "A recovery process has been started for the device\n"); card->use_hard_stop = 1; __qeth_l2_set_offline(card->gdev, 1); rc = __qeth_l2_set_online(card->gdev, 1); @@ -1126,27 +1129,27 @@ static int qeth_l2_recover(void *ptr) qeth_clear_thread_start_bit(card, QETH_RECOVER_THREAD); qeth_clear_thread_running_bit(card, QETH_RECOVER_THREAD); if (!rc) - PRINT_INFO("Device %s successfully recovered!\n", - CARD_BUS_ID(card)); + dev_info(&card->gdev->dev, + "Device successfully recovered!\n"); else { rtnl_lock(); dev_close(card->dev); rtnl_unlock(); - PRINT_INFO("Device %s could not be recovered!\n", - CARD_BUS_ID(card)); + dev_warn(&card->gdev->dev, "The qeth device driver " + "failed to recover an error on the device\n"); } return 0; } static int __init qeth_l2_init(void) { - PRINT_INFO("register layer 2 discipline\n"); + pr_info("register layer 2 discipline\n"); return 0; } static void __exit qeth_l2_exit(void) { - PRINT_INFO("unregister layer 2 discipline\n"); + pr_info("unregister layer 2 discipline\n"); } static void qeth_l2_shutdown(struct ccwgroup_device *gdev) diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index ed59fedd5922..dee786c5c5fe 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -8,6 +8,9 @@ * Frank Blaschka <frank.blaschka@de.ibm.com> */ +#define KMSG_COMPONENT "qeth" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/string.h> @@ -917,8 +920,8 @@ static int qeth_l3_register_addr_entry(struct qeth_card *card, if (rc) { QETH_DBF_TEXT(TRACE, 2, "FAILED"); qeth_l3_ipaddr_to_string(addr->proto, (u8 *)&addr->u, buf); - PRINT_WARN("Could not register IP address %s (rc=0x%x/%d)\n", - buf, rc, rc); + dev_warn(&card->gdev->dev, + "Registering IP address %s failed\n", buf); } return rc; } @@ -1029,24 +1032,22 @@ static int qeth_l3_setadapter_parms(struct qeth_card *card) QETH_DBF_TEXT(SETUP, 2, "setadprm"); if (!qeth_is_supported(card, IPA_SETADAPTERPARMS)) { - PRINT_WARN("set adapter parameters not supported " - "on device %s.\n", - CARD_BUS_ID(card)); + dev_info(&card->gdev->dev, + "set adapter parameters not supported.\n"); QETH_DBF_TEXT(SETUP, 2, " notsupp"); return 0; } rc = qeth_query_setadapterparms(card); if (rc) { - PRINT_WARN("couldn't set adapter parameters on device %s: " - "x%x\n", CARD_BUS_ID(card), rc); + QETH_DBF_MESSAGE(2, "%s couldn't set adapter parameters: " + "0x%x\n", card->gdev->dev.bus_id, rc); return rc; } if (qeth_adp_supported(card, IPA_SETADP_ALTER_MAC_ADDRESS)) { rc = qeth_setadpparms_change_macaddr(card); if (rc) - PRINT_WARN("couldn't get MAC address on " - "device %s: x%x\n", - CARD_BUS_ID(card), rc); + dev_warn(&card->gdev->dev, "Reading the adapter MAC" + " address failed\n", rc); } if ((card->info.link_type == QETH_LINK_TYPE_HSTR) || @@ -1160,16 +1161,17 @@ static int qeth_l3_start_ipa_arp_processing(struct qeth_card *card) QETH_DBF_TEXT(TRACE, 3, "ipaarp"); if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) { - PRINT_WARN("ARP processing not supported " - "on %s!\n", QETH_CARD_IFNAME(card)); + dev_info(&card->gdev->dev, + "ARP processing not supported on %s!\n", + QETH_CARD_IFNAME(card)); return 0; } rc = qeth_l3_send_simple_setassparms(card, IPA_ARP_PROCESSING, IPA_CMD_ASS_START, 0); if (rc) { - PRINT_WARN("Could not start ARP processing " - "assist on %s: 0x%x\n", - QETH_CARD_IFNAME(card), rc); + dev_warn(&card->gdev->dev, + "Starting ARP processing support for %s failed\n", + QETH_CARD_IFNAME(card)); } return rc; } @@ -1181,19 +1183,21 @@ static int qeth_l3_start_ipa_ip_fragmentation(struct qeth_card *card) QETH_DBF_TEXT(TRACE, 3, "ipaipfrg"); if (!qeth_is_supported(card, IPA_IP_FRAGMENTATION)) { - PRINT_INFO("Hardware IP fragmentation not supported on %s\n", - QETH_CARD_IFNAME(card)); + dev_info(&card->gdev->dev, + "Hardware IP fragmentation not supported on %s\n", + QETH_CARD_IFNAME(card)); return -EOPNOTSUPP; } rc = qeth_l3_send_simple_setassparms(card, IPA_IP_FRAGMENTATION, IPA_CMD_ASS_START, 0); if (rc) { - PRINT_WARN("Could not start Hardware IP fragmentation " - "assist on %s: 0x%x\n", - QETH_CARD_IFNAME(card), rc); + dev_warn(&card->gdev->dev, + "Starting IP fragmentation support for %s failed\n", + QETH_CARD_IFNAME(card)); } else - PRINT_INFO("Hardware IP fragmentation enabled \n"); + dev_info(&card->gdev->dev, + "Hardware IP fragmentation enabled \n"); return rc; } @@ -1207,17 +1211,18 @@ static int qeth_l3_start_ipa_source_mac(struct qeth_card *card) return -EOPNOTSUPP; if (!qeth_is_supported(card, IPA_SOURCE_MAC)) { - PRINT_INFO("Inbound source address not " - "supported on %s\n", QETH_CARD_IFNAME(card)); + dev_info(&card->gdev->dev, + "Inbound source address not supported on %s\n", + QETH_CARD_IFNAME(card)); return -EOPNOTSUPP; } rc = qeth_l3_send_simple_setassparms(card, IPA_SOURCE_MAC, IPA_CMD_ASS_START, 0); if (rc) - PRINT_WARN("Could not start inbound source " - "assist on %s: 0x%x\n", - QETH_CARD_IFNAME(card), rc); + dev_warn(&card->gdev->dev, + "Starting proxy ARP support for %s failed\n", + QETH_CARD_IFNAME(card)); return rc; } @@ -1228,19 +1233,19 @@ static int qeth_l3_start_ipa_vlan(struct qeth_card *card) QETH_DBF_TEXT(TRACE, 3, "strtvlan"); if (!qeth_is_supported(card, IPA_FULL_VLAN)) { - PRINT_WARN("VLAN not supported on %s\n", - QETH_CARD_IFNAME(card)); + dev_info(&card->gdev->dev, + "VLAN not supported on %s\n", QETH_CARD_IFNAME(card)); return -EOPNOTSUPP; } rc = qeth_l3_send_simple_setassparms(card, IPA_VLAN_PRIO, IPA_CMD_ASS_START, 0); if (rc) { - PRINT_WARN("Could not start vlan " - "assist on %s: 0x%x\n", - QETH_CARD_IFNAME(card), rc); + dev_warn(&card->gdev->dev, + "Starting VLAN support for %s failed\n", + QETH_CARD_IFNAME(card)); } else { - PRINT_INFO("VLAN enabled \n"); + dev_info(&card->gdev->dev, "VLAN enabled\n"); } return rc; } @@ -1252,19 +1257,20 @@ static int qeth_l3_start_ipa_multicast(struct qeth_card *card) QETH_DBF_TEXT(TRACE, 3, "stmcast"); if (!qeth_is_supported(card, IPA_MULTICASTING)) { - PRINT_WARN("Multicast not supported on %s\n", - QETH_CARD_IFNAME(card)); + dev_info(&card->gdev->dev, + "Multicast not supported on %s\n", + QETH_CARD_IFNAME(card)); return -EOPNOTSUPP; } rc = qeth_l3_send_simple_setassparms(card, IPA_MULTICASTING, IPA_CMD_ASS_START, 0); if (rc) { - PRINT_WARN("Could not start multicast " - "assist on %s: rc=%i\n", - QETH_CARD_IFNAME(card), rc); + dev_warn(&card->gdev->dev, + "Starting multicast support for %s failed\n", + QETH_CARD_IFNAME(card)); } else { - PRINT_INFO("Multicast enabled\n"); + dev_info(&card->gdev->dev, "Multicast enabled\n"); card->dev->flags |= IFF_MULTICAST; } return rc; @@ -1315,36 +1321,37 @@ static int qeth_l3_softsetup_ipv6(struct qeth_card *card) rc = qeth_l3_query_ipassists(card, QETH_PROT_IPV6); if (rc) { - PRINT_ERR("IPv6 query ipassist failed on %s\n", - QETH_CARD_IFNAME(card)); + dev_err(&card->gdev->dev, + "Activating IPv6 support for %s failed\n", + QETH_CARD_IFNAME(card)); return rc; } rc = qeth_l3_send_simple_setassparms(card, IPA_IPV6, IPA_CMD_ASS_START, 3); if (rc) { - PRINT_WARN("IPv6 start assist (version 4) failed " - "on %s: 0x%x\n", - QETH_CARD_IFNAME(card), rc); + dev_warn(&card->gdev->dev, + "Activating IPv6 support for %s failed\n", + QETH_CARD_IFNAME(card)); return rc; } rc = qeth_l3_send_simple_setassparms_ipv6(card, IPA_IPV6, IPA_CMD_ASS_START); if (rc) { - PRINT_WARN("IPV6 start assist (version 6) failed " - "on %s: 0x%x\n", - QETH_CARD_IFNAME(card), rc); + dev_warn(&card->gdev->dev, + "Activating IPv6 support for %s failed\n", + QETH_CARD_IFNAME(card)); return rc; } rc = qeth_l3_send_simple_setassparms_ipv6(card, IPA_PASSTHRU, IPA_CMD_ASS_START); if (rc) { - PRINT_WARN("Could not enable passthrough " - "on %s: 0x%x\n", - QETH_CARD_IFNAME(card), rc); + dev_warn(&card->gdev->dev, + "Enabling the passthrough mode for %s failed\n", + QETH_CARD_IFNAME(card)); return rc; } out: - PRINT_INFO("IPV6 enabled \n"); + dev_info(&card->gdev->dev, "IPV6 enabled\n"); return 0; } #endif @@ -1356,8 +1363,8 @@ static int qeth_l3_start_ipa_ipv6(struct qeth_card *card) QETH_DBF_TEXT(TRACE, 3, "strtipv6"); if (!qeth_is_supported(card, IPA_IPV6)) { - PRINT_WARN("IPv6 not supported on %s\n", - QETH_CARD_IFNAME(card)); + dev_info(&card->gdev->dev, + "IPv6 not supported on %s\n", QETH_CARD_IFNAME(card)); return 0; } #ifdef CONFIG_QETH_IPV6 @@ -1373,34 +1380,35 @@ static int qeth_l3_start_ipa_broadcast(struct qeth_card *card) QETH_DBF_TEXT(TRACE, 3, "stbrdcst"); card->info.broadcast_capable = 0; if (!qeth_is_supported(card, IPA_FILTERING)) { - PRINT_WARN("Broadcast not supported on %s\n", - QETH_CARD_IFNAME(card)); + dev_info(&card->gdev->dev, + "Broadcast not supported on %s\n", + QETH_CARD_IFNAME(card)); rc = -EOPNOTSUPP; goto out; } rc = qeth_l3_send_simple_setassparms(card, IPA_FILTERING, IPA_CMD_ASS_START, 0); if (rc) { - PRINT_WARN("Could not enable broadcasting filtering " - "on %s: 0x%x\n", - QETH_CARD_IFNAME(card), rc); + dev_warn(&card->gdev->dev, "Enabling broadcast filtering for " + "%s failed\n", QETH_CARD_IFNAME(card)); goto out; } rc = qeth_l3_send_simple_setassparms(card, IPA_FILTERING, IPA_CMD_ASS_CONFIGURE, 1); if (rc) { - PRINT_WARN("Could not set up broadcast filtering on %s: 0x%x\n", - QETH_CARD_IFNAME(card), rc); + dev_warn(&card->gdev->dev, + "Setting up broadcast filtering for %s failed\n", + QETH_CARD_IFNAME(card)); goto out; } card->info.broadcast_capable = QETH_BROADCAST_WITH_ECHO; - PRINT_INFO("Broadcast enabled \n"); + dev_info(&card->gdev->dev, "Broadcast enabled\n"); rc = qeth_l3_send_simple_setassparms(card, IPA_FILTERING, IPA_CMD_ASS_ENABLE, 1); if (rc) { - PRINT_WARN("Could not set up broadcast echo filtering on " - "%s: 0x%x\n", QETH_CARD_IFNAME(card), rc); + dev_warn(&card->gdev->dev, "Setting up broadcast echo " + "filtering for %s failed\n", QETH_CARD_IFNAME(card)); goto out; } card->info.broadcast_capable = QETH_BROADCAST_WITHOUT_ECHO; @@ -1419,18 +1427,18 @@ static int qeth_l3_send_checksum_command(struct qeth_card *card) rc = qeth_l3_send_simple_setassparms(card, IPA_INBOUND_CHECKSUM, IPA_CMD_ASS_START, 0); if (rc) { - PRINT_WARN("Starting Inbound HW Checksumming failed on %s: " - "0x%x,\ncontinuing using Inbound SW Checksumming\n", - QETH_CARD_IFNAME(card), rc); + dev_warn(&card->gdev->dev, "Starting HW checksumming for %s " + "failed, using SW checksumming\n", + QETH_CARD_IFNAME(card)); return rc; } rc = qeth_l3_send_simple_setassparms(card, IPA_INBOUND_CHECKSUM, IPA_CMD_ASS_ENABLE, card->info.csum_mask); if (rc) { - PRINT_WARN("Enabling Inbound HW Checksumming failed on %s: " - "0x%x,\ncontinuing using Inbound SW Checksumming\n", - QETH_CARD_IFNAME(card), rc); + dev_warn(&card->gdev->dev, "Enabling HW checksumming for %s " + "failed, using SW checksumming\n", + QETH_CARD_IFNAME(card)); return rc; } return 0; @@ -1443,26 +1451,30 @@ static int qeth_l3_start_ipa_checksum(struct qeth_card *card) QETH_DBF_TEXT(TRACE, 3, "strtcsum"); if (card->options.checksum_type == NO_CHECKSUMMING) { - PRINT_WARN("Using no checksumming on %s.\n", - QETH_CARD_IFNAME(card)); + dev_info(&card->gdev->dev, + "Using no checksumming on %s.\n", + QETH_CARD_IFNAME(card)); return 0; } if (card->options.checksum_type == SW_CHECKSUMMING) { - PRINT_WARN("Using SW checksumming on %s.\n", - QETH_CARD_IFNAME(card)); + dev_info(&card->gdev->dev, + "Using SW checksumming on %s.\n", + QETH_CARD_IFNAME(card)); return 0; } if (!qeth_is_supported(card, IPA_INBOUND_CHECKSUM)) { - PRINT_WARN("Inbound HW Checksumming not " - "supported on %s,\ncontinuing " - "using Inbound SW Checksumming\n", - QETH_CARD_IFNAME(card)); + dev_info(&card->gdev->dev, + "Inbound HW Checksumming not " + "supported on %s,\ncontinuing " + "using Inbound SW Checksumming\n", + QETH_CARD_IFNAME(card)); card->options.checksum_type = SW_CHECKSUMMING; return 0; } rc = qeth_l3_send_checksum_command(card); if (!rc) - PRINT_INFO("HW Checksumming (inbound) enabled \n"); + dev_info(&card->gdev->dev, + "HW Checksumming (inbound) enabled\n"); return rc; } @@ -1474,18 +1486,20 @@ static int qeth_l3_start_ipa_tso(struct qeth_card *card) QETH_DBF_TEXT(TRACE, 3, "sttso"); if (!qeth_is_supported(card, IPA_OUTBOUND_TSO)) { - PRINT_WARN("Outbound TSO not supported on %s\n", - QETH_CARD_IFNAME(card)); + dev_info(&card->gdev->dev, + "Outbound TSO not supported on %s\n", + QETH_CARD_IFNAME(card)); rc = -EOPNOTSUPP; } else { rc = qeth_l3_send_simple_setassparms(card, IPA_OUTBOUND_TSO, IPA_CMD_ASS_START, 0); if (rc) - PRINT_WARN("Could not start outbound TSO " - "assist on %s: rc=%i\n", - QETH_CARD_IFNAME(card), rc); + dev_warn(&card->gdev->dev, "Starting outbound TCP " + "segmentation offload for %s failed\n", + QETH_CARD_IFNAME(card)); else - PRINT_INFO("Outbound TSO enabled\n"); + dev_info(&card->gdev->dev, + "Outbound TSO enabled\n"); } if (rc && (card->options.large_send == QETH_LARGE_SEND_TSO)) { card->options.large_send = QETH_LARGE_SEND_NO; @@ -1578,12 +1592,8 @@ static int qeth_l3_get_unique_id_cb(struct qeth_card *card, else { card->info.unique_id = UNIQUE_ID_IF_CREATE_ADDR_FAILED | UNIQUE_ID_NOT_BY_CARD; - PRINT_WARN("couldn't get a unique id from the card on device " - "%s (result=x%x), using default id. ipv6 " - "autoconfig on other lpars may lead to duplicate " - "ip addresses. please use manually " - "configured ones.\n", - CARD_BUS_ID(card), cmd->hdr.return_code); + dev_warn(&card->gdev->dev, "The network adapter failed to " + "generate a unique ID\n"); } return 0; } @@ -3086,9 +3096,8 @@ static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode) if (rc) { QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); if (rc == 0xe080) { - PRINT_WARN("LAN on card %s if offline! " - "Waiting for STARTLAN from card.\n", - CARD_BUS_ID(card)); + dev_warn(&card->gdev->dev, + "The LAN is offline\n"); card->lan_online = 0; } return rc; @@ -3194,8 +3203,8 @@ static int qeth_l3_recover(void *ptr) if (!qeth_do_run_thread(card, QETH_RECOVER_THREAD)) return 0; QETH_DBF_TEXT(TRACE, 2, "recover2"); - PRINT_WARN("Recovery of device %s started ...\n", - CARD_BUS_ID(card)); + dev_warn(&card->gdev->dev, + "A recovery process has been started for the device\n"); card->use_hard_stop = 1; __qeth_l3_set_offline(card->gdev, 1); rc = __qeth_l3_set_online(card->gdev, 1); @@ -3203,14 +3212,14 @@ static int qeth_l3_recover(void *ptr) qeth_clear_thread_start_bit(card, QETH_RECOVER_THREAD); qeth_clear_thread_running_bit(card, QETH_RECOVER_THREAD); if (!rc) - PRINT_INFO("Device %s successfully recovered!\n", - CARD_BUS_ID(card)); + dev_info(&card->gdev->dev, + "Device successfully recovered!\n"); else { rtnl_lock(); dev_close(card->dev); rtnl_unlock(); - PRINT_INFO("Device %s could not be recovered!\n", - CARD_BUS_ID(card)); + dev_warn(&card->gdev->dev, "The qeth device driver " + "failed to recover an error on the device\n"); } return 0; } @@ -3344,7 +3353,7 @@ static int qeth_l3_register_notifiers(void) return rc; } #else - PRINT_WARN("layer 3 discipline no IPv6 support\n"); + pr_warning("There is no IPv6 support for the layer 3 discipline\n"); #endif return 0; } @@ -3363,7 +3372,7 @@ static int __init qeth_l3_init(void) { int rc = 0; - PRINT_INFO("register layer 3 discipline\n"); + pr_info("register layer 3 discipline\n"); rc = qeth_l3_register_notifiers(); return rc; } @@ -3371,7 +3380,7 @@ static int __init qeth_l3_init(void) static void __exit qeth_l3_exit(void) { qeth_l3_unregister_notifiers(); - PRINT_INFO("unregister layer 3 discipline\n"); + pr_info("unregister layer 3 discipline\n"); } module_init(qeth_l3_init); diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c index 3b56220fb900..e529b55b3ce9 100644 --- a/drivers/s390/scsi/zfcp_aux.c +++ b/drivers/s390/scsi/zfcp_aux.c @@ -25,9 +25,15 @@ * Sven Schuetz */ +#define KMSG_COMPONENT "zfcp" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include <linux/miscdevice.h> +#include <linux/seq_file.h> #include "zfcp_ext.h" +#define ZFCP_BUS_ID_SIZE 20 + static char *device; MODULE_AUTHOR("IBM Deutschland Entwicklung GmbH - linux390@de.ibm.com"); @@ -83,9 +89,9 @@ static int __init zfcp_device_setup(char *devstr) strcpy(str, devstr); token = strsep(&str, ","); - if (!token || strlen(token) >= BUS_ID_SIZE) + if (!token || strlen(token) >= ZFCP_BUS_ID_SIZE) goto err_out; - strncpy(zfcp_data.init_busid, token, BUS_ID_SIZE); + strncpy(zfcp_data.init_busid, token, ZFCP_BUS_ID_SIZE); token = strsep(&str, ","); if (!token || strict_strtoull(token, 0, @@ -102,7 +108,7 @@ static int __init zfcp_device_setup(char *devstr) err_out: kfree(str); - pr_err("zfcp: %s is not a valid SCSI device\n", devstr); + pr_err("%s is not a valid SCSI device\n", devstr); return 0; } @@ -186,13 +192,13 @@ static int __init zfcp_module_init(void) retval = misc_register(&zfcp_cfdc_misc); if (retval) { - pr_err("zfcp: Registering the misc device zfcp_cfdc failed\n"); + pr_err("Registering the misc device zfcp_cfdc failed\n"); goto out_misc; } retval = zfcp_ccw_register(); if (retval) { - pr_err("zfcp: The zfcp device driver could not register with " + pr_err("The zfcp device driver could not register with " "the common I/O layer\n"); goto out_ccw_register; } @@ -436,6 +442,16 @@ static void _zfcp_status_read_scheduler(struct work_struct *work) stat_work)); } +static void zfcp_print_sl(struct seq_file *m, struct service_level *sl) +{ + struct zfcp_adapter *adapter = + container_of(sl, struct zfcp_adapter, service_level); + + seq_printf(m, "zfcp: %s microcode level %x\n", + dev_name(&adapter->ccw_device->dev), + adapter->fsf_lic_version); +} + /** * zfcp_adapter_enqueue - enqueue a new adapter to the list * @ccw_device: pointer to the struct cc_device @@ -500,6 +516,8 @@ int zfcp_adapter_enqueue(struct ccw_device *ccw_device) INIT_WORK(&adapter->stat_work, _zfcp_status_read_scheduler); INIT_WORK(&adapter->scan_work, _zfcp_scan_ports_later); + adapter->service_level.seq_print = zfcp_print_sl; + /* mark adapter unusable as long as sysfs registration is not complete */ atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status); @@ -610,7 +628,8 @@ struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn, atomic_set_mask(status | ZFCP_STATUS_COMMON_REMOVE, &port->status); atomic_set(&port->refcount, 0); - dev_set_name(&port->sysfs_device, "0x%016llx", wwpn); + dev_set_name(&port->sysfs_device, "0x%016llx", + (unsigned long long)wwpn); port->sysfs_device.parent = &adapter->ccw_device->dev; port->sysfs_device.release = zfcp_sysfs_port_release; diff --git a/drivers/s390/scsi/zfcp_ccw.c b/drivers/s390/scsi/zfcp_ccw.c index b04038c74786..728147131e1d 100644 --- a/drivers/s390/scsi/zfcp_ccw.c +++ b/drivers/s390/scsi/zfcp_ccw.c @@ -6,6 +6,9 @@ * Copyright IBM Corporation 2002, 2008 */ +#define KMSG_COMPONENT "zfcp" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include "zfcp_ext.h" /** @@ -116,7 +119,9 @@ static int zfcp_ccw_set_online(struct ccw_device *ccw_device) zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED, 85, NULL); zfcp_erp_wait(adapter); - goto out; + up(&zfcp_data.config_sema); + flush_work(&adapter->scan_work); + return 0; out_scsi_register: zfcp_erp_thread_kill(adapter); diff --git a/drivers/s390/scsi/zfcp_cfdc.c b/drivers/s390/scsi/zfcp_cfdc.c index ec2abceca6dc..f1a7518e67ed 100644 --- a/drivers/s390/scsi/zfcp_cfdc.c +++ b/drivers/s390/scsi/zfcp_cfdc.c @@ -7,6 +7,9 @@ * Copyright IBM Corporation 2008 */ +#define KMSG_COMPONENT "zfcp" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include <linux/types.h> #include <linux/miscdevice.h> #include <asm/ccwdev.h> diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c index 060f5f2352ec..735d675623f8 100644 --- a/drivers/s390/scsi/zfcp_dbf.c +++ b/drivers/s390/scsi/zfcp_dbf.c @@ -6,6 +6,9 @@ * Copyright IBM Corporation 2002, 2008 */ +#define KMSG_COMPONENT "zfcp" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include <linux/ctype.h> #include <asm/debug.h> #include "zfcp_ext.h" @@ -30,7 +33,7 @@ static void zfcp_dbf_hexdump(debug_info_t *dbf, void *to, int to_len, dump->offset = offset; dump->size = min(from_len - offset, room); memcpy(dump->data, from + offset, dump->size); - debug_event(dbf, level, dump, dump->size); + debug_event(dbf, level, dump, dump->size + sizeof(*dump)); } } @@ -108,7 +111,7 @@ static int zfcp_dbf_view_header(debug_info_t *id, struct debug_view *view, t.tv_sec, t.tv_nsec); zfcp_dbf_out(&p, "cpu", "%02i", entry->id.fields.cpuid); } else { - zfcp_dbf_outd(&p, NULL, dump->data, dump->size, dump->offset, + zfcp_dbf_outd(&p, "", dump->data, dump->size, dump->offset, dump->total_size); if ((dump->offset + dump->size) == dump->total_size) p += sprintf(p, "\n"); @@ -366,6 +369,7 @@ static void zfcp_hba_dbf_view_response(char **p, break; zfcp_dbf_out(p, "scsi_cmnd", "0x%0Lx", r->u.fcp.cmnd); zfcp_dbf_out(p, "scsi_serial", "0x%016Lx", r->u.fcp.serial); + p += sprintf(*p, "\n"); break; case FSF_QTCB_OPEN_PORT_WITH_DID: @@ -465,7 +469,8 @@ static int zfcp_hba_dbf_view_format(debug_info_t *id, struct debug_view *view, else if (strncmp(r->tag, "berr", ZFCP_DBF_TAG_SIZE) == 0) zfcp_hba_dbf_view_berr(&p, &r->u.berr); - p += sprintf(p, "\n"); + if (strncmp(r->tag, "resp", ZFCP_DBF_TAG_SIZE) != 0) + p += sprintf(p, "\n"); return p - out_buf; } @@ -880,6 +885,7 @@ void zfcp_san_dbf_event_ct_request(struct zfcp_fsf_req *fsf_req) struct ct_hdr *hdr = sg_virt(ct->req); struct zfcp_san_dbf_record *r = &adapter->san_dbf_buf; struct zfcp_san_dbf_record_ct_request *oct = &r->u.ct_req; + int level = 3; unsigned long flags; spin_lock_irqsave(&adapter->san_dbf_lock, flags); @@ -896,9 +902,10 @@ void zfcp_san_dbf_event_ct_request(struct zfcp_fsf_req *fsf_req) oct->options = hdr->options; oct->max_res_size = hdr->max_res_size; oct->len = min((int)ct->req->length - (int)sizeof(struct ct_hdr), - ZFCP_DBF_CT_PAYLOAD); - memcpy(oct->payload, (void *)hdr + sizeof(struct ct_hdr), oct->len); - debug_event(adapter->san_dbf, 3, r, sizeof(*r)); + ZFCP_DBF_SAN_MAX_PAYLOAD); + debug_event(adapter->san_dbf, level, r, sizeof(*r)); + zfcp_dbf_hexdump(adapter->san_dbf, r, sizeof(*r), level, + (void *)hdr + sizeof(struct ct_hdr), oct->len); spin_unlock_irqrestore(&adapter->san_dbf_lock, flags); } @@ -914,6 +921,7 @@ void zfcp_san_dbf_event_ct_response(struct zfcp_fsf_req *fsf_req) struct ct_hdr *hdr = sg_virt(ct->resp); struct zfcp_san_dbf_record *r = &adapter->san_dbf_buf; struct zfcp_san_dbf_record_ct_response *rct = &r->u.ct_resp; + int level = 3; unsigned long flags; spin_lock_irqsave(&adapter->san_dbf_lock, flags); @@ -929,9 +937,10 @@ void zfcp_san_dbf_event_ct_response(struct zfcp_fsf_req *fsf_req) rct->expl = hdr->reason_code_expl; rct->vendor_unique = hdr->vendor_unique; rct->len = min((int)ct->resp->length - (int)sizeof(struct ct_hdr), - ZFCP_DBF_CT_PAYLOAD); - memcpy(rct->payload, (void *)hdr + sizeof(struct ct_hdr), rct->len); - debug_event(adapter->san_dbf, 3, r, sizeof(*r)); + ZFCP_DBF_SAN_MAX_PAYLOAD); + debug_event(adapter->san_dbf, level, r, sizeof(*r)); + zfcp_dbf_hexdump(adapter->san_dbf, r, sizeof(*r), level, + (void *)hdr + sizeof(struct ct_hdr), rct->len); spin_unlock_irqrestore(&adapter->san_dbf_lock, flags); } @@ -954,7 +963,7 @@ static void zfcp_san_dbf_event_els(const char *tag, int level, rec->u.els.ls_code = ls_code; debug_event(adapter->san_dbf, level, rec, sizeof(*rec)); zfcp_dbf_hexdump(adapter->san_dbf, rec, sizeof(*rec), level, - buffer, min(buflen, ZFCP_DBF_ELS_MAX_PAYLOAD)); + buffer, min(buflen, ZFCP_DBF_SAN_MAX_PAYLOAD)); spin_unlock_irqrestore(&adapter->san_dbf_lock, flags); } @@ -1008,8 +1017,6 @@ static int zfcp_san_dbf_view_format(debug_info_t *id, struct debug_view *view, char *out_buf, const char *in_buf) { struct zfcp_san_dbf_record *r = (struct zfcp_san_dbf_record *)in_buf; - char *buffer = NULL; - int buflen = 0, total = 0; char *p = out_buf; if (strncmp(r->tag, "dump", ZFCP_DBF_TAG_SIZE) == 0) @@ -1029,9 +1036,6 @@ static int zfcp_san_dbf_view_format(debug_info_t *id, struct debug_view *view, zfcp_dbf_out(&p, "gs_subtype", "0x%02x", ct->gs_subtype); zfcp_dbf_out(&p, "options", "0x%02x", ct->options); zfcp_dbf_out(&p, "max_res_size", "0x%04x", ct->max_res_size); - total = ct->len; - buffer = ct->payload; - buflen = min(total, ZFCP_DBF_CT_PAYLOAD); } else if (strncmp(r->tag, "rctc", ZFCP_DBF_TAG_SIZE) == 0) { struct zfcp_san_dbf_record_ct_response *ct = &r->u.ct_resp; zfcp_dbf_out(&p, "cmd_rsp_code", "0x%04x", ct->cmd_rsp_code); @@ -1039,23 +1043,12 @@ static int zfcp_san_dbf_view_format(debug_info_t *id, struct debug_view *view, zfcp_dbf_out(&p, "reason_code", "0x%02x", ct->reason_code); zfcp_dbf_out(&p, "reason_code_expl", "0x%02x", ct->expl); zfcp_dbf_out(&p, "vendor_unique", "0x%02x", ct->vendor_unique); - total = ct->len; - buffer = ct->payload; - buflen = min(total, ZFCP_DBF_CT_PAYLOAD); } else if (strncmp(r->tag, "oels", ZFCP_DBF_TAG_SIZE) == 0 || strncmp(r->tag, "rels", ZFCP_DBF_TAG_SIZE) == 0 || strncmp(r->tag, "iels", ZFCP_DBF_TAG_SIZE) == 0) { struct zfcp_san_dbf_record_els *els = &r->u.els; zfcp_dbf_out(&p, "ls_code", "0x%02x", els->ls_code); - total = els->len; - buffer = els->payload; - buflen = min(total, ZFCP_DBF_ELS_PAYLOAD); } - - zfcp_dbf_outd(&p, "payload", buffer, buflen, 0, total); - if (buflen == total) - p += sprintf(p, "\n"); - return p - out_buf; } diff --git a/drivers/s390/scsi/zfcp_dbf.h b/drivers/s390/scsi/zfcp_dbf.h index e8f450801fea..5d6b2dff855b 100644 --- a/drivers/s390/scsi/zfcp_dbf.h +++ b/drivers/s390/scsi/zfcp_dbf.h @@ -163,8 +163,6 @@ struct zfcp_san_dbf_record_ct_request { u8 options; u16 max_res_size; u32 len; -#define ZFCP_DBF_CT_PAYLOAD 24 - u8 payload[ZFCP_DBF_CT_PAYLOAD]; } __attribute__ ((packed)); struct zfcp_san_dbf_record_ct_response { @@ -174,15 +172,11 @@ struct zfcp_san_dbf_record_ct_response { u8 expl; u8 vendor_unique; u32 len; - u8 payload[ZFCP_DBF_CT_PAYLOAD]; } __attribute__ ((packed)); struct zfcp_san_dbf_record_els { u8 ls_code; u32 len; -#define ZFCP_DBF_ELS_PAYLOAD 32 -#define ZFCP_DBF_ELS_MAX_PAYLOAD 1024 - u8 payload[ZFCP_DBF_ELS_PAYLOAD]; } __attribute__ ((packed)); struct zfcp_san_dbf_record { @@ -196,6 +190,8 @@ struct zfcp_san_dbf_record { struct zfcp_san_dbf_record_ct_response ct_resp; struct zfcp_san_dbf_record_els els; } u; +#define ZFCP_DBF_SAN_MAX_PAYLOAD 1024 + u8 payload[32]; } __attribute__ ((packed)); struct zfcp_scsi_dbf_record { diff --git a/drivers/s390/scsi/zfcp_def.h b/drivers/s390/scsi/zfcp_def.h index 9ce4c75bd190..e19e46ae4a68 100644 --- a/drivers/s390/scsi/zfcp_def.h +++ b/drivers/s390/scsi/zfcp_def.h @@ -33,6 +33,7 @@ #include <asm/qdio.h> #include <asm/debug.h> #include <asm/ebcdic.h> +#include <asm/sysinfo.h> #include "zfcp_dbf.h" #include "zfcp_fsf.h" @@ -515,6 +516,7 @@ struct zfcp_adapter { struct fsf_qtcb_bottom_port *stats_reset_data; unsigned long stats_reset; struct work_struct scan_work; + struct service_level service_level; atomic_t qdio_outb_full; /* queue full incidents */ }; diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index 9040f738ff33..e63eebf096b9 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -6,6 +6,9 @@ * Copyright IBM Corporation 2002, 2008 */ +#define KMSG_COMPONENT "zfcp" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include "zfcp_ext.h" #define ZFCP_MAX_ERPS 3 @@ -472,6 +475,7 @@ static void zfcp_erp_strategy_check_fsfreq(struct zfcp_erp_action *act) ZFCP_STATUS_ERP_TIMEDOUT)) { act->fsf_req->status |= ZFCP_STATUS_FSFREQ_DISMISSED; zfcp_rec_dbf_event_action(142, act); + act->fsf_req->erp_action = NULL; } if (act->status & ZFCP_STATUS_ERP_TIMEDOUT) zfcp_rec_dbf_event_action(143, act); @@ -1279,8 +1283,11 @@ static void zfcp_erp_action_cleanup(struct zfcp_erp_action *act, int result) break; case ZFCP_ERP_ACTION_REOPEN_ADAPTER: - if (result != ZFCP_ERP_SUCCEEDED) + if (result != ZFCP_ERP_SUCCEEDED) { + unregister_service_level(&adapter->service_level); zfcp_erp_rports_del(adapter); + } else + register_service_level(&adapter->service_level); zfcp_adapter_put(adapter); break; } diff --git a/drivers/s390/scsi/zfcp_fc.c b/drivers/s390/scsi/zfcp_fc.c index 1a7c80a77ff5..eb679f4bfb7a 100644 --- a/drivers/s390/scsi/zfcp_fc.c +++ b/drivers/s390/scsi/zfcp_fc.c @@ -6,6 +6,9 @@ * Copyright IBM Corporation 2008 */ +#define KMSG_COMPONENT "zfcp" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include "zfcp_ext.h" struct ct_iu_gpn_ft_req { diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index 5ae1d497e5ed..5c83ad0c4dfa 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -6,6 +6,9 @@ * Copyright IBM Corporation 2002, 2008 */ +#define KMSG_COMPONENT "zfcp" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include <linux/blktrace_api.h> #include "zfcp_ext.h" @@ -683,6 +686,7 @@ static struct zfcp_fsf_req *zfcp_fsf_alloc_noqtcb(mempool_t *pool) if (!req) return NULL; memset(req, 0, sizeof(*req)); + req->pool = pool; return req; } @@ -769,28 +773,24 @@ static struct zfcp_fsf_req *zfcp_fsf_req_create(struct zfcp_adapter *adapter, static int zfcp_fsf_req_send(struct zfcp_fsf_req *req) { struct zfcp_adapter *adapter = req->adapter; - struct zfcp_qdio_queue *req_q = &adapter->req_q; + unsigned long flags; int idx; /* put allocated FSF request into hash table */ - spin_lock(&adapter->req_list_lock); + spin_lock_irqsave(&adapter->req_list_lock, flags); idx = zfcp_reqlist_hash(req->req_id); list_add_tail(&req->list, &adapter->req_list[idx]); - spin_unlock(&adapter->req_list_lock); + spin_unlock_irqrestore(&adapter->req_list_lock, flags); - req->qdio_outb_usage = atomic_read(&req_q->count); + req->qdio_outb_usage = atomic_read(&adapter->req_q.count); req->issued = get_clock(); if (zfcp_qdio_send(req)) { - /* Queues are down..... */ del_timer(&req->timer); - spin_lock(&adapter->req_list_lock); - zfcp_reqlist_remove(adapter, req); - spin_unlock(&adapter->req_list_lock); - /* undo changes in request queue made for this request */ - atomic_add(req->sbal_number, &req_q->count); - req_q->first -= req->sbal_number; - req_q->first += QDIO_MAX_BUFFERS_PER_Q; - req_q->first %= QDIO_MAX_BUFFERS_PER_Q; /* wrap */ + spin_lock_irqsave(&adapter->req_list_lock, flags); + /* lookup request again, list might have changed */ + if (zfcp_reqlist_find_safe(adapter, req)) + zfcp_reqlist_remove(adapter, req); + spin_unlock_irqrestore(&adapter->req_list_lock, flags); zfcp_erp_adapter_reopen(adapter, 0, 116, req); return -EIO; } diff --git a/drivers/s390/scsi/zfcp_qdio.c b/drivers/s390/scsi/zfcp_qdio.c index 664752f90b20..d3b55fb66f13 100644 --- a/drivers/s390/scsi/zfcp_qdio.c +++ b/drivers/s390/scsi/zfcp_qdio.c @@ -6,6 +6,9 @@ * Copyright IBM Corporation 2002, 2008 */ +#define KMSG_COMPONENT "zfcp" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include "zfcp_ext.h" /* FIXME(tune): free space should be one max. SBAL chain plus what? */ diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index ca8f85f3dad4..64636deea579 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -6,6 +6,9 @@ * Copyright IBM Corporation 2002, 2008 */ +#define KMSG_COMPONENT "zfcp" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include "zfcp_ext.h" #include <asm/atomic.h> @@ -24,14 +27,10 @@ char *zfcp_get_fcp_sns_info_ptr(struct fcp_rsp_iu *fcp_rsp_iu) static void zfcp_scsi_slave_destroy(struct scsi_device *sdpnt) { struct zfcp_unit *unit = (struct zfcp_unit *) sdpnt->hostdata; - WARN_ON(!unit); - if (unit) { - atomic_clear_mask(ZFCP_STATUS_UNIT_REGISTERED, &unit->status); - sdpnt->hostdata = NULL; - unit->device = NULL; - zfcp_erp_unit_failed(unit, 12, NULL); - zfcp_unit_put(unit); - } + atomic_clear_mask(ZFCP_STATUS_UNIT_REGISTERED, &unit->status); + unit->device = NULL; + zfcp_erp_unit_failed(unit, 12, NULL); + zfcp_unit_put(unit); } static int zfcp_scsi_slave_configure(struct scsi_device *sdp) diff --git a/drivers/s390/scsi/zfcp_sysfs.c b/drivers/s390/scsi/zfcp_sysfs.c index ca9293ba1766..899af2b45b1e 100644 --- a/drivers/s390/scsi/zfcp_sysfs.c +++ b/drivers/s390/scsi/zfcp_sysfs.c @@ -6,6 +6,9 @@ * Copyright IBM Corporation 2008 */ +#define KMSG_COMPONENT "zfcp" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include "zfcp_ext.h" #define ZFCP_DEV_ATTR(_feat, _name, _mode, _show, _store) \ diff --git a/drivers/s390/sysinfo.c b/drivers/s390/sysinfo.c index c3e4ab07b9cc..0eea90781385 100644 --- a/drivers/s390/sysinfo.c +++ b/drivers/s390/sysinfo.c @@ -1,17 +1,21 @@ /* * drivers/s390/sysinfo.c * - * Copyright (C) 2001 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Ulrich Weigand (Ulrich.Weigand@de.ibm.com) + * Copyright IBM Corp. 2001, 2008 + * Author(s): Ulrich Weigand (Ulrich.Weigand@de.ibm.com) + * Martin Schwidefsky <schwidefsky@de.ibm.com> */ #include <linux/kernel.h> #include <linux/mm.h> #include <linux/proc_fs.h> +#include <linux/seq_file.h> #include <linux/init.h> #include <linux/delay.h> +#include <linux/module.h> #include <asm/ebcdic.h> #include <asm/sysinfo.h> +#include <asm/cpcmd.h> /* Sigh, math-emu. Don't ask. */ #include <asm/sfp-util.h> @@ -271,6 +275,125 @@ static __init int create_proc_sysinfo(void) __initcall(create_proc_sysinfo); +/* + * Service levels interface. + */ + +static DECLARE_RWSEM(service_level_sem); +static LIST_HEAD(service_level_list); + +int register_service_level(struct service_level *slr) +{ + struct service_level *ptr; + + down_write(&service_level_sem); + list_for_each_entry(ptr, &service_level_list, list) + if (ptr == slr) { + up_write(&service_level_sem); + return -EEXIST; + } + list_add_tail(&slr->list, &service_level_list); + up_write(&service_level_sem); + return 0; +} +EXPORT_SYMBOL(register_service_level); + +int unregister_service_level(struct service_level *slr) +{ + struct service_level *ptr, *next; + int rc = -ENOENT; + + down_write(&service_level_sem); + list_for_each_entry_safe(ptr, next, &service_level_list, list) { + if (ptr != slr) + continue; + list_del(&ptr->list); + rc = 0; + break; + } + up_write(&service_level_sem); + return rc; +} +EXPORT_SYMBOL(unregister_service_level); + +static void *service_level_start(struct seq_file *m, loff_t *pos) +{ + down_read(&service_level_sem); + return seq_list_start(&service_level_list, *pos); +} + +static void *service_level_next(struct seq_file *m, void *p, loff_t *pos) +{ + return seq_list_next(p, &service_level_list, pos); +} + +static void service_level_stop(struct seq_file *m, void *p) +{ + up_read(&service_level_sem); +} + +static int service_level_show(struct seq_file *m, void *p) +{ + struct service_level *slr; + + slr = list_entry(p, struct service_level, list); + slr->seq_print(m, slr); + return 0; +} + +static const struct seq_operations service_level_seq_ops = { + .start = service_level_start, + .next = service_level_next, + .stop = service_level_stop, + .show = service_level_show +}; + +static int service_level_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &service_level_seq_ops); +} + +static const struct file_operations service_level_ops = { + .open = service_level_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release +}; + +static void service_level_vm_print(struct seq_file *m, + struct service_level *slr) +{ + char *query_buffer, *str; + + query_buffer = kmalloc(1024, GFP_KERNEL | GFP_DMA); + if (!query_buffer) + return; + cpcmd("QUERY CPLEVEL", query_buffer, 1024, NULL); + str = strchr(query_buffer, '\n'); + if (str) + *str = 0; + seq_printf(m, "VM: %s\n", query_buffer); + kfree(query_buffer); +} + +static struct service_level service_level_vm = { + .seq_print = service_level_vm_print +}; + +static __init int create_proc_service_level(void) +{ + proc_create("service_levels", 0, NULL, &service_level_ops); + if (MACHINE_IS_VM) + register_service_level(&service_level_vm); + return 0; +} + +subsys_initcall(create_proc_service_level); + +/* + * Bogomips calculation based on cpu capability. + */ + int get_cpu_capability(unsigned int *capability) { struct sysinfo_1_2_2 *info; diff --git a/drivers/sbus/char/bbc_i2c.c b/drivers/sbus/char/bbc_i2c.c index f08e169ba1b5..054f5dd58931 100644 --- a/drivers/sbus/char/bbc_i2c.c +++ b/drivers/sbus/char/bbc_i2c.c @@ -361,7 +361,7 @@ fail: extern int bbc_envctrl_init(struct bbc_i2c_bus *bp); extern void bbc_envctrl_cleanup(struct bbc_i2c_bus *bp); -static int __devinit bbc_i2c_probe(struct of_device *op, +static int __init bbc_i2c_probe(struct of_device *op, const struct of_device_id *match) { struct bbc_i2c_bus *bp; @@ -386,7 +386,7 @@ static int __devinit bbc_i2c_probe(struct of_device *op, return err; } -static int __devexit bbc_i2c_remove(struct of_device *op) +static int __exit bbc_i2c_remove(struct of_device *op) { struct bbc_i2c_bus *bp = dev_get_drvdata(&op->dev); @@ -417,7 +417,7 @@ static struct of_platform_driver bbc_i2c_driver = { .name = "bbc_i2c", .match_table = bbc_i2c_match, .probe = bbc_i2c_probe, - .remove = __devexit_p(bbc_i2c_remove), + .remove = __exit_p(bbc_i2c_remove), }; static int __init bbc_i2c_init(void) diff --git a/drivers/sbus/char/jsflash.c b/drivers/sbus/char/jsflash.c index 2bec9ccc0293..a9a9893a5f95 100644 --- a/drivers/sbus/char/jsflash.c +++ b/drivers/sbus/char/jsflash.c @@ -36,7 +36,6 @@ #include <linux/poll.h> #include <linux/init.h> #include <linux/string.h> -#include <linux/smp_lock.h> #include <linux/genhd.h> #include <linux/blkdev.h> diff --git a/drivers/scsi/advansys.c b/drivers/scsi/advansys.c index 218777bfc143..399fe559e4de 100644 --- a/drivers/scsi/advansys.c +++ b/drivers/scsi/advansys.c @@ -13872,8 +13872,10 @@ static int __devinit advansys_board_found(struct Scsi_Host *shost, advansys_wide_free_mem(boardp); free_irq(boardp->irq, shost); err_free_dma: +#ifdef CONFIG_ISA if (shost->dma_channel != NO_ISA_DMA) free_dma(shost->dma_channel); +#endif err_free_proc: kfree(boardp->prtbuf); err_unmap: @@ -13894,10 +13896,12 @@ static int advansys_release(struct Scsi_Host *shost) ASC_DBG(1, "begin\n"); scsi_remove_host(shost); free_irq(board->irq, shost); +#ifdef CONFIG_ISA if (shost->dma_channel != NO_ISA_DMA) { ASC_DBG(1, "free_dma()\n"); free_dma(shost->dma_channel); } +#endif if (ASC_NARROW_BOARD(board)) { dma_unmap_single(board->dev, board->dvc_var.asc_dvc_var.overrun_dma, diff --git a/drivers/scsi/aic94xx/aic94xx_tmf.c b/drivers/scsi/aic94xx/aic94xx_tmf.c index d4640ef6d44f..78eb86fc6276 100644 --- a/drivers/scsi/aic94xx/aic94xx_tmf.c +++ b/drivers/scsi/aic94xx/aic94xx_tmf.c @@ -189,7 +189,7 @@ int asd_I_T_nexus_reset(struct domain_device *dev) asd_clear_nexus_I_T(dev, NEXUS_PHASE_PRE); /* send a hard reset */ ASD_DPRINTK("sending %s reset to %s\n", - reset_type ? "hard" : "soft", phy->dev.bus_id); + reset_type ? "hard" : "soft", dev_name(&phy->dev)); res = sas_phy_reset(phy, reset_type); if (res == TMF_RESP_FUNC_COMPLETE) { /* wait for the maximum settle time */ diff --git a/drivers/scsi/dpt_i2o.c b/drivers/scsi/dpt_i2o.c index 8aba4fdfb522..6194ed5d02c4 100644 --- a/drivers/scsi/dpt_i2o.c +++ b/drivers/scsi/dpt_i2o.c @@ -2445,7 +2445,7 @@ static s32 adpt_i2o_to_scsi(void __iomem *reply, struct scsi_cmnd* cmd) hba_status = detailed_status >> 8; // calculate resid for sg - scsi_set_resid(cmd, scsi_bufflen(cmd) - readl(reply+5)); + scsi_set_resid(cmd, scsi_bufflen(cmd) - readl(reply+20)); pHba = (adpt_hba*) cmd->device->host->hostdata[0]; @@ -2456,7 +2456,7 @@ static s32 adpt_i2o_to_scsi(void __iomem *reply, struct scsi_cmnd* cmd) case I2O_SCSI_DSC_SUCCESS: cmd->result = (DID_OK << 16); // handle underflow - if(readl(reply+5) < cmd->underflow ) { + if (readl(reply+20) < cmd->underflow) { cmd->result = (DID_ERROR <<16); printk(KERN_WARNING"%s: SCSI CMD underflow\n",pHba->name); } diff --git a/drivers/scsi/gdth.c b/drivers/scsi/gdth.c index c387c15a2128..fb247fdfa2bd 100644 --- a/drivers/scsi/gdth.c +++ b/drivers/scsi/gdth.c @@ -588,7 +588,7 @@ static struct pci_driver gdth_pci_driver = { .remove = gdth_pci_remove_one, }; -static void gdth_pci_remove_one(struct pci_dev *pdev) +static void __devexit gdth_pci_remove_one(struct pci_dev *pdev) { gdth_ha_str *ha = pci_get_drvdata(pdev); @@ -600,7 +600,7 @@ static void gdth_pci_remove_one(struct pci_dev *pdev) pci_disable_device(pdev); } -static int gdth_pci_init_one(struct pci_dev *pdev, +static int __devinit gdth_pci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { ushort vendor = pdev->vendor; @@ -853,7 +853,7 @@ static int __init gdth_init_isa(ulong32 bios_adr,gdth_ha_str *ha) #endif /* CONFIG_ISA */ #ifdef CONFIG_PCI -static int gdth_init_pci(struct pci_dev *pdev, gdth_pci_str *pcistr, +static int __devinit gdth_init_pci(struct pci_dev *pdev, gdth_pci_str *pcistr, gdth_ha_str *ha) { register gdt6_dpram_str __iomem *dp6_ptr; @@ -1237,7 +1237,7 @@ static int gdth_init_pci(struct pci_dev *pdev, gdth_pci_str *pcistr, /* controller protocol functions */ -static void __init gdth_enable_int(gdth_ha_str *ha) +static void __devinit gdth_enable_int(gdth_ha_str *ha) { ulong flags; gdt2_dpram_str __iomem *dp2_ptr; @@ -1553,7 +1553,7 @@ static int gdth_internal_cmd(gdth_ha_str *ha, unchar service, ushort opcode, /* search for devices */ -static int __init gdth_search_drives(gdth_ha_str *ha) +static int __devinit gdth_search_drives(gdth_ha_str *ha) { ushort cdev_cnt, i; int ok; @@ -4935,7 +4935,7 @@ static int __init gdth_eisa_probe_one(ushort eisa_slot) #endif /* CONFIG_EISA */ #ifdef CONFIG_PCI -static int gdth_pci_probe_one(gdth_pci_str *pcistr, +static int __devinit gdth_pci_probe_one(gdth_pci_str *pcistr, gdth_ha_str **ha_out) { struct Scsi_Host *shp; diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 3fdbb13e80a8..aa670a1d1513 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -388,8 +388,7 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize) shost->dma_boundary = 0xffffffff; device_initialize(&shost->shost_gendev); - snprintf(shost->shost_gendev.bus_id, BUS_ID_SIZE, "host%d", - shost->host_no); + dev_set_name(&shost->shost_gendev, "host%d", shost->host_no); #ifndef CONFIG_SYSFS_DEPRECATED shost->shost_gendev.bus = &scsi_bus_type; #endif @@ -398,8 +397,7 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize) device_initialize(&shost->shost_dev); shost->shost_dev.parent = &shost->shost_gendev; shost->shost_dev.class = &shost_class; - snprintf(shost->shost_dev.bus_id, BUS_ID_SIZE, "host%d", - shost->host_no); + dev_set_name(&shost->shost_dev, "host%d", shost->host_no); shost->shost_dev.groups = scsi_sysfs_shost_attr_groups; shost->ehandler = kthread_run(scsi_error_handler, shost, diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index ded854a6dd35..d773030c4194 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -2184,7 +2184,7 @@ static void ipr_dump_location_data(struct ipr_ioa_cfg *ioa_cfg, sizeof(struct ipr_dump_entry_header); driver_dump->location_entry.hdr.data_type = IPR_DUMP_DATA_TYPE_ASCII; driver_dump->location_entry.hdr.id = IPR_DUMP_LOCATION_ID; - strcpy(driver_dump->location_entry.location, ioa_cfg->pdev->dev.bus_id); + strcpy(driver_dump->location_entry.location, dev_name(&ioa_cfg->pdev->dev)); driver_dump->hdr.num_entries++; } diff --git a/drivers/scsi/ipr.h b/drivers/scsi/ipr.h index 4871dd1f2582..d5723c92c322 100644 --- a/drivers/scsi/ipr.h +++ b/drivers/scsi/ipr.h @@ -1272,7 +1272,7 @@ struct ipr_dump_entry_header { struct ipr_dump_location_entry { struct ipr_dump_entry_header hdr; - u8 location[BUS_ID_SIZE]; + u8 location[20]; }__attribute__((packed)); struct ipr_dump_trace_entry { diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index 709a6f75ca9d..facc5bfcf7db 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -169,7 +169,7 @@ int sas_notify_lldd_dev_found(struct domain_device *dev) if (res) { printk("sas: driver on pcidev %s cannot handle " "device %llx, error:%d\n", - sas_ha->dev->bus_id, + dev_name(sas_ha->dev), SAS_ADDR(dev->sas_addr), res); } } diff --git a/drivers/scsi/libsas/sas_dump.c b/drivers/scsi/libsas/sas_dump.c index bf34a236f946..c17c25030f1c 100644 --- a/drivers/scsi/libsas/sas_dump.c +++ b/drivers/scsi/libsas/sas_dump.c @@ -56,7 +56,7 @@ void sas_dprint_phye(int phyid, enum phy_event pe) void sas_dprint_hae(struct sas_ha_struct *sas_ha, enum ha_event he) { - SAS_DPRINTK("ha %s: %s event\n", sas_ha->dev->bus_id, + SAS_DPRINTK("ha %s: %s event\n", dev_name(sas_ha->dev), sas_hae_str[he]); } diff --git a/drivers/scsi/libsas/sas_port.c b/drivers/scsi/libsas/sas_port.c index 139935a121b4..e6ac59c023f1 100644 --- a/drivers/scsi/libsas/sas_port.c +++ b/drivers/scsi/libsas/sas_port.c @@ -113,7 +113,7 @@ static void sas_form_port(struct asd_sas_phy *phy) sas_port_add_phy(port->port, phy->phy); SAS_DPRINTK("%s added to %s, phy_mask:0x%x (%16llx)\n", - phy->phy->dev.bus_id,port->port->dev.bus_id, + dev_name(&phy->phy->dev), dev_name(&port->port->dev), port->phy_mask, SAS_ADDR(port->attached_sas_addr)); diff --git a/drivers/scsi/mac_esp.c b/drivers/scsi/mac_esp.c index 887682a24e36..c24e86f07804 100644 --- a/drivers/scsi/mac_esp.c +++ b/drivers/scsi/mac_esp.c @@ -53,7 +53,8 @@ struct mac_esp_priv { void __iomem *pdma_io; int error; }; -static struct platform_device *internal_esp, *external_esp; +static struct platform_device *internal_pdev, *external_pdev; +static struct esp *esp_chips[2]; #define MAC_ESP_GET_PRIV(esp) ((struct mac_esp_priv *) \ platform_get_drvdata((struct platform_device *) \ @@ -170,7 +171,7 @@ static inline int mac_esp_wait_for_dreq(struct esp *esp) #define MAC_ESP_PDMA_LOOP(operands) \ asm volatile ( \ - " tstw %2 \n" \ + " tstw %1 \n" \ " jbeq 20f \n" \ "1: movew " operands " \n" \ "2: movew " operands " \n" \ @@ -188,14 +189,14 @@ static inline int mac_esp_wait_for_dreq(struct esp *esp) "14: movew " operands " \n" \ "15: movew " operands " \n" \ "16: movew " operands " \n" \ - " subqw #1,%2 \n" \ + " subqw #1,%1 \n" \ " jbne 1b \n" \ - "20: tstw %3 \n" \ + "20: tstw %2 \n" \ " jbeq 30f \n" \ "21: movew " operands " \n" \ - " subqw #1,%3 \n" \ + " subqw #1,%2 \n" \ " jbne 21b \n" \ - "30: tstw %4 \n" \ + "30: tstw %3 \n" \ " jbeq 40f \n" \ "31: moveb " operands " \n" \ "32: nop \n" \ @@ -223,8 +224,8 @@ static inline int mac_esp_wait_for_dreq(struct esp *esp) " .long 31b,40b \n" \ " .long 32b,40b \n" \ " .previous \n" \ - : "+a" (addr) \ - : "a" (mep->pdma_io), "r" (count32), "r" (count2), "g" (esp_count)) + : "+a" (addr), "+r" (count32), "+r" (count2) \ + : "g" (count1), "a" (mep->pdma_io)) static void mac_esp_send_pdma_cmd(struct esp *esp, u32 addr, u32 esp_count, u32 dma_count, int write, u8 cmd) @@ -247,19 +248,20 @@ static void mac_esp_send_pdma_cmd(struct esp *esp, u32 addr, u32 esp_count, do { unsigned int count32 = esp_count >> 5; unsigned int count2 = (esp_count & 0x1F) >> 1; + unsigned int count1 = esp_count & 1; unsigned int start_addr = addr; if (mac_esp_wait_for_dreq(esp)) break; if (write) { - MAC_ESP_PDMA_LOOP("%1@,%0@+"); + MAC_ESP_PDMA_LOOP("%4@,%0@+"); esp_count -= addr - start_addr; } else { unsigned int n; - MAC_ESP_PDMA_LOOP("%0@+,%1@"); + MAC_ESP_PDMA_LOOP("%0@+,%4@"); if (mac_esp_wait_for_empty_fifo(esp)) break; @@ -442,6 +444,32 @@ static u32 mac_esp_dma_length_limit(struct esp *esp, u32 dma_addr, u32 dma_len) return dma_len > 0xFFFF ? 0xFFFF : dma_len; } +static irqreturn_t mac_scsi_esp_intr(int irq, void *dev_id) +{ + int got_intr; + + /* + * This is an edge triggered IRQ, so we have to be careful to + * avoid missing a transition when it is shared by two ESP devices. + */ + + do { + got_intr = 0; + if (esp_chips[0] && + (mac_esp_read8(esp_chips[0], ESP_STATUS) & ESP_STAT_INTR)) { + (void)scsi_esp_intr(irq, esp_chips[0]); + got_intr = 1; + } + if (esp_chips[1] && + (mac_esp_read8(esp_chips[1], ESP_STATUS) & ESP_STAT_INTR)) { + (void)scsi_esp_intr(irq, esp_chips[1]); + got_intr = 1; + } + } while (got_intr); + + return IRQ_HANDLED; +} + static struct esp_driver_ops mac_esp_ops = { .esp_write8 = mac_esp_write8, .esp_read8 = mac_esp_read8, @@ -556,10 +584,16 @@ static int __devinit esp_mac_probe(struct platform_device *dev) } host->irq = IRQ_MAC_SCSI; - err = request_irq(host->irq, scsi_esp_intr, IRQF_SHARED, "Mac ESP", - esp); - if (err < 0) - goto fail_free_priv; + esp_chips[dev->id] = esp; + mb(); + if (esp_chips[!dev->id] == NULL) { + err = request_irq(host->irq, mac_scsi_esp_intr, 0, + "Mac ESP", NULL); + if (err < 0) { + esp_chips[dev->id] = NULL; + goto fail_free_priv; + } + } err = scsi_esp_register(esp, &dev->dev); if (err) @@ -568,7 +602,8 @@ static int __devinit esp_mac_probe(struct platform_device *dev) return 0; fail_free_irq: - free_irq(host->irq, esp); + if (esp_chips[!dev->id] == NULL) + free_irq(host->irq, esp); fail_free_priv: kfree(mep); fail_free_command_block: @@ -587,7 +622,9 @@ static int __devexit esp_mac_remove(struct platform_device *dev) scsi_esp_unregister(esp); - free_irq(irq, esp); + esp_chips[dev->id] = NULL; + if (!(esp_chips[0] || esp_chips[1])) + free_irq(irq, NULL); kfree(mep); @@ -614,19 +651,18 @@ static int __init mac_esp_init(void) if (err) return err; - internal_esp = platform_device_alloc(DRV_MODULE_NAME, 0); - if (internal_esp && platform_device_add(internal_esp)) { - platform_device_put(internal_esp); - internal_esp = NULL; + internal_pdev = platform_device_alloc(DRV_MODULE_NAME, 0); + if (internal_pdev && platform_device_add(internal_pdev)) { + platform_device_put(internal_pdev); + internal_pdev = NULL; } - - external_esp = platform_device_alloc(DRV_MODULE_NAME, 1); - if (external_esp && platform_device_add(external_esp)) { - platform_device_put(external_esp); - external_esp = NULL; + external_pdev = platform_device_alloc(DRV_MODULE_NAME, 1); + if (external_pdev && platform_device_add(external_pdev)) { + platform_device_put(external_pdev); + external_pdev = NULL; } - if (internal_esp || external_esp) { + if (internal_pdev || external_pdev) { return 0; } else { platform_driver_unregister(&esp_mac_driver); @@ -638,13 +674,13 @@ static void __exit mac_esp_exit(void) { platform_driver_unregister(&esp_mac_driver); - if (internal_esp) { - platform_device_unregister(internal_esp); - internal_esp = NULL; + if (internal_pdev) { + platform_device_unregister(internal_pdev); + internal_pdev = NULL; } - if (external_esp) { - platform_device_unregister(external_esp); - external_esp = NULL; + if (external_pdev) { + platform_device_unregister(external_pdev); + external_pdev = NULL; } } diff --git a/drivers/scsi/mac_scsi.c b/drivers/scsi/mac_scsi.c index 0248919bc2df..bf2a1c516293 100644 --- a/drivers/scsi/mac_scsi.c +++ b/drivers/scsi/mac_scsi.c @@ -47,7 +47,6 @@ #include <asm/macintosh.h> #include <asm/macints.h> -#include <asm/machw.h> #include <asm/mac_via.h> #include "scsi.h" diff --git a/drivers/scsi/megaraid.c b/drivers/scsi/megaraid.c index 28c9da7d4a5c..7dc62deb4087 100644 --- a/drivers/scsi/megaraid.c +++ b/drivers/scsi/megaraid.c @@ -4402,6 +4402,10 @@ mega_internal_command(adapter_t *adapter, megacmd_t *mc, mega_passthru *pthru) scb_t *scb; int rval; + scmd = scsi_allocate_command(GFP_KERNEL); + if (!scmd) + return -ENOMEM; + /* * The internal commands share one command id and hence are * serialized. This is so because we want to reserve maximum number of @@ -4412,12 +4416,11 @@ mega_internal_command(adapter_t *adapter, megacmd_t *mc, mega_passthru *pthru) scb = &adapter->int_scb; memset(scb, 0, sizeof(scb_t)); - scmd = &adapter->int_scmd; - memset(scmd, 0, sizeof(Scsi_Cmnd)); - sdev = kzalloc(sizeof(struct scsi_device), GFP_KERNEL); scmd->device = sdev; + memset(adapter->int_cdb, 0, sizeof(adapter->int_cdb)); + scmd->cmnd = adapter->int_cdb; scmd->device->host = adapter->host; scmd->host_scribble = (void *)scb; scmd->cmnd[0] = MEGA_INTERNAL_CMD; @@ -4456,6 +4459,8 @@ mega_internal_command(adapter_t *adapter, megacmd_t *mc, mega_passthru *pthru) mutex_unlock(&adapter->int_mtx); + scsi_free_command(GFP_KERNEL, scmd); + return rval; } diff --git a/drivers/scsi/megaraid.h b/drivers/scsi/megaraid.h index ee70bd4ae4ba..795201fa0b48 100644 --- a/drivers/scsi/megaraid.h +++ b/drivers/scsi/megaraid.h @@ -888,8 +888,8 @@ typedef struct { u8 sglen; /* f/w supported scatter-gather list length */ + unsigned char int_cdb[MAX_COMMAND_SIZE]; scb_t int_scb; - Scsi_Cmnd int_scmd; struct mutex int_mtx; /* To synchronize the internal commands */ struct completion int_waitq; /* wait queue for internal diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index f25f41a499e5..b97194096d8e 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -2547,7 +2547,6 @@ typedef struct scsi_qla_host { uint8_t fcode_revision[16]; uint32_t fw_revision[4]; - uint16_t fdt_odd_index; uint32_t fdt_wrt_disable; uint32_t fdt_erase_cmd; uint32_t fdt_block_size; diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index a470f2d3270d..4218f20f5ed5 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -140,7 +140,6 @@ int qla2100_pci_config(scsi_qla_host_t *ha) { uint16_t w; - uint32_t d; unsigned long flags; struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; @@ -151,10 +150,7 @@ qla2100_pci_config(scsi_qla_host_t *ha) w |= (PCI_COMMAND_PARITY | PCI_COMMAND_SERR); pci_write_config_word(ha->pdev, PCI_COMMAND, w); - /* Reset expansion ROM address decode enable */ - pci_read_config_dword(ha->pdev, PCI_ROM_ADDRESS, &d); - d &= ~PCI_ROM_ADDRESS_ENABLE; - pci_write_config_dword(ha->pdev, PCI_ROM_ADDRESS, d); + pci_disable_rom(ha->pdev); /* Get PCI bus information. */ spin_lock_irqsave(&ha->hardware_lock, flags); @@ -174,7 +170,6 @@ int qla2300_pci_config(scsi_qla_host_t *ha) { uint16_t w; - uint32_t d; unsigned long flags = 0; uint32_t cnt; struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; @@ -236,10 +231,7 @@ qla2300_pci_config(scsi_qla_host_t *ha) pci_write_config_byte(ha->pdev, PCI_LATENCY_TIMER, 0x80); - /* Reset expansion ROM address decode enable */ - pci_read_config_dword(ha->pdev, PCI_ROM_ADDRESS, &d); - d &= ~PCI_ROM_ADDRESS_ENABLE; - pci_write_config_dword(ha->pdev, PCI_ROM_ADDRESS, d); + pci_disable_rom(ha->pdev); /* Get PCI bus information. */ spin_lock_irqsave(&ha->hardware_lock, flags); @@ -259,7 +251,6 @@ int qla24xx_pci_config(scsi_qla_host_t *ha) { uint16_t w; - uint32_t d; unsigned long flags = 0; struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; @@ -281,10 +272,7 @@ qla24xx_pci_config(scsi_qla_host_t *ha) if (pci_find_capability(ha->pdev, PCI_CAP_ID_EXP)) pcie_set_readrq(ha->pdev, 2048); - /* Reset expansion ROM address decode enable */ - pci_read_config_dword(ha->pdev, PCI_ROM_ADDRESS, &d); - d &= ~PCI_ROM_ADDRESS_ENABLE; - pci_write_config_dword(ha->pdev, PCI_ROM_ADDRESS, d); + pci_disable_rom(ha->pdev); ha->chip_revision = ha->pdev->revision; @@ -306,7 +294,6 @@ int qla25xx_pci_config(scsi_qla_host_t *ha) { uint16_t w; - uint32_t d; pci_set_master(ha->pdev); pci_try_set_mwi(ha->pdev); @@ -320,10 +307,7 @@ qla25xx_pci_config(scsi_qla_host_t *ha) if (pci_find_capability(ha->pdev, PCI_CAP_ID_EXP)) pcie_set_readrq(ha->pdev, 2048); - /* Reset expansion ROM address decode enable */ - pci_read_config_dword(ha->pdev, PCI_ROM_ADDRESS, &d); - d &= ~PCI_ROM_ADDRESS_ENABLE; - pci_write_config_dword(ha->pdev, PCI_ROM_ADDRESS, d); + pci_disable_rom(ha->pdev); ha->chip_revision = ha->pdev->revision; @@ -980,7 +964,6 @@ qla2x00_setup_chip(scsi_qla_host_t *ha) &ha->fw_minor_version, &ha->fw_subminor_version, &ha->fw_attributes, &ha->fw_memory_size); - qla2x00_resize_request_q(ha); ha->flags.npiv_supported = 0; if ((IS_QLA24XX(ha) || IS_QLA25XX(ha) || IS_QLA84XX(ha)) && @@ -992,6 +975,7 @@ qla2x00_setup_chip(scsi_qla_host_t *ha) ha->max_npiv_vports = MIN_MULTI_ID_FABRIC - 1; } + qla2x00_resize_request_q(ha); if (ql2xallocfwdump) qla2x00_alloc_fw_dump(ha); diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c index 36bc6851e23d..3402746ec128 100644 --- a/drivers/scsi/qla2xxx/qla_mbx.c +++ b/drivers/scsi/qla2xxx/qla_mbx.c @@ -1964,7 +1964,7 @@ qla2x00_get_resource_cnts(scsi_qla_host_t *ha, uint16_t *cur_xchg_cnt, *cur_iocb_cnt = mcp->mb[7]; if (orig_iocb_cnt) *orig_iocb_cnt = mcp->mb[10]; - if (max_npiv_vports) + if (ha->flags.npiv_supported && max_npiv_vports) *max_npiv_vports = mcp->mb[11]; } diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 21dd182ad512..35567203ef61 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -728,6 +728,7 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd) if (ha->isp_ops->abort_command(ha, sp)) { DEBUG2(printk("%s(%ld): abort_command " "mbx failed.\n", __func__, ha->host_no)); + ret = FAILED; } else { DEBUG3(printk("%s(%ld): abort_command " "mbx success.\n", __func__, ha->host_no)); diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c index 90a13211717f..e4af678eb2d6 100644 --- a/drivers/scsi/qla2xxx/qla_sup.c +++ b/drivers/scsi/qla2xxx/qla_sup.c @@ -722,6 +722,7 @@ done: static void qla2xxx_get_fdt_info(scsi_qla_host_t *ha) { +#define FLASH_BLK_SIZE_4K 0x1000 #define FLASH_BLK_SIZE_32K 0x8000 #define FLASH_BLK_SIZE_64K 0x10000 const char *loc, *locations[] = { "MID", "FDT" }; @@ -755,7 +756,6 @@ qla2xxx_get_fdt_info(scsi_qla_host_t *ha) loc = locations[1]; mid = le16_to_cpu(fdt->man_id); fid = le16_to_cpu(fdt->id); - ha->fdt_odd_index = mid == 0x1f; ha->fdt_wrt_disable = fdt->wrt_disable_bits; ha->fdt_erase_cmd = flash_conf_to_access_addr(0x0300 | fdt->erase_cmd); ha->fdt_block_size = le32_to_cpu(fdt->block_size); @@ -788,8 +788,7 @@ no_flash_data: ha->fdt_block_size = FLASH_BLK_SIZE_64K; break; case 0x1f: /* Atmel 26DF081A. */ - ha->fdt_odd_index = 1; - ha->fdt_block_size = FLASH_BLK_SIZE_64K; + ha->fdt_block_size = FLASH_BLK_SIZE_4K; ha->fdt_erase_cmd = flash_conf_to_access_addr(0x0320); ha->fdt_unprotect_sec_cmd = flash_conf_to_access_addr(0x0339); ha->fdt_protect_sec_cmd = flash_conf_to_access_addr(0x0336); @@ -801,9 +800,9 @@ no_flash_data: } done: DEBUG2(qla_printk(KERN_DEBUG, ha, "FDT[%s]: (0x%x/0x%x) erase=0x%x " - "pro=%x upro=%x idx=%d wrtd=0x%x blk=0x%x.\n", loc, mid, fid, + "pro=%x upro=%x wrtd=0x%x blk=0x%x.\n", loc, mid, fid, ha->fdt_erase_cmd, ha->fdt_protect_sec_cmd, - ha->fdt_unprotect_sec_cmd, ha->fdt_odd_index, ha->fdt_wrt_disable, + ha->fdt_unprotect_sec_cmd, ha->fdt_wrt_disable, ha->fdt_block_size)); } @@ -987,13 +986,9 @@ qla24xx_write_flash_data(scsi_qla_host_t *ha, uint32_t *dwptr, uint32_t faddr, qla24xx_unprotect_flash(ha); for (liter = 0; liter < dwords; liter++, faddr++, dwptr++) { - if (ha->fdt_odd_index) { - findex = faddr << 2; - fdata = findex & sec_mask; - } else { - findex = faddr; - fdata = (findex & sec_mask) << 2; - } + + findex = faddr; + fdata = (findex & sec_mask) << 2; /* Are we at the beginning of a sector? */ if ((findex & rest_addr) == 0) { diff --git a/drivers/scsi/qla2xxx/qla_version.h b/drivers/scsi/qla2xxx/qla_version.h index be5e299df528..eea6720adf16 100644 --- a/drivers/scsi/qla2xxx/qla_version.h +++ b/drivers/scsi/qla2xxx/qla_version.h @@ -7,7 +7,7 @@ /* * Driver version */ -#define QLA2XXX_VERSION "8.02.01-k8" +#define QLA2XXX_VERSION "8.02.01-k9" #define QLA_DRIVER_MAJOR_VER 8 #define QLA_DRIVER_MINOR_VER 2 diff --git a/drivers/scsi/raid_class.c b/drivers/scsi/raid_class.c index 913a931176ef..8e5c169b03fb 100644 --- a/drivers/scsi/raid_class.c +++ b/drivers/scsi/raid_class.c @@ -237,8 +237,7 @@ int raid_component_add(struct raid_template *r,struct device *raid_dev, rc->dev.parent = get_device(component_dev); rc->num = rd->component_count++; - snprintf(rc->dev.bus_id, sizeof(rc->dev.bus_id), - "component-%d", rc->num); + dev_set_name(&rc->dev, "component-%d", rc->num); list_add_tail(&rc->node, &rd->component_list); rc->dev.class = &raid_class.class; err = device_add(&rc->dev); diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index 27c633f55794..6eebd0bbe8a8 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -2508,7 +2508,7 @@ static void pseudo_0_release(struct device *dev) } static struct device pseudo_primary = { - .bus_id = "pseudo_0", + .init_name = "pseudo_0", .release = pseudo_0_release, }; @@ -2680,7 +2680,7 @@ static int sdebug_add_adapter(void) sdbg_host->dev.bus = &pseudo_lld_bus; sdbg_host->dev.parent = &pseudo_primary; sdbg_host->dev.release = &sdebug_release_adapter; - sprintf(sdbg_host->dev.bus_id, "adapter%d", scsi_debug_add_host); + dev_set_name(&sdbg_host->dev, "adapter%d", scsi_debug_add_host); error = device_register(&sdbg_host->dev); diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 94ed262bdf0c..386361778ebb 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -1340,9 +1340,10 @@ int scsi_decide_disposition(struct scsi_cmnd *scmd) * LLD/transport was disrupted during processing of the IO. * The transport class is now blocked/blocking, * and the transport will decide what to do with the IO - * based on its timers and recovery capablilities. + * based on its timers and recovery capablilities if + * there are enough retries. */ - return ADD_TO_MLQUEUE; + goto maybe_retry; case DID_TRANSPORT_FAILFAST: /* * The transport decided to failfast the IO (most likely diff --git a/drivers/scsi/scsi_ioctl.c b/drivers/scsi/scsi_ioctl.c index dc1cfb2fd76b..9ed8e2cbac30 100644 --- a/drivers/scsi/scsi_ioctl.c +++ b/drivers/scsi/scsi_ioctl.c @@ -170,7 +170,8 @@ static int scsi_ioctl_get_pci(struct scsi_device *sdev, void __user *arg) if (!dev) return -ENXIO; - return copy_to_user(arg, dev->bus_id, sizeof(dev->bus_id))? -EFAULT: 0; + return copy_to_user(arg, + dev_name(dev), strlen(dev_name(dev)))? -EFAULT: 0; } diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index f5d3b96890dc..fa45a1a66867 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -567,15 +567,18 @@ static inline int scsi_host_is_busy(struct Scsi_Host *shost) */ static void scsi_run_queue(struct request_queue *q) { - struct scsi_device *starved_head = NULL, *sdev = q->queuedata; + struct scsi_device *sdev = q->queuedata; struct Scsi_Host *shost = sdev->host; + LIST_HEAD(starved_list); unsigned long flags; if (scsi_target(sdev)->single_lun) scsi_single_lun_run(sdev); spin_lock_irqsave(shost->host_lock, flags); - while (!list_empty(&shost->starved_list) && !scsi_host_is_busy(shost)) { + list_splice_init(&shost->starved_list, &starved_list); + + while (!list_empty(&starved_list)) { int flagset; /* @@ -588,24 +591,18 @@ static void scsi_run_queue(struct request_queue *q) * scsi_request_fn must get the host_lock before checking * or modifying starved_list or starved_entry. */ - sdev = list_entry(shost->starved_list.next, - struct scsi_device, starved_entry); - /* - * The *queue_ready functions can add a device back onto the - * starved list's tail, so we must check for a infinite loop. - */ - if (sdev == starved_head) + if (scsi_host_is_busy(shost)) break; - if (!starved_head) - starved_head = sdev; + sdev = list_entry(starved_list.next, + struct scsi_device, starved_entry); + list_del_init(&sdev->starved_entry); if (scsi_target_is_busy(scsi_target(sdev))) { list_move_tail(&sdev->starved_entry, &shost->starved_list); continue; } - list_del_init(&sdev->starved_entry); spin_unlock(shost->host_lock); spin_lock(sdev->request_queue->queue_lock); @@ -621,6 +618,8 @@ static void scsi_run_queue(struct request_queue *q) spin_lock(shost->host_lock); } + /* put any unprocessed entries back */ + list_splice(&starved_list, &shost->starved_list); spin_unlock_irqrestore(shost->host_lock, flags); blk_run_queue(q); diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index b14dc02c3ded..61141fb189fc 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -411,8 +411,7 @@ static struct scsi_target *scsi_alloc_target(struct device *parent, device_initialize(dev); starget->reap_ref = 1; dev->parent = get_device(parent); - sprintf(dev->bus_id, "target%d:%d:%d", - shost->host_no, channel, id); + dev_set_name(dev, "target%d:%d:%d", shost->host_no, channel, id); #ifndef CONFIG_SYSFS_DEPRECATED dev->bus = &scsi_bus_type; #endif @@ -1010,7 +1009,7 @@ static int scsi_probe_and_add_lun(struct scsi_target *starget, if (rescan || !scsi_device_created(sdev)) { SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: device exists on %s\n", - sdev->sdev_gendev.bus_id)); + dev_name(&sdev->sdev_gendev))); if (sdevp) *sdevp = sdev; else @@ -1149,7 +1148,7 @@ static void scsi_sequential_lun_scan(struct scsi_target *starget, struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: Sequential scan of" - "%s\n", starget->dev.bus_id)); + "%s\n", dev_name(&starget->dev))); max_dev_lun = min(max_scsi_luns, shost->max_lun); /* diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 93c28f30bbd7..da63802cbf9d 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -1079,16 +1079,14 @@ void scsi_sysfs_device_initialize(struct scsi_device *sdev) device_initialize(&sdev->sdev_gendev); sdev->sdev_gendev.bus = &scsi_bus_type; sdev->sdev_gendev.type = &scsi_dev_type; - sprintf(sdev->sdev_gendev.bus_id,"%d:%d:%d:%d", - sdev->host->host_no, sdev->channel, sdev->id, - sdev->lun); - + dev_set_name(&sdev->sdev_gendev, "%d:%d:%d:%d", + sdev->host->host_no, sdev->channel, sdev->id, sdev->lun); + device_initialize(&sdev->sdev_dev); sdev->sdev_dev.parent = &sdev->sdev_gendev; sdev->sdev_dev.class = &sdev_class; - snprintf(sdev->sdev_dev.bus_id, BUS_ID_SIZE, - "%d:%d:%d:%d", sdev->host->host_no, - sdev->channel, sdev->id, sdev->lun); + dev_set_name(&sdev->sdev_dev, "%d:%d:%d:%d", + sdev->host->host_no, sdev->channel, sdev->id, sdev->lun); sdev->scsi_level = starget->scsi_level; transport_setup_device(&sdev->sdev_gendev); spin_lock_irqsave(shost->host_lock, flags); diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index 1e71abf0607a..c845853600a2 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -2486,8 +2486,8 @@ fc_rport_create(struct Scsi_Host *shost, int channel, device_initialize(dev); /* takes self reference */ dev->parent = get_device(&shost->shost_gendev); /* parent reference */ dev->release = fc_rport_dev_release; - sprintf(dev->bus_id, "rport-%d:%d-%d", - shost->host_no, channel, rport->number); + dev_set_name(dev, "rport-%d:%d-%d", + shost->host_no, channel, rport->number); transport_setup_device(dev); error = device_add(dev); @@ -3157,8 +3157,8 @@ fc_vport_setup(struct Scsi_Host *shost, int channel, struct device *pdev, device_initialize(dev); /* takes self reference */ dev->parent = get_device(pdev); /* takes parent reference */ dev->release = fc_vport_dev_release; - sprintf(dev->bus_id, "vport-%d:%d-%d", - shost->host_no, channel, vport->number); + dev_set_name(dev, "vport-%d:%d-%d", + shost->host_no, channel, vport->number); transport_setup_device(dev); error = device_add(dev); @@ -3181,19 +3181,19 @@ fc_vport_setup(struct Scsi_Host *shost, int channel, struct device *pdev, */ if (pdev != &shost->shost_gendev) { error = sysfs_create_link(&shost->shost_gendev.kobj, - &dev->kobj, dev->bus_id); + &dev->kobj, dev_name(dev)); if (error) printk(KERN_ERR "%s: Cannot create vport symlinks for " "%s, err=%d\n", - __func__, dev->bus_id, error); + __func__, dev_name(dev), error); } spin_lock_irqsave(shost->host_lock, flags); vport->flags &= ~FC_VPORT_CREATING; spin_unlock_irqrestore(shost->host_lock, flags); dev_printk(KERN_NOTICE, pdev, - "%s created via shost%d channel %d\n", dev->bus_id, + "%s created via shost%d channel %d\n", dev_name(dev), shost->host_no, channel); *ret_vport = vport; @@ -3290,7 +3290,7 @@ fc_vport_terminate(struct fc_vport *vport) return stat; if (dev->parent != &shost->shost_gendev) - sysfs_remove_link(&shost->shost_gendev.kobj, dev->bus_id); + sysfs_remove_link(&shost->shost_gendev.kobj, dev_name(dev)); transport_remove_device(dev); device_del(dev); transport_destroy_device(dev); @@ -3322,7 +3322,7 @@ fc_vport_sched_delete(struct work_struct *work) dev_printk(KERN_ERR, vport->dev.parent, "%s: %s could not be deleted created via " "shost%d channel %d - error %d\n", __func__, - vport->dev.bus_id, vport->shost->host_no, + dev_name(&vport->dev), vport->shost->host_no, vport->channel, stat); } diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index 4a803ebaf508..75c9297694cb 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -187,8 +187,7 @@ iscsi_create_endpoint(int dd_size) ep->id = id; ep->dev.class = &iscsi_endpoint_class; - snprintf(ep->dev.bus_id, BUS_ID_SIZE, "ep-%llu", - (unsigned long long) id); + dev_set_name(&ep->dev, "ep-%llu", (unsigned long long) id); err = device_register(&ep->dev); if (err) goto free_ep; @@ -724,8 +723,7 @@ int iscsi_add_session(struct iscsi_cls_session *session, unsigned int target_id) } session->target_id = id; - snprintf(session->dev.bus_id, BUS_ID_SIZE, "session%u", - session->sid); + dev_set_name(&session->dev, "session%u", session->sid); err = device_add(&session->dev); if (err) { iscsi_cls_session_printk(KERN_ERR, session, @@ -898,8 +896,7 @@ iscsi_create_conn(struct iscsi_cls_session *session, int dd_size, uint32_t cid) if (!get_device(&session->dev)) goto free_conn; - snprintf(conn->dev.bus_id, BUS_ID_SIZE, "connection%d:%u", - session->sid, cid); + dev_set_name(&conn->dev, "connection%d:%u", session->sid, cid); conn->dev.parent = &session->dev; conn->dev.release = iscsi_conn_release; err = device_register(&conn->dev); @@ -1816,7 +1813,7 @@ iscsi_register_transport(struct iscsi_transport *tt) priv->t.create_work_queue = 1; priv->dev.class = &iscsi_transport_class; - snprintf(priv->dev.bus_id, BUS_ID_SIZE, "%s", tt->name); + dev_set_name(&priv->dev, "%s", tt->name); err = device_register(&priv->dev); if (err) goto free_priv; diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c index 366609386be1..50988cbf7b2d 100644 --- a/drivers/scsi/scsi_transport_sas.c +++ b/drivers/scsi/scsi_transport_sas.c @@ -207,7 +207,7 @@ static int sas_bsg_initialize(struct Scsi_Host *shost, struct sas_rphy *rphy) struct request_queue *q; int error; struct device *dev; - char namebuf[BUS_ID_SIZE]; + char namebuf[20]; const char *name; void (*release)(struct device *); @@ -219,7 +219,7 @@ static int sas_bsg_initialize(struct Scsi_Host *shost, struct sas_rphy *rphy) if (rphy) { q = blk_init_queue(sas_non_host_smp_request, NULL); dev = &rphy->dev; - name = dev->bus_id; + name = dev_name(dev); release = NULL; } else { q = blk_init_queue(sas_host_smp_request, NULL); @@ -629,10 +629,10 @@ struct sas_phy *sas_phy_alloc(struct device *parent, int number) INIT_LIST_HEAD(&phy->port_siblings); if (scsi_is_sas_expander_device(parent)) { struct sas_rphy *rphy = dev_to_rphy(parent); - sprintf(phy->dev.bus_id, "phy-%d:%d:%d", shost->host_no, + dev_set_name(&phy->dev, "phy-%d:%d:%d", shost->host_no, rphy->scsi_target_id, number); } else - sprintf(phy->dev.bus_id, "phy-%d:%d", shost->host_no, number); + dev_set_name(&phy->dev, "phy-%d:%d", shost->host_no, number); transport_setup_device(&phy->dev); @@ -770,7 +770,7 @@ static void sas_port_create_link(struct sas_port *port, int res; res = sysfs_create_link(&port->dev.kobj, &phy->dev.kobj, - phy->dev.bus_id); + dev_name(&phy->dev)); if (res) goto err; res = sysfs_create_link(&phy->dev.kobj, &port->dev.kobj, "port"); @@ -785,7 +785,7 @@ err: static void sas_port_delete_link(struct sas_port *port, struct sas_phy *phy) { - sysfs_remove_link(&port->dev.kobj, phy->dev.bus_id); + sysfs_remove_link(&port->dev.kobj, dev_name(&phy->dev)); sysfs_remove_link(&phy->dev.kobj, "port"); } @@ -821,11 +821,11 @@ struct sas_port *sas_port_alloc(struct device *parent, int port_id) if (scsi_is_sas_expander_device(parent)) { struct sas_rphy *rphy = dev_to_rphy(parent); - sprintf(port->dev.bus_id, "port-%d:%d:%d", shost->host_no, - rphy->scsi_target_id, port->port_identifier); + dev_set_name(&port->dev, "port-%d:%d:%d", shost->host_no, + rphy->scsi_target_id, port->port_identifier); } else - sprintf(port->dev.bus_id, "port-%d:%d", shost->host_no, - port->port_identifier); + dev_set_name(&port->dev, "port-%d:%d", shost->host_no, + port->port_identifier); transport_setup_device(&port->dev); @@ -935,7 +935,7 @@ void sas_port_delete(struct sas_port *port) if (port->is_backlink) { struct device *parent = port->dev.parent; - sysfs_remove_link(&port->dev.kobj, parent->bus_id); + sysfs_remove_link(&port->dev.kobj, dev_name(parent)); port->is_backlink = 0; } @@ -984,7 +984,8 @@ void sas_port_add_phy(struct sas_port *port, struct sas_phy *phy) /* If this trips, you added a phy that was already * part of a different port */ if (unlikely(tmp != phy)) { - dev_printk(KERN_ERR, &port->dev, "trying to add phy %s fails: it's already part of another port\n", phy->dev.bus_id); + dev_printk(KERN_ERR, &port->dev, "trying to add phy %s fails: it's already part of another port\n", + dev_name(&phy->dev)); BUG(); } } else { @@ -1023,7 +1024,7 @@ void sas_port_mark_backlink(struct sas_port *port) return; port->is_backlink = 1; res = sysfs_create_link(&port->dev.kobj, &parent->kobj, - parent->bus_id); + dev_name(parent)); if (res) goto err; return; @@ -1367,11 +1368,12 @@ struct sas_rphy *sas_end_device_alloc(struct sas_port *parent) rdev->rphy.dev.release = sas_end_device_release; if (scsi_is_sas_expander_device(parent->dev.parent)) { struct sas_rphy *rphy = dev_to_rphy(parent->dev.parent); - sprintf(rdev->rphy.dev.bus_id, "end_device-%d:%d:%d", - shost->host_no, rphy->scsi_target_id, parent->port_identifier); + dev_set_name(&rdev->rphy.dev, "end_device-%d:%d:%d", + shost->host_no, rphy->scsi_target_id, + parent->port_identifier); } else - sprintf(rdev->rphy.dev.bus_id, "end_device-%d:%d", - shost->host_no, parent->port_identifier); + dev_set_name(&rdev->rphy.dev, "end_device-%d:%d", + shost->host_no, parent->port_identifier); rdev->rphy.identify.device_type = SAS_END_DEVICE; sas_rphy_initialize(&rdev->rphy); transport_setup_device(&rdev->rphy.dev); @@ -1411,8 +1413,8 @@ struct sas_rphy *sas_expander_alloc(struct sas_port *parent, mutex_lock(&sas_host->lock); rdev->rphy.scsi_target_id = sas_host->next_expander_id++; mutex_unlock(&sas_host->lock); - sprintf(rdev->rphy.dev.bus_id, "expander-%d:%d", - shost->host_no, rdev->rphy.scsi_target_id); + dev_set_name(&rdev->rphy.dev, "expander-%d:%d", + shost->host_no, rdev->rphy.scsi_target_id); rdev->rphy.identify.device_type = type; sas_rphy_initialize(&rdev->rphy); transport_setup_device(&rdev->rphy.dev); @@ -1445,7 +1447,7 @@ int sas_rphy_add(struct sas_rphy *rphy) transport_add_device(&rphy->dev); transport_configure_device(&rphy->dev); if (sas_bsg_initialize(shost, rphy)) - printk("fail to a bsg device %s\n", rphy->dev.bus_id); + printk("fail to a bsg device %s\n", dev_name(&rphy->dev)); mutex_lock(&sas_host->lock); diff --git a/drivers/scsi/scsi_transport_srp.c b/drivers/scsi/scsi_transport_srp.c index 8a7af951d98a..21a045e0559f 100644 --- a/drivers/scsi/scsi_transport_srp.c +++ b/drivers/scsi/scsi_transport_srp.c @@ -212,7 +212,7 @@ struct srp_rport *srp_rport_add(struct Scsi_Host *shost, rport->roles = ids->roles; id = atomic_inc_return(&to_srp_host_attrs(shost)->next_port_id); - sprintf(rport->dev.bus_id, "port-%d:%d", shost->host_no, id); + dev_set_name(&rport->dev, "port-%d:%d", shost->host_no, id); transport_setup_device(&rport->dev); diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index c9e1242eaf25..daf28759d9a8 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -1875,7 +1875,7 @@ static int sd_probe(struct device *dev) device_initialize(&sdkp->dev); sdkp->dev.parent = &sdp->sdev_gendev; sdkp->dev.class = &sd_disk_class; - strncpy(sdkp->dev.bus_id, sdp->sdev_gendev.bus_id, BUS_ID_SIZE); + dev_set_name(&sdkp->dev, dev_name(&sdp->sdev_gendev)); if (device_add(&sdkp->dev)) goto out_free_index; diff --git a/drivers/scsi/ses.c b/drivers/scsi/ses.c index 1bcf3c33d7ff..3b77b3b9c11e 100644 --- a/drivers/scsi/ses.c +++ b/drivers/scsi/ses.c @@ -525,7 +525,7 @@ static int ses_intf_add(struct device *cdev, if (!scomp) goto err_free; - edev = enclosure_register(cdev->parent, sdev->sdev_gendev.bus_id, + edev = enclosure_register(cdev->parent, dev_name(&sdev->sdev_gendev), components, &ses_enclosure_callbacks); if (IS_ERR(edev)) { err = PTR_ERR(edev); diff --git a/drivers/serial/amba-pl010.c b/drivers/serial/amba-pl010.c index 71562689116f..e3a5ad5ef1d6 100644 --- a/drivers/serial/amba-pl010.c +++ b/drivers/serial/amba-pl010.c @@ -692,7 +692,7 @@ static int pl010_probe(struct amba_device *dev, void *id) goto free; } - uap->clk = clk_get(&dev->dev, "UARTCLK"); + uap->clk = clk_get(&dev->dev, NULL); if (IS_ERR(uap->clk)) { ret = PTR_ERR(uap->clk); goto unmap; diff --git a/drivers/serial/amba-pl011.c b/drivers/serial/amba-pl011.c index b7180046f8db..8b2b9700f3e4 100644 --- a/drivers/serial/amba-pl011.c +++ b/drivers/serial/amba-pl011.c @@ -756,7 +756,7 @@ static int pl011_probe(struct amba_device *dev, void *id) goto free; } - uap->clk = clk_get(&dev->dev, "UARTCLK"); + uap->clk = clk_get(&dev->dev, NULL); if (IS_ERR(uap->clk)) { ret = PTR_ERR(uap->clk); goto unmap; diff --git a/drivers/serial/atmel_serial.c b/drivers/serial/atmel_serial.c index 61fb8b6d19af..d5efd6c77904 100644 --- a/drivers/serial/atmel_serial.c +++ b/drivers/serial/atmel_serial.c @@ -1258,6 +1258,8 @@ static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port, atmel_port->clk = clk_get(&pdev->dev, "usart"); clk_enable(atmel_port->clk); port->uartclk = clk_get_rate(atmel_port->clk); + clk_disable(atmel_port->clk); + /* only enable clock when USART is in use */ } atmel_port->use_dma_rx = data->use_dma_rx; @@ -1379,6 +1381,8 @@ static int __init atmel_console_setup(struct console *co, char *options) return -ENODEV; } + clk_enable(atmel_ports[co->index].clk); + UART_PUT_IDR(port, -1); UART_PUT_CR(port, ATMEL_US_RSTSTA | ATMEL_US_RSTRX); UART_PUT_CR(port, ATMEL_US_TXEN | ATMEL_US_RXEN); @@ -1403,7 +1407,7 @@ static struct console atmel_console = { .data = &atmel_uart, }; -#define ATMEL_CONSOLE_DEVICE &atmel_console +#define ATMEL_CONSOLE_DEVICE (&atmel_console) /* * Early console initialization (before VM subsystem initialized). @@ -1534,6 +1538,15 @@ static int __devinit atmel_serial_probe(struct platform_device *pdev) if (ret) goto err_add_port; + if (atmel_is_console_port(&port->uart) + && ATMEL_CONSOLE_DEVICE->flags & CON_ENABLED) { + /* + * The serial core enabled the clock for us, so undo + * the clk_enable() in atmel_console_setup() + */ + clk_disable(port->clk); + } + device_init_wakeup(&pdev->dev, 1); platform_set_drvdata(pdev, port); @@ -1544,7 +1557,6 @@ err_add_port: port->rx_ring.buf = NULL; err_alloc_ring: if (!atmel_is_console_port(&port->uart)) { - clk_disable(port->clk); clk_put(port->clk); port->clk = NULL; } @@ -1568,7 +1580,6 @@ static int __devexit atmel_serial_remove(struct platform_device *pdev) /* "port" is allocated statically, so we shouldn't free it */ - clk_disable(atmel_port->clk); clk_put(atmel_port->clk); return ret; diff --git a/drivers/serial/pmac_zilog.c b/drivers/serial/pmac_zilog.c index 317b061f7641..ad3488504010 100644 --- a/drivers/serial/pmac_zilog.c +++ b/drivers/serial/pmac_zilog.c @@ -1383,6 +1383,29 @@ static int pmz_verify_port(struct uart_port *port, struct serial_struct *ser) return -EINVAL; } +#ifdef CONFIG_CONSOLE_POLL + +static int pmz_poll_get_char(struct uart_port *port) +{ + struct uart_pmac_port *uap = (struct uart_pmac_port *)port; + + while ((read_zsreg(uap, R0) & Rx_CH_AV) == 0) + udelay(5); + return read_zsdata(uap); +} + +static void pmz_poll_put_char(struct uart_port *port, unsigned char c) +{ + struct uart_pmac_port *uap = (struct uart_pmac_port *)port; + + /* Wait for the transmit buffer to empty. */ + while ((read_zsreg(uap, R0) & Tx_BUF_EMP) == 0) + udelay(5); + write_zsdata(uap, c); +} + +#endif + static struct uart_ops pmz_pops = { .tx_empty = pmz_tx_empty, .set_mctrl = pmz_set_mctrl, @@ -1400,6 +1423,10 @@ static struct uart_ops pmz_pops = { .request_port = pmz_request_port, .config_port = pmz_config_port, .verify_port = pmz_verify_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = pmz_poll_get_char, + .poll_put_char = pmz_poll_put_char, +#endif }; /* diff --git a/drivers/serial/pxa.c b/drivers/serial/pxa.c index abc00be55433..f6e3b86bb0be 100644 --- a/drivers/serial/pxa.c +++ b/drivers/serial/pxa.c @@ -48,6 +48,7 @@ #include <mach/hardware.h> #include <asm/irq.h> #include <mach/pxa-regs.h> +#include <mach/regs-uart.h> struct uart_pxa_port { @@ -766,7 +767,7 @@ static int serial_pxa_probe(struct platform_device *dev) if (!sport) return -ENOMEM; - sport->clk = clk_get(&dev->dev, "UARTCLK"); + sport->clk = clk_get(&dev->dev, NULL); if (IS_ERR(sport->clk)) { ret = PTR_ERR(sport->clk); goto err_free; diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c index 874786a11fe9..1b7a33b93f7d 100644 --- a/drivers/serial/serial_core.c +++ b/drivers/serial/serial_core.c @@ -2049,7 +2049,7 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *port) if (!tries) printk(KERN_ERR "%s%s%s%d: Unable to drain " "transmitter\n", - port->dev ? port->dev->bus_id : "", + port->dev ? dev_name(port->dev) : "", port->dev ? ": " : "", drv->dev_name, drv->tty_driver->name_base + port->line); @@ -2175,7 +2175,7 @@ uart_report_port(struct uart_driver *drv, struct uart_port *port) } printk(KERN_INFO "%s%s%s%d at %s (irq = %d) is a %s\n", - port->dev ? port->dev->bus_id : "", + port->dev ? dev_name(port->dev) : "", port->dev ? ": " : "", drv->dev_name, drv->tty_driver->name_base + port->line, diff --git a/drivers/serial/serial_lh7a40x.c b/drivers/serial/serial_lh7a40x.c index 61dc8b3daa26..a7bf024a8286 100644 --- a/drivers/serial/serial_lh7a40x.c +++ b/drivers/serial/serial_lh7a40x.c @@ -41,9 +41,10 @@ #include <linux/tty_flip.h> #include <linux/serial_core.h> #include <linux/serial.h> +#include <linux/io.h> -#include <asm/io.h> #include <asm/irq.h> +#include <mach/hardware.h> #define DEV_MAJOR 204 #define DEV_MINOR 16 diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c index 5c0f32c7fbf6..31532e97fb96 100644 --- a/drivers/serial/sh-sci.c +++ b/drivers/serial/sh-sci.c @@ -101,6 +101,12 @@ static void sci_stop_tx(struct uart_port *port); static struct sci_port sci_ports[SCI_NPORTS]; static struct uart_driver sci_uart_driver; +static inline struct sci_port * +to_sci_port(struct uart_port *uart) +{ + return container_of(uart, struct sci_port, port); +} + #if defined(CONFIG_SERIAL_SH_SCI_CONSOLE) && \ defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_SH_KGDB) static inline void handle_error(struct uart_port *port) @@ -124,7 +130,8 @@ static int get_char(struct uart_port *port) } } while (!(status & SCxSR_RDxF(port))); c = sci_in(port, SCxRDR); - sci_in(port, SCxSR); /* Dummy read */ + /* Dummy read */ + sci_in(port, SCxSR); sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); spin_unlock_irqrestore(&port->lock, flags); @@ -144,9 +151,9 @@ static void put_char(struct uart_port *port, char c) status = sci_in(port, SCxSR); } while (!(status & SCxSR_TDxE(port))); - sci_out(port, SCxTDR, c); sci_in(port, SCxSR); /* Dummy read */ sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port)); + sci_out(port, SCxTDR, c); spin_unlock_irqrestore(&port->lock, flags); } @@ -161,7 +168,7 @@ static void put_string(struct sci_port *sci_port, const char *buffer, int count) #if defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_SH_KGDB) int checksum; - int usegdb=0; + int usegdb = 0; #ifdef CONFIG_SH_STANDARD_BIOS /* This call only does a trap the first time it is @@ -181,7 +188,8 @@ static void put_string(struct sci_port *sci_port, const char *buffer, int count) put_char(port, 'O'); /* 'O'utput to console */ checksum = 'O'; - for (i=0; i<count; i++) { /* Don't use run length encoding */ + /* Don't use run length encoding */ + for (i = 0; i < count; i++) { int h, l; c = *p++; @@ -197,7 +205,7 @@ static void put_string(struct sci_port *sci_port, const char *buffer, int count) } while (get_char(port) != '+'); } else #endif /* CONFIG_SH_STANDARD_BIOS || CONFIG_SH_KGDB */ - for (i=0; i<count; i++) { + for (i = 0; i < count; i++) { if (*p == 10) put_char(port, '\r'); put_char(port, *p++); @@ -208,35 +216,34 @@ static void put_string(struct sci_port *sci_port, const char *buffer, int count) #ifdef CONFIG_SH_KGDB static int kgdb_sci_getchar(void) { - int c; + int c; - /* Keep trying to read a character, this could be neater */ - while ((c = get_char(&kgdb_sci_port->port)) < 0) + /* Keep trying to read a character, this could be neater */ + while ((c = get_char(&kgdb_sci_port->port)) < 0) cpu_relax(); - return c; + return c; } static inline void kgdb_sci_putchar(int c) { - put_char(&kgdb_sci_port->port, c); + put_char(&kgdb_sci_port->port, c); } #endif /* CONFIG_SH_KGDB */ #if defined(__H8300S__) enum { sci_disable, sci_enable }; -static void h8300_sci_config(struct uart_port* port, unsigned int ctrl) +static void h8300_sci_config(struct uart_port *port, unsigned int ctrl) { - volatile unsigned char *mstpcrl=(volatile unsigned char *)MSTPCRL; + volatile unsigned char *mstpcrl = (volatile unsigned char *)MSTPCRL; int ch = (port->mapbase - SMR0) >> 3; unsigned char mask = 1 << (ch+1); - if (ctrl == sci_disable) { + if (ctrl == sci_disable) *mstpcrl |= mask; - } else { + else *mstpcrl &= ~mask; - } } static inline void h8300_sci_enable(struct uart_port *port) @@ -251,7 +258,7 @@ static inline void h8300_sci_disable(struct uart_port *port) #endif #if defined(__H8300H__) || defined(__H8300S__) -static void sci_init_pins_sci(struct uart_port* port, unsigned int cflag) +static void sci_init_pins_sci(struct uart_port *port, unsigned int cflag) { int ch = (port->mapbase - SMR0) >> 3; @@ -285,14 +292,13 @@ static void sci_init_pins_irda(struct uart_port *port, unsigned int cflag) #endif #if defined(CONFIG_CPU_SUBTYPE_SH7710) || defined(CONFIG_CPU_SUBTYPE_SH7712) -static void sci_init_pins_scif(struct uart_port* port, unsigned int cflag) +static void sci_init_pins_scif(struct uart_port *port, unsigned int cflag) { unsigned int fcr_val = 0; set_sh771x_scif_pfc(port); - if (cflag & CRTSCTS) { + if (cflag & CRTSCTS) fcr_val |= SCFCR_MCE; - } sci_out(port, SCFCR, fcr_val); } #elif defined(CONFIG_CPU_SUBTYPE_SH7720) || defined(CONFIG_CPU_SUBTYPE_SH7721) @@ -419,18 +425,26 @@ static inline int scif_rxroom(struct uart_port *port) #elif defined(CONFIG_CPU_SUBTYPE_SH7763) static inline int scif_txroom(struct uart_port *port) { - if((port->mapbase == 0xffe00000) || (port->mapbase == 0xffe08000)) /* SCIF0/1*/ + if ((port->mapbase == 0xffe00000) || + (port->mapbase == 0xffe08000)) { + /* SCIF0/1*/ return SCIF_TXROOM_MAX - (sci_in(port, SCTFDR) & 0xff); - else /* SCIF2 */ + } else { + /* SCIF2 */ return SCIF2_TXROOM_MAX - (sci_in(port, SCFDR) >> 8); + } } static inline int scif_rxroom(struct uart_port *port) { - if((port->mapbase == 0xffe00000) || (port->mapbase == 0xffe08000)) /* SCIF0/1*/ + if ((port->mapbase == 0xffe00000) || + (port->mapbase == 0xffe08000)) { + /* SCIF0/1*/ return sci_in(port, SCRFDR) & 0xff; - else /* SCIF2 */ + } else { + /* SCIF2 */ return sci_in(port, SCFDR) & SCIF2_RFDC_MASK; + } } #else static inline int scif_txroom(struct uart_port *port) @@ -446,12 +460,12 @@ static inline int scif_rxroom(struct uart_port *port) static inline int sci_txroom(struct uart_port *port) { - return ((sci_in(port, SCxSR) & SCI_TDRE) != 0); + return (sci_in(port, SCxSR) & SCI_TDRE) != 0; } static inline int sci_rxroom(struct uart_port *port) { - return ((sci_in(port, SCxSR) & SCxSR_RDxF(port)) != 0); + return (sci_in(port, SCxSR) & SCxSR_RDxF(port)) != 0; } /* ********************************************************************** * @@ -469,19 +483,18 @@ static void sci_transmit_chars(struct uart_port *port) status = sci_in(port, SCxSR); if (!(status & SCxSR_TDxE(port))) { ctrl = sci_in(port, SCSCR); - if (uart_circ_empty(xmit)) { + if (uart_circ_empty(xmit)) ctrl &= ~SCI_CTRL_FLAGS_TIE; - } else { + else ctrl |= SCI_CTRL_FLAGS_TIE; - } sci_out(port, SCSCR, ctrl); return; } - if (port->type == PORT_SCIF) - count = scif_txroom(port); - else + if (port->type == PORT_SCI) count = sci_txroom(port); + else + count = scif_txroom(port); do { unsigned char c; @@ -510,7 +523,7 @@ static void sci_transmit_chars(struct uart_port *port) } else { ctrl = sci_in(port, SCSCR); - if (port->type == PORT_SCIF) { + if (port->type != PORT_SCI) { sci_in(port, SCxSR); /* Dummy read */ sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port)); } @@ -521,11 +534,11 @@ static void sci_transmit_chars(struct uart_port *port) } /* On SH3, SCIF may read end-of-break as a space->mark char */ -#define STEPFN(c) ({int __c=(c); (((__c-1)|(__c)) == -1); }) +#define STEPFN(c) ({int __c = (c); (((__c-1)|(__c)) == -1); }) static inline void sci_receive_chars(struct uart_port *port) { - struct sci_port *sci_port = (struct sci_port *)port; + struct sci_port *sci_port = to_sci_port(port); struct tty_struct *tty = port->info->port.tty; int i, count, copied = 0; unsigned short status; @@ -536,10 +549,10 @@ static inline void sci_receive_chars(struct uart_port *port) return; while (1) { - if (port->type == PORT_SCIF) - count = scif_rxroom(port); - else + if (port->type == PORT_SCI) count = sci_rxroom(port); + else + count = scif_rxroom(port); /* Don't copy more bytes than there is room for in the buffer */ count = tty_buffer_request_room(tty, count); @@ -550,13 +563,13 @@ static inline void sci_receive_chars(struct uart_port *port) if (port->type == PORT_SCI) { char c = sci_in(port, SCxRDR); - if (uart_handle_sysrq_char(port, c) || sci_port->break_flag) + if (uart_handle_sysrq_char(port, c) || + sci_port->break_flag) count = 0; - else { + else tty_insert_flip_char(tty, c, TTY_NORMAL); - } } else { - for (i=0; i<count; i++) { + for (i = 0; i < count; i++) { char c = sci_in(port, SCxRDR); status = sci_in(port, SCxSR); #if defined(CONFIG_CPU_SH3) @@ -657,7 +670,7 @@ static inline int sci_handle_errors(struct uart_port *port) if (status & SCxSR_FER(port)) { if (sci_rxd_in(port) == 0) { /* Notify of BREAK */ - struct sci_port *sci_port = (struct sci_port *)port; + struct sci_port *sci_port = to_sci_port(port); if (!sci_port->break_flag) { sci_port->break_flag = 1; @@ -666,10 +679,11 @@ static inline int sci_handle_errors(struct uart_port *port) /* Do sysrq handling. */ if (uart_handle_break(port)) return 0; - pr_debug("sci: BREAK detected\n"); + pr_debug("sci: BREAK detected\n"); if (tty_insert_flip_char(tty, 0, TTY_BREAK)) - copied++; - } + copied++; + } + } else { /* frame error */ if (tty_insert_flip_char(tty, 0, TTY_FRAME)) @@ -714,7 +728,7 @@ static inline int sci_handle_breaks(struct uart_port *port) #if defined(SCIF_ORER) /* XXX: Handle SCIF overrun error */ - if (port->type == PORT_SCIF && (sci_in(port, SCLSR) & SCIF_ORER) != 0) { + if (port->type != PORT_SCI && (sci_in(port, SCLSR) & SCIF_ORER) != 0) { sci_out(port, SCLSR, 0); if (tty_insert_flip_char(tty, 0, TTY_OVERRUN)) { copied++; @@ -764,7 +778,7 @@ static irqreturn_t sci_er_interrupt(int irq, void *ptr) } } else { #if defined(SCIF_ORER) - if((sci_in(port, SCLSR) & SCIF_ORER) != 0) { + if ((sci_in(port, SCLSR) & SCIF_ORER) != 0) { struct tty_struct *tty = port->info->port.tty; sci_out(port, SCLSR, 0); @@ -801,8 +815,8 @@ static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr) struct uart_port *port = ptr; irqreturn_t ret = IRQ_NONE; - ssr_status = sci_in(port,SCxSR); - scr_status = sci_in(port,SCSCR); + ssr_status = sci_in(port, SCxSR); + scr_status = sci_in(port, SCSCR); /* Tx Interrupt */ if ((ssr_status & 0x0020) && (scr_status & SCI_CTRL_FLAGS_TIE)) @@ -832,7 +846,7 @@ static int sci_notifier(struct notifier_block *self, int i; if ((phase == CPUFREQ_POSTCHANGE) || - (phase == CPUFREQ_RESUMECHANGE)){ + (phase == CPUFREQ_RESUMECHANGE)) { for (i = 0; i < SCI_NPORTS; i++) { struct uart_port *port = &sci_ports[i].port; struct clk *clk; @@ -904,12 +918,12 @@ static void sci_free_irq(struct sci_port *port) { int i; - if (port->irqs[0] == port->irqs[1]) { - if (!port->irqs[0]) - printk("sci: sci_free_irq error\n"); + if (port->irqs[0] == port->irqs[1]) { + if (!port->irqs[0]) + printk(KERN_ERR "sci: sci_free_irq error\n"); else - free_irq(port->irqs[0], port); - } else { + free_irq(port->irqs[0], port); + } else { for (i = 0; i < ARRAY_SIZE(port->irqs); i++) { if (!port->irqs[i]) continue; @@ -1042,7 +1056,7 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, sci_out(port, SCSCR, 0x00); /* TE=0, RE=0, CKE1=0 */ - if (port->type == PORT_SCIF) + if (port->type != PORT_SCI) sci_out(port, SCFCR, SCFCR_RFRST | SCFCR_TFRST); smr_val = sci_in(port, SCSMR) & 3; @@ -1060,12 +1074,12 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, sci_out(port, SCSMR, smr_val); if (t > 0) { - if(t >= 256) { + if (t >= 256) { sci_out(port, SCSMR, (sci_in(port, SCSMR) & ~3) | 1); t >>= 2; - } else { + } else sci_out(port, SCSMR, sci_in(port, SCSMR) & ~3); - } + sci_out(port, SCBRR, t); udelay((1000000+(baud-1)) / baud); /* Wait one bit interval */ } @@ -1076,15 +1090,20 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, sci_out(port, SCSCR, SCSCR_INIT(port)); if ((termios->c_cflag & CREAD) != 0) - sci_start_rx(port,0); + sci_start_rx(port, 0); } static const char *sci_type(struct uart_port *port) { switch (port->type) { - case PORT_SCI: return "sci"; - case PORT_SCIF: return "scif"; - case PORT_IRDA: return "irda"; + case PORT_IRDA: + return "irda"; + case PORT_SCI: + return "sci"; + case PORT_SCIF: + return "scif"; + case PORT_SCIFA: + return "scifa"; } return NULL; @@ -1112,6 +1131,7 @@ static void sci_config_port(struct uart_port *port, int flags) s->init_pins = sci_init_pins_sci; break; case PORT_SCIF: + case PORT_SCIFA: s->init_pins = sci_init_pins_scif; break; case PORT_IRDA: @@ -1385,9 +1405,9 @@ console_initcall(kgdb_console_init); #endif /* CONFIG_SH_KGDB_CONSOLE */ #if defined(CONFIG_SH_KGDB_CONSOLE) -#define SCI_CONSOLE &kgdb_console +#define SCI_CONSOLE (&kgdb_console) #elif defined(CONFIG_SERIAL_SH_SCI_CONSOLE) -#define SCI_CONSOLE &serial_console +#define SCI_CONSOLE (&serial_console) #else #define SCI_CONSOLE 0 #endif diff --git a/drivers/serial/sh-sci.h b/drivers/serial/sh-sci.h index 6163a45f968f..6da755d63529 100644 --- a/drivers/serial/sh-sci.h +++ b/drivers/serial/sh-sci.h @@ -133,13 +133,20 @@ # define SCSPTR5 0xffef0024 /* 16 bit SCIF */ # define SCIF_OPER 0x0001 /* Overrun error bit */ # define SCSCR_INIT(port) 0x3a /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ -#elif defined(CONFIG_CPU_SUBTYPE_SH7203) || \ +#elif defined(CONFIG_CPU_SUBTYPE_SH7201) || \ + defined(CONFIG_CPU_SUBTYPE_SH7203) || \ defined(CONFIG_CPU_SUBTYPE_SH7206) || \ defined(CONFIG_CPU_SUBTYPE_SH7263) # define SCSPTR0 0xfffe8020 /* 16 bit SCIF */ # define SCSPTR1 0xfffe8820 /* 16 bit SCIF */ # define SCSPTR2 0xfffe9020 /* 16 bit SCIF */ # define SCSPTR3 0xfffe9820 /* 16 bit SCIF */ +# if defined(CONFIG_CPU_SUBTYPE_SH7201) +# define SCSPTR4 0xfffeA020 /* 16 bit SCIF */ +# define SCSPTR5 0xfffeA820 /* 16 bit SCIF */ +# define SCSPTR6 0xfffeB020 /* 16 bit SCIF */ +# define SCSPTR7 0xfffeB820 /* 16 bit SCIF */ +# endif # define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ #elif defined(CONFIG_CPU_SUBTYPE_SH7619) # define SCSPTR0 0xf8400020 /* 16 bit SCIF */ @@ -289,18 +296,18 @@ #define CPU_SCIx_FNS(name, sci_offset, sci_size, scif_offset, scif_size)\ static inline unsigned int sci_##name##_in(struct uart_port *port) \ { \ - if (port->type == PORT_SCI) { \ - SCI_IN(sci_size, sci_offset) \ - } else { \ - SCI_IN(scif_size, scif_offset); \ + if (port->type == PORT_SCIF) { \ + SCI_IN(scif_size, scif_offset) \ + } else { /* PORT_SCI or PORT_SCIFA */ \ + SCI_IN(sci_size, sci_offset); \ } \ } \ static inline void sci_##name##_out(struct uart_port *port, unsigned int value) \ { \ - if (port->type == PORT_SCI) { \ - SCI_OUT(sci_size, sci_offset, value) \ - } else { \ - SCI_OUT(scif_size, scif_offset, value); \ + if (port->type == PORT_SCIF) { \ + SCI_OUT(scif_size, scif_offset, value) \ + } else { /* PORT_SCI or PORT_SCIFA */ \ + SCI_OUT(sci_size, sci_offset, value); \ } \ } @@ -664,7 +671,8 @@ static inline int sci_rxd_in(struct uart_port *port) return ctrl_inw(SCSPTR5) & 0x0001 ? 1 : 0; /* SCIF */ return 1; } -#elif defined(CONFIG_CPU_SUBTYPE_SH7203) || \ +#elif defined(CONFIG_CPU_SUBTYPE_SH7201) || \ + defined(CONFIG_CPU_SUBTYPE_SH7203) || \ defined(CONFIG_CPU_SUBTYPE_SH7206) || \ defined(CONFIG_CPU_SUBTYPE_SH7263) static inline int sci_rxd_in(struct uart_port *port) @@ -677,6 +685,16 @@ static inline int sci_rxd_in(struct uart_port *port) return ctrl_inw(SCSPTR2) & 0x0001 ? 1 : 0; /* SCIF */ if (port->mapbase == 0xfffe9800) return ctrl_inw(SCSPTR3) & 0x0001 ? 1 : 0; /* SCIF */ +#if defined(CONFIG_CPU_SUBTYPE_SH7201) + if (port->mapbase == 0xfffeA000) + return ctrl_inw(SCSPTR0) & 0x0001 ? 1 : 0; /* SCIF */ + if (port->mapbase == 0xfffeA800) + return ctrl_inw(SCSPTR1) & 0x0001 ? 1 : 0; /* SCIF */ + if (port->mapbase == 0xfffeB000) + return ctrl_inw(SCSPTR2) & 0x0001 ? 1 : 0; /* SCIF */ + if (port->mapbase == 0xfffeB800) + return ctrl_inw(SCSPTR3) & 0x0001 ? 1 : 0; /* SCIF */ +#endif return 1; } #elif defined(CONFIG_CPU_SUBTYPE_SH7619) diff --git a/drivers/serial/uartlite.c b/drivers/serial/uartlite.c index 6a3f8fb0c9dd..3317148a4b93 100644 --- a/drivers/serial/uartlite.c +++ b/drivers/serial/uartlite.c @@ -286,8 +286,8 @@ static void ulite_release_port(struct uart_port *port) static int ulite_request_port(struct uart_port *port) { - pr_debug("ulite console: port=%p; port->mapbase=%x\n", - port, port->mapbase); + pr_debug("ulite console: port=%p; port->mapbase=%llx\n", + port, (unsigned long long) port->mapbase); if (!request_mem_region(port->mapbase, ULITE_REGION, "uartlite")) { dev_err(port->dev, "Memory region busy\n"); diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c index 02f9320f3efc..8abae4ad0fa5 100644 --- a/drivers/spi/atmel_spi.c +++ b/drivers/spi/atmel_spi.c @@ -766,6 +766,7 @@ static int __init atmel_spi_probe(struct platform_device *pdev) /* Initialize the hardware */ clk_enable(clk); spi_writel(as, CR, SPI_BIT(SWRST)); + spi_writel(as, CR, SPI_BIT(SWRST)); /* AT91SAM9263 Rev B workaround */ spi_writel(as, MR, SPI_BIT(MSTR) | SPI_BIT(MODFDIS)); spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS)); spi_writel(as, CR, SPI_BIT(SPIEN)); @@ -782,6 +783,7 @@ static int __init atmel_spi_probe(struct platform_device *pdev) out_reset_hw: spi_writel(as, CR, SPI_BIT(SWRST)); + spi_writel(as, CR, SPI_BIT(SWRST)); /* AT91SAM9263 Rev B workaround */ clk_disable(clk); free_irq(irq, master); out_unmap_regs: @@ -805,6 +807,7 @@ static int __exit atmel_spi_remove(struct platform_device *pdev) spin_lock_irq(&as->lock); as->stopping = 1; spi_writel(as, CR, SPI_BIT(SWRST)); + spi_writel(as, CR, SPI_BIT(SWRST)); /* AT91SAM9263 Rev B workaround */ spi_readl(as, SR); spin_unlock_irq(&as->lock); diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c index dae87b1a4c6e..6104f461a3cd 100644 --- a/drivers/spi/pxa2xx_spi.c +++ b/drivers/spi/pxa2xx_spi.c @@ -32,8 +32,8 @@ #include <asm/io.h> #include <asm/irq.h> #include <asm/delay.h> -#include <asm/dma.h> +#include <mach/dma.h> #include <mach/hardware.h> #include <mach/pxa-regs.h> #include <mach/regs-ssp.h> @@ -352,21 +352,21 @@ static int map_dma_buffers(struct driver_data *drv_data) } else drv_data->tx_map_len = drv_data->len; - /* Stream map the rx buffer */ - drv_data->rx_dma = dma_map_single(dev, drv_data->rx, - drv_data->rx_map_len, - DMA_FROM_DEVICE); - if (dma_mapping_error(dev, drv_data->rx_dma)) - return 0; - - /* Stream map the tx buffer */ + /* Stream map the tx buffer. Always do DMA_TO_DEVICE first + * so we flush the cache *before* invalidating it, in case + * the tx and rx buffers overlap. + */ drv_data->tx_dma = dma_map_single(dev, drv_data->tx, - drv_data->tx_map_len, - DMA_TO_DEVICE); + drv_data->tx_map_len, DMA_TO_DEVICE); + if (dma_mapping_error(dev, drv_data->tx_dma)) + return 0; - if (dma_mapping_error(dev, drv_data->tx_dma)) { - dma_unmap_single(dev, drv_data->rx_dma, + /* Stream map the rx buffer */ + drv_data->rx_dma = dma_map_single(dev, drv_data->rx, drv_data->rx_map_len, DMA_FROM_DEVICE); + if (dma_mapping_error(dev, drv_data->rx_dma)) { + dma_unmap_single(dev, drv_data->tx_dma, + drv_data->tx_map_len, DMA_TO_DEVICE); return 0; } diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 3734dc9708e1..643908b74bc0 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -47,7 +47,7 @@ modalias_show(struct device *dev, struct device_attribute *a, char *buf) { const struct spi_device *spi = to_spi_device(dev); - return snprintf(buf, BUS_ID_SIZE + 1, "%s\n", spi->modalias); + return sprintf(buf, "%s\n", spi->modalias); } static struct device_attribute spi_dev_attrs[] = { @@ -63,7 +63,7 @@ static int spi_match_device(struct device *dev, struct device_driver *drv) { const struct spi_device *spi = to_spi_device(dev); - return strncmp(spi->modalias, drv->name, BUS_ID_SIZE) == 0; + return strcmp(spi->modalias, drv->name) == 0; } static int spi_uevent(struct device *dev, struct kobj_uevent_env *env) @@ -243,8 +243,7 @@ int spi_add_device(struct spi_device *spi) } /* Set the bus ID string */ - snprintf(spi->dev.bus_id, sizeof spi->dev.bus_id, - "%s.%u", spi->master->dev.bus_id, + dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->master->dev), spi->chip_select); @@ -254,7 +253,7 @@ int spi_add_device(struct spi_device *spi) */ mutex_lock(&spi_add_lock); - if (bus_find_device_by_name(&spi_bus_type, NULL, spi->dev.bus_id) + if (bus_find_device_by_name(&spi_bus_type, NULL, dev_name(&spi->dev)) != NULL) { dev_err(dev, "chipselect %d already in use\n", spi->chip_select); @@ -269,7 +268,7 @@ int spi_add_device(struct spi_device *spi) status = spi->master->setup(spi); if (status < 0) { dev_err(dev, "can't %s %s, status %d\n", - "setup", spi->dev.bus_id, status); + "setup", dev_name(&spi->dev), status); goto done; } @@ -277,9 +276,9 @@ int spi_add_device(struct spi_device *spi) status = device_add(&spi->dev); if (status < 0) dev_err(dev, "can't %s %s, status %d\n", - "add", spi->dev.bus_id, status); + "add", dev_name(&spi->dev), status); else - dev_dbg(dev, "registered child %s\n", spi->dev.bus_id); + dev_dbg(dev, "registered child %s\n", dev_name(&spi->dev)); done: mutex_unlock(&spi_add_lock); @@ -504,12 +503,11 @@ int spi_register_master(struct spi_master *master) /* register the device, then userspace will see it. * registration fails if the bus ID is in use. */ - snprintf(master->dev.bus_id, sizeof master->dev.bus_id, - "spi%u", master->bus_num); + dev_set_name(&master->dev, "spi%u", master->bus_num); status = device_add(&master->dev); if (status < 0) goto done; - dev_dbg(dev, "registered master %s%s\n", master->dev.bus_id, + dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev), dynamic ? " (dynamic)" : ""); /* populate children from any spi device tables */ diff --git a/drivers/spi/spi_bitbang.c b/drivers/spi/spi_bitbang.c index 96cc39ecb6e2..85e61f451218 100644 --- a/drivers/spi/spi_bitbang.c +++ b/drivers/spi/spi_bitbang.c @@ -475,7 +475,7 @@ int spi_bitbang_start(struct spi_bitbang *bitbang) /* this task is the only thing to touch the SPI bits */ bitbang->busy = 0; bitbang->workqueue = create_singlethread_workqueue( - bitbang->master->dev.parent->bus_id); + dev_name(bitbang->master->dev.parent)); if (bitbang->workqueue == NULL) { status = -EBUSY; goto err1; diff --git a/drivers/spi/spi_butterfly.c b/drivers/spi/spi_butterfly.c index 0ee2b2090252..c2184866fa9c 100644 --- a/drivers/spi/spi_butterfly.c +++ b/drivers/spi/spi_butterfly.c @@ -287,7 +287,7 @@ static void butterfly_attach(struct parport *p) pp->dataflash = spi_new_device(pp->bitbang.master, &pp->info[0]); if (pp->dataflash) pr_debug("%s: dataflash at %s\n", p->name, - pp->dataflash->dev.bus_id); + dev_name(&pp->dataflash->dev)); // dev_info(_what?_, ...) pr_info("%s: AVR Butterfly\n", p->name); diff --git a/drivers/spi/spi_imx.c b/drivers/spi/spi_imx.c index 61ba147e384d..0b4db0ce78d6 100644 --- a/drivers/spi/spi_imx.c +++ b/drivers/spi/spi_imx.c @@ -506,20 +506,6 @@ static int map_dma_buffers(struct driver_data *drv_data) if (!IS_DMA_ALIGNED(drv_data->rx) || !IS_DMA_ALIGNED(drv_data->tx)) return -1; - /* NULL rx means write-only transfer and no map needed - since rx DMA will not be used */ - if (drv_data->rx) { - buf = drv_data->rx; - drv_data->rx_dma = dma_map_single( - dev, - buf, - drv_data->len, - DMA_FROM_DEVICE); - if (dma_mapping_error(dev, drv_data->rx_dma)) - return -1; - drv_data->rx_dma_needs_unmap = 1; - } - if (drv_data->tx == NULL) { /* Read only message --> use drv_data->dummy_dma_buf for dummy writes to achive reads */ @@ -533,18 +519,31 @@ static int map_dma_buffers(struct driver_data *drv_data) buf, drv_data->tx_map_len, DMA_TO_DEVICE); - if (dma_mapping_error(dev, drv_data->tx_dma)) { - if (drv_data->rx_dma) { - dma_unmap_single(dev, - drv_data->rx_dma, - drv_data->len, - DMA_FROM_DEVICE); - drv_data->rx_dma_needs_unmap = 0; - } + if (dma_mapping_error(dev, drv_data->tx_dma)) return -1; - } drv_data->tx_dma_needs_unmap = 1; + /* NULL rx means write-only transfer and no map needed + * since rx DMA will not be used */ + if (drv_data->rx) { + buf = drv_data->rx; + drv_data->rx_dma = dma_map_single(dev, + buf, + drv_data->len, + DMA_FROM_DEVICE); + if (dma_mapping_error(dev, drv_data->rx_dma)) { + if (drv_data->tx_dma) { + dma_unmap_single(dev, + drv_data->tx_dma, + drv_data->tx_map_len, + DMA_TO_DEVICE); + drv_data->tx_dma_needs_unmap = 0; + } + return -1; + } + drv_data->rx_dma_needs_unmap = 1; + } + return 0; } diff --git a/drivers/spi/spi_lm70llp.c b/drivers/spi/spi_lm70llp.c index 39d8d8ad65c0..568c781ad91c 100644 --- a/drivers/spi/spi_lm70llp.c +++ b/drivers/spi/spi_lm70llp.c @@ -1,5 +1,5 @@ /* - * spi_lm70llp.c - driver for lm70llp eval board for the LM70 sensor + * spi_lm70llp.c - driver for LM70EVAL-LLP board for the LM70 sensor * * Copyright (C) 2006 Kaiwan N Billimoria <kaiwan@designergraphix.com> * @@ -40,8 +40,12 @@ * master controller driver. The hwmon/lm70 driver is a "SPI protocol * driver", layered on top of this one and usable without the lm70llp. * + * Datasheet and Schematic: * The LM70 is a temperature sensor chip from National Semiconductor; its * datasheet is available at http://www.national.com/pf/LM/LM70.html + * The schematic for this particular board (the LM70EVAL-LLP) is + * available (on page 4) here: + * http://www.national.com/appinfo/tempsensors/files/LM70LLPEVALmanual.pdf * * Also see Documentation/spi/spi-lm70llp. The SPI<->parport code here is * (heavily) based on spi-butterfly by David Brownell. @@ -64,7 +68,7 @@ * * Note that parport pin 13 actually gets inverted by the transistor * arrangement which lets either the parport or the LM70 drive the - * SI/SO signal. + * SI/SO signal (see the schematic for details). */ #define DRVNAME "spi-lm70llp" @@ -106,12 +110,16 @@ static inline struct spi_lm70llp *spidev_to_pp(struct spi_device *spi) static inline void deassertCS(struct spi_lm70llp *pp) { u8 data = parport_read_data(pp->port); + + data &= ~0x80; /* pull D7/SI-out low while de-asserted */ parport_write_data(pp->port, data | nCS); } static inline void assertCS(struct spi_lm70llp *pp) { u8 data = parport_read_data(pp->port); + + data |= 0x80; /* pull D7/SI-out high so lm70 drives SO-in */ parport_write_data(pp->port, data & ~nCS); } @@ -184,22 +192,7 @@ static void lm70_chipselect(struct spi_device *spi, int value) */ static u32 lm70_txrx(struct spi_device *spi, unsigned nsecs, u32 word, u8 bits) { - static u32 sio=0; - static int first_time=1; - - /* First time: perform SPI bitbang and return the LSB of - * the result of the SPI call. - */ - if (first_time) { - sio = bitbang_txrx_be_cpha0(spi, nsecs, 0, word, bits); - first_time=0; - return (sio & 0x00ff); - } - /* Return the MSB of the result of the SPI call */ - else { - first_time=1; - return (sio >> 8); - } + return bitbang_txrx_be_cpha0(spi, nsecs, 0, word, bits); } static void spi_lm70llp_attach(struct parport *p) @@ -287,16 +280,15 @@ static void spi_lm70llp_attach(struct parport *p) pp->spidev_lm70 = spi_new_device(pp->bitbang.master, &pp->info); if (pp->spidev_lm70) dev_dbg(&pp->spidev_lm70->dev, "spidev_lm70 at %s\n", - pp->spidev_lm70->dev.bus_id); + dev_name(&pp->spidev_lm70->dev)); else { printk(KERN_WARNING "%s: spi_new_device failed\n", DRVNAME); status = -ENODEV; goto out_bitbang_stop; } - pp->spidev_lm70->bits_per_word = 16; + pp->spidev_lm70->bits_per_word = 8; lm70llp = pp; - return; out_bitbang_stop: @@ -326,7 +318,6 @@ static void spi_lm70llp_detach(struct parport *p) /* power down */ parport_write_data(pp->port, 0); - msleep(10); parport_release(pp->pd); parport_unregister_device(pp->pd); diff --git a/drivers/spi/spi_s3c24xx.c b/drivers/spi/spi_s3c24xx.c index c252cbac00f1..256d18395a23 100644 --- a/drivers/spi/spi_s3c24xx.c +++ b/drivers/spi/spi_s3c24xx.c @@ -28,7 +28,7 @@ #include <mach/hardware.h> #include <mach/regs-gpio.h> -#include <asm/plat-s3c24xx/regs-spi.h> +#include <plat/regs-spi.h> #include <mach/spi.h> struct s3c24xx_spi { diff --git a/drivers/ssb/Kconfig b/drivers/ssb/Kconfig index 307b1f62d949..b1b947edcf01 100644 --- a/drivers/ssb/Kconfig +++ b/drivers/ssb/Kconfig @@ -1,10 +1,11 @@ -menu "Sonics Silicon Backplane" - config SSB_POSSIBLE bool depends on HAS_IOMEM && HAS_DMA default y +menu "Sonics Silicon Backplane" + depends on SSB_POSSIBLE + config SSB tristate "Sonics Silicon Backplane support" depends on SSB_POSSIBLE diff --git a/drivers/ssb/main.c b/drivers/ssb/main.c index 0ffabf5c0b60..65a1ed951a1d 100644 --- a/drivers/ssb/main.c +++ b/drivers/ssb/main.c @@ -226,7 +226,7 @@ int ssb_devices_freeze(struct ssb_bus *bus) err = drv->suspend(dev, state); if (err) { ssb_printk(KERN_ERR PFX "Failed to freeze device %s\n", - dev->dev->bus_id); + dev_name(dev->dev)); goto err_unwind; } } @@ -269,7 +269,7 @@ int ssb_devices_thaw(struct ssb_bus *bus) err = drv->resume(dev); if (err) { ssb_printk(KERN_ERR PFX "Failed to thaw device %s\n", - dev->dev->bus_id); + dev_name(dev->dev)); } } @@ -454,8 +454,7 @@ static int ssb_devices_register(struct ssb_bus *bus) dev->release = ssb_release_dev; dev->bus = &ssb_bustype; - snprintf(dev->bus_id, sizeof(dev->bus_id), - "ssb%u:%d", bus->busnumber, dev_idx); + dev_set_name(dev, "ssb%u:%d", bus->busnumber, dev_idx); switch (bus->bustype) { case SSB_BUSTYPE_PCI: @@ -480,7 +479,7 @@ static int ssb_devices_register(struct ssb_bus *bus) if (err) { ssb_printk(KERN_ERR PFX "Could not register %s\n", - dev->bus_id); + dev_name(dev)); /* Set dev to NULL to not unregister * dev on error unwinding. */ sdev->dev = NULL; @@ -796,7 +795,7 @@ int ssb_bus_pcibus_register(struct ssb_bus *bus, err = ssb_bus_register(bus, ssb_pci_get_invariants, 0); if (!err) { ssb_printk(KERN_INFO PFX "Sonics Silicon Backplane found on " - "PCI device %s\n", host_pci->dev.bus_id); + "PCI device %s\n", dev_name(&host_pci->dev)); } return err; diff --git a/drivers/ssb/pcihost_wrapper.c b/drivers/ssb/pcihost_wrapper.c index e82db4aaa050..26737a010c6d 100644 --- a/drivers/ssb/pcihost_wrapper.c +++ b/drivers/ssb/pcihost_wrapper.c @@ -65,7 +65,7 @@ static int ssb_pcihost_probe(struct pci_dev *dev, err = pci_enable_device(dev); if (err) goto err_kfree_ssb; - name = dev->dev.bus_id; + name = dev_name(&dev->dev); if (dev->driver && dev->driver->name) name = dev->driver->name; err = pci_request_regions(dev, name); diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index e1654f59eb70..c95b286a1239 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -21,7 +21,23 @@ menuconfig STAGING If in doubt, say N here. -if STAGING + +config STAGING_EXCLUDE_BUILD + bool "Exclude Staging drivers from being built" if STAGING + default y + ---help--- + Are you sure you really want to build the staging drivers? + They taint your kernel, don't live up to the normal Linux + kernel quality standards, are a bit crufty around the edges, + and might go off and kick your dog when you aren't paying + attention. + + Say N here to be able to select and build the Staging drivers. + This option is primarily here to prevent them from being built + when selecting 'make allyesconfg' and 'make allmodconfig' so + don't be all that put off, your dog will be just fine. + +if !STAGING_EXCLUDE_BUILD source "drivers/staging/et131x/Kconfig" @@ -45,4 +61,4 @@ source "drivers/staging/at76_usb/Kconfig" source "drivers/staging/poch/Kconfig" -endif # STAGING +endif # !STAGING_EXCLUDE_BUILD diff --git a/drivers/staging/echo/echo.c b/drivers/staging/echo/echo.c index b8f2c5e9dee5..fd4007e329e7 100644 --- a/drivers/staging/echo/echo.c +++ b/drivers/staging/echo/echo.c @@ -106,7 +106,6 @@ #include <linux/kernel.h> /* We're doing kernel work */ #include <linux/module.h> -#include <linux/kernel.h> #include <linux/slab.h> #include "bit_operations.h" diff --git a/drivers/staging/me4000/me4000.c b/drivers/staging/me4000/me4000.c index cf8b01bcac8d..0394e2709278 100644 --- a/drivers/staging/me4000/me4000.c +++ b/drivers/staging/me4000/me4000.c @@ -39,7 +39,6 @@ #include <asm/uaccess.h> #include <asm/io.h> #include <asm/system.h> -#include <asm/uaccess.h> /* Include-File for the Meilhaus ME-4000 I/O board */ #include "me4000.h" diff --git a/drivers/staging/usbip/Kconfig b/drivers/staging/usbip/Kconfig index 7426235ccc44..217fb7e62c2f 100644 --- a/drivers/staging/usbip/Kconfig +++ b/drivers/staging/usbip/Kconfig @@ -1,6 +1,6 @@ config USB_IP_COMMON tristate "USB IP support (EXPERIMENTAL)" - depends on USB && EXPERIMENTAL + depends on USB && NET && EXPERIMENTAL default N ---help--- This enables pushing USB packets over IP to allow remote diff --git a/drivers/telephony/phonedev.c b/drivers/telephony/phonedev.c index 37caf4d69037..b52cc830c0b4 100644 --- a/drivers/telephony/phonedev.c +++ b/drivers/telephony/phonedev.c @@ -8,7 +8,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * Author: Alan Cox, <alan@redhat.com> + * Author: Alan Cox, <alan@lxorguk.ukuu.org.uk> * * Fixes: Mar 01 2000 Thomas Sparr, <thomas.l.sparr@telia.com> * phone_register_device now works with unit!=PHONE_UNIT_ANY diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c index fe07462d5947..8171ca17b936 100644 --- a/drivers/thermal/thermal_sys.c +++ b/drivers/thermal/thermal_sys.c @@ -579,7 +579,7 @@ static void thermal_release(struct device *dev) struct thermal_zone_device *tz; struct thermal_cooling_device *cdev; - if (!strncmp(dev->bus_id, "thermal_zone", sizeof "thermal_zone" - 1)) { + if (!strncmp(dev_name(dev), "thermal_zone", sizeof "thermal_zone" - 1)) { tz = to_thermal_zone(dev); kfree(tz); } else { @@ -630,7 +630,7 @@ struct thermal_cooling_device *thermal_cooling_device_register(char *type, cdev->ops = ops; cdev->device.class = &thermal_class; cdev->devdata = devdata; - sprintf(cdev->device.bus_id, "cooling_device%d", cdev->id); + dev_set_name(&cdev->device, "cooling_device%d", cdev->id); result = device_register(&cdev->device); if (result) { release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id); @@ -769,7 +769,7 @@ struct thermal_zone_device *thermal_zone_device_register(char *type, tz->device.class = &thermal_class; tz->devdata = devdata; tz->trips = trips; - sprintf(tz->device.bus_id, "thermal_zone%d", tz->id); + dev_set_name(&tz->device, "thermal_zone%d", tz->id); result = device_register(&tz->device); if (result) { release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id); diff --git a/drivers/uio/uio_cif.c b/drivers/uio/uio_cif.c index 57376060b978..c60b8fcf0e3e 100644 --- a/drivers/uio/uio_cif.c +++ b/drivers/uio/uio_cif.c @@ -57,8 +57,7 @@ static int __devinit hilscher_pci_probe(struct pci_dev *dev, info->mem[0].addr = pci_resource_start(dev, 0); if (!info->mem[0].addr) goto out_release; - info->mem[0].internal_addr = ioremap(pci_resource_start(dev, 0), - pci_resource_len(dev, 0)); + info->mem[0].internal_addr = pci_ioremap_bar(dev, 0); if (!info->mem[0].internal_addr) goto out_release; diff --git a/drivers/uio/uio_pdrv_genirq.c b/drivers/uio/uio_pdrv_genirq.c index 1f82c83a92ae..3f06818cf9fa 100644 --- a/drivers/uio/uio_pdrv_genirq.c +++ b/drivers/uio/uio_pdrv_genirq.c @@ -81,7 +81,8 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev) goto bad0; } - if (uioinfo->handler || uioinfo->irqcontrol || uioinfo->irq_flags) { + if (uioinfo->handler || uioinfo->irqcontrol || + uioinfo->irq_flags & IRQF_SHARED) { dev_err(&pdev->dev, "interrupt configuration error\n"); goto bad0; } @@ -132,7 +133,7 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev) * Interrupt sharing is not supported. */ - uioinfo->irq_flags = IRQF_DISABLED; + uioinfo->irq_flags |= IRQF_DISABLED; uioinfo->handler = uio_pdrv_genirq_handler; uioinfo->irqcontrol = uio_pdrv_genirq_irqcontrol; uioinfo->priv = priv; diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 289d81adfb9c..83babb0a1df7 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -150,4 +150,6 @@ source "drivers/usb/atm/Kconfig" source "drivers/usb/gadget/Kconfig" +source "drivers/usb/otg/Kconfig" + endif # USB_SUPPORT diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 20104443081a..00b47ea24f86 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -158,16 +158,12 @@ static int acm_wb_is_avail(struct acm *acm) } /* - * Finish write. + * Finish write. Caller must hold acm->write_lock */ static void acm_write_done(struct acm *acm, struct acm_wb *wb) { - unsigned long flags; - - spin_lock_irqsave(&acm->write_lock, flags); wb->use = 0; acm->transmitting--; - spin_unlock_irqrestore(&acm->write_lock, flags); } /* @@ -482,6 +478,7 @@ static void acm_write_bulk(struct urb *urb) { struct acm_wb *wb = urb->context; struct acm *acm = wb->instance; + unsigned long flags; if (verbose || urb->status || (urb->actual_length != urb->transfer_buffer_length)) @@ -490,7 +487,9 @@ static void acm_write_bulk(struct urb *urb) urb->transfer_buffer_length, urb->status); + spin_lock_irqsave(&acm->write_lock, flags); acm_write_done(acm, wb); + spin_unlock_irqrestore(&acm->write_lock, flags); if (ACM_READY(acm)) schedule_work(&acm->work); else @@ -1276,7 +1275,7 @@ static int acm_suspend(struct usb_interface *intf, pm_message_t message) struct acm *acm = usb_get_intfdata(intf); int cnt; - if (acm->dev->auto_pm) { + if (message.event & PM_EVENT_AUTO) { int b; spin_lock_irq(&acm->read_lock); diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 5a8ecc045e3f..3771d6e6d0cc 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -764,7 +764,8 @@ static int wdm_suspend(struct usb_interface *intf, pm_message_t message) mutex_lock(&desc->plock); #ifdef CONFIG_PM - if (interface_to_usbdev(desc->intf)->auto_pm && test_bit(WDM_IN_USE, &desc->flags)) { + if ((message.event & PM_EVENT_AUTO) && + test_bit(WDM_IN_USE, &desc->flags)) { rv = -EBUSY; } else { #endif diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 8e74657f106c..9f7fefd8c2eb 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -21,6 +21,7 @@ #include <linux/init.h> #include <linux/module.h> +#include <linux/kernel.h> #include <linux/fs.h> #include <linux/uaccess.h> #include <linux/kref.h> @@ -481,7 +482,6 @@ static ssize_t usbtmc_write(struct file *filp, const char __user *buf, int retval; int actual; unsigned long int n_bytes; - int n; int remaining; int done; int this_part; @@ -525,11 +525,8 @@ static ssize_t usbtmc_write(struct file *filp, const char __user *buf, goto exit; } - n_bytes = 12 + this_part; - if (this_part % 4) - n_bytes += 4 - this_part % 4; - for (n = 12 + this_part; n < n_bytes; n++) - buffer[n] = 0; + n_bytes = roundup(12 + this_part, 4); + memset(buffer + 12 + this_part, 0, n_bytes - (12 + this_part)); retval = usb_bulk_msg(data->usb_dev, usb_sndbulkpipe(data->usb_dev, diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 2bccefebff1b..9bf97fca2259 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -979,9 +979,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, return -EINVAL; if (!uurb->buffer) return -EINVAL; - if (uurb->signr != 0 && (uurb->signr < SIGRTMIN || - uurb->signr > SIGRTMAX)) - return -EINVAL; if (!(uurb->type == USBDEVFS_URB_TYPE_CONTROL && (uurb->endpoint & ~USB_ENDPOINT_DIR_MASK) == 0)) { ifnum = findintfep(ps->dev, uurb->endpoint); @@ -1399,8 +1396,6 @@ static int proc_disconnectsignal(struct dev_state *ps, void __user *arg) if (copy_from_user(&ds, arg, sizeof(ds))) return -EFAULT; - if (ds.signr != 0 && (ds.signr < SIGRTMIN || ds.signr > SIGRTMAX)) - return -EINVAL; ps->discsignr = ds.signr; ps->disccontext = ds.context; return 0; diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 3d7793d93031..3b28897ee147 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -184,6 +184,20 @@ static int usb_unbind_device(struct device *dev) return 0; } +/* + * Cancel any pending scheduled resets + * + * [see usb_queue_reset_device()] + * + * Called after unconfiguring / when releasing interfaces. See + * comments in __usb_queue_reset_device() regarding + * udev->reset_running. + */ +static void usb_cancel_queued_reset(struct usb_interface *iface) +{ + if (iface->reset_running == 0) + cancel_work_sync(&iface->reset_ws); +} /* called from driver core with dev locked */ static int usb_probe_interface(struct device *dev) @@ -242,6 +256,7 @@ static int usb_probe_interface(struct device *dev) mark_quiesced(intf); intf->needs_remote_wakeup = 0; intf->condition = USB_INTERFACE_UNBOUND; + usb_cancel_queued_reset(intf); } else intf->condition = USB_INTERFACE_BOUND; @@ -272,6 +287,7 @@ static int usb_unbind_interface(struct device *dev) usb_disable_interface(udev, intf); driver->disconnect(intf); + usb_cancel_queued_reset(intf); /* Reset other interface state. * We cannot do a Set-Interface if the device is suspended or @@ -378,8 +394,10 @@ void usb_driver_release_interface(struct usb_driver *driver, if (device_is_registered(dev)) { iface->condition = USB_INTERFACE_UNBINDING; device_release_driver(dev); + } else { + iface->condition = USB_INTERFACE_UNBOUND; + usb_cancel_queued_reset(iface); } - dev->driver = NULL; usb_set_intfdata(iface, NULL); @@ -902,7 +920,7 @@ static int usb_suspend_device(struct usb_device *udev, pm_message_t msg) } /* Caller has locked udev's pm_mutex */ -static int usb_resume_device(struct usb_device *udev) +static int usb_resume_device(struct usb_device *udev, pm_message_t msg) { struct usb_device_driver *udriver; int status = 0; @@ -920,7 +938,7 @@ static int usb_resume_device(struct usb_device *udev) udev->reset_resume = 1; udriver = to_usb_device_driver(udev->dev.driver); - status = udriver->resume(udev); + status = udriver->resume(udev, msg); done: dev_vdbg(&udev->dev, "%s: status %d\n", __func__, status); @@ -940,7 +958,8 @@ static int usb_suspend_interface(struct usb_device *udev, if (udev->state == USB_STATE_NOTATTACHED || !is_active(intf)) goto done; - if (intf->condition == USB_INTERFACE_UNBOUND) /* This can't happen */ + /* This can happen; see usb_driver_release_interface() */ + if (intf->condition == USB_INTERFACE_UNBOUND) goto done; driver = to_usb_driver(intf->dev.driver); @@ -948,7 +967,7 @@ static int usb_suspend_interface(struct usb_device *udev, status = driver->suspend(intf, msg); if (status == 0) mark_quiesced(intf); - else if (!udev->auto_pm) + else if (!(msg.event & PM_EVENT_AUTO)) dev_err(&intf->dev, "%s error %d\n", "suspend", status); } else { @@ -966,7 +985,7 @@ static int usb_suspend_interface(struct usb_device *udev, /* Caller has locked intf's usb_device's pm_mutex */ static int usb_resume_interface(struct usb_device *udev, - struct usb_interface *intf, int reset_resume) + struct usb_interface *intf, pm_message_t msg, int reset_resume) { struct usb_driver *driver; int status = 0; @@ -1090,7 +1109,7 @@ static int autosuspend_check(struct usb_device *udev, int reschedule) if (reschedule) { if (!timer_pending(&udev->autosuspend.timer)) { queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend, - round_jiffies_relative(suspend_time - j)); + round_jiffies_up_relative(suspend_time - j)); } return -EAGAIN; } @@ -1117,10 +1136,9 @@ static inline int autosuspend_check(struct usb_device *udev, int reschedule) * all the interfaces which were suspended are resumed so that they remain * in the same state as the device. * - * If an autosuspend is in progress (@udev->auto_pm is set), the routine - * checks first to make sure that neither the device itself or any of its - * active interfaces is in use (pm_usage_cnt is greater than 0). If they - * are, the autosuspend fails. + * If an autosuspend is in progress the routine checks first to make sure + * that neither the device itself or any of its active interfaces is in use + * (pm_usage_cnt is greater than 0). If they are, the autosuspend fails. * * If the suspend succeeds, the routine recursively queues an autosuspend * request for @udev's parent device, thereby propagating the change up @@ -1155,7 +1173,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) udev->do_remote_wakeup = device_may_wakeup(&udev->dev); - if (udev->auto_pm) { + if (msg.event & PM_EVENT_AUTO) { status = autosuspend_check(udev, 0); if (status < 0) goto done; @@ -1175,13 +1193,16 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) /* If the suspend failed, resume interfaces that did get suspended */ if (status != 0) { + pm_message_t msg2; + + msg2.event = msg.event ^ (PM_EVENT_SUSPEND | PM_EVENT_RESUME); while (--i >= 0) { intf = udev->actconfig->interface[i]; - usb_resume_interface(udev, intf, 0); + usb_resume_interface(udev, intf, msg2, 0); } /* Try another autosuspend when the interfaces aren't busy */ - if (udev->auto_pm) + if (msg.event & PM_EVENT_AUTO) autosuspend_check(udev, status == -EBUSY); /* If the suspend succeeded then prevent any more URB submissions, @@ -1211,6 +1232,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) /** * usb_resume_both - resume a USB device and its interfaces * @udev: the usb_device to resume + * @msg: Power Management message describing this state transition * * This is the central routine for resuming USB devices. It calls the * the resume method for @udev and then calls the resume methods for all @@ -1236,7 +1258,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) * * This routine can run only in process context. */ -static int usb_resume_both(struct usb_device *udev) +static int usb_resume_both(struct usb_device *udev, pm_message_t msg) { int status = 0; int i; @@ -1252,14 +1274,15 @@ static int usb_resume_both(struct usb_device *udev) /* Propagate the resume up the tree, if necessary */ if (udev->state == USB_STATE_SUSPENDED) { - if (udev->auto_pm && udev->autoresume_disabled) { + if ((msg.event & PM_EVENT_AUTO) && + udev->autoresume_disabled) { status = -EPERM; goto done; } if (parent) { status = usb_autoresume_device(parent); if (status == 0) { - status = usb_resume_device(udev); + status = usb_resume_device(udev, msg); if (status || udev->state == USB_STATE_NOTATTACHED) { usb_autosuspend_device(parent); @@ -1282,15 +1305,16 @@ static int usb_resume_both(struct usb_device *udev) /* We can't progagate beyond the USB subsystem, * so if a root hub's controller is suspended * then we're stuck. */ - status = usb_resume_device(udev); + status = usb_resume_device(udev, msg); } } else if (udev->reset_resume) - status = usb_resume_device(udev); + status = usb_resume_device(udev, msg); if (status == 0 && udev->actconfig) { for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { intf = udev->actconfig->interface[i]; - usb_resume_interface(udev, intf, udev->reset_resume); + usb_resume_interface(udev, intf, msg, + udev->reset_resume); } } @@ -1318,13 +1342,13 @@ static int usb_autopm_do_device(struct usb_device *udev, int inc_usage_cnt) udev->last_busy = jiffies; if (inc_usage_cnt >= 0 && udev->pm_usage_cnt > 0) { if (udev->state == USB_STATE_SUSPENDED) - status = usb_resume_both(udev); + status = usb_resume_both(udev, PMSG_AUTO_RESUME); if (status != 0) udev->pm_usage_cnt -= inc_usage_cnt; else if (inc_usage_cnt) udev->last_busy = jiffies; } else if (inc_usage_cnt <= 0 && udev->pm_usage_cnt <= 0) { - status = usb_suspend_both(udev, PMSG_SUSPEND); + status = usb_suspend_both(udev, PMSG_AUTO_SUSPEND); } usb_pm_unlock(udev); return status; @@ -1339,6 +1363,19 @@ void usb_autosuspend_work(struct work_struct *work) usb_autopm_do_device(udev, 0); } +/* usb_autoresume_work - callback routine to autoresume a USB device */ +void usb_autoresume_work(struct work_struct *work) +{ + struct usb_device *udev = + container_of(work, struct usb_device, autoresume); + + /* Wake it up, let the drivers do their thing, and then put it + * back to sleep. + */ + if (usb_autopm_do_device(udev, 1) == 0) + usb_autopm_do_device(udev, -1); +} + /** * usb_autosuspend_device - delayed autosuspend of a USB device and its interfaces * @udev: the usb_device to autosuspend @@ -1435,13 +1472,14 @@ static int usb_autopm_do_interface(struct usb_interface *intf, udev->last_busy = jiffies; if (inc_usage_cnt >= 0 && intf->pm_usage_cnt > 0) { if (udev->state == USB_STATE_SUSPENDED) - status = usb_resume_both(udev); + status = usb_resume_both(udev, + PMSG_AUTO_RESUME); if (status != 0) intf->pm_usage_cnt -= inc_usage_cnt; else udev->last_busy = jiffies; } else if (inc_usage_cnt <= 0 && intf->pm_usage_cnt <= 0) { - status = usb_suspend_both(udev, PMSG_SUSPEND); + status = usb_suspend_both(udev, PMSG_AUTO_SUSPEND); } } usb_pm_unlock(udev); @@ -1490,6 +1528,45 @@ void usb_autopm_put_interface(struct usb_interface *intf) EXPORT_SYMBOL_GPL(usb_autopm_put_interface); /** + * usb_autopm_put_interface_async - decrement a USB interface's PM-usage counter + * @intf: the usb_interface whose counter should be decremented + * + * This routine does essentially the same thing as + * usb_autopm_put_interface(): it decrements @intf's usage counter and + * queues a delayed autosuspend request if the counter is <= 0. The + * difference is that it does not acquire the device's pm_mutex; + * callers must handle all synchronization issues themselves. + * + * Typically a driver would call this routine during an URB's completion + * handler, if no more URBs were pending. + * + * This routine can run in atomic context. + */ +void usb_autopm_put_interface_async(struct usb_interface *intf) +{ + struct usb_device *udev = interface_to_usbdev(intf); + int status = 0; + + if (intf->condition == USB_INTERFACE_UNBOUND) { + status = -ENODEV; + } else { + udev->last_busy = jiffies; + --intf->pm_usage_cnt; + if (udev->autosuspend_disabled || udev->autosuspend_delay < 0) + status = -EPERM; + else if (intf->pm_usage_cnt <= 0 && + !timer_pending(&udev->autosuspend.timer)) { + queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend, + round_jiffies_up_relative( + udev->autosuspend_delay)); + } + } + dev_vdbg(&intf->dev, "%s: status %d cnt %d\n", + __func__, status, intf->pm_usage_cnt); +} +EXPORT_SYMBOL_GPL(usb_autopm_put_interface_async); + +/** * usb_autopm_get_interface - increment a USB interface's PM-usage counter * @intf: the usb_interface whose counter should be incremented * @@ -1535,6 +1612,37 @@ int usb_autopm_get_interface(struct usb_interface *intf) EXPORT_SYMBOL_GPL(usb_autopm_get_interface); /** + * usb_autopm_get_interface_async - increment a USB interface's PM-usage counter + * @intf: the usb_interface whose counter should be incremented + * + * This routine does much the same thing as + * usb_autopm_get_interface(): it increments @intf's usage counter and + * queues an autoresume request if the result is > 0. The differences + * are that it does not acquire the device's pm_mutex (callers must + * handle all synchronization issues themselves), and it does not + * autoresume the device directly (it only queues a request). After a + * successful call, the device will generally not yet be resumed. + * + * This routine can run in atomic context. + */ +int usb_autopm_get_interface_async(struct usb_interface *intf) +{ + struct usb_device *udev = interface_to_usbdev(intf); + int status = 0; + + if (intf->condition == USB_INTERFACE_UNBOUND) + status = -ENODEV; + else if (udev->autoresume_disabled) + status = -EPERM; + else if (++intf->pm_usage_cnt > 0 && udev->state == USB_STATE_SUSPENDED) + queue_work(ksuspend_usb_wq, &udev->autoresume); + dev_vdbg(&intf->dev, "%s: status %d cnt %d\n", + __func__, status, intf->pm_usage_cnt); + return status; +} +EXPORT_SYMBOL_GPL(usb_autopm_get_interface_async); + +/** * usb_autopm_set_interface - set a USB interface's autosuspend state * @intf: the usb_interface whose state should be set * @@ -1561,6 +1669,9 @@ EXPORT_SYMBOL_GPL(usb_autopm_set_interface); void usb_autosuspend_work(struct work_struct *work) {} +void usb_autoresume_work(struct work_struct *work) +{} + #endif /* CONFIG_USB_SUSPEND */ /** @@ -1593,6 +1704,7 @@ int usb_external_suspend_device(struct usb_device *udev, pm_message_t msg) /** * usb_external_resume_device - external resume of a USB device and its interfaces * @udev: the usb_device to resume + * @msg: Power Management message describing this state transition * * This routine handles external resume requests: ones not generated * internally by a USB driver (autoresume) but rather coming from the user @@ -1601,13 +1713,13 @@ int usb_external_suspend_device(struct usb_device *udev, pm_message_t msg) * * The caller must hold @udev's device lock. */ -int usb_external_resume_device(struct usb_device *udev) +int usb_external_resume_device(struct usb_device *udev, pm_message_t msg) { int status; usb_pm_lock(udev); udev->auto_pm = 0; - status = usb_resume_both(udev); + status = usb_resume_both(udev, msg); udev->last_busy = jiffies; usb_pm_unlock(udev); if (status == 0) @@ -1620,7 +1732,7 @@ int usb_external_resume_device(struct usb_device *udev) return status; } -int usb_suspend(struct device *dev, pm_message_t message) +int usb_suspend(struct device *dev, pm_message_t msg) { struct usb_device *udev; @@ -1639,10 +1751,10 @@ int usb_suspend(struct device *dev, pm_message_t message) } udev->skip_sys_resume = 0; - return usb_external_suspend_device(udev, message); + return usb_external_suspend_device(udev, msg); } -int usb_resume(struct device *dev) +int usb_resume(struct device *dev, pm_message_t msg) { struct usb_device *udev; @@ -1654,7 +1766,7 @@ int usb_resume(struct device *dev) */ if (udev->skip_sys_resume) return 0; - return usb_external_resume_device(udev); + return usb_external_resume_device(udev, msg); } #endif /* CONFIG_PM */ diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index 7e912f21fd36..30ecac3af15a 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -200,18 +200,18 @@ static int generic_suspend(struct usb_device *udev, pm_message_t msg) * interfaces manually by doing a bus (or "global") suspend. */ if (!udev->parent) - rc = hcd_bus_suspend(udev); + rc = hcd_bus_suspend(udev, msg); /* Non-root devices don't need to do anything for FREEZE or PRETHAW */ else if (msg.event == PM_EVENT_FREEZE || msg.event == PM_EVENT_PRETHAW) rc = 0; else - rc = usb_port_suspend(udev); + rc = usb_port_suspend(udev, msg); return rc; } -static int generic_resume(struct usb_device *udev) +static int generic_resume(struct usb_device *udev, pm_message_t msg) { int rc; @@ -221,9 +221,9 @@ static int generic_resume(struct usb_device *udev) * interfaces manually by doing a bus (or "global") resume. */ if (!udev->parent) - rc = hcd_bus_resume(udev); + rc = hcd_bus_resume(udev, msg); else - rc = usb_port_resume(udev); + rc = usb_port_resume(udev, msg); return rc; } diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index e1b42626d04d..a0079876d74e 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1573,14 +1573,14 @@ int usb_hcd_get_frame_number (struct usb_device *udev) #ifdef CONFIG_PM -int hcd_bus_suspend(struct usb_device *rhdev) +int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg) { struct usb_hcd *hcd = container_of(rhdev->bus, struct usb_hcd, self); int status; int old_state = hcd->state; dev_dbg(&rhdev->dev, "bus %s%s\n", - rhdev->auto_pm ? "auto-" : "", "suspend"); + (msg.event & PM_EVENT_AUTO ? "auto-" : ""), "suspend"); if (!hcd->driver->bus_suspend) { status = -ENOENT; } else { @@ -1598,14 +1598,14 @@ int hcd_bus_suspend(struct usb_device *rhdev) return status; } -int hcd_bus_resume(struct usb_device *rhdev) +int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg) { struct usb_hcd *hcd = container_of(rhdev->bus, struct usb_hcd, self); int status; int old_state = hcd->state; dev_dbg(&rhdev->dev, "usb %s%s\n", - rhdev->auto_pm ? "auto-" : "", "resume"); + (msg.event & PM_EVENT_AUTO ? "auto-" : ""), "resume"); if (!hcd->driver->bus_resume) return -ENOENT; if (hcd->state == HC_STATE_RUNNING) @@ -1638,7 +1638,7 @@ static void hcd_resume_work(struct work_struct *work) usb_lock_device(udev); usb_mark_last_busy(udev); - usb_external_resume_device(udev); + usb_external_resume_device(udev, PMSG_REMOTE_RESUME); usb_unlock_device(udev); } @@ -2028,7 +2028,7 @@ EXPORT_SYMBOL_GPL(usb_hcd_platform_shutdown); /*-------------------------------------------------------------------------*/ -#if defined(CONFIG_USB_MON) +#if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE) struct usb_mon_operations *mon_ops; @@ -2064,4 +2064,4 @@ void usb_mon_deregister (void) } EXPORT_SYMBOL_GPL (usb_mon_deregister); -#endif /* CONFIG_USB_MON */ +#endif /* CONFIG_USB_MON || CONFIG_USB_MON_MODULE */ diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index 9465e70f4dd0..aa5da82d9071 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -16,6 +16,8 @@ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#ifndef __USB_CORE_HCD_H +#define __USB_CORE_HCD_H #ifdef __KERNEL__ @@ -386,8 +388,8 @@ extern int usb_find_interface_driver(struct usb_device *dev, #ifdef CONFIG_PM extern void usb_hcd_resume_root_hub(struct usb_hcd *hcd); extern void usb_root_hub_lost_power(struct usb_device *rhdev); -extern int hcd_bus_suspend(struct usb_device *rhdev); -extern int hcd_bus_resume(struct usb_device *rhdev); +extern int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg); +extern int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg); #else static inline void usb_hcd_resume_root_hub(struct usb_hcd *hcd) { @@ -419,7 +421,7 @@ static inline void usbfs_cleanup(void) { } /*-------------------------------------------------------------------------*/ -#if defined(CONFIG_USB_MON) +#if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE) struct usb_mon_operations { void (*urb_submit)(struct usb_bus *bus, struct urb *urb); @@ -461,7 +463,7 @@ static inline void usbmon_urb_submit_error(struct usb_bus *bus, struct urb *urb, static inline void usbmon_urb_complete(struct usb_bus *bus, struct urb *urb, int status) {} -#endif /* CONFIG_USB_MON */ +#endif /* CONFIG_USB_MON || CONFIG_USB_MON_MODULE */ /*-------------------------------------------------------------------------*/ @@ -490,3 +492,5 @@ extern struct rw_semaphore ehci_cf_port_reset_rwsem; extern unsigned long usb_hcds_loaded; #endif /* __KERNEL__ */ + +#endif /* __USB_CORE_HCD_H */ diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index b19cbfcd51da..fc99ef67761d 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1374,8 +1374,9 @@ static void usb_stop_pm(struct usb_device *udev) usb_autosuspend_device(udev->parent); usb_pm_unlock(udev); - /* Stop any autosuspend requests already submitted */ - cancel_rearming_delayed_work(&udev->autosuspend); + /* Stop any autosuspend or autoresume requests already submitted */ + cancel_delayed_work_sync(&udev->autosuspend); + cancel_work_sync(&udev->autoresume); } #else @@ -1635,6 +1636,10 @@ int usb_new_device(struct usb_device *udev) { int err; + /* Increment the parent's count of unsuspended children */ + if (udev->parent) + usb_autoresume_device(udev->parent); + usb_detect_quirks(udev); /* Determine quirks */ err = usb_configure_device(udev); /* detect & probe dev/intfs */ if (err < 0) @@ -1643,9 +1648,8 @@ int usb_new_device(struct usb_device *udev) udev->dev.devt = MKDEV(USB_DEVICE_MAJOR, (((udev->bus->busnum-1) * 128) + (udev->devnum-1))); - /* Increment the parent's count of unsuspended children */ - if (udev->parent) - usb_autoresume_device(udev->parent); + /* Tell the world! */ + announce_device(udev); /* Register the device. The device driver is responsible * for adding the device files to sysfs and for configuring @@ -1659,13 +1663,11 @@ int usb_new_device(struct usb_device *udev) /* put device-specific files into sysfs */ usb_create_sysfs_dev_files(udev); - - /* Tell the world! */ - announce_device(udev); return err; fail: usb_set_device_state(udev, USB_STATE_NOTATTACHED); + usb_stop_pm(udev); return err; } @@ -1982,7 +1984,7 @@ static int check_port_resume_type(struct usb_device *udev, * * Returns 0 on success, else negative errno. */ -int usb_port_suspend(struct usb_device *udev) +int usb_port_suspend(struct usb_device *udev, pm_message_t msg) { struct usb_hub *hub = hdev_to_hub(udev->parent); int port1 = udev->portnum; @@ -2021,7 +2023,7 @@ int usb_port_suspend(struct usb_device *udev) } else { /* device has up to 10 msec to fully suspend */ dev_dbg(&udev->dev, "usb %ssuspend\n", - udev->auto_pm ? "auto-" : ""); + (msg.event & PM_EVENT_AUTO ? "auto-" : "")); usb_set_device_state(udev, USB_STATE_SUSPENDED); msleep(10); } @@ -2140,7 +2142,7 @@ static int finish_port_resume(struct usb_device *udev) * * Returns 0 on success, else negative errno. */ -int usb_port_resume(struct usb_device *udev) +int usb_port_resume(struct usb_device *udev, pm_message_t msg) { struct usb_hub *hub = hdev_to_hub(udev->parent); int port1 = udev->portnum; @@ -2165,7 +2167,7 @@ int usb_port_resume(struct usb_device *udev) } else { /* drive resume for at least 20 msec */ dev_dbg(&udev->dev, "usb %sresume\n", - udev->auto_pm ? "auto-" : ""); + (msg.event & PM_EVENT_AUTO ? "auto-" : "")); msleep(25); /* Virtual root hubs can trigger on GET_PORT_STATUS to @@ -2206,7 +2208,7 @@ static int remote_wakeup(struct usb_device *udev) if (udev->state == USB_STATE_SUSPENDED) { dev_dbg(&udev->dev, "usb %sresume\n", "wakeup-"); usb_mark_last_busy(udev); - status = usb_external_resume_device(udev); + status = usb_external_resume_device(udev, PMSG_REMOTE_RESUME); } return status; } @@ -2215,14 +2217,14 @@ static int remote_wakeup(struct usb_device *udev) /* When CONFIG_USB_SUSPEND isn't set, we never suspend or resume any ports. */ -int usb_port_suspend(struct usb_device *udev) +int usb_port_suspend(struct usb_device *udev, pm_message_t msg) { return 0; } /* However we may need to do a reset-resume */ -int usb_port_resume(struct usb_device *udev) +int usb_port_resume(struct usb_device *udev, pm_message_t msg) { struct usb_hub *hub = hdev_to_hub(udev->parent); int port1 = udev->portnum; @@ -2262,7 +2264,7 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg) udev = hdev->children [port1-1]; if (udev && udev->can_submit) { - if (!hdev->auto_pm) + if (!(msg.event & PM_EVENT_AUTO)) dev_dbg(&intf->dev, "port %d nyet suspended\n", port1); return -EBUSY; @@ -3517,3 +3519,46 @@ int usb_reset_device(struct usb_device *udev) return ret; } EXPORT_SYMBOL_GPL(usb_reset_device); + + +/** + * usb_queue_reset_device - Reset a USB device from an atomic context + * @iface: USB interface belonging to the device to reset + * + * This function can be used to reset a USB device from an atomic + * context, where usb_reset_device() won't work (as it blocks). + * + * Doing a reset via this method is functionally equivalent to calling + * usb_reset_device(), except for the fact that it is delayed to a + * workqueue. This means that any drivers bound to other interfaces + * might be unbound, as well as users from usbfs in user space. + * + * Corner cases: + * + * - Scheduling two resets at the same time from two different drivers + * attached to two different interfaces of the same device is + * possible; depending on how the driver attached to each interface + * handles ->pre_reset(), the second reset might happen or not. + * + * - If a driver is unbound and it had a pending reset, the reset will + * be cancelled. + * + * - This function can be called during .probe() or .disconnect() + * times. On return from .disconnect(), any pending resets will be + * cancelled. + * + * There is no no need to lock/unlock the @reset_ws as schedule_work() + * does its own. + * + * NOTE: We don't do any reference count tracking because it is not + * needed. The lifecycle of the work_struct is tied to the + * usb_interface. Before destroying the interface we cancel the + * work_struct, so the fact that work_struct is queued and or + * running means the interface (and thus, the device) exist and + * are referenced. + */ +void usb_queue_reset_device(struct usb_interface *iface) +{ + schedule_work(&iface->reset_ws); +} +EXPORT_SYMBOL_GPL(usb_queue_reset_device); diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 887738577b28..aadf29f09c45 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -139,9 +139,9 @@ int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, dr->bRequestType = requesttype; dr->bRequest = request; - dr->wValue = cpu_to_le16p(&value); - dr->wIndex = cpu_to_le16p(&index); - dr->wLength = cpu_to_le16p(&size); + dr->wValue = cpu_to_le16(value); + dr->wIndex = cpu_to_le16(index); + dr->wLength = cpu_to_le16(size); /* dbg("usb_control_msg"); */ @@ -1091,6 +1091,7 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0) continue; dev_dbg(&dev->dev, "unregistering interface %s\n", dev_name(&interface->dev)); + interface->unregistering = 1; usb_remove_sysfs_intf_files(interface); device_del(&interface->dev); } @@ -1440,6 +1441,46 @@ static struct usb_interface_assoc_descriptor *find_iad(struct usb_device *dev, return retval; } + +/* + * Internal function to queue a device reset + * + * This is initialized into the workstruct in 'struct + * usb_device->reset_ws' that is launched by + * message.c:usb_set_configuration() when initializing each 'struct + * usb_interface'. + * + * It is safe to get the USB device without reference counts because + * the life cycle of @iface is bound to the life cycle of @udev. Then, + * this function will be ran only if @iface is alive (and before + * freeing it any scheduled instances of it will have been cancelled). + * + * We need to set a flag (usb_dev->reset_running) because when we call + * the reset, the interfaces might be unbound. The current interface + * cannot try to remove the queued work as it would cause a deadlock + * (you cannot remove your work from within your executing + * workqueue). This flag lets it know, so that + * usb_cancel_queued_reset() doesn't try to do it. + * + * See usb_queue_reset_device() for more details + */ +void __usb_queue_reset_device(struct work_struct *ws) +{ + int rc; + struct usb_interface *iface = + container_of(ws, struct usb_interface, reset_ws); + struct usb_device *udev = interface_to_usbdev(iface); + + rc = usb_lock_device_for_reset(udev, iface); + if (rc >= 0) { + iface->reset_running = 1; + usb_reset_device(udev); + iface->reset_running = 0; + usb_unlock_device(udev); + } +} + + /* * usb_set_configuration - Makes a particular device setting be current * @dev: the device whose configuration is being updated @@ -1610,6 +1651,7 @@ free_interfaces: intf->dev.type = &usb_if_device_type; intf->dev.groups = usb_interface_groups; intf->dev.dma_mask = dev->dev.dma_mask; + INIT_WORK(&intf->reset_ws, __usb_queue_reset_device); device_initialize(&intf->dev); mark_quiesced(intf); dev_set_name(&intf->dev, "%d-%s:%d.%d", diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index f66fba11fbd5..0f0ccf640114 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -359,19 +359,19 @@ set_level(struct device *dev, struct device_attribute *attr, strncmp(buf, on_string, len) == 0) { udev->autosuspend_disabled = 1; udev->autoresume_disabled = 0; - rc = usb_external_resume_device(udev); + rc = usb_external_resume_device(udev, PMSG_USER_RESUME); } else if (len == sizeof auto_string - 1 && strncmp(buf, auto_string, len) == 0) { udev->autosuspend_disabled = 0; udev->autoresume_disabled = 0; - rc = usb_external_resume_device(udev); + rc = usb_external_resume_device(udev, PMSG_USER_RESUME); } else if (len == sizeof suspend_string - 1 && strncmp(buf, suspend_string, len) == 0) { udev->autosuspend_disabled = 0; udev->autoresume_disabled = 1; - rc = usb_external_suspend_device(udev, PMSG_SUSPEND); + rc = usb_external_suspend_device(udev, PMSG_USER_SUSPEND); } else rc = -EINVAL; @@ -812,35 +812,14 @@ struct attribute_group *usb_interface_groups[] = { NULL }; -static inline void usb_create_intf_ep_files(struct usb_interface *intf, - struct usb_device *udev) -{ - struct usb_host_interface *iface_desc; - int i; - - iface_desc = intf->cur_altsetting; - for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) - usb_create_ep_files(&intf->dev, &iface_desc->endpoint[i], - udev); -} - -static inline void usb_remove_intf_ep_files(struct usb_interface *intf) -{ - struct usb_host_interface *iface_desc; - int i; - - iface_desc = intf->cur_altsetting; - for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) - usb_remove_ep_files(&iface_desc->endpoint[i]); -} - int usb_create_sysfs_intf_files(struct usb_interface *intf) { struct usb_device *udev = interface_to_usbdev(intf); struct usb_host_interface *alt = intf->cur_altsetting; + int i; int retval; - if (intf->sysfs_files_created) + if (intf->sysfs_files_created || intf->unregistering) return 0; /* The interface string may be present in some altsettings @@ -851,18 +830,22 @@ int usb_create_sysfs_intf_files(struct usb_interface *intf) alt->string = usb_cache_string(udev, alt->desc.iInterface); if (alt->string) retval = device_create_file(&intf->dev, &dev_attr_interface); - usb_create_intf_ep_files(intf, udev); + for (i = 0; i < alt->desc.bNumEndpoints; ++i) + usb_create_ep_files(&intf->dev, &alt->endpoint[i], udev); intf->sysfs_files_created = 1; return 0; } void usb_remove_sysfs_intf_files(struct usb_interface *intf) { - struct device *dev = &intf->dev; + struct usb_host_interface *alt = intf->cur_altsetting; + int i; if (!intf->sysfs_files_created) return; - usb_remove_intf_ep_files(intf); - device_remove_file(dev, &dev_attr_interface); + + for (i = 0; i < alt->desc.bNumEndpoints; ++i) + usb_remove_ep_files(&alt->endpoint[i]); + device_remove_file(&intf->dev, &dev_attr_interface); intf->sysfs_files_created = 0; } diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 4342bd9c3bb6..1f68af9db3f7 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -85,8 +85,8 @@ EXPORT_SYMBOL_GPL(usb_alloc_urb); * Must be called when a user of a urb is finished with it. When the last user * of the urb calls this function, the memory of the urb is freed. * - * Note: The transfer buffer associated with the urb is not freed, that must be - * done elsewhere. + * Note: The transfer buffer associated with the urb is not freed unless the + * URB_FREE_BUFFER transfer flag is set. */ void usb_free_urb(struct urb *urb) { diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index be1fa0723f2c..18ffa17f4b32 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -253,7 +253,7 @@ static int usb_dev_prepare(struct device *dev) static void usb_dev_complete(struct device *dev) { /* Currently used only for rebinding interfaces */ - usb_resume(dev); /* Implement eventually? */ + usb_resume(dev, PMSG_RESUME); /* Message event is meaningless */ } static int usb_dev_suspend(struct device *dev) @@ -263,7 +263,7 @@ static int usb_dev_suspend(struct device *dev) static int usb_dev_resume(struct device *dev) { - return usb_resume(dev); + return usb_resume(dev, PMSG_RESUME); } static int usb_dev_freeze(struct device *dev) @@ -273,7 +273,7 @@ static int usb_dev_freeze(struct device *dev) static int usb_dev_thaw(struct device *dev) { - return usb_resume(dev); + return usb_resume(dev, PMSG_THAW); } static int usb_dev_poweroff(struct device *dev) @@ -283,10 +283,10 @@ static int usb_dev_poweroff(struct device *dev) static int usb_dev_restore(struct device *dev) { - return usb_resume(dev); + return usb_resume(dev, PMSG_RESTORE); } -static struct pm_ops usb_device_pm_ops = { +static struct dev_pm_ops usb_device_pm_ops = { .prepare = usb_dev_prepare, .complete = usb_dev_complete, .suspend = usb_dev_suspend, @@ -301,7 +301,7 @@ static struct pm_ops usb_device_pm_ops = { #define ksuspend_usb_init() 0 #define ksuspend_usb_cleanup() do {} while (0) -#define usb_device_pm_ops (*(struct pm_ops *)0) +#define usb_device_pm_ops (*(struct dev_pm_ops *)0) #endif /* CONFIG_PM */ @@ -402,6 +402,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, #ifdef CONFIG_PM mutex_init(&dev->pm_mutex); INIT_DELAYED_WORK(&dev->autosuspend, usb_autosuspend_work); + INIT_WORK(&dev->autoresume, usb_autoresume_work); dev->autosuspend_delay = usb_autosuspend_delay * HZ; dev->connect_time = jiffies; dev->active_duration = -jiffies; @@ -513,10 +514,7 @@ EXPORT_SYMBOL_GPL(usb_put_intf); * disconnect; in some drivers (such as usb-storage) the disconnect() * or suspend() method will block waiting for a device reset to complete. * - * Returns a negative error code for failure, otherwise 1 or 0 to indicate - * that the device will or will not have to be unlocked. (0 can be - * returned when an interface is given and is BINDING, because in that - * case the driver already owns the device lock.) + * Returns a negative error code for failure, otherwise 0. */ int usb_lock_device_for_reset(struct usb_device *udev, const struct usb_interface *iface) @@ -527,16 +525,9 @@ int usb_lock_device_for_reset(struct usb_device *udev, return -ENODEV; if (udev->state == USB_STATE_SUSPENDED) return -EHOSTUNREACH; - if (iface) { - switch (iface->condition) { - case USB_INTERFACE_BINDING: - return 0; - case USB_INTERFACE_BOUND: - break; - default: - return -EINTR; - } - } + if (iface && (iface->condition == USB_INTERFACE_UNBINDING || + iface->condition == USB_INTERFACE_UNBOUND)) + return -EINTR; while (usb_trylock_device(udev) != 0) { @@ -550,10 +541,11 @@ int usb_lock_device_for_reset(struct usb_device *udev, return -ENODEV; if (udev->state == USB_STATE_SUSPENDED) return -EHOSTUNREACH; - if (iface && iface->condition != USB_INTERFACE_BOUND) + if (iface && (iface->condition == USB_INTERFACE_UNBINDING || + iface->condition == USB_INTERFACE_UNBOUND)) return -EINTR; } - return 1; + return 0; } EXPORT_SYMBOL_GPL(usb_lock_device_for_reset); @@ -962,9 +954,6 @@ void usb_buffer_unmap_sg(const struct usb_device *dev, int is_in, } EXPORT_SYMBOL_GPL(usb_buffer_unmap_sg); -/* format to disable USB on kernel command line is: nousb */ -__module_param_call("", nousb, param_set_bool, param_get_bool, &nousb, 0444); - /* * for external read access to <nousb> */ @@ -1052,6 +1041,11 @@ static void __exit usb_exit(void) ksuspend_usb_cleanup(); } +/* To disable USB, kernel command line is 'nousb' not 'usbcore.nousb' */ +#undef MODULE_PARAM_PREFIX +#define MODULE_PARAM_PREFIX +module_param(nousb, bool, 0444); + subsys_initcall(usb_init); module_exit(usb_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 9a1a45ac3add..9fb195665fa8 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -1,3 +1,5 @@ +#include <linux/pm.h> + /* Functions local to drivers/usb/core/ */ extern int usb_create_sysfs_dev_files(struct usb_device *dev); @@ -42,14 +44,16 @@ extern void usb_host_cleanup(void); #ifdef CONFIG_PM extern int usb_suspend(struct device *dev, pm_message_t msg); -extern int usb_resume(struct device *dev); +extern int usb_resume(struct device *dev, pm_message_t msg); extern void usb_autosuspend_work(struct work_struct *work); -extern int usb_port_suspend(struct usb_device *dev); -extern int usb_port_resume(struct usb_device *dev); +extern void usb_autoresume_work(struct work_struct *work); +extern int usb_port_suspend(struct usb_device *dev, pm_message_t msg); +extern int usb_port_resume(struct usb_device *dev, pm_message_t msg); extern int usb_external_suspend_device(struct usb_device *udev, pm_message_t msg); -extern int usb_external_resume_device(struct usb_device *udev); +extern int usb_external_resume_device(struct usb_device *udev, + pm_message_t msg); static inline void usb_pm_lock(struct usb_device *udev) { @@ -63,12 +67,12 @@ static inline void usb_pm_unlock(struct usb_device *udev) #else -static inline int usb_port_suspend(struct usb_device *udev) +static inline int usb_port_suspend(struct usb_device *udev, pm_message_t msg) { return 0; } -static inline int usb_port_resume(struct usb_device *udev) +static inline int usb_port_resume(struct usb_device *udev, pm_message_t msg) { return 0; } diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index dd4cd5a51370..b61d101184d5 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -305,6 +305,27 @@ config USB_GADGET_MUSB_HDRC This OTG-capable silicon IP is used in dual designs including the TI DaVinci, OMAP 243x, OMAP 343x, and TUSB 6010. +config USB_GADGET_IMX + boolean "Freescale IMX USB Peripheral Controller" + depends on ARCH_MX1 + help + Freescale's IMX series include an integrated full speed + USB 1.1 device controller. The controller in the IMX series + is register-compatible. + + It has Six fixed-function endpoints, as well as endpoint + zero (for control transfers). + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "imx_udc" and force all + gadget drivers to also be dynamically linked. + +config USB_IMX + tristate + depends on USB_GADGET_IMX + default USB_GADGET + select USB_GADGET_SELECTED + config USB_GADGET_M66592 boolean "Renesas M66592 USB Peripheral Controller" select USB_GADGET_DUALSPEED @@ -377,6 +398,24 @@ config USB_FSL_QE default USB_GADGET select USB_GADGET_SELECTED +config USB_GADGET_CI13XXX + boolean "MIPS USB CI13xxx" + depends on PCI + select USB_GADGET_DUALSPEED + help + MIPS USB IP core family device controller + Currently it only supports IP part number CI13412 + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "ci13xxx_udc" and force all + gadget drivers to also be dynamically linked. + +config USB_CI13XXX + tristate + depends on USB_GADGET_CI13XXX + default USB_GADGET + select USB_GADGET_SELECTED + config USB_GADGET_NET2280 boolean "NetChip 228x" depends on PCI diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index bd4041b47dce..39a51d746cb7 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_USB_NET2280) += net2280.o obj-$(CONFIG_USB_AMD5536UDC) += amd5536udc.o obj-$(CONFIG_USB_PXA25X) += pxa25x_udc.o obj-$(CONFIG_USB_PXA27X) += pxa27x_udc.o +obj-$(CONFIG_USB_IMX) += imx_udc.o obj-$(CONFIG_USB_GOKU) += goku_udc.o obj-$(CONFIG_USB_OMAP) += omap_udc.o obj-$(CONFIG_USB_LH7A40X) += lh7a40x_udc.o @@ -19,6 +20,7 @@ obj-$(CONFIG_USB_ATMEL_USBA) += atmel_usba_udc.o obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o obj-$(CONFIG_USB_M66592) += m66592-udc.o obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o +obj-$(CONFIG_USB_CI13XXX) += ci13xxx_udc.o # # USB gadget drivers diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index a8a1de413321..0b2bb8f0706d 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -1474,7 +1474,7 @@ static struct at91_udc controller = { .ep0 = &controller.ep[0].ep, .name = driver_name, .dev = { - .bus_id = "gadget", + .init_name = "gadget", .release = nop_release, } }, diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c index ae30ab1d264f..65b03e3445a1 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -1034,7 +1034,7 @@ static struct usba_udc the_udc = { .is_dualspeed = 1, .name = "atmel_usba_udc", .dev = { - .bus_id = "gadget", + .init_name = "gadget", .release = nop_release, }, }, diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c new file mode 100644 index 000000000000..bebf911c7e5f --- /dev/null +++ b/drivers/usb/gadget/ci13xxx_udc.c @@ -0,0 +1,2830 @@ +/* + * ci13xxx_udc.c - MIPS USB IP core family device controller + * + * Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved. + * + * Author: David Lopo + * + * 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. + */ + +/* + * Description: MIPS USB IP core family device controller + * Currently it only supports IP part number CI13412 + * + * This driver is composed of several blocks: + * - HW: hardware interface + * - DBG: debug facilities (optional) + * - UTIL: utilities + * - ISR: interrupts handling + * - ENDPT: endpoint operations (Gadget API) + * - GADGET: gadget operations (Gadget API) + * - BUS: bus glue code, bus abstraction layer + * - PCI: PCI core interface and PCI resources (interrupts, memory...) + * + * Compile Options + * - CONFIG_USB_GADGET_DEBUG_FILES: enable debug facilities + * - STALL_IN: non-empty bulk-in pipes cannot be halted + * if defined mass storage compliance succeeds but with warnings + * => case 4: Hi > Dn + * => case 5: Hi > Di + * => case 8: Hi <> Do + * if undefined usbtest 13 fails + * - TRACE: enable function tracing (depends on DEBUG) + * + * Main Features + * - Chapter 9 & Mass Storage Compliance with Gadget File Storage + * - Chapter 9 Compliance with Gadget Zero (STALL_IN undefined) + * - Normal & LPM support + * + * USBTEST Report + * - OK: 0-12, 13 (STALL_IN defined) & 14 + * - Not Supported: 15 & 16 (ISO) + * + * TODO List + * - OTG + * - Isochronous & Interrupt Traffic + * - Handle requests which spawns into several TDs + * - GET_STATUS(device) - always reports 0 + * - Gadget API (majority of optional features) + * - Suspend & Remote Wakeup + */ +#include <linux/device.h> +#include <linux/dmapool.h> +#include <linux/dma-mapping.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> + +#include "ci13xxx_udc.h" + + +/****************************************************************************** + * DEFINE + *****************************************************************************/ +/* ctrl register bank access */ +static DEFINE_SPINLOCK(udc_lock); + +/* driver name */ +#define UDC_DRIVER_NAME "ci13xxx_udc" + +/* control endpoint description */ +static const struct usb_endpoint_descriptor +ctrl_endpt_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, + .wMaxPacketSize = cpu_to_le16(CTRL_PAYLOAD_MAX), +}; + +/* UDC descriptor */ +static struct ci13xxx *_udc; + +/* Interrupt statistics */ +#define ISR_MASK 0x1F +static struct { + u32 test; + u32 ui; + u32 uei; + u32 pci; + u32 uri; + u32 sli; + u32 none; + struct { + u32 cnt; + u32 buf[ISR_MASK+1]; + u32 idx; + } hndl; +} isr_statistics; + +/** + * ffs_nr: find first (least significant) bit set + * @x: the word to search + * + * This function returns bit number (instead of position) + */ +static int ffs_nr(u32 x) +{ + int n = ffs(x); + + return n ? n-1 : 32; +} + +/****************************************************************************** + * HW block + *****************************************************************************/ +/* register bank descriptor */ +static struct { + unsigned lpm; /* is LPM? */ + void __iomem *abs; /* bus map offset */ + void __iomem *cap; /* bus map offset + CAP offset + CAP data */ + size_t size; /* bank size */ +} hw_bank; + +/* UDC register map */ +#define ABS_CAPLENGTH (0x100UL) +#define ABS_HCCPARAMS (0x108UL) +#define ABS_DCCPARAMS (0x124UL) +#define ABS_TESTMODE (hw_bank.lpm ? 0x0FCUL : 0x138UL) +/* offset to CAPLENTGH (addr + data) */ +#define CAP_USBCMD (0x000UL) +#define CAP_USBSTS (0x004UL) +#define CAP_USBINTR (0x008UL) +#define CAP_DEVICEADDR (0x014UL) +#define CAP_ENDPTLISTADDR (0x018UL) +#define CAP_PORTSC (0x044UL) +#define CAP_DEVLC (0x0B4UL) +#define CAP_USBMODE (hw_bank.lpm ? 0x0C8UL : 0x068UL) +#define CAP_ENDPTSETUPSTAT (hw_bank.lpm ? 0x0D8UL : 0x06CUL) +#define CAP_ENDPTPRIME (hw_bank.lpm ? 0x0DCUL : 0x070UL) +#define CAP_ENDPTFLUSH (hw_bank.lpm ? 0x0E0UL : 0x074UL) +#define CAP_ENDPTSTAT (hw_bank.lpm ? 0x0E4UL : 0x078UL) +#define CAP_ENDPTCOMPLETE (hw_bank.lpm ? 0x0E8UL : 0x07CUL) +#define CAP_ENDPTCTRL (hw_bank.lpm ? 0x0ECUL : 0x080UL) +#define CAP_LAST (hw_bank.lpm ? 0x12CUL : 0x0C0UL) + +/* maximum number of enpoints: valid only after hw_device_reset() */ +static unsigned hw_ep_max; + +/** + * hw_ep_bit: calculates the bit number + * @num: endpoint number + * @dir: endpoint direction + * + * This function returns bit number + */ +static inline int hw_ep_bit(int num, int dir) +{ + return num + (dir ? 16 : 0); +} + +/** + * hw_aread: reads from register bitfield + * @addr: address relative to bus map + * @mask: bitfield mask + * + * This function returns register bitfield data + */ +static u32 hw_aread(u32 addr, u32 mask) +{ + return ioread32(addr + hw_bank.abs) & mask; +} + +/** + * hw_awrite: writes to register bitfield + * @addr: address relative to bus map + * @mask: bitfield mask + * @data: new data + */ +static void hw_awrite(u32 addr, u32 mask, u32 data) +{ + iowrite32(hw_aread(addr, ~mask) | (data & mask), + addr + hw_bank.abs); +} + +/** + * hw_cread: reads from register bitfield + * @addr: address relative to CAP offset plus content + * @mask: bitfield mask + * + * This function returns register bitfield data + */ +static u32 hw_cread(u32 addr, u32 mask) +{ + return ioread32(addr + hw_bank.cap) & mask; +} + +/** + * hw_cwrite: writes to register bitfield + * @addr: address relative to CAP offset plus content + * @mask: bitfield mask + * @data: new data + */ +static void hw_cwrite(u32 addr, u32 mask, u32 data) +{ + iowrite32(hw_cread(addr, ~mask) | (data & mask), + addr + hw_bank.cap); +} + +/** + * hw_ctest_and_clear: tests & clears register bitfield + * @addr: address relative to CAP offset plus content + * @mask: bitfield mask + * + * This function returns register bitfield data + */ +static u32 hw_ctest_and_clear(u32 addr, u32 mask) +{ + u32 reg = hw_cread(addr, mask); + + iowrite32(reg, addr + hw_bank.cap); + return reg; +} + +/** + * hw_ctest_and_write: tests & writes register bitfield + * @addr: address relative to CAP offset plus content + * @mask: bitfield mask + * @data: new data + * + * This function returns register bitfield data + */ +static u32 hw_ctest_and_write(u32 addr, u32 mask, u32 data) +{ + u32 reg = hw_cread(addr, ~0); + + iowrite32((reg & ~mask) | (data & mask), addr + hw_bank.cap); + return (reg & mask) >> ffs_nr(mask); +} + +/** + * hw_device_reset: resets chip (execute without interruption) + * @base: register base address + * + * This function returns an error code + */ +static int hw_device_reset(void __iomem *base) +{ + u32 reg; + + /* bank is a module variable */ + hw_bank.abs = base; + + hw_bank.cap = hw_bank.abs; + hw_bank.cap += ABS_CAPLENGTH; + hw_bank.cap += ioread8(hw_bank.cap); + + reg = hw_aread(ABS_HCCPARAMS, HCCPARAMS_LEN) >> ffs_nr(HCCPARAMS_LEN); + hw_bank.lpm = reg; + hw_bank.size = hw_bank.cap - hw_bank.abs; + hw_bank.size += CAP_LAST; + hw_bank.size /= sizeof(u32); + + /* should flush & stop before reset */ + hw_cwrite(CAP_ENDPTFLUSH, ~0, ~0); + hw_cwrite(CAP_USBCMD, USBCMD_RS, 0); + + hw_cwrite(CAP_USBCMD, USBCMD_RST, USBCMD_RST); + while (hw_cread(CAP_USBCMD, USBCMD_RST)) + udelay(10); /* not RTOS friendly */ + + /* USBMODE should be configured step by step */ + hw_cwrite(CAP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE); + hw_cwrite(CAP_USBMODE, USBMODE_CM, USBMODE_CM_DEVICE); + hw_cwrite(CAP_USBMODE, USBMODE_SLOM, USBMODE_SLOM); /* HW >= 2.3 */ + + if (hw_cread(CAP_USBMODE, USBMODE_CM) != USBMODE_CM_DEVICE) { + pr_err("cannot enter in device mode"); + pr_err("lpm = %i", hw_bank.lpm); + return -ENODEV; + } + + reg = hw_aread(ABS_DCCPARAMS, DCCPARAMS_DEN) >> ffs_nr(DCCPARAMS_DEN); + if (reg == 0 || reg > ENDPT_MAX) + return -ENODEV; + + hw_ep_max = reg; /* cache hw ENDPT_MAX */ + + /* setup lock mode ? */ + + /* ENDPTSETUPSTAT is '0' by default */ + + /* HCSPARAMS.bf.ppc SHOULD BE zero for device */ + + return 0; +} + +/** + * hw_device_state: enables/disables interrupts & starts/stops device (execute + * without interruption) + * @dma: 0 => disable, !0 => enable and set dma engine + * + * This function returns an error code + */ +static int hw_device_state(u32 dma) +{ + if (dma) { + hw_cwrite(CAP_ENDPTLISTADDR, ~0, dma); + /* interrupt, error, port change, reset, sleep/suspend */ + hw_cwrite(CAP_USBINTR, ~0, + USBi_UI|USBi_UEI|USBi_PCI|USBi_URI|USBi_SLI); + hw_cwrite(CAP_USBCMD, USBCMD_RS, USBCMD_RS); + } else { + hw_cwrite(CAP_USBCMD, USBCMD_RS, 0); + hw_cwrite(CAP_USBINTR, ~0, 0); + } + return 0; +} + +/** + * hw_ep_flush: flush endpoint fifo (execute without interruption) + * @num: endpoint number + * @dir: endpoint direction + * + * This function returns an error code + */ +static int hw_ep_flush(int num, int dir) +{ + int n = hw_ep_bit(num, dir); + + do { + /* flush any pending transfer */ + hw_cwrite(CAP_ENDPTFLUSH, BIT(n), BIT(n)); + while (hw_cread(CAP_ENDPTFLUSH, BIT(n))) + cpu_relax(); + } while (hw_cread(CAP_ENDPTSTAT, BIT(n))); + + return 0; +} + +/** + * hw_ep_disable: disables endpoint (execute without interruption) + * @num: endpoint number + * @dir: endpoint direction + * + * This function returns an error code + */ +static int hw_ep_disable(int num, int dir) +{ + hw_ep_flush(num, dir); + hw_cwrite(CAP_ENDPTCTRL + num * sizeof(u32), + dir ? ENDPTCTRL_TXE : ENDPTCTRL_RXE, 0); + return 0; +} + +/** + * hw_ep_enable: enables endpoint (execute without interruption) + * @num: endpoint number + * @dir: endpoint direction + * @type: endpoint type + * + * This function returns an error code + */ +static int hw_ep_enable(int num, int dir, int type) +{ + u32 mask, data; + + if (dir) { + mask = ENDPTCTRL_TXT; /* type */ + data = type << ffs_nr(mask); + + mask |= ENDPTCTRL_TXS; /* unstall */ + mask |= ENDPTCTRL_TXR; /* reset data toggle */ + data |= ENDPTCTRL_TXR; + mask |= ENDPTCTRL_TXE; /* enable */ + data |= ENDPTCTRL_TXE; + } else { + mask = ENDPTCTRL_RXT; /* type */ + data = type << ffs_nr(mask); + + mask |= ENDPTCTRL_RXS; /* unstall */ + mask |= ENDPTCTRL_RXR; /* reset data toggle */ + data |= ENDPTCTRL_RXR; + mask |= ENDPTCTRL_RXE; /* enable */ + data |= ENDPTCTRL_RXE; + } + hw_cwrite(CAP_ENDPTCTRL + num * sizeof(u32), mask, data); + return 0; +} + +/** + * hw_ep_get_halt: return endpoint halt status + * @num: endpoint number + * @dir: endpoint direction + * + * This function returns 1 if endpoint halted + */ +static int hw_ep_get_halt(int num, int dir) +{ + u32 mask = dir ? ENDPTCTRL_TXS : ENDPTCTRL_RXS; + + return hw_cread(CAP_ENDPTCTRL + num * sizeof(u32), mask) ? 1 : 0; +} + +/** + * hw_ep_is_primed: test if endpoint is primed (execute without interruption) + * @num: endpoint number + * @dir: endpoint direction + * + * This function returns true if endpoint primed + */ +static int hw_ep_is_primed(int num, int dir) +{ + u32 reg = hw_cread(CAP_ENDPTPRIME, ~0) | hw_cread(CAP_ENDPTSTAT, ~0); + + return test_bit(hw_ep_bit(num, dir), (void *)®); +} + +/** + * hw_test_and_clear_setup_status: test & clear setup status (execute without + * interruption) + * @n: bit number (endpoint) + * + * This function returns setup status + */ +static int hw_test_and_clear_setup_status(int n) +{ + return hw_ctest_and_clear(CAP_ENDPTSETUPSTAT, BIT(n)); +} + +/** + * hw_ep_prime: primes endpoint (execute without interruption) + * @num: endpoint number + * @dir: endpoint direction + * @is_ctrl: true if control endpoint + * + * This function returns an error code + */ +static int hw_ep_prime(int num, int dir, int is_ctrl) +{ + int n = hw_ep_bit(num, dir); + + /* the caller should flush first */ + if (hw_ep_is_primed(num, dir)) + return -EBUSY; + + if (is_ctrl && dir == RX && hw_cread(CAP_ENDPTSETUPSTAT, BIT(num))) + return -EAGAIN; + + hw_cwrite(CAP_ENDPTPRIME, BIT(n), BIT(n)); + + while (hw_cread(CAP_ENDPTPRIME, BIT(n))) + cpu_relax(); + if (is_ctrl && dir == RX && hw_cread(CAP_ENDPTSETUPSTAT, BIT(num))) + return -EAGAIN; + + /* status shoult be tested according with manual but it doesn't work */ + return 0; +} + +/** + * hw_ep_set_halt: configures ep halt & resets data toggle after clear (execute + * without interruption) + * @num: endpoint number + * @dir: endpoint direction + * @value: true => stall, false => unstall + * + * This function returns an error code + */ +static int hw_ep_set_halt(int num, int dir, int value) +{ + if (value != 0 && value != 1) + return -EINVAL; + + do { + u32 addr = CAP_ENDPTCTRL + num * sizeof(u32); + u32 mask_xs = dir ? ENDPTCTRL_TXS : ENDPTCTRL_RXS; + u32 mask_xr = dir ? ENDPTCTRL_TXR : ENDPTCTRL_RXR; + + /* data toggle - reserved for EP0 but it's in ESS */ + hw_cwrite(addr, mask_xs|mask_xr, value ? mask_xs : mask_xr); + + } while (value != hw_ep_get_halt(num, dir)); + + return 0; +} + +/** + * hw_intr_clear: disables interrupt & clears interrupt status (execute without + * interruption) + * @n: interrupt bit + * + * This function returns an error code + */ +static int hw_intr_clear(int n) +{ + if (n >= REG_BITS) + return -EINVAL; + + hw_cwrite(CAP_USBINTR, BIT(n), 0); + hw_cwrite(CAP_USBSTS, BIT(n), BIT(n)); + return 0; +} + +/** + * hw_intr_force: enables interrupt & forces interrupt status (execute without + * interruption) + * @n: interrupt bit + * + * This function returns an error code + */ +static int hw_intr_force(int n) +{ + if (n >= REG_BITS) + return -EINVAL; + + hw_awrite(ABS_TESTMODE, TESTMODE_FORCE, TESTMODE_FORCE); + hw_cwrite(CAP_USBINTR, BIT(n), BIT(n)); + hw_cwrite(CAP_USBSTS, BIT(n), BIT(n)); + hw_awrite(ABS_TESTMODE, TESTMODE_FORCE, 0); + return 0; +} + +/** + * hw_is_port_high_speed: test if port is high speed + * + * This function returns true if high speed port + */ +static int hw_port_is_high_speed(void) +{ + return hw_bank.lpm ? hw_cread(CAP_DEVLC, DEVLC_PSPD) : + hw_cread(CAP_PORTSC, PORTSC_HSP); +} + +/** + * hw_port_test_get: reads port test mode value + * + * This function returns port test mode value + */ +static u8 hw_port_test_get(void) +{ + return hw_cread(CAP_PORTSC, PORTSC_PTC) >> ffs_nr(PORTSC_PTC); +} + +/** + * hw_port_test_set: writes port test mode (execute without interruption) + * @mode: new value + * + * This function returns an error code + */ +static int hw_port_test_set(u8 mode) +{ + const u8 TEST_MODE_MAX = 7; + + if (mode > TEST_MODE_MAX) + return -EINVAL; + + hw_cwrite(CAP_PORTSC, PORTSC_PTC, mode << ffs_nr(PORTSC_PTC)); + return 0; +} + +/** + * hw_read_intr_enable: returns interrupt enable register + * + * This function returns register data + */ +static u32 hw_read_intr_enable(void) +{ + return hw_cread(CAP_USBINTR, ~0); +} + +/** + * hw_read_intr_status: returns interrupt status register + * + * This function returns register data + */ +static u32 hw_read_intr_status(void) +{ + return hw_cread(CAP_USBSTS, ~0); +} + +/** + * hw_register_read: reads all device registers (execute without interruption) + * @buf: destination buffer + * @size: buffer size + * + * This function returns number of registers read + */ +static size_t hw_register_read(u32 *buf, size_t size) +{ + unsigned i; + + if (size > hw_bank.size) + size = hw_bank.size; + + for (i = 0; i < size; i++) + buf[i] = hw_aread(i * sizeof(u32), ~0); + + return size; +} + +/** + * hw_register_write: writes to register + * @addr: register address + * @data: register value + * + * This function returns an error code + */ +static int hw_register_write(u16 addr, u32 data) +{ + /* align */ + addr /= sizeof(u32); + + if (addr >= hw_bank.size) + return -EINVAL; + + /* align */ + addr *= sizeof(u32); + + hw_awrite(addr, ~0, data); + return 0; +} + +/** + * hw_test_and_clear_complete: test & clear complete status (execute without + * interruption) + * @n: bit number (endpoint) + * + * This function returns complete status + */ +static int hw_test_and_clear_complete(int n) +{ + return hw_ctest_and_clear(CAP_ENDPTCOMPLETE, BIT(n)); +} + +/** + * hw_test_and_clear_intr_active: test & clear active interrupts (execute + * without interruption) + * + * This function returns active interrutps + */ +static u32 hw_test_and_clear_intr_active(void) +{ + u32 reg = hw_read_intr_status() & hw_read_intr_enable(); + + hw_cwrite(CAP_USBSTS, ~0, reg); + return reg; +} + +/** + * hw_test_and_clear_setup_guard: test & clear setup guard (execute without + * interruption) + * + * This function returns guard value + */ +static int hw_test_and_clear_setup_guard(void) +{ + return hw_ctest_and_write(CAP_USBCMD, USBCMD_SUTW, 0); +} + +/** + * hw_test_and_set_setup_guard: test & set setup guard (execute without + * interruption) + * + * This function returns guard value + */ +static int hw_test_and_set_setup_guard(void) +{ + return hw_ctest_and_write(CAP_USBCMD, USBCMD_SUTW, USBCMD_SUTW); +} + +/** + * hw_usb_set_address: configures USB address (execute without interruption) + * @value: new USB address + * + * This function returns an error code + */ +static int hw_usb_set_address(u8 value) +{ + /* advance */ + hw_cwrite(CAP_DEVICEADDR, DEVICEADDR_USBADR | DEVICEADDR_USBADRA, + value << ffs_nr(DEVICEADDR_USBADR) | DEVICEADDR_USBADRA); + return 0; +} + +/** + * hw_usb_reset: restart device after a bus reset (execute without + * interruption) + * + * This function returns an error code + */ +static int hw_usb_reset(void) +{ + hw_usb_set_address(0); + + /* ESS flushes only at end?!? */ + hw_cwrite(CAP_ENDPTFLUSH, ~0, ~0); /* flush all EPs */ + + /* clear setup token semaphores */ + hw_cwrite(CAP_ENDPTSETUPSTAT, 0, 0); /* writes its content */ + + /* clear complete status */ + hw_cwrite(CAP_ENDPTCOMPLETE, 0, 0); /* writes its content */ + + /* wait until all bits cleared */ + while (hw_cread(CAP_ENDPTPRIME, ~0)) + udelay(10); /* not RTOS friendly */ + + /* reset all endpoints ? */ + + /* reset internal status and wait for further instructions + no need to verify the port reset status (ESS does it) */ + + return 0; +} + +/****************************************************************************** + * DBG block + *****************************************************************************/ +/** + * show_device: prints information about device capabilities and status + * + * Check "device.h" for details + */ +static ssize_t show_device(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + struct usb_gadget *gadget = &udc->gadget; + int n = 0; + + dbg_trace("[%s] %p\n", __func__, buf); + if (attr == NULL || buf == NULL) { + dev_err(dev, "[%s] EINVAL\n", __func__); + return 0; + } + + n += scnprintf(buf + n, PAGE_SIZE - n, "speed = %d\n", + gadget->speed); + n += scnprintf(buf + n, PAGE_SIZE - n, "is_dualspeed = %d\n", + gadget->is_dualspeed); + n += scnprintf(buf + n, PAGE_SIZE - n, "is_otg = %d\n", + gadget->is_otg); + n += scnprintf(buf + n, PAGE_SIZE - n, "is_a_peripheral = %d\n", + gadget->is_a_peripheral); + n += scnprintf(buf + n, PAGE_SIZE - n, "b_hnp_enable = %d\n", + gadget->b_hnp_enable); + n += scnprintf(buf + n, PAGE_SIZE - n, "a_hnp_support = %d\n", + gadget->a_hnp_support); + n += scnprintf(buf + n, PAGE_SIZE - n, "a_alt_hnp_support = %d\n", + gadget->a_alt_hnp_support); + n += scnprintf(buf + n, PAGE_SIZE - n, "name = %s\n", + (gadget->name ? gadget->name : "")); + + return n; +} +static DEVICE_ATTR(device, S_IRUSR, show_device, NULL); + +/** + * show_driver: prints information about attached gadget (if any) + * + * Check "device.h" for details + */ +static ssize_t show_driver(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + struct usb_gadget_driver *driver = udc->driver; + int n = 0; + + dbg_trace("[%s] %p\n", __func__, buf); + if (attr == NULL || buf == NULL) { + dev_err(dev, "[%s] EINVAL\n", __func__); + return 0; + } + + if (driver == NULL) + return scnprintf(buf, PAGE_SIZE, + "There is no gadget attached!\n"); + + n += scnprintf(buf + n, PAGE_SIZE - n, "function = %s\n", + (driver->function ? driver->function : "")); + n += scnprintf(buf + n, PAGE_SIZE - n, "max speed = %d\n", + driver->speed); + + return n; +} +static DEVICE_ATTR(driver, S_IRUSR, show_driver, NULL); + +/* Maximum event message length */ +#define DBG_DATA_MSG 64UL + +/* Maximum event messages */ +#define DBG_DATA_MAX 128UL + +/* Event buffer descriptor */ +static struct { + char (buf[DBG_DATA_MAX])[DBG_DATA_MSG]; /* buffer */ + unsigned idx; /* index */ + unsigned tty; /* print to console? */ + rwlock_t lck; /* lock */ +} dbg_data = { + .idx = 0, + .tty = 0, + .lck = __RW_LOCK_UNLOCKED(lck) +}; + +/** + * dbg_dec: decrements debug event index + * @idx: buffer index + */ +static void dbg_dec(unsigned *idx) +{ + *idx = (*idx - 1) & (DBG_DATA_MAX-1); +} + +/** + * dbg_inc: increments debug event index + * @idx: buffer index + */ +static void dbg_inc(unsigned *idx) +{ + *idx = (*idx + 1) & (DBG_DATA_MAX-1); +} + +/** + * dbg_print: prints the common part of the event + * @addr: endpoint address + * @name: event name + * @status: status + * @extra: extra information + */ +static void dbg_print(u8 addr, const char *name, int status, const char *extra) +{ + struct timeval tval; + unsigned int stamp; + unsigned long flags; + + write_lock_irqsave(&dbg_data.lck, flags); + + do_gettimeofday(&tval); + stamp = tval.tv_sec & 0xFFFF; /* 2^32 = 4294967296. Limit to 4096s */ + stamp = stamp * 1000000 + tval.tv_usec; + + scnprintf(dbg_data.buf[dbg_data.idx], DBG_DATA_MSG, + "%04X\t» %02X %-7.7s %4i «\t%s\n", + stamp, addr, name, status, extra); + + dbg_inc(&dbg_data.idx); + + write_unlock_irqrestore(&dbg_data.lck, flags); + + if (dbg_data.tty != 0) + pr_notice("%04X\t» %02X %-7.7s %4i «\t%s\n", + stamp, addr, name, status, extra); +} + +/** + * dbg_done: prints a DONE event + * @addr: endpoint address + * @td: transfer descriptor + * @status: status + */ +static void dbg_done(u8 addr, const u32 token, int status) +{ + char msg[DBG_DATA_MSG]; + + scnprintf(msg, sizeof(msg), "%d %02X", + (int)(token & TD_TOTAL_BYTES) >> ffs_nr(TD_TOTAL_BYTES), + (int)(token & TD_STATUS) >> ffs_nr(TD_STATUS)); + dbg_print(addr, "DONE", status, msg); +} + +/** + * dbg_event: prints a generic event + * @addr: endpoint address + * @name: event name + * @status: status + */ +static void dbg_event(u8 addr, const char *name, int status) +{ + if (name != NULL) + dbg_print(addr, name, status, ""); +} + +/* + * dbg_queue: prints a QUEUE event + * @addr: endpoint address + * @req: USB request + * @status: status + */ +static void dbg_queue(u8 addr, const struct usb_request *req, int status) +{ + char msg[DBG_DATA_MSG]; + + if (req != NULL) { + scnprintf(msg, sizeof(msg), + "%d %d", !req->no_interrupt, req->length); + dbg_print(addr, "QUEUE", status, msg); + } +} + +/** + * dbg_setup: prints a SETUP event + * @addr: endpoint address + * @req: setup request + */ +static void dbg_setup(u8 addr, const struct usb_ctrlrequest *req) +{ + char msg[DBG_DATA_MSG]; + + if (req != NULL) { + scnprintf(msg, sizeof(msg), + "%02X %02X %04X %04X %d", req->bRequestType, + req->bRequest, le16_to_cpu(req->wValue), + le16_to_cpu(req->wIndex), le16_to_cpu(req->wLength)); + dbg_print(addr, "SETUP", 0, msg); + } +} + +/** + * show_events: displays the event buffer + * + * Check "device.h" for details + */ +static ssize_t show_events(struct device *dev, struct device_attribute *attr, + char *buf) +{ + unsigned long flags; + unsigned i, j, n = 0; + + dbg_trace("[%s] %p\n", __func__, buf); + if (attr == NULL || buf == NULL) { + dev_err(dev, "[%s] EINVAL\n", __func__); + return 0; + } + + read_lock_irqsave(&dbg_data.lck, flags); + + i = dbg_data.idx; + for (dbg_dec(&i); i != dbg_data.idx; dbg_dec(&i)) { + n += strlen(dbg_data.buf[i]); + if (n >= PAGE_SIZE) { + n -= strlen(dbg_data.buf[i]); + break; + } + } + for (j = 0, dbg_inc(&i); j < n; dbg_inc(&i)) + j += scnprintf(buf + j, PAGE_SIZE - j, + "%s", dbg_data.buf[i]); + + read_unlock_irqrestore(&dbg_data.lck, flags); + + return n; +} + +/** + * store_events: configure if events are going to be also printed to console + * + * Check "device.h" for details + */ +static ssize_t store_events(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned tty; + + dbg_trace("[%s] %p, %d\n", __func__, buf, count); + if (attr == NULL || buf == NULL) { + dev_err(dev, "[%s] EINVAL\n", __func__); + goto done; + } + + if (sscanf(buf, "%u", &tty) != 1 || tty > 1) { + dev_err(dev, "<1|0>: enable|disable console log\n"); + goto done; + } + + dbg_data.tty = tty; + dev_info(dev, "tty = %u", dbg_data.tty); + + done: + return count; +} +static DEVICE_ATTR(events, S_IRUSR | S_IWUSR, show_events, store_events); + +/** + * show_inters: interrupt status, enable status and historic + * + * Check "device.h" for details + */ +static ssize_t show_inters(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + unsigned long flags; + u32 intr; + unsigned i, j, n = 0; + + dbg_trace("[%s] %p\n", __func__, buf); + if (attr == NULL || buf == NULL) { + dev_err(dev, "[%s] EINVAL\n", __func__); + return 0; + } + + spin_lock_irqsave(udc->lock, flags); + + n += scnprintf(buf + n, PAGE_SIZE - n, + "status = %08x\n", hw_read_intr_status()); + n += scnprintf(buf + n, PAGE_SIZE - n, + "enable = %08x\n", hw_read_intr_enable()); + + n += scnprintf(buf + n, PAGE_SIZE - n, "*test = %d\n", + isr_statistics.test); + n += scnprintf(buf + n, PAGE_SIZE - n, "» ui = %d\n", + isr_statistics.ui); + n += scnprintf(buf + n, PAGE_SIZE - n, "» uei = %d\n", + isr_statistics.uei); + n += scnprintf(buf + n, PAGE_SIZE - n, "» pci = %d\n", + isr_statistics.pci); + n += scnprintf(buf + n, PAGE_SIZE - n, "» uri = %d\n", + isr_statistics.uri); + n += scnprintf(buf + n, PAGE_SIZE - n, "» sli = %d\n", + isr_statistics.sli); + n += scnprintf(buf + n, PAGE_SIZE - n, "*none = %d\n", + isr_statistics.none); + n += scnprintf(buf + n, PAGE_SIZE - n, "*hndl = %d\n", + isr_statistics.hndl.cnt); + + for (i = isr_statistics.hndl.idx, j = 0; j <= ISR_MASK; j++, i++) { + i &= ISR_MASK; + intr = isr_statistics.hndl.buf[i]; + + if (USBi_UI & intr) + n += scnprintf(buf + n, PAGE_SIZE - n, "ui "); + intr &= ~USBi_UI; + if (USBi_UEI & intr) + n += scnprintf(buf + n, PAGE_SIZE - n, "uei "); + intr &= ~USBi_UEI; + if (USBi_PCI & intr) + n += scnprintf(buf + n, PAGE_SIZE - n, "pci "); + intr &= ~USBi_PCI; + if (USBi_URI & intr) + n += scnprintf(buf + n, PAGE_SIZE - n, "uri "); + intr &= ~USBi_URI; + if (USBi_SLI & intr) + n += scnprintf(buf + n, PAGE_SIZE - n, "sli "); + intr &= ~USBi_SLI; + if (intr) + n += scnprintf(buf + n, PAGE_SIZE - n, "??? "); + if (isr_statistics.hndl.buf[i]) + n += scnprintf(buf + n, PAGE_SIZE - n, "\n"); + } + + spin_unlock_irqrestore(udc->lock, flags); + + return n; +} + +/** + * store_inters: enable & force or disable an individual interrutps + * (to be used for test purposes only) + * + * Check "device.h" for details + */ +static ssize_t store_inters(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + unsigned long flags; + unsigned en, bit; + + dbg_trace("[%s] %p, %d\n", __func__, buf, count); + if (attr == NULL || buf == NULL) { + dev_err(dev, "[%s] EINVAL\n", __func__); + goto done; + } + + if (sscanf(buf, "%u %u", &en, &bit) != 2 || en > 1) { + dev_err(dev, "<1|0> <bit>: enable|disable interrupt"); + goto done; + } + + spin_lock_irqsave(udc->lock, flags); + if (en) { + if (hw_intr_force(bit)) + dev_err(dev, "invalid bit number\n"); + else + isr_statistics.test++; + } else { + if (hw_intr_clear(bit)) + dev_err(dev, "invalid bit number\n"); + } + spin_unlock_irqrestore(udc->lock, flags); + + done: + return count; +} +static DEVICE_ATTR(inters, S_IRUSR | S_IWUSR, show_inters, store_inters); + +/** + * show_port_test: reads port test mode + * + * Check "device.h" for details + */ +static ssize_t show_port_test(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + unsigned long flags; + unsigned mode; + + dbg_trace("[%s] %p\n", __func__, buf); + if (attr == NULL || buf == NULL) { + dev_err(dev, "[%s] EINVAL\n", __func__); + return 0; + } + + spin_lock_irqsave(udc->lock, flags); + mode = hw_port_test_get(); + spin_unlock_irqrestore(udc->lock, flags); + + return scnprintf(buf, PAGE_SIZE, "mode = %u\n", mode); +} + +/** + * store_port_test: writes port test mode + * + * Check "device.h" for details + */ +static ssize_t store_port_test(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + unsigned long flags; + unsigned mode; + + dbg_trace("[%s] %p, %d\n", __func__, buf, count); + if (attr == NULL || buf == NULL) { + dev_err(dev, "[%s] EINVAL\n", __func__); + goto done; + } + + if (sscanf(buf, "%u", &mode) != 1) { + dev_err(dev, "<mode>: set port test mode"); + goto done; + } + + spin_lock_irqsave(udc->lock, flags); + if (hw_port_test_set(mode)) + dev_err(dev, "invalid mode\n"); + spin_unlock_irqrestore(udc->lock, flags); + + done: + return count; +} +static DEVICE_ATTR(port_test, S_IRUSR | S_IWUSR, + show_port_test, store_port_test); + +/** + * show_qheads: DMA contents of all queue heads + * + * Check "device.h" for details + */ +static ssize_t show_qheads(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + unsigned long flags; + unsigned i, j, n = 0; + + dbg_trace("[%s] %p\n", __func__, buf); + if (attr == NULL || buf == NULL) { + dev_err(dev, "[%s] EINVAL\n", __func__); + return 0; + } + + spin_lock_irqsave(udc->lock, flags); + for (i = 0; i < hw_ep_max; i++) { + struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[i]; + n += scnprintf(buf + n, PAGE_SIZE - n, + "EP=%02i: RX=%08X TX=%08X\n", + i, (u32)mEp->qh[RX].dma, (u32)mEp->qh[TX].dma); + for (j = 0; j < (sizeof(struct ci13xxx_qh)/sizeof(u32)); j++) { + n += scnprintf(buf + n, PAGE_SIZE - n, + " %04X: %08X %08X\n", j, + *((u32 *)mEp->qh[RX].ptr + j), + *((u32 *)mEp->qh[TX].ptr + j)); + } + } + spin_unlock_irqrestore(udc->lock, flags); + + return n; +} +static DEVICE_ATTR(qheads, S_IRUSR, show_qheads, NULL); + +/** + * show_registers: dumps all registers + * + * Check "device.h" for details + */ +static ssize_t show_registers(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + unsigned long flags; + u32 dump[512]; + unsigned i, k, n = 0; + + dbg_trace("[%s] %p\n", __func__, buf); + if (attr == NULL || buf == NULL) { + dev_err(dev, "[%s] EINVAL\n", __func__); + return 0; + } + + spin_lock_irqsave(udc->lock, flags); + k = hw_register_read(dump, sizeof(dump)/sizeof(u32)); + spin_unlock_irqrestore(udc->lock, flags); + + for (i = 0; i < k; i++) { + n += scnprintf(buf + n, PAGE_SIZE - n, + "reg[0x%04X] = 0x%08X\n", + i * (unsigned)sizeof(u32), dump[i]); + } + + return n; +} + +/** + * store_registers: writes value to register address + * + * Check "device.h" for details + */ +static ssize_t store_registers(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + unsigned long addr, data, flags; + + dbg_trace("[%s] %p, %d\n", __func__, buf, count); + if (attr == NULL || buf == NULL) { + dev_err(dev, "[%s] EINVAL\n", __func__); + goto done; + } + + if (sscanf(buf, "%li %li", &addr, &data) != 2) { + dev_err(dev, "<addr> <data>: write data to register address"); + goto done; + } + + spin_lock_irqsave(udc->lock, flags); + if (hw_register_write(addr, data)) + dev_err(dev, "invalid address range\n"); + spin_unlock_irqrestore(udc->lock, flags); + + done: + return count; +} +static DEVICE_ATTR(registers, S_IRUSR | S_IWUSR, + show_registers, store_registers); + +/** + * show_requests: DMA contents of all requests currently queued (all endpts) + * + * Check "device.h" for details + */ +static ssize_t show_requests(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + unsigned long flags; + struct list_head *ptr = NULL; + struct ci13xxx_req *req = NULL; + unsigned i, j, k, n = 0, qSize = sizeof(struct ci13xxx_td)/sizeof(u32); + + dbg_trace("[%s] %p\n", __func__, buf); + if (attr == NULL || buf == NULL) { + dev_err(dev, "[%s] EINVAL\n", __func__); + return 0; + } + + spin_lock_irqsave(udc->lock, flags); + for (i = 0; i < hw_ep_max; i++) + for (k = RX; k <= TX; k++) + list_for_each(ptr, &udc->ci13xxx_ep[i].qh[k].queue) + { + req = list_entry(ptr, + struct ci13xxx_req, queue); + + n += scnprintf(buf + n, PAGE_SIZE - n, + "EP=%02i: TD=%08X %s\n", + i, (u32)req->dma, + ((k == RX) ? "RX" : "TX")); + + for (j = 0; j < qSize; j++) + n += scnprintf(buf + n, PAGE_SIZE - n, + " %04X: %08X\n", j, + *((u32 *)req->ptr + j)); + } + spin_unlock_irqrestore(udc->lock, flags); + + return n; +} +static DEVICE_ATTR(requests, S_IRUSR, show_requests, NULL); + +/** + * dbg_create_files: initializes the attribute interface + * @dev: device + * + * This function returns an error code + */ +__maybe_unused static int dbg_create_files(struct device *dev) +{ + int retval = 0; + + if (dev == NULL) + return -EINVAL; + retval = device_create_file(dev, &dev_attr_device); + if (retval) + goto done; + retval = device_create_file(dev, &dev_attr_driver); + if (retval) + goto rm_device; + retval = device_create_file(dev, &dev_attr_events); + if (retval) + goto rm_driver; + retval = device_create_file(dev, &dev_attr_inters); + if (retval) + goto rm_events; + retval = device_create_file(dev, &dev_attr_port_test); + if (retval) + goto rm_inters; + retval = device_create_file(dev, &dev_attr_qheads); + if (retval) + goto rm_port_test; + retval = device_create_file(dev, &dev_attr_registers); + if (retval) + goto rm_qheads; + retval = device_create_file(dev, &dev_attr_requests); + if (retval) + goto rm_registers; + return 0; + + rm_registers: + device_remove_file(dev, &dev_attr_registers); + rm_qheads: + device_remove_file(dev, &dev_attr_qheads); + rm_port_test: + device_remove_file(dev, &dev_attr_port_test); + rm_inters: + device_remove_file(dev, &dev_attr_inters); + rm_events: + device_remove_file(dev, &dev_attr_events); + rm_driver: + device_remove_file(dev, &dev_attr_driver); + rm_device: + device_remove_file(dev, &dev_attr_device); + done: + return retval; +} + +/** + * dbg_remove_files: destroys the attribute interface + * @dev: device + * + * This function returns an error code + */ +__maybe_unused static int dbg_remove_files(struct device *dev) +{ + if (dev == NULL) + return -EINVAL; + device_remove_file(dev, &dev_attr_requests); + device_remove_file(dev, &dev_attr_registers); + device_remove_file(dev, &dev_attr_qheads); + device_remove_file(dev, &dev_attr_port_test); + device_remove_file(dev, &dev_attr_inters); + device_remove_file(dev, &dev_attr_events); + device_remove_file(dev, &dev_attr_driver); + device_remove_file(dev, &dev_attr_device); + return 0; +} + +/****************************************************************************** + * UTIL block + *****************************************************************************/ +/** + * _usb_addr: calculates endpoint address from direction & number + * @ep: endpoint + */ +static inline u8 _usb_addr(struct ci13xxx_ep *ep) +{ + return ((ep->dir == TX) ? USB_ENDPOINT_DIR_MASK : 0) | ep->num; +} + +/** + * _hardware_queue: configures a request at hardware level + * @gadget: gadget + * @mEp: endpoint + * + * This function returns an error code + */ +static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) +{ + unsigned i; + + trace("%p, %p", mEp, mReq); + + /* don't queue twice */ + if (mReq->req.status == -EALREADY) + return -EALREADY; + + if (hw_ep_is_primed(mEp->num, mEp->dir)) + return -EBUSY; + + mReq->req.status = -EALREADY; + + if (mReq->req.length && !mReq->req.dma) { + mReq->req.dma = \ + dma_map_single(mEp->device, mReq->req.buf, + mReq->req.length, mEp->dir ? + DMA_TO_DEVICE : DMA_FROM_DEVICE); + if (mReq->req.dma == 0) + return -ENOMEM; + + mReq->map = 1; + } + + /* + * TD configuration + * TODO - handle requests which spawns into several TDs + */ + memset(mReq->ptr, 0, sizeof(*mReq->ptr)); + mReq->ptr->next |= TD_TERMINATE; + mReq->ptr->token = mReq->req.length << ffs_nr(TD_TOTAL_BYTES); + mReq->ptr->token &= TD_TOTAL_BYTES; + mReq->ptr->token |= TD_IOC; + mReq->ptr->token |= TD_STATUS_ACTIVE; + mReq->ptr->page[0] = mReq->req.dma; + for (i = 1; i < 5; i++) + mReq->ptr->page[i] = + (mReq->req.dma + i * PAGE_SIZE) & ~TD_RESERVED_MASK; + + /* + * QH configuration + * At this point it's guaranteed exclusive access to qhead + * (endpt is not primed) so it's no need to use tripwire + */ + mEp->qh[mEp->dir].ptr->td.next = mReq->dma; /* TERMINATE = 0 */ + mEp->qh[mEp->dir].ptr->td.token &= ~TD_STATUS; /* clear status */ + if (mReq->req.zero == 0) + mEp->qh[mEp->dir].ptr->cap |= QH_ZLT; + else + mEp->qh[mEp->dir].ptr->cap &= ~QH_ZLT; + + wmb(); /* synchronize before ep prime */ + + return hw_ep_prime(mEp->num, mEp->dir, + mEp->type == USB_ENDPOINT_XFER_CONTROL); +} + +/** + * _hardware_dequeue: handles a request at hardware level + * @gadget: gadget + * @mEp: endpoint + * + * This function returns an error code + */ +static int _hardware_dequeue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) +{ + trace("%p, %p", mEp, mReq); + + if (mReq->req.status != -EALREADY) + return -EINVAL; + + if (hw_ep_is_primed(mEp->num, mEp->dir)) + hw_ep_flush(mEp->num, mEp->dir); + + mReq->req.status = 0; + + if (mReq->map) { + dma_unmap_single(mEp->device, mReq->req.dma, mReq->req.length, + mEp->dir ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + mReq->req.dma = 0; + mReq->map = 0; + } + + mReq->req.status = mReq->ptr->token & TD_STATUS; + if ((TD_STATUS_ACTIVE & mReq->req.status) != 0) + mReq->req.status = -ECONNRESET; + else if ((TD_STATUS_HALTED & mReq->req.status) != 0) + mReq->req.status = -1; + else if ((TD_STATUS_DT_ERR & mReq->req.status) != 0) + mReq->req.status = -1; + else if ((TD_STATUS_TR_ERR & mReq->req.status) != 0) + mReq->req.status = -1; + + mReq->req.actual = mReq->ptr->token & TD_TOTAL_BYTES; + mReq->req.actual >>= ffs_nr(TD_TOTAL_BYTES); + mReq->req.actual = mReq->req.length - mReq->req.actual; + mReq->req.actual = mReq->req.status ? 0 : mReq->req.actual; + + return mReq->req.actual; +} + +/** + * _ep_nuke: dequeues all endpoint requests + * @mEp: endpoint + * + * This function returns an error code + * Caller must hold lock + */ +static int _ep_nuke(struct ci13xxx_ep *mEp) +__releases(mEp->lock) +__acquires(mEp->lock) +{ + trace("%p", mEp); + + if (mEp == NULL) + return -EINVAL; + + hw_ep_flush(mEp->num, mEp->dir); + + while (!list_empty(&mEp->qh[mEp->dir].queue)) { + + /* pop oldest request */ + struct ci13xxx_req *mReq = \ + list_entry(mEp->qh[mEp->dir].queue.next, + struct ci13xxx_req, queue); + list_del_init(&mReq->queue); + mReq->req.status = -ESHUTDOWN; + + if (!mReq->req.no_interrupt && mReq->req.complete != NULL) { + spin_unlock(mEp->lock); + mReq->req.complete(&mEp->ep, &mReq->req); + spin_lock(mEp->lock); + } + } + return 0; +} + +/** + * _gadget_stop_activity: stops all USB activity, flushes & disables all endpts + * @gadget: gadget + * + * This function returns an error code + * Caller must hold lock + */ +static int _gadget_stop_activity(struct usb_gadget *gadget) +__releases(udc->lock) +__acquires(udc->lock) +{ + struct usb_ep *ep; + struct ci13xxx *udc = container_of(gadget, struct ci13xxx, gadget); + struct ci13xxx_ep *mEp = container_of(gadget->ep0, + struct ci13xxx_ep, ep); + + trace("%p", gadget); + + if (gadget == NULL) + return -EINVAL; + + spin_unlock(udc->lock); + + /* flush all endpoints */ + gadget_for_each_ep(ep, gadget) { + usb_ep_fifo_flush(ep); + } + usb_ep_fifo_flush(gadget->ep0); + + udc->driver->disconnect(gadget); + + /* make sure to disable all endpoints */ + gadget_for_each_ep(ep, gadget) { + usb_ep_disable(ep); + } + usb_ep_disable(gadget->ep0); + + if (mEp->status != NULL) { + usb_ep_free_request(gadget->ep0, mEp->status); + mEp->status = NULL; + } + + spin_lock(udc->lock); + + return 0; +} + +/****************************************************************************** + * ISR block + *****************************************************************************/ +/** + * isr_reset_handler: USB reset interrupt handler + * @udc: UDC device + * + * This function resets USB engine after a bus reset occurred + */ +static void isr_reset_handler(struct ci13xxx *udc) +__releases(udc->lock) +__acquires(udc->lock) +{ + struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[0]; + int retval; + + trace("%p", udc); + + if (udc == NULL) { + err("EINVAL"); + return; + } + + dbg_event(0xFF, "BUS RST", 0); + + retval = _gadget_stop_activity(&udc->gadget); + if (retval) + goto done; + + retval = hw_usb_reset(); + if (retval) + goto done; + + spin_unlock(udc->lock); + retval = usb_ep_enable(&mEp->ep, &ctrl_endpt_desc); + if (!retval) { + mEp->status = usb_ep_alloc_request(&mEp->ep, GFP_KERNEL); + if (mEp->status == NULL) { + usb_ep_disable(&mEp->ep); + retval = -ENOMEM; + } + } + spin_lock(udc->lock); + + done: + if (retval) + err("error: %i", retval); +} + +/** + * isr_get_status_complete: get_status request complete function + * @ep: endpoint + * @req: request handled + * + * Caller must release lock + */ +static void isr_get_status_complete(struct usb_ep *ep, struct usb_request *req) +{ + trace("%p, %p", ep, req); + + if (ep == NULL || req == NULL) { + err("EINVAL"); + return; + } + + kfree(req->buf); + usb_ep_free_request(ep, req); +} + +/** + * isr_get_status_response: get_status request response + * @ep: endpoint + * @setup: setup request packet + * + * This function returns an error code + */ +static int isr_get_status_response(struct ci13xxx_ep *mEp, + struct usb_ctrlrequest *setup) +__releases(mEp->lock) +__acquires(mEp->lock) +{ + struct usb_request *req = NULL; + gfp_t gfp_flags = GFP_ATOMIC; + int dir, num, retval; + + trace("%p, %p", mEp, setup); + + if (mEp == NULL || setup == NULL) + return -EINVAL; + + spin_unlock(mEp->lock); + req = usb_ep_alloc_request(&mEp->ep, gfp_flags); + spin_lock(mEp->lock); + if (req == NULL) + return -ENOMEM; + + req->complete = isr_get_status_complete; + req->length = 2; + req->buf = kzalloc(req->length, gfp_flags); + if (req->buf == NULL) { + retval = -ENOMEM; + goto err_free_req; + } + + if ((setup->bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE) { + /* TODO: D1 - Remote Wakeup; D0 - Self Powered */ + retval = 0; + } else if ((setup->bRequestType & USB_RECIP_MASK) \ + == USB_RECIP_ENDPOINT) { + dir = (le16_to_cpu(setup->wIndex) & USB_ENDPOINT_DIR_MASK) ? + TX : RX; + num = le16_to_cpu(setup->wIndex) & USB_ENDPOINT_NUMBER_MASK; + *((u16 *)req->buf) = hw_ep_get_halt(num, dir); + } + /* else do nothing; reserved for future use */ + + spin_unlock(mEp->lock); + retval = usb_ep_queue(&mEp->ep, req, gfp_flags); + spin_lock(mEp->lock); + if (retval) + goto err_free_buf; + + return 0; + + err_free_buf: + kfree(req->buf); + err_free_req: + spin_unlock(mEp->lock); + usb_ep_free_request(&mEp->ep, req); + spin_lock(mEp->lock); + return retval; +} + +/** + * isr_setup_status_phase: queues the status phase of a setup transation + * @mEp: endpoint + * + * This function returns an error code + */ +static int isr_setup_status_phase(struct ci13xxx_ep *mEp) +__releases(mEp->lock) +__acquires(mEp->lock) +{ + int retval; + + trace("%p", mEp); + + /* mEp is always valid & configured */ + + if (mEp->type == USB_ENDPOINT_XFER_CONTROL) + mEp->dir = (mEp->dir == TX) ? RX : TX; + + mEp->status->no_interrupt = 1; + + spin_unlock(mEp->lock); + retval = usb_ep_queue(&mEp->ep, mEp->status, GFP_ATOMIC); + spin_lock(mEp->lock); + + return retval; +} + +/** + * isr_tr_complete_low: transaction complete low level handler + * @mEp: endpoint + * + * This function returns an error code + * Caller must hold lock + */ +static int isr_tr_complete_low(struct ci13xxx_ep *mEp) +__releases(mEp->lock) +__acquires(mEp->lock) +{ + struct ci13xxx_req *mReq; + int retval; + + trace("%p", mEp); + + if (list_empty(&mEp->qh[mEp->dir].queue)) + return -EINVAL; + + /* pop oldest request */ + mReq = list_entry(mEp->qh[mEp->dir].queue.next, + struct ci13xxx_req, queue); + list_del_init(&mReq->queue); + + retval = _hardware_dequeue(mEp, mReq); + if (retval < 0) { + dbg_event(_usb_addr(mEp), "DONE", retval); + goto done; + } + + dbg_done(_usb_addr(mEp), mReq->ptr->token, retval); + + if (!mReq->req.no_interrupt && mReq->req.complete != NULL) { + spin_unlock(mEp->lock); + mReq->req.complete(&mEp->ep, &mReq->req); + spin_lock(mEp->lock); + } + + if (!list_empty(&mEp->qh[mEp->dir].queue)) { + mReq = list_entry(mEp->qh[mEp->dir].queue.next, + struct ci13xxx_req, queue); + _hardware_enqueue(mEp, mReq); + } + + done: + return retval; +} + +/** + * isr_tr_complete_handler: transaction complete interrupt handler + * @udc: UDC descriptor + * + * This function handles traffic events + */ +static void isr_tr_complete_handler(struct ci13xxx *udc) +__releases(udc->lock) +__acquires(udc->lock) +{ + unsigned i; + + trace("%p", udc); + + if (udc == NULL) { + err("EINVAL"); + return; + } + + for (i = 0; i < hw_ep_max; i++) { + struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[i]; + int type, num, err = -EINVAL; + struct usb_ctrlrequest req; + + + if (mEp->desc == NULL) + continue; /* not configured */ + + if ((mEp->dir == RX && hw_test_and_clear_complete(i)) || + (mEp->dir == TX && hw_test_and_clear_complete(i + 16))) { + err = isr_tr_complete_low(mEp); + if (mEp->type == USB_ENDPOINT_XFER_CONTROL) { + if (err > 0) /* needs status phase */ + err = isr_setup_status_phase(mEp); + if (err < 0) { + dbg_event(_usb_addr(mEp), + "ERROR", err); + spin_unlock(udc->lock); + if (usb_ep_set_halt(&mEp->ep)) + err("error: ep_set_halt"); + spin_lock(udc->lock); + } + } + } + + if (mEp->type != USB_ENDPOINT_XFER_CONTROL || + !hw_test_and_clear_setup_status(i)) + continue; + + if (i != 0) { + warn("ctrl traffic received at endpoint"); + continue; + } + + /* read_setup_packet */ + do { + hw_test_and_set_setup_guard(); + memcpy(&req, &mEp->qh[RX].ptr->setup, sizeof(req)); + } while (!hw_test_and_clear_setup_guard()); + + type = req.bRequestType; + + mEp->dir = (type & USB_DIR_IN) ? TX : RX; + + dbg_setup(_usb_addr(mEp), &req); + + switch (req.bRequest) { + case USB_REQ_CLEAR_FEATURE: + if (type != (USB_DIR_OUT|USB_RECIP_ENDPOINT) && + le16_to_cpu(req.wValue) != USB_ENDPOINT_HALT) + goto delegate; + if (req.wLength != 0) + break; + num = le16_to_cpu(req.wIndex); + num &= USB_ENDPOINT_NUMBER_MASK; + if (!udc->ci13xxx_ep[num].wedge) { + spin_unlock(udc->lock); + err = usb_ep_clear_halt( + &udc->ci13xxx_ep[num].ep); + spin_lock(udc->lock); + if (err) + break; + } + err = isr_setup_status_phase(mEp); + break; + case USB_REQ_GET_STATUS: + if (type != (USB_DIR_IN|USB_RECIP_DEVICE) && + type != (USB_DIR_IN|USB_RECIP_ENDPOINT) && + type != (USB_DIR_IN|USB_RECIP_INTERFACE)) + goto delegate; + if (le16_to_cpu(req.wLength) != 2 || + le16_to_cpu(req.wValue) != 0) + break; + err = isr_get_status_response(mEp, &req); + break; + case USB_REQ_SET_ADDRESS: + if (type != (USB_DIR_OUT|USB_RECIP_DEVICE)) + goto delegate; + if (le16_to_cpu(req.wLength) != 0 || + le16_to_cpu(req.wIndex) != 0) + break; + err = hw_usb_set_address((u8)le16_to_cpu(req.wValue)); + if (err) + break; + err = isr_setup_status_phase(mEp); + break; + case USB_REQ_SET_FEATURE: + if (type != (USB_DIR_OUT|USB_RECIP_ENDPOINT) && + le16_to_cpu(req.wValue) != USB_ENDPOINT_HALT) + goto delegate; + if (req.wLength != 0) + break; + num = le16_to_cpu(req.wIndex); + num &= USB_ENDPOINT_NUMBER_MASK; + + spin_unlock(udc->lock); + err = usb_ep_set_halt(&udc->ci13xxx_ep[num].ep); + spin_lock(udc->lock); + if (err) + break; + err = isr_setup_status_phase(mEp); + break; + default: +delegate: + if (req.wLength == 0) /* no data phase */ + mEp->dir = TX; + + spin_unlock(udc->lock); + err = udc->driver->setup(&udc->gadget, &req); + spin_lock(udc->lock); + break; + } + + if (err < 0) { + dbg_event(_usb_addr(mEp), "ERROR", err); + + spin_unlock(udc->lock); + if (usb_ep_set_halt(&mEp->ep)) + err("error: ep_set_halt"); + spin_lock(udc->lock); + } + } +} + +/****************************************************************************** + * ENDPT block + *****************************************************************************/ +/** + * ep_enable: configure endpoint, making it usable + * + * Check usb_ep_enable() at "usb_gadget.h" for details + */ +static int ep_enable(struct usb_ep *ep, + const struct usb_endpoint_descriptor *desc) +{ + struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep); + int direction, retval = 0; + unsigned long flags; + + trace("%p, %p", ep, desc); + + if (ep == NULL || desc == NULL) + return -EINVAL; + + spin_lock_irqsave(mEp->lock, flags); + + /* only internal SW should enable ctrl endpts */ + + mEp->desc = desc; + + if (!list_empty(&mEp->qh[mEp->dir].queue)) + warn("enabling a non-empty endpoint!"); + + mEp->dir = (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ? TX : RX; + mEp->num = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + mEp->type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + + mEp->ep.maxpacket = __constant_le16_to_cpu(desc->wMaxPacketSize); + + direction = mEp->dir; + do { + dbg_event(_usb_addr(mEp), "ENABLE", 0); + + if (mEp->type == USB_ENDPOINT_XFER_CONTROL) + mEp->qh[mEp->dir].ptr->cap |= QH_IOS; + else if (mEp->type == USB_ENDPOINT_XFER_ISOC) + mEp->qh[mEp->dir].ptr->cap &= ~QH_MULT; + else + mEp->qh[mEp->dir].ptr->cap &= ~QH_ZLT; + + mEp->qh[mEp->dir].ptr->cap |= + (mEp->ep.maxpacket << ffs_nr(QH_MAX_PKT)) & QH_MAX_PKT; + mEp->qh[mEp->dir].ptr->td.next |= TD_TERMINATE; /* needed? */ + + retval |= hw_ep_enable(mEp->num, mEp->dir, mEp->type); + + if (mEp->type == USB_ENDPOINT_XFER_CONTROL) + mEp->dir = (mEp->dir == TX) ? RX : TX; + + } while (mEp->dir != direction); + + spin_unlock_irqrestore(mEp->lock, flags); + return retval; +} + +/** + * ep_disable: endpoint is no longer usable + * + * Check usb_ep_disable() at "usb_gadget.h" for details + */ +static int ep_disable(struct usb_ep *ep) +{ + struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep); + int direction, retval = 0; + unsigned long flags; + + trace("%p", ep); + + if (ep == NULL) + return -EINVAL; + else if (mEp->desc == NULL) + return -EBUSY; + + spin_lock_irqsave(mEp->lock, flags); + + /* only internal SW should disable ctrl endpts */ + + direction = mEp->dir; + do { + dbg_event(_usb_addr(mEp), "DISABLE", 0); + + retval |= _ep_nuke(mEp); + retval |= hw_ep_disable(mEp->num, mEp->dir); + + if (mEp->type == USB_ENDPOINT_XFER_CONTROL) + mEp->dir = (mEp->dir == TX) ? RX : TX; + + } while (mEp->dir != direction); + + mEp->desc = NULL; + + spin_unlock_irqrestore(mEp->lock, flags); + return retval; +} + +/** + * ep_alloc_request: allocate a request object to use with this endpoint + * + * Check usb_ep_alloc_request() at "usb_gadget.h" for details + */ +static struct usb_request *ep_alloc_request(struct usb_ep *ep, gfp_t gfp_flags) +{ + struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep); + struct ci13xxx_req *mReq = NULL; + unsigned long flags; + + trace("%p, %i", ep, gfp_flags); + + if (ep == NULL) { + err("EINVAL"); + return NULL; + } + + spin_lock_irqsave(mEp->lock, flags); + + mReq = kzalloc(sizeof(struct ci13xxx_req), gfp_flags); + if (mReq != NULL) { + INIT_LIST_HEAD(&mReq->queue); + + mReq->ptr = dma_pool_alloc(mEp->td_pool, gfp_flags, + &mReq->dma); + if (mReq->ptr == NULL) { + kfree(mReq); + mReq = NULL; + } + } + + dbg_event(_usb_addr(mEp), "ALLOC", mReq == NULL); + + spin_unlock_irqrestore(mEp->lock, flags); + + return (mReq == NULL) ? NULL : &mReq->req; +} + +/** + * ep_free_request: frees a request object + * + * Check usb_ep_free_request() at "usb_gadget.h" for details + */ +static void ep_free_request(struct usb_ep *ep, struct usb_request *req) +{ + struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep); + struct ci13xxx_req *mReq = container_of(req, struct ci13xxx_req, req); + unsigned long flags; + + trace("%p, %p", ep, req); + + if (ep == NULL || req == NULL) { + err("EINVAL"); + return; + } else if (!list_empty(&mReq->queue)) { + err("EBUSY"); + return; + } + + spin_lock_irqsave(mEp->lock, flags); + + if (mReq->ptr) + dma_pool_free(mEp->td_pool, mReq->ptr, mReq->dma); + kfree(mReq); + + dbg_event(_usb_addr(mEp), "FREE", 0); + + spin_unlock_irqrestore(mEp->lock, flags); +} + +/** + * ep_queue: queues (submits) an I/O request to an endpoint + * + * Check usb_ep_queue()* at usb_gadget.h" for details + */ +static int ep_queue(struct usb_ep *ep, struct usb_request *req, + gfp_t __maybe_unused gfp_flags) +{ + struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep); + struct ci13xxx_req *mReq = container_of(req, struct ci13xxx_req, req); + int retval = 0; + unsigned long flags; + + trace("%p, %p, %X", ep, req, gfp_flags); + + if (ep == NULL || req == NULL || mEp->desc == NULL) + return -EINVAL; + + spin_lock_irqsave(mEp->lock, flags); + + if (mEp->type == USB_ENDPOINT_XFER_CONTROL && + !list_empty(&mEp->qh[mEp->dir].queue)) { + _ep_nuke(mEp); + retval = -EOVERFLOW; + warn("endpoint ctrl %X nuked", _usb_addr(mEp)); + } + + /* first nuke then test link, e.g. previous status has not sent */ + if (!list_empty(&mReq->queue)) { + retval = -EBUSY; + err("request already in queue"); + goto done; + } + + if (req->length > (4 * PAGE_SIZE)) { + req->length = (4 * PAGE_SIZE); + retval = -EMSGSIZE; + warn("request length truncated"); + } + + dbg_queue(_usb_addr(mEp), req, retval); + + /* push request */ + mReq->req.status = -EINPROGRESS; + mReq->req.actual = 0; + list_add_tail(&mReq->queue, &mEp->qh[mEp->dir].queue); + + retval = _hardware_enqueue(mEp, mReq); + if (retval == -EALREADY || retval == -EBUSY) { + dbg_event(_usb_addr(mEp), "QUEUE", retval); + retval = 0; + } + + done: + spin_unlock_irqrestore(mEp->lock, flags); + return retval; +} + +/** + * ep_dequeue: dequeues (cancels, unlinks) an I/O request from an endpoint + * + * Check usb_ep_dequeue() at "usb_gadget.h" for details + */ +static int ep_dequeue(struct usb_ep *ep, struct usb_request *req) +{ + struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep); + struct ci13xxx_req *mReq = container_of(req, struct ci13xxx_req, req); + unsigned long flags; + + trace("%p, %p", ep, req); + + if (ep == NULL || req == NULL || mEp->desc == NULL || + list_empty(&mReq->queue) || list_empty(&mEp->qh[mEp->dir].queue)) + return -EINVAL; + + spin_lock_irqsave(mEp->lock, flags); + + dbg_event(_usb_addr(mEp), "DEQUEUE", 0); + + if (mReq->req.status == -EALREADY) + _hardware_dequeue(mEp, mReq); + + /* pop request */ + list_del_init(&mReq->queue); + req->status = -ECONNRESET; + + if (!mReq->req.no_interrupt && mReq->req.complete != NULL) { + spin_unlock(mEp->lock); + mReq->req.complete(&mEp->ep, &mReq->req); + spin_lock(mEp->lock); + } + + spin_unlock_irqrestore(mEp->lock, flags); + return 0; +} + +/** + * ep_set_halt: sets the endpoint halt feature + * + * Check usb_ep_set_halt() at "usb_gadget.h" for details + */ +static int ep_set_halt(struct usb_ep *ep, int value) +{ + struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep); + int direction, retval = 0; + unsigned long flags; + + trace("%p, %i", ep, value); + + if (ep == NULL || mEp->desc == NULL) + return -EINVAL; + + spin_lock_irqsave(mEp->lock, flags); + +#ifndef STALL_IN + /* g_file_storage MS compliant but g_zero fails chapter 9 compliance */ + if (value && mEp->type == USB_ENDPOINT_XFER_BULK && mEp->dir == TX && + !list_empty(&mEp->qh[mEp->dir].queue)) { + spin_unlock_irqrestore(mEp->lock, flags); + return -EAGAIN; + } +#endif + + direction = mEp->dir; + do { + dbg_event(_usb_addr(mEp), "HALT", value); + retval |= hw_ep_set_halt(mEp->num, mEp->dir, value); + + if (!value) + mEp->wedge = 0; + + if (mEp->type == USB_ENDPOINT_XFER_CONTROL) + mEp->dir = (mEp->dir == TX) ? RX : TX; + + } while (mEp->dir != direction); + + spin_unlock_irqrestore(mEp->lock, flags); + return retval; +} + +/** + * ep_set_wedge: sets the halt feature and ignores clear requests + * + * Check usb_ep_set_wedge() at "usb_gadget.h" for details + */ +static int ep_set_wedge(struct usb_ep *ep) +{ + struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep); + unsigned long flags; + + trace("%p", ep); + + if (ep == NULL || mEp->desc == NULL) + return -EINVAL; + + spin_lock_irqsave(mEp->lock, flags); + + dbg_event(_usb_addr(mEp), "WEDGE", 0); + mEp->wedge = 1; + + spin_unlock_irqrestore(mEp->lock, flags); + + return usb_ep_set_halt(ep); +} + +/** + * ep_fifo_flush: flushes contents of a fifo + * + * Check usb_ep_fifo_flush() at "usb_gadget.h" for details + */ +static void ep_fifo_flush(struct usb_ep *ep) +{ + struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep); + unsigned long flags; + + trace("%p", ep); + + if (ep == NULL) { + err("%02X: -EINVAL", _usb_addr(mEp)); + return; + } + + spin_lock_irqsave(mEp->lock, flags); + + dbg_event(_usb_addr(mEp), "FFLUSH", 0); + hw_ep_flush(mEp->num, mEp->dir); + + spin_unlock_irqrestore(mEp->lock, flags); +} + +/** + * Endpoint-specific part of the API to the USB controller hardware + * Check "usb_gadget.h" for details + */ +static const struct usb_ep_ops usb_ep_ops = { + .enable = ep_enable, + .disable = ep_disable, + .alloc_request = ep_alloc_request, + .free_request = ep_free_request, + .queue = ep_queue, + .dequeue = ep_dequeue, + .set_halt = ep_set_halt, + .set_wedge = ep_set_wedge, + .fifo_flush = ep_fifo_flush, +}; + +/****************************************************************************** + * GADGET block + *****************************************************************************/ +/** + * Device operations part of the API to the USB controller hardware, + * which don't involve endpoints (or i/o) + * Check "usb_gadget.h" for details + */ +static const struct usb_gadget_ops usb_gadget_ops; + +/** + * usb_gadget_register_driver: register a gadget driver + * + * Check usb_gadget_register_driver() at "usb_gadget.h" for details + * Interrupts are enabled here + */ +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + struct ci13xxx *udc = _udc; + unsigned long i, k, flags; + int retval = -ENOMEM; + + trace("%p", driver); + + if (driver == NULL || + driver->bind == NULL || + driver->unbind == NULL || + driver->setup == NULL || + driver->disconnect == NULL || + driver->suspend == NULL || + driver->resume == NULL) + return -EINVAL; + else if (udc == NULL) + return -ENODEV; + else if (udc->driver != NULL) + return -EBUSY; + + /* alloc resources */ + udc->qh_pool = dma_pool_create("ci13xxx_qh", &udc->gadget.dev, + sizeof(struct ci13xxx_qh), + 64, PAGE_SIZE); + if (udc->qh_pool == NULL) + return -ENOMEM; + + udc->td_pool = dma_pool_create("ci13xxx_td", &udc->gadget.dev, + sizeof(struct ci13xxx_td), + 64, PAGE_SIZE); + if (udc->td_pool == NULL) { + dma_pool_destroy(udc->qh_pool); + udc->qh_pool = NULL; + return -ENOMEM; + } + + spin_lock_irqsave(udc->lock, flags); + + info("hw_ep_max = %d", hw_ep_max); + + udc->driver = driver; + udc->gadget.ops = NULL; + udc->gadget.dev.driver = NULL; + + retval = 0; + for (i = 0; i < hw_ep_max; i++) { + struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[i]; + + scnprintf(mEp->name, sizeof(mEp->name), "ep%i", (int)i); + + mEp->lock = udc->lock; + mEp->device = &udc->gadget.dev; + mEp->td_pool = udc->td_pool; + + mEp->ep.name = mEp->name; + mEp->ep.ops = &usb_ep_ops; + mEp->ep.maxpacket = CTRL_PAYLOAD_MAX; + + /* this allocation cannot be random */ + for (k = RX; k <= TX; k++) { + INIT_LIST_HEAD(&mEp->qh[k].queue); + mEp->qh[k].ptr = dma_pool_alloc(udc->qh_pool, + GFP_KERNEL, + &mEp->qh[k].dma); + if (mEp->qh[k].ptr == NULL) + retval = -ENOMEM; + else + memset(mEp->qh[k].ptr, 0, + sizeof(*mEp->qh[k].ptr)); + } + if (i == 0) + udc->gadget.ep0 = &mEp->ep; + else + list_add_tail(&mEp->ep.ep_list, &udc->gadget.ep_list); + } + if (retval) + goto done; + + /* bind gadget */ + driver->driver.bus = NULL; + udc->gadget.ops = &usb_gadget_ops; + udc->gadget.dev.driver = &driver->driver; + + spin_unlock_irqrestore(udc->lock, flags); + retval = driver->bind(&udc->gadget); /* MAY SLEEP */ + spin_lock_irqsave(udc->lock, flags); + + if (retval) { + udc->gadget.ops = NULL; + udc->gadget.dev.driver = NULL; + goto done; + } + + retval = hw_device_state(udc->ci13xxx_ep[0].qh[RX].dma); + + done: + spin_unlock_irqrestore(udc->lock, flags); + if (retval) + usb_gadget_unregister_driver(driver); + return retval; +} +EXPORT_SYMBOL(usb_gadget_register_driver); + +/** + * usb_gadget_unregister_driver: unregister a gadget driver + * + * Check usb_gadget_unregister_driver() at "usb_gadget.h" for details + */ +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct ci13xxx *udc = _udc; + unsigned long i, k, flags; + + trace("%p", driver); + + if (driver == NULL || + driver->bind == NULL || + driver->unbind == NULL || + driver->setup == NULL || + driver->disconnect == NULL || + driver->suspend == NULL || + driver->resume == NULL || + driver != udc->driver) + return -EINVAL; + + spin_lock_irqsave(udc->lock, flags); + + hw_device_state(0); + + /* unbind gadget */ + if (udc->gadget.ops != NULL) { + _gadget_stop_activity(&udc->gadget); + + spin_unlock_irqrestore(udc->lock, flags); + driver->unbind(&udc->gadget); /* MAY SLEEP */ + spin_lock_irqsave(udc->lock, flags); + + udc->gadget.ops = NULL; + udc->gadget.dev.driver = NULL; + } + + /* free resources */ + for (i = 0; i < hw_ep_max; i++) { + struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[i]; + + if (i == 0) + udc->gadget.ep0 = NULL; + else if (!list_empty(&mEp->ep.ep_list)) + list_del_init(&mEp->ep.ep_list); + + for (k = RX; k <= TX; k++) + if (mEp->qh[k].ptr != NULL) + dma_pool_free(udc->qh_pool, + mEp->qh[k].ptr, mEp->qh[k].dma); + } + + udc->driver = NULL; + + spin_unlock_irqrestore(udc->lock, flags); + + if (udc->td_pool != NULL) { + dma_pool_destroy(udc->td_pool); + udc->td_pool = NULL; + } + if (udc->qh_pool != NULL) { + dma_pool_destroy(udc->qh_pool); + udc->qh_pool = NULL; + } + + return 0; +} +EXPORT_SYMBOL(usb_gadget_unregister_driver); + +/****************************************************************************** + * BUS block + *****************************************************************************/ +/** + * udc_irq: global interrupt handler + * + * This function returns IRQ_HANDLED if the IRQ has been handled + * It locks access to registers + */ +static irqreturn_t udc_irq(void) +{ + struct ci13xxx *udc = _udc; + irqreturn_t retval; + u32 intr; + + trace(); + + if (udc == NULL) { + err("ENODEV"); + return IRQ_HANDLED; + } + + spin_lock(udc->lock); + intr = hw_test_and_clear_intr_active(); + if (intr) { + isr_statistics.hndl.buf[isr_statistics.hndl.idx++] = intr; + isr_statistics.hndl.idx &= ISR_MASK; + isr_statistics.hndl.cnt++; + + /* order defines priority - do NOT change it */ + if (USBi_URI & intr) { + isr_statistics.uri++; + isr_reset_handler(udc); + } + if (USBi_PCI & intr) { + isr_statistics.pci++; + udc->gadget.speed = hw_port_is_high_speed() ? + USB_SPEED_HIGH : USB_SPEED_FULL; + } + if (USBi_UEI & intr) + isr_statistics.uei++; + if (USBi_UI & intr) { + isr_statistics.ui++; + isr_tr_complete_handler(udc); + } + if (USBi_SLI & intr) + isr_statistics.sli++; + retval = IRQ_HANDLED; + } else { + isr_statistics.none++; + retval = IRQ_NONE; + } + spin_unlock(udc->lock); + + return retval; +} + +/** + * udc_release: driver release function + * @dev: device + * + * Currently does nothing + */ +static void udc_release(struct device *dev) +{ + trace("%p", dev); + + if (dev == NULL) + err("EINVAL"); +} + +/** + * udc_probe: parent probe must call this to initialize UDC + * @dev: parent device + * @regs: registers base address + * @name: driver name + * + * This function returns an error code + * No interrupts active, the IRQ has not been requested yet + * Kernel assumes 32-bit DMA operations by default, no need to dma_set_mask + */ +static int udc_probe(struct device *dev, void __iomem *regs, const char *name) +{ + struct ci13xxx *udc; + int retval = 0; + + trace("%p, %p, %p", dev, regs, name); + + if (dev == NULL || regs == NULL || name == NULL) + return -EINVAL; + + udc = kzalloc(sizeof(struct ci13xxx), GFP_KERNEL); + if (udc == NULL) + return -ENOMEM; + + udc->lock = &udc_lock; + + retval = hw_device_reset(regs); + if (retval) + goto done; + + udc->gadget.ops = NULL; + udc->gadget.speed = USB_SPEED_UNKNOWN; + udc->gadget.is_dualspeed = 1; + udc->gadget.is_otg = 0; + udc->gadget.name = name; + + INIT_LIST_HEAD(&udc->gadget.ep_list); + udc->gadget.ep0 = NULL; + + strcpy(udc->gadget.dev.bus_id, "gadget"); + udc->gadget.dev.dma_mask = dev->dma_mask; + udc->gadget.dev.parent = dev; + udc->gadget.dev.release = udc_release; + + retval = device_register(&udc->gadget.dev); + if (retval) + goto done; + +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + retval = dbg_create_files(&udc->gadget.dev); +#endif + if (retval) { + device_unregister(&udc->gadget.dev); + goto done; + } + + _udc = udc; + return retval; + + done: + err("error = %i", retval); + kfree(udc); + _udc = NULL; + return retval; +} + +/** + * udc_remove: parent remove must call this to remove UDC + * + * No interrupts active, the IRQ has been released + */ +static void udc_remove(void) +{ + struct ci13xxx *udc = _udc; + + if (udc == NULL) { + err("EINVAL"); + return; + } + +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + dbg_remove_files(&udc->gadget.dev); +#endif + device_unregister(&udc->gadget.dev); + + kfree(udc); + _udc = NULL; +} + +/****************************************************************************** + * PCI block + *****************************************************************************/ +/** + * ci13xxx_pci_irq: interrut handler + * @irq: irq number + * @pdev: USB Device Controller interrupt source + * + * This function returns IRQ_HANDLED if the IRQ has been handled + * This is an ISR don't trace, use attribute interface instead + */ +static irqreturn_t ci13xxx_pci_irq(int irq, void *pdev) +{ + if (irq == 0) { + dev_err(&((struct pci_dev *)pdev)->dev, "Invalid IRQ0 usage!"); + return IRQ_HANDLED; + } + return udc_irq(); +} + +/** + * ci13xxx_pci_probe: PCI probe + * @pdev: USB device controller being probed + * @id: PCI hotplug ID connecting controller to UDC framework + * + * This function returns an error code + * Allocates basic PCI resources for this USB device controller, and then + * invokes the udc_probe() method to start the UDC associated with it + */ +static int __devinit ci13xxx_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + void __iomem *regs = NULL; + int retval = 0; + + if (id == NULL) + return -EINVAL; + + retval = pci_enable_device(pdev); + if (retval) + goto done; + + if (!pdev->irq) { + dev_err(&pdev->dev, "No IRQ, check BIOS/PCI setup!"); + retval = -ENODEV; + goto disable_device; + } + + retval = pci_request_regions(pdev, UDC_DRIVER_NAME); + if (retval) + goto disable_device; + + /* BAR 0 holds all the registers */ + regs = pci_iomap(pdev, 0, 0); + if (!regs) { + dev_err(&pdev->dev, "Error mapping memory!"); + retval = -EFAULT; + goto release_regions; + } + pci_set_drvdata(pdev, (__force void *)regs); + + pci_set_master(pdev); + pci_try_set_mwi(pdev); + + retval = udc_probe(&pdev->dev, regs, UDC_DRIVER_NAME); + if (retval) + goto iounmap; + + /* our device does not have MSI capability */ + + retval = request_irq(pdev->irq, ci13xxx_pci_irq, IRQF_SHARED, + UDC_DRIVER_NAME, pdev); + if (retval) + goto gadget_remove; + + return 0; + + gadget_remove: + udc_remove(); + iounmap: + pci_iounmap(pdev, regs); + release_regions: + pci_release_regions(pdev); + disable_device: + pci_disable_device(pdev); + done: + return retval; +} + +/** + * ci13xxx_pci_remove: PCI remove + * @pdev: USB Device Controller being removed + * + * Reverses the effect of ci13xxx_pci_probe(), + * first invoking the udc_remove() and then releases + * all PCI resources allocated for this USB device controller + */ +static void __devexit ci13xxx_pci_remove(struct pci_dev *pdev) +{ + free_irq(pdev->irq, pdev); + udc_remove(); + pci_iounmap(pdev, (__force void __iomem *)pci_get_drvdata(pdev)); + pci_release_regions(pdev); + pci_disable_device(pdev); +} + +/** + * PCI device table + * PCI device structure + * + * Check "pci.h" for details + */ +static DEFINE_PCI_DEVICE_TABLE(ci13xxx_pci_id_table) = { + { PCI_DEVICE(0x153F, 0x1004) }, + { PCI_DEVICE(0x153F, 0x1006) }, + { 0, 0, 0, 0, 0, 0, 0 /* end: all zeroes */ } +}; +MODULE_DEVICE_TABLE(pci, ci13xxx_pci_id_table); + +static struct pci_driver ci13xxx_pci_driver = { + .name = UDC_DRIVER_NAME, + .id_table = ci13xxx_pci_id_table, + .probe = ci13xxx_pci_probe, + .remove = __devexit_p(ci13xxx_pci_remove), +}; + +/** + * ci13xxx_pci_init: module init + * + * Driver load + */ +static int __init ci13xxx_pci_init(void) +{ + return pci_register_driver(&ci13xxx_pci_driver); +} +module_init(ci13xxx_pci_init); + +/** + * ci13xxx_pci_exit: module exit + * + * Driver unload + */ +static void __exit ci13xxx_pci_exit(void) +{ + pci_unregister_driver(&ci13xxx_pci_driver); +} +module_exit(ci13xxx_pci_exit); + +MODULE_AUTHOR("MIPS - David Lopo <dlopo@chipidea.mips.com>"); +MODULE_DESCRIPTION("MIPS CI13XXX USB Peripheral Controller"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("June 2008"); diff --git a/drivers/usb/gadget/ci13xxx_udc.h b/drivers/usb/gadget/ci13xxx_udc.h new file mode 100644 index 000000000000..4026e9cede34 --- /dev/null +++ b/drivers/usb/gadget/ci13xxx_udc.h @@ -0,0 +1,195 @@ +/* + * ci13xxx_udc.h - structures, registers, and macros MIPS USB IP core + * + * Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved. + * + * Author: David Lopo + * + * 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. + * + * Description: MIPS USB IP core family device controller + * Structures, registers and logging macros + */ + +#ifndef _CI13XXX_h_ +#define _CI13XXX_h_ + +/****************************************************************************** + * DEFINE + *****************************************************************************/ +#define ENDPT_MAX (16) +#define CTRL_PAYLOAD_MAX (64) +#define RX (0) /* similar to USB_DIR_OUT but can be used as an index */ +#define TX (1) /* similar to USB_DIR_IN but can be used as an index */ + +/****************************************************************************** + * STRUCTURES + *****************************************************************************/ +/* DMA layout of transfer descriptors */ +struct ci13xxx_td { + /* 0 */ + u32 next; +#define TD_TERMINATE BIT(0) + /* 1 */ + u32 token; +#define TD_STATUS (0x00FFUL << 0) +#define TD_STATUS_TR_ERR BIT(3) +#define TD_STATUS_DT_ERR BIT(5) +#define TD_STATUS_HALTED BIT(6) +#define TD_STATUS_ACTIVE BIT(7) +#define TD_MULTO (0x0003UL << 10) +#define TD_IOC BIT(15) +#define TD_TOTAL_BYTES (0x7FFFUL << 16) + /* 2 */ + u32 page[5]; +#define TD_CURR_OFFSET (0x0FFFUL << 0) +#define TD_FRAME_NUM (0x07FFUL << 0) +#define TD_RESERVED_MASK (0x0FFFUL << 0) +} __attribute__ ((packed)); + +/* DMA layout of queue heads */ +struct ci13xxx_qh { + /* 0 */ + u32 cap; +#define QH_IOS BIT(15) +#define QH_MAX_PKT (0x07FFUL << 16) +#define QH_ZLT BIT(29) +#define QH_MULT (0x0003UL << 30) + /* 1 */ + u32 curr; + /* 2 - 8 */ + struct ci13xxx_td td; + /* 9 */ + u32 RESERVED; + struct usb_ctrlrequest setup; +} __attribute__ ((packed)); + +/* Extension of usb_request */ +struct ci13xxx_req { + struct usb_request req; + unsigned map; + struct list_head queue; + struct ci13xxx_td *ptr; + dma_addr_t dma; +}; + +/* Extension of usb_ep */ +struct ci13xxx_ep { + struct usb_ep ep; + const struct usb_endpoint_descriptor *desc; + u8 dir; + u8 num; + u8 type; + char name[16]; + struct { + struct list_head queue; + struct ci13xxx_qh *ptr; + dma_addr_t dma; + } qh[2]; + struct usb_request *status; + int wedge; + + /* global resources */ + spinlock_t *lock; + struct device *device; + struct dma_pool *td_pool; +}; + +/* CI13XXX UDC descriptor & global resources */ +struct ci13xxx { + spinlock_t *lock; /* ctrl register bank access */ + + struct dma_pool *qh_pool; /* DMA pool for queue heads */ + struct dma_pool *td_pool; /* DMA pool for transfer descs */ + + struct usb_gadget gadget; /* USB slave device */ + struct ci13xxx_ep ci13xxx_ep[ENDPT_MAX]; /* extended endpts */ + + struct usb_gadget_driver *driver; /* 3rd party gadget driver */ +}; + +/****************************************************************************** + * REGISTERS + *****************************************************************************/ +/* register size */ +#define REG_BITS (32) + +/* HCCPARAMS */ +#define HCCPARAMS_LEN BIT(17) + +/* DCCPARAMS */ +#define DCCPARAMS_DEN (0x1F << 0) +#define DCCPARAMS_DC BIT(7) + +/* TESTMODE */ +#define TESTMODE_FORCE BIT(0) + +/* USBCMD */ +#define USBCMD_RS BIT(0) +#define USBCMD_RST BIT(1) +#define USBCMD_SUTW BIT(13) + +/* USBSTS & USBINTR */ +#define USBi_UI BIT(0) +#define USBi_UEI BIT(1) +#define USBi_PCI BIT(2) +#define USBi_URI BIT(6) +#define USBi_SLI BIT(8) + +/* DEVICEADDR */ +#define DEVICEADDR_USBADRA BIT(24) +#define DEVICEADDR_USBADR (0x7FUL << 25) + +/* PORTSC */ +#define PORTSC_SUSP BIT(7) +#define PORTSC_HSP BIT(9) +#define PORTSC_PTC (0x0FUL << 16) + +/* DEVLC */ +#define DEVLC_PSPD (0x03UL << 25) +#define DEVLC_PSPD_HS (0x02UL << 25) + +/* USBMODE */ +#define USBMODE_CM (0x03UL << 0) +#define USBMODE_CM_IDLE (0x00UL << 0) +#define USBMODE_CM_DEVICE (0x02UL << 0) +#define USBMODE_CM_HOST (0x03UL << 0) +#define USBMODE_SLOM BIT(3) + +/* ENDPTCTRL */ +#define ENDPTCTRL_RXS BIT(0) +#define ENDPTCTRL_RXT (0x03UL << 2) +#define ENDPTCTRL_RXR BIT(6) /* reserved for port 0 */ +#define ENDPTCTRL_RXE BIT(7) +#define ENDPTCTRL_TXS BIT(16) +#define ENDPTCTRL_TXT (0x03UL << 18) +#define ENDPTCTRL_TXR BIT(22) /* reserved for port 0 */ +#define ENDPTCTRL_TXE BIT(23) + +/****************************************************************************** + * LOGGING + *****************************************************************************/ +#define ci13xxx_printk(level, format, args...) \ +do { \ + if (_udc == NULL) \ + printk(level "[%s] " format "\n", __func__, ## args); \ + else \ + dev_printk(level, _udc->gadget.dev.parent, \ + "[%s] " format "\n", __func__, ## args); \ +} while (0) + +#define err(format, args...) ci13xxx_printk(KERN_ERR, format, ## args) +#define warn(format, args...) ci13xxx_printk(KERN_WARNING, format, ## args) +#define info(format, args...) ci13xxx_printk(KERN_INFO, format, ## args) + +#ifdef TRACE +#define trace(format, args...) ci13xxx_printk(KERN_DEBUG, format, ## args) +#define dbg_trace(format, args...) dev_dbg(dev, format, ##args) +#else +#define trace(format, args...) do {} while (0) +#define dbg_trace(format, args...) do {} while (0) +#endif + +#endif /* _CI13XXX_h_ */ diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c index 9462e30192d8..a36b1175b18d 100644 --- a/drivers/usb/gadget/epautoconf.c +++ b/drivers/usb/gadget/epautoconf.c @@ -161,7 +161,7 @@ ep_matches ( /* report address */ desc->bEndpointAddress &= USB_DIR_IN; if (isdigit (ep->name [2])) { - u8 num = simple_strtol (&ep->name [2], NULL, 10); + u8 num = simple_strtoul (&ep->name [2], NULL, 10); desc->bEndpointAddress |= num; #ifdef MANY_ENDPOINTS } else if (desc->bEndpointAddress & USB_DIR_IN) { diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c index 5ee1590b8e9c..c1d34df0b157 100644 --- a/drivers/usb/gadget/f_acm.c +++ b/drivers/usb/gadget/f_acm.c @@ -463,7 +463,11 @@ static int acm_cdc_notify(struct f_acm *acm, u8 type, u16 value, notify->wLength = cpu_to_le16(length); memcpy(buf, data, length); + /* ep_queue() can complete immediately if it fills the fifo... */ + spin_unlock(&acm->lock); status = usb_ep_queue(ep, req, GFP_ATOMIC); + spin_lock(&acm->lock); + if (status < 0) { ERROR(acm->port.func.config->cdev, "acm ttyGS%d can't notify serial state, %d\n", diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c index 659b3d9671c4..428b5993575a 100644 --- a/drivers/usb/gadget/f_rndis.c +++ b/drivers/usb/gadget/f_rndis.c @@ -172,7 +172,6 @@ static struct usb_interface_descriptor rndis_data_intf __initdata = { .bDescriptorType = USB_DT_INTERFACE, /* .bInterfaceNumber = DYNAMIC */ - .bAlternateSetting = 1, .bNumEndpoints = 2, .bInterfaceClass = USB_CLASS_CDC_DATA, .bInterfaceSubClass = 0, @@ -303,7 +302,7 @@ static void rndis_response_available(void *_rndis) __le32 *data = req->buf; int status; - if (atomic_inc_return(&rndis->notify_count)) + if (atomic_inc_return(&rndis->notify_count) != 1) return; /* Send RNDIS RESPONSE_AVAILABLE notification; a diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index c4e62a6297d7..1aee5b063330 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -1,7 +1,7 @@ /* * file_storage.c -- File-backed USB Storage Gadget, for USB development * - * Copyright (C) 2003-2007 Alan Stern + * Copyright (C) 2003-2008 Alan Stern * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,16 +38,17 @@ /* * The File-backed Storage Gadget acts as a USB Mass Storage device, - * appearing to the host as a disk drive. In addition to providing an - * example of a genuinely useful gadget driver for a USB device, it also - * illustrates a technique of double-buffering for increased throughput. - * Last but not least, it gives an easy way to probe the behavior of the - * Mass Storage drivers in a USB host. + * appearing to the host as a disk drive or as a CD-ROM drive. In addition + * to providing an example of a genuinely useful gadget driver for a USB + * device, it also illustrates a technique of double-buffering for increased + * throughput. Last but not least, it gives an easy way to probe the + * behavior of the Mass Storage drivers in a USB host. * * Backing storage is provided by a regular file or a block device, specified * by the "file" module parameter. Access can be limited to read-only by - * setting the optional "ro" module parameter. The gadget will indicate that - * it has removable media if the optional "removable" module parameter is set. + * setting the optional "ro" module parameter. (For CD-ROM emulation, + * access is always read-only.) The gadget will indicate that it has + * removable media if the optional "removable" module parameter is set. * * The gadget supports the Control-Bulk (CB), Control-Bulk-Interrupt (CBI), * and Bulk-Only (also known as Bulk-Bulk-Bulk or BBB) transports, selected @@ -64,7 +65,12 @@ * The default number of LUNs is taken from the number of "file" elements; * it is 1 if "file" is not given. If "removable" is not set then a backing * file must be specified for each LUN. If it is set, then an unspecified - * or empty backing filename means the LUN's medium is not loaded. + * or empty backing filename means the LUN's medium is not loaded. Ideally + * each LUN would be settable independently as a disk drive or a CD-ROM + * drive, but currently all LUNs have to be the same type. The CD-ROM + * emulation includes a single data track and no audio tracks; hence there + * need be only one backing file per LUN. Note also that the CD-ROM block + * length is set to 512 rather than the more common value 2048. * * Requirements are modest; only a bulk-in and a bulk-out endpoint are * needed (an interrupt-out endpoint is also needed for CBI). The memory @@ -91,6 +97,8 @@ * USB device controller (usually true), * boolean to permit the driver to halt * bulk endpoints + * cdrom Default false, boolean for whether to emulate + * a CD-ROM drive * transport=XXX Default BBB, transport name (CB, CBI, or BBB) * protocol=YYY Default SCSI, protocol name (RBC, 8020 or * ATAPI, QIC, UFI, 8070, or SCSI; @@ -103,15 +111,16 @@ * PAGE_CACHE_SIZE) * * If CONFIG_USB_FILE_STORAGE_TEST is not set, only the "file", "ro", - * "removable", "luns", and "stall" options are available; default values - * are used for everything else. + * "removable", "luns", "stall", and "cdrom" options are available; default + * values are used for everything else. * * The pathnames of the backing files and the ro settings are available in * the attribute files "file" and "ro" in the lun<n> subdirectory of the * gadget's sysfs directory. If the "removable" option is set, writing to * these files will simulate ejecting/loading the medium (writing an empty * line means eject) and adjusting a write-enable tab. Changes to the ro - * setting are not allowed when the medium is loaded. + * setting are not allowed when the medium is loaded or if CD-ROM emulation + * is being used. * * This gadget driver is heavily based on "Gadget Zero" by David Brownell. * The driver's SCSI command interface was based on the "Information @@ -261,7 +270,7 @@ #define DRIVER_DESC "File-backed Storage Gadget" #define DRIVER_NAME "g_file_storage" -#define DRIVER_VERSION "7 August 2007" +#define DRIVER_VERSION "20 November 2008" static const char longname[] = DRIVER_DESC; static const char shortname[] = DRIVER_NAME; @@ -341,6 +350,7 @@ static struct { int removable; int can_stall; + int cdrom; char *transport_parm; char *protocol_parm; @@ -359,6 +369,7 @@ static struct { .protocol_parm = "SCSI", .removable = 0, .can_stall = 1, + .cdrom = 0, .vendor = DRIVER_VENDOR_ID, .product = DRIVER_PRODUCT_ID, .release = 0xffff, // Use controller chip type @@ -382,6 +393,9 @@ MODULE_PARM_DESC(removable, "true to simulate removable media"); module_param_named(stall, mod_data.can_stall, bool, S_IRUGO); MODULE_PARM_DESC(stall, "false to prevent bulk stalls"); +module_param_named(cdrom, mod_data.cdrom, bool, S_IRUGO); +MODULE_PARM_DESC(cdrom, "true to emulate cdrom instead of disk"); + /* In the non-TEST version, only the module parameters listed above * are available. */ @@ -411,6 +425,10 @@ MODULE_PARM_DESC(buflen, "I/O buffer size"); /*-------------------------------------------------------------------------*/ +/* SCSI device types */ +#define TYPE_DISK 0x00 +#define TYPE_CDROM 0x05 + /* USB protocol value = the transport method */ #define USB_PR_CBI 0x00 // Control/Bulk/Interrupt #define USB_PR_CB 0x01 // Control/Bulk w/o interrupt @@ -487,6 +505,8 @@ struct interrupt_data { #define SC_READ_12 0xa8 #define SC_READ_CAPACITY 0x25 #define SC_READ_FORMAT_CAPACITIES 0x23 +#define SC_READ_HEADER 0x44 +#define SC_READ_TOC 0x43 #define SC_RELEASE 0x17 #define SC_REQUEST_SENSE 0x03 #define SC_RESERVE 0x16 @@ -2022,23 +2042,28 @@ static int do_inquiry(struct fsg_dev *fsg, struct fsg_buffhd *bh) u8 *buf = (u8 *) bh->buf; static char vendor_id[] = "Linux "; - static char product_id[] = "File-Stor Gadget"; + static char product_disk_id[] = "File-Stor Gadget"; + static char product_cdrom_id[] = "File-CD Gadget "; if (!fsg->curlun) { // Unsupported LUNs are okay fsg->bad_lun_okay = 1; memset(buf, 0, 36); buf[0] = 0x7f; // Unsupported, no device-type + buf[4] = 31; // Additional length return 36; } - memset(buf, 0, 8); // Non-removable, direct-access device + memset(buf, 0, 8); + buf[0] = (mod_data.cdrom ? TYPE_CDROM : TYPE_DISK); if (mod_data.removable) buf[1] = 0x80; buf[2] = 2; // ANSI SCSI level 2 buf[3] = 2; // SCSI-2 INQUIRY data format buf[4] = 31; // Additional length // No special options - sprintf(buf + 8, "%-8s%-16s%04x", vendor_id, product_id, + sprintf(buf + 8, "%-8s%-16s%04x", vendor_id, + (mod_data.cdrom ? product_cdrom_id : + product_disk_id), mod_data.release); return 36; } @@ -2117,6 +2142,75 @@ static int do_read_capacity(struct fsg_dev *fsg, struct fsg_buffhd *bh) } +static void store_cdrom_address(u8 *dest, int msf, u32 addr) +{ + if (msf) { + /* Convert to Minutes-Seconds-Frames */ + addr >>= 2; /* Convert to 2048-byte frames */ + addr += 2*75; /* Lead-in occupies 2 seconds */ + dest[3] = addr % 75; /* Frames */ + addr /= 75; + dest[2] = addr % 60; /* Seconds */ + addr /= 60; + dest[1] = addr; /* Minutes */ + dest[0] = 0; /* Reserved */ + } else { + /* Absolute sector */ + put_be32(dest, addr); + } +} + +static int do_read_header(struct fsg_dev *fsg, struct fsg_buffhd *bh) +{ + struct lun *curlun = fsg->curlun; + int msf = fsg->cmnd[1] & 0x02; + u32 lba = get_be32(&fsg->cmnd[2]); + u8 *buf = (u8 *) bh->buf; + + if ((fsg->cmnd[1] & ~0x02) != 0) { /* Mask away MSF */ + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + if (lba >= curlun->num_sectors) { + curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + return -EINVAL; + } + + memset(buf, 0, 8); + buf[0] = 0x01; /* 2048 bytes of user data, rest is EC */ + store_cdrom_address(&buf[4], msf, lba); + return 8; +} + + +static int do_read_toc(struct fsg_dev *fsg, struct fsg_buffhd *bh) +{ + struct lun *curlun = fsg->curlun; + int msf = fsg->cmnd[1] & 0x02; + int start_track = fsg->cmnd[6]; + u8 *buf = (u8 *) bh->buf; + + if ((fsg->cmnd[1] & ~0x02) != 0 || /* Mask away MSF */ + start_track > 1) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + memset(buf, 0, 20); + buf[1] = (20-2); /* TOC data length */ + buf[2] = 1; /* First track number */ + buf[3] = 1; /* Last track number */ + buf[5] = 0x16; /* Data track, copying allowed */ + buf[6] = 0x01; /* Only track is number 1 */ + store_cdrom_address(&buf[8], msf, 0); + + buf[13] = 0x16; /* Lead-out track is data */ + buf[14] = 0xAA; /* Lead-out track number */ + store_cdrom_address(&buf[16], msf, curlun->num_sectors); + return 20; +} + + static int do_mode_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) { struct lun *curlun = fsg->curlun; @@ -2864,6 +2958,26 @@ static int do_scsi_command(struct fsg_dev *fsg) reply = do_read_capacity(fsg, bh); break; + case SC_READ_HEADER: + if (!mod_data.cdrom) + goto unknown_cmnd; + fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]); + if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, + (3<<7) | (0x1f<<1), 1, + "READ HEADER")) == 0) + reply = do_read_header(fsg, bh); + break; + + case SC_READ_TOC: + if (!mod_data.cdrom) + goto unknown_cmnd; + fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]); + if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, + (7<<6) | (1<<1), 1, + "READ TOC")) == 0) + reply = do_read_toc(fsg, bh); + break; + case SC_READ_FORMAT_CAPACITIES: fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]); if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, @@ -2949,6 +3063,7 @@ static int do_scsi_command(struct fsg_dev *fsg) // Fall through default: + unknown_cmnd: fsg->data_size_from_cmnd = 0; sprintf(unknown, "Unknown x%02x", fsg->cmnd[0]); if ((reply = check_command(fsg, fsg->cmnd_size, @@ -3514,6 +3629,7 @@ static int open_backing_file(struct lun *curlun, const char *filename) struct inode *inode = NULL; loff_t size; loff_t num_sectors; + loff_t min_sectors; /* R/W if we can, R/O if we must */ ro = curlun->ro; @@ -3557,8 +3673,19 @@ static int open_backing_file(struct lun *curlun, const char *filename) rc = (int) size; goto out; } - num_sectors = size >> 9; // File size in 512-byte sectors - if (num_sectors == 0) { + num_sectors = size >> 9; // File size in 512-byte blocks + min_sectors = 1; + if (mod_data.cdrom) { + num_sectors &= ~3; // Reduce to a multiple of 2048 + min_sectors = 300*4; // Smallest track is 300 frames + if (num_sectors >= 256*60*75*4) { + num_sectors = (256*60*75 - 1) * 4; + LINFO(curlun, "file too big: %s\n", filename); + LINFO(curlun, "using only first %d blocks\n", + (int) num_sectors); + } + } + if (num_sectors < min_sectors) { LINFO(curlun, "file too small: %s\n", filename); rc = -ETOOSMALL; goto out; @@ -3861,9 +3988,12 @@ static int __init fsg_bind(struct usb_gadget *gadget) goto out; if (mod_data.removable) { // Enable the store_xxx attributes - dev_attr_ro.attr.mode = dev_attr_file.attr.mode = 0644; - dev_attr_ro.store = store_ro; + dev_attr_file.attr.mode = 0644; dev_attr_file.store = store_file; + if (!mod_data.cdrom) { + dev_attr_ro.attr.mode = 0644; + dev_attr_ro.store = store_ro; + } } /* Find out how many LUNs there should be */ @@ -3888,6 +4018,8 @@ static int __init fsg_bind(struct usb_gadget *gadget) for (i = 0; i < fsg->nluns; ++i) { curlun = &fsg->luns[i]; curlun->ro = mod_data.ro[i]; + if (mod_data.cdrom) + curlun->ro = 1; curlun->dev.release = lun_release; curlun->dev.parent = &gadget->dev; curlun->dev.driver = &fsg_driver.driver; @@ -4047,9 +4179,9 @@ static int __init fsg_bind(struct usb_gadget *gadget) mod_data.protocol_name, mod_data.protocol_type); DBG(fsg, "VendorID=x%04x, ProductID=x%04x, Release=x%04x\n", mod_data.vendor, mod_data.product, mod_data.release); - DBG(fsg, "removable=%d, stall=%d, buflen=%u\n", + DBG(fsg, "removable=%d, stall=%d, cdrom=%d, buflen=%u\n", mod_data.removable, mod_data.can_stall, - mod_data.buflen); + mod_data.cdrom, mod_data.buflen); DBG(fsg, "I/O thread pid: %d\n", task_pid_nr(fsg->thread_task)); set_bit(REGISTERED, &fsg->atomic_bitflags); diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c index 1fe8b44787b3..d6c5bcd40064 100644 --- a/drivers/usb/gadget/fsl_qe_udc.c +++ b/drivers/usb/gadget/fsl_qe_udc.c @@ -26,6 +26,7 @@ #include <linux/ioport.h> #include <linux/types.h> #include <linux/errno.h> +#include <linux/err.h> #include <linux/slab.h> #include <linux/list.h> #include <linux/interrupt.h> @@ -370,6 +371,9 @@ static int qe_ep_bd_init(struct qe_udc *udc, unsigned char pipe_num) /* alloc multi-ram for BD rings and set the ep parameters */ tmp_addr = cpm_muram_alloc(sizeof(struct qe_bd) * (bdring_len + USB_BDRING_LEN_TX), QE_ALIGNMENT_OF_BD); + if (IS_ERR_VALUE(tmp_addr)) + return -ENOMEM; + out_be16(&epparam->rbase, (u16)tmp_addr); out_be16(&epparam->tbase, (u16)(tmp_addr + (sizeof(struct qe_bd) * bdring_len))); @@ -689,7 +693,7 @@ en_done2: en_done1: spin_unlock_irqrestore(&udc->lock, flags); en_done: - dev_dbg(udc->dev, "failed to initialize %s\n", ep->ep.name); + dev_err(udc->dev, "failed to initialize %s\n", ep->ep.name); return -ENODEV; } @@ -2363,6 +2367,9 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) nuke(loop_ep, -ESHUTDOWN); spin_unlock_irqrestore(&udc_controller->lock, flags); + /* report disconnect; the controller is already quiesced */ + driver->disconnect(&udc_controller->gadget); + /* unbind gadget and unhook driver. */ driver->unbind(&udc_controller->gadget); udc_controller->gadget.dev.driver = NULL; @@ -2405,6 +2412,8 @@ static struct qe_udc __devinit *qe_udc_config(struct of_device *ofdev) tmp_addr = cpm_muram_alloc((USB_MAX_ENDPOINTS * sizeof(struct usb_ep_para)), USB_EP_PARA_ALIGNMENT); + if (IS_ERR_VALUE(tmp_addr)) + goto cleanup; for (i = 0; i < USB_MAX_ENDPOINTS; i++) { out_be16(&usbpram->epptr[i], (u16)tmp_addr); @@ -2510,7 +2519,7 @@ static int __devinit qe_udc_probe(struct of_device *ofdev, /* Initialize the udc structure including QH member and other member */ udc_controller = qe_udc_config(ofdev); if (!udc_controller) { - dev_dbg(&ofdev->dev, "udc_controll is NULL\n"); + dev_err(&ofdev->dev, "failed to initialize\n"); return -ENOMEM; } @@ -2542,7 +2551,7 @@ static int __devinit qe_udc_probe(struct of_device *ofdev, device_initialize(&udc_controller->gadget.dev); - strcpy(udc_controller->gadget.dev.bus_id, "gadget"); + dev_set_name(&udc_controller->gadget.dev, "gadget"); udc_controller->gadget.dev.release = qe_udc_release; udc_controller->gadget.dev.parent = &ofdev->dev; @@ -2565,7 +2574,7 @@ static int __devinit qe_udc_probe(struct of_device *ofdev, /* create a buf for ZLP send, need to remain zeroed */ udc_controller->nullbuf = kzalloc(256, GFP_KERNEL); if (udc_controller->nullbuf == NULL) { - dev_dbg(udc_controller->dev, "cannot alloc nullbuf\n"); + dev_err(udc_controller->dev, "cannot alloc nullbuf\n"); ret = -ENOMEM; goto err3; } diff --git a/drivers/usb/gadget/fsl_usb2_udc.c b/drivers/usb/gadget/fsl_usb2_udc.c index 091bb55c9aa7..f3c6703cffda 100644 --- a/drivers/usb/gadget/fsl_usb2_udc.c +++ b/drivers/usb/gadget/fsl_usb2_udc.c @@ -1836,6 +1836,9 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) nuke(loop_ep, -ESHUTDOWN); spin_unlock_irqrestore(&udc_controller->lock, flags); + /* report disconnect; the controller is already quiesced */ + driver->disconnect(&udc_controller->gadget); + /* unbind gadget and unhook driver. */ driver->unbind(&udc_controller->gadget); udc_controller->gadget.dev.driver = NULL; diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h index 4e3107dd2f34..ec6d439a2aa5 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/gadget_chips.h @@ -110,7 +110,6 @@ #define gadget_is_at91(g) 0 #endif -/* status unclear */ #ifdef CONFIG_USB_GADGET_IMX #define gadget_is_imx(g) !strcmp("imx_udc", (g)->name) #else @@ -158,6 +157,11 @@ #define gadget_is_fsl_qe(g) 0 #endif +#ifdef CONFIG_USB_GADGET_CI13XXX +#define gadget_is_ci13xxx(g) (!strcmp("ci13xxx_udc", (g)->name)) +#else +#define gadget_is_ci13xxx(g) 0 +#endif // CONFIG_USB_GADGET_SX2 // CONFIG_USB_GADGET_AU1X00 @@ -225,6 +229,8 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget) return 0x21; else if (gadget_is_fsl_qe(gadget)) return 0x22; + else if (gadget_is_ci13xxx(gadget)) + return 0x23; return -ENOENT; } diff --git a/drivers/usb/gadget/imx_udc.c b/drivers/usb/gadget/imx_udc.c new file mode 100644 index 000000000000..718cbe7bdf8f --- /dev/null +++ b/drivers/usb/gadget/imx_udc.c @@ -0,0 +1,1494 @@ +/* + * driver/usb/gadget/imx_udc.c + * + * Copyright (C) 2005 Mike Lee(eemike@gmail.com) + * Copyright (C) 2008 Darius Augulis <augulis.darius@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/list.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/clk.h> +#include <linux/delay.h> + +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> + +#include <mach/usb.h> +#include <mach/hardware.h> + +#include "imx_udc.h" + +static const char driver_name[] = "imx_udc"; +static const char ep0name[] = "ep0"; + +void ep0_chg_stat(const char *label, struct imx_udc_struct *imx_usb, + enum ep0_state stat); + +/******************************************************************************* + * IMX UDC hardware related functions + ******************************************************************************* + */ + +void imx_udc_enable(struct imx_udc_struct *imx_usb) +{ + int temp = __raw_readl(imx_usb->base + USB_CTRL); + __raw_writel(temp | CTRL_FE_ENA | CTRL_AFE_ENA, imx_usb->base + USB_CTRL); + imx_usb->gadget.speed = USB_SPEED_FULL; +} + +void imx_udc_disable(struct imx_udc_struct *imx_usb) +{ + int temp = __raw_readl(imx_usb->base + USB_CTRL); + + __raw_writel(temp & ~(CTRL_FE_ENA | CTRL_AFE_ENA), + imx_usb->base + USB_CTRL); + + ep0_chg_stat(__func__, imx_usb, EP0_IDLE); + imx_usb->gadget.speed = USB_SPEED_UNKNOWN; +} + +void imx_udc_reset(struct imx_udc_struct *imx_usb) +{ + int temp = __raw_readl(imx_usb->base + USB_ENAB); + + /* set RST bit */ + __raw_writel(temp | ENAB_RST, imx_usb->base + USB_ENAB); + + /* wait RST bit to clear */ + do {} while (__raw_readl(imx_usb->base + USB_ENAB) & ENAB_RST); + + /* wait CFG bit to assert */ + do {} while (!(__raw_readl(imx_usb->base + USB_DADR) & DADR_CFG)); + + /* udc module is now ready */ +} + +void imx_udc_config(struct imx_udc_struct *imx_usb) +{ + u8 ep_conf[5]; + u8 i, j, cfg; + struct imx_ep_struct *imx_ep; + + /* wait CFG bit to assert */ + do {} while (!(__raw_readl(imx_usb->base + USB_DADR) & DADR_CFG)); + + /* Download the endpoint buffer for endpoint 0. */ + for (j = 0; j < 5; j++) { + i = (j == 2 ? imx_usb->imx_ep[0].fifosize : 0x00); + __raw_writeb(i, imx_usb->base + USB_DDAT); + do {} while (__raw_readl(imx_usb->base + USB_DADR) & DADR_BSY); + } + + /* Download the endpoint buffers for endpoints 1-5. + * We specify two configurations, one interface + */ + for (cfg = 1; cfg < 3; cfg++) { + for (i = 1; i < IMX_USB_NB_EP; i++) { + imx_ep = &imx_usb->imx_ep[i]; + /* EP no | Config no */ + ep_conf[0] = (i << 4) | (cfg << 2); + /* Type | Direction */ + ep_conf[1] = (imx_ep->bmAttributes << 3) | + (EP_DIR(imx_ep) << 2); + /* Max packet size */ + ep_conf[2] = imx_ep->fifosize; + /* TRXTYP */ + ep_conf[3] = 0xC0; + /* FIFO no */ + ep_conf[4] = i; + + D_INI(imx_usb->dev, + "<%s> ep%d_conf[%d]:" + "[%02x-%02x-%02x-%02x-%02x]\n", + __func__, i, cfg, + ep_conf[0], ep_conf[1], ep_conf[2], + ep_conf[3], ep_conf[4]); + + for (j = 0; j < 5; j++) { + __raw_writeb(ep_conf[j], + imx_usb->base + USB_DDAT); + do {} while (__raw_readl(imx_usb->base + USB_DADR) + & DADR_BSY); + } + } + } + + /* wait CFG bit to clear */ + do {} while (__raw_readl(imx_usb->base + USB_DADR) & DADR_CFG); +} + +void imx_udc_init_irq(struct imx_udc_struct *imx_usb) +{ + int i; + + /* Mask and clear all irqs */ + __raw_writel(0xFFFFFFFF, imx_usb->base + USB_MASK); + __raw_writel(0xFFFFFFFF, imx_usb->base + USB_INTR); + for (i = 0; i < IMX_USB_NB_EP; i++) { + __raw_writel(0x1FF, imx_usb->base + USB_EP_MASK(i)); + __raw_writel(0x1FF, imx_usb->base + USB_EP_INTR(i)); + } + + /* Enable USB irqs */ + __raw_writel(INTR_MSOF | INTR_FRAME_MATCH, imx_usb->base + USB_MASK); + + /* Enable EP0 irqs */ + __raw_writel(0x1FF & ~(EPINTR_DEVREQ | EPINTR_EOT | EPINTR_EOF + | EPINTR_FIFO_EMPTY | EPINTR_FIFO_FULL), + imx_usb->base + USB_EP_MASK(0)); +} + +void imx_udc_init_ep(struct imx_udc_struct *imx_usb) +{ + int i, max, temp; + struct imx_ep_struct *imx_ep; + for (i = 0; i < IMX_USB_NB_EP; i++) { + imx_ep = &imx_usb->imx_ep[i]; + switch (imx_ep->fifosize) { + case 8: + max = 0; + break; + case 16: + max = 1; + break; + case 32: + max = 2; + break; + case 64: + max = 3; + break; + default: + max = 1; + break; + } + temp = (EP_DIR(imx_ep) << 7) | (max << 5) + | (imx_ep->bmAttributes << 3); + __raw_writel(temp, imx_usb->base + USB_EP_STAT(i)); + __raw_writel(temp | EPSTAT_FLUSH, imx_usb->base + USB_EP_STAT(i)); + D_INI(imx_usb->dev, "<%s> ep%d_stat %08x\n", __func__, i, + __raw_readl(imx_usb->base + USB_EP_STAT(i))); + } +} + +void imx_udc_init_fifo(struct imx_udc_struct *imx_usb) +{ + int i, temp; + struct imx_ep_struct *imx_ep; + for (i = 0; i < IMX_USB_NB_EP; i++) { + imx_ep = &imx_usb->imx_ep[i]; + + /* Fifo control */ + temp = EP_DIR(imx_ep) ? 0x0B000000 : 0x0F000000; + __raw_writel(temp, imx_usb->base + USB_EP_FCTRL(i)); + D_INI(imx_usb->dev, "<%s> ep%d_fctrl %08x\n", __func__, i, + __raw_readl(imx_usb->base + USB_EP_FCTRL(i))); + + /* Fifo alarm */ + temp = (i ? imx_ep->fifosize / 2 : 0); + __raw_writel(temp, imx_usb->base + USB_EP_FALRM(i)); + D_INI(imx_usb->dev, "<%s> ep%d_falrm %08x\n", __func__, i, + __raw_readl(imx_usb->base + USB_EP_FALRM(i))); + } +} + +static void imx_udc_init(struct imx_udc_struct *imx_usb) +{ + /* Reset UDC */ + imx_udc_reset(imx_usb); + + /* Download config to enpoint buffer */ + imx_udc_config(imx_usb); + + /* Setup interrups */ + imx_udc_init_irq(imx_usb); + + /* Setup endpoints */ + imx_udc_init_ep(imx_usb); + + /* Setup fifos */ + imx_udc_init_fifo(imx_usb); +} + +void imx_ep_irq_enable(struct imx_ep_struct *imx_ep) +{ + + int i = EP_NO(imx_ep); + + __raw_writel(0x1FF, imx_ep->imx_usb->base + USB_EP_MASK(i)); + __raw_writel(0x1FF, imx_ep->imx_usb->base + USB_EP_INTR(i)); + __raw_writel(0x1FF & ~(EPINTR_EOT | EPINTR_EOF | EPINTR_FIFO_EMPTY + | EPINTR_FIFO_FULL), imx_ep->imx_usb->base + USB_EP_MASK(i)); +} + +void imx_ep_irq_disable(struct imx_ep_struct *imx_ep) +{ + + int i = EP_NO(imx_ep); + + __raw_writel(0x1FF, imx_ep->imx_usb->base + USB_EP_MASK(i)); + __raw_writel(0x1FF, imx_ep->imx_usb->base + USB_EP_INTR(i)); +} + +int imx_ep_empty(struct imx_ep_struct *imx_ep) +{ + struct imx_udc_struct *imx_usb = imx_ep->imx_usb; + + return __raw_readl(imx_usb->base + USB_EP_FSTAT(EP_NO(imx_ep))) + & FSTAT_EMPTY; +} + +unsigned imx_fifo_bcount(struct imx_ep_struct *imx_ep) +{ + struct imx_udc_struct *imx_usb = imx_ep->imx_usb; + + return (__raw_readl(imx_usb->base + USB_EP_STAT(EP_NO(imx_ep))) + & EPSTAT_BCOUNT) >> 16; +} + +void imx_flush(struct imx_ep_struct *imx_ep) +{ + struct imx_udc_struct *imx_usb = imx_ep->imx_usb; + + int temp = __raw_readl(imx_usb->base + USB_EP_STAT(EP_NO(imx_ep))); + __raw_writel(temp | EPSTAT_FLUSH, + imx_usb->base + USB_EP_STAT(EP_NO(imx_ep))); +} + +void imx_force_stall(struct imx_ep_struct *imx_ep) +{ + struct imx_udc_struct *imx_usb = imx_ep->imx_usb; + int temp; + + imx_flush(imx_ep); + temp = __raw_readl(imx_usb->base + USB_EP_STAT(EP_NO(imx_ep))); + __raw_writel(temp | EPSTAT_STALL, + imx_usb->base + USB_EP_STAT(EP_NO(imx_ep))); +} + +static int imx_udc_get_frame(struct usb_gadget *_gadget) +{ + struct imx_udc_struct *imx_usb = container_of(_gadget, + struct imx_udc_struct, gadget); + + return __raw_readl(imx_usb->base + USB_FRAME) & 0x7FF; +} + +static int imx_udc_wakeup(struct usb_gadget *_gadget) +{ + return 0; +} + +/******************************************************************************* + * USB request control functions + ******************************************************************************* + */ + +static void ep_add_request(struct imx_ep_struct *imx_ep, struct imx_request *req) +{ + if (unlikely(!req)) + return; + + req->in_use = 1; + list_add_tail(&req->queue, &imx_ep->queue); +} + +static void ep_del_request(struct imx_ep_struct *imx_ep, struct imx_request *req) +{ + if (unlikely(!req)) + return; + + list_del_init(&req->queue); + req->in_use = 0; +} + +static void done(struct imx_ep_struct *imx_ep, struct imx_request *req, int status) +{ + ep_del_request(imx_ep, req); + + if (likely(req->req.status == -EINPROGRESS)) + req->req.status = status; + else + status = req->req.status; + + if (status && status != -ESHUTDOWN) + D_REQ(imx_ep->imx_usb->dev, + "<%s> complete %s req %p stat %d len %u/%u\n", __func__, + imx_ep->ep.name, &req->req, status, + req->req.actual, req->req.length); + + req->req.complete(&imx_ep->ep, &req->req); +} + +static void nuke(struct imx_ep_struct *imx_ep, int status) +{ + struct imx_request *req; + + while (!list_empty(&imx_ep->queue)) { + req = list_entry(imx_ep->queue.next, struct imx_request, queue); + done(imx_ep, req, status); + } +} + +/******************************************************************************* + * Data tansfer over USB functions + ******************************************************************************* + */ +static int read_packet(struct imx_ep_struct *imx_ep, struct imx_request *req) +{ + u8 *buf; + int bytes_ep, bufferspace, count, i; + + bytes_ep = imx_fifo_bcount(imx_ep); + bufferspace = req->req.length - req->req.actual; + + buf = req->req.buf + req->req.actual; + prefetchw(buf); + + if (unlikely(imx_ep_empty(imx_ep))) + count = 0; /* zlp */ + else + count = min(bytes_ep, bufferspace); + + for (i = count; i > 0; i--) + *buf++ = __raw_readb(imx_ep->imx_usb->base + + USB_EP_FDAT0(EP_NO(imx_ep))); + req->req.actual += count; + + return count; +} + +static int write_packet(struct imx_ep_struct *imx_ep, struct imx_request *req) +{ + u8 *buf; + int length, count, temp; + + buf = req->req.buf + req->req.actual; + prefetch(buf); + + length = min(req->req.length - req->req.actual, (u32)imx_ep->fifosize); + + if (imx_fifo_bcount(imx_ep) + length > imx_ep->fifosize) { + D_TRX(imx_ep->imx_usb->dev, "<%s> packet overfill %s fifo\n", + __func__, imx_ep->ep.name); + return -1; + } + + req->req.actual += length; + count = length; + + if (!count && req->req.zero) { /* zlp */ + temp = __raw_readl(imx_ep->imx_usb->base + + USB_EP_STAT(EP_NO(imx_ep))); + __raw_writel(temp | EPSTAT_ZLPS, imx_ep->imx_usb->base + + USB_EP_STAT(EP_NO(imx_ep))); + D_TRX(imx_ep->imx_usb->dev, "<%s> zero packet\n", __func__); + return 0; + } + + while (count--) { + if (count == 0) { /* last byte */ + temp = __raw_readl(imx_ep->imx_usb->base + + USB_EP_FCTRL(EP_NO(imx_ep))); + __raw_writel(temp | FCTRL_WFR, imx_ep->imx_usb->base + + USB_EP_FCTRL(EP_NO(imx_ep))); + } + __raw_writeb(*buf++, + imx_ep->imx_usb->base + USB_EP_FDAT0(EP_NO(imx_ep))); + } + + return length; +} + +static int read_fifo(struct imx_ep_struct *imx_ep, struct imx_request *req) +{ + int bytes = 0, + count, + is_short, + completed = 0; + + while (__raw_readl(imx_ep->imx_usb->base + USB_EP_FSTAT(EP_NO(imx_ep))) + & FSTAT_FR) { + count = read_packet(imx_ep, req); + bytes += count; + + is_short = (count != imx_ep->fifosize); + if (is_short || req->req.actual == req->req.length) { + completed = 1; + break; + } + } + + if (completed || !req->req.length) { + done(imx_ep, req, 0); + if (!EP_NO(imx_ep)) + ep0_chg_stat(__func__, imx_ep->imx_usb, EP0_IDLE); + } + + D_TRX(imx_ep->imx_usb->dev, "<%s> bytes read: %d\n", __func__, bytes); + + return completed; +} + +static int write_fifo(struct imx_ep_struct *imx_ep, struct imx_request *req) +{ + int bytes = 0, + count, + is_short = 0; + + while (!is_short) { + count = write_packet(imx_ep, req); + if (count < 0) + break; /* busy */ + bytes += count; + + /* last packet "must be" short (or a zlp) */ + is_short = (count != imx_ep->fifosize); + + if (unlikely(is_short)) { + done(imx_ep, req, 0); + if (!EP_NO(imx_ep)) + ep0_chg_stat(__func__, imx_ep->imx_usb, EP0_IDLE); + } + } + + D_TRX(imx_ep->imx_usb->dev, "<%s> bytes sent: %d\n", __func__, bytes); + + return is_short; +} + +/******************************************************************************* + * Endpoint handlers + ******************************************************************************* + */ +static int handle_ep(struct imx_ep_struct *imx_ep) +{ + struct imx_request *req; + int completed = 0; + + do { + if (!list_empty(&imx_ep->queue)) + req = list_entry(imx_ep->queue.next, + struct imx_request, queue); + else { + D_REQ(imx_ep->imx_usb->dev, "<%s> no request on %s\n", + __func__, imx_ep->ep.name); + return 0; + } + + if (EP_DIR(imx_ep)) /* to host */ + completed = write_fifo(imx_ep, req); + else /* to device */ + completed = read_fifo(imx_ep, req); + + D_REQ(imx_ep->imx_usb->dev, "<%s> %s req<%p> %s\n", + __func__, imx_ep->ep.name, req, + completed ? "completed" : "not completed"); + + dump_ep_stat(__func__, imx_ep); + + } while (completed); + + return 0; +} + +static int handle_ep0(struct imx_ep_struct *imx_ep) +{ + struct imx_request *req = NULL; + int ret = 0; + + if (!list_empty(&imx_ep->queue)) + req = list_entry(imx_ep->queue.next, struct imx_request, queue); + + if (req) { + switch (imx_ep->imx_usb->ep0state) { + + case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR */ + write_fifo(imx_ep, req); + break; + case EP0_OUT_DATA_PHASE: /* SET_DESCRIPTOR */ + read_fifo(imx_ep, req); + break; + default: + D_EP0(imx_ep->imx_usb->dev, + "<%s> ep0 i/o, odd state %d\n", + __func__, imx_ep->imx_usb->ep0state); + ep_del_request(imx_ep, req); + ret = -EL2HLT; + break; + } + } + + return ret; +} + +static void handle_ep0_devreq(struct imx_udc_struct *imx_usb) +{ + struct imx_ep_struct *imx_ep = &imx_usb->imx_ep[0]; + union { + struct usb_ctrlrequest r; + u8 raw[8]; + u32 word[2]; + } u; + int temp, i; + + nuke(imx_ep, -EPROTO); + + /* read SETUP packet */ + for (i = 0; i < 2; i++) { + if (imx_ep_empty(imx_ep)) { + D_EP0(imx_usb->dev, + "<%s> no setup packet received\n", __func__); + goto stall; + } + u.word[i] = __raw_readl(imx_usb->base + USB_EP_FDAT(EP_NO(imx_ep))); + } + + temp = imx_ep_empty(imx_ep); + while (!imx_ep_empty(imx_ep)) { + i = __raw_readl(imx_usb->base + USB_EP_FDAT(EP_NO(imx_ep))); + D_EP0(imx_usb->dev, + "<%s> wrong to have extra bytes for setup : 0x%08x\n", + __func__, i); + } + if (!temp) + goto stall; + + le16_to_cpus(&u.r.wValue); + le16_to_cpus(&u.r.wIndex); + le16_to_cpus(&u.r.wLength); + + D_EP0(imx_usb->dev, "<%s> SETUP %02x.%02x v%04x i%04x l%04x\n", + __func__, u.r.bRequestType, u.r.bRequest, + u.r.wValue, u.r.wIndex, u.r.wLength); + + if (imx_usb->set_config) { + /* NACK the host by using CMDOVER */ + temp = __raw_readl(imx_usb->base + USB_CTRL); + __raw_writel(temp | CTRL_CMDOVER, imx_usb->base + USB_CTRL); + + D_EP0(imx_usb->dev, + "<%s> set config req is pending, NACK the host\n", + __func__); + return; + } + + if (u.r.bRequestType & USB_DIR_IN) + ep0_chg_stat(__func__, imx_usb, EP0_IN_DATA_PHASE); + else + ep0_chg_stat(__func__, imx_usb, EP0_OUT_DATA_PHASE); + + i = imx_usb->driver->setup(&imx_usb->gadget, &u.r); + if (i < 0) { + D_EP0(imx_usb->dev, "<%s> protocol STALL, ep0 err %d\n", + __func__, i); + goto stall; + } + + return; +stall: + imx_force_stall(imx_ep); + ep0_chg_stat(__func__, imx_usb, EP0_STALL); + return; +} + +/******************************************************************************* + * USB gadget callback functions + ******************************************************************************* + */ + +static int imx_ep_enable(struct usb_ep *usb_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct imx_ep_struct *imx_ep = container_of(usb_ep, + struct imx_ep_struct, ep); + struct imx_udc_struct *imx_usb = imx_ep->imx_usb; + unsigned long flags; + + if (!usb_ep + || !desc + || !EP_NO(imx_ep) + || desc->bDescriptorType != USB_DT_ENDPOINT + || imx_ep->bEndpointAddress != desc->bEndpointAddress) { + D_EPX(imx_usb->dev, + "<%s> bad ep or descriptor\n", __func__); + return -EINVAL; + } + + if (imx_ep->bmAttributes != desc->bmAttributes) { + D_EPX(imx_usb->dev, + "<%s> %s type mismatch\n", __func__, usb_ep->name); + return -EINVAL; + } + + if (imx_ep->fifosize < le16_to_cpu(desc->wMaxPacketSize)) { + D_EPX(imx_usb->dev, + "<%s> bad %s maxpacket\n", __func__, usb_ep->name); + return -ERANGE; + } + + if (!imx_usb->driver || imx_usb->gadget.speed == USB_SPEED_UNKNOWN) { + D_EPX(imx_usb->dev, "<%s> bogus device state\n", __func__); + return -ESHUTDOWN; + } + + spin_lock_irqsave(&imx_ep->lock, flags); + + imx_ep->stopped = 0; + imx_flush(imx_ep); + imx_ep_irq_enable(imx_ep); + + spin_unlock_irqrestore(&imx_ep->lock, flags); + + D_EPX(imx_usb->dev, "<%s> ENABLED %s\n", __func__, usb_ep->name); + return 0; +} + +static int imx_ep_disable(struct usb_ep *usb_ep) +{ + struct imx_ep_struct *imx_ep = container_of(usb_ep, + struct imx_ep_struct, ep); + unsigned long flags; + + if (!usb_ep || !EP_NO(imx_ep) || !list_empty(&imx_ep->queue)) { + D_EPX(imx_ep->imx_usb->dev, "<%s> %s can not be disabled\n", + __func__, usb_ep ? imx_ep->ep.name : NULL); + return -EINVAL; + } + + spin_lock_irqsave(&imx_ep->lock, flags); + + imx_ep->stopped = 1; + nuke(imx_ep, -ESHUTDOWN); + imx_flush(imx_ep); + imx_ep_irq_disable(imx_ep); + + spin_unlock_irqrestore(&imx_ep->lock, flags); + + D_EPX(imx_ep->imx_usb->dev, + "<%s> DISABLED %s\n", __func__, usb_ep->name); + return 0; +} + +static struct usb_request *imx_ep_alloc_request + (struct usb_ep *usb_ep, gfp_t gfp_flags) +{ + struct imx_request *req; + + req = kzalloc(sizeof *req, gfp_flags); + if (!req || !usb_ep) + return 0; + + INIT_LIST_HEAD(&req->queue); + req->in_use = 0; + + return &req->req; +} + +static void imx_ep_free_request + (struct usb_ep *usb_ep, struct usb_request *usb_req) +{ + struct imx_request *req; + + req = container_of(usb_req, struct imx_request, req); + WARN_ON(!list_empty(&req->queue)); + kfree(req); +} + +static int imx_ep_queue + (struct usb_ep *usb_ep, struct usb_request *usb_req, gfp_t gfp_flags) +{ + struct imx_ep_struct *imx_ep = container_of + (usb_ep, struct imx_ep_struct, ep); + struct imx_request *req = container_of + (usb_req, struct imx_request, req); + struct imx_udc_struct *imx_usb = imx_ep->imx_usb; + unsigned long flags; + int ret = 0; + + if (imx_usb->set_config && !EP_NO(imx_ep)) { + /* + Special care on IMX udc. + Ignore enqueue when after set configuration from the + host. This assume all gadget drivers reply set + configuration with the next ep0 req enqueue. + */ + imx_usb->set_config = 0; + D_EPX(imx_usb->dev, + "<%s> gadget reply set config\n", __func__); + return 0; + } + + if (unlikely(!usb_req || !req || !usb_req->complete || !usb_req->buf)) { + D_EPX(imx_usb->dev, "<%s> bad params\n", __func__); + return -EINVAL; + } + + if (unlikely(!usb_ep || !imx_ep)) { + D_EPX(imx_usb->dev, "<%s> bad ep\n", __func__); + return -EINVAL; + } + + if (!imx_usb->driver || imx_usb->gadget.speed == USB_SPEED_UNKNOWN) { + D_EPX(imx_usb->dev, "<%s> bogus device state\n", __func__); + return -ESHUTDOWN; + } + + spin_lock_irqsave(&imx_ep->lock, flags); + + /* Debug */ + dump_usb_stat(__func__, imx_usb); + dump_req(__func__, imx_ep, usb_req); + + if (imx_ep->stopped) { + usb_req->status = -ESHUTDOWN; + ret = -ESHUTDOWN; + goto out; + } + + if (req->in_use) { + D_REQ(imx_usb->dev, + "<%s> refusing to queue req %p (already queued)\n", + __func__, req); + goto out; + } + + usb_req->status = -EINPROGRESS; + usb_req->actual = 0; + + ep_add_request(imx_ep, req); + + if (!EP_NO(imx_ep)) /* ep0 */ + ret = handle_ep0(imx_ep); + else + ret = handle_ep(imx_ep); +out: + spin_unlock_irqrestore(&imx_ep->lock, flags); + return ret; +} + +static int imx_ep_dequeue(struct usb_ep *usb_ep, struct usb_request *usb_req) +{ + + struct imx_ep_struct *imx_ep = container_of + (usb_ep, struct imx_ep_struct, ep); + struct imx_request *req; + unsigned long flags; + + if (unlikely(!usb_ep || !EP_NO(imx_ep))) { + D_EPX(imx_ep->imx_usb->dev, "<%s> bad ep\n", __func__); + return -EINVAL; + } + + spin_lock_irqsave(&imx_ep->lock, flags); + + /* make sure it's actually queued on this endpoint */ + list_for_each_entry(req, &imx_ep->queue, queue) { + if (&req->req == usb_req) + break; + } + if (&req->req != usb_req) { + spin_unlock_irqrestore(&imx_ep->lock, flags); + return -EINVAL; + } + + done(imx_ep, req, -ECONNRESET); + + spin_unlock_irqrestore(&imx_ep->lock, flags); + return 0; +} + +static int imx_ep_set_halt(struct usb_ep *usb_ep, int value) +{ + struct imx_ep_struct *imx_ep = container_of + (usb_ep, struct imx_ep_struct, ep); + unsigned long flags; + + if (unlikely(!usb_ep || !EP_NO(imx_ep))) { + D_EPX(imx_ep->imx_usb->dev, "<%s> bad ep\n", __func__); + return -EINVAL; + } + + spin_lock_irqsave(&imx_ep->lock, flags); + + if ((imx_ep->bEndpointAddress & USB_DIR_IN) + && !list_empty(&imx_ep->queue)) { + spin_unlock_irqrestore(&imx_ep->lock, flags); + return -EAGAIN; + } + + imx_force_stall(imx_ep); + + spin_unlock_irqrestore(&imx_ep->lock, flags); + + D_EPX(imx_ep->imx_usb->dev, "<%s> %s halt\n", __func__, usb_ep->name); + return 0; +} + +static int imx_ep_fifo_status(struct usb_ep *usb_ep) +{ + struct imx_ep_struct *imx_ep = container_of + (usb_ep, struct imx_ep_struct, ep); + + if (!usb_ep) { + D_EPX(imx_ep->imx_usb->dev, "<%s> bad ep\n", __func__); + return -ENODEV; + } + + if (imx_ep->imx_usb->gadget.speed == USB_SPEED_UNKNOWN) + return 0; + else + return imx_fifo_bcount(imx_ep); +} + +static void imx_ep_fifo_flush(struct usb_ep *usb_ep) +{ + struct imx_ep_struct *imx_ep = container_of + (usb_ep, struct imx_ep_struct, ep); + unsigned long flags; + + spin_lock_irqsave(&imx_ep->lock, flags); + + if (!usb_ep || !EP_NO(imx_ep) || !list_empty(&imx_ep->queue)) { + D_EPX(imx_ep->imx_usb->dev, "<%s> bad ep\n", __func__); + spin_unlock_irqrestore(&imx_ep->lock, flags); + return; + } + + /* toggle and halt bits stay unchanged */ + imx_flush(imx_ep); + + spin_unlock_irqrestore(&imx_ep->lock, flags); +} + +static struct usb_ep_ops imx_ep_ops = { + .enable = imx_ep_enable, + .disable = imx_ep_disable, + + .alloc_request = imx_ep_alloc_request, + .free_request = imx_ep_free_request, + + .queue = imx_ep_queue, + .dequeue = imx_ep_dequeue, + + .set_halt = imx_ep_set_halt, + .fifo_status = imx_ep_fifo_status, + .fifo_flush = imx_ep_fifo_flush, +}; + +/******************************************************************************* + * USB endpoint control functions + ******************************************************************************* + */ + +void ep0_chg_stat(const char *label, + struct imx_udc_struct *imx_usb, enum ep0_state stat) +{ + D_STT(imx_usb->dev, "<%s> from %15s to %15s\n", + label, state_name[imx_usb->ep0state], state_name[stat]); + + if (imx_usb->ep0state == stat) + return; + + imx_usb->ep0state = stat; +} + +static void usb_init_data(struct imx_udc_struct *imx_usb) +{ + struct imx_ep_struct *imx_ep; + u8 i; + + /* device/ep0 records init */ + INIT_LIST_HEAD(&imx_usb->gadget.ep_list); + INIT_LIST_HEAD(&imx_usb->gadget.ep0->ep_list); + ep0_chg_stat(__func__, imx_usb, EP0_IDLE); + + /* basic endpoint records init */ + for (i = 0; i < IMX_USB_NB_EP; i++) { + imx_ep = &imx_usb->imx_ep[i]; + + if (i) { + list_add_tail(&imx_ep->ep.ep_list, + &imx_usb->gadget.ep_list); + imx_ep->stopped = 1; + } else + imx_ep->stopped = 0; + + INIT_LIST_HEAD(&imx_ep->queue); + spin_lock_init(&imx_ep->lock); + } +} + +static void udc_stop_activity(struct imx_udc_struct *imx_usb, + struct usb_gadget_driver *driver) +{ + struct imx_ep_struct *imx_ep; + int i; + + + if (imx_usb->gadget.speed == USB_SPEED_UNKNOWN) + driver = 0; + /* prevent new request submissions, kill any outstanding requests */ + for (i = 1; i < IMX_USB_NB_EP; i++) { + imx_ep = &imx_usb->imx_ep[i]; + imx_flush(imx_ep); + imx_ep->stopped = 1; + nuke(imx_ep, -ESHUTDOWN); + } + imx_usb->gadget.speed = USB_SPEED_UNKNOWN; + if (driver) { + imx_usb->cfg = 0; + imx_usb->intf = 0; + imx_usb->alt = 0; + driver->disconnect(&imx_usb->gadget); + } +} + +/******************************************************************************* + * Interrupt handlers + ******************************************************************************* + */ + +static irqreturn_t imx_udc_irq(int irq, void *dev) +{ + struct imx_udc_struct *imx_usb = dev; + struct usb_ctrlrequest u; + int intr, temp, cfg, intf, alt; + + + intr = __raw_readl(imx_usb->base + USB_INTR); + + if (intr & (INTR_WAKEUP | INTR_SUSPEND | INTR_RESUME | INTR_RESET_START + | INTR_RESET_STOP | INTR_CFG_CHG)) { + dump_intr(__func__, intr, imx_usb->dev); + dump_usb_stat(__func__, imx_usb); + } + + if (!imx_usb->driver) { + /*imx_udc_disable(imx_usb);*/ + goto end_irq; + } + + if (intr & INTR_WAKEUP) { + if (imx_usb->gadget.speed == USB_SPEED_UNKNOWN + && imx_usb->driver && imx_usb->driver->resume) + imx_usb->driver->resume(&imx_usb->gadget); + __raw_writel(intr, imx_usb->base + USB_INTR); + imx_usb->set_config = 0; + imx_usb->gadget.speed = USB_SPEED_FULL; + } + + if (intr & INTR_SUSPEND) { + if (imx_usb->gadget.speed != USB_SPEED_UNKNOWN + && imx_usb->driver && imx_usb->driver->suspend) + imx_usb->driver->suspend(&imx_usb->gadget); + imx_usb->gadget.speed = USB_SPEED_UNKNOWN; + } + + if (intr & INTR_RESET_START) { + udc_stop_activity(imx_usb, imx_usb->driver); + __raw_writel(intr, imx_usb->base + USB_INTR); + imx_usb->set_config = 0; + imx_usb->gadget.speed = USB_SPEED_FULL; + } + + if (intr & INTR_RESET_STOP) + imx_usb->gadget.speed = USB_SPEED_FULL; + + if (intr & INTR_CFG_CHG) { + temp = __raw_readl(imx_usb->base + USB_STAT); + cfg = (temp & STAT_CFG) >> 5; + intf = (temp & STAT_INTF) >> 3; + alt = temp & STAT_ALTSET; + + D_IRQ(imx_usb->dev, + "<%s> orig config C=%d, I=%d, A=%d / " + "req config C=%d, I=%d, A=%d\n", + __func__, imx_usb->cfg, imx_usb->intf, imx_usb->alt, + cfg, intf, alt); + + if (cfg != 1 && cfg != 2) + goto end_irq; /* can not happen */ + + imx_usb->set_config = 0; + + /* Config setup */ + if (imx_usb->cfg != cfg) { + u.bRequest = USB_REQ_SET_CONFIGURATION; + u.bRequestType = USB_DIR_OUT | + USB_TYPE_STANDARD | + USB_RECIP_DEVICE; + u.wValue = cfg; + u.wIndex = 0; + u.wLength = 0; + imx_usb->cfg = cfg; + imx_usb->set_config = 1; + imx_usb->driver->setup(&imx_usb->gadget, &u); + } + + if (imx_usb->intf != intf || imx_usb->alt != alt) { + + u.bRequest = USB_REQ_SET_INTERFACE; + u.bRequestType = USB_DIR_OUT | + USB_TYPE_STANDARD | + USB_RECIP_INTERFACE; + u.wValue = alt; + u.wIndex = intf; + u.wLength = 0; + imx_usb->intf = intf; + imx_usb->alt = alt; + imx_usb->set_config = 1; + imx_usb->driver->setup(&imx_usb->gadget, &u); + } + } + + if (intr & INTR_SOF) { + /* copy from motorola bsp. + We must enable SOF intr and signal CMDOVER. + Datasheet don't specifiy this action, but it + is done in motorola bsp, so just copy it + */ + if (imx_usb->ep0state == EP0_IDLE) { + temp = __raw_readl(imx_usb->base + USB_CTRL); + __raw_writel(temp | CTRL_CMDOVER, imx_usb->base + USB_CTRL); + } + } + +end_irq: + __raw_writel(intr, imx_usb->base + USB_INTR); + return IRQ_HANDLED; +} + +static irqreturn_t imx_udc_ctrl_irq(int irq, void *dev) +{ + struct imx_udc_struct *imx_usb = dev; + int intr = __raw_readl(imx_usb->base + USB_EP_INTR(0)); + + dump_ep_intr(__func__, 0, intr, imx_usb->dev); + + if (!imx_usb->driver) { + __raw_writel(intr, imx_usb->base + USB_EP_INTR(0)); + return IRQ_HANDLED; + } + + /* DEVREQ IRQ has highest priority */ + if (intr & EPINTR_DEVREQ) + handle_ep0_devreq(imx_usb); + /* Seem i.MX is missing EOF interrupt sometimes. + * Therefore we monitor both EOF and FIFO_EMPTY interrups + * when transmiting, and both EOF and FIFO_FULL when + * receiving data. + */ + else if (intr & (EPINTR_EOF | EPINTR_FIFO_EMPTY | EPINTR_FIFO_FULL)) + handle_ep0(&imx_usb->imx_ep[0]); + + __raw_writel(intr, imx_usb->base + USB_EP_INTR(0)); + + return IRQ_HANDLED; +} + +static irqreturn_t imx_udc_bulk_irq(int irq, void *dev) +{ + struct imx_udc_struct *imx_usb = dev; + struct imx_ep_struct *imx_ep = &imx_usb->imx_ep[irq - USBD_INT0]; + int intr = __raw_readl(imx_usb->base + USB_EP_INTR(EP_NO(imx_ep))); + + dump_ep_intr(__func__, irq - USBD_INT0, intr, imx_usb->dev); + + if (!imx_usb->driver) { + __raw_writel(intr, imx_usb->base + USB_EP_INTR(EP_NO(imx_ep))); + return IRQ_HANDLED; + } + + handle_ep(imx_ep); + + __raw_writel(intr, imx_usb->base + USB_EP_INTR(EP_NO(imx_ep))); + + return IRQ_HANDLED; +} + +irq_handler_t intr_handler(int i) +{ + switch (i) { + case 0: + return imx_udc_ctrl_irq; + case 1: + case 2: + case 3: + case 4: + case 5: + return imx_udc_bulk_irq; + default: + return imx_udc_irq; + } +} + +/******************************************************************************* + * Static defined IMX UDC structure + ******************************************************************************* + */ + +static const struct usb_gadget_ops imx_udc_ops = { + .get_frame = imx_udc_get_frame, + .wakeup = imx_udc_wakeup, +}; + +static struct imx_udc_struct controller = { + .gadget = { + .ops = &imx_udc_ops, + .ep0 = &controller.imx_ep[0].ep, + .name = driver_name, + .dev = { + .bus_id = "gadget", + }, + }, + + .imx_ep[0] = { + .ep = { + .name = ep0name, + .ops = &imx_ep_ops, + .maxpacket = 32, + }, + .imx_usb = &controller, + .fifosize = 32, + .bEndpointAddress = 0, + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, + }, + + .imx_ep[1] = { + .ep = { + .name = "ep1in-bulk", + .ops = &imx_ep_ops, + .maxpacket = 64, + }, + .imx_usb = &controller, + .fifosize = 64, + .bEndpointAddress = USB_DIR_IN | 1, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + }, + .imx_ep[2] = { + .ep = { + .name = "ep2out-bulk", + .ops = &imx_ep_ops, + .maxpacket = 64, + }, + .imx_usb = &controller, + .fifosize = 64, + .bEndpointAddress = USB_DIR_OUT | 2, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + }, + .imx_ep[3] = { + .ep = { + .name = "ep3out-bulk", + .ops = &imx_ep_ops, + .maxpacket = 32, + }, + .imx_usb = &controller, + .fifosize = 32, + .bEndpointAddress = USB_DIR_OUT | 3, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + }, + .imx_ep[4] = { + .ep = { + .name = "ep4in-int", + .ops = &imx_ep_ops, + .maxpacket = 16, + }, + .imx_usb = &controller, + .fifosize = 16, + .bEndpointAddress = USB_DIR_IN | 4, + .bmAttributes = USB_ENDPOINT_XFER_INT, + }, + .imx_ep[5] = { + .ep = { + .name = "ep5out-int", + .ops = &imx_ep_ops, + .maxpacket = 16, + }, + .imx_usb = &controller, + .fifosize = 16, + .bEndpointAddress = USB_DIR_OUT | 5, + .bmAttributes = USB_ENDPOINT_XFER_INT, + }, +}; + +/******************************************************************************* + * USB gadged driver functions + ******************************************************************************* + */ +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + struct imx_udc_struct *imx_usb = &controller; + int retval; + + if (!driver + || driver->speed < USB_SPEED_FULL + || !driver->bind + || !driver->disconnect + || !driver->setup) + return -EINVAL; + if (!imx_usb) + return -ENODEV; + if (imx_usb->driver) + return -EBUSY; + + /* first hook up the driver ... */ + imx_usb->driver = driver; + imx_usb->gadget.dev.driver = &driver->driver; + + retval = device_add(&imx_usb->gadget.dev); + if (retval) + goto fail; + retval = driver->bind(&imx_usb->gadget); + if (retval) { + D_INI(imx_usb->dev, "<%s> bind to driver %s --> error %d\n", + __func__, driver->driver.name, retval); + device_del(&imx_usb->gadget.dev); + + goto fail; + } + + D_INI(imx_usb->dev, "<%s> registered gadget driver '%s'\n", + __func__, driver->driver.name); + imx_udc_enable(imx_usb); + imx_usb->cfg = 0; + imx_usb->intf = 0; + imx_usb->alt = 0; + + return 0; +fail: + imx_usb->driver = NULL; + imx_usb->gadget.dev.driver = NULL; + return retval; +} +EXPORT_SYMBOL(usb_gadget_register_driver); + +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct imx_udc_struct *imx_usb = &controller; + + if (!imx_usb) + return -ENODEV; + if (!driver || driver != imx_usb->driver || !driver->unbind) + return -EINVAL; + + udc_stop_activity(imx_usb, driver); + imx_udc_disable(imx_usb); + + driver->unbind(&imx_usb->gadget); + imx_usb->gadget.dev.driver = NULL; + imx_usb->driver = NULL; + + device_del(&imx_usb->gadget.dev); + + D_INI(imx_usb->dev, "<%s> unregistered gadget driver '%s'\n", + __func__, driver->driver.name); + + return 0; +} +EXPORT_SYMBOL(usb_gadget_unregister_driver); + +/******************************************************************************* + * Module functions + ******************************************************************************* + */ + +static int __init imx_udc_probe(struct platform_device *pdev) +{ + struct imx_udc_struct *imx_usb = &controller; + struct resource *res; + struct imxusb_platform_data *pdata; + struct clk *clk; + void __iomem *base; + int ret = 0; + int i, res_size; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "can't get device resources\n"); + return -ENODEV; + } + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "driver needs platform data\n"); + return -ENODEV; + } + + res_size = res->end - res->start + 1; + if (!request_mem_region(res->start, res_size, res->name)) { + dev_err(&pdev->dev, "can't allocate %d bytes at %d address\n", + res_size, res->start); + return -ENOMEM; + } + + if (pdata->init) { + ret = pdata->init(&pdev->dev); + if (ret) + goto fail0; + } + + base = ioremap(res->start, res_size); + if (!base) { + dev_err(&pdev->dev, "ioremap failed\n"); + ret = -EIO; + goto fail1; + } + + clk = clk_get(NULL, "usbd_clk"); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + dev_err(&pdev->dev, "can't get USB clock\n"); + goto fail2; + } + clk_enable(clk); + + if (clk_get_rate(clk) != 48000000) { + D_INI(&pdev->dev, + "Bad USB clock (%d Hz), changing to 48000000 Hz\n", + (int)clk_get_rate(clk)); + if (clk_set_rate(clk, 48000000)) { + dev_err(&pdev->dev, + "Unable to set correct USB clock (48MHz)\n"); + ret = -EIO; + goto fail3; + } + } + + for (i = 0; i < IMX_USB_NB_EP + 1; i++) { + imx_usb->usbd_int[i] = platform_get_irq(pdev, i); + if (imx_usb->usbd_int[i] < 0) { + dev_err(&pdev->dev, "can't get irq number\n"); + ret = -ENODEV; + goto fail3; + } + } + + for (i = 0; i < IMX_USB_NB_EP + 1; i++) { + ret = request_irq(imx_usb->usbd_int[i], intr_handler(i), + IRQF_DISABLED, driver_name, imx_usb); + if (ret) { + dev_err(&pdev->dev, "can't get irq %i, err %d\n", + imx_usb->usbd_int[i], ret); + for (--i; i >= 0; i--) + free_irq(imx_usb->usbd_int[i], imx_usb); + goto fail3; + } + } + + imx_usb->res = res; + imx_usb->base = base; + imx_usb->clk = clk; + imx_usb->dev = &pdev->dev; + + device_initialize(&imx_usb->gadget.dev); + + imx_usb->gadget.dev.parent = &pdev->dev; + imx_usb->gadget.dev.dma_mask = pdev->dev.dma_mask; + + platform_set_drvdata(pdev, imx_usb); + + dump_usb_stat(__func__, imx_usb); + + usb_init_data(imx_usb); + imx_udc_init(imx_usb); + + dump_usb_stat(__func__, imx_usb); + + return 0; + +fail3: + clk_put(clk); + clk_disable(clk); +fail2: + iounmap(base); +fail1: + if (pdata->exit) + pdata->exit(&pdev->dev); +fail0: + release_mem_region(res->start, res_size); + return ret; +} + +static int __exit imx_udc_remove(struct platform_device *pdev) +{ + struct imx_udc_struct *imx_usb = platform_get_drvdata(pdev); + struct imxusb_platform_data *pdata = pdev->dev.platform_data; + int i; + + imx_udc_disable(imx_usb); + + for (i = 0; i < IMX_USB_NB_EP + 1; i++) + free_irq(imx_usb->usbd_int[i], imx_usb); + + clk_put(imx_usb->clk); + clk_disable(imx_usb->clk); + iounmap(imx_usb->base); + + release_mem_region(imx_usb->res->start, + imx_usb->res->end - imx_usb->res->start + 1); + + if (pdata->exit) + pdata->exit(&pdev->dev); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +/*----------------------------------------------------------------------------*/ + +#ifdef CONFIG_PM +#define imx_udc_suspend NULL +#define imx_udc_resume NULL +#else +#define imx_udc_suspend NULL +#define imx_udc_resume NULL +#endif + +/*----------------------------------------------------------------------------*/ + +static struct platform_driver udc_driver = { + .driver = { + .name = driver_name, + .owner = THIS_MODULE, + }, + .remove = __exit_p(imx_udc_remove), + .suspend = imx_udc_suspend, + .resume = imx_udc_resume, +}; + +static int __init udc_init(void) +{ + return platform_driver_probe(&udc_driver, imx_udc_probe); +} +module_init(udc_init); + +static void __exit udc_exit(void) +{ + platform_driver_unregister(&udc_driver); +} +module_exit(udc_exit); + +MODULE_DESCRIPTION("IMX USB Device Controller driver"); +MODULE_AUTHOR("Darius Augulis <augulis.darius@gmail.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:imx_udc"); diff --git a/drivers/usb/gadget/imx_udc.h b/drivers/usb/gadget/imx_udc.h new file mode 100644 index 000000000000..192161a756bd --- /dev/null +++ b/drivers/usb/gadget/imx_udc.h @@ -0,0 +1,327 @@ +/* + * Copyright (C) 2005 Mike Lee(eemike@gmail.com) + * + * This udc driver is now under testing and code is based on pxa2xx_udc.h + * Please use it with your own risk! + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __LINUX_USB_GADGET_IMX_H +#define __LINUX_USB_GADGET_IMX_H + +#include <linux/types.h> + +/* Helper macros */ +#define EP_NO(ep) ((ep->bEndpointAddress) & ~USB_DIR_IN) /* IN:1, OUT:0 */ +#define EP_DIR(ep) ((ep->bEndpointAddress) & USB_DIR_IN ? 1 : 0) +#define irq_to_ep(irq) (((irq) >= USBD_INT0) || ((irq) <= USBD_INT6) ? ((irq) - USBD_INT0) : (USBD_INT6)) /*should not happen*/ +#define ep_to_irq(ep) (EP_NO((ep)) + USBD_INT0) +#define IMX_USB_NB_EP 6 + +/* Driver structures */ +struct imx_request { + struct usb_request req; + struct list_head queue; + unsigned int in_use; +}; + +enum ep0_state { + EP0_IDLE, + EP0_IN_DATA_PHASE, + EP0_OUT_DATA_PHASE, + EP0_END_XFER, + EP0_STALL, +}; + +struct imx_ep_struct { + struct usb_ep ep; + struct imx_udc_struct *imx_usb; + struct list_head queue; + spinlock_t lock; + unsigned char stopped; + unsigned char fifosize; + unsigned char bEndpointAddress; + unsigned char bmAttributes; +}; + +struct imx_udc_struct { + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + struct device *dev; + struct imx_ep_struct imx_ep[IMX_USB_NB_EP]; + struct clk *clk; + enum ep0_state ep0state; + struct resource *res; + void __iomem *base; + spinlock_t lock; + unsigned char set_config; + int cfg, + intf, + alt, + usbd_int[7]; +}; + +/* USB registers */ +#define USB_FRAME (0x00) /* USB frame */ +#define USB_SPEC (0x04) /* USB Spec */ +#define USB_STAT (0x08) /* USB Status */ +#define USB_CTRL (0x0C) /* USB Control */ +#define USB_DADR (0x10) /* USB Desc RAM addr */ +#define USB_DDAT (0x14) /* USB Desc RAM/EP buffer data */ +#define USB_INTR (0x18) /* USB interrupt */ +#define USB_MASK (0x1C) /* USB Mask */ +#define USB_ENAB (0x24) /* USB Enable */ +#define USB_EP_STAT(x) (0x30 + (x*0x30)) /* USB status/control */ +#define USB_EP_INTR(x) (0x34 + (x*0x30)) /* USB interrupt */ +#define USB_EP_MASK(x) (0x38 + (x*0x30)) /* USB mask */ +#define USB_EP_FDAT(x) (0x3C + (x*0x30)) /* USB FIFO data */ +#define USB_EP_FDAT0(x) (0x3C + (x*0x30)) /* USB FIFO data */ +#define USB_EP_FDAT1(x) (0x3D + (x*0x30)) /* USB FIFO data */ +#define USB_EP_FDAT2(x) (0x3E + (x*0x30)) /* USB FIFO data */ +#define USB_EP_FDAT3(x) (0x3F + (x*0x30)) /* USB FIFO data */ +#define USB_EP_FSTAT(x) (0x40 + (x*0x30)) /* USB FIFO status */ +#define USB_EP_FCTRL(x) (0x44 + (x*0x30)) /* USB FIFO control */ +#define USB_EP_LRFP(x) (0x48 + (x*0x30)) /* USB last read frame pointer */ +#define USB_EP_LWFP(x) (0x4C + (x*0x30)) /* USB last write frame pointer */ +#define USB_EP_FALRM(x) (0x50 + (x*0x30)) /* USB FIFO alarm */ +#define USB_EP_FRDP(x) (0x54 + (x*0x30)) /* USB FIFO read pointer */ +#define USB_EP_FWRP(x) (0x58 + (x*0x30)) /* USB FIFO write pointer */ +/* USB Control Register Bit Fields.*/ +#define CTRL_CMDOVER (1<<6) /* UDC status */ +#define CTRL_CMDERROR (1<<5) /* UDC status */ +#define CTRL_FE_ENA (1<<3) /* Enable Font End logic */ +#define CTRL_UDC_RST (1<<2) /* UDC reset */ +#define CTRL_AFE_ENA (1<<1) /* Analog Font end enable */ +#define CTRL_RESUME (1<<0) /* UDC resume */ +/* USB Status Register Bit Fields.*/ +#define STAT_RST (1<<8) +#define STAT_SUSP (1<<7) +#define STAT_CFG (3<<5) +#define STAT_INTF (3<<3) +#define STAT_ALTSET (7<<0) +/* USB Interrupt Status/Mask Registers Bit fields */ +#define INTR_WAKEUP (1<<31) /* Wake up Interrupt */ +#define INTR_MSOF (1<<7) /* Missed Start of Frame */ +#define INTR_SOF (1<<6) /* Start of Frame */ +#define INTR_RESET_STOP (1<<5) /* Reset Signaling stop */ +#define INTR_RESET_START (1<<4) /* Reset Signaling start */ +#define INTR_RESUME (1<<3) /* Suspend to resume */ +#define INTR_SUSPEND (1<<2) /* Active to suspend */ +#define INTR_FRAME_MATCH (1<<1) /* Frame matched */ +#define INTR_CFG_CHG (1<<0) /* Configuration change occurred */ +/* USB Enable Register Bit Fields.*/ +#define ENAB_RST (1<<31) /* Reset USB modules */ +#define ENAB_ENAB (1<<30) /* Enable USB modules*/ +#define ENAB_SUSPEND (1<<29) /* Suspend USB modules */ +#define ENAB_ENDIAN (1<<28) /* Endian of USB modules */ +#define ENAB_PWRMD (1<<0) /* Power mode of USB modules */ +/* USB Descriptor Ram Address Register bit fields */ +#define DADR_CFG (1<<31) /* Configuration */ +#define DADR_BSY (1<<30) /* Busy status */ +#define DADR_DADR (0x1FF) /* Descriptor Ram Address */ +/* USB Descriptor RAM/Endpoint Buffer Data Register bit fields */ +#define DDAT_DDAT (0xFF) /* Descriptor Endpoint Buffer */ +/* USB Endpoint Status Register bit fields */ +#define EPSTAT_BCOUNT (0x7F<<16) /* Endpoint FIFO byte count */ +#define EPSTAT_SIP (1<<8) /* Endpoint setup in progress */ +#define EPSTAT_DIR (1<<7) /* Endpoint transfer direction */ +#define EPSTAT_MAX (3<<5) /* Endpoint Max packet size */ +#define EPSTAT_TYP (3<<3) /* Endpoint type */ +#define EPSTAT_ZLPS (1<<2) /* Send zero length packet */ +#define EPSTAT_FLUSH (1<<1) /* Endpoint FIFO Flush */ +#define EPSTAT_STALL (1<<0) /* Force stall */ +/* USB Endpoint FIFO Status Register bit fields */ +#define FSTAT_FRAME_STAT (0xF<<24) /* Frame status bit [0-3] */ +#define FSTAT_ERR (1<<22) /* FIFO error */ +#define FSTAT_UF (1<<21) /* FIFO underflow */ +#define FSTAT_OF (1<<20) /* FIFO overflow */ +#define FSTAT_FR (1<<19) /* FIFO frame ready */ +#define FSTAT_FULL (1<<18) /* FIFO full */ +#define FSTAT_ALRM (1<<17) /* FIFO alarm */ +#define FSTAT_EMPTY (1<<16) /* FIFO empty */ +/* USB Endpoint FIFO Control Register bit fields */ +#define FCTRL_WFR (1<<29) /* Write frame end */ +/* USB Endpoint Interrupt Status Regsiter bit fields */ +#define EPINTR_FIFO_FULL (1<<8) /* fifo full */ +#define EPINTR_FIFO_EMPTY (1<<7) /* fifo empty */ +#define EPINTR_FIFO_ERROR (1<<6) /* fifo error */ +#define EPINTR_FIFO_HIGH (1<<5) /* fifo high */ +#define EPINTR_FIFO_LOW (1<<4) /* fifo low */ +#define EPINTR_MDEVREQ (1<<3) /* multi Device request */ +#define EPINTR_EOT (1<<2) /* fifo end of transfer */ +#define EPINTR_DEVREQ (1<<1) /* Device request */ +#define EPINTR_EOF (1<<0) /* fifo end of frame */ + +/* Debug macros */ +#ifdef DEBUG + +//#define DEBUG_IRQ +//#define DEBUG_REQ +//#define DEBUG_STATES +//#define DEBUG_TRX +//#define DEBUG_INIT +//#define DEBUG_EP0 +#define DEBUG_EPX + +#ifdef DEBUG_IRQ + static void dump_ep_intr(const char *label, int nr, int irqreg, struct device *dev) + { + dev_dbg(dev, "<%s> EP%d_INTR=[%s%s%s%s%s%s%s%s%s]\n", label, nr, + (irqreg & EPINTR_FIFO_FULL) ? " full" : "", + (irqreg & EPINTR_FIFO_EMPTY) ? " fempty" : "", + (irqreg & EPINTR_FIFO_ERROR) ? " ferr" : "", + (irqreg & EPINTR_FIFO_HIGH) ? " fhigh" : "", + (irqreg & EPINTR_FIFO_LOW) ? " flow" : "", + (irqreg & EPINTR_MDEVREQ) ? " mreq" : "", + (irqreg & EPINTR_EOF) ? " eof" : "", + (irqreg & EPINTR_DEVREQ) ? " devreq" : "", + (irqreg & EPINTR_EOT) ? " eot" : ""); + } + + static void dump_intr(const char *label, int irqreg, struct device *dev) + { + dev_dbg(dev, "<%s> USB_INTR=[%s%s%s%s%s%s%s%s%s]\n", label, + (irqreg & INTR_WAKEUP) ? " wake" : "", + (irqreg & INTR_MSOF) ? " msof" : "", + (irqreg & INTR_SOF) ? " sof" : "", + (irqreg & INTR_RESUME) ? " resume" : "", + (irqreg & INTR_SUSPEND) ? " suspend" : "", + (irqreg & INTR_RESET_STOP) ? " noreset" : "", + (irqreg & INTR_RESET_START) ? " reset" : "", + (irqreg & INTR_FRAME_MATCH) ? " fmatch" : "", + (irqreg & INTR_CFG_CHG) ? " config" : ""); + } + #define D_IRQ(dev, args...) dev_dbg(dev, ## args) +#else + #define dump_ep_intr(x, y, z, i) do {} while (0) + #define dump_intr(x, y, z) do {} while (0) + #define D_IRQ(dev, args...) do {} while (0) +#endif /* DEBUG_IRQ */ + +#ifdef DEBUG_REQ + static void dump_req(const char *label, struct imx_ep_struct *imx_ep, struct usb_request *req) + { + int i; + + if (!req || !req->buf) { + dev_dbg(imx_ep->imx_usb->dev, "<%s> req or req buf is free\n", label); + return; + } + + if ((!EP_NO(imx_ep) && imx_ep->imx_usb->ep0state == EP0_IN_DATA_PHASE) + || (EP_NO(imx_ep) && EP_DIR(imx_ep))) { + + dev_dbg(imx_ep->imx_usb->dev, "<%s> ep%d IN request for [%d] bytes; dump <", label, EP_NO(imx_ep), req->length); + for (i = 0; i < req->length; i++) + printk("%02x-", *((u8 *)req->buf + i)); + printk(">\n"); + } else + dev_dbg(imx_ep->imx_usb->dev, "<%s> ep%d OUT request for [%d] bytes\n", label, EP_NO(imx_ep), req->length); + } + #define D_REQ(dev, args...) dev_dbg(dev, ## args) +#else + #define dump_req(x, y, z) do {} while (0) + #define D_REQ(dev, args...) do {} while (0) +#endif /* DEBUG_REQ */ + +#ifdef DEBUG_STATES + static const char *state_name[] = { + "EP0_IDLE", + "EP0_IN_DATA_PHASE", + "EP0_OUT_DATA_PHASE", + "EP0_END_XFER", + "EP0_STALL" + }; + + static void dump_usb_stat(const char *label, struct imx_udc_struct *imx_usb) + { + int temp = __raw_readl(imx_usb->base + USB_STAT); + + dev_dbg(imx_usb->dev, + "<%s> USB_STAT=[%s%s CFG=%d, INTF=%d, ALTR=%d]\n", label, + (temp & STAT_RST) ? " reset" : "", + (temp & STAT_SUSP) ? " suspend" : "", + (temp & STAT_CFG) >> 5, + (temp & STAT_INTF) >> 3, + (temp & STAT_ALTSET)); + } + + static void dump_ep_stat(const char *label, struct imx_ep_struct *imx_ep) + { + int temp = __raw_readl(imx_ep->imx_usb->base + USB_EP_INTR(EP_NO(imx_ep))); + + dev_dbg(imx_ep->imx_usb->dev, + "<%s> EP%d_INTR=[%s%s%s%s%s%s%s%s%s]\n", label, EP_NO(imx_ep), + (temp & EPINTR_FIFO_FULL) ? " full" : "", + (temp & EPINTR_FIFO_EMPTY) ? " fempty" : "", + (temp & EPINTR_FIFO_ERROR) ? " ferr" : "", + (temp & EPINTR_FIFO_HIGH) ? " fhigh" : "", + (temp & EPINTR_FIFO_LOW) ? " flow" : "", + (temp & EPINTR_MDEVREQ) ? " mreq" : "", + (temp & EPINTR_EOF) ? " eof" : "", + (temp & EPINTR_DEVREQ) ? " devreq" : "", + (temp & EPINTR_EOT) ? " eot" : ""); + + temp = __raw_readl(imx_ep->imx_usb->base + USB_EP_STAT(EP_NO(imx_ep))); + + dev_dbg(imx_ep->imx_usb->dev, + "<%s> EP%d_STAT=[%s%s bcount=%d]\n", label, EP_NO(imx_ep), + (temp & EPSTAT_SIP) ? " sip" : "", + (temp & EPSTAT_STALL) ? " stall" : "", + (temp & EPSTAT_BCOUNT) >> 16); + + temp = __raw_readl(imx_ep->imx_usb->base + USB_EP_FSTAT(EP_NO(imx_ep))); + + dev_dbg(imx_ep->imx_usb->dev, + "<%s> EP%d_FSTAT=[%s%s%s%s%s%s%s]\n", label, EP_NO(imx_ep), + (temp & FSTAT_ERR) ? " ferr" : "", + (temp & FSTAT_UF) ? " funder" : "", + (temp & FSTAT_OF) ? " fover" : "", + (temp & FSTAT_FR) ? " fready" : "", + (temp & FSTAT_FULL) ? " ffull" : "", + (temp & FSTAT_ALRM) ? " falarm" : "", + (temp & FSTAT_EMPTY) ? " fempty" : ""); + } + #define D_STT(dev, args...) dev_dbg(dev, ## args) +#else + #define dump_ep_stat(x, y) do {} while (0) + #define dump_usb_stat(x, y) do {} while (0) + #define D_STT(dev, args...) do {} while (0) +#endif /* DEBUG_STATES */ + +#ifdef DEBUG_TRX + #define D_TRX(dev, args...) dev_dbg(dev, ## args) +#else + #define D_TRX(dev, args...) do {} while (0) +#endif /* DEBUG_TRX */ + +#ifdef DEBUG_INIT + #define D_INI(dev, args...) dev_dbg(dev, ## args) +#else + #define D_INI(dev, args...) do {} while (0) +#endif /* DEBUG_INIT */ + +#ifdef DEBUG_EP0 + #define D_EP0(dev, args...) dev_dbg(dev, ## args) +#else + #define D_EP0(dev, args...) do {} while (0) +#endif /* DEBUG_EP0 */ + +#ifdef DEBUG_EPX + #define D_EPX(dev, args...) dev_dbg(dev, ## args) +#else + #define D_EPX(dev, args...) do {} while (0) +#endif /* DEBUG_EP0 */ + +#endif /* DEBUG */ + +#endif /* __LINUX_USB_GADGET_IMX_H */ diff --git a/drivers/usb/gadget/lh7a40x_udc.c b/drivers/usb/gadget/lh7a40x_udc.c index c6e7df04c69a..d554b0895603 100644 --- a/drivers/usb/gadget/lh7a40x_udc.c +++ b/drivers/usb/gadget/lh7a40x_udc.c @@ -1981,7 +1981,7 @@ static struct lh7a40x_udc memory = { .ep0 = &memory.ep[0].ep, .name = driver_name, .dev = { - .bus_id = "gadget", + .init_name = "gadget", .release = nop_release, }, }, diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/m66592-udc.c index 77b44fb48f0a..43dcf9e1af6b 100644 --- a/drivers/usb/gadget/m66592-udc.c +++ b/drivers/usb/gadget/m66592-udc.c @@ -623,7 +623,6 @@ static void start_ep0(struct m66592_ep *ep, struct m66592_request *req) #if defined(CONFIG_SUPERH_BUILT_IN_M66592) static void init_controller(struct m66592 *m66592) { - usbf_start_clock(); m66592_bset(m66592, M66592_HSE, M66592_SYSCFG); /* High spd */ m66592_bclr(m66592, M66592_USBE, M66592_SYSCFG); m66592_bclr(m66592, M66592_DPRPU, M66592_SYSCFG); @@ -671,9 +670,7 @@ static void init_controller(struct m66592 *m66592) static void disable_controller(struct m66592 *m66592) { -#if defined(CONFIG_SUPERH_BUILT_IN_M66592) - usbf_stop_clock(); -#else +#if !defined(CONFIG_SUPERH_BUILT_IN_M66592) m66592_bclr(m66592, M66592_SCKE, M66592_SYSCFG); udelay(1); m66592_bclr(m66592, M66592_PLLC, M66592_SYSCFG); @@ -686,9 +683,7 @@ static void disable_controller(struct m66592 *m66592) static void m66592_start_xclock(struct m66592 *m66592) { -#if defined(CONFIG_SUPERH_BUILT_IN_M66592) - usbf_start_clock(); -#else +#if !defined(CONFIG_SUPERH_BUILT_IN_M66592) u16 tmp; tmp = m66592_read(m66592, M66592_SYSCFG); @@ -1539,7 +1534,10 @@ static int __exit m66592_remove(struct platform_device *pdev) iounmap(m66592->reg); free_irq(platform_get_irq(pdev, 0), m66592); m66592_free_request(&m66592->ep[0].ep, m66592->ep0_req); - usbf_stop_clock(); +#if defined(CONFIG_SUPERH_BUILT_IN_M66592) && defined(CONFIG_HAVE_CLK) + clk_disable(m66592->clk); + clk_put(m66592->clk); +#endif kfree(m66592); return 0; } @@ -1548,22 +1546,22 @@ static void nop_completion(struct usb_ep *ep, struct usb_request *r) { } -#define resource_len(r) (((r)->end - (r)->start) + 1) - static int __init m66592_probe(struct platform_device *pdev) { struct resource *res; int irq; void __iomem *reg = NULL; struct m66592 *m66592 = NULL; +#if defined(CONFIG_SUPERH_BUILT_IN_M66592) && defined(CONFIG_HAVE_CLK) + char clk_name[8]; +#endif int ret = 0; int i; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - (char *)udc_name); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { ret = -ENODEV; - pr_err("platform_get_resource_byname error.\n"); + pr_err("platform_get_resource error.\n"); goto clean_up; } @@ -1574,7 +1572,7 @@ static int __init m66592_probe(struct platform_device *pdev) goto clean_up; } - reg = ioremap(res->start, resource_len(res)); + reg = ioremap(res->start, resource_size(res)); if (reg == NULL) { ret = -ENOMEM; pr_err("ioremap error.\n"); @@ -1614,6 +1612,16 @@ static int __init m66592_probe(struct platform_device *pdev) goto clean_up; } +#if defined(CONFIG_SUPERH_BUILT_IN_M66592) && defined(CONFIG_HAVE_CLK) + snprintf(clk_name, sizeof(clk_name), "usbf%d", pdev->id); + m66592->clk = clk_get(&pdev->dev, clk_name); + if (IS_ERR(m66592->clk)) { + dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name); + ret = PTR_ERR(m66592->clk); + goto clean_up2; + } + clk_enable(m66592->clk); +#endif INIT_LIST_HEAD(&m66592->gadget.ep_list); m66592->gadget.ep0 = &m66592->ep[0].ep; INIT_LIST_HEAD(&m66592->gadget.ep0->ep_list); @@ -1645,7 +1653,7 @@ static int __init m66592_probe(struct platform_device *pdev) m66592->ep0_req = m66592_alloc_request(&m66592->ep[0].ep, GFP_KERNEL); if (m66592->ep0_req == NULL) - goto clean_up2; + goto clean_up3; m66592->ep0_req->complete = nop_completion; init_controller(m66592); @@ -1653,7 +1661,12 @@ static int __init m66592_probe(struct platform_device *pdev) dev_info(&pdev->dev, "version %s\n", DRIVER_VERSION); return 0; +clean_up3: +#if defined(CONFIG_SUPERH_BUILT_IN_M66592) && defined(CONFIG_HAVE_CLK) + clk_disable(m66592->clk); + clk_put(m66592->clk); clean_up2: +#endif free_irq(irq, m66592); clean_up: if (m66592) { diff --git a/drivers/usb/gadget/m66592-udc.h b/drivers/usb/gadget/m66592-udc.h index f118f00f1466..286ce07e7960 100644 --- a/drivers/usb/gadget/m66592-udc.h +++ b/drivers/usb/gadget/m66592-udc.h @@ -23,6 +23,10 @@ #ifndef __M66592_UDC_H__ #define __M66592_UDC_H__ +#if defined(CONFIG_SUPERH_BUILT_IN_M66592) && defined(CONFIG_HAVE_CLK) +#include <linux/clk.h> +#endif + #define M66592_SYSCFG 0x00 #define M66592_XTAL 0xC000 /* b15-14: Crystal selection */ #define M66592_XTAL48 0x8000 /* 48MHz */ @@ -476,6 +480,9 @@ struct m66592_ep { struct m66592 { spinlock_t lock; void __iomem *reg; +#if defined(CONFIG_SUPERH_BUILT_IN_M66592) && defined(CONFIG_HAVE_CLK) + struct clk *clk; +#endif struct usb_gadget gadget; struct usb_gadget_driver *driver; @@ -604,26 +611,6 @@ static inline void m66592_mdfy(struct m66592 *m66592, u16 val, u16 pat, #define m66592_bset(m66592, val, offset) \ m66592_mdfy(m66592, val, 0, offset) -#if defined(CONFIG_SUPERH_BUILT_IN_M66592) -#include <asm/io.h> -#define MSTPCR2 0xA4150038 /* for SH7722 */ -#define MSTPCR2_USB 0x00000800 - -static inline void usbf_start_clock(void) -{ - ctrl_outl(ctrl_inl(MSTPCR2) & ~MSTPCR2_USB, MSTPCR2); -} - -static inline void usbf_stop_clock(void) -{ - ctrl_outl(ctrl_inl(MSTPCR2) | MSTPCR2_USB, MSTPCR2); -} - -#else -#define usbf_start_clock(x) -#define usbf_stop_clock(x) -#endif /* if defined(CONFIG_SUPERH_BUILT_IN_M66592) */ - #endif /* ifndef __M66592_UDC_H__ */ diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index 8ae70de2c37d..12c6d83b218c 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -669,7 +669,7 @@ fill_dma_desc (struct net2280_ep *ep, struct net2280_request *req, int valid) /* 2280 may be polling VALID_BIT through ep->dma->dmadesc */ wmb (); - td->dmacount = cpu_to_le32p (&dmacount); + td->dmacount = cpu_to_le32(dmacount); } static const u32 dmactl_default = diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index 34e9e393f929..57d9641c6bf8 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -3006,7 +3006,7 @@ cleanup1: cleanup0: if (xceiv) - put_device(xceiv->dev); + otg_put_transceiver(xceiv); if (cpu_is_omap16xx() || cpu_is_omap24xx()) { clk_disable(hhc_clk); @@ -3034,7 +3034,7 @@ static int __exit omap_udc_remove(struct platform_device *pdev) pullup_disable(udc); if (udc->transceiver) { - put_device(udc->transceiver->dev); + otg_put_transceiver(udc->transceiver); udc->transceiver = NULL; } omap_writew(0, UDC_SYSCON1); diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index da6e93c201d2..9b36205c5759 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -141,7 +141,11 @@ static int is_vbus_present(void) if (mach->gpio_vbus) { int value = gpio_get_value(mach->gpio_vbus); - return mach->gpio_vbus_inverted ? !value : value; + + if (mach->gpio_vbus_inverted) + return !value; + else + return !!value; } if (mach->udc_is_connected) return mach->udc_is_connected(); @@ -982,7 +986,7 @@ static int pxa25x_udc_vbus_session(struct usb_gadget *_gadget, int is_active) struct pxa25x_udc *udc; udc = container_of(_gadget, struct pxa25x_udc, gadget); - udc->vbus = (is_active != 0); + udc->vbus = is_active; DMSG("vbus %s\n", is_active ? "supplied" : "inactive"); pullup(udc); return 0; @@ -1399,12 +1403,8 @@ lubbock_vbus_irq(int irq, void *_dev) static irqreturn_t udc_vbus_irq(int irq, void *_dev) { struct pxa25x_udc *dev = _dev; - int vbus = gpio_get_value(dev->mach->gpio_vbus); - if (dev->mach->gpio_vbus_inverted) - vbus = !vbus; - - pxa25x_udc_vbus_session(&dev->gadget, vbus); + pxa25x_udc_vbus_session(&dev->gadget, is_vbus_present()); return IRQ_HANDLED; } @@ -1833,7 +1833,7 @@ static struct pxa25x_udc memory = { .ep0 = &memory.ep[0].ep, .name = driver_name, .dev = { - .bus_id = "gadget", + .init_name = "gadget", .release = nop_release, }, }, @@ -2145,7 +2145,7 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev) if (irq < 0) return -ENODEV; - dev->clk = clk_get(&pdev->dev, "UDCCLK"); + dev->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(dev->clk)) { retval = PTR_ERR(dev->clk); goto err_clk; @@ -2198,7 +2198,7 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev) udc_disable(dev); udc_reinit(dev); - dev->vbus = is_vbus_present(); + dev->vbus = !!is_vbus_present(); /* irq setup after old hardware state is cleaned up */ retval = request_irq(irq, pxa25x_udc_irq, diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index caa37c95802c..990f40f988d4 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -430,7 +430,6 @@ static void pio_irq_enable(struct pxa_ep *ep) /** * pio_irq_disable - Disables irq generation for one endpoint * @ep: udc endpoint - * @index: endpoint number */ static void pio_irq_disable(struct pxa_ep *ep) { @@ -586,7 +585,6 @@ static void inc_ep_stats_reqs(struct pxa_ep *ep, int is_in) * inc_ep_stats_bytes - Update ep stats counts * @ep: physical endpoint * @count: bytes transfered on endpoint - * @req: usb request * @is_in: ep direction (USB_DIR_IN or 0) */ static void inc_ep_stats_bytes(struct pxa_ep *ep, int count, int is_in) @@ -2162,7 +2160,7 @@ static struct pxa_udc memory = { .ep0 = &memory.udc_usb_ep[0].usb_ep, .name = driver_name, .dev = { - .bus_id = "gadget", + .init_name = "gadget", }, }, @@ -2226,7 +2224,7 @@ static int __init pxa_udc_probe(struct platform_device *pdev) udc->dev = &pdev->dev; udc->mach = pdev->dev.platform_data; - udc->clk = clk_get(&pdev->dev, "UDCCLK"); + udc->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(udc->clk)) { retval = PTR_ERR(udc->clk); goto err_clk; diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c index 00ba06b44752..9a2b8920532d 100644 --- a/drivers/usb/gadget/s3c2410_udc.c +++ b/drivers/usb/gadget/s3c2410_udc.c @@ -36,6 +36,7 @@ #include <linux/interrupt.h> #include <linux/platform_device.h> #include <linux/clk.h> +#include <linux/gpio.h> #include <linux/debugfs.h> #include <linux/seq_file.h> @@ -51,10 +52,9 @@ #include <mach/irqs.h> #include <mach/hardware.h> -#include <mach/regs-gpio.h> -#include <asm/plat-s3c24xx/regs-udc.h> -#include <asm/plat-s3c24xx/udc.h> +#include <plat/regs-udc.h> +#include <plat/udc.h> #include "s3c2410_udc.h" @@ -1510,11 +1510,7 @@ static irqreturn_t s3c2410_udc_vbus_irq(int irq, void *_dev) dprintk(DEBUG_NORMAL, "%s()\n", __func__); - /* some cpus cannot read from an line configured to IRQ! */ - s3c2410_gpio_cfgpin(udc_info->vbus_pin, S3C2410_GPIO_INPUT); - value = s3c2410_gpio_getpin(udc_info->vbus_pin); - s3c2410_gpio_cfgpin(udc_info->vbus_pin, S3C2410_GPIO_SFN2); - + value = gpio_get_value(udc_info->vbus_pin) ? 1 : 0; if (udc_info->vbus_pin_inverted) value = !value; @@ -1727,7 +1723,7 @@ static struct s3c2410_udc memory = { .ep0 = &memory.ep[0].ep, .name = gadget_name, .dev = { - .bus_id = "gadget", + .init_name = "gadget", }, }, @@ -1802,7 +1798,7 @@ static int s3c2410_udc_probe(struct platform_device *pdev) struct s3c2410_udc *udc = &memory; struct device *dev = &pdev->dev; int retval; - unsigned int irq; + int irq; dev_dbg(dev, "%s()\n", __func__); @@ -1861,7 +1857,7 @@ static int s3c2410_udc_probe(struct platform_device *pdev) /* irq setup after old hardware state is cleaned up */ retval = request_irq(IRQ_USBD, s3c2410_udc_irq, - IRQF_DISABLED, gadget_name, udc); + IRQF_DISABLED, gadget_name, udc); if (retval != 0) { dev_err(dev, "cannot get irq %i, err %d\n", IRQ_USBD, retval); @@ -1872,17 +1868,28 @@ static int s3c2410_udc_probe(struct platform_device *pdev) dev_dbg(dev, "got irq %i\n", IRQ_USBD); if (udc_info && udc_info->vbus_pin > 0) { - irq = s3c2410_gpio_getirq(udc_info->vbus_pin); + retval = gpio_request(udc_info->vbus_pin, "udc vbus"); + if (retval < 0) { + dev_err(dev, "cannot claim vbus pin\n"); + goto err_int; + } + + irq = gpio_to_irq(udc_info->vbus_pin); + if (irq < 0) { + dev_err(dev, "no irq for gpio vbus pin\n"); + goto err_gpio_claim; + } + retval = request_irq(irq, s3c2410_udc_vbus_irq, IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED, gadget_name, udc); if (retval != 0) { - dev_err(dev, "can't get vbus irq %i, err %d\n", + dev_err(dev, "can't get vbus irq %d, err %d\n", irq, retval); retval = -EBUSY; - goto err_int; + goto err_gpio_claim; } dev_dbg(dev, "got irq %i\n", irq); @@ -1902,6 +1909,9 @@ static int s3c2410_udc_probe(struct platform_device *pdev) return 0; +err_gpio_claim: + if (udc_info && udc_info->vbus_pin > 0) + gpio_free(udc_info->vbus_pin); err_int: free_irq(IRQ_USBD, udc); err_map: @@ -1927,7 +1937,7 @@ static int s3c2410_udc_remove(struct platform_device *pdev) debugfs_remove(udc->regs_info); if (udc_info && udc_info->vbus_pin > 0) { - irq = s3c2410_gpio_getirq(udc_info->vbus_pin); + irq = gpio_to_irq(udc_info->vbus_pin); free_irq(irq, udc); } diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 56f592dc0b36..2b476b6b3d4d 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -96,6 +96,19 @@ config USB_EHCI_HCD_PPC_OF Enables support for the USB controller present on the PowerPC OpenFirmware platform bus. +config USB_OXU210HP_HCD + tristate "OXU210HP HCD support" + depends on USB + ---help--- + The OXU210HP is an USB host/OTG/device controller. Enable this + option if your board has this chip. If unsure, say N. + + This driver does not support isochronous transfers and doesn't + implement OTG nor USB device controllers. + + To compile this driver as a module, choose M here: the + module will be called oxu210hp-hcd. + config USB_ISP116X_HCD tristate "ISP116X HCD support" depends on USB @@ -110,29 +123,18 @@ config USB_ISP116X_HCD config USB_ISP1760_HCD tristate "ISP 1760 HCD support" - depends on USB && EXPERIMENTAL + depends on USB && EXPERIMENTAL && (PCI || PPC_OF) ---help--- The ISP1760 chip is a USB 2.0 host controller. This driver does not support isochronous transfers or OTG. + This USB controller is usually attached to a non-DMA-Master + capable bus. NXP's eval kit brings this chip on PCI card + where the chip itself is behind a PLB to simulate such + a bus. To compile this driver as a module, choose M here: the - module will be called isp1760-hcd. - -config USB_ISP1760_PCI - bool "Support for the PCI bus" - depends on USB_ISP1760_HCD && PCI - ---help--- - Enables support for the device present on the PCI bus. - This should only be required if you happen to have the eval kit from - NXP and you are going to test it. - -config USB_ISP1760_OF - bool "Support for the OF platform bus" - depends on USB_ISP1760_HCD && PPC_OF - ---help--- - Enables support for the device present on the PowerPC - OpenFirmware platform bus. + module will be called isp1760. config USB_OHCI_HCD tristate "OHCI HCD support" diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 23be22224044..e5f3f20787e4 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_USB_WHCI_HCD) += whci/ obj-$(CONFIG_PCI) += pci-quirks.o obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o +obj-$(CONFIG_USB_OXU210HP_HCD) += oxu210hp-hcd.o obj-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index 0cb53ca8d343..7f4ace73d44a 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -455,9 +455,7 @@ static void qh_lines ( (scratch >> 16) & 0x7fff, scratch, td->urb); - if (temp < 0) - temp = 0; - else if (size < temp) + if (size < temp) temp = size; size -= temp; next += temp; @@ -466,9 +464,7 @@ static void qh_lines ( } temp = snprintf (next, size, "\n"); - if (temp < 0) - temp = 0; - else if (size < temp) + if (size < temp) temp = size; size -= temp; next += temp; diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 15a803b206b8..4725d15d096f 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -643,7 +643,7 @@ static int ehci_run (struct usb_hcd *hcd) static irqreturn_t ehci_irq (struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); - u32 status, pcd_status = 0, cmd; + u32 status, masked_status, pcd_status = 0, cmd; int bh; spin_lock (&ehci->lock); @@ -656,14 +656,14 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) goto dead; } - status &= INTR_MASK; - if (!status) { /* irq sharing? */ + masked_status = status & INTR_MASK; + if (!masked_status) { /* irq sharing? */ spin_unlock(&ehci->lock); return IRQ_NONE; } /* clear (just) interrupts */ - ehci_writel(ehci, status, &ehci->regs->status); + ehci_writel(ehci, masked_status, &ehci->regs->status); cmd = ehci_readl(ehci, &ehci->regs->command); bh = 0; @@ -734,18 +734,17 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) /* PCI errors [4.15.2.4] */ if (unlikely ((status & STS_FATAL) != 0)) { + ehci_err(ehci, "fatal error\n"); dbg_cmd(ehci, "fatal", cmd); dbg_status(ehci, "fatal", status); - if (status & STS_HALT) { - ehci_err (ehci, "fatal error\n"); + ehci_halt(ehci); dead: - ehci_reset (ehci); - ehci_writel(ehci, 0, &ehci->regs->configured_flag); - /* generic layer kills/unlinks all urbs, then - * uses ehci_stop to clean up the rest - */ - bh = 1; - } + ehci_reset(ehci); + ehci_writel(ehci, 0, &ehci->regs->configured_flag); + /* generic layer kills/unlinks all urbs, then + * uses ehci_stop to clean up the rest + */ + bh = 1; } if (bh) diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 218f9660d7ee..97a53a48a3d8 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -194,6 +194,7 @@ static int ehci_bus_resume (struct usb_hcd *hcd) u32 temp; u32 power_okay; int i; + u8 resume_needed = 0; if (time_before (jiffies, ehci->next_statechange)) msleep(5); @@ -228,7 +229,9 @@ static int ehci_bus_resume (struct usb_hcd *hcd) /* Some controller/firmware combinations need a delay during which * they set up the port statuses. See Bugzilla #8190. */ - mdelay(8); + spin_unlock_irq(&ehci->lock); + msleep(8); + spin_lock_irq(&ehci->lock); /* manually resume the ports we suspended during bus_suspend() */ i = HCS_N_PORTS (ehci->hcs_params); @@ -236,12 +239,21 @@ static int ehci_bus_resume (struct usb_hcd *hcd) temp = ehci_readl(ehci, &ehci->regs->port_status [i]); temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); if (test_bit(i, &ehci->bus_suspended) && - (temp & PORT_SUSPEND)) + (temp & PORT_SUSPEND)) { temp |= PORT_RESUME; + resume_needed = 1; + } ehci_writel(ehci, temp, &ehci->regs->port_status [i]); } + + /* msleep for 20ms only if code is trying to resume port */ + if (resume_needed) { + spin_unlock_irq(&ehci->lock); + msleep(20); + spin_lock_irq(&ehci->lock); + } + i = HCS_N_PORTS (ehci->hcs_params); - mdelay (20); while (i--) { temp = ehci_readl(ehci, &ehci->regs->port_status [i]); if (test_bit(i, &ehci->bus_suspended) && @@ -422,8 +434,15 @@ static int check_reset_complete ( port_status &= ~PORT_RWC_BITS; ehci_writel(ehci, port_status, status_reg); - } else + /* ensure 440EPX ohci controller state is operational */ + if (ehci->has_amcc_usb23) + set_ohci_hcfs(ehci, 1); + } else { ehci_dbg (ehci, "port %d high speed\n", index + 1); + /* ensure 440EPx ohci controller state is suspended */ + if (ehci->has_amcc_usb23) + set_ohci_hcfs(ehci, 0); + } return port_status; } diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index c46a58f9181d..36864f958444 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -66,6 +66,8 @@ static int ehci_pci_setup(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); struct pci_dev *pdev = to_pci_dev(hcd->self.controller); + struct pci_dev *p_smbus; + u8 rev; u32 temp; int retval; @@ -166,6 +168,28 @@ static int ehci_pci_setup(struct usb_hcd *hcd) pci_write_config_byte(pdev, 0x4b, tmp | 0x20); } break; + case PCI_VENDOR_ID_ATI: + /* SB600 and old version of SB700 have a bug in EHCI controller, + * which causes usb devices lose response in some cases. + */ + if ((pdev->device == 0x4386) || (pdev->device == 0x4396)) { + p_smbus = pci_get_device(PCI_VENDOR_ID_ATI, + PCI_DEVICE_ID_ATI_SBX00_SMBUS, + NULL); + if (!p_smbus) + break; + rev = p_smbus->revision; + if ((pdev->device == 0x4386) || (rev == 0x3a) + || (rev == 0x3b)) { + u8 tmp; + ehci_info(ehci, "applying AMD SB600/SB700 USB " + "freeze workaround\n"); + pci_read_config_byte(pdev, 0x53, &tmp); + pci_write_config_byte(pdev, 0x53, tmp | (1<<3)); + } + pci_dev_put(p_smbus); + } + break; } ehci_reset(ehci); diff --git a/drivers/usb/host/ehci-ppc-of.c b/drivers/usb/host/ehci-ppc-of.c index b018deed2e8f..ef732b704f53 100644 --- a/drivers/usb/host/ehci-ppc-of.c +++ b/drivers/usb/host/ehci-ppc-of.c @@ -107,11 +107,13 @@ ehci_hcd_ppc_of_probe(struct of_device *op, const struct of_device_id *match) { struct device_node *dn = op->node; struct usb_hcd *hcd; - struct ehci_hcd *ehci; + struct ehci_hcd *ehci = NULL; struct resource res; int irq; int rv; + struct device_node *np; + if (usb_disabled()) return -ENODEV; @@ -149,6 +151,20 @@ ehci_hcd_ppc_of_probe(struct of_device *op, const struct of_device_id *match) } ehci = hcd_to_ehci(hcd); + np = of_find_compatible_node(NULL, NULL, "ibm,usb-ohci-440epx"); + if (np != NULL) { + /* claim we really affected by usb23 erratum */ + if (!of_address_to_resource(np, 0, &res)) + ehci->ohci_hcctrl_reg = ioremap(res.start + + OHCI_HCCTRL_OFFSET, OHCI_HCCTRL_LEN); + else + pr_debug(__FILE__ ": no ohci offset in fdt\n"); + if (!ehci->ohci_hcctrl_reg) { + pr_debug(__FILE__ ": ioremap for ohci hcctrl failed\n"); + } else { + ehci->has_amcc_usb23 = 1; + } + } if (of_get_property(dn, "big-endian", NULL)) { ehci->big_endian_mmio = 1; @@ -181,6 +197,9 @@ err_ioremap: irq_dispose_mapping(irq); err_irq: release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + + if (ehci->has_amcc_usb23) + iounmap(ehci->ohci_hcctrl_reg); err_rmr: usb_put_hcd(hcd); @@ -191,6 +210,11 @@ err_rmr: static int ehci_hcd_ppc_of_remove(struct of_device *op) { struct usb_hcd *hcd = dev_get_drvdata(&op->dev); + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + + struct device_node *np; + struct resource res; + dev_set_drvdata(&op->dev, NULL); dev_dbg(&op->dev, "stopping PPC-OF USB Controller\n"); @@ -201,6 +225,25 @@ static int ehci_hcd_ppc_of_remove(struct of_device *op) irq_dispose_mapping(hcd->irq); release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + /* use request_mem_region to test if the ohci driver is loaded. if so + * ensure the ohci core is operational. + */ + if (ehci->has_amcc_usb23) { + np = of_find_compatible_node(NULL, NULL, "ibm,usb-ohci-440epx"); + if (np != NULL) { + if (!of_address_to_resource(np, 0, &res)) + if (!request_mem_region(res.start, + 0x4, hcd_name)) + set_ohci_hcfs(ehci, 1); + else + release_mem_region(res.start, 0x4); + else + pr_debug(__FILE__ ": no ohci offset in fdt\n"); + of_node_put(np); + } + + iounmap(ehci->ohci_hcctrl_reg); + } usb_put_hcd(hcd); return 0; diff --git a/drivers/usb/host/ehci-ps3.c b/drivers/usb/host/ehci-ps3.c index 0eba894bcb01..9c9da35abc6c 100644 --- a/drivers/usb/host/ehci-ps3.c +++ b/drivers/usb/host/ehci-ps3.c @@ -205,6 +205,7 @@ static int ps3_ehci_remove(struct ps3_system_bus_device *dev) tmp = hcd->irq; + ehci_shutdown(hcd); usb_remove_hcd(hcd); ps3_system_bus_set_driver_data(dev, NULL); diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 4a0c5a78b2ed..a081ee65bde6 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -918,7 +918,7 @@ iso_stream_init ( */ stream->usecs = HS_USECS_ISO (maxp); bandwidth = stream->usecs * 8; - bandwidth /= 1 << (interval - 1); + bandwidth /= interval; } else { u32 addr; @@ -951,7 +951,7 @@ iso_stream_init ( } else stream->raw_mask = smask_out [hs_transfers - 1]; bandwidth = stream->usecs + stream->c_usecs; - bandwidth /= 1 << (interval + 2); + bandwidth /= interval << 3; /* stream->splits gets created from raw_mask later */ stream->address = cpu_to_hc32(ehci, addr); diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index b11798d17ae5..fb7054ccf4fc 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -120,6 +120,16 @@ struct ehci_hcd { /* one per controller */ unsigned has_fsl_port_bug:1; /* FreeScale */ unsigned big_endian_mmio:1; unsigned big_endian_desc:1; + unsigned has_amcc_usb23:1; + + /* required for usb32 quirk */ + #define OHCI_CTRL_HCFS (3 << 6) + #define OHCI_USB_OPER (2 << 6) + #define OHCI_USB_SUSPEND (3 << 6) + + #define OHCI_HCCTRL_OFFSET 0x4 + #define OHCI_HCCTRL_LEN 0x4 + __hc32 *ohci_hcctrl_reg; u8 sbrn; /* packed release number */ @@ -183,16 +193,14 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action) * the async ring; just the I/O watchdog. Note that if a * SHRINK were pending, OFF would never be requested. */ - enum ehci_timer_action oldactions = ehci->actions; + if (timer_pending(&ehci->watchdog) + && ((BIT(TIMER_ASYNC_SHRINK) | BIT(TIMER_ASYNC_OFF)) + & ehci->actions)) + return; if (!test_and_set_bit (action, &ehci->actions)) { unsigned long t; - if (timer_pending(&ehci->watchdog) - && ((BIT(TIMER_ASYNC_SHRINK) | BIT(TIMER_ASYNC_OFF)) - & oldactions)) - return; - switch (action) { case TIMER_IO_WATCHDOG: t = EHCI_IO_JIFFIES; @@ -208,7 +216,7 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action) t = DIV_ROUND_UP(EHCI_SHRINK_FRAMES * HZ, 1000) + 1; break; } - mod_timer(&ehci->watchdog, round_jiffies(t + jiffies)); + mod_timer(&ehci->watchdog, t + jiffies); } } @@ -638,6 +646,30 @@ static inline void ehci_writel(const struct ehci_hcd *ehci, #endif } +/* + * On certain ppc-44x SoC there is a HW issue, that could only worked around with + * explicit suspend/operate of OHCI. This function hereby makes sense only on that arch. + * Other common bits are dependant on has_amcc_usb23 quirk flag. + */ +#ifdef CONFIG_44x +static inline void set_ohci_hcfs(struct ehci_hcd *ehci, int operational) +{ + u32 hc_control; + + hc_control = (readl_be(ehci->ohci_hcctrl_reg) & ~OHCI_CTRL_HCFS); + if (operational) + hc_control |= OHCI_USB_OPER; + else + hc_control |= OHCI_USB_SUSPEND; + + writel_be(hc_control, ehci->ohci_hcctrl_reg); + (void) readl_be(ehci->ohci_hcctrl_reg); +} +#else +static inline void set_ohci_hcfs(struct ehci_hcd *ehci, int operational) +{ } +#endif + /*-------------------------------------------------------------------------*/ /* diff --git a/drivers/usb/host/isp1760-if.c b/drivers/usb/host/isp1760-if.c index af849f596135..b87ca7cf4b37 100644 --- a/drivers/usb/host/isp1760-if.c +++ b/drivers/usb/host/isp1760-if.c @@ -14,16 +14,16 @@ #include "../core/hcd.h" #include "isp1760-hcd.h" -#ifdef CONFIG_USB_ISP1760_OF +#ifdef CONFIG_PPC_OF #include <linux/of.h> #include <linux/of_platform.h> #endif -#ifdef CONFIG_USB_ISP1760_PCI +#ifdef CONFIG_PCI #include <linux/pci.h> #endif -#ifdef CONFIG_USB_ISP1760_OF +#ifdef CONFIG_PPC_OF static int of_isp1760_probe(struct of_device *dev, const struct of_device_id *match) { @@ -128,7 +128,7 @@ static struct of_platform_driver isp1760_of_driver = { }; #endif -#ifdef CONFIG_USB_ISP1760_PCI +#ifdef CONFIG_PCI static u32 nxp_pci_io_base; static u32 iolength; static u32 pci_mem_phy0; @@ -288,28 +288,28 @@ static struct pci_driver isp1761_pci_driver = { static int __init isp1760_init(void) { - int ret = -ENODEV; + int ret; init_kmem_once(); -#ifdef CONFIG_USB_ISP1760_OF +#ifdef CONFIG_PPC_OF ret = of_register_platform_driver(&isp1760_of_driver); if (ret) { deinit_kmem_cache(); return ret; } #endif -#ifdef CONFIG_USB_ISP1760_PCI +#ifdef CONFIG_PCI ret = pci_register_driver(&isp1761_pci_driver); if (ret) goto unreg_of; #endif return ret; -#ifdef CONFIG_USB_ISP1760_PCI +#ifdef CONFIG_PCI unreg_of: #endif -#ifdef CONFIG_USB_ISP1760_OF +#ifdef CONFIG_PPC_OF of_unregister_platform_driver(&isp1760_of_driver); #endif deinit_kmem_cache(); @@ -319,10 +319,10 @@ module_init(isp1760_init); static void __exit isp1760_exit(void) { -#ifdef CONFIG_USB_ISP1760_OF +#ifdef CONFIG_PPC_OF of_unregister_platform_driver(&isp1760_of_driver); #endif -#ifdef CONFIG_USB_ISP1760_PCI +#ifdef CONFIG_PCI pci_unregister_driver(&isp1761_pci_driver); #endif deinit_kmem_cache(); diff --git a/drivers/usb/host/ohci-pnx4008.c b/drivers/usb/host/ohci-pnx4008.c index e306ca6aef3d..100bf3d8437c 100644 --- a/drivers/usb/host/ohci-pnx4008.c +++ b/drivers/usb/host/ohci-pnx4008.c @@ -106,65 +106,34 @@ extern int ocpi_enable(void); static struct clk *usb_clk; -static int isp1301_probe(struct i2c_adapter *adap); -static int isp1301_detach(struct i2c_client *client); - static const unsigned short normal_i2c[] = { ISP1301_I2C_ADDR, ISP1301_I2C_ADDR + 1, I2C_CLIENT_END }; -static const unsigned short dummy_i2c_addrlist[] = { I2C_CLIENT_END }; - -static struct i2c_client_address_data addr_data = { - .normal_i2c = normal_i2c, - .probe = dummy_i2c_addrlist, - .ignore = dummy_i2c_addrlist, -}; - -struct i2c_driver isp1301_driver = { - .driver = { - .name = "isp1301_pnx", - }, - .attach_adapter = isp1301_probe, - .detach_client = isp1301_detach, -}; -static int isp1301_attach(struct i2c_adapter *adap, int addr, int kind) +static int isp1301_probe(struct i2c_client *client, + const struct i2c_device_id *id) { - struct i2c_client *c; - int err; - - c = kzalloc(sizeof(*c), GFP_KERNEL); - if (!c) - return -ENOMEM; - - strlcpy(c->name, "isp1301_pnx", I2C_NAME_SIZE); - c->flags = 0; - c->addr = addr; - c->adapter = adap; - c->driver = &isp1301_driver; - - err = i2c_attach_client(c); - if (err) { - kfree(c); - return err; - } - - isp1301_i2c_client = c; - return 0; } -static int isp1301_probe(struct i2c_adapter *adap) +static int isp1301_remove(struct i2c_client *client) { - return i2c_probe(adap, &addr_data, isp1301_attach); -} - -static int isp1301_detach(struct i2c_client *client) -{ - i2c_detach_client(client); - kfree(isp1301_i2c_client); return 0; } +const struct i2c_device_id isp1301_id[] = { + { "isp1301_pnx", 0 }, + { } +}; + +struct i2c_driver isp1301_driver = { + .driver = { + .name = "isp1301_pnx", + }, + .probe = isp1301_probe, + .remove = isp1301_remove, + .id_table = isp1301_id, +}; + static void i2c_write(u8 buf, u8 subaddr) { char tmpbuf[2]; @@ -328,6 +297,8 @@ static int __devinit usb_hcd_pnx4008_probe(struct platform_device *pdev) struct usb_hcd *hcd = 0; struct ohci_hcd *ohci; const struct hc_driver *driver = &ohci_pnx4008_hc_driver; + struct i2c_adapter *i2c_adap; + struct i2c_board_info i2c_info; int ret = 0, irq; @@ -351,9 +322,20 @@ static int __devinit usb_hcd_pnx4008_probe(struct platform_device *pdev) ret = i2c_add_driver(&isp1301_driver); if (ret < 0) { - err("failed to connect I2C to ISP1301 USB Transceiver"); + err("failed to add ISP1301 driver"); goto out; } + i2c_adap = i2c_get_adapter(2); + memset(&i2c_info, 0, sizeof(struct i2c_board_info)); + strlcpy(i2c_info.name, "isp1301_pnx", I2C_NAME_SIZE); + isp1301_i2c_client = i2c_new_probed_device(i2c_adap, &i2c_info, + normal_i2c); + i2c_put_adapter(i2c_adap); + if (!isp1301_i2c_client) { + err("failed to connect I2C to ISP1301 USB Transceiver"); + ret = -ENODEV; + goto out_i2c_driver; + } isp1301_configure(); @@ -429,6 +411,9 @@ out3: out2: clk_put(usb_clk); out1: + i2c_unregister_client(isp1301_i2c_client); + isp1301_i2c_client = NULL; +out_i2c_driver: i2c_del_driver(&isp1301_driver); out: return ret; @@ -445,6 +430,8 @@ static int usb_hcd_pnx4008_remove(struct platform_device *pdev) pnx4008_unset_usb_bits(); clk_disable(usb_clk); clk_put(usb_clk); + i2c_unregister_client(isp1301_i2c_client); + isp1301_i2c_client = NULL; i2c_del_driver(&isp1301_driver); platform_set_drvdata(pdev, NULL); diff --git a/drivers/usb/host/ohci-ppc-of.c b/drivers/usb/host/ohci-ppc-of.c index 7ac53264ead3..68a301710297 100644 --- a/drivers/usb/host/ohci-ppc-of.c +++ b/drivers/usb/host/ohci-ppc-of.c @@ -91,6 +91,7 @@ ohci_hcd_ppc_of_probe(struct of_device *op, const struct of_device_id *match) int rv; int is_bigendian; + struct device_node *np; if (usb_disabled()) return -ENODEV; @@ -147,6 +148,30 @@ ohci_hcd_ppc_of_probe(struct of_device *op, const struct of_device_id *match) if (rv == 0) return 0; + /* by now, 440epx is known to show usb_23 erratum */ + np = of_find_compatible_node(NULL, NULL, "ibm,usb-ehci-440epx"); + + /* Work around - At this point ohci_run has executed, the + * controller is running, everything, the root ports, etc., is + * set up. If the ehci driver is loaded, put the ohci core in + * the suspended state. The ehci driver will bring it out of + * suspended state when / if a non-high speed USB device is + * attached to the USB Host port. If the ehci driver is not + * loaded, do nothing. request_mem_region is used to test if + * the ehci driver is loaded. + */ + if (np != NULL) { + if (!of_address_to_resource(np, 0, &res)) { + if (!request_mem_region(res.start, 0x4, hcd_name)) { + writel_be((readl_be(&ohci->regs->control) | + OHCI_USB_SUSPEND), &ohci->regs->control); + (void) readl_be(&ohci->regs->control); + } else + release_mem_region(res.start, 0x4); + } else + pr_debug(__FILE__ ": cannot get ehci offset from fdt\n"); + } + iounmap(hcd->regs); err_ioremap: irq_dispose_mapping(irq); diff --git a/drivers/usb/host/ohci-ps3.c b/drivers/usb/host/ohci-ps3.c index 2089d8a46c4b..3c1a3b5f89f1 100644 --- a/drivers/usb/host/ohci-ps3.c +++ b/drivers/usb/host/ohci-ps3.c @@ -192,7 +192,7 @@ fail_start: return result; } -static int ps3_ohci_remove (struct ps3_system_bus_device *dev) +static int ps3_ohci_remove(struct ps3_system_bus_device *dev) { unsigned int tmp; struct usb_hcd *hcd = @@ -205,6 +205,7 @@ static int ps3_ohci_remove (struct ps3_system_bus_device *dev) tmp = hcd->irq; + ohci_shutdown(hcd); usb_remove_hcd(hcd); ps3_system_bus_set_driver_data(dev, NULL); diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c index e294d430733b..e44dc2cbca24 100644 --- a/drivers/usb/host/ohci-pxa27x.c +++ b/drivers/usb/host/ohci-pxa27x.c @@ -296,7 +296,7 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device return -ENXIO; } - usb_clk = clk_get(&pdev->dev, "USBCLK"); + usb_clk = clk_get(&pdev->dev, NULL); if (IS_ERR(usb_clk)) return PTR_ERR(usb_clk); diff --git a/drivers/usb/host/ohci-tmio.c b/drivers/usb/host/ohci-tmio.c index f9f134af0bd1..8dabe8e31d8c 100644 --- a/drivers/usb/host/ohci-tmio.c +++ b/drivers/usb/host/ohci-tmio.c @@ -201,7 +201,7 @@ static int __devinit ohci_hcd_tmio_drv_probe(struct platform_device *dev) if (!cell) return -EINVAL; - hcd = usb_create_hcd(&ohci_tmio_hc_driver, &dev->dev, dev->dev.bus_id); + hcd = usb_create_hcd(&ohci_tmio_hc_driver, &dev->dev, dev_name(&dev->dev)); if (!hcd) { ret = -ENOMEM; goto err_usb_create_hcd; diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c new file mode 100644 index 000000000000..75548f7c716b --- /dev/null +++ b/drivers/usb/host/oxu210hp-hcd.c @@ -0,0 +1,3985 @@ +/* + * Copyright (c) 2008 Rodolfo Giometti <giometti@linux.it> + * Copyright (c) 2008 Eurotech S.p.A. <info@eurtech.it> + * + * This code is *strongly* based on EHCI-HCD code by David Brownell since + * the chip is a quasi-EHCI compatible. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/dmapool.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/ioport.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/timer.h> +#include <linux/list.h> +#include <linux/interrupt.h> +#include <linux/reboot.h> +#include <linux/usb.h> +#include <linux/moduleparam.h> +#include <linux/dma-mapping.h> +#include <linux/io.h> + +#include "../core/hcd.h" + +#include <asm/irq.h> +#include <asm/system.h> +#include <asm/unaligned.h> + +#include <linux/irq.h> +#include <linux/platform_device.h> + +#include "oxu210hp.h" + +#define DRIVER_VERSION "0.0.50" + +/* + * Main defines + */ + +#define oxu_dbg(oxu, fmt, args...) \ + dev_dbg(oxu_to_hcd(oxu)->self.controller , fmt , ## args) +#define oxu_err(oxu, fmt, args...) \ + dev_err(oxu_to_hcd(oxu)->self.controller , fmt , ## args) +#define oxu_info(oxu, fmt, args...) \ + dev_info(oxu_to_hcd(oxu)->self.controller , fmt , ## args) + +static inline struct usb_hcd *oxu_to_hcd(struct oxu_hcd *oxu) +{ + return container_of((void *) oxu, struct usb_hcd, hcd_priv); +} + +static inline struct oxu_hcd *hcd_to_oxu(struct usb_hcd *hcd) +{ + return (struct oxu_hcd *) (hcd->hcd_priv); +} + +/* + * Debug stuff + */ + +#undef OXU_URB_TRACE +#undef OXU_VERBOSE_DEBUG + +#ifdef OXU_VERBOSE_DEBUG +#define oxu_vdbg oxu_dbg +#else +#define oxu_vdbg(oxu, fmt, args...) /* Nop */ +#endif + +#ifdef DEBUG + +static int __attribute__((__unused__)) +dbg_status_buf(char *buf, unsigned len, const char *label, u32 status) +{ + return scnprintf(buf, len, "%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s", + label, label[0] ? " " : "", status, + (status & STS_ASS) ? " Async" : "", + (status & STS_PSS) ? " Periodic" : "", + (status & STS_RECL) ? " Recl" : "", + (status & STS_HALT) ? " Halt" : "", + (status & STS_IAA) ? " IAA" : "", + (status & STS_FATAL) ? " FATAL" : "", + (status & STS_FLR) ? " FLR" : "", + (status & STS_PCD) ? " PCD" : "", + (status & STS_ERR) ? " ERR" : "", + (status & STS_INT) ? " INT" : "" + ); +} + +static int __attribute__((__unused__)) +dbg_intr_buf(char *buf, unsigned len, const char *label, u32 enable) +{ + return scnprintf(buf, len, "%s%sintrenable %02x%s%s%s%s%s%s", + label, label[0] ? " " : "", enable, + (enable & STS_IAA) ? " IAA" : "", + (enable & STS_FATAL) ? " FATAL" : "", + (enable & STS_FLR) ? " FLR" : "", + (enable & STS_PCD) ? " PCD" : "", + (enable & STS_ERR) ? " ERR" : "", + (enable & STS_INT) ? " INT" : "" + ); +} + +static const char *const fls_strings[] = + { "1024", "512", "256", "??" }; + +static int dbg_command_buf(char *buf, unsigned len, + const char *label, u32 command) +{ + return scnprintf(buf, len, + "%s%scommand %06x %s=%d ithresh=%d%s%s%s%s period=%s%s %s", + label, label[0] ? " " : "", command, + (command & CMD_PARK) ? "park" : "(park)", + CMD_PARK_CNT(command), + (command >> 16) & 0x3f, + (command & CMD_LRESET) ? " LReset" : "", + (command & CMD_IAAD) ? " IAAD" : "", + (command & CMD_ASE) ? " Async" : "", + (command & CMD_PSE) ? " Periodic" : "", + fls_strings[(command >> 2) & 0x3], + (command & CMD_RESET) ? " Reset" : "", + (command & CMD_RUN) ? "RUN" : "HALT" + ); +} + +static int dbg_port_buf(char *buf, unsigned len, const char *label, + int port, u32 status) +{ + char *sig; + + /* signaling state */ + switch (status & (3 << 10)) { + case 0 << 10: + sig = "se0"; + break; + case 1 << 10: + sig = "k"; /* low speed */ + break; + case 2 << 10: + sig = "j"; + break; + default: + sig = "?"; + break; + } + + return scnprintf(buf, len, + "%s%sport %d status %06x%s%s sig=%s%s%s%s%s%s%s%s%s%s", + label, label[0] ? " " : "", port, status, + (status & PORT_POWER) ? " POWER" : "", + (status & PORT_OWNER) ? " OWNER" : "", + sig, + (status & PORT_RESET) ? " RESET" : "", + (status & PORT_SUSPEND) ? " SUSPEND" : "", + (status & PORT_RESUME) ? " RESUME" : "", + (status & PORT_OCC) ? " OCC" : "", + (status & PORT_OC) ? " OC" : "", + (status & PORT_PEC) ? " PEC" : "", + (status & PORT_PE) ? " PE" : "", + (status & PORT_CSC) ? " CSC" : "", + (status & PORT_CONNECT) ? " CONNECT" : "" + ); +} + +#else + +static inline int __attribute__((__unused__)) +dbg_status_buf(char *buf, unsigned len, const char *label, u32 status) +{ return 0; } + +static inline int __attribute__((__unused__)) +dbg_command_buf(char *buf, unsigned len, const char *label, u32 command) +{ return 0; } + +static inline int __attribute__((__unused__)) +dbg_intr_buf(char *buf, unsigned len, const char *label, u32 enable) +{ return 0; } + +static inline int __attribute__((__unused__)) +dbg_port_buf(char *buf, unsigned len, const char *label, int port, u32 status) +{ return 0; } + +#endif /* DEBUG */ + +/* functions have the "wrong" filename when they're output... */ +#define dbg_status(oxu, label, status) { \ + char _buf[80]; \ + dbg_status_buf(_buf, sizeof _buf, label, status); \ + oxu_dbg(oxu, "%s\n", _buf); \ +} + +#define dbg_cmd(oxu, label, command) { \ + char _buf[80]; \ + dbg_command_buf(_buf, sizeof _buf, label, command); \ + oxu_dbg(oxu, "%s\n", _buf); \ +} + +#define dbg_port(oxu, label, port, status) { \ + char _buf[80]; \ + dbg_port_buf(_buf, sizeof _buf, label, port, status); \ + oxu_dbg(oxu, "%s\n", _buf); \ +} + +/* + * Module parameters + */ + +/* Initial IRQ latency: faster than hw default */ +static int log2_irq_thresh; /* 0 to 6 */ +module_param(log2_irq_thresh, int, S_IRUGO); +MODULE_PARM_DESC(log2_irq_thresh, "log2 IRQ latency, 1-64 microframes"); + +/* Initial park setting: slower than hw default */ +static unsigned park; +module_param(park, uint, S_IRUGO); +MODULE_PARM_DESC(park, "park setting; 1-3 back-to-back async packets"); + +/* For flakey hardware, ignore overcurrent indicators */ +static int ignore_oc; +module_param(ignore_oc, bool, S_IRUGO); +MODULE_PARM_DESC(ignore_oc, "ignore bogus hardware overcurrent indications"); + + +static void ehci_work(struct oxu_hcd *oxu); +static int oxu_hub_control(struct usb_hcd *hcd, + u16 typeReq, u16 wValue, u16 wIndex, + char *buf, u16 wLength); + +/* + * Local functions + */ + +/* Low level read/write registers functions */ +static inline u32 oxu_readl(void *base, u32 reg) +{ + return readl(base + reg); +} + +static inline void oxu_writel(void *base, u32 reg, u32 val) +{ + writel(val, base + reg); +} + +static inline void timer_action_done(struct oxu_hcd *oxu, + enum ehci_timer_action action) +{ + clear_bit(action, &oxu->actions); +} + +static inline void timer_action(struct oxu_hcd *oxu, + enum ehci_timer_action action) +{ + if (!test_and_set_bit(action, &oxu->actions)) { + unsigned long t; + + switch (action) { + case TIMER_IAA_WATCHDOG: + t = EHCI_IAA_JIFFIES; + break; + case TIMER_IO_WATCHDOG: + t = EHCI_IO_JIFFIES; + break; + case TIMER_ASYNC_OFF: + t = EHCI_ASYNC_JIFFIES; + break; + case TIMER_ASYNC_SHRINK: + default: + t = EHCI_SHRINK_JIFFIES; + break; + } + t += jiffies; + /* all timings except IAA watchdog can be overridden. + * async queue SHRINK often precedes IAA. while it's ready + * to go OFF neither can matter, and afterwards the IO + * watchdog stops unless there's still periodic traffic. + */ + if (action != TIMER_IAA_WATCHDOG + && t > oxu->watchdog.expires + && timer_pending(&oxu->watchdog)) + return; + mod_timer(&oxu->watchdog, t); + } +} + +/* + * handshake - spin reading hc until handshake completes or fails + * @ptr: address of hc register to be read + * @mask: bits to look at in result of read + * @done: value of those bits when handshake succeeds + * @usec: timeout in microseconds + * + * Returns negative errno, or zero on success + * + * Success happens when the "mask" bits have the specified value (hardware + * handshake done). There are two failure modes: "usec" have passed (major + * hardware flakeout), or the register reads as all-ones (hardware removed). + * + * That last failure should_only happen in cases like physical cardbus eject + * before driver shutdown. But it also seems to be caused by bugs in cardbus + * bridge shutdown: shutting down the bridge before the devices using it. + */ +static int handshake(struct oxu_hcd *oxu, void __iomem *ptr, + u32 mask, u32 done, int usec) +{ + u32 result; + + do { + result = readl(ptr); + if (result == ~(u32)0) /* card removed */ + return -ENODEV; + result &= mask; + if (result == done) + return 0; + udelay(1); + usec--; + } while (usec > 0); + return -ETIMEDOUT; +} + +/* Force HC to halt state from unknown (EHCI spec section 2.3) */ +static int ehci_halt(struct oxu_hcd *oxu) +{ + u32 temp = readl(&oxu->regs->status); + + /* disable any irqs left enabled by previous code */ + writel(0, &oxu->regs->intr_enable); + + if ((temp & STS_HALT) != 0) + return 0; + + temp = readl(&oxu->regs->command); + temp &= ~CMD_RUN; + writel(temp, &oxu->regs->command); + return handshake(oxu, &oxu->regs->status, + STS_HALT, STS_HALT, 16 * 125); +} + +/* Put TDI/ARC silicon into EHCI mode */ +static void tdi_reset(struct oxu_hcd *oxu) +{ + u32 __iomem *reg_ptr; + u32 tmp; + + reg_ptr = (u32 __iomem *)(((u8 __iomem *)oxu->regs) + 0x68); + tmp = readl(reg_ptr); + tmp |= 0x3; + writel(tmp, reg_ptr); +} + +/* Reset a non-running (STS_HALT == 1) controller */ +static int ehci_reset(struct oxu_hcd *oxu) +{ + int retval; + u32 command = readl(&oxu->regs->command); + + command |= CMD_RESET; + dbg_cmd(oxu, "reset", command); + writel(command, &oxu->regs->command); + oxu_to_hcd(oxu)->state = HC_STATE_HALT; + oxu->next_statechange = jiffies; + retval = handshake(oxu, &oxu->regs->command, + CMD_RESET, 0, 250 * 1000); + + if (retval) + return retval; + + tdi_reset(oxu); + + return retval; +} + +/* Idle the controller (from running) */ +static void ehci_quiesce(struct oxu_hcd *oxu) +{ + u32 temp; + +#ifdef DEBUG + if (!HC_IS_RUNNING(oxu_to_hcd(oxu)->state)) + BUG(); +#endif + + /* wait for any schedule enables/disables to take effect */ + temp = readl(&oxu->regs->command) << 10; + temp &= STS_ASS | STS_PSS; + if (handshake(oxu, &oxu->regs->status, STS_ASS | STS_PSS, + temp, 16 * 125) != 0) { + oxu_to_hcd(oxu)->state = HC_STATE_HALT; + return; + } + + /* then disable anything that's still active */ + temp = readl(&oxu->regs->command); + temp &= ~(CMD_ASE | CMD_IAAD | CMD_PSE); + writel(temp, &oxu->regs->command); + + /* hardware can take 16 microframes to turn off ... */ + if (handshake(oxu, &oxu->regs->status, STS_ASS | STS_PSS, + 0, 16 * 125) != 0) { + oxu_to_hcd(oxu)->state = HC_STATE_HALT; + return; + } +} + +static int check_reset_complete(struct oxu_hcd *oxu, int index, + u32 __iomem *status_reg, int port_status) +{ + if (!(port_status & PORT_CONNECT)) { + oxu->reset_done[index] = 0; + return port_status; + } + + /* if reset finished and it's still not enabled -- handoff */ + if (!(port_status & PORT_PE)) { + oxu_dbg(oxu, "Failed to enable port %d on root hub TT\n", + index+1); + return port_status; + } else + oxu_dbg(oxu, "port %d high speed\n", index + 1); + + return port_status; +} + +static void ehci_hub_descriptor(struct oxu_hcd *oxu, + struct usb_hub_descriptor *desc) +{ + int ports = HCS_N_PORTS(oxu->hcs_params); + u16 temp; + + desc->bDescriptorType = 0x29; + desc->bPwrOn2PwrGood = 10; /* oxu 1.0, 2.3.9 says 20ms max */ + desc->bHubContrCurrent = 0; + + desc->bNbrPorts = ports; + temp = 1 + (ports / 8); + desc->bDescLength = 7 + 2 * temp; + + /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */ + memset(&desc->bitmap[0], 0, temp); + memset(&desc->bitmap[temp], 0xff, temp); + + temp = 0x0008; /* per-port overcurrent reporting */ + if (HCS_PPC(oxu->hcs_params)) + temp |= 0x0001; /* per-port power control */ + else + temp |= 0x0002; /* no power switching */ + desc->wHubCharacteristics = (__force __u16)cpu_to_le16(temp); +} + + +/* Allocate an OXU210HP on-chip memory data buffer + * + * An on-chip memory data buffer is required for each OXU210HP USB transfer. + * Each transfer descriptor has one or more on-chip memory data buffers. + * + * Data buffers are allocated from a fix sized pool of data blocks. + * To minimise fragmentation and give reasonable memory utlisation, + * data buffers are allocated with sizes the power of 2 multiples of + * the block size, starting on an address a multiple of the allocated size. + * + * FIXME: callers of this function require a buffer to be allocated for + * len=0. This is a waste of on-chip memory and should be fix. Then this + * function should be changed to not allocate a buffer for len=0. + */ +static int oxu_buf_alloc(struct oxu_hcd *oxu, struct ehci_qtd *qtd, int len) +{ + int n_blocks; /* minium blocks needed to hold len */ + int a_blocks; /* blocks allocated */ + int i, j; + + /* Don't allocte bigger than supported */ + if (len > BUFFER_SIZE * BUFFER_NUM) { + oxu_err(oxu, "buffer too big (%d)\n", len); + return -ENOMEM; + } + + spin_lock(&oxu->mem_lock); + + /* Number of blocks needed to hold len */ + n_blocks = (len + BUFFER_SIZE - 1) / BUFFER_SIZE; + + /* Round the number of blocks up to the power of 2 */ + for (a_blocks = 1; a_blocks < n_blocks; a_blocks <<= 1) + ; + + /* Find a suitable available data buffer */ + for (i = 0; i < BUFFER_NUM; + i += max(a_blocks, (int)oxu->db_used[i])) { + + /* Check all the required blocks are available */ + for (j = 0; j < a_blocks; j++) + if (oxu->db_used[i + j]) + break; + + if (j != a_blocks) + continue; + + /* Allocate blocks found! */ + qtd->buffer = (void *) &oxu->mem->db_pool[i]; + qtd->buffer_dma = virt_to_phys(qtd->buffer); + + qtd->qtd_buffer_len = BUFFER_SIZE * a_blocks; + oxu->db_used[i] = a_blocks; + + spin_unlock(&oxu->mem_lock); + + return 0; + } + + /* Failed */ + + spin_unlock(&oxu->mem_lock); + + return -ENOMEM; +} + +static void oxu_buf_free(struct oxu_hcd *oxu, struct ehci_qtd *qtd) +{ + int index; + + spin_lock(&oxu->mem_lock); + + index = (qtd->buffer - (void *) &oxu->mem->db_pool[0]) + / BUFFER_SIZE; + oxu->db_used[index] = 0; + qtd->qtd_buffer_len = 0; + qtd->buffer_dma = 0; + qtd->buffer = NULL; + + spin_unlock(&oxu->mem_lock); + + return; +} + +static inline void ehci_qtd_init(struct ehci_qtd *qtd, dma_addr_t dma) +{ + memset(qtd, 0, sizeof *qtd); + qtd->qtd_dma = dma; + qtd->hw_token = cpu_to_le32(QTD_STS_HALT); + qtd->hw_next = EHCI_LIST_END; + qtd->hw_alt_next = EHCI_LIST_END; + INIT_LIST_HEAD(&qtd->qtd_list); +} + +static inline void oxu_qtd_free(struct oxu_hcd *oxu, struct ehci_qtd *qtd) +{ + int index; + + if (qtd->buffer) + oxu_buf_free(oxu, qtd); + + spin_lock(&oxu->mem_lock); + + index = qtd - &oxu->mem->qtd_pool[0]; + oxu->qtd_used[index] = 0; + + spin_unlock(&oxu->mem_lock); + + return; +} + +static struct ehci_qtd *ehci_qtd_alloc(struct oxu_hcd *oxu) +{ + int i; + struct ehci_qtd *qtd = NULL; + + spin_lock(&oxu->mem_lock); + + for (i = 0; i < QTD_NUM; i++) + if (!oxu->qtd_used[i]) + break; + + if (i < QTD_NUM) { + qtd = (struct ehci_qtd *) &oxu->mem->qtd_pool[i]; + memset(qtd, 0, sizeof *qtd); + + qtd->hw_token = cpu_to_le32(QTD_STS_HALT); + qtd->hw_next = EHCI_LIST_END; + qtd->hw_alt_next = EHCI_LIST_END; + INIT_LIST_HEAD(&qtd->qtd_list); + + qtd->qtd_dma = virt_to_phys(qtd); + + oxu->qtd_used[i] = 1; + } + + spin_unlock(&oxu->mem_lock); + + return qtd; +} + +static void oxu_qh_free(struct oxu_hcd *oxu, struct ehci_qh *qh) +{ + int index; + + spin_lock(&oxu->mem_lock); + + index = qh - &oxu->mem->qh_pool[0]; + oxu->qh_used[index] = 0; + + spin_unlock(&oxu->mem_lock); + + return; +} + +static void qh_destroy(struct kref *kref) +{ + struct ehci_qh *qh = container_of(kref, struct ehci_qh, kref); + struct oxu_hcd *oxu = qh->oxu; + + /* clean qtds first, and know this is not linked */ + if (!list_empty(&qh->qtd_list) || qh->qh_next.ptr) { + oxu_dbg(oxu, "unused qh not empty!\n"); + BUG(); + } + if (qh->dummy) + oxu_qtd_free(oxu, qh->dummy); + oxu_qh_free(oxu, qh); +} + +static struct ehci_qh *oxu_qh_alloc(struct oxu_hcd *oxu) +{ + int i; + struct ehci_qh *qh = NULL; + + spin_lock(&oxu->mem_lock); + + for (i = 0; i < QHEAD_NUM; i++) + if (!oxu->qh_used[i]) + break; + + if (i < QHEAD_NUM) { + qh = (struct ehci_qh *) &oxu->mem->qh_pool[i]; + memset(qh, 0, sizeof *qh); + + kref_init(&qh->kref); + qh->oxu = oxu; + qh->qh_dma = virt_to_phys(qh); + INIT_LIST_HEAD(&qh->qtd_list); + + /* dummy td enables safe urb queuing */ + qh->dummy = ehci_qtd_alloc(oxu); + if (qh->dummy == NULL) { + oxu_dbg(oxu, "no dummy td\n"); + oxu->qh_used[i] = 0; + + return NULL; + } + + oxu->qh_used[i] = 1; + } + + spin_unlock(&oxu->mem_lock); + + return qh; +} + +/* to share a qh (cpu threads, or hc) */ +static inline struct ehci_qh *qh_get(struct ehci_qh *qh) +{ + kref_get(&qh->kref); + return qh; +} + +static inline void qh_put(struct ehci_qh *qh) +{ + kref_put(&qh->kref, qh_destroy); +} + +static void oxu_murb_free(struct oxu_hcd *oxu, struct oxu_murb *murb) +{ + int index; + + spin_lock(&oxu->mem_lock); + + index = murb - &oxu->murb_pool[0]; + oxu->murb_used[index] = 0; + + spin_unlock(&oxu->mem_lock); + + return; +} + +static struct oxu_murb *oxu_murb_alloc(struct oxu_hcd *oxu) + +{ + int i; + struct oxu_murb *murb = NULL; + + spin_lock(&oxu->mem_lock); + + for (i = 0; i < MURB_NUM; i++) + if (!oxu->murb_used[i]) + break; + + if (i < MURB_NUM) { + murb = &(oxu->murb_pool)[i]; + + oxu->murb_used[i] = 1; + } + + spin_unlock(&oxu->mem_lock); + + return murb; +} + +/* The queue heads and transfer descriptors are managed from pools tied + * to each of the "per device" structures. + * This is the initialisation and cleanup code. + */ +static void ehci_mem_cleanup(struct oxu_hcd *oxu) +{ + kfree(oxu->murb_pool); + oxu->murb_pool = NULL; + + if (oxu->async) + qh_put(oxu->async); + oxu->async = NULL; + + del_timer(&oxu->urb_timer); + + oxu->periodic = NULL; + + /* shadow periodic table */ + kfree(oxu->pshadow); + oxu->pshadow = NULL; +} + +/* Remember to add cleanup code (above) if you add anything here. + */ +static int ehci_mem_init(struct oxu_hcd *oxu, gfp_t flags) +{ + int i; + + for (i = 0; i < oxu->periodic_size; i++) + oxu->mem->frame_list[i] = EHCI_LIST_END; + for (i = 0; i < QHEAD_NUM; i++) + oxu->qh_used[i] = 0; + for (i = 0; i < QTD_NUM; i++) + oxu->qtd_used[i] = 0; + + oxu->murb_pool = kcalloc(MURB_NUM, sizeof(struct oxu_murb), flags); + if (!oxu->murb_pool) + goto fail; + + for (i = 0; i < MURB_NUM; i++) + oxu->murb_used[i] = 0; + + oxu->async = oxu_qh_alloc(oxu); + if (!oxu->async) + goto fail; + + oxu->periodic = (__le32 *) &oxu->mem->frame_list; + oxu->periodic_dma = virt_to_phys(oxu->periodic); + + for (i = 0; i < oxu->periodic_size; i++) + oxu->periodic[i] = EHCI_LIST_END; + + /* software shadow of hardware table */ + oxu->pshadow = kcalloc(oxu->periodic_size, sizeof(void *), flags); + if (oxu->pshadow != NULL) + return 0; + +fail: + oxu_dbg(oxu, "couldn't init memory\n"); + ehci_mem_cleanup(oxu); + return -ENOMEM; +} + +/* Fill a qtd, returning how much of the buffer we were able to queue up. + */ +static int qtd_fill(struct ehci_qtd *qtd, dma_addr_t buf, size_t len, + int token, int maxpacket) +{ + int i, count; + u64 addr = buf; + + /* one buffer entry per 4K ... first might be short or unaligned */ + qtd->hw_buf[0] = cpu_to_le32((u32)addr); + qtd->hw_buf_hi[0] = cpu_to_le32((u32)(addr >> 32)); + count = 0x1000 - (buf & 0x0fff); /* rest of that page */ + if (likely(len < count)) /* ... iff needed */ + count = len; + else { + buf += 0x1000; + buf &= ~0x0fff; + + /* per-qtd limit: from 16K to 20K (best alignment) */ + for (i = 1; count < len && i < 5; i++) { + addr = buf; + qtd->hw_buf[i] = cpu_to_le32((u32)addr); + qtd->hw_buf_hi[i] = cpu_to_le32((u32)(addr >> 32)); + buf += 0x1000; + if ((count + 0x1000) < len) + count += 0x1000; + else + count = len; + } + + /* short packets may only terminate transfers */ + if (count != len) + count -= (count % maxpacket); + } + qtd->hw_token = cpu_to_le32((count << 16) | token); + qtd->length = count; + + return count; +} + +static inline void qh_update(struct oxu_hcd *oxu, + struct ehci_qh *qh, struct ehci_qtd *qtd) +{ + /* writes to an active overlay are unsafe */ + BUG_ON(qh->qh_state != QH_STATE_IDLE); + + qh->hw_qtd_next = QTD_NEXT(qtd->qtd_dma); + qh->hw_alt_next = EHCI_LIST_END; + + /* Except for control endpoints, we make hardware maintain data + * toggle (like OHCI) ... here (re)initialize the toggle in the QH, + * and set the pseudo-toggle in udev. Only usb_clear_halt() will + * ever clear it. + */ + if (!(qh->hw_info1 & cpu_to_le32(1 << 14))) { + unsigned is_out, epnum; + + is_out = !(qtd->hw_token & cpu_to_le32(1 << 8)); + epnum = (le32_to_cpup(&qh->hw_info1) >> 8) & 0x0f; + if (unlikely(!usb_gettoggle(qh->dev, epnum, is_out))) { + qh->hw_token &= ~__constant_cpu_to_le32(QTD_TOGGLE); + usb_settoggle(qh->dev, epnum, is_out, 1); + } + } + + /* HC must see latest qtd and qh data before we clear ACTIVE+HALT */ + wmb(); + qh->hw_token &= __constant_cpu_to_le32(QTD_TOGGLE | QTD_STS_PING); +} + +/* If it weren't for a common silicon quirk (writing the dummy into the qh + * overlay, so qh->hw_token wrongly becomes inactive/halted), only fault + * recovery (including urb dequeue) would need software changes to a QH... + */ +static void qh_refresh(struct oxu_hcd *oxu, struct ehci_qh *qh) +{ + struct ehci_qtd *qtd; + + if (list_empty(&qh->qtd_list)) + qtd = qh->dummy; + else { + qtd = list_entry(qh->qtd_list.next, + struct ehci_qtd, qtd_list); + /* first qtd may already be partially processed */ + if (cpu_to_le32(qtd->qtd_dma) == qh->hw_current) + qtd = NULL; + } + + if (qtd) + qh_update(oxu, qh, qtd); +} + +static void qtd_copy_status(struct oxu_hcd *oxu, struct urb *urb, + size_t length, u32 token) +{ + /* count IN/OUT bytes, not SETUP (even short packets) */ + if (likely(QTD_PID(token) != 2)) + urb->actual_length += length - QTD_LENGTH(token); + + /* don't modify error codes */ + if (unlikely(urb->status != -EINPROGRESS)) + return; + + /* force cleanup after short read; not always an error */ + if (unlikely(IS_SHORT_READ(token))) + urb->status = -EREMOTEIO; + + /* serious "can't proceed" faults reported by the hardware */ + if (token & QTD_STS_HALT) { + if (token & QTD_STS_BABBLE) { + /* FIXME "must" disable babbling device's port too */ + urb->status = -EOVERFLOW; + } else if (token & QTD_STS_MMF) { + /* fs/ls interrupt xfer missed the complete-split */ + urb->status = -EPROTO; + } else if (token & QTD_STS_DBE) { + urb->status = (QTD_PID(token) == 1) /* IN ? */ + ? -ENOSR /* hc couldn't read data */ + : -ECOMM; /* hc couldn't write data */ + } else if (token & QTD_STS_XACT) { + /* timeout, bad crc, wrong PID, etc; retried */ + if (QTD_CERR(token)) + urb->status = -EPIPE; + else { + oxu_dbg(oxu, "devpath %s ep%d%s 3strikes\n", + urb->dev->devpath, + usb_pipeendpoint(urb->pipe), + usb_pipein(urb->pipe) ? "in" : "out"); + urb->status = -EPROTO; + } + /* CERR nonzero + no errors + halt --> stall */ + } else if (QTD_CERR(token)) + urb->status = -EPIPE; + else /* unknown */ + urb->status = -EPROTO; + + oxu_vdbg(oxu, "dev%d ep%d%s qtd token %08x --> status %d\n", + usb_pipedevice(urb->pipe), + usb_pipeendpoint(urb->pipe), + usb_pipein(urb->pipe) ? "in" : "out", + token, urb->status); + } +} + +static void ehci_urb_done(struct oxu_hcd *oxu, struct urb *urb) +__releases(oxu->lock) +__acquires(oxu->lock) +{ + if (likely(urb->hcpriv != NULL)) { + struct ehci_qh *qh = (struct ehci_qh *) urb->hcpriv; + + /* S-mask in a QH means it's an interrupt urb */ + if ((qh->hw_info2 & __constant_cpu_to_le32(QH_SMASK)) != 0) { + + /* ... update hc-wide periodic stats (for usbfs) */ + oxu_to_hcd(oxu)->self.bandwidth_int_reqs--; + } + qh_put(qh); + } + + urb->hcpriv = NULL; + switch (urb->status) { + case -EINPROGRESS: /* success */ + urb->status = 0; + default: /* fault */ + break; + case -EREMOTEIO: /* fault or normal */ + if (!(urb->transfer_flags & URB_SHORT_NOT_OK)) + urb->status = 0; + break; + case -ECONNRESET: /* canceled */ + case -ENOENT: + break; + } + +#ifdef OXU_URB_TRACE + oxu_dbg(oxu, "%s %s urb %p ep%d%s status %d len %d/%d\n", + __func__, urb->dev->devpath, urb, + usb_pipeendpoint(urb->pipe), + usb_pipein(urb->pipe) ? "in" : "out", + urb->status, + urb->actual_length, urb->transfer_buffer_length); +#endif + + /* complete() can reenter this HCD */ + spin_unlock(&oxu->lock); + usb_hcd_giveback_urb(oxu_to_hcd(oxu), urb, urb->status); + spin_lock(&oxu->lock); +} + +static void start_unlink_async(struct oxu_hcd *oxu, struct ehci_qh *qh); +static void unlink_async(struct oxu_hcd *oxu, struct ehci_qh *qh); + +static void intr_deschedule(struct oxu_hcd *oxu, struct ehci_qh *qh); +static int qh_schedule(struct oxu_hcd *oxu, struct ehci_qh *qh); + +#define HALT_BIT __constant_cpu_to_le32(QTD_STS_HALT) + +/* Process and free completed qtds for a qh, returning URBs to drivers. + * Chases up to qh->hw_current. Returns number of completions called, + * indicating how much "real" work we did. + */ +static unsigned qh_completions(struct oxu_hcd *oxu, struct ehci_qh *qh) +{ + struct ehci_qtd *last = NULL, *end = qh->dummy; + struct list_head *entry, *tmp; + int stopped; + unsigned count = 0; + int do_status = 0; + u8 state; + struct oxu_murb *murb = NULL; + + if (unlikely(list_empty(&qh->qtd_list))) + return count; + + /* completions (or tasks on other cpus) must never clobber HALT + * till we've gone through and cleaned everything up, even when + * they add urbs to this qh's queue or mark them for unlinking. + * + * NOTE: unlinking expects to be done in queue order. + */ + state = qh->qh_state; + qh->qh_state = QH_STATE_COMPLETING; + stopped = (state == QH_STATE_IDLE); + + /* remove de-activated QTDs from front of queue. + * after faults (including short reads), cleanup this urb + * then let the queue advance. + * if queue is stopped, handles unlinks. + */ + list_for_each_safe(entry, tmp, &qh->qtd_list) { + struct ehci_qtd *qtd; + struct urb *urb; + u32 token = 0; + + qtd = list_entry(entry, struct ehci_qtd, qtd_list); + urb = qtd->urb; + + /* Clean up any state from previous QTD ...*/ + if (last) { + if (likely(last->urb != urb)) { + if (last->urb->complete == NULL) { + murb = (struct oxu_murb *) last->urb; + last->urb = murb->main; + if (murb->last) { + ehci_urb_done(oxu, last->urb); + count++; + } + oxu_murb_free(oxu, murb); + } else { + ehci_urb_done(oxu, last->urb); + count++; + } + } + oxu_qtd_free(oxu, last); + last = NULL; + } + + /* ignore urbs submitted during completions we reported */ + if (qtd == end) + break; + + /* hardware copies qtd out of qh overlay */ + rmb(); + token = le32_to_cpu(qtd->hw_token); + + /* always clean up qtds the hc de-activated */ + if ((token & QTD_STS_ACTIVE) == 0) { + + if ((token & QTD_STS_HALT) != 0) { + stopped = 1; + + /* magic dummy for some short reads; qh won't advance. + * that silicon quirk can kick in with this dummy too. + */ + } else if (IS_SHORT_READ(token) && + !(qtd->hw_alt_next & EHCI_LIST_END)) { + stopped = 1; + goto halt; + } + + /* stop scanning when we reach qtds the hc is using */ + } else if (likely(!stopped && + HC_IS_RUNNING(oxu_to_hcd(oxu)->state))) { + break; + + } else { + stopped = 1; + + if (unlikely(!HC_IS_RUNNING(oxu_to_hcd(oxu)->state))) + urb->status = -ESHUTDOWN; + + /* ignore active urbs unless some previous qtd + * for the urb faulted (including short read) or + * its urb was canceled. we may patch qh or qtds. + */ + if (likely(urb->status == -EINPROGRESS)) + continue; + + /* issue status after short control reads */ + if (unlikely(do_status != 0) + && QTD_PID(token) == 0 /* OUT */) { + do_status = 0; + continue; + } + + /* token in overlay may be most current */ + if (state == QH_STATE_IDLE + && cpu_to_le32(qtd->qtd_dma) + == qh->hw_current) + token = le32_to_cpu(qh->hw_token); + + /* force halt for unlinked or blocked qh, so we'll + * patch the qh later and so that completions can't + * activate it while we "know" it's stopped. + */ + if ((HALT_BIT & qh->hw_token) == 0) { +halt: + qh->hw_token |= HALT_BIT; + wmb(); + } + } + + /* Remove it from the queue */ + qtd_copy_status(oxu, urb->complete ? + urb : ((struct oxu_murb *) urb)->main, + qtd->length, token); + if ((usb_pipein(qtd->urb->pipe)) && + (NULL != qtd->transfer_buffer)) + memcpy(qtd->transfer_buffer, qtd->buffer, qtd->length); + do_status = (urb->status == -EREMOTEIO) + && usb_pipecontrol(urb->pipe); + + if (stopped && qtd->qtd_list.prev != &qh->qtd_list) { + last = list_entry(qtd->qtd_list.prev, + struct ehci_qtd, qtd_list); + last->hw_next = qtd->hw_next; + } + list_del(&qtd->qtd_list); + last = qtd; + } + + /* last urb's completion might still need calling */ + if (likely(last != NULL)) { + if (last->urb->complete == NULL) { + murb = (struct oxu_murb *) last->urb; + last->urb = murb->main; + if (murb->last) { + ehci_urb_done(oxu, last->urb); + count++; + } + oxu_murb_free(oxu, murb); + } else { + ehci_urb_done(oxu, last->urb); + count++; + } + oxu_qtd_free(oxu, last); + } + + /* restore original state; caller must unlink or relink */ + qh->qh_state = state; + + /* be sure the hardware's done with the qh before refreshing + * it after fault cleanup, or recovering from silicon wrongly + * overlaying the dummy qtd (which reduces DMA chatter). + */ + if (stopped != 0 || qh->hw_qtd_next == EHCI_LIST_END) { + switch (state) { + case QH_STATE_IDLE: + qh_refresh(oxu, qh); + break; + case QH_STATE_LINKED: + /* should be rare for periodic transfers, + * except maybe high bandwidth ... + */ + if ((__constant_cpu_to_le32(QH_SMASK) + & qh->hw_info2) != 0) { + intr_deschedule(oxu, qh); + (void) qh_schedule(oxu, qh); + } else + unlink_async(oxu, qh); + break; + /* otherwise, unlink already started */ + } + } + + return count; +} + +/* High bandwidth multiplier, as encoded in highspeed endpoint descriptors */ +#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03)) +/* ... and packet size, for any kind of endpoint descriptor */ +#define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff) + +/* Reverse of qh_urb_transaction: free a list of TDs. + * used for cleanup after errors, before HC sees an URB's TDs. + */ +static void qtd_list_free(struct oxu_hcd *oxu, + struct urb *urb, struct list_head *qtd_list) +{ + struct list_head *entry, *temp; + + list_for_each_safe(entry, temp, qtd_list) { + struct ehci_qtd *qtd; + + qtd = list_entry(entry, struct ehci_qtd, qtd_list); + list_del(&qtd->qtd_list); + oxu_qtd_free(oxu, qtd); + } +} + +/* Create a list of filled qtds for this URB; won't link into qh. + */ +static struct list_head *qh_urb_transaction(struct oxu_hcd *oxu, + struct urb *urb, + struct list_head *head, + gfp_t flags) +{ + struct ehci_qtd *qtd, *qtd_prev; + dma_addr_t buf; + int len, maxpacket; + int is_input; + u32 token; + void *transfer_buf = NULL; + int ret; + + /* + * URBs map to sequences of QTDs: one logical transaction + */ + qtd = ehci_qtd_alloc(oxu); + if (unlikely(!qtd)) + return NULL; + list_add_tail(&qtd->qtd_list, head); + qtd->urb = urb; + + token = QTD_STS_ACTIVE; + token |= (EHCI_TUNE_CERR << 10); + /* for split transactions, SplitXState initialized to zero */ + + len = urb->transfer_buffer_length; + is_input = usb_pipein(urb->pipe); + if (!urb->transfer_buffer && urb->transfer_buffer_length && is_input) + urb->transfer_buffer = phys_to_virt(urb->transfer_dma); + + if (usb_pipecontrol(urb->pipe)) { + /* SETUP pid */ + ret = oxu_buf_alloc(oxu, qtd, sizeof(struct usb_ctrlrequest)); + if (ret) + goto cleanup; + + qtd_fill(qtd, qtd->buffer_dma, sizeof(struct usb_ctrlrequest), + token | (2 /* "setup" */ << 8), 8); + memcpy(qtd->buffer, qtd->urb->setup_packet, + sizeof(struct usb_ctrlrequest)); + + /* ... and always at least one more pid */ + token ^= QTD_TOGGLE; + qtd_prev = qtd; + qtd = ehci_qtd_alloc(oxu); + if (unlikely(!qtd)) + goto cleanup; + qtd->urb = urb; + qtd_prev->hw_next = QTD_NEXT(qtd->qtd_dma); + list_add_tail(&qtd->qtd_list, head); + + /* for zero length DATA stages, STATUS is always IN */ + if (len == 0) + token |= (1 /* "in" */ << 8); + } + + /* + * Data transfer stage: buffer setup + */ + + ret = oxu_buf_alloc(oxu, qtd, len); + if (ret) + goto cleanup; + + buf = qtd->buffer_dma; + transfer_buf = urb->transfer_buffer; + + if (!is_input) + memcpy(qtd->buffer, qtd->urb->transfer_buffer, len); + + if (is_input) + token |= (1 /* "in" */ << 8); + /* else it's already initted to "out" pid (0 << 8) */ + + maxpacket = max_packet(usb_maxpacket(urb->dev, urb->pipe, !is_input)); + + /* + * buffer gets wrapped in one or more qtds; + * last one may be "short" (including zero len) + * and may serve as a control status ack + */ + for (;;) { + int this_qtd_len; + + this_qtd_len = qtd_fill(qtd, buf, len, token, maxpacket); + qtd->transfer_buffer = transfer_buf; + len -= this_qtd_len; + buf += this_qtd_len; + transfer_buf += this_qtd_len; + if (is_input) + qtd->hw_alt_next = oxu->async->hw_alt_next; + + /* qh makes control packets use qtd toggle; maybe switch it */ + if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0) + token ^= QTD_TOGGLE; + + if (likely(len <= 0)) + break; + + qtd_prev = qtd; + qtd = ehci_qtd_alloc(oxu); + if (unlikely(!qtd)) + goto cleanup; + if (likely(len > 0)) { + ret = oxu_buf_alloc(oxu, qtd, len); + if (ret) + goto cleanup; + } + qtd->urb = urb; + qtd_prev->hw_next = QTD_NEXT(qtd->qtd_dma); + list_add_tail(&qtd->qtd_list, head); + } + + /* unless the bulk/interrupt caller wants a chance to clean + * up after short reads, hc should advance qh past this urb + */ + if (likely((urb->transfer_flags & URB_SHORT_NOT_OK) == 0 + || usb_pipecontrol(urb->pipe))) + qtd->hw_alt_next = EHCI_LIST_END; + + /* + * control requests may need a terminating data "status" ack; + * bulk ones may need a terminating short packet (zero length). + */ + if (likely(urb->transfer_buffer_length != 0)) { + int one_more = 0; + + if (usb_pipecontrol(urb->pipe)) { + one_more = 1; + token ^= 0x0100; /* "in" <--> "out" */ + token |= QTD_TOGGLE; /* force DATA1 */ + } else if (usb_pipebulk(urb->pipe) + && (urb->transfer_flags & URB_ZERO_PACKET) + && !(urb->transfer_buffer_length % maxpacket)) { + one_more = 1; + } + if (one_more) { + qtd_prev = qtd; + qtd = ehci_qtd_alloc(oxu); + if (unlikely(!qtd)) + goto cleanup; + qtd->urb = urb; + qtd_prev->hw_next = QTD_NEXT(qtd->qtd_dma); + list_add_tail(&qtd->qtd_list, head); + + /* never any data in such packets */ + qtd_fill(qtd, 0, 0, token, 0); + } + } + + /* by default, enable interrupt on urb completion */ + qtd->hw_token |= __constant_cpu_to_le32(QTD_IOC); + return head; + +cleanup: + qtd_list_free(oxu, urb, head); + return NULL; +} + +/* Each QH holds a qtd list; a QH is used for everything except iso. + * + * For interrupt urbs, the scheduler must set the microframe scheduling + * mask(s) each time the QH gets scheduled. For highspeed, that's + * just one microframe in the s-mask. For split interrupt transactions + * there are additional complications: c-mask, maybe FSTNs. + */ +static struct ehci_qh *qh_make(struct oxu_hcd *oxu, + struct urb *urb, gfp_t flags) +{ + struct ehci_qh *qh = oxu_qh_alloc(oxu); + u32 info1 = 0, info2 = 0; + int is_input, type; + int maxp = 0; + + if (!qh) + return qh; + + /* + * init endpoint/device data for this QH + */ + info1 |= usb_pipeendpoint(urb->pipe) << 8; + info1 |= usb_pipedevice(urb->pipe) << 0; + + is_input = usb_pipein(urb->pipe); + type = usb_pipetype(urb->pipe); + maxp = usb_maxpacket(urb->dev, urb->pipe, !is_input); + + /* Compute interrupt scheduling parameters just once, and save. + * - allowing for high bandwidth, how many nsec/uframe are used? + * - split transactions need a second CSPLIT uframe; same question + * - splits also need a schedule gap (for full/low speed I/O) + * - qh has a polling interval + * + * For control/bulk requests, the HC or TT handles these. + */ + if (type == PIPE_INTERRUPT) { + qh->usecs = NS_TO_US(usb_calc_bus_time(USB_SPEED_HIGH, + is_input, 0, + hb_mult(maxp) * max_packet(maxp))); + qh->start = NO_FRAME; + + if (urb->dev->speed == USB_SPEED_HIGH) { + qh->c_usecs = 0; + qh->gap_uf = 0; + + qh->period = urb->interval >> 3; + if (qh->period == 0 && urb->interval != 1) { + /* NOTE interval 2 or 4 uframes could work. + * But interval 1 scheduling is simpler, and + * includes high bandwidth. + */ + dbg("intr period %d uframes, NYET!", + urb->interval); + goto done; + } + } else { + struct usb_tt *tt = urb->dev->tt; + int think_time; + + /* gap is f(FS/LS transfer times) */ + qh->gap_uf = 1 + usb_calc_bus_time(urb->dev->speed, + is_input, 0, maxp) / (125 * 1000); + + /* FIXME this just approximates SPLIT/CSPLIT times */ + if (is_input) { /* SPLIT, gap, CSPLIT+DATA */ + qh->c_usecs = qh->usecs + HS_USECS(0); + qh->usecs = HS_USECS(1); + } else { /* SPLIT+DATA, gap, CSPLIT */ + qh->usecs += HS_USECS(1); + qh->c_usecs = HS_USECS(0); + } + + think_time = tt ? tt->think_time : 0; + qh->tt_usecs = NS_TO_US(think_time + + usb_calc_bus_time(urb->dev->speed, + is_input, 0, max_packet(maxp))); + qh->period = urb->interval; + } + } + + /* support for tt scheduling, and access to toggles */ + qh->dev = urb->dev; + + /* using TT? */ + switch (urb->dev->speed) { + case USB_SPEED_LOW: + info1 |= (1 << 12); /* EPS "low" */ + /* FALL THROUGH */ + + case USB_SPEED_FULL: + /* EPS 0 means "full" */ + if (type != PIPE_INTERRUPT) + info1 |= (EHCI_TUNE_RL_TT << 28); + if (type == PIPE_CONTROL) { + info1 |= (1 << 27); /* for TT */ + info1 |= 1 << 14; /* toggle from qtd */ + } + info1 |= maxp << 16; + + info2 |= (EHCI_TUNE_MULT_TT << 30); + info2 |= urb->dev->ttport << 23; + + /* NOTE: if (PIPE_INTERRUPT) { scheduler sets c-mask } */ + + break; + + case USB_SPEED_HIGH: /* no TT involved */ + info1 |= (2 << 12); /* EPS "high" */ + if (type == PIPE_CONTROL) { + info1 |= (EHCI_TUNE_RL_HS << 28); + info1 |= 64 << 16; /* usb2 fixed maxpacket */ + info1 |= 1 << 14; /* toggle from qtd */ + info2 |= (EHCI_TUNE_MULT_HS << 30); + } else if (type == PIPE_BULK) { + info1 |= (EHCI_TUNE_RL_HS << 28); + info1 |= 512 << 16; /* usb2 fixed maxpacket */ + info2 |= (EHCI_TUNE_MULT_HS << 30); + } else { /* PIPE_INTERRUPT */ + info1 |= max_packet(maxp) << 16; + info2 |= hb_mult(maxp) << 30; + } + break; + default: + dbg("bogus dev %p speed %d", urb->dev, urb->dev->speed); +done: + qh_put(qh); + return NULL; + } + + /* NOTE: if (PIPE_INTERRUPT) { scheduler sets s-mask } */ + + /* init as live, toggle clear, advance to dummy */ + qh->qh_state = QH_STATE_IDLE; + qh->hw_info1 = cpu_to_le32(info1); + qh->hw_info2 = cpu_to_le32(info2); + usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), !is_input, 1); + qh_refresh(oxu, qh); + return qh; +} + +/* Move qh (and its qtds) onto async queue; maybe enable queue. + */ +static void qh_link_async(struct oxu_hcd *oxu, struct ehci_qh *qh) +{ + __le32 dma = QH_NEXT(qh->qh_dma); + struct ehci_qh *head; + + /* (re)start the async schedule? */ + head = oxu->async; + timer_action_done(oxu, TIMER_ASYNC_OFF); + if (!head->qh_next.qh) { + u32 cmd = readl(&oxu->regs->command); + + if (!(cmd & CMD_ASE)) { + /* in case a clear of CMD_ASE didn't take yet */ + (void)handshake(oxu, &oxu->regs->status, + STS_ASS, 0, 150); + cmd |= CMD_ASE | CMD_RUN; + writel(cmd, &oxu->regs->command); + oxu_to_hcd(oxu)->state = HC_STATE_RUNNING; + /* posted write need not be known to HC yet ... */ + } + } + + /* clear halt and/or toggle; and maybe recover from silicon quirk */ + if (qh->qh_state == QH_STATE_IDLE) + qh_refresh(oxu, qh); + + /* splice right after start */ + qh->qh_next = head->qh_next; + qh->hw_next = head->hw_next; + wmb(); + + head->qh_next.qh = qh; + head->hw_next = dma; + + qh->qh_state = QH_STATE_LINKED; + /* qtd completions reported later by interrupt */ +} + +#define QH_ADDR_MASK __constant_cpu_to_le32(0x7f) + +/* + * For control/bulk/interrupt, return QH with these TDs appended. + * Allocates and initializes the QH if necessary. + * Returns null if it can't allocate a QH it needs to. + * If the QH has TDs (urbs) already, that's great. + */ +static struct ehci_qh *qh_append_tds(struct oxu_hcd *oxu, + struct urb *urb, struct list_head *qtd_list, + int epnum, void **ptr) +{ + struct ehci_qh *qh = NULL; + + qh = (struct ehci_qh *) *ptr; + if (unlikely(qh == NULL)) { + /* can't sleep here, we have oxu->lock... */ + qh = qh_make(oxu, urb, GFP_ATOMIC); + *ptr = qh; + } + if (likely(qh != NULL)) { + struct ehci_qtd *qtd; + + if (unlikely(list_empty(qtd_list))) + qtd = NULL; + else + qtd = list_entry(qtd_list->next, struct ehci_qtd, + qtd_list); + + /* control qh may need patching ... */ + if (unlikely(epnum == 0)) { + + /* usb_reset_device() briefly reverts to address 0 */ + if (usb_pipedevice(urb->pipe) == 0) + qh->hw_info1 &= ~QH_ADDR_MASK; + } + + /* just one way to queue requests: swap with the dummy qtd. + * only hc or qh_refresh() ever modify the overlay. + */ + if (likely(qtd != NULL)) { + struct ehci_qtd *dummy; + dma_addr_t dma; + __le32 token; + + /* to avoid racing the HC, use the dummy td instead of + * the first td of our list (becomes new dummy). both + * tds stay deactivated until we're done, when the + * HC is allowed to fetch the old dummy (4.10.2). + */ + token = qtd->hw_token; + qtd->hw_token = HALT_BIT; + wmb(); + dummy = qh->dummy; + + dma = dummy->qtd_dma; + *dummy = *qtd; + dummy->qtd_dma = dma; + + list_del(&qtd->qtd_list); + list_add(&dummy->qtd_list, qtd_list); + list_splice(qtd_list, qh->qtd_list.prev); + + ehci_qtd_init(qtd, qtd->qtd_dma); + qh->dummy = qtd; + + /* hc must see the new dummy at list end */ + dma = qtd->qtd_dma; + qtd = list_entry(qh->qtd_list.prev, + struct ehci_qtd, qtd_list); + qtd->hw_next = QTD_NEXT(dma); + + /* let the hc process these next qtds */ + dummy->hw_token = (token & ~(0x80)); + wmb(); + dummy->hw_token = token; + + urb->hcpriv = qh_get(qh); + } + } + return qh; +} + +static int submit_async(struct oxu_hcd *oxu, struct urb *urb, + struct list_head *qtd_list, gfp_t mem_flags) +{ + struct ehci_qtd *qtd; + int epnum; + unsigned long flags; + struct ehci_qh *qh = NULL; + int rc = 0; + + qtd = list_entry(qtd_list->next, struct ehci_qtd, qtd_list); + epnum = urb->ep->desc.bEndpointAddress; + +#ifdef OXU_URB_TRACE + oxu_dbg(oxu, "%s %s urb %p ep%d%s len %d, qtd %p [qh %p]\n", + __func__, urb->dev->devpath, urb, + epnum & 0x0f, (epnum & USB_DIR_IN) ? "in" : "out", + urb->transfer_buffer_length, + qtd, urb->ep->hcpriv); +#endif + + spin_lock_irqsave(&oxu->lock, flags); + if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, + &oxu_to_hcd(oxu)->flags))) { + rc = -ESHUTDOWN; + goto done; + } + + qh = qh_append_tds(oxu, urb, qtd_list, epnum, &urb->ep->hcpriv); + if (unlikely(qh == NULL)) { + rc = -ENOMEM; + goto done; + } + + /* Control/bulk operations through TTs don't need scheduling, + * the HC and TT handle it when the TT has a buffer ready. + */ + if (likely(qh->qh_state == QH_STATE_IDLE)) + qh_link_async(oxu, qh_get(qh)); +done: + spin_unlock_irqrestore(&oxu->lock, flags); + if (unlikely(qh == NULL)) + qtd_list_free(oxu, urb, qtd_list); + return rc; +} + +/* The async qh for the qtds being reclaimed are now unlinked from the HC */ + +static void end_unlink_async(struct oxu_hcd *oxu) +{ + struct ehci_qh *qh = oxu->reclaim; + struct ehci_qh *next; + + timer_action_done(oxu, TIMER_IAA_WATCHDOG); + + qh->qh_state = QH_STATE_IDLE; + qh->qh_next.qh = NULL; + qh_put(qh); /* refcount from reclaim */ + + /* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */ + next = qh->reclaim; + oxu->reclaim = next; + oxu->reclaim_ready = 0; + qh->reclaim = NULL; + + qh_completions(oxu, qh); + + if (!list_empty(&qh->qtd_list) + && HC_IS_RUNNING(oxu_to_hcd(oxu)->state)) + qh_link_async(oxu, qh); + else { + qh_put(qh); /* refcount from async list */ + + /* it's not free to turn the async schedule on/off; leave it + * active but idle for a while once it empties. + */ + if (HC_IS_RUNNING(oxu_to_hcd(oxu)->state) + && oxu->async->qh_next.qh == NULL) + timer_action(oxu, TIMER_ASYNC_OFF); + } + + if (next) { + oxu->reclaim = NULL; + start_unlink_async(oxu, next); + } +} + +/* makes sure the async qh will become idle */ +/* caller must own oxu->lock */ + +static void start_unlink_async(struct oxu_hcd *oxu, struct ehci_qh *qh) +{ + int cmd = readl(&oxu->regs->command); + struct ehci_qh *prev; + +#ifdef DEBUG + assert_spin_locked(&oxu->lock); + if (oxu->reclaim || (qh->qh_state != QH_STATE_LINKED + && qh->qh_state != QH_STATE_UNLINK_WAIT)) + BUG(); +#endif + + /* stop async schedule right now? */ + if (unlikely(qh == oxu->async)) { + /* can't get here without STS_ASS set */ + if (oxu_to_hcd(oxu)->state != HC_STATE_HALT + && !oxu->reclaim) { + /* ... and CMD_IAAD clear */ + writel(cmd & ~CMD_ASE, &oxu->regs->command); + wmb(); + /* handshake later, if we need to */ + timer_action_done(oxu, TIMER_ASYNC_OFF); + } + return; + } + + qh->qh_state = QH_STATE_UNLINK; + oxu->reclaim = qh = qh_get(qh); + + prev = oxu->async; + while (prev->qh_next.qh != qh) + prev = prev->qh_next.qh; + + prev->hw_next = qh->hw_next; + prev->qh_next = qh->qh_next; + wmb(); + + if (unlikely(oxu_to_hcd(oxu)->state == HC_STATE_HALT)) { + /* if (unlikely(qh->reclaim != 0)) + * this will recurse, probably not much + */ + end_unlink_async(oxu); + return; + } + + oxu->reclaim_ready = 0; + cmd |= CMD_IAAD; + writel(cmd, &oxu->regs->command); + (void) readl(&oxu->regs->command); + timer_action(oxu, TIMER_IAA_WATCHDOG); +} + +static void scan_async(struct oxu_hcd *oxu) +{ + struct ehci_qh *qh; + enum ehci_timer_action action = TIMER_IO_WATCHDOG; + + if (!++(oxu->stamp)) + oxu->stamp++; + timer_action_done(oxu, TIMER_ASYNC_SHRINK); +rescan: + qh = oxu->async->qh_next.qh; + if (likely(qh != NULL)) { + do { + /* clean any finished work for this qh */ + if (!list_empty(&qh->qtd_list) + && qh->stamp != oxu->stamp) { + int temp; + + /* unlinks could happen here; completion + * reporting drops the lock. rescan using + * the latest schedule, but don't rescan + * qhs we already finished (no looping). + */ + qh = qh_get(qh); + qh->stamp = oxu->stamp; + temp = qh_completions(oxu, qh); + qh_put(qh); + if (temp != 0) + goto rescan; + } + + /* unlink idle entries, reducing HC PCI usage as well + * as HCD schedule-scanning costs. delay for any qh + * we just scanned, there's a not-unusual case that it + * doesn't stay idle for long. + * (plus, avoids some kind of re-activation race.) + */ + if (list_empty(&qh->qtd_list)) { + if (qh->stamp == oxu->stamp) + action = TIMER_ASYNC_SHRINK; + else if (!oxu->reclaim + && qh->qh_state == QH_STATE_LINKED) + start_unlink_async(oxu, qh); + } + + qh = qh->qh_next.qh; + } while (qh); + } + if (action == TIMER_ASYNC_SHRINK) + timer_action(oxu, TIMER_ASYNC_SHRINK); +} + +/* + * periodic_next_shadow - return "next" pointer on shadow list + * @periodic: host pointer to qh/itd/sitd + * @tag: hardware tag for type of this record + */ +static union ehci_shadow *periodic_next_shadow(union ehci_shadow *periodic, + __le32 tag) +{ + switch (tag) { + default: + case Q_TYPE_QH: + return &periodic->qh->qh_next; + } +} + +/* caller must hold oxu->lock */ +static void periodic_unlink(struct oxu_hcd *oxu, unsigned frame, void *ptr) +{ + union ehci_shadow *prev_p = &oxu->pshadow[frame]; + __le32 *hw_p = &oxu->periodic[frame]; + union ehci_shadow here = *prev_p; + + /* find predecessor of "ptr"; hw and shadow lists are in sync */ + while (here.ptr && here.ptr != ptr) { + prev_p = periodic_next_shadow(prev_p, Q_NEXT_TYPE(*hw_p)); + hw_p = here.hw_next; + here = *prev_p; + } + /* an interrupt entry (at list end) could have been shared */ + if (!here.ptr) + return; + + /* update shadow and hardware lists ... the old "next" pointers + * from ptr may still be in use, the caller updates them. + */ + *prev_p = *periodic_next_shadow(&here, Q_NEXT_TYPE(*hw_p)); + *hw_p = *here.hw_next; +} + +/* how many of the uframe's 125 usecs are allocated? */ +static unsigned short periodic_usecs(struct oxu_hcd *oxu, + unsigned frame, unsigned uframe) +{ + __le32 *hw_p = &oxu->periodic[frame]; + union ehci_shadow *q = &oxu->pshadow[frame]; + unsigned usecs = 0; + + while (q->ptr) { + switch (Q_NEXT_TYPE(*hw_p)) { + case Q_TYPE_QH: + default: + /* is it in the S-mask? */ + if (q->qh->hw_info2 & cpu_to_le32(1 << uframe)) + usecs += q->qh->usecs; + /* ... or C-mask? */ + if (q->qh->hw_info2 & cpu_to_le32(1 << (8 + uframe))) + usecs += q->qh->c_usecs; + hw_p = &q->qh->hw_next; + q = &q->qh->qh_next; + break; + } + } +#ifdef DEBUG + if (usecs > 100) + oxu_err(oxu, "uframe %d sched overrun: %d usecs\n", + frame * 8 + uframe, usecs); +#endif + return usecs; +} + +static int enable_periodic(struct oxu_hcd *oxu) +{ + u32 cmd; + int status; + + /* did clearing PSE did take effect yet? + * takes effect only at frame boundaries... + */ + status = handshake(oxu, &oxu->regs->status, STS_PSS, 0, 9 * 125); + if (status != 0) { + oxu_to_hcd(oxu)->state = HC_STATE_HALT; + return status; + } + + cmd = readl(&oxu->regs->command) | CMD_PSE; + writel(cmd, &oxu->regs->command); + /* posted write ... PSS happens later */ + oxu_to_hcd(oxu)->state = HC_STATE_RUNNING; + + /* make sure ehci_work scans these */ + oxu->next_uframe = readl(&oxu->regs->frame_index) + % (oxu->periodic_size << 3); + return 0; +} + +static int disable_periodic(struct oxu_hcd *oxu) +{ + u32 cmd; + int status; + + /* did setting PSE not take effect yet? + * takes effect only at frame boundaries... + */ + status = handshake(oxu, &oxu->regs->status, STS_PSS, STS_PSS, 9 * 125); + if (status != 0) { + oxu_to_hcd(oxu)->state = HC_STATE_HALT; + return status; + } + + cmd = readl(&oxu->regs->command) & ~CMD_PSE; + writel(cmd, &oxu->regs->command); + /* posted write ... */ + + oxu->next_uframe = -1; + return 0; +} + +/* periodic schedule slots have iso tds (normal or split) first, then a + * sparse tree for active interrupt transfers. + * + * this just links in a qh; caller guarantees uframe masks are set right. + * no FSTN support (yet; oxu 0.96+) + */ +static int qh_link_periodic(struct oxu_hcd *oxu, struct ehci_qh *qh) +{ + unsigned i; + unsigned period = qh->period; + + dev_dbg(&qh->dev->dev, + "link qh%d-%04x/%p start %d [%d/%d us]\n", + period, le32_to_cpup(&qh->hw_info2) & (QH_CMASK | QH_SMASK), + qh, qh->start, qh->usecs, qh->c_usecs); + + /* high bandwidth, or otherwise every microframe */ + if (period == 0) + period = 1; + + for (i = qh->start; i < oxu->periodic_size; i += period) { + union ehci_shadow *prev = &oxu->pshadow[i]; + __le32 *hw_p = &oxu->periodic[i]; + union ehci_shadow here = *prev; + __le32 type = 0; + + /* skip the iso nodes at list head */ + while (here.ptr) { + type = Q_NEXT_TYPE(*hw_p); + if (type == Q_TYPE_QH) + break; + prev = periodic_next_shadow(prev, type); + hw_p = &here.qh->hw_next; + here = *prev; + } + + /* sorting each branch by period (slow-->fast) + * enables sharing interior tree nodes + */ + while (here.ptr && qh != here.qh) { + if (qh->period > here.qh->period) + break; + prev = &here.qh->qh_next; + hw_p = &here.qh->hw_next; + here = *prev; + } + /* link in this qh, unless some earlier pass did that */ + if (qh != here.qh) { + qh->qh_next = here; + if (here.qh) + qh->hw_next = *hw_p; + wmb(); + prev->qh = qh; + *hw_p = QH_NEXT(qh->qh_dma); + } + } + qh->qh_state = QH_STATE_LINKED; + qh_get(qh); + + /* update per-qh bandwidth for usbfs */ + oxu_to_hcd(oxu)->self.bandwidth_allocated += qh->period + ? ((qh->usecs + qh->c_usecs) / qh->period) + : (qh->usecs * 8); + + /* maybe enable periodic schedule processing */ + if (!oxu->periodic_sched++) + return enable_periodic(oxu); + + return 0; +} + +static void qh_unlink_periodic(struct oxu_hcd *oxu, struct ehci_qh *qh) +{ + unsigned i; + unsigned period; + + /* FIXME: + * IF this isn't high speed + * and this qh is active in the current uframe + * (and overlay token SplitXstate is false?) + * THEN + * qh->hw_info1 |= __constant_cpu_to_le32(1 << 7 "ignore"); + */ + + /* high bandwidth, or otherwise part of every microframe */ + period = qh->period; + if (period == 0) + period = 1; + + for (i = qh->start; i < oxu->periodic_size; i += period) + periodic_unlink(oxu, i, qh); + + /* update per-qh bandwidth for usbfs */ + oxu_to_hcd(oxu)->self.bandwidth_allocated -= qh->period + ? ((qh->usecs + qh->c_usecs) / qh->period) + : (qh->usecs * 8); + + dev_dbg(&qh->dev->dev, + "unlink qh%d-%04x/%p start %d [%d/%d us]\n", + qh->period, + le32_to_cpup(&qh->hw_info2) & (QH_CMASK | QH_SMASK), + qh, qh->start, qh->usecs, qh->c_usecs); + + /* qh->qh_next still "live" to HC */ + qh->qh_state = QH_STATE_UNLINK; + qh->qh_next.ptr = NULL; + qh_put(qh); + + /* maybe turn off periodic schedule */ + oxu->periodic_sched--; + if (!oxu->periodic_sched) + (void) disable_periodic(oxu); +} + +static void intr_deschedule(struct oxu_hcd *oxu, struct ehci_qh *qh) +{ + unsigned wait; + + qh_unlink_periodic(oxu, qh); + + /* simple/paranoid: always delay, expecting the HC needs to read + * qh->hw_next or finish a writeback after SPLIT/CSPLIT ... and + * expect khubd to clean up after any CSPLITs we won't issue. + * active high speed queues may need bigger delays... + */ + if (list_empty(&qh->qtd_list) + || (__constant_cpu_to_le32(QH_CMASK) & qh->hw_info2) != 0) + wait = 2; + else + wait = 55; /* worst case: 3 * 1024 */ + + udelay(wait); + qh->qh_state = QH_STATE_IDLE; + qh->hw_next = EHCI_LIST_END; + wmb(); +} + +static int check_period(struct oxu_hcd *oxu, + unsigned frame, unsigned uframe, + unsigned period, unsigned usecs) +{ + int claimed; + + /* complete split running into next frame? + * given FSTN support, we could sometimes check... + */ + if (uframe >= 8) + return 0; + + /* + * 80% periodic == 100 usec/uframe available + * convert "usecs we need" to "max already claimed" + */ + usecs = 100 - usecs; + + /* we "know" 2 and 4 uframe intervals were rejected; so + * for period 0, check _every_ microframe in the schedule. + */ + if (unlikely(period == 0)) { + do { + for (uframe = 0; uframe < 7; uframe++) { + claimed = periodic_usecs(oxu, frame, uframe); + if (claimed > usecs) + return 0; + } + } while ((frame += 1) < oxu->periodic_size); + + /* just check the specified uframe, at that period */ + } else { + do { + claimed = periodic_usecs(oxu, frame, uframe); + if (claimed > usecs) + return 0; + } while ((frame += period) < oxu->periodic_size); + } + + return 1; +} + +static int check_intr_schedule(struct oxu_hcd *oxu, + unsigned frame, unsigned uframe, + const struct ehci_qh *qh, __le32 *c_maskp) +{ + int retval = -ENOSPC; + + if (qh->c_usecs && uframe >= 6) /* FSTN territory? */ + goto done; + + if (!check_period(oxu, frame, uframe, qh->period, qh->usecs)) + goto done; + if (!qh->c_usecs) { + retval = 0; + *c_maskp = 0; + goto done; + } + +done: + return retval; +} + +/* "first fit" scheduling policy used the first time through, + * or when the previous schedule slot can't be re-used. + */ +static int qh_schedule(struct oxu_hcd *oxu, struct ehci_qh *qh) +{ + int status; + unsigned uframe; + __le32 c_mask; + unsigned frame; /* 0..(qh->period - 1), or NO_FRAME */ + + qh_refresh(oxu, qh); + qh->hw_next = EHCI_LIST_END; + frame = qh->start; + + /* reuse the previous schedule slots, if we can */ + if (frame < qh->period) { + uframe = ffs(le32_to_cpup(&qh->hw_info2) & QH_SMASK); + status = check_intr_schedule(oxu, frame, --uframe, + qh, &c_mask); + } else { + uframe = 0; + c_mask = 0; + status = -ENOSPC; + } + + /* else scan the schedule to find a group of slots such that all + * uframes have enough periodic bandwidth available. + */ + if (status) { + /* "normal" case, uframing flexible except with splits */ + if (qh->period) { + frame = qh->period - 1; + do { + for (uframe = 0; uframe < 8; uframe++) { + status = check_intr_schedule(oxu, + frame, uframe, qh, + &c_mask); + if (status == 0) + break; + } + } while (status && frame--); + + /* qh->period == 0 means every uframe */ + } else { + frame = 0; + status = check_intr_schedule(oxu, 0, 0, qh, &c_mask); + } + if (status) + goto done; + qh->start = frame; + + /* reset S-frame and (maybe) C-frame masks */ + qh->hw_info2 &= __constant_cpu_to_le32(~(QH_CMASK | QH_SMASK)); + qh->hw_info2 |= qh->period + ? cpu_to_le32(1 << uframe) + : __constant_cpu_to_le32(QH_SMASK); + qh->hw_info2 |= c_mask; + } else + oxu_dbg(oxu, "reused qh %p schedule\n", qh); + + /* stuff into the periodic schedule */ + status = qh_link_periodic(oxu, qh); +done: + return status; +} + +static int intr_submit(struct oxu_hcd *oxu, struct urb *urb, + struct list_head *qtd_list, gfp_t mem_flags) +{ + unsigned epnum; + unsigned long flags; + struct ehci_qh *qh; + int status = 0; + struct list_head empty; + + /* get endpoint and transfer/schedule data */ + epnum = urb->ep->desc.bEndpointAddress; + + spin_lock_irqsave(&oxu->lock, flags); + + if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, + &oxu_to_hcd(oxu)->flags))) { + status = -ESHUTDOWN; + goto done; + } + + /* get qh and force any scheduling errors */ + INIT_LIST_HEAD(&empty); + qh = qh_append_tds(oxu, urb, &empty, epnum, &urb->ep->hcpriv); + if (qh == NULL) { + status = -ENOMEM; + goto done; + } + if (qh->qh_state == QH_STATE_IDLE) { + status = qh_schedule(oxu, qh); + if (status != 0) + goto done; + } + + /* then queue the urb's tds to the qh */ + qh = qh_append_tds(oxu, urb, qtd_list, epnum, &urb->ep->hcpriv); + BUG_ON(qh == NULL); + + /* ... update usbfs periodic stats */ + oxu_to_hcd(oxu)->self.bandwidth_int_reqs++; + +done: + spin_unlock_irqrestore(&oxu->lock, flags); + if (status) + qtd_list_free(oxu, urb, qtd_list); + + return status; +} + +static inline int itd_submit(struct oxu_hcd *oxu, struct urb *urb, + gfp_t mem_flags) +{ + oxu_dbg(oxu, "iso support is missing!\n"); + return -ENOSYS; +} + +static inline int sitd_submit(struct oxu_hcd *oxu, struct urb *urb, + gfp_t mem_flags) +{ + oxu_dbg(oxu, "split iso support is missing!\n"); + return -ENOSYS; +} + +static void scan_periodic(struct oxu_hcd *oxu) +{ + unsigned frame, clock, now_uframe, mod; + unsigned modified; + + mod = oxu->periodic_size << 3; + + /* + * When running, scan from last scan point up to "now" + * else clean up by scanning everything that's left. + * Touches as few pages as possible: cache-friendly. + */ + now_uframe = oxu->next_uframe; + if (HC_IS_RUNNING(oxu_to_hcd(oxu)->state)) + clock = readl(&oxu->regs->frame_index); + else + clock = now_uframe + mod - 1; + clock %= mod; + + for (;;) { + union ehci_shadow q, *q_p; + __le32 type, *hw_p; + unsigned uframes; + + /* don't scan past the live uframe */ + frame = now_uframe >> 3; + if (frame == (clock >> 3)) + uframes = now_uframe & 0x07; + else { + /* safe to scan the whole frame at once */ + now_uframe |= 0x07; + uframes = 8; + } + +restart: + /* scan each element in frame's queue for completions */ + q_p = &oxu->pshadow[frame]; + hw_p = &oxu->periodic[frame]; + q.ptr = q_p->ptr; + type = Q_NEXT_TYPE(*hw_p); + modified = 0; + + while (q.ptr != NULL) { + union ehci_shadow temp; + int live; + + live = HC_IS_RUNNING(oxu_to_hcd(oxu)->state); + switch (type) { + case Q_TYPE_QH: + /* handle any completions */ + temp.qh = qh_get(q.qh); + type = Q_NEXT_TYPE(q.qh->hw_next); + q = q.qh->qh_next; + modified = qh_completions(oxu, temp.qh); + if (unlikely(list_empty(&temp.qh->qtd_list))) + intr_deschedule(oxu, temp.qh); + qh_put(temp.qh); + break; + default: + dbg("corrupt type %d frame %d shadow %p", + type, frame, q.ptr); + q.ptr = NULL; + } + + /* assume completion callbacks modify the queue */ + if (unlikely(modified)) + goto restart; + } + + /* Stop when we catch up to the HC */ + + /* FIXME: this assumes we won't get lapped when + * latencies climb; that should be rare, but... + * detect it, and just go all the way around. + * FLR might help detect this case, so long as latencies + * don't exceed periodic_size msec (default 1.024 sec). + */ + + /* FIXME: likewise assumes HC doesn't halt mid-scan */ + + if (now_uframe == clock) { + unsigned now; + + if (!HC_IS_RUNNING(oxu_to_hcd(oxu)->state)) + break; + oxu->next_uframe = now_uframe; + now = readl(&oxu->regs->frame_index) % mod; + if (now_uframe == now) + break; + + /* rescan the rest of this frame, then ... */ + clock = now; + } else { + now_uframe++; + now_uframe %= mod; + } + } +} + +/* On some systems, leaving remote wakeup enabled prevents system shutdown. + * The firmware seems to think that powering off is a wakeup event! + * This routine turns off remote wakeup and everything else, on all ports. + */ +static void ehci_turn_off_all_ports(struct oxu_hcd *oxu) +{ + int port = HCS_N_PORTS(oxu->hcs_params); + + while (port--) + writel(PORT_RWC_BITS, &oxu->regs->port_status[port]); +} + +static void ehci_port_power(struct oxu_hcd *oxu, int is_on) +{ + unsigned port; + + if (!HCS_PPC(oxu->hcs_params)) + return; + + oxu_dbg(oxu, "...power%s ports...\n", is_on ? "up" : "down"); + for (port = HCS_N_PORTS(oxu->hcs_params); port > 0; ) + (void) oxu_hub_control(oxu_to_hcd(oxu), + is_on ? SetPortFeature : ClearPortFeature, + USB_PORT_FEAT_POWER, + port--, NULL, 0); + msleep(20); +} + +/* Called from some interrupts, timers, and so on. + * It calls driver completion functions, after dropping oxu->lock. + */ +static void ehci_work(struct oxu_hcd *oxu) +{ + timer_action_done(oxu, TIMER_IO_WATCHDOG); + if (oxu->reclaim_ready) + end_unlink_async(oxu); + + /* another CPU may drop oxu->lock during a schedule scan while + * it reports urb completions. this flag guards against bogus + * attempts at re-entrant schedule scanning. + */ + if (oxu->scanning) + return; + oxu->scanning = 1; + scan_async(oxu); + if (oxu->next_uframe != -1) + scan_periodic(oxu); + oxu->scanning = 0; + + /* the IO watchdog guards against hardware or driver bugs that + * misplace IRQs, and should let us run completely without IRQs. + * such lossage has been observed on both VT6202 and VT8235. + */ + if (HC_IS_RUNNING(oxu_to_hcd(oxu)->state) && + (oxu->async->qh_next.ptr != NULL || + oxu->periodic_sched != 0)) + timer_action(oxu, TIMER_IO_WATCHDOG); +} + +static void unlink_async(struct oxu_hcd *oxu, struct ehci_qh *qh) +{ + /* if we need to use IAA and it's busy, defer */ + if (qh->qh_state == QH_STATE_LINKED + && oxu->reclaim + && HC_IS_RUNNING(oxu_to_hcd(oxu)->state)) { + struct ehci_qh *last; + + for (last = oxu->reclaim; + last->reclaim; + last = last->reclaim) + continue; + qh->qh_state = QH_STATE_UNLINK_WAIT; + last->reclaim = qh; + + /* bypass IAA if the hc can't care */ + } else if (!HC_IS_RUNNING(oxu_to_hcd(oxu)->state) && oxu->reclaim) + end_unlink_async(oxu); + + /* something else might have unlinked the qh by now */ + if (qh->qh_state == QH_STATE_LINKED) + start_unlink_async(oxu, qh); +} + +/* + * USB host controller methods + */ + +static irqreturn_t oxu210_hcd_irq(struct usb_hcd *hcd) +{ + struct oxu_hcd *oxu = hcd_to_oxu(hcd); + u32 status, pcd_status = 0; + int bh; + + spin_lock(&oxu->lock); + + status = readl(&oxu->regs->status); + + /* e.g. cardbus physical eject */ + if (status == ~(u32) 0) { + oxu_dbg(oxu, "device removed\n"); + goto dead; + } + + status &= INTR_MASK; + if (!status) { /* irq sharing? */ + spin_unlock(&oxu->lock); + return IRQ_NONE; + } + + /* clear (just) interrupts */ + writel(status, &oxu->regs->status); + readl(&oxu->regs->command); /* unblock posted write */ + bh = 0; + +#ifdef OXU_VERBOSE_DEBUG + /* unrequested/ignored: Frame List Rollover */ + dbg_status(oxu, "irq", status); +#endif + + /* INT, ERR, and IAA interrupt rates can be throttled */ + + /* normal [4.15.1.2] or error [4.15.1.1] completion */ + if (likely((status & (STS_INT|STS_ERR)) != 0)) + bh = 1; + + /* complete the unlinking of some qh [4.15.2.3] */ + if (status & STS_IAA) { + oxu->reclaim_ready = 1; + bh = 1; + } + + /* remote wakeup [4.3.1] */ + if (status & STS_PCD) { + unsigned i = HCS_N_PORTS(oxu->hcs_params); + pcd_status = status; + + /* resume root hub? */ + if (!(readl(&oxu->regs->command) & CMD_RUN)) + usb_hcd_resume_root_hub(hcd); + + while (i--) { + int pstatus = readl(&oxu->regs->port_status[i]); + + if (pstatus & PORT_OWNER) + continue; + if (!(pstatus & PORT_RESUME) + || oxu->reset_done[i] != 0) + continue; + + /* start 20 msec resume signaling from this port, + * and make khubd collect PORT_STAT_C_SUSPEND to + * stop that signaling. + */ + oxu->reset_done[i] = jiffies + msecs_to_jiffies(20); + oxu_dbg(oxu, "port %d remote wakeup\n", i + 1); + mod_timer(&hcd->rh_timer, oxu->reset_done[i]); + } + } + + /* PCI errors [4.15.2.4] */ + if (unlikely((status & STS_FATAL) != 0)) { + /* bogus "fatal" IRQs appear on some chips... why? */ + status = readl(&oxu->regs->status); + dbg_cmd(oxu, "fatal", readl(&oxu->regs->command)); + dbg_status(oxu, "fatal", status); + if (status & STS_HALT) { + oxu_err(oxu, "fatal error\n"); +dead: + ehci_reset(oxu); + writel(0, &oxu->regs->configured_flag); + /* generic layer kills/unlinks all urbs, then + * uses oxu_stop to clean up the rest + */ + bh = 1; + } + } + + if (bh) + ehci_work(oxu); + spin_unlock(&oxu->lock); + if (pcd_status & STS_PCD) + usb_hcd_poll_rh_status(hcd); + return IRQ_HANDLED; +} + +static irqreturn_t oxu_irq(struct usb_hcd *hcd) +{ + struct oxu_hcd *oxu = hcd_to_oxu(hcd); + int ret = IRQ_HANDLED; + + u32 status = oxu_readl(hcd->regs, OXU_CHIPIRQSTATUS); + u32 enable = oxu_readl(hcd->regs, OXU_CHIPIRQEN_SET); + + /* Disable all interrupt */ + oxu_writel(hcd->regs, OXU_CHIPIRQEN_CLR, enable); + + if ((oxu->is_otg && (status & OXU_USBOTGI)) || + (!oxu->is_otg && (status & OXU_USBSPHI))) + oxu210_hcd_irq(hcd); + else + ret = IRQ_NONE; + + /* Enable all interrupt back */ + oxu_writel(hcd->regs, OXU_CHIPIRQEN_SET, enable); + + return ret; +} + +static void oxu_watchdog(unsigned long param) +{ + struct oxu_hcd *oxu = (struct oxu_hcd *) param; + unsigned long flags; + + spin_lock_irqsave(&oxu->lock, flags); + + /* lost IAA irqs wedge things badly; seen with a vt8235 */ + if (oxu->reclaim) { + u32 status = readl(&oxu->regs->status); + if (status & STS_IAA) { + oxu_vdbg(oxu, "lost IAA\n"); + writel(STS_IAA, &oxu->regs->status); + oxu->reclaim_ready = 1; + } + } + + /* stop async processing after it's idled a bit */ + if (test_bit(TIMER_ASYNC_OFF, &oxu->actions)) + start_unlink_async(oxu, oxu->async); + + /* oxu could run by timer, without IRQs ... */ + ehci_work(oxu); + + spin_unlock_irqrestore(&oxu->lock, flags); +} + +/* One-time init, only for memory state. + */ +static int oxu_hcd_init(struct usb_hcd *hcd) +{ + struct oxu_hcd *oxu = hcd_to_oxu(hcd); + u32 temp; + int retval; + u32 hcc_params; + + spin_lock_init(&oxu->lock); + + init_timer(&oxu->watchdog); + oxu->watchdog.function = oxu_watchdog; + oxu->watchdog.data = (unsigned long) oxu; + + /* + * hw default: 1K periodic list heads, one per frame. + * periodic_size can shrink by USBCMD update if hcc_params allows. + */ + oxu->periodic_size = DEFAULT_I_TDPS; + retval = ehci_mem_init(oxu, GFP_KERNEL); + if (retval < 0) + return retval; + + /* controllers may cache some of the periodic schedule ... */ + hcc_params = readl(&oxu->caps->hcc_params); + if (HCC_ISOC_CACHE(hcc_params)) /* full frame cache */ + oxu->i_thresh = 8; + else /* N microframes cached */ + oxu->i_thresh = 2 + HCC_ISOC_THRES(hcc_params); + + oxu->reclaim = NULL; + oxu->reclaim_ready = 0; + oxu->next_uframe = -1; + + /* + * dedicate a qh for the async ring head, since we couldn't unlink + * a 'real' qh without stopping the async schedule [4.8]. use it + * as the 'reclamation list head' too. + * its dummy is used in hw_alt_next of many tds, to prevent the qh + * from automatically advancing to the next td after short reads. + */ + oxu->async->qh_next.qh = NULL; + oxu->async->hw_next = QH_NEXT(oxu->async->qh_dma); + oxu->async->hw_info1 = cpu_to_le32(QH_HEAD); + oxu->async->hw_token = cpu_to_le32(QTD_STS_HALT); + oxu->async->hw_qtd_next = EHCI_LIST_END; + oxu->async->qh_state = QH_STATE_LINKED; + oxu->async->hw_alt_next = QTD_NEXT(oxu->async->dummy->qtd_dma); + + /* clear interrupt enables, set irq latency */ + if (log2_irq_thresh < 0 || log2_irq_thresh > 6) + log2_irq_thresh = 0; + temp = 1 << (16 + log2_irq_thresh); + if (HCC_CANPARK(hcc_params)) { + /* HW default park == 3, on hardware that supports it (like + * NVidia and ALI silicon), maximizes throughput on the async + * schedule by avoiding QH fetches between transfers. + * + * With fast usb storage devices and NForce2, "park" seems to + * make problems: throughput reduction (!), data errors... + */ + if (park) { + park = min(park, (unsigned) 3); + temp |= CMD_PARK; + temp |= park << 8; + } + oxu_dbg(oxu, "park %d\n", park); + } + if (HCC_PGM_FRAMELISTLEN(hcc_params)) { + /* periodic schedule size can be smaller than default */ + temp &= ~(3 << 2); + temp |= (EHCI_TUNE_FLS << 2); + } + oxu->command = temp; + + return 0; +} + +/* Called during probe() after chip reset completes. + */ +static int oxu_reset(struct usb_hcd *hcd) +{ + struct oxu_hcd *oxu = hcd_to_oxu(hcd); + int ret; + + spin_lock_init(&oxu->mem_lock); + INIT_LIST_HEAD(&oxu->urb_list); + oxu->urb_len = 0; + + /* FIMXE */ + hcd->self.controller->dma_mask = 0UL; + + if (oxu->is_otg) { + oxu->caps = hcd->regs + OXU_OTG_CAP_OFFSET; + oxu->regs = hcd->regs + OXU_OTG_CAP_OFFSET + \ + HC_LENGTH(readl(&oxu->caps->hc_capbase)); + + oxu->mem = hcd->regs + OXU_SPH_MEM; + } else { + oxu->caps = hcd->regs + OXU_SPH_CAP_OFFSET; + oxu->regs = hcd->regs + OXU_SPH_CAP_OFFSET + \ + HC_LENGTH(readl(&oxu->caps->hc_capbase)); + + oxu->mem = hcd->regs + OXU_OTG_MEM; + } + + oxu->hcs_params = readl(&oxu->caps->hcs_params); + oxu->sbrn = 0x20; + + ret = oxu_hcd_init(hcd); + if (ret) + return ret; + + return 0; +} + +static int oxu_run(struct usb_hcd *hcd) +{ + struct oxu_hcd *oxu = hcd_to_oxu(hcd); + int retval; + u32 temp, hcc_params; + + hcd->uses_new_polling = 1; + hcd->poll_rh = 0; + + /* EHCI spec section 4.1 */ + retval = ehci_reset(oxu); + if (retval != 0) { + ehci_mem_cleanup(oxu); + return retval; + } + writel(oxu->periodic_dma, &oxu->regs->frame_list); + writel((u32) oxu->async->qh_dma, &oxu->regs->async_next); + + /* hcc_params controls whether oxu->regs->segment must (!!!) + * be used; it constrains QH/ITD/SITD and QTD locations. + * pci_pool consistent memory always uses segment zero. + * streaming mappings for I/O buffers, like pci_map_single(), + * can return segments above 4GB, if the device allows. + * + * NOTE: the dma mask is visible through dma_supported(), so + * drivers can pass this info along ... like NETIF_F_HIGHDMA, + * Scsi_Host.highmem_io, and so forth. It's readonly to all + * host side drivers though. + */ + hcc_params = readl(&oxu->caps->hcc_params); + if (HCC_64BIT_ADDR(hcc_params)) + writel(0, &oxu->regs->segment); + + oxu->command &= ~(CMD_LRESET | CMD_IAAD | CMD_PSE | + CMD_ASE | CMD_RESET); + oxu->command |= CMD_RUN; + writel(oxu->command, &oxu->regs->command); + dbg_cmd(oxu, "init", oxu->command); + + /* + * Start, enabling full USB 2.0 functionality ... usb 1.1 devices + * are explicitly handed to companion controller(s), so no TT is + * involved with the root hub. (Except where one is integrated, + * and there's no companion controller unless maybe for USB OTG.) + */ + hcd->state = HC_STATE_RUNNING; + writel(FLAG_CF, &oxu->regs->configured_flag); + readl(&oxu->regs->command); /* unblock posted writes */ + + temp = HC_VERSION(readl(&oxu->caps->hc_capbase)); + oxu_info(oxu, "USB %x.%x started, quasi-EHCI %x.%02x, driver %s%s\n", + ((oxu->sbrn & 0xf0)>>4), (oxu->sbrn & 0x0f), + temp >> 8, temp & 0xff, DRIVER_VERSION, + ignore_oc ? ", overcurrent ignored" : ""); + + writel(INTR_MASK, &oxu->regs->intr_enable); /* Turn On Interrupts */ + + return 0; +} + +static void oxu_stop(struct usb_hcd *hcd) +{ + struct oxu_hcd *oxu = hcd_to_oxu(hcd); + + /* Turn off port power on all root hub ports. */ + ehci_port_power(oxu, 0); + + /* no more interrupts ... */ + del_timer_sync(&oxu->watchdog); + + spin_lock_irq(&oxu->lock); + if (HC_IS_RUNNING(hcd->state)) + ehci_quiesce(oxu); + + ehci_reset(oxu); + writel(0, &oxu->regs->intr_enable); + spin_unlock_irq(&oxu->lock); + + /* let companion controllers work when we aren't */ + writel(0, &oxu->regs->configured_flag); + + /* root hub is shut down separately (first, when possible) */ + spin_lock_irq(&oxu->lock); + if (oxu->async) + ehci_work(oxu); + spin_unlock_irq(&oxu->lock); + ehci_mem_cleanup(oxu); + + dbg_status(oxu, "oxu_stop completed", readl(&oxu->regs->status)); +} + +/* Kick in for silicon on any bus (not just pci, etc). + * This forcibly disables dma and IRQs, helping kexec and other cases + * where the next system software may expect clean state. + */ +static void oxu_shutdown(struct usb_hcd *hcd) +{ + struct oxu_hcd *oxu = hcd_to_oxu(hcd); + + (void) ehci_halt(oxu); + ehci_turn_off_all_ports(oxu); + + /* make BIOS/etc use companion controller during reboot */ + writel(0, &oxu->regs->configured_flag); + + /* unblock posted writes */ + readl(&oxu->regs->configured_flag); +} + +/* Non-error returns are a promise to giveback() the urb later + * we drop ownership so next owner (or urb unlink) can get it + * + * urb + dev is in hcd.self.controller.urb_list + * we're queueing TDs onto software and hardware lists + * + * hcd-specific init for hcpriv hasn't been done yet + * + * NOTE: control, bulk, and interrupt share the same code to append TDs + * to a (possibly active) QH, and the same QH scanning code. + */ +static int __oxu_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, + gfp_t mem_flags) +{ + struct oxu_hcd *oxu = hcd_to_oxu(hcd); + struct list_head qtd_list; + + INIT_LIST_HEAD(&qtd_list); + + switch (usb_pipetype(urb->pipe)) { + case PIPE_CONTROL: + case PIPE_BULK: + default: + if (!qh_urb_transaction(oxu, urb, &qtd_list, mem_flags)) + return -ENOMEM; + return submit_async(oxu, urb, &qtd_list, mem_flags); + + case PIPE_INTERRUPT: + if (!qh_urb_transaction(oxu, urb, &qtd_list, mem_flags)) + return -ENOMEM; + return intr_submit(oxu, urb, &qtd_list, mem_flags); + + case PIPE_ISOCHRONOUS: + if (urb->dev->speed == USB_SPEED_HIGH) + return itd_submit(oxu, urb, mem_flags); + else + return sitd_submit(oxu, urb, mem_flags); + } +} + +/* This function is responsible for breaking URBs with big data size + * into smaller size and processing small urbs in sequence. + */ +static int oxu_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, + gfp_t mem_flags) +{ + struct oxu_hcd *oxu = hcd_to_oxu(hcd); + int num, rem; + int transfer_buffer_length; + void *transfer_buffer; + struct urb *murb; + int i, ret; + + /* If not bulk pipe just enqueue the URB */ + if (!usb_pipebulk(urb->pipe)) + return __oxu_urb_enqueue(hcd, urb, mem_flags); + + /* Otherwise we should verify the USB transfer buffer size! */ + transfer_buffer = urb->transfer_buffer; + transfer_buffer_length = urb->transfer_buffer_length; + + num = urb->transfer_buffer_length / 4096; + rem = urb->transfer_buffer_length % 4096; + if (rem != 0) + num++; + + /* If URB is smaller than 4096 bytes just enqueue it! */ + if (num == 1) + return __oxu_urb_enqueue(hcd, urb, mem_flags); + + /* Ok, we have more job to do! :) */ + + for (i = 0; i < num - 1; i++) { + /* Get free micro URB poll till a free urb is recieved */ + + do { + murb = (struct urb *) oxu_murb_alloc(oxu); + if (!murb) + schedule(); + } while (!murb); + + /* Coping the urb */ + memcpy(murb, urb, sizeof(struct urb)); + + murb->transfer_buffer_length = 4096; + murb->transfer_buffer = transfer_buffer + i * 4096; + + /* Null pointer for the encodes that this is a micro urb */ + murb->complete = NULL; + + ((struct oxu_murb *) murb)->main = urb; + ((struct oxu_murb *) murb)->last = 0; + + /* This loop is to guarantee urb to be processed when there's + * not enough resources at a particular time by retrying. + */ + do { + ret = __oxu_urb_enqueue(hcd, murb, mem_flags); + if (ret) + schedule(); + } while (ret); + } + + /* Last urb requires special handling */ + + /* Get free micro URB poll till a free urb is recieved */ + do { + murb = (struct urb *) oxu_murb_alloc(oxu); + if (!murb) + schedule(); + } while (!murb); + + /* Coping the urb */ + memcpy(murb, urb, sizeof(struct urb)); + + murb->transfer_buffer_length = rem > 0 ? rem : 4096; + murb->transfer_buffer = transfer_buffer + (num - 1) * 4096; + + /* Null pointer for the encodes that this is a micro urb */ + murb->complete = NULL; + + ((struct oxu_murb *) murb)->main = urb; + ((struct oxu_murb *) murb)->last = 1; + + do { + ret = __oxu_urb_enqueue(hcd, murb, mem_flags); + if (ret) + schedule(); + } while (ret); + + return ret; +} + +/* Remove from hardware lists. + * Completions normally happen asynchronously + */ +static int oxu_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) +{ + struct oxu_hcd *oxu = hcd_to_oxu(hcd); + struct ehci_qh *qh; + unsigned long flags; + + spin_lock_irqsave(&oxu->lock, flags); + switch (usb_pipetype(urb->pipe)) { + case PIPE_CONTROL: + case PIPE_BULK: + default: + qh = (struct ehci_qh *) urb->hcpriv; + if (!qh) + break; + unlink_async(oxu, qh); + break; + + case PIPE_INTERRUPT: + qh = (struct ehci_qh *) urb->hcpriv; + if (!qh) + break; + switch (qh->qh_state) { + case QH_STATE_LINKED: + intr_deschedule(oxu, qh); + /* FALL THROUGH */ + case QH_STATE_IDLE: + qh_completions(oxu, qh); + break; + default: + oxu_dbg(oxu, "bogus qh %p state %d\n", + qh, qh->qh_state); + goto done; + } + + /* reschedule QH iff another request is queued */ + if (!list_empty(&qh->qtd_list) + && HC_IS_RUNNING(hcd->state)) { + int status; + + status = qh_schedule(oxu, qh); + spin_unlock_irqrestore(&oxu->lock, flags); + + if (status != 0) { + /* shouldn't happen often, but ... + * FIXME kill those tds' urbs + */ + err("can't reschedule qh %p, err %d", + qh, status); + } + return status; + } + break; + } +done: + spin_unlock_irqrestore(&oxu->lock, flags); + return 0; +} + +/* Bulk qh holds the data toggle */ +static void oxu_endpoint_disable(struct usb_hcd *hcd, + struct usb_host_endpoint *ep) +{ + struct oxu_hcd *oxu = hcd_to_oxu(hcd); + unsigned long flags; + struct ehci_qh *qh, *tmp; + + /* ASSERT: any requests/urbs are being unlinked */ + /* ASSERT: nobody can be submitting urbs for this any more */ + +rescan: + spin_lock_irqsave(&oxu->lock, flags); + qh = ep->hcpriv; + if (!qh) + goto done; + + /* endpoints can be iso streams. for now, we don't + * accelerate iso completions ... so spin a while. + */ + if (qh->hw_info1 == 0) { + oxu_vdbg(oxu, "iso delay\n"); + goto idle_timeout; + } + + if (!HC_IS_RUNNING(hcd->state)) + qh->qh_state = QH_STATE_IDLE; + switch (qh->qh_state) { + case QH_STATE_LINKED: + for (tmp = oxu->async->qh_next.qh; + tmp && tmp != qh; + tmp = tmp->qh_next.qh) + continue; + /* periodic qh self-unlinks on empty */ + if (!tmp) + goto nogood; + unlink_async(oxu, qh); + /* FALL THROUGH */ + case QH_STATE_UNLINK: /* wait for hw to finish? */ +idle_timeout: + spin_unlock_irqrestore(&oxu->lock, flags); + schedule_timeout_uninterruptible(1); + goto rescan; + case QH_STATE_IDLE: /* fully unlinked */ + if (list_empty(&qh->qtd_list)) { + qh_put(qh); + break; + } + /* else FALL THROUGH */ + default: +nogood: + /* caller was supposed to have unlinked any requests; + * that's not our job. just leak this memory. + */ + oxu_err(oxu, "qh %p (#%02x) state %d%s\n", + qh, ep->desc.bEndpointAddress, qh->qh_state, + list_empty(&qh->qtd_list) ? "" : "(has tds)"); + break; + } + ep->hcpriv = NULL; +done: + spin_unlock_irqrestore(&oxu->lock, flags); + return; +} + +static int oxu_get_frame(struct usb_hcd *hcd) +{ + struct oxu_hcd *oxu = hcd_to_oxu(hcd); + + return (readl(&oxu->regs->frame_index) >> 3) % + oxu->periodic_size; +} + +/* Build "status change" packet (one or two bytes) from HC registers */ +static int oxu_hub_status_data(struct usb_hcd *hcd, char *buf) +{ + struct oxu_hcd *oxu = hcd_to_oxu(hcd); + u32 temp, mask, status = 0; + int ports, i, retval = 1; + unsigned long flags; + + /* if !USB_SUSPEND, root hub timers won't get shut down ... */ + if (!HC_IS_RUNNING(hcd->state)) + return 0; + + /* init status to no-changes */ + buf[0] = 0; + ports = HCS_N_PORTS(oxu->hcs_params); + if (ports > 7) { + buf[1] = 0; + retval++; + } + + /* Some boards (mostly VIA?) report bogus overcurrent indications, + * causing massive log spam unless we completely ignore them. It + * may be relevant that VIA VT8235 controlers, where PORT_POWER is + * always set, seem to clear PORT_OCC and PORT_CSC when writing to + * PORT_POWER; that's surprising, but maybe within-spec. + */ + if (!ignore_oc) + mask = PORT_CSC | PORT_PEC | PORT_OCC; + else + mask = PORT_CSC | PORT_PEC; + + /* no hub change reports (bit 0) for now (power, ...) */ + + /* port N changes (bit N)? */ + spin_lock_irqsave(&oxu->lock, flags); + for (i = 0; i < ports; i++) { + temp = readl(&oxu->regs->port_status[i]); + + /* + * Return status information even for ports with OWNER set. + * Otherwise khubd wouldn't see the disconnect event when a + * high-speed device is switched over to the companion + * controller by the user. + */ + + if (!(temp & PORT_CONNECT)) + oxu->reset_done[i] = 0; + if ((temp & mask) != 0 || ((temp & PORT_RESUME) != 0 && + time_after_eq(jiffies, oxu->reset_done[i]))) { + if (i < 7) + buf[0] |= 1 << (i + 1); + else + buf[1] |= 1 << (i - 7); + status = STS_PCD; + } + } + /* FIXME autosuspend idle root hubs */ + spin_unlock_irqrestore(&oxu->lock, flags); + return status ? retval : 0; +} + +/* Returns the speed of a device attached to a port on the root hub. */ +static inline unsigned int oxu_port_speed(struct oxu_hcd *oxu, + unsigned int portsc) +{ + switch ((portsc >> 26) & 3) { + case 0: + return 0; + case 1: + return 1 << USB_PORT_FEAT_LOWSPEED; + case 2: + default: + return 1 << USB_PORT_FEAT_HIGHSPEED; + } +} + +#define PORT_WAKE_BITS (PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E) +static int oxu_hub_control(struct usb_hcd *hcd, u16 typeReq, + u16 wValue, u16 wIndex, char *buf, u16 wLength) +{ + struct oxu_hcd *oxu = hcd_to_oxu(hcd); + int ports = HCS_N_PORTS(oxu->hcs_params); + u32 __iomem *status_reg = &oxu->regs->port_status[wIndex - 1]; + u32 temp, status; + unsigned long flags; + int retval = 0; + unsigned selector; + + /* + * FIXME: support SetPortFeatures USB_PORT_FEAT_INDICATOR. + * HCS_INDICATOR may say we can change LEDs to off/amber/green. + * (track current state ourselves) ... blink for diagnostics, + * power, "this is the one", etc. EHCI spec supports this. + */ + + spin_lock_irqsave(&oxu->lock, flags); + switch (typeReq) { + case ClearHubFeature: + switch (wValue) { + case C_HUB_LOCAL_POWER: + case C_HUB_OVER_CURRENT: + /* no hub-wide feature/status flags */ + break; + default: + goto error; + } + break; + case ClearPortFeature: + if (!wIndex || wIndex > ports) + goto error; + wIndex--; + temp = readl(status_reg); + + /* + * Even if OWNER is set, so the port is owned by the + * companion controller, khubd needs to be able to clear + * the port-change status bits (especially + * USB_PORT_FEAT_C_CONNECTION). + */ + + switch (wValue) { + case USB_PORT_FEAT_ENABLE: + writel(temp & ~PORT_PE, status_reg); + break; + case USB_PORT_FEAT_C_ENABLE: + writel((temp & ~PORT_RWC_BITS) | PORT_PEC, status_reg); + break; + case USB_PORT_FEAT_SUSPEND: + if (temp & PORT_RESET) + goto error; + if (temp & PORT_SUSPEND) { + if ((temp & PORT_PE) == 0) + goto error; + /* resume signaling for 20 msec */ + temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); + writel(temp | PORT_RESUME, status_reg); + oxu->reset_done[wIndex] = jiffies + + msecs_to_jiffies(20); + } + break; + case USB_PORT_FEAT_C_SUSPEND: + /* we auto-clear this feature */ + break; + case USB_PORT_FEAT_POWER: + if (HCS_PPC(oxu->hcs_params)) + writel(temp & ~(PORT_RWC_BITS | PORT_POWER), + status_reg); + break; + case USB_PORT_FEAT_C_CONNECTION: + writel((temp & ~PORT_RWC_BITS) | PORT_CSC, status_reg); + break; + case USB_PORT_FEAT_C_OVER_CURRENT: + writel((temp & ~PORT_RWC_BITS) | PORT_OCC, status_reg); + break; + case USB_PORT_FEAT_C_RESET: + /* GetPortStatus clears reset */ + break; + default: + goto error; + } + readl(&oxu->regs->command); /* unblock posted write */ + break; + case GetHubDescriptor: + ehci_hub_descriptor(oxu, (struct usb_hub_descriptor *) + buf); + break; + case GetHubStatus: + /* no hub-wide feature/status flags */ + memset(buf, 0, 4); + break; + case GetPortStatus: + if (!wIndex || wIndex > ports) + goto error; + wIndex--; + status = 0; + temp = readl(status_reg); + + /* wPortChange bits */ + if (temp & PORT_CSC) + status |= 1 << USB_PORT_FEAT_C_CONNECTION; + if (temp & PORT_PEC) + status |= 1 << USB_PORT_FEAT_C_ENABLE; + if ((temp & PORT_OCC) && !ignore_oc) + status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT; + + /* whoever resumes must GetPortStatus to complete it!! */ + if (temp & PORT_RESUME) { + + /* Remote Wakeup received? */ + if (!oxu->reset_done[wIndex]) { + /* resume signaling for 20 msec */ + oxu->reset_done[wIndex] = jiffies + + msecs_to_jiffies(20); + /* check the port again */ + mod_timer(&oxu_to_hcd(oxu)->rh_timer, + oxu->reset_done[wIndex]); + } + + /* resume completed? */ + else if (time_after_eq(jiffies, + oxu->reset_done[wIndex])) { + status |= 1 << USB_PORT_FEAT_C_SUSPEND; + oxu->reset_done[wIndex] = 0; + + /* stop resume signaling */ + temp = readl(status_reg); + writel(temp & ~(PORT_RWC_BITS | PORT_RESUME), + status_reg); + retval = handshake(oxu, status_reg, + PORT_RESUME, 0, 2000 /* 2msec */); + if (retval != 0) { + oxu_err(oxu, + "port %d resume error %d\n", + wIndex + 1, retval); + goto error; + } + temp &= ~(PORT_SUSPEND|PORT_RESUME|(3<<10)); + } + } + + /* whoever resets must GetPortStatus to complete it!! */ + if ((temp & PORT_RESET) + && time_after_eq(jiffies, + oxu->reset_done[wIndex])) { + status |= 1 << USB_PORT_FEAT_C_RESET; + oxu->reset_done[wIndex] = 0; + + /* force reset to complete */ + writel(temp & ~(PORT_RWC_BITS | PORT_RESET), + status_reg); + /* REVISIT: some hardware needs 550+ usec to clear + * this bit; seems too long to spin routinely... + */ + retval = handshake(oxu, status_reg, + PORT_RESET, 0, 750); + if (retval != 0) { + oxu_err(oxu, "port %d reset error %d\n", + wIndex + 1, retval); + goto error; + } + + /* see what we found out */ + temp = check_reset_complete(oxu, wIndex, status_reg, + readl(status_reg)); + } + + /* transfer dedicated ports to the companion hc */ + if ((temp & PORT_CONNECT) && + test_bit(wIndex, &oxu->companion_ports)) { + temp &= ~PORT_RWC_BITS; + temp |= PORT_OWNER; + writel(temp, status_reg); + oxu_dbg(oxu, "port %d --> companion\n", wIndex + 1); + temp = readl(status_reg); + } + + /* + * Even if OWNER is set, there's no harm letting khubd + * see the wPortStatus values (they should all be 0 except + * for PORT_POWER anyway). + */ + + if (temp & PORT_CONNECT) { + status |= 1 << USB_PORT_FEAT_CONNECTION; + /* status may be from integrated TT */ + status |= oxu_port_speed(oxu, temp); + } + if (temp & PORT_PE) + status |= 1 << USB_PORT_FEAT_ENABLE; + if (temp & (PORT_SUSPEND|PORT_RESUME)) + status |= 1 << USB_PORT_FEAT_SUSPEND; + if (temp & PORT_OC) + status |= 1 << USB_PORT_FEAT_OVER_CURRENT; + if (temp & PORT_RESET) + status |= 1 << USB_PORT_FEAT_RESET; + if (temp & PORT_POWER) + status |= 1 << USB_PORT_FEAT_POWER; + +#ifndef OXU_VERBOSE_DEBUG + if (status & ~0xffff) /* only if wPortChange is interesting */ +#endif + dbg_port(oxu, "GetStatus", wIndex + 1, temp); + put_unaligned(cpu_to_le32(status), (__le32 *) buf); + break; + case SetHubFeature: + switch (wValue) { + case C_HUB_LOCAL_POWER: + case C_HUB_OVER_CURRENT: + /* no hub-wide feature/status flags */ + break; + default: + goto error; + } + break; + case SetPortFeature: + selector = wIndex >> 8; + wIndex &= 0xff; + if (!wIndex || wIndex > ports) + goto error; + wIndex--; + temp = readl(status_reg); + if (temp & PORT_OWNER) + break; + + temp &= ~PORT_RWC_BITS; + switch (wValue) { + case USB_PORT_FEAT_SUSPEND: + if ((temp & PORT_PE) == 0 + || (temp & PORT_RESET) != 0) + goto error; + if (device_may_wakeup(&hcd->self.root_hub->dev)) + temp |= PORT_WAKE_BITS; + writel(temp | PORT_SUSPEND, status_reg); + break; + case USB_PORT_FEAT_POWER: + if (HCS_PPC(oxu->hcs_params)) + writel(temp | PORT_POWER, status_reg); + break; + case USB_PORT_FEAT_RESET: + if (temp & PORT_RESUME) + goto error; + /* line status bits may report this as low speed, + * which can be fine if this root hub has a + * transaction translator built in. + */ + oxu_vdbg(oxu, "port %d reset\n", wIndex + 1); + temp |= PORT_RESET; + temp &= ~PORT_PE; + + /* + * caller must wait, then call GetPortStatus + * usb 2.0 spec says 50 ms resets on root + */ + oxu->reset_done[wIndex] = jiffies + + msecs_to_jiffies(50); + writel(temp, status_reg); + break; + + /* For downstream facing ports (these): one hub port is put + * into test mode according to USB2 11.24.2.13, then the hub + * must be reset (which for root hub now means rmmod+modprobe, + * or else system reboot). See EHCI 2.3.9 and 4.14 for info + * about the EHCI-specific stuff. + */ + case USB_PORT_FEAT_TEST: + if (!selector || selector > 5) + goto error; + ehci_quiesce(oxu); + ehci_halt(oxu); + temp |= selector << 16; + writel(temp, status_reg); + break; + + default: + goto error; + } + readl(&oxu->regs->command); /* unblock posted writes */ + break; + + default: +error: + /* "stall" on error */ + retval = -EPIPE; + } + spin_unlock_irqrestore(&oxu->lock, flags); + return retval; +} + +#ifdef CONFIG_PM + +static int oxu_bus_suspend(struct usb_hcd *hcd) +{ + struct oxu_hcd *oxu = hcd_to_oxu(hcd); + int port; + int mask; + + oxu_dbg(oxu, "suspend root hub\n"); + + if (time_before(jiffies, oxu->next_statechange)) + msleep(5); + + port = HCS_N_PORTS(oxu->hcs_params); + spin_lock_irq(&oxu->lock); + + /* stop schedules, clean any completed work */ + if (HC_IS_RUNNING(hcd->state)) { + ehci_quiesce(oxu); + hcd->state = HC_STATE_QUIESCING; + } + oxu->command = readl(&oxu->regs->command); + if (oxu->reclaim) + oxu->reclaim_ready = 1; + ehci_work(oxu); + + /* Unlike other USB host controller types, EHCI doesn't have + * any notion of "global" or bus-wide suspend. The driver has + * to manually suspend all the active unsuspended ports, and + * then manually resume them in the bus_resume() routine. + */ + oxu->bus_suspended = 0; + while (port--) { + u32 __iomem *reg = &oxu->regs->port_status[port]; + u32 t1 = readl(reg) & ~PORT_RWC_BITS; + u32 t2 = t1; + + /* keep track of which ports we suspend */ + if ((t1 & PORT_PE) && !(t1 & PORT_OWNER) && + !(t1 & PORT_SUSPEND)) { + t2 |= PORT_SUSPEND; + set_bit(port, &oxu->bus_suspended); + } + + /* enable remote wakeup on all ports */ + if (device_may_wakeup(&hcd->self.root_hub->dev)) + t2 |= PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E; + else + t2 &= ~(PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E); + + if (t1 != t2) { + oxu_vdbg(oxu, "port %d, %08x -> %08x\n", + port + 1, t1, t2); + writel(t2, reg); + } + } + + /* turn off now-idle HC */ + del_timer_sync(&oxu->watchdog); + ehci_halt(oxu); + hcd->state = HC_STATE_SUSPENDED; + + /* allow remote wakeup */ + mask = INTR_MASK; + if (!device_may_wakeup(&hcd->self.root_hub->dev)) + mask &= ~STS_PCD; + writel(mask, &oxu->regs->intr_enable); + readl(&oxu->regs->intr_enable); + + oxu->next_statechange = jiffies + msecs_to_jiffies(10); + spin_unlock_irq(&oxu->lock); + return 0; +} + +/* Caller has locked the root hub, and should reset/reinit on error */ +static int oxu_bus_resume(struct usb_hcd *hcd) +{ + struct oxu_hcd *oxu = hcd_to_oxu(hcd); + u32 temp; + int i; + + if (time_before(jiffies, oxu->next_statechange)) + msleep(5); + spin_lock_irq(&oxu->lock); + + /* Ideally and we've got a real resume here, and no port's power + * was lost. (For PCI, that means Vaux was maintained.) But we + * could instead be restoring a swsusp snapshot -- so that BIOS was + * the last user of the controller, not reset/pm hardware keeping + * state we gave to it. + */ + temp = readl(&oxu->regs->intr_enable); + oxu_dbg(oxu, "resume root hub%s\n", temp ? "" : " after power loss"); + + /* at least some APM implementations will try to deliver + * IRQs right away, so delay them until we're ready. + */ + writel(0, &oxu->regs->intr_enable); + + /* re-init operational registers */ + writel(0, &oxu->regs->segment); + writel(oxu->periodic_dma, &oxu->regs->frame_list); + writel((u32) oxu->async->qh_dma, &oxu->regs->async_next); + + /* restore CMD_RUN, framelist size, and irq threshold */ + writel(oxu->command, &oxu->regs->command); + + /* Some controller/firmware combinations need a delay during which + * they set up the port statuses. See Bugzilla #8190. */ + mdelay(8); + + /* manually resume the ports we suspended during bus_suspend() */ + i = HCS_N_PORTS(oxu->hcs_params); + while (i--) { + temp = readl(&oxu->regs->port_status[i]); + temp &= ~(PORT_RWC_BITS + | PORT_WKOC_E | PORT_WKDISC_E | PORT_WKCONN_E); + if (test_bit(i, &oxu->bus_suspended) && (temp & PORT_SUSPEND)) { + oxu->reset_done[i] = jiffies + msecs_to_jiffies(20); + temp |= PORT_RESUME; + } + writel(temp, &oxu->regs->port_status[i]); + } + i = HCS_N_PORTS(oxu->hcs_params); + mdelay(20); + while (i--) { + temp = readl(&oxu->regs->port_status[i]); + if (test_bit(i, &oxu->bus_suspended) && (temp & PORT_SUSPEND)) { + temp &= ~(PORT_RWC_BITS | PORT_RESUME); + writel(temp, &oxu->regs->port_status[i]); + oxu_vdbg(oxu, "resumed port %d\n", i + 1); + } + } + (void) readl(&oxu->regs->command); + + /* maybe re-activate the schedule(s) */ + temp = 0; + if (oxu->async->qh_next.qh) + temp |= CMD_ASE; + if (oxu->periodic_sched) + temp |= CMD_PSE; + if (temp) { + oxu->command |= temp; + writel(oxu->command, &oxu->regs->command); + } + + oxu->next_statechange = jiffies + msecs_to_jiffies(5); + hcd->state = HC_STATE_RUNNING; + + /* Now we can safely re-enable irqs */ + writel(INTR_MASK, &oxu->regs->intr_enable); + + spin_unlock_irq(&oxu->lock); + return 0; +} + +#else + +static int oxu_bus_suspend(struct usb_hcd *hcd) +{ + return 0; +} + +static int oxu_bus_resume(struct usb_hcd *hcd) +{ + return 0; +} + +#endif /* CONFIG_PM */ + +static const struct hc_driver oxu_hc_driver = { + .description = "oxu210hp_hcd", + .product_desc = "oxu210hp HCD", + .hcd_priv_size = sizeof(struct oxu_hcd), + + /* + * Generic hardware linkage + */ + .irq = oxu_irq, + .flags = HCD_MEMORY | HCD_USB2, + + /* + * Basic lifecycle operations + */ + .reset = oxu_reset, + .start = oxu_run, + .stop = oxu_stop, + .shutdown = oxu_shutdown, + + /* + * Managing i/o requests and associated device resources + */ + .urb_enqueue = oxu_urb_enqueue, + .urb_dequeue = oxu_urb_dequeue, + .endpoint_disable = oxu_endpoint_disable, + + /* + * Scheduling support + */ + .get_frame_number = oxu_get_frame, + + /* + * Root hub support + */ + .hub_status_data = oxu_hub_status_data, + .hub_control = oxu_hub_control, + .bus_suspend = oxu_bus_suspend, + .bus_resume = oxu_bus_resume, +}; + +/* + * Module stuff + */ + +static void oxu_configuration(struct platform_device *pdev, void *base) +{ + u32 tmp; + + /* Initialize top level registers. + * First write ever + */ + oxu_writel(base, OXU_HOSTIFCONFIG, 0x0000037D); + oxu_writel(base, OXU_SOFTRESET, OXU_SRESET); + oxu_writel(base, OXU_HOSTIFCONFIG, 0x0000037D); + + tmp = oxu_readl(base, OXU_PIOBURSTREADCTRL); + oxu_writel(base, OXU_PIOBURSTREADCTRL, tmp | 0x0040); + + oxu_writel(base, OXU_ASO, OXU_SPHPOEN | OXU_OVRCCURPUPDEN | + OXU_COMPARATOR | OXU_ASO_OP); + + tmp = oxu_readl(base, OXU_CLKCTRL_SET); + oxu_writel(base, OXU_CLKCTRL_SET, tmp | OXU_SYSCLKEN | OXU_USBOTGCLKEN); + + /* Clear all top interrupt enable */ + oxu_writel(base, OXU_CHIPIRQEN_CLR, 0xff); + + /* Clear all top interrupt status */ + oxu_writel(base, OXU_CHIPIRQSTATUS, 0xff); + + /* Enable all needed top interrupt except OTG SPH core */ + oxu_writel(base, OXU_CHIPIRQEN_SET, OXU_USBSPHLPWUI | OXU_USBOTGLPWUI); +} + +static int oxu_verify_id(struct platform_device *pdev, void *base) +{ + u32 id; + char *bo[] = { + "reserved", + "128-pin LQFP", + "84-pin TFBGA", + "reserved", + }; + + /* Read controller signature register to find a match */ + id = oxu_readl(base, OXU_DEVICEID); + dev_info(&pdev->dev, "device ID %x\n", id); + if ((id & OXU_REV_MASK) != (OXU_REV_2100 << OXU_REV_SHIFT)) + return -1; + + dev_info(&pdev->dev, "found device %x %s (%04x:%04x)\n", + id >> OXU_REV_SHIFT, + bo[(id & OXU_BO_MASK) >> OXU_BO_SHIFT], + (id & OXU_MAJ_REV_MASK) >> OXU_MAJ_REV_SHIFT, + (id & OXU_MIN_REV_MASK) >> OXU_MIN_REV_SHIFT); + + return 0; +} + +static const struct hc_driver oxu_hc_driver; +static struct usb_hcd *oxu_create(struct platform_device *pdev, + unsigned long memstart, unsigned long memlen, + void *base, int irq, int otg) +{ + struct device *dev = &pdev->dev; + + struct usb_hcd *hcd; + struct oxu_hcd *oxu; + int ret; + + /* Set endian mode and host mode */ + oxu_writel(base + (otg ? OXU_OTG_CORE_OFFSET : OXU_SPH_CORE_OFFSET), + OXU_USBMODE, + OXU_CM_HOST_ONLY | OXU_ES_LITTLE | OXU_VBPS); + + hcd = usb_create_hcd(&oxu_hc_driver, dev, + otg ? "oxu210hp_otg" : "oxu210hp_sph"); + if (!hcd) + return ERR_PTR(-ENOMEM); + + hcd->rsrc_start = memstart; + hcd->rsrc_len = memlen; + hcd->regs = base; + hcd->irq = irq; + hcd->state = HC_STATE_HALT; + + oxu = hcd_to_oxu(hcd); + oxu->is_otg = otg; + + ret = usb_add_hcd(hcd, irq, IRQF_SHARED); + if (ret < 0) + return ERR_PTR(ret); + + return hcd; +} + +static int oxu_init(struct platform_device *pdev, + unsigned long memstart, unsigned long memlen, + void *base, int irq) +{ + struct oxu_info *info = platform_get_drvdata(pdev); + struct usb_hcd *hcd; + int ret; + + /* First time configuration at start up */ + oxu_configuration(pdev, base); + + ret = oxu_verify_id(pdev, base); + if (ret) { + dev_err(&pdev->dev, "no devices found!\n"); + return -ENODEV; + } + + /* Create the OTG controller */ + hcd = oxu_create(pdev, memstart, memlen, base, irq, 1); + if (IS_ERR(hcd)) { + dev_err(&pdev->dev, "cannot create OTG controller!\n"); + ret = PTR_ERR(hcd); + goto error_create_otg; + } + info->hcd[0] = hcd; + + /* Create the SPH host controller */ + hcd = oxu_create(pdev, memstart, memlen, base, irq, 0); + if (IS_ERR(hcd)) { + dev_err(&pdev->dev, "cannot create SPH controller!\n"); + ret = PTR_ERR(hcd); + goto error_create_sph; + } + info->hcd[1] = hcd; + + oxu_writel(base, OXU_CHIPIRQEN_SET, + oxu_readl(base, OXU_CHIPIRQEN_SET) | 3); + + return 0; + +error_create_sph: + usb_remove_hcd(info->hcd[0]); + usb_put_hcd(info->hcd[0]); + +error_create_otg: + return ret; +} + +static int oxu_drv_probe(struct platform_device *pdev) +{ + struct resource *res; + void *base; + unsigned long memstart, memlen; + int irq, ret; + struct oxu_info *info; + + if (usb_disabled()) + return -ENODEV; + + /* + * Get the platform resources + */ + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + dev_err(&pdev->dev, + "no IRQ! Check %s setup!\n", dev_name(&pdev->dev)); + return -ENODEV; + } + irq = res->start; + dev_dbg(&pdev->dev, "IRQ resource %d\n", irq); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "no registers address! Check %s setup!\n", + dev_name(&pdev->dev)); + return -ENODEV; + } + memstart = res->start; + memlen = res->end - res->start + 1; + dev_dbg(&pdev->dev, "MEM resource %lx-%lx\n", memstart, memlen); + if (!request_mem_region(memstart, memlen, + oxu_hc_driver.description)) { + dev_dbg(&pdev->dev, "memory area already in use\n"); + return -EBUSY; + } + + ret = set_irq_type(irq, IRQF_TRIGGER_FALLING); + if (ret) { + dev_err(&pdev->dev, "error setting irq type\n"); + ret = -EFAULT; + goto error_set_irq_type; + } + + base = ioremap(memstart, memlen); + if (!base) { + dev_dbg(&pdev->dev, "error mapping memory\n"); + ret = -EFAULT; + goto error_ioremap; + } + + /* Allocate a driver data struct to hold useful info for both + * SPH & OTG devices + */ + info = kzalloc(sizeof(struct oxu_info), GFP_KERNEL); + if (!info) { + dev_dbg(&pdev->dev, "error allocating memory\n"); + ret = -EFAULT; + goto error_alloc; + } + platform_set_drvdata(pdev, info); + + ret = oxu_init(pdev, memstart, memlen, base, irq); + if (ret < 0) { + dev_dbg(&pdev->dev, "cannot init USB devices\n"); + goto error_init; + } + + dev_info(&pdev->dev, "devices enabled and running\n"); + platform_set_drvdata(pdev, info); + + return 0; + +error_init: + kfree(info); + platform_set_drvdata(pdev, NULL); + +error_alloc: + iounmap(base); + +error_set_irq_type: +error_ioremap: + release_mem_region(memstart, memlen); + + dev_err(&pdev->dev, "init %s fail, %d\n", dev_name(&pdev->dev), ret); + return ret; +} + +static void oxu_remove(struct platform_device *pdev, struct usb_hcd *hcd) +{ + usb_remove_hcd(hcd); + usb_put_hcd(hcd); +} + +static int oxu_drv_remove(struct platform_device *pdev) +{ + struct oxu_info *info = platform_get_drvdata(pdev); + unsigned long memstart = info->hcd[0]->rsrc_start, + memlen = info->hcd[0]->rsrc_len; + void *base = info->hcd[0]->regs; + + oxu_remove(pdev, info->hcd[0]); + oxu_remove(pdev, info->hcd[1]); + + iounmap(base); + release_mem_region(memstart, memlen); + + kfree(info); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static void oxu_drv_shutdown(struct platform_device *pdev) +{ + oxu_drv_remove(pdev); +} + +#if 0 +/* FIXME: TODO */ +static int oxu_drv_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct usb_hcd *hcd = dev_get_drvdata(dev); + + return 0; +} + +static int oxu_drv_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct usb_hcd *hcd = dev_get_drvdata(dev); + + return 0; +} +#else +#define oxu_drv_suspend NULL +#define oxu_drv_resume NULL +#endif + +static struct platform_driver oxu_driver = { + .probe = oxu_drv_probe, + .remove = oxu_drv_remove, + .shutdown = oxu_drv_shutdown, + .suspend = oxu_drv_suspend, + .resume = oxu_drv_resume, + .driver = { + .name = "oxu210hp-hcd", + .bus = &platform_bus_type + } +}; + +static int __init oxu_module_init(void) +{ + int retval = 0; + + retval = platform_driver_register(&oxu_driver); + if (retval < 0) + return retval; + + return retval; +} + +static void __exit oxu_module_cleanup(void) +{ + platform_driver_unregister(&oxu_driver); +} + +module_init(oxu_module_init); +module_exit(oxu_module_cleanup); + +MODULE_DESCRIPTION("Oxford OXU210HP HCD driver - ver. " DRIVER_VERSION); +MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/host/oxu210hp.h b/drivers/usb/host/oxu210hp.h new file mode 100644 index 000000000000..8910e271cc7d --- /dev/null +++ b/drivers/usb/host/oxu210hp.h @@ -0,0 +1,447 @@ +/* + * Host interface registers + */ + +#define OXU_DEVICEID 0x00 + #define OXU_REV_MASK 0xffff0000 + #define OXU_REV_SHIFT 16 + #define OXU_REV_2100 0x2100 + #define OXU_BO_SHIFT 8 + #define OXU_BO_MASK (0x3 << OXU_BO_SHIFT) + #define OXU_MAJ_REV_SHIFT 4 + #define OXU_MAJ_REV_MASK (0xf << OXU_MAJ_REV_SHIFT) + #define OXU_MIN_REV_SHIFT 0 + #define OXU_MIN_REV_MASK (0xf << OXU_MIN_REV_SHIFT) +#define OXU_HOSTIFCONFIG 0x04 +#define OXU_SOFTRESET 0x08 + #define OXU_SRESET (1 << 0) + +#define OXU_PIOBURSTREADCTRL 0x0C + +#define OXU_CHIPIRQSTATUS 0x10 +#define OXU_CHIPIRQEN_SET 0x14 +#define OXU_CHIPIRQEN_CLR 0x18 + #define OXU_USBSPHLPWUI 0x00000080 + #define OXU_USBOTGLPWUI 0x00000040 + #define OXU_USBSPHI 0x00000002 + #define OXU_USBOTGI 0x00000001 + +#define OXU_CLKCTRL_SET 0x1C + #define OXU_SYSCLKEN 0x00000008 + #define OXU_USBSPHCLKEN 0x00000002 + #define OXU_USBOTGCLKEN 0x00000001 + +#define OXU_ASO 0x68 + #define OXU_SPHPOEN 0x00000100 + #define OXU_OVRCCURPUPDEN 0x00000800 + #define OXU_ASO_OP (1 << 10) + #define OXU_COMPARATOR 0x000004000 + +#define OXU_USBMODE 0x1A8 + #define OXU_VBPS 0x00000020 + #define OXU_ES_LITTLE 0x00000000 + #define OXU_CM_HOST_ONLY 0x00000003 + +/* + * Proper EHCI structs & defines + */ + +/* Magic numbers that can affect system performance */ +#define EHCI_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */ +#define EHCI_TUNE_RL_HS 4 /* nak throttle; see 4.9 */ +#define EHCI_TUNE_RL_TT 0 +#define EHCI_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */ +#define EHCI_TUNE_MULT_TT 1 +#define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */ + +struct oxu_hcd; + +/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */ + +/* Section 2.2 Host Controller Capability Registers */ +struct ehci_caps { + /* these fields are specified as 8 and 16 bit registers, + * but some hosts can't perform 8 or 16 bit PCI accesses. + */ + u32 hc_capbase; +#define HC_LENGTH(p) (((p)>>00)&0x00ff) /* bits 7:0 */ +#define HC_VERSION(p) (((p)>>16)&0xffff) /* bits 31:16 */ + u32 hcs_params; /* HCSPARAMS - offset 0x4 */ +#define HCS_DEBUG_PORT(p) (((p)>>20)&0xf) /* bits 23:20, debug port? */ +#define HCS_INDICATOR(p) ((p)&(1 << 16)) /* true: has port indicators */ +#define HCS_N_CC(p) (((p)>>12)&0xf) /* bits 15:12, #companion HCs */ +#define HCS_N_PCC(p) (((p)>>8)&0xf) /* bits 11:8, ports per CC */ +#define HCS_PORTROUTED(p) ((p)&(1 << 7)) /* true: port routing */ +#define HCS_PPC(p) ((p)&(1 << 4)) /* true: port power control */ +#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */ + + u32 hcc_params; /* HCCPARAMS - offset 0x8 */ +#define HCC_EXT_CAPS(p) (((p)>>8)&0xff) /* for pci extended caps */ +#define HCC_ISOC_CACHE(p) ((p)&(1 << 7)) /* true: can cache isoc frame */ +#define HCC_ISOC_THRES(p) (((p)>>4)&0x7) /* bits 6:4, uframes cached */ +#define HCC_CANPARK(p) ((p)&(1 << 2)) /* true: can park on async qh */ +#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1)) /* true: periodic_size changes*/ +#define HCC_64BIT_ADDR(p) ((p)&(1)) /* true: can use 64-bit addr */ + u8 portroute[8]; /* nibbles for routing - offset 0xC */ +} __attribute__ ((packed)); + + +/* Section 2.3 Host Controller Operational Registers */ +struct ehci_regs { + /* USBCMD: offset 0x00 */ + u32 command; +/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */ +#define CMD_PARK (1<<11) /* enable "park" on async qh */ +#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */ +#define CMD_LRESET (1<<7) /* partial reset (no ports, etc) */ +#define CMD_IAAD (1<<6) /* "doorbell" interrupt async advance */ +#define CMD_ASE (1<<5) /* async schedule enable */ +#define CMD_PSE (1<<4) /* periodic schedule enable */ +/* 3:2 is periodic frame list size */ +#define CMD_RESET (1<<1) /* reset HC not bus */ +#define CMD_RUN (1<<0) /* start/stop HC */ + + /* USBSTS: offset 0x04 */ + u32 status; +#define STS_ASS (1<<15) /* Async Schedule Status */ +#define STS_PSS (1<<14) /* Periodic Schedule Status */ +#define STS_RECL (1<<13) /* Reclamation */ +#define STS_HALT (1<<12) /* Not running (any reason) */ +/* some bits reserved */ + /* these STS_* flags are also intr_enable bits (USBINTR) */ +#define STS_IAA (1<<5) /* Interrupted on async advance */ +#define STS_FATAL (1<<4) /* such as some PCI access errors */ +#define STS_FLR (1<<3) /* frame list rolled over */ +#define STS_PCD (1<<2) /* port change detect */ +#define STS_ERR (1<<1) /* "error" completion (overflow, ...) */ +#define STS_INT (1<<0) /* "normal" completion (short, ...) */ + +#define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT) + + /* USBINTR: offset 0x08 */ + u32 intr_enable; + + /* FRINDEX: offset 0x0C */ + u32 frame_index; /* current microframe number */ + /* CTRLDSSEGMENT: offset 0x10 */ + u32 segment; /* address bits 63:32 if needed */ + /* PERIODICLISTBASE: offset 0x14 */ + u32 frame_list; /* points to periodic list */ + /* ASYNCLISTADDR: offset 0x18 */ + u32 async_next; /* address of next async queue head */ + + u32 reserved[9]; + + /* CONFIGFLAG: offset 0x40 */ + u32 configured_flag; +#define FLAG_CF (1<<0) /* true: we'll support "high speed" */ + + /* PORTSC: offset 0x44 */ + u32 port_status[0]; /* up to N_PORTS */ +/* 31:23 reserved */ +#define PORT_WKOC_E (1<<22) /* wake on overcurrent (enable) */ +#define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */ +#define PORT_WKCONN_E (1<<20) /* wake on connect (enable) */ +/* 19:16 for port testing */ +#define PORT_LED_OFF (0<<14) +#define PORT_LED_AMBER (1<<14) +#define PORT_LED_GREEN (2<<14) +#define PORT_LED_MASK (3<<14) +#define PORT_OWNER (1<<13) /* true: companion hc owns this port */ +#define PORT_POWER (1<<12) /* true: has power (see PPC) */ +#define PORT_USB11(x) (((x)&(3<<10)) == (1<<10)) /* USB 1.1 device */ +/* 11:10 for detecting lowspeed devices (reset vs release ownership) */ +/* 9 reserved */ +#define PORT_RESET (1<<8) /* reset port */ +#define PORT_SUSPEND (1<<7) /* suspend port */ +#define PORT_RESUME (1<<6) /* resume it */ +#define PORT_OCC (1<<5) /* over current change */ +#define PORT_OC (1<<4) /* over current active */ +#define PORT_PEC (1<<3) /* port enable change */ +#define PORT_PE (1<<2) /* port enable */ +#define PORT_CSC (1<<1) /* connect status change */ +#define PORT_CONNECT (1<<0) /* device connected */ +#define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC) +} __attribute__ ((packed)); + +/* Appendix C, Debug port ... intended for use with special "debug devices" + * that can help if there's no serial console. (nonstandard enumeration.) + */ +struct ehci_dbg_port { + u32 control; +#define DBGP_OWNER (1<<30) +#define DBGP_ENABLED (1<<28) +#define DBGP_DONE (1<<16) +#define DBGP_INUSE (1<<10) +#define DBGP_ERRCODE(x) (((x)>>7)&0x07) +# define DBGP_ERR_BAD 1 +# define DBGP_ERR_SIGNAL 2 +#define DBGP_ERROR (1<<6) +#define DBGP_GO (1<<5) +#define DBGP_OUT (1<<4) +#define DBGP_LEN(x) (((x)>>0)&0x0f) + u32 pids; +#define DBGP_PID_GET(x) (((x)>>16)&0xff) +#define DBGP_PID_SET(data, tok) (((data)<<8)|(tok)) + u32 data03; + u32 data47; + u32 address; +#define DBGP_EPADDR(dev, ep) (((dev)<<8)|(ep)) +} __attribute__ ((packed)); + + +#define QTD_NEXT(dma) cpu_to_le32((u32)dma) + +/* + * EHCI Specification 0.95 Section 3.5 + * QTD: describe data transfer components (buffer, direction, ...) + * See Fig 3-6 "Queue Element Transfer Descriptor Block Diagram". + * + * These are associated only with "QH" (Queue Head) structures, + * used with control, bulk, and interrupt transfers. + */ +struct ehci_qtd { + /* first part defined by EHCI spec */ + __le32 hw_next; /* see EHCI 3.5.1 */ + __le32 hw_alt_next; /* see EHCI 3.5.2 */ + __le32 hw_token; /* see EHCI 3.5.3 */ +#define QTD_TOGGLE (1 << 31) /* data toggle */ +#define QTD_LENGTH(tok) (((tok)>>16) & 0x7fff) +#define QTD_IOC (1 << 15) /* interrupt on complete */ +#define QTD_CERR(tok) (((tok)>>10) & 0x3) +#define QTD_PID(tok) (((tok)>>8) & 0x3) +#define QTD_STS_ACTIVE (1 << 7) /* HC may execute this */ +#define QTD_STS_HALT (1 << 6) /* halted on error */ +#define QTD_STS_DBE (1 << 5) /* data buffer error (in HC) */ +#define QTD_STS_BABBLE (1 << 4) /* device was babbling (qtd halted) */ +#define QTD_STS_XACT (1 << 3) /* device gave illegal response */ +#define QTD_STS_MMF (1 << 2) /* incomplete split transaction */ +#define QTD_STS_STS (1 << 1) /* split transaction state */ +#define QTD_STS_PING (1 << 0) /* issue PING? */ + __le32 hw_buf[5]; /* see EHCI 3.5.4 */ + __le32 hw_buf_hi[5]; /* Appendix B */ + + /* the rest is HCD-private */ + dma_addr_t qtd_dma; /* qtd address */ + struct list_head qtd_list; /* sw qtd list */ + struct urb *urb; /* qtd's urb */ + size_t length; /* length of buffer */ + + u32 qtd_buffer_len; + void *buffer; + dma_addr_t buffer_dma; + void *transfer_buffer; + void *transfer_dma; +} __attribute__ ((aligned(32))); + +/* mask NakCnt+T in qh->hw_alt_next */ +#define QTD_MASK __constant_cpu_to_le32 (~0x1f) + +#define IS_SHORT_READ(token) (QTD_LENGTH(token) != 0 && QTD_PID(token) == 1) + +/* Type tag from {qh, itd, sitd, fstn}->hw_next */ +#define Q_NEXT_TYPE(dma) ((dma) & __constant_cpu_to_le32 (3 << 1)) + +/* values for that type tag */ +#define Q_TYPE_QH __constant_cpu_to_le32 (1 << 1) + +/* next async queue entry, or pointer to interrupt/periodic QH */ +#define QH_NEXT(dma) (cpu_to_le32(((u32)dma)&~0x01f)|Q_TYPE_QH) + +/* for periodic/async schedules and qtd lists, mark end of list */ +#define EHCI_LIST_END __constant_cpu_to_le32(1) /* "null pointer" to hw */ + +/* + * Entries in periodic shadow table are pointers to one of four kinds + * of data structure. That's dictated by the hardware; a type tag is + * encoded in the low bits of the hardware's periodic schedule. Use + * Q_NEXT_TYPE to get the tag. + * + * For entries in the async schedule, the type tag always says "qh". + */ +union ehci_shadow { + struct ehci_qh *qh; /* Q_TYPE_QH */ + __le32 *hw_next; /* (all types) */ + void *ptr; +}; + +/* + * EHCI Specification 0.95 Section 3.6 + * QH: describes control/bulk/interrupt endpoints + * See Fig 3-7 "Queue Head Structure Layout". + * + * These appear in both the async and (for interrupt) periodic schedules. + */ + +struct ehci_qh { + /* first part defined by EHCI spec */ + __le32 hw_next; /* see EHCI 3.6.1 */ + __le32 hw_info1; /* see EHCI 3.6.2 */ +#define QH_HEAD 0x00008000 + __le32 hw_info2; /* see EHCI 3.6.2 */ +#define QH_SMASK 0x000000ff +#define QH_CMASK 0x0000ff00 +#define QH_HUBADDR 0x007f0000 +#define QH_HUBPORT 0x3f800000 +#define QH_MULT 0xc0000000 + __le32 hw_current; /* qtd list - see EHCI 3.6.4 */ + + /* qtd overlay (hardware parts of a struct ehci_qtd) */ + __le32 hw_qtd_next; + __le32 hw_alt_next; + __le32 hw_token; + __le32 hw_buf[5]; + __le32 hw_buf_hi[5]; + + /* the rest is HCD-private */ + dma_addr_t qh_dma; /* address of qh */ + union ehci_shadow qh_next; /* ptr to qh; or periodic */ + struct list_head qtd_list; /* sw qtd list */ + struct ehci_qtd *dummy; + struct ehci_qh *reclaim; /* next to reclaim */ + + struct oxu_hcd *oxu; + struct kref kref; + unsigned stamp; + + u8 qh_state; +#define QH_STATE_LINKED 1 /* HC sees this */ +#define QH_STATE_UNLINK 2 /* HC may still see this */ +#define QH_STATE_IDLE 3 /* HC doesn't see this */ +#define QH_STATE_UNLINK_WAIT 4 /* LINKED and on reclaim q */ +#define QH_STATE_COMPLETING 5 /* don't touch token.HALT */ + + /* periodic schedule info */ + u8 usecs; /* intr bandwidth */ + u8 gap_uf; /* uframes split/csplit gap */ + u8 c_usecs; /* ... split completion bw */ + u16 tt_usecs; /* tt downstream bandwidth */ + unsigned short period; /* polling interval */ + unsigned short start; /* where polling starts */ +#define NO_FRAME ((unsigned short)~0) /* pick new start */ + struct usb_device *dev; /* access to TT */ +} __attribute__ ((aligned(32))); + +/* + * Proper OXU210HP structs + */ + +#define OXU_OTG_CORE_OFFSET 0x00400 +#define OXU_OTG_CAP_OFFSET (OXU_OTG_CORE_OFFSET + 0x100) +#define OXU_SPH_CORE_OFFSET 0x00800 +#define OXU_SPH_CAP_OFFSET (OXU_SPH_CORE_OFFSET + 0x100) + +#define OXU_OTG_MEM 0xE000 +#define OXU_SPH_MEM 0x16000 + +/* Only how many elements & element structure are specifies here. */ +/* 2 host controllers are enabled - total size <= 28 kbytes */ +#define DEFAULT_I_TDPS 1024 +#define QHEAD_NUM 16 +#define QTD_NUM 32 +#define SITD_NUM 8 +#define MURB_NUM 8 + +#define BUFFER_NUM 8 +#define BUFFER_SIZE 512 + +struct oxu_info { + struct usb_hcd *hcd[2]; +}; + +struct oxu_buf { + u8 buffer[BUFFER_SIZE]; +} __attribute__ ((aligned(BUFFER_SIZE))); + +struct oxu_onchip_mem { + struct oxu_buf db_pool[BUFFER_NUM]; + + u32 frame_list[DEFAULT_I_TDPS]; + struct ehci_qh qh_pool[QHEAD_NUM]; + struct ehci_qtd qtd_pool[QTD_NUM]; +} __attribute__ ((aligned(4 << 10))); + +#define EHCI_MAX_ROOT_PORTS 15 /* see HCS_N_PORTS */ + +struct oxu_murb { + struct urb urb; + struct urb *main; + u8 last; +}; + +struct oxu_hcd { /* one per controller */ + unsigned int is_otg:1; + + u8 qh_used[QHEAD_NUM]; + u8 qtd_used[QTD_NUM]; + u8 db_used[BUFFER_NUM]; + u8 murb_used[MURB_NUM]; + + struct oxu_onchip_mem __iomem *mem; + spinlock_t mem_lock; + + struct timer_list urb_timer; + + struct ehci_caps __iomem *caps; + struct ehci_regs __iomem *regs; + + __u32 hcs_params; /* cached register copy */ + spinlock_t lock; + + /* async schedule support */ + struct ehci_qh *async; + struct ehci_qh *reclaim; + unsigned reclaim_ready:1; + unsigned scanning:1; + + /* periodic schedule support */ + unsigned periodic_size; + __le32 *periodic; /* hw periodic table */ + dma_addr_t periodic_dma; + unsigned i_thresh; /* uframes HC might cache */ + + union ehci_shadow *pshadow; /* mirror hw periodic table */ + int next_uframe; /* scan periodic, start here */ + unsigned periodic_sched; /* periodic activity count */ + + /* per root hub port */ + unsigned long reset_done[EHCI_MAX_ROOT_PORTS]; + /* bit vectors (one bit per port) */ + unsigned long bus_suspended; /* which ports were + * already suspended at the + * start of a bus suspend + */ + unsigned long companion_ports;/* which ports are dedicated + * to the companion controller + */ + + struct timer_list watchdog; + unsigned long actions; + unsigned stamp; + unsigned long next_statechange; + u32 command; + + /* SILICON QUIRKS */ + struct list_head urb_list; /* this is the head to urb + * queue that didn't get enough + * resources + */ + struct oxu_murb *murb_pool; /* murb per split big urb */ + unsigned urb_len; + + u8 sbrn; /* packed release number */ +}; + +#define EHCI_IAA_JIFFIES (HZ/100) /* arbitrary; ~10 msec */ +#define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */ +#define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */ +#define EHCI_SHRINK_JIFFIES (HZ/200) /* async qh unlink delay */ + +enum ehci_timer_action { + TIMER_IO_WATCHDOG, + TIMER_IAA_WATCHDOG, + TIMER_ASYNC_SHRINK, + TIMER_ASYNC_OFF, +}; + +#include <linux/oxu210hp.h> diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c index ae6e70edd745..75b69847918e 100644 --- a/drivers/usb/host/pci-quirks.c +++ b/drivers/usb/host/pci-quirks.c @@ -172,9 +172,9 @@ static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev) if (!mmio_resource_enabled(pdev, 0)) return; - base = ioremap_nocache(pci_resource_start(pdev, 0), - pci_resource_len(pdev, 0)); - if (base == NULL) return; + base = pci_ioremap_bar(pdev, 0); + if (base == NULL) + return; /* On PA-RISC, PDC can leave IR set incorrectly; ignore it there. */ #ifndef __hppa__ @@ -221,9 +221,9 @@ static void __devinit quirk_usb_disable_ehci(struct pci_dev *pdev) if (!mmio_resource_enabled(pdev, 0)) return; - base = ioremap_nocache(pci_resource_start(pdev, 0), - pci_resource_len(pdev, 0)); - if (base == NULL) return; + base = pci_ioremap_bar(pdev, 0); + if (base == NULL) + return; cap_length = readb(base); op_reg_base = base + cap_length; @@ -271,7 +271,7 @@ static void __devinit quirk_usb_disable_ehci(struct pci_dev *pdev) /* if boot firmware now owns EHCI, spin till * it hands it over. */ - msec = 5000; + msec = 1000; while ((cap & EHCI_USBLEGSUP_BIOS) && (msec > 0)) { tried_handoff = 1; msleep(10); diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index c18d8790c410..319041205b57 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -114,6 +114,9 @@ static int r8a66597_clock_enable(struct r8a66597 *r8a66597) int i = 0; #if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) +#if defined(CONFIG_HAVE_CLK) + clk_enable(r8a66597->clk); +#endif do { r8a66597_write(r8a66597, SCKE, SYSCFG0); tmp = r8a66597_read(r8a66597, SYSCFG0); @@ -154,7 +157,11 @@ static void r8a66597_clock_disable(struct r8a66597 *r8a66597) { r8a66597_bclr(r8a66597, SCKE, SYSCFG0); udelay(1); -#if !defined(CONFIG_SUPERH_ON_CHIP_R8A66597) +#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) +#if defined(CONFIG_HAVE_CLK) + clk_disable(r8a66597->clk); +#endif +#else r8a66597_bclr(r8a66597, PLLC, SYSCFG0); r8a66597_bclr(r8a66597, XCKE, SYSCFG0); r8a66597_bclr(r8a66597, USBE, SYSCFG0); @@ -1763,11 +1770,12 @@ static void r8a66597_timer(unsigned long _r8a66597) { struct r8a66597 *r8a66597 = (struct r8a66597 *)_r8a66597; unsigned long flags; + int port; spin_lock_irqsave(&r8a66597->lock, flags); - r8a66597_root_hub_control(r8a66597, 0); - r8a66597_root_hub_control(r8a66597, 1); + for (port = 0; port < R8A66597_MAX_ROOT_HUB; port++) + r8a66597_root_hub_control(r8a66597, port); spin_unlock_irqrestore(&r8a66597->lock, flags); } @@ -2260,13 +2268,18 @@ static int __init_or_module r8a66597_remove(struct platform_device *pdev) del_timer_sync(&r8a66597->rh_timer); usb_remove_hcd(hcd); iounmap((void *)r8a66597->reg); +#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) && defined(CONFIG_HAVE_CLK) + clk_put(r8a66597->clk); +#endif usb_put_hcd(hcd); return 0; } -#define resource_len(r) (((r)->end - (r)->start) + 1) static int __init r8a66597_probe(struct platform_device *pdev) { +#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) && defined(CONFIG_HAVE_CLK) + char clk_name[8]; +#endif struct resource *res = NULL, *ires; int irq = -1; void __iomem *reg = NULL; @@ -2282,11 +2295,10 @@ static int __init r8a66597_probe(struct platform_device *pdev) goto clean_up; } - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - (char *)hcd_name); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { ret = -ENODEV; - dev_err(&pdev->dev, "platform_get_resource_byname error.\n"); + dev_err(&pdev->dev, "platform_get_resource error.\n"); goto clean_up; } @@ -2301,7 +2313,7 @@ static int __init r8a66597_probe(struct platform_device *pdev) irq = ires->start; irq_trigger = ires->flags & IRQF_TRIGGER_MASK; - reg = ioremap(res->start, resource_len(res)); + reg = ioremap(res->start, resource_size(res)); if (reg == NULL) { ret = -ENOMEM; dev_err(&pdev->dev, "ioremap error.\n"); @@ -2319,6 +2331,16 @@ static int __init r8a66597_probe(struct platform_device *pdev) memset(r8a66597, 0, sizeof(struct r8a66597)); dev_set_drvdata(&pdev->dev, r8a66597); +#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) && defined(CONFIG_HAVE_CLK) + snprintf(clk_name, sizeof(clk_name), "usb%d", pdev->id); + r8a66597->clk = clk_get(&pdev->dev, clk_name); + if (IS_ERR(r8a66597->clk)) { + dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name); + ret = PTR_ERR(r8a66597->clk); + goto clean_up2; + } +#endif + spin_lock_init(&r8a66597->lock); init_timer(&r8a66597->rh_timer); r8a66597->rh_timer.function = r8a66597_timer; @@ -2364,11 +2386,18 @@ static int __init r8a66597_probe(struct platform_device *pdev) ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | irq_trigger); if (ret != 0) { dev_err(&pdev->dev, "Failed to add hcd\n"); - goto clean_up; + goto clean_up3; } return 0; +clean_up3: +#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) && defined(CONFIG_HAVE_CLK) + clk_put(r8a66597->clk); +clean_up2: +#endif + usb_put_hcd(hcd); + clean_up: if (reg) iounmap(reg); diff --git a/drivers/usb/host/r8a66597.h b/drivers/usb/host/r8a66597.h index 84ee01417315..ecacde4d69b0 100644 --- a/drivers/usb/host/r8a66597.h +++ b/drivers/usb/host/r8a66597.h @@ -26,6 +26,10 @@ #ifndef __R8A66597_H__ #define __R8A66597_H__ +#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) && defined(CONFIG_HAVE_CLK) +#include <linux/clk.h> +#endif + #define SYSCFG0 0x00 #define SYSCFG1 0x02 #define SYSSTS0 0x04 @@ -481,7 +485,9 @@ struct r8a66597_root_hub { struct r8a66597 { spinlock_t lock; unsigned long reg; - +#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) && defined(CONFIG_HAVE_CLK) + struct clk *clk; +#endif struct r8a66597_device device0; struct r8a66597_root_hub root_hub[R8A66597_MAX_ROOT_HUB]; struct list_head pipe_queue[R8A66597_MAX_NUM_PIPE]; diff --git a/drivers/usb/image/microtek.c b/drivers/usb/image/microtek.c index 885867a86de8..4541dfcea88f 100644 --- a/drivers/usb/image/microtek.c +++ b/drivers/usb/image/microtek.c @@ -350,17 +350,16 @@ static int mts_scsi_abort(struct scsi_cmnd *srb) static int mts_scsi_host_reset(struct scsi_cmnd *srb) { struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]); - int result, rc; + int result; MTS_DEBUG_GOT_HERE(); mts_debug_dump(desc); - rc = usb_lock_device_for_reset(desc->usb_dev, desc->usb_intf); - if (rc < 0) - return FAILED; - result = usb_reset_device(desc->usb_dev); - if (rc) + result = usb_lock_device_for_reset(desc->usb_dev, desc->usb_intf); + if (result == 0) { + result = usb_reset_device(desc->usb_dev); usb_unlock_device(desc->usb_dev); + } return result ? FAILED : SUCCESS; } diff --git a/drivers/usb/misc/berry_charge.c b/drivers/usb/misc/berry_charge.c index 24e2dc3148a4..c05a85bc5925 100644 --- a/drivers/usb/misc/berry_charge.c +++ b/drivers/usb/misc/berry_charge.c @@ -123,6 +123,11 @@ static int berry_probe(struct usb_interface *intf, { struct usb_device *udev = interface_to_usbdev(intf); + if (udev->bus_mA < 500) { + dbg(&udev->dev, "Not enough power to charge available\n"); + return -ENODEV; + } + dbg(&udev->dev, "Power is set to %dmA\n", udev->actconfig->desc.bMaxPower * 2); diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c index 69c34a58e205..b4ec716de7da 100644 --- a/drivers/usb/misc/sisusbvga/sisusb.c +++ b/drivers/usb/misc/sisusbvga/sisusb.c @@ -3270,6 +3270,7 @@ static struct usb_device_id sisusb_table [] = { { USB_DEVICE(0x0711, 0x0900) }, { USB_DEVICE(0x0711, 0x0901) }, { USB_DEVICE(0x0711, 0x0902) }, + { USB_DEVICE(0x0711, 0x0903) }, { USB_DEVICE(0x0711, 0x0918) }, { USB_DEVICE(0x182d, 0x021c) }, { USB_DEVICE(0x182d, 0x0269) }, diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index 444c69c447be..5f1a19d1497d 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -192,8 +192,6 @@ static struct urb *simple_alloc_urb ( { struct urb *urb; - if (bytes < 0) - return NULL; urb = usb_alloc_urb (0, GFP_KERNEL); if (!urb) return urb; diff --git a/drivers/usb/misc/vstusb.c b/drivers/usb/misc/vstusb.c index 8648470c81ca..63dff9ba73c5 100644 --- a/drivers/usb/misc/vstusb.c +++ b/drivers/usb/misc/vstusb.c @@ -620,7 +620,7 @@ static long vstusb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) __func__); retval = -EFAULT; } else { - dev_dbg(&dev->dev, "%s: recv %d bytes from pipe %d\n", + dev_dbg(&dev->dev, "%s: recv %zd bytes from pipe %d\n", __func__, usb_data.count, usb_data.pipe); } diff --git a/drivers/usb/mon/Kconfig b/drivers/usb/mon/Kconfig index deb9ddffa402..f28f350cd96a 100644 --- a/drivers/usb/mon/Kconfig +++ b/drivers/usb/mon/Kconfig @@ -3,14 +3,13 @@ # config USB_MON - bool "USB Monitor" - depends on USB!=n - default y + tristate "USB Monitor" + depends on USB + default y if USB=y + default m if USB=m help - If you say Y here, a component which captures the USB traffic + If you select this option, a component which captures the USB traffic between peripheral-specific drivers and HC drivers will be built. For more information, see <file:Documentation/usb/usbmon.txt>. - This is somewhat experimental at this time, but it should be safe. - - If unsure, say Y. + If unsure, say Y (if allowed), otherwise M. diff --git a/drivers/usb/mon/Makefile b/drivers/usb/mon/Makefile index 0f76ed5e1617..c6516b566731 100644 --- a/drivers/usb/mon/Makefile +++ b/drivers/usb/mon/Makefile @@ -4,5 +4,4 @@ usbmon-objs := mon_main.o mon_stat.o mon_text.o mon_bin.o mon_dma.o -# This does not use CONFIG_USB_MON because we want this to use a tristate. -obj-$(CONFIG_USB) += usbmon.o +obj-$(CONFIG_USB_MON) += usbmon.o diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c index c9de3f027aab..e06810aef2df 100644 --- a/drivers/usb/mon/mon_bin.c +++ b/drivers/usb/mon/mon_bin.c @@ -687,7 +687,10 @@ static ssize_t mon_bin_read(struct file *file, char __user *buf, } if (rp->b_read >= sizeof(struct mon_bin_hdr)) { - step_len = min(nbytes, (size_t)ep->len_cap); + step_len = ep->len_cap; + step_len -= rp->b_read - sizeof(struct mon_bin_hdr); + if (step_len > nbytes) + step_len = nbytes; offset = rp->b_out + PKT_SIZE; offset += rp->b_read - sizeof(struct mon_bin_hdr); if (offset >= rp->b_size) diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index dfb3bcbe00fc..0d566dc5ce06 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -32,9 +32,9 @@ #include <linux/io.h> #include <linux/gpio.h> -#include <asm/arch/hardware.h> -#include <asm/arch/memory.h> -#include <asm/arch/gpio.h> +#include <mach/arch/hardware.h> +#include <mach/arch/memory.h> +#include <mach/arch/gpio.h> #include <asm/mach-types.h> #include "musb_core.h" @@ -364,6 +364,18 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci) return IRQ_HANDLED; } +int musb_platform_set_mode(struct musb *musb, u8 mode) +{ + /* EVM can't do this (right?) */ + return -EIO; +} + +int musb_platform_set_mode(struct musb *musb, u8 mode) +{ + /* EVM can't do this (right?) */ + return -EIO; +} + int __init musb_platform_init(struct musb *musb) { void __iomem *tibase = musb->ctrl_base; diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 4a35745b30be..83720f61fdba 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -114,8 +114,8 @@ -unsigned debug; -module_param(debug, uint, S_IRUGO | S_IWUSR); +unsigned musb_debug; +module_param(musb_debug, uint, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(debug, "Debug message level. Default = 0"); #define DRIVER_AUTHOR "Mentor Graphics, Texas Instruments, Nokia" @@ -1212,7 +1212,7 @@ static int __init ep_config_from_table(struct musb *musb) if (epn >= musb->config->num_eps) { pr_debug("%s: invalid ep %d\n", musb_driver_name, epn); - continue; + return -EINVAL; } offset = fifo_setup(musb, hw_ep + epn, cfg++, offset); if (offset < 0) { @@ -1671,17 +1671,20 @@ musb_mode_store(struct device *dev, struct device_attribute *attr, { struct musb *musb = dev_to_musb(dev); unsigned long flags; + int status; spin_lock_irqsave(&musb->lock, flags); - if (!strncmp(buf, "host", 4)) - musb_platform_set_mode(musb, MUSB_HOST); - if (!strncmp(buf, "peripheral", 10)) - musb_platform_set_mode(musb, MUSB_PERIPHERAL); - if (!strncmp(buf, "otg", 3)) - musb_platform_set_mode(musb, MUSB_OTG); + if (sysfs_streq(buf, "host")) + status = musb_platform_set_mode(musb, MUSB_HOST); + else if (sysfs_streq(buf, "peripheral")) + status = musb_platform_set_mode(musb, MUSB_PERIPHERAL); + else if (sysfs_streq(buf, "otg")) + status = musb_platform_set_mode(musb, MUSB_OTG); + else + status = -EINVAL; spin_unlock_irqrestore(&musb->lock, flags); - return n; + return (status == 0) ? n : status; } static DEVICE_ATTR(mode, 0644, musb_mode_show, musb_mode_store); @@ -1781,7 +1784,7 @@ allocate_instance(struct device *dev, #ifdef CONFIG_USB_MUSB_HDRC_HCD struct usb_hcd *hcd; - hcd = usb_create_hcd(&musb_hc_driver, dev, dev->bus_id); + hcd = usb_create_hcd(&musb_hc_driver, dev, dev_name(dev)); if (!hcd) return NULL; /* usbcore sets dev->driver_data to hcd, and sometimes uses that... */ @@ -1810,7 +1813,6 @@ allocate_instance(struct device *dev, for (epnum = 0, ep = musb->endpoints; epnum < musb->config->num_eps; epnum++, ep++) { - ep->musb = musb; ep->epnum = epnum; } @@ -1838,7 +1840,7 @@ static void musb_free(struct musb *musb) musb_gadget_cleanup(musb); #endif - if (musb->nIrq >= 0) { + if (musb->nIrq >= 0 && musb->irq_wake) { disable_irq_wake(musb->nIrq); free_irq(musb->nIrq, musb); } @@ -1984,15 +1986,19 @@ bad_config: INIT_WORK(&musb->irq_work, musb_irq_work); /* attach to the IRQ */ - if (request_irq(nIrq, musb->isr, 0, dev->bus_id, musb)) { + if (request_irq(nIrq, musb->isr, 0, dev_name(dev), musb)) { dev_err(dev, "request_irq %d failed!\n", nIrq); status = -ENODEV; goto fail2; } musb->nIrq = nIrq; /* FIXME this handles wakeup irqs wrong */ - if (enable_irq_wake(nIrq) == 0) + if (enable_irq_wake(nIrq) == 0) { + musb->irq_wake = 1; device_init_wakeup(dev, 1); + } else { + musb->irq_wake = 0; + } pr_info("%s: USB %s mode controller at %p using %s, IRQ %d\n", musb_driver_name, @@ -2248,7 +2254,7 @@ static int __init musb_init(void) "host" #endif ", debug=%d\n", - musb_driver_name, debug); + musb_driver_name, musb_debug); return platform_driver_probe(&musb_driver, musb_probe); } diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index 82227251931b..d45d258e63f0 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -359,6 +359,7 @@ struct musb { struct otg_transceiver xceiv; int nIrq; + unsigned irq_wake:1; struct musb_hw_ep endpoints[MUSB_C_NUM_EPS]; #define control_ep endpoints @@ -467,7 +468,7 @@ extern void musb_platform_disable(struct musb *musb); extern void musb_hnp_stop(struct musb *musb); -extern void musb_platform_set_mode(struct musb *musb, u8 musb_mode); +extern int musb_platform_set_mode(struct musb *musb, u8 musb_mode); #if defined(CONFIG_USB_TUSB6010) || \ defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP34XX) diff --git a/drivers/usb/musb/musb_debug.h b/drivers/usb/musb/musb_debug.h index 4d2794441b15..9fc1db44c72c 100644 --- a/drivers/usb/musb/musb_debug.h +++ b/drivers/usb/musb/musb_debug.h @@ -48,11 +48,11 @@ __func__, __LINE__ , ## args); \ } } while (0) -extern unsigned debug; +extern unsigned musb_debug; static inline int _dbg_level(unsigned l) { - return debug >= l; + return musb_debug >= l; } #define DBG(level, fmt, args...) xprintk(level, KERN_DEBUG, fmt, ## args) diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index d6a802c224fa..6197daeab8f9 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -1633,7 +1633,7 @@ int __init musb_gadget_setup(struct musb *musb) musb->g.speed = USB_SPEED_UNKNOWN; /* this "gadget" abstracts/virtualizes the controller */ - strcpy(musb->g.dev.bus_id, "gadget"); + dev_set_name(&musb->g.dev, "gadget"); musb->g.dev.parent = musb->controller; musb->g.dev.dma_mask = musb->controller->dma_mask; musb->g.dev.release = musb_gadget_release; diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 3133990f04ec..bf25ef4cc151 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -112,18 +112,21 @@ static void musb_h_tx_flush_fifo(struct musb_hw_ep *ep) { void __iomem *epio = ep->regs; u16 csr; + u16 lastcsr = 0; int retries = 1000; csr = musb_readw(epio, MUSB_TXCSR); while (csr & MUSB_TXCSR_FIFONOTEMPTY) { - DBG(5, "Host TX FIFONOTEMPTY csr: %02x\n", csr); + if (csr != lastcsr) + DBG(3, "Host TX FIFONOTEMPTY csr: %02x\n", csr); + lastcsr = csr; csr |= MUSB_TXCSR_FLUSHFIFO; musb_writew(epio, MUSB_TXCSR, csr); csr = musb_readw(epio, MUSB_TXCSR); - if (retries-- < 1) { - ERR("Could not flush host TX fifo: csr: %04x\n", csr); + if (WARN(retries-- < 1, + "Could not flush host TX%d fifo: csr: %04x\n", + ep->epnum, csr)) return; - } mdelay(1); } } @@ -268,7 +271,7 @@ __musb_giveback(struct musb *musb, struct urb *urb, int status) __releases(musb->lock) __acquires(musb->lock) { - DBG(({ int level; switch (urb->status) { + DBG(({ int level; switch (status) { case 0: level = 4; break; @@ -283,8 +286,8 @@ __acquires(musb->lock) level = 2; break; }; level; }), - "complete %p (%d), dev%d ep%d%s, %d/%d\n", - urb, urb->status, + "complete %p %pF (%d), dev%d ep%d%s, %d/%d\n", + urb, urb->complete, status, usb_pipedevice(urb->pipe), usb_pipeendpoint(urb->pipe), usb_pipein(urb->pipe) ? "in" : "out", @@ -378,6 +381,19 @@ musb_giveback(struct musb_qh *qh, struct urb *urb, int status) switch (qh->type) { + case USB_ENDPOINT_XFER_CONTROL: + case USB_ENDPOINT_XFER_BULK: + /* fifo policy for these lists, except that NAKing + * should rotate a qh to the end (for fairness). + */ + if (qh->mux == 1) { + head = qh->ring.prev; + list_del(&qh->ring); + kfree(qh); + qh = first_qh(head); + break; + } + case USB_ENDPOINT_XFER_ISOC: case USB_ENDPOINT_XFER_INT: /* this is where periodic bandwidth should be @@ -388,17 +404,6 @@ musb_giveback(struct musb_qh *qh, struct urb *urb, int status) kfree(qh); qh = NULL; break; - - case USB_ENDPOINT_XFER_CONTROL: - case USB_ENDPOINT_XFER_BULK: - /* fifo policy for these lists, except that NAKing - * should rotate a qh to the end (for fairness). - */ - head = qh->ring.prev; - list_del(&qh->ring); - kfree(qh); - qh = first_qh(head); - break; } } return qh; @@ -986,8 +991,10 @@ static bool musb_h_ep0_continue(struct musb *musb, u16 len, struct urb *urb) if (fifo_count) { fifo_dest = (u8 *) (urb->transfer_buffer + urb->actual_length); - DBG(3, "Sending %d bytes to %p\n", - fifo_count, fifo_dest); + DBG(3, "Sending %d byte%s to ep0 fifo %p\n", + fifo_count, + (fifo_count == 1) ? "" : "s", + fifo_dest); musb_write_fifo(hw_ep, fifo_count, fifo_dest); urb->actual_length += fifo_count; @@ -1507,10 +1514,29 @@ void musb_host_rx(struct musb *musb, u8 epnum) musb_writew(hw_ep->regs, MUSB_RXCSR, val); #ifdef CONFIG_USB_INVENTRA_DMA + if (usb_pipeisoc(pipe)) { + struct usb_iso_packet_descriptor *d; + + d = urb->iso_frame_desc + qh->iso_idx; + d->actual_length = xfer_len; + + /* even if there was an error, we did the dma + * for iso_frame_desc->length + */ + if (d->status != EILSEQ && d->status != -EOVERFLOW) + d->status = 0; + + if (++qh->iso_idx >= urb->number_of_packets) + done = true; + else + done = false; + + } else { /* done if urb buffer is full or short packet is recd */ done = (urb->actual_length + xfer_len >= urb->transfer_buffer_length || dma->actual_len < qh->maxpacket); + } /* send IN token for next packet, without AUTOREQ */ if (!done) { @@ -1547,7 +1573,8 @@ void musb_host_rx(struct musb *musb, u8 epnum) if (dma) { struct dma_controller *c; u16 rx_count; - int ret; + int ret, length; + dma_addr_t buf; rx_count = musb_readw(epio, MUSB_RXCOUNT); @@ -1560,6 +1587,35 @@ void musb_host_rx(struct musb *musb, u8 epnum) c = musb->dma_controller; + if (usb_pipeisoc(pipe)) { + int status = 0; + struct usb_iso_packet_descriptor *d; + + d = urb->iso_frame_desc + qh->iso_idx; + + if (iso_err) { + status = -EILSEQ; + urb->error_count++; + } + if (rx_count > d->length) { + if (status == 0) { + status = -EOVERFLOW; + urb->error_count++; + } + DBG(2, "** OVERFLOW %d into %d\n",\ + rx_count, d->length); + + length = d->length; + } else + length = rx_count; + d->status = status; + buf = urb->transfer_dma + d->offset; + } else { + length = rx_count; + buf = urb->transfer_dma + + urb->actual_length; + } + dma->desired_mode = 0; #ifdef USE_MODE1 /* because of the issue below, mode 1 will @@ -1571,6 +1627,12 @@ void musb_host_rx(struct musb *musb, u8 epnum) urb->actual_length) > qh->maxpacket) dma->desired_mode = 1; + if (rx_count < hw_ep->max_packet_sz_rx) { + length = rx_count; + dma->bDesiredMode = 0; + } else { + length = urb->transfer_buffer_length; + } #endif /* Disadvantage of using mode 1: @@ -1608,12 +1670,7 @@ void musb_host_rx(struct musb *musb, u8 epnum) */ ret = c->channel_program( dma, qh->maxpacket, - dma->desired_mode, - urb->transfer_dma - + urb->actual_length, - (dma->desired_mode == 0) - ? rx_count - : urb->transfer_buffer_length); + dma->desired_mode, buf, length); if (!ret) { c->channel_release(dma); @@ -1631,19 +1688,6 @@ void musb_host_rx(struct musb *musb, u8 epnum) } } - if (dma && usb_pipeisoc(pipe)) { - struct usb_iso_packet_descriptor *d; - int iso_stat = status; - - d = urb->iso_frame_desc + qh->iso_idx; - d->actual_length += xfer_len; - if (iso_err) { - iso_stat = -EILSEQ; - urb->error_count++; - } - d->status = iso_stat; - } - finish: urb->actual_length += xfer_len; qh->offset += xfer_len; @@ -1671,22 +1715,9 @@ static int musb_schedule( struct list_head *head = NULL; /* use fixed hardware for control and bulk */ - switch (qh->type) { - case USB_ENDPOINT_XFER_CONTROL: + if (qh->type == USB_ENDPOINT_XFER_CONTROL) { head = &musb->control; hw_ep = musb->control_ep; - break; - case USB_ENDPOINT_XFER_BULK: - hw_ep = musb->bulk_ep; - if (is_in) - head = &musb->in_bulk; - else - head = &musb->out_bulk; - break; - } - if (head) { - idle = list_empty(head); - list_add_tail(&qh->ring, head); goto success; } @@ -1725,19 +1756,34 @@ static int musb_schedule( else diff = hw_ep->max_packet_sz_tx - qh->maxpacket; - if (diff > 0 && best_diff > diff) { + if (diff >= 0 && best_diff > diff) { best_diff = diff; best_end = epnum; } } - if (best_end < 0) + /* use bulk reserved ep1 if no other ep is free */ + if (best_end < 0 && qh->type == USB_ENDPOINT_XFER_BULK) { + hw_ep = musb->bulk_ep; + if (is_in) + head = &musb->in_bulk; + else + head = &musb->out_bulk; + goto success; + } else if (best_end < 0) { return -ENOSPC; + } idle = 1; + qh->mux = 0; hw_ep = musb->endpoints + best_end; musb->periodic[best_end] = qh; DBG(4, "qh %p periodic slot %d\n", qh, best_end); success: + if (head) { + idle = list_empty(head); + list_add_tail(&qh->ring, head); + qh->mux = 1; + } qh->hw_ep = hw_ep; qh->hep->hcpriv = qh; if (idle) @@ -2015,11 +2061,13 @@ static int musb_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) sched = &musb->control; break; case USB_ENDPOINT_XFER_BULK: - if (usb_pipein(urb->pipe)) - sched = &musb->in_bulk; - else - sched = &musb->out_bulk; - break; + if (qh->mux == 1) { + if (usb_pipein(urb->pipe)) + sched = &musb->in_bulk; + else + sched = &musb->out_bulk; + break; + } default: /* REVISIT when we get a schedule tree, periodic * transfers won't always be at the head of a @@ -2067,11 +2115,13 @@ musb_h_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep) sched = &musb->control; break; case USB_ENDPOINT_XFER_BULK: - if (is_in) - sched = &musb->in_bulk; - else - sched = &musb->out_bulk; - break; + if (qh->mux == 1) { + if (is_in) + sched = &musb->in_bulk; + else + sched = &musb->out_bulk; + break; + } default: /* REVISIT when we get a schedule tree, periodic transfers * won't always be at the head of a singleton queue... diff --git a/drivers/usb/musb/musb_host.h b/drivers/usb/musb/musb_host.h index 77bcdb9d5b32..0b7fbcd21963 100644 --- a/drivers/usb/musb/musb_host.h +++ b/drivers/usb/musb/musb_host.h @@ -53,6 +53,7 @@ struct musb_qh { struct list_head ring; /* of musb_qh */ /* struct musb_qh *next; */ /* for periodic tree */ + u8 mux; /* qh multiplexed to hw_ep */ unsigned offset; /* in urb->transfer_buffer */ unsigned segsize; /* current xfer fragment */ diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c index 8c734ef2c1ed..d193b7849087 100644 --- a/drivers/usb/musb/musbhsdma.c +++ b/drivers/usb/musb/musbhsdma.c @@ -424,7 +424,7 @@ dma_controller_create(struct musb *musb, void __iomem *base) controller->controller.channel_abort = dma_channel_abort; if (request_irq(irq, dma_controller_irq, IRQF_DISABLED, - musb->controller->bus_id, &controller->controller)) { + dev_name(musb->controller), &controller->controller)) { dev_err(dev, "request_irq %d failed!\n", irq); dma_controller_destroy(&controller->controller); diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index 9d2dcb121c5e..901dffdf23b1 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -53,13 +53,15 @@ static void musb_do_idle(unsigned long _musb) { struct musb *musb = (void *)_musb; unsigned long flags; +#ifdef CONFIG_USB_MUSB_HDRC_HCD u8 power; +#endif u8 devctl; - devctl = musb_readb(musb->mregs, MUSB_DEVCTL); - spin_lock_irqsave(&musb->lock, flags); + devctl = musb_readb(musb->mregs, MUSB_DEVCTL); + switch (musb->xceiv.state) { case OTG_STATE_A_WAIT_BCON: devctl &= ~MUSB_DEVCTL_SESSION; @@ -194,7 +196,7 @@ static int omap_set_power(struct otg_transceiver *x, unsigned mA) static int musb_platform_resume(struct musb *musb); -void musb_platform_set_mode(struct musb *musb, u8 musb_mode) +int musb_platform_set_mode(struct musb *musb, u8 musb_mode) { u8 devctl = musb_readb(musb->mregs, MUSB_DEVCTL); @@ -202,15 +204,24 @@ void musb_platform_set_mode(struct musb *musb, u8 musb_mode) musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); switch (musb_mode) { +#ifdef CONFIG_USB_MUSB_HDRC_HCD case MUSB_HOST: otg_set_host(&musb->xceiv, musb->xceiv.host); break; +#endif +#ifdef CONFIG_USB_GADGET_MUSB_HDRC case MUSB_PERIPHERAL: otg_set_peripheral(&musb->xceiv, musb->xceiv.gadget); break; +#endif +#ifdef CONFIG_USB_MUSB_OTG case MUSB_OTG: break; +#endif + default: + return -EINVAL; } + return 0; } int __init musb_platform_init(struct musb *musb) diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index b73b036f3d77..9e20fd070d71 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -598,14 +598,14 @@ static void tusb_source_power(struct musb *musb, int is_on) * and peripheral modes in non-OTG configurations by reconfiguring hardware * and then setting musb->board_mode. For now, only support OTG mode. */ -void musb_platform_set_mode(struct musb *musb, u8 musb_mode) +int musb_platform_set_mode(struct musb *musb, u8 musb_mode) { void __iomem *tbase = musb->ctrl_base; u32 otg_stat, phy_otg_ctrl, phy_otg_ena, dev_conf; if (musb->board_mode != MUSB_OTG) { ERR("Changing mode currently only supported in OTG mode\n"); - return; + return -EINVAL; } otg_stat = musb_readl(tbase, TUSB_DEV_OTG_STAT); @@ -641,7 +641,8 @@ void musb_platform_set_mode(struct musb *musb, u8 musb_mode) #endif default: - DBG(2, "Trying to set unknown mode %i\n", musb_mode); + DBG(2, "Trying to set mode %i\n", musb_mode); + return -EINVAL; } musb_writel(tbase, TUSB_PHY_OTG_CTRL, @@ -655,6 +656,8 @@ void musb_platform_set_mode(struct musb *musb, u8 musb_mode) !(otg_stat & TUSB_DEV_OTG_STAT_ID_STATUS)) INFO("Cannot be peripheral with mini-A cable " "otg_stat: %08x\n", otg_stat); + + return 0; } static inline unsigned long diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig new file mode 100644 index 000000000000..afe91bfea7f2 --- /dev/null +++ b/drivers/usb/otg/Kconfig @@ -0,0 +1,44 @@ +# +# USB OTG infrastructure may be needed for peripheral-only, host-only, +# or OTG-capable configurations when OTG transceivers or controllers +# are used. +# + +comment "OTG and related infrastructure" + +if USB || USB_GADGET + +config USB_OTG_UTILS + bool + help + Select this to make sure the build includes objects from + the OTG infrastructure directory. + +# +# USB Transceiver Drivers +# +config USB_GPIO_VBUS + tristate "GPIO based peripheral-only VBUS sensing 'transceiver'" + depends on GENERIC_GPIO + select USB_OTG_UTILS + help + Provides simple GPIO VBUS sensing for controllers with an + internal transceiver via the otg_transceiver interface, and + optionally control of a D+ pullup GPIO as well as a VBUS + current limit regulator. + +config ISP1301_OMAP + tristate "Philips ISP1301 with OMAP OTG" + depends on I2C && ARCH_OMAP_OTG + select USB_OTG_UTILS + help + If you say yes here you get support for the Philips ISP1301 + USB-On-The-Go transceiver working with the OMAP OTG controller. + The ISP1301 is a full speed USB transceiver which is used in + products including H2, H3, and H4 development boards for Texas + Instruments OMAP processors. + + This driver can also be built as a module. If so, the module + will be called isp1301_omap. + +endif # USB || OTG diff --git a/drivers/usb/otg/Makefile b/drivers/usb/otg/Makefile new file mode 100644 index 000000000000..7ceefc676fcb --- /dev/null +++ b/drivers/usb/otg/Makefile @@ -0,0 +1,17 @@ +# +# OTG infrastructure and transceiver drivers +# + +# infrastructure +obj-$(CONFIG_USB_OTG_UTILS) += otg.o + +# transceiver drivers +obj-$(CONFIG_USB_GPIO_VBUS) += gpio_vbus.o +obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o + +ifeq ($(CONFIG_USB_DEBUG),y) +EXTRA_CFLAGS += -DDEBUG +else ifeq ($(CONFIG_USB_GADGET_DEBUG),y) +EXTRA_CFLAGS += -DDEBUG +endif + diff --git a/drivers/usb/otg/gpio_vbus.c b/drivers/usb/otg/gpio_vbus.c new file mode 100644 index 000000000000..63a6036f04be --- /dev/null +++ b/drivers/usb/otg/gpio_vbus.c @@ -0,0 +1,335 @@ +/* + * gpio-vbus.c - simple GPIO VBUS sensing driver for B peripheral devices + * + * Copyright (c) 2008 Philipp Zabel <philipp.zabel@gmail.com> + * + * 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/kernel.h> +#include <linux/platform_device.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/usb.h> + +#include <linux/regulator/consumer.h> + +#include <linux/usb/gadget.h> +#include <linux/usb/gpio_vbus.h> +#include <linux/usb/otg.h> + + +/* + * A simple GPIO VBUS sensing driver for B peripheral only devices + * with internal transceivers. It can control a D+ pullup GPIO and + * a regulator to limit the current drawn from VBUS. + * + * Needs to be loaded before the UDC driver that will use it. + */ +struct gpio_vbus_data { + struct otg_transceiver otg; + struct device *dev; + struct regulator *vbus_draw; + int vbus_draw_enabled; + unsigned mA; +}; + + +/* + * This driver relies on "both edges" triggering. VBUS has 100 msec to + * stabilize, so the peripheral controller driver may need to cope with + * some bouncing due to current surges (e.g. charging local capacitance) + * and contact chatter. + * + * REVISIT in desperate straits, toggling between rising and falling + * edges might be workable. + */ +#define VBUS_IRQ_FLAGS \ + ( IRQF_SAMPLE_RANDOM | IRQF_SHARED \ + | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING ) + + +/* interface to regulator framework */ +static void set_vbus_draw(struct gpio_vbus_data *gpio_vbus, unsigned mA) +{ + struct regulator *vbus_draw = gpio_vbus->vbus_draw; + int enabled; + + if (!vbus_draw) + return; + + enabled = gpio_vbus->vbus_draw_enabled; + if (mA) { + regulator_set_current_limit(vbus_draw, 0, 1000 * mA); + if (!enabled) { + regulator_enable(vbus_draw); + gpio_vbus->vbus_draw_enabled = 1; + } + } else { + if (enabled) { + regulator_disable(vbus_draw); + gpio_vbus->vbus_draw_enabled = 0; + } + } + gpio_vbus->mA = mA; +} + +/* VBUS change IRQ handler */ +static irqreturn_t gpio_vbus_irq(int irq, void *data) +{ + struct platform_device *pdev = data; + struct gpio_vbus_mach_info *pdata = pdev->dev.platform_data; + struct gpio_vbus_data *gpio_vbus = platform_get_drvdata(pdev); + int gpio, vbus; + + vbus = gpio_get_value(pdata->gpio_vbus); + if (pdata->gpio_vbus_inverted) + vbus = !vbus; + + dev_dbg(&pdev->dev, "VBUS %s (gadget: %s)\n", + vbus ? "supplied" : "inactive", + gpio_vbus->otg.gadget ? gpio_vbus->otg.gadget->name : "none"); + + if (!gpio_vbus->otg.gadget) + return IRQ_HANDLED; + + /* Peripheral controllers which manage the pullup themselves won't have + * gpio_pullup configured here. If it's configured here, we'll do what + * isp1301_omap::b_peripheral() does and enable the pullup here... although + * that may complicate usb_gadget_{,dis}connect() support. + */ + gpio = pdata->gpio_pullup; + if (vbus) { + gpio_vbus->otg.state = OTG_STATE_B_PERIPHERAL; + usb_gadget_vbus_connect(gpio_vbus->otg.gadget); + + /* drawing a "unit load" is *always* OK, except for OTG */ + set_vbus_draw(gpio_vbus, 100); + + /* optionally enable D+ pullup */ + if (gpio_is_valid(gpio)) + gpio_set_value(gpio, !pdata->gpio_pullup_inverted); + } else { + /* optionally disable D+ pullup */ + if (gpio_is_valid(gpio)) + gpio_set_value(gpio, pdata->gpio_pullup_inverted); + + set_vbus_draw(gpio_vbus, 0); + + usb_gadget_vbus_disconnect(gpio_vbus->otg.gadget); + gpio_vbus->otg.state = OTG_STATE_B_IDLE; + } + + return IRQ_HANDLED; +} + +/* OTG transceiver interface */ + +/* bind/unbind the peripheral controller */ +static int gpio_vbus_set_peripheral(struct otg_transceiver *otg, + struct usb_gadget *gadget) +{ + struct gpio_vbus_data *gpio_vbus; + struct gpio_vbus_mach_info *pdata; + struct platform_device *pdev; + int gpio, irq; + + gpio_vbus = container_of(otg, struct gpio_vbus_data, otg); + pdev = to_platform_device(gpio_vbus->dev); + pdata = gpio_vbus->dev->platform_data; + irq = gpio_to_irq(pdata->gpio_vbus); + gpio = pdata->gpio_pullup; + + if (!gadget) { + dev_dbg(&pdev->dev, "unregistering gadget '%s'\n", + otg->gadget->name); + + /* optionally disable D+ pullup */ + if (gpio_is_valid(gpio)) + gpio_set_value(gpio, pdata->gpio_pullup_inverted); + + set_vbus_draw(gpio_vbus, 0); + + usb_gadget_vbus_disconnect(otg->gadget); + otg->state = OTG_STATE_UNDEFINED; + + otg->gadget = NULL; + return 0; + } + + otg->gadget = gadget; + dev_dbg(&pdev->dev, "registered gadget '%s'\n", gadget->name); + + /* initialize connection state */ + gpio_vbus_irq(irq, pdev); + return 0; +} + +/* effective for B devices, ignored for A-peripheral */ +static int gpio_vbus_set_power(struct otg_transceiver *otg, unsigned mA) +{ + struct gpio_vbus_data *gpio_vbus; + + gpio_vbus = container_of(otg, struct gpio_vbus_data, otg); + + if (otg->state == OTG_STATE_B_PERIPHERAL) + set_vbus_draw(gpio_vbus, mA); + return 0; +} + +/* for non-OTG B devices: set/clear transceiver suspend mode */ +static int gpio_vbus_set_suspend(struct otg_transceiver *otg, int suspend) +{ + struct gpio_vbus_data *gpio_vbus; + + gpio_vbus = container_of(otg, struct gpio_vbus_data, otg); + + /* draw max 0 mA from vbus in suspend mode; or the previously + * recorded amount of current if not suspended + * + * NOTE: high powered configs (mA > 100) may draw up to 2.5 mA + * if they're wake-enabled ... we don't handle that yet. + */ + return gpio_vbus_set_power(otg, suspend ? 0 : gpio_vbus->mA); +} + +/* platform driver interface */ + +static int __init gpio_vbus_probe(struct platform_device *pdev) +{ + struct gpio_vbus_mach_info *pdata = pdev->dev.platform_data; + struct gpio_vbus_data *gpio_vbus; + struct resource *res; + int err, gpio, irq; + + if (!pdata || !gpio_is_valid(pdata->gpio_vbus)) + return -EINVAL; + gpio = pdata->gpio_vbus; + + gpio_vbus = kzalloc(sizeof(struct gpio_vbus_data), GFP_KERNEL); + if (!gpio_vbus) + return -ENOMEM; + + platform_set_drvdata(pdev, gpio_vbus); + gpio_vbus->dev = &pdev->dev; + gpio_vbus->otg.label = "gpio-vbus"; + gpio_vbus->otg.state = OTG_STATE_UNDEFINED; + gpio_vbus->otg.set_peripheral = gpio_vbus_set_peripheral; + gpio_vbus->otg.set_power = gpio_vbus_set_power; + gpio_vbus->otg.set_suspend = gpio_vbus_set_suspend; + + err = gpio_request(gpio, "vbus_detect"); + if (err) { + dev_err(&pdev->dev, "can't request vbus gpio %d, err: %d\n", + gpio, err); + goto err_gpio; + } + gpio_direction_input(gpio); + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (res) { + irq = res->start; + res->flags &= IRQF_TRIGGER_MASK; + res->flags |= IRQF_SAMPLE_RANDOM | IRQF_SHARED; + } else + irq = gpio_to_irq(gpio); + + /* if data line pullup is in use, initialize it to "not pulling up" */ + gpio = pdata->gpio_pullup; + if (gpio_is_valid(gpio)) { + err = gpio_request(gpio, "udc_pullup"); + if (err) { + dev_err(&pdev->dev, + "can't request pullup gpio %d, err: %d\n", + gpio, err); + gpio_free(pdata->gpio_vbus); + goto err_gpio; + } + gpio_direction_output(gpio, pdata->gpio_pullup_inverted); + } + + err = request_irq(irq, gpio_vbus_irq, VBUS_IRQ_FLAGS, + "vbus_detect", pdev); + if (err) { + dev_err(&pdev->dev, "can't request irq %i, err: %d\n", + irq, err); + goto err_irq; + } + + /* only active when a gadget is registered */ + err = otg_set_transceiver(&gpio_vbus->otg); + if (err) { + dev_err(&pdev->dev, "can't register transceiver, err: %d\n", + err); + goto err_otg; + } + + gpio_vbus->vbus_draw = regulator_get(&pdev->dev, "vbus_draw"); + if (IS_ERR(gpio_vbus->vbus_draw)) { + dev_dbg(&pdev->dev, "can't get vbus_draw regulator, err: %ld\n", + PTR_ERR(gpio_vbus->vbus_draw)); + gpio_vbus->vbus_draw = NULL; + } + + return 0; +err_otg: + free_irq(irq, &pdev->dev); +err_irq: + if (gpio_is_valid(pdata->gpio_pullup)) + gpio_free(pdata->gpio_pullup); + gpio_free(pdata->gpio_vbus); +err_gpio: + platform_set_drvdata(pdev, NULL); + kfree(gpio_vbus); + return err; +} + +static int __exit gpio_vbus_remove(struct platform_device *pdev) +{ + struct gpio_vbus_data *gpio_vbus = platform_get_drvdata(pdev); + struct gpio_vbus_mach_info *pdata = pdev->dev.platform_data; + int gpio = pdata->gpio_vbus; + + regulator_put(gpio_vbus->vbus_draw); + + otg_set_transceiver(NULL); + + free_irq(gpio_to_irq(gpio), &pdev->dev); + if (gpio_is_valid(pdata->gpio_pullup)) + gpio_free(pdata->gpio_pullup); + gpio_free(gpio); + platform_set_drvdata(pdev, NULL); + kfree(gpio_vbus); + + return 0; +} + +/* NOTE: the gpio-vbus device may *NOT* be hotplugged */ + +MODULE_ALIAS("platform:gpio-vbus"); + +static struct platform_driver gpio_vbus_driver = { + .driver = { + .name = "gpio-vbus", + .owner = THIS_MODULE, + }, + .remove = __exit_p(gpio_vbus_remove), +}; + +static int __init gpio_vbus_init(void) +{ + return platform_driver_probe(&gpio_vbus_driver, gpio_vbus_probe); +} +module_init(gpio_vbus_init); + +static void __exit gpio_vbus_exit(void) +{ + platform_driver_unregister(&gpio_vbus_driver); +} +module_exit(gpio_vbus_exit); + +MODULE_DESCRIPTION("simple GPIO controlled OTG transceiver driver"); +MODULE_AUTHOR("Philipp Zabel"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/chips/isp1301_omap.c b/drivers/usb/otg/isp1301_omap.c index 28902ebd5539..e0d56ef2bcb0 100644 --- a/drivers/i2c/chips/isp1301_omap.c +++ b/drivers/usb/otg/isp1301_omap.c @@ -25,6 +25,7 @@ #include <linux/slab.h> #include <linux/interrupt.h> #include <linux/platform_device.h> +#include <linux/gpio.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> #include <linux/usb.h> @@ -33,7 +34,10 @@ #include <linux/workqueue.h> #include <asm/irq.h> +#include <asm/mach-types.h> + #include <mach/usb.h> +#include <mach/mux.h> #ifndef DEBUG @@ -88,14 +92,9 @@ struct isp1301 { /*-------------------------------------------------------------------------*/ -#ifdef CONFIG_MACH_OMAP_H2 - /* board-specific PM hooks */ -#include <asm/gpio.h> -#include <mach/mux.h> -#include <asm/mach-types.h> - +#if defined(CONFIG_MACH_OMAP_H2) || defined(CONFIG_MACH_OMAP_H3) #if defined(CONFIG_TPS65010) || defined(CONFIG_TPS65010_MODULE) @@ -135,6 +134,33 @@ static inline void notresponding(struct isp1301 *isp) #endif +#if defined(CONFIG_MACH_OMAP_H4) + +static void enable_vbus_draw(struct isp1301 *isp, unsigned mA) +{ + /* H4 controls this by DIP switch S2.4; no soft control. + * ON means the charger is always enabled. Leave it OFF + * unless the OTG port is used only in B-peripheral mode. + */ +} + +static void enable_vbus_source(struct isp1301 *isp) +{ + /* this board won't supply more than 8mA vbus power. + * some boards can switch a 100ma "unit load" (or more). + */ +} + + +/* products will deliver OTG messages with LEDs, GUI, etc */ +static inline void notresponding(struct isp1301 *isp) +{ + printk(KERN_NOTICE "OTG device not responding.\n"); +} + + +#endif + /*-------------------------------------------------------------------------*/ static struct i2c_driver isp1301_driver; @@ -334,8 +360,7 @@ static int gadget_suspend(struct isp1301 *isp) * NOTE: guaranteeing certain response times might mean we shouldn't * share keventd's work queue; a realtime task might be safest. */ -void -isp1301_defer_work(struct isp1301 *isp, int work) +static void isp1301_defer_work(struct isp1301 *isp, int work) { int status; @@ -512,7 +537,6 @@ static void update_otg1(struct isp1301 *isp, u8 int_src) otg_ctrl &= ~OTG_XCEIV_INPUTS; otg_ctrl &= ~(OTG_ID|OTG_ASESSVLD|OTG_VBUSVLD); - if (int_src & INTR_SESS_VLD) otg_ctrl |= OTG_ASESSVLD; else if (isp->otg.state == OTG_STATE_A_WAIT_VFALL) { @@ -886,11 +910,11 @@ static int otg_probe(struct platform_device *dev) static int otg_remove(struct platform_device *dev) { - otg_dev = 0; + otg_dev = NULL; return 0; } -struct platform_driver omap_otg_driver = { +static struct platform_driver omap_otg_driver = { .probe = otg_probe, .remove = otg_remove, .driver = { @@ -1212,6 +1236,8 @@ static void isp1301_release(struct device *dev) isp = dev_get_drvdata(dev); + /* FIXME -- not with a "new style" driver, it doesn't!! */ + /* ugly -- i2c hijacks our memory hook to wait_for_completion() */ if (isp->i2c_release) isp->i2c_release(dev); @@ -1233,7 +1259,7 @@ static int __exit isp1301_remove(struct i2c_client *i2c) otg_unbind(isp); #endif if (machine_is_omap_h2()) - omap_free_gpio(2); + gpio_free(2); isp->timer.data = 0; set_bit(WORK_STOP, &isp->todo); @@ -1241,7 +1267,7 @@ static int __exit isp1301_remove(struct i2c_client *i2c) flush_scheduled_work(); put_device(&i2c->dev); - the_transceiver = 0; + the_transceiver = NULL; return 0; } @@ -1295,7 +1321,7 @@ isp1301_set_host(struct otg_transceiver *otg, struct usb_bus *host) if (!host) { omap_writew(0, OTG_IRQ_EN); power_down(isp); - isp->otg.host = 0; + isp->otg.host = NULL; return 0; } @@ -1344,7 +1370,9 @@ static int isp1301_set_peripheral(struct otg_transceiver *otg, struct usb_gadget *gadget) { struct isp1301 *isp = container_of(otg, struct isp1301, otg); +#ifndef CONFIG_USB_OTG u32 l; +#endif if (!otg || isp != the_transceiver) return -ENODEV; @@ -1354,7 +1382,7 @@ isp1301_set_peripheral(struct otg_transceiver *otg, struct usb_gadget *gadget) if (!isp->otg.default_a) enable_vbus_draw(isp, 0); usb_gadget_vbus_disconnect(isp->otg.gadget); - isp->otg.gadget = 0; + isp->otg.gadget = NULL; power_down(isp); return 0; } @@ -1379,7 +1407,7 @@ isp1301_set_peripheral(struct otg_transceiver *otg, struct usb_gadget *gadget) power_up(isp); isp->otg.state = OTG_STATE_B_IDLE; - if (machine_is_omap_h2()) + if (machine_is_omap_h2() || machine_is_omap_h3()) isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0); isp1301_set_bits(isp, ISP1301_INTERRUPT_RISING, @@ -1499,7 +1527,8 @@ isp1301_start_hnp(struct otg_transceiver *dev) /*-------------------------------------------------------------------------*/ -static int __init isp1301_probe(struct i2c_client *i2c) +static int __init +isp1301_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { int status; struct isp1301 *isp; @@ -1647,7 +1676,7 @@ module_init(isp_init); static void __exit isp_exit(void) { if (the_transceiver) - otg_set_transceiver(0); + otg_set_transceiver(NULL); i2c_del_driver(&isp1301_driver); } module_exit(isp_exit); diff --git a/drivers/usb/otg/otg.c b/drivers/usb/otg/otg.c new file mode 100644 index 000000000000..019d775291d1 --- /dev/null +++ b/drivers/usb/otg/otg.c @@ -0,0 +1,59 @@ +/* + * otg.c -- USB OTG utility code + * + * Copyright (C) 2004 Texas Instruments + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/device.h> + +#include <linux/usb/otg.h> + +static struct otg_transceiver *xceiv; + +/** + * otg_get_transceiver - find the (single) OTG transceiver driver + * + * Returns the transceiver driver, after getting a refcount to it; or + * null if there is no such transceiver. The caller is responsible for + * calling otg_put_transceiver() to release that count. + */ +struct otg_transceiver *otg_get_transceiver(void) +{ + if (xceiv) + get_device(xceiv->dev); + return xceiv; +} +EXPORT_SYMBOL(otg_get_transceiver); + +/** + * otg_put_transceiver - release the (single) OTG transceiver driver + * @x: the transceiver returned by otg_get_transceiver() + * + * Releases a refcount the caller received from otg_get_transceiver(). + */ +void otg_put_transceiver(struct otg_transceiver *x) +{ + put_device(x->dev); +} +EXPORT_SYMBOL(otg_put_transceiver); + +/** + * otg_set_transceiver - release the (single) OTG transceiver driver + * @x: the USB OTG transceiver to be used; or NULL + * + * This call is exclusively for use by transceiver drivers. + */ +int otg_set_transceiver(struct otg_transceiver *x) +{ + if (xceiv && x) + return -EBUSY; + xceiv = x; + return 0; +} +EXPORT_SYMBOL(otg_set_transceiver); diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig index 70338f4ec918..da30784a0ef2 100644 --- a/drivers/usb/serial/Kconfig +++ b/drivers/usb/serial/Kconfig @@ -565,6 +565,15 @@ config USB_SERIAL_OMNINET To compile this driver as a module, choose M here: the module will be called omninet. +config USB_SERIAL_OPTICON + tristate "USB Opticon Barcode driver (serial mode)" + help + Say Y here if you want to use a Opticon USB Barcode device + in serial emulation mode. + + To compile this driver as a module, choose M here: the + module will be called opticon. + config USB_SERIAL_DEBUG tristate "USB Debugging Device" help diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile index 6047f818adfe..9a4f59e0ee5f 100644 --- a/drivers/usb/serial/Makefile +++ b/drivers/usb/serial/Makefile @@ -41,6 +41,7 @@ obj-$(CONFIG_USB_SERIAL_MOS7840) += mos7840.o obj-$(CONFIG_USB_SERIAL_MOTOROLA) += moto_modem.o obj-$(CONFIG_USB_SERIAL_NAVMAN) += navman.o obj-$(CONFIG_USB_SERIAL_OMNINET) += omninet.o +obj-$(CONFIG_USB_SERIAL_OPTICON) += opticon.o obj-$(CONFIG_USB_SERIAL_OPTION) += option.o obj-$(CONFIG_USB_SERIAL_OTI6858) += oti6858.o obj-$(CONFIG_USB_SERIAL_PL2303) += pl2303.o diff --git a/drivers/usb/serial/cp2101.c b/drivers/usb/serial/cp2101.c index 8008d0bc80ad..cfaf1f085535 100644 --- a/drivers/usb/serial/cp2101.c +++ b/drivers/usb/serial/cp2101.c @@ -56,6 +56,7 @@ static void cp2101_shutdown(struct usb_serial *); static int debug; static struct usb_device_id id_table [] = { + { USB_DEVICE(0x0471, 0x066A) }, /* AKTAKOM ACE-1001 cable */ { USB_DEVICE(0x0489, 0xE000) }, /* Pirelli Broadband S.p.A, DP-L10 SIP/GSM Mobile */ { USB_DEVICE(0x08e6, 0x5501) }, /* Gemalto Prox-PU/CU contactless smartcard reader */ { USB_DEVICE(0x0FCF, 0x1003) }, /* Dynastream ANT development board */ @@ -67,6 +68,7 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(0x10C4, 0x800A) }, /* SPORTident BSM7-D-USB main station */ { USB_DEVICE(0x10C4, 0x803B) }, /* Pololu USB-serial converter */ { USB_DEVICE(0x10C4, 0x8053) }, /* Enfora EDG1228 */ + { USB_DEVICE(0x10C4, 0x8054) }, /* Enfora GSM2228 */ { USB_DEVICE(0x10C4, 0x8066) }, /* Argussoft In-System Programmer */ { USB_DEVICE(0x10C4, 0x807A) }, /* Crumb128 board */ { USB_DEVICE(0x10C4, 0x80CA) }, /* Degree Controls Inc */ @@ -85,6 +87,7 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(0x10C4, 0x8218) }, /* Lipowsky Industrie Elektronik GmbH, HARP-1 */ { USB_DEVICE(0x10c4, 0x8293) }, /* Telegesys ETRX2USB */ { USB_DEVICE(0x10C4, 0x8341) }, /* Siemens MC35PU GPRS Modem */ + { USB_DEVICE(0x10C4, 0x83A8) }, /* Amber Wireless AMB2560 */ { USB_DEVICE(0x10C4, 0xEA60) }, /* Silicon Labs factory default */ { USB_DEVICE(0x10C4, 0xEA61) }, /* Silicon Labs factory default */ { USB_DEVICE(0x10C4, 0xF001) }, /* Elan Digital Systems USBscope50 */ diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c index 3ac59a8a980f..f530032ed93d 100644 --- a/drivers/usb/serial/ipw.c +++ b/drivers/usb/serial/ipw.c @@ -473,7 +473,7 @@ static struct usb_serial_driver ipw_device = { -static int usb_ipw_init(void) +static int __init usb_ipw_init(void) { int retval; @@ -490,7 +490,7 @@ static int usb_ipw_init(void) return 0; } -static void usb_ipw_exit(void) +static void __exit usb_ipw_exit(void) { usb_deregister(&usb_ipw_driver); usb_serial_deregister(&ipw_device); diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c index b679a556b98d..4e2cda93da59 100644 --- a/drivers/usb/serial/ir-usb.c +++ b/drivers/usb/serial/ir-usb.c @@ -26,7 +26,7 @@ * Introduced common header to be used also in USB Gadget Framework. * Still needs some other style fixes. * - * 2007_Jun_21 Alan Cox <alan@redhat.com> + * 2007_Jun_21 Alan Cox <alan@lxorguk.ukuu.org.uk> * Minimal cleanups for some of the driver problens and tty layer abuse. * Still needs fixing to allow multiple dongles. * diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c new file mode 100644 index 000000000000..cea326f1f105 --- /dev/null +++ b/drivers/usb/serial/opticon.c @@ -0,0 +1,358 @@ +/* + * Opticon USB barcode to serial driver + * + * Copyright (C) 2008 Greg Kroah-Hartman <gregkh@suse.de> + * Copyright (C) 2008 Novell 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/kernel.h> +#include <linux/init.h> +#include <linux/tty.h> +#include <linux/tty_driver.h> +#include <linux/tty_flip.h> +#include <linux/module.h> +#include <linux/usb.h> +#include <linux/usb/serial.h> +#include <linux/uaccess.h> + +static int debug; + +static struct usb_device_id id_table[] = { + { USB_DEVICE(0x065a, 0x0009) }, + { }, +}; +MODULE_DEVICE_TABLE(usb, id_table); + +/* This structure holds all of the individual device information */ +struct opticon_private { + struct usb_device *udev; + struct usb_serial *serial; + struct usb_serial_port *port; + unsigned char *bulk_in_buffer; + struct urb *bulk_read_urb; + int buffer_size; + u8 bulk_address; + spinlock_t lock; /* protects the following flags */ + bool throttled; + bool actually_throttled; + bool rts; +}; + +static void opticon_bulk_callback(struct urb *urb) +{ + struct opticon_private *priv = urb->context; + unsigned char *data = urb->transfer_buffer; + struct usb_serial_port *port = priv->port; + int status = urb->status; + struct tty_struct *tty; + int result; + int available_room = 0; + int data_length; + + dbg("%s - port %d", __func__, port->number); + + switch (status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", + __func__, status); + return; + default: + dbg("%s - nonzero urb status received: %d", + __func__, status); + goto exit; + } + + usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, + data); + + if (urb->actual_length > 2) { + data_length = urb->actual_length - 2; + + /* + * Data from the device comes with a 2 byte header: + * + * <0x00><0x00>data... + * This is real data to be sent to the tty layer + * <0x00><0x01)level + * This is a RTS level change, the third byte is the RTS + * value (0 for low, 1 for high). + */ + if ((data[0] == 0x00) && (data[1] == 0x00)) { + /* real data, send it to the tty layer */ + tty = tty_port_tty_get(&port->port); + if (tty) { + available_room = tty_buffer_request_room(tty, + data_length); + if (available_room) { + tty_insert_flip_string(tty, data, + available_room); + tty_flip_buffer_push(tty); + } + tty_kref_put(tty); + } + } else { + if ((data[0] == 0x00) && (data[1] == 0x01)) { + if (data[2] == 0x00) + priv->rts = false; + else + priv->rts = true; + /* FIXME change the RTS level */ + } else { + dev_dbg(&priv->udev->dev, + "Unknown data packet received from the device:" + " %2x %2x\n", + data[0], data[1]); + } + } + } else { + dev_dbg(&priv->udev->dev, + "Improper ammount of data received from the device, " + "%d bytes", urb->actual_length); + } + +exit: + spin_lock(&priv->lock); + + /* Continue trying to always read if we should */ + if (!priv->throttled) { + usb_fill_bulk_urb(priv->bulk_read_urb, priv->udev, + usb_rcvbulkpipe(priv->udev, + priv->bulk_address), + priv->bulk_in_buffer, priv->buffer_size, + opticon_bulk_callback, priv); + result = usb_submit_urb(port->read_urb, GFP_ATOMIC); + if (result) + dev_err(&port->dev, + "%s - failed resubmitting read urb, error %d\n", + __func__, result); + } else + priv->actually_throttled = true; + spin_unlock(&priv->lock); +} + +static int opticon_open(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp) +{ + struct opticon_private *priv = usb_get_serial_data(port->serial); + unsigned long flags; + int result = 0; + + dbg("%s - port %d", __func__, port->number); + + spin_lock_irqsave(&priv->lock, flags); + priv->throttled = false; + priv->actually_throttled = false; + priv->port = port; + spin_unlock_irqrestore(&priv->lock, flags); + + /* + * Force low_latency on so that our tty_push actually forces the data + * through, otherwise it is scheduled, and with high data rates (like + * with OHCI) data can get lost. + */ + if (tty) + tty->low_latency = 1; + + /* Start reading from the device */ + usb_fill_bulk_urb(priv->bulk_read_urb, priv->udev, + usb_rcvbulkpipe(priv->udev, + priv->bulk_address), + priv->bulk_in_buffer, priv->buffer_size, + opticon_bulk_callback, priv); + result = usb_submit_urb(priv->bulk_read_urb, GFP_KERNEL); + if (result) + dev_err(&port->dev, + "%s - failed resubmitting read urb, error %d\n", + __func__, result); + return result; +} + +static void opticon_close(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp) +{ + struct opticon_private *priv = usb_get_serial_data(port->serial); + + dbg("%s - port %d", __func__, port->number); + + /* shutdown our urbs */ + usb_kill_urb(priv->bulk_read_urb); +} + +static void opticon_throttle(struct tty_struct *tty) +{ + struct usb_serial_port *port = tty->driver_data; + struct opticon_private *priv = usb_get_serial_data(port->serial); + unsigned long flags; + + dbg("%s - port %d", __func__, port->number); + spin_lock_irqsave(&priv->lock, flags); + priv->throttled = true; + spin_unlock_irqrestore(&priv->lock, flags); +} + + +static void opticon_unthrottle(struct tty_struct *tty) +{ + struct usb_serial_port *port = tty->driver_data; + struct opticon_private *priv = usb_get_serial_data(port->serial); + unsigned long flags; + int result; + + dbg("%s - port %d", __func__, port->number); + + spin_lock_irqsave(&priv->lock, flags); + priv->throttled = false; + priv->actually_throttled = false; + spin_unlock_irqrestore(&priv->lock, flags); + + priv->bulk_read_urb->dev = port->serial->dev; + result = usb_submit_urb(priv->bulk_read_urb, GFP_ATOMIC); + if (result) + dev_err(&port->dev, + "%s - failed submitting read urb, error %d\n", + __func__, result); +} + +static int opticon_startup(struct usb_serial *serial) +{ + struct opticon_private *priv; + struct usb_host_interface *intf; + int i; + int retval = -ENOMEM; + bool bulk_in_found = false; + + /* create our private serial structure */ + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (priv == NULL) { + dev_err(&serial->dev->dev, "%s - Out of memory\n", __func__); + return -ENOMEM; + } + spin_lock_init(&priv->lock); + priv->serial = serial; + priv->port = serial->port[0]; + priv->udev = serial->dev; + + /* find our bulk endpoint */ + intf = serial->interface->altsetting; + for (i = 0; i < intf->desc.bNumEndpoints; ++i) { + struct usb_endpoint_descriptor *endpoint; + + endpoint = &intf->endpoint[i].desc; + if (!usb_endpoint_is_bulk_in(endpoint)) + continue; + + priv->bulk_read_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!priv->bulk_read_urb) { + dev_err(&priv->udev->dev, "out of memory\n"); + goto error; + } + + priv->buffer_size = le16_to_cpu(endpoint->wMaxPacketSize) * 2; + priv->bulk_in_buffer = kmalloc(priv->buffer_size, GFP_KERNEL); + if (!priv->bulk_in_buffer) { + dev_err(&priv->udev->dev, "out of memory\n"); + goto error; + } + + priv->bulk_address = endpoint->bEndpointAddress; + + /* set up our bulk urb */ + usb_fill_bulk_urb(priv->bulk_read_urb, priv->udev, + usb_rcvbulkpipe(priv->udev, + endpoint->bEndpointAddress), + priv->bulk_in_buffer, priv->buffer_size, + opticon_bulk_callback, priv); + + bulk_in_found = true; + break; + } + + if (!bulk_in_found) { + dev_err(&priv->udev->dev, + "Error - the proper endpoints were not found!\n"); + goto error; + } + + usb_set_serial_data(serial, priv); + return 0; + +error: + usb_free_urb(priv->bulk_read_urb); + kfree(priv->bulk_in_buffer); + kfree(priv); + return retval; +} + +static void opticon_shutdown(struct usb_serial *serial) +{ + struct opticon_private *priv = usb_get_serial_data(serial); + + dbg("%s", __func__); + + usb_kill_urb(priv->bulk_read_urb); + usb_free_urb(priv->bulk_read_urb); + kfree(priv->bulk_in_buffer); + kfree(priv); + usb_set_serial_data(serial, NULL); +} + + +static struct usb_driver opticon_driver = { + .name = "opticon", + .probe = usb_serial_probe, + .disconnect = usb_serial_disconnect, + .id_table = id_table, + .no_dynamic_id = 1, +}; + +static struct usb_serial_driver opticon_device = { + .driver = { + .owner = THIS_MODULE, + .name = "opticon", + }, + .id_table = id_table, + .usb_driver = &opticon_driver, + .num_ports = 1, + .attach = opticon_startup, + .open = opticon_open, + .close = opticon_close, + .shutdown = opticon_shutdown, + .throttle = opticon_throttle, + .unthrottle = opticon_unthrottle, +}; + +static int __init opticon_init(void) +{ + int retval; + + retval = usb_serial_register(&opticon_device); + if (retval) + return retval; + retval = usb_register(&opticon_driver); + if (retval) + usb_serial_deregister(&opticon_device); + return retval; +} + +static void __exit opticon_exit(void) +{ + usb_deregister(&opticon_driver); + usb_serial_deregister(&opticon_device); +} + +module_init(opticon_init); +module_exit(opticon_exit); +MODULE_LICENSE("GPL"); + +module_param(debug, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug enabled or not"); diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index bd07eaa300b9..809697b3c7fc 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -160,6 +160,11 @@ static int option_send_setup(struct tty_struct *tty, struct usb_serial_port *po #define NOVATELWIRELESS_VENDOR_ID 0x1410 +/* YISO PRODUCTS */ + +#define YISO_VENDOR_ID 0x0EAB +#define YISO_PRODUCT_U893 0xC893 + /* MERLIN EVDO PRODUCTS */ #define NOVATELWIRELESS_PRODUCT_V640 0x1100 #define NOVATELWIRELESS_PRODUCT_V620 0x1110 @@ -219,6 +224,7 @@ static int option_send_setup(struct tty_struct *tty, struct usb_serial_port *po #define ONDA_VENDOR_ID 0x19d2 #define ONDA_PRODUCT_MSA501HS 0x0001 #define ONDA_PRODUCT_ET502HS 0x0002 +#define ONDA_PRODUCT_MT503HS 0x0200 #define BANDRICH_VENDOR_ID 0x1A8D #define BANDRICH_PRODUCT_C100_1 0x1002 @@ -408,6 +414,41 @@ static struct usb_device_id option_ids[] = { { USB_DEVICE(AXESSTEL_VENDOR_ID, AXESSTEL_PRODUCT_MV110H) }, { USB_DEVICE(ONDA_VENDOR_ID, ONDA_PRODUCT_MSA501HS) }, { USB_DEVICE(ONDA_VENDOR_ID, ONDA_PRODUCT_ET502HS) }, + { USB_DEVICE(ONDA_VENDOR_ID, 0x0003) }, + { USB_DEVICE(ONDA_VENDOR_ID, 0x0004) }, + { USB_DEVICE(ONDA_VENDOR_ID, 0x0005) }, + { USB_DEVICE(ONDA_VENDOR_ID, 0x0006) }, + { USB_DEVICE(ONDA_VENDOR_ID, 0x0007) }, + { USB_DEVICE(ONDA_VENDOR_ID, 0x0008) }, + { USB_DEVICE(ONDA_VENDOR_ID, 0x0009) }, + { USB_DEVICE(ONDA_VENDOR_ID, 0x000a) }, + { USB_DEVICE(ONDA_VENDOR_ID, 0x000b) }, + { USB_DEVICE(ONDA_VENDOR_ID, 0x000c) }, + { USB_DEVICE(ONDA_VENDOR_ID, 0x000d) }, + { USB_DEVICE(ONDA_VENDOR_ID, 0x000e) }, + { USB_DEVICE(ONDA_VENDOR_ID, 0x000f) }, + { USB_DEVICE(ONDA_VENDOR_ID, 0x0010) }, + { USB_DEVICE(ONDA_VENDOR_ID, 0x0011) }, + { USB_DEVICE(ONDA_VENDOR_ID, 0x0012) }, + { USB_DEVICE(ONDA_VENDOR_ID, 0x0013) }, + { USB_DEVICE(ONDA_VENDOR_ID, 0x0014) }, + { USB_DEVICE(ONDA_VENDOR_ID, 0x0015) }, + { USB_DEVICE(ONDA_VENDOR_ID, 0x0016) }, + { USB_DEVICE(ONDA_VENDOR_ID, 0x0017) }, + { USB_DEVICE(ONDA_VENDOR_ID, 0x0018) }, + { USB_DEVICE(ONDA_VENDOR_ID, 0x0019) }, + { USB_DEVICE(ONDA_VENDOR_ID, 0x0020) }, + { USB_DEVICE(ONDA_VENDOR_ID, 0x0021) }, + { USB_DEVICE(ONDA_VENDOR_ID, 0x0022) }, + { USB_DEVICE(ONDA_VENDOR_ID, 0x0023) }, + { USB_DEVICE(ONDA_VENDOR_ID, 0x0024) }, + { USB_DEVICE(ONDA_VENDOR_ID, 0x0025) }, + { USB_DEVICE(ONDA_VENDOR_ID, 0x0026) }, + { USB_DEVICE(ONDA_VENDOR_ID, 0x0027) }, + { USB_DEVICE(ONDA_VENDOR_ID, 0x0028) }, + { USB_DEVICE(ONDA_VENDOR_ID, 0x0029) }, + { USB_DEVICE(ONDA_VENDOR_ID, ONDA_PRODUCT_MT503HS) }, + { USB_DEVICE(YISO_VENDOR_ID, YISO_PRODUCT_U893) }, { USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_C100_1) }, { USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_C100_2) }, { USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_1004) }, diff --git a/drivers/usb/storage/Kconfig b/drivers/usb/storage/Kconfig index 3d9249632ae1..c68b738900bd 100644 --- a/drivers/usb/storage/Kconfig +++ b/drivers/usb/storage/Kconfig @@ -2,8 +2,8 @@ # USB Storage driver configuration # -comment "NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'" -comment "may also be needed; see USB_STORAGE Help for more information" +comment "NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may also be needed;" +comment "see USB_STORAGE Help for more information" depends on USB config USB_STORAGE diff --git a/drivers/usb/storage/protocol.c b/drivers/usb/storage/protocol.c index 3b3357e20ea7..be441d84bc64 100644 --- a/drivers/usb/storage/protocol.c +++ b/drivers/usb/storage/protocol.c @@ -56,9 +56,9 @@ * Protocol routines ***********************************************************************/ -void usb_stor_qic157_command(struct scsi_cmnd *srb, struct us_data *us) +void usb_stor_pad12_command(struct scsi_cmnd *srb, struct us_data *us) { - /* Pad the ATAPI command with zeros + /* Pad the SCSI command with zeros out to 12 bytes * * NOTE: This only works because a scsi_cmnd struct field contains * a unsigned char cmnd[16], so we know we have storage available @@ -73,26 +73,6 @@ void usb_stor_qic157_command(struct scsi_cmnd *srb, struct us_data *us) usb_stor_invoke_transport(srb, us); } -void usb_stor_ATAPI_command(struct scsi_cmnd *srb, struct us_data *us) -{ - /* Pad the ATAPI command with zeros - * - * NOTE: This only works because a scsi_cmnd struct field contains - * a unsigned char cmnd[16], so we know we have storage available - */ - - /* Pad the ATAPI command with zeros */ - for (; srb->cmd_len<12; srb->cmd_len++) - srb->cmnd[srb->cmd_len] = 0; - - /* set command length to 12 bytes */ - srb->cmd_len = 12; - - /* send the command to the transport layer */ - usb_stor_invoke_transport(srb, us); -} - - void usb_stor_ufi_command(struct scsi_cmnd *srb, struct us_data *us) { /* fix some commands -- this is a form of mode translation diff --git a/drivers/usb/storage/protocol.h b/drivers/usb/storage/protocol.h index 487056ffb516..ffc3e2af0156 100644 --- a/drivers/usb/storage/protocol.h +++ b/drivers/usb/storage/protocol.h @@ -40,8 +40,7 @@ #define _PROTOCOL_H_ /* Protocol handling routines */ -extern void usb_stor_ATAPI_command(struct scsi_cmnd*, struct us_data*); -extern void usb_stor_qic157_command(struct scsi_cmnd*, struct us_data*); +extern void usb_stor_pad12_command(struct scsi_cmnd*, struct us_data*); extern void usb_stor_ufi_command(struct scsi_cmnd*, struct us_data*); extern void usb_stor_transparent_scsi_command(struct scsi_cmnd*, struct us_data*); diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index 09779f6a8179..e9d6c196a7ab 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -129,6 +129,14 @@ static int slave_configure(struct scsi_device *sdev) max_sectors); } + /* Some USB host controllers can't do DMA; they have to use PIO. + * They indicate this by setting their dma_mask to NULL. For + * such controllers we need to make sure the block layer sets + * up bounce buffers in addressable memory. + */ + if (!us->pusb_dev->bus->controller->dma_mask) + blk_queue_bounce_limit(sdev->request_queue, BLK_BOUNCE_HIGH); + /* We can't put these settings in slave_alloc() because that gets * called before the device type is known. Consequently these * settings can't be overridden via the scsi devinfo mechanism. */ @@ -170,6 +178,10 @@ static int slave_configure(struct scsi_device *sdev) if (us->fflags & US_FL_CAPACITY_HEURISTICS) sdev->guess_capacity = 1; + /* assume SPC3 or latter devices support sense size > 18 */ + if (sdev->scsi_level > SCSI_SPC_2) + us->fflags |= US_FL_SANE_SENSE; + /* Some devices report a SCSI revision level above 2 but are * unable to handle the REPORT LUNS command (for which * support is mandatory at level 3). Since we already have diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index 79108d5d3171..9cc30afd6d31 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -579,6 +579,20 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) } /* + * Determine if this device is SAT by seeing if the + * command executed successfully. Otherwise we'll have + * to wait for at least one CHECK_CONDITION to determine + * SANE_SENSE support + */ + if ((srb->cmnd[0] == ATA_16 || srb->cmnd[0] == ATA_12) && + result == USB_STOR_TRANSPORT_GOOD && + !(us->fflags & US_FL_SANE_SENSE) && + !(srb->cmnd[2] & 0x20)) { + US_DEBUGP("-- SAT supported, increasing auto-sense\n"); + us->fflags |= US_FL_SANE_SENSE; + } + + /* * A short transfer on a command where we don't expect it * is unusual, but it doesn't mean we need to auto-sense. */ @@ -595,10 +609,15 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) if (need_auto_sense) { int temp_result; struct scsi_eh_save ses; + int sense_size = US_SENSE_SIZE; + + /* device supports and needs bigger sense buffer */ + if (us->fflags & US_FL_SANE_SENSE) + sense_size = ~0; US_DEBUGP("Issuing auto-REQUEST_SENSE\n"); - scsi_eh_prep_cmnd(srb, &ses, NULL, 0, US_SENSE_SIZE); + scsi_eh_prep_cmnd(srb, &ses, NULL, 0, sense_size); /* FIXME: we must do the protocol translation here */ if (us->subclass == US_SC_RBC || us->subclass == US_SC_SCSI || @@ -632,6 +651,25 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) return; } + /* If the sense data returned is larger than 18-bytes then we + * assume this device supports requesting more in the future. + * The response code must be 70h through 73h inclusive. + */ + if (srb->sense_buffer[7] > (US_SENSE_SIZE - 8) && + !(us->fflags & US_FL_SANE_SENSE) && + (srb->sense_buffer[0] & 0x7C) == 0x70) { + US_DEBUGP("-- SANE_SENSE support enabled\n"); + us->fflags |= US_FL_SANE_SENSE; + + /* Indicate to the user that we truncated their sense + * because we didn't know it supported larger sense. + */ + US_DEBUGP("-- Sense data truncated to %i from %i\n", + US_SENSE_SIZE, + srb->sense_buffer[7] + 8); + srb->sense_buffer[7] = (US_SENSE_SIZE - 8); + } + US_DEBUGP("-- Result from auto-sense is %d\n", temp_result); US_DEBUGP("-- code: 0x%x, key: 0x%x, ASC: 0x%x, ASCQ: 0x%x\n", srb->sense_buffer[0], @@ -718,10 +756,10 @@ void usb_stor_stop_transport(struct us_data *us) } /* - * Control/Bulk/Interrupt transport + * Control/Bulk and Control/Bulk/Interrupt transport */ -int usb_stor_CBI_transport(struct scsi_cmnd *srb, struct us_data *us) +int usb_stor_CB_transport(struct scsi_cmnd *srb, struct us_data *us) { unsigned int transfer_length = scsi_bufflen(srb); unsigned int pipe = 0; @@ -763,6 +801,13 @@ int usb_stor_CBI_transport(struct scsi_cmnd *srb, struct us_data *us) } /* STATUS STAGE */ + + /* NOTE: CB does not have a status stage. Silly, I know. So + * we have to catch this at a higher level. + */ + if (us->protocol != US_PR_CBI) + return USB_STOR_TRANSPORT_GOOD; + result = usb_stor_intr_transfer(us, us->iobuf, 2); US_DEBUGP("Got interrupt data (0x%x, 0x%x)\n", us->iobuf[0], us->iobuf[1]); @@ -817,56 +862,6 @@ int usb_stor_CBI_transport(struct scsi_cmnd *srb, struct us_data *us) } /* - * Control/Bulk transport - */ -int usb_stor_CB_transport(struct scsi_cmnd *srb, struct us_data *us) -{ - unsigned int transfer_length = scsi_bufflen(srb); - int result; - - /* COMMAND STAGE */ - /* let's send the command via the control pipe */ - result = usb_stor_ctrl_transfer(us, us->send_ctrl_pipe, - US_CBI_ADSC, - USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, - us->ifnum, srb->cmnd, srb->cmd_len); - - /* check the return code for the command */ - US_DEBUGP("Call to usb_stor_ctrl_transfer() returned %d\n", result); - - /* if we stalled the command, it means command failed */ - if (result == USB_STOR_XFER_STALLED) { - return USB_STOR_TRANSPORT_FAILED; - } - - /* Uh oh... serious problem here */ - if (result != USB_STOR_XFER_GOOD) { - return USB_STOR_TRANSPORT_ERROR; - } - - /* DATA STAGE */ - /* transfer the data payload for this command, if one exists*/ - if (transfer_length) { - unsigned int pipe = srb->sc_data_direction == DMA_FROM_DEVICE ? - us->recv_bulk_pipe : us->send_bulk_pipe; - result = usb_stor_bulk_srb(us, pipe, srb); - US_DEBUGP("CB data stage result is 0x%x\n", result); - - /* if we stalled the data transfer it means command failed */ - if (result == USB_STOR_XFER_STALLED) - return USB_STOR_TRANSPORT_FAILED; - if (result > USB_STOR_XFER_STALLED) - return USB_STOR_TRANSPORT_ERROR; - } - - /* STATUS STAGE */ - /* NOTE: CB does not have a status stage. Silly, I know. So - * we have to catch this at a higher level. - */ - return USB_STOR_TRANSPORT_GOOD; -} - -/* * Bulk only transport */ @@ -1173,10 +1168,9 @@ int usb_stor_Bulk_reset(struct us_data *us) */ int usb_stor_port_reset(struct us_data *us) { - int result, rc_lock; + int result; - result = rc_lock = - usb_lock_device_for_reset(us->pusb_dev, us->pusb_intf); + result = usb_lock_device_for_reset(us->pusb_dev, us->pusb_intf); if (result < 0) US_DEBUGP("unable to lock device for reset: %d\n", result); else { @@ -1189,8 +1183,7 @@ int usb_stor_port_reset(struct us_data *us) US_DEBUGP("usb_reset_device returns %d\n", result); } - if (rc_lock) - usb_unlock_device(us->pusb_dev); + usb_unlock_device(us->pusb_dev); } return result; } diff --git a/drivers/usb/storage/transport.h b/drivers/usb/storage/transport.h index e70b88182f0e..242ff5e791a5 100644 --- a/drivers/usb/storage/transport.h +++ b/drivers/usb/storage/transport.h @@ -113,8 +113,6 @@ struct bulk_cs_wrap { #define US_CBI_ADSC 0 -extern int usb_stor_CBI_transport(struct scsi_cmnd *, struct us_data*); - extern int usb_stor_CB_transport(struct scsi_cmnd *, struct us_data*); extern int usb_stor_CB_reset(struct us_data*); diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index fb9e20e624c1..b964ed590212 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -85,6 +85,13 @@ UNUSUAL_DEV( 0x03f0, 0x0307, 0x0001, 0x0001, US_SC_8070, US_PR_USBAT, init_usbat_cd, 0), #endif +/* Reported by Ben Efros <ben@pc-doctor.com> */ +UNUSUAL_DEV( 0x03f0, 0x070c, 0x0000, 0x0000, + "HP", + "Personal Media Drive", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_SANE_SENSE ), + /* Reported by Grant Grundler <grundler@parisc-linux.org> * HP r707 camera in "Disk" mode with 2.00.23 or 2.00.24 firmware. */ @@ -167,6 +174,21 @@ UNUSUAL_DEV( 0x0421, 0x005d, 0x0001, 0x0600, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_CAPACITY ), +/* Patch for Nokia 5310 capacity */ +UNUSUAL_DEV( 0x0421, 0x006a, 0x0000, 0x0591, + "Nokia", + "5310", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY ), + +/* Submitted by Ricky Wong Yung Fei <evilbladewarrior@gmail.com> */ +/* Nokia 7610 Supernova - Too many sectors reported in usb storage mode */ +UNUSUAL_DEV( 0x0421, 0x00f5, 0x0000, 0x0470, + "Nokia", + "7610 Supernova", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY ), + /* Reported by Mario Rettig <mariorettig@web.de> */ UNUSUAL_DEV( 0x0421, 0x042e, 0x0100, 0x0100, "Nokia", @@ -233,26 +255,19 @@ UNUSUAL_DEV( 0x0421, 0x0495, 0x0370, 0x0370, US_FL_MAX_SECTORS_64 ), /* Reported by Cedric Godin <cedric@belbone.be> */ -UNUSUAL_DEV( 0x0421, 0x04b9, 0x0551, 0x0551, +UNUSUAL_DEV( 0x0421, 0x04b9, 0x0500, 0x0551, "Nokia", "5300", US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_CAPACITY ), /* Reported by Richard Nauber <RichardNauber@web.de> */ -UNUSUAL_DEV( 0x0421, 0x04fa, 0x0601, 0x0601, +UNUSUAL_DEV( 0x0421, 0x04fa, 0x0550, 0x0660, "Nokia", "6300", US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_CAPACITY ), -/* Patch for Nokia 5310 capacity */ -UNUSUAL_DEV( 0x0421, 0x006a, 0x0000, 0x0591, - "Nokia", - "5310", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_CAPACITY ), - /* Reported by Olaf Hering <olh@suse.de> from novell bug #105878 */ UNUSUAL_DEV( 0x0424, 0x0fdc, 0x0210, 0x0210, "SMSC", @@ -303,6 +318,18 @@ UNUSUAL_DEV( 0x045a, 0x5210, 0x0101, 0x0101, US_SC_SCSI, US_PR_KARMA, rio_karma_init, 0), #endif +/* Reported by Tamas Kerecsen <kerecsen@bigfoot.com> + * Obviously the PROM has not been customized by the VAR; + * the Vendor and Product string descriptors are: + * Generic Mass Storage (PROTOTYPE--Remember to change idVendor) + * Generic Manufacturer (PROTOTYPE--Remember to change idVendor) + */ +UNUSUAL_DEV( 0x045e, 0xffff, 0x0000, 0x0000, + "Mitac", + "GPS", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_MAX_SECTORS_64 ), + /* * This virtual floppy is found in Sun equipment (x4600, x4200m2, etc.) * Reported by Pete Zaitcev <zaitcev@redhat.com> @@ -347,6 +374,15 @@ UNUSUAL_DEV( 0x04a4, 0x0004, 0x0001, 0x0001, "DVD-CAM DZ-MV100A Camcorder", US_SC_SCSI, US_PR_CB, NULL, US_FL_SINGLE_LUN), +/* BENQ DC5330 + * Reported by Manuel Fombuena <mfombuena@ya.com> and + * Frank Copeland <fjc@thingy.apana.org.au> */ +UNUSUAL_DEV( 0x04a5, 0x3010, 0x0100, 0x0100, + "Tekom Technologies, Inc", + "300_CAMERA", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + /* Patch for Nikon coolpix 2000 * Submitted by Fabien Cosse <fabien.cosse@wanadoo.fr>*/ UNUSUAL_DEV( 0x04b0, 0x0301, 0x0010, 0x0010, @@ -362,6 +398,13 @@ UNUSUAL_DEV( 0x04b0, 0x0401, 0x0200, 0x0200, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_CAPACITY), +/* Reported by Tobias Kunze Briseno <t-linux@fictive.com> */ +UNUSUAL_DEV( 0x04b0, 0x0403, 0x0200, 0x0200, + "NIKON", + "NIKON DSC D2H", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY), + /* Reported by Milinevsky Dmitry <niam.niam@gmail.com> */ UNUSUAL_DEV( 0x04b0, 0x0409, 0x0100, 0x0100, "NIKON", @@ -418,6 +461,13 @@ UNUSUAL_DEV( 0x04b0, 0x0417, 0x0100, 0x0100, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_CAPACITY), +/* Reported by paul ready <lxtwin@homecall.co.uk> */ +UNUSUAL_DEV( 0x04b0, 0x0419, 0x0100, 0x0200, + "NIKON", + "NIKON DSC D300", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY), + /* Reported by Doug Maxey (dwm@austin.ibm.com) */ UNUSUAL_DEV( 0x04b3, 0x4001, 0x0110, 0x0110, "IBM", @@ -425,15 +475,6 @@ UNUSUAL_DEV( 0x04b3, 0x4001, 0x0110, 0x0110, US_SC_DEVICE, US_PR_CB, NULL, US_FL_MAX_SECTORS_MIN), -/* BENQ DC5330 - * Reported by Manuel Fombuena <mfombuena@ya.com> and - * Frank Copeland <fjc@thingy.apana.org.au> */ -UNUSUAL_DEV( 0x04a5, 0x3010, 0x0100, 0x0100, - "Tekom Technologies, Inc", - "300_CAMERA", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_IGNORE_RESIDUE ), - #ifdef CONFIG_USB_STORAGE_CYPRESS_ATACB /* CY7C68300 : support atacb */ UNUSUAL_DEV( 0x04b4, 0x6830, 0x0000, 0x9999, @@ -752,15 +793,15 @@ UNUSUAL_DEV( 0x054c, 0x006d, 0x0000, 0x9999, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY ), -/* Submitted by Mike Alborn <malborn@deandra.homeip.net> */ -UNUSUAL_DEV( 0x054c, 0x016a, 0x0000, 0x9999, +/* Submitted by Frank Engel <frankie@cse.unsw.edu.au> */ +UNUSUAL_DEV( 0x054c, 0x0099, 0x0000, 0x9999, "Sony", "PEG Mass Storage", US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY ), - -/* Submitted by Frank Engel <frankie@cse.unsw.edu.au> */ -UNUSUAL_DEV( 0x054c, 0x0099, 0x0000, 0x9999, + +/* Submitted by Mike Alborn <malborn@deandra.homeip.net> */ +UNUSUAL_DEV( 0x054c, 0x016a, 0x0000, 0x9999, "Sony", "PEG Mass Storage", US_SC_DEVICE, US_PR_DEVICE, NULL, @@ -949,6 +990,13 @@ UNUSUAL_DEV( 0x05e3, 0x0702, 0x0000, 0xffff, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_GO_SLOW | US_FL_MAX_SECTORS_64 ), +/* Reported by Ben Efros <ben@pc-doctor.com> */ +UNUSUAL_DEV( 0x05e3, 0x0723, 0x9451, 0x9451, + "Genesys Logic", + "USB to SATA", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_SANE_SENSE ), + /* Reported by Hanno Boeck <hanno@gmx.de> * Taken from the Lycoris Kernel */ UNUSUAL_DEV( 0x0636, 0x0003, 0x0000, 0x9999, @@ -1258,6 +1306,13 @@ UNUSUAL_DEV( 0x0839, 0x000a, 0x0001, 0x0001, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY), +/* Reported by Luciano Rocha <luciano@eurotux.com> */ +UNUSUAL_DEV( 0x0840, 0x0082, 0x0001, 0x0001, + "Argosy", + "Storage", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY), + /* Entry and supporting patch by Theodore Kilgore <kilgota@auburn.edu>. * Flag will support Bulk devices which use a standards-violating 32-byte * Command Block Wrapper. Here, the "DC2MEGA" cameras (several brands) with @@ -1281,17 +1336,6 @@ UNUSUAL_DEV( 0x0851, 0x1543, 0x0200, 0x0200, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_NOT_LOCKABLE), -/* Andrew Lunn <andrew@lunn.ch> - * PanDigital Digital Picture Frame. Does not like ALLOW_MEDIUM_REMOVAL - * on LUN 4. - * Note: Vend:Prod clash with "Ltd Maxell WS30 Slim Digital Camera" -*/ -UNUSUAL_DEV( 0x0851, 0x1543, 0x0200, 0x0200, - "PanDigital", - "Photo Frame", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_NOT_LOCKABLE), - /* Submitted by Jan De Luyck <lkml@kcore.org> */ UNUSUAL_DEV( 0x08bd, 0x1100, 0x0000, 0x0000, "CITIZEN", @@ -1387,6 +1431,13 @@ UNUSUAL_DEV( 0x0af0, 0x7401, 0x0000, 0x0000, US_SC_DEVICE, US_PR_DEVICE, NULL, 0 ), +/* Reported by Ben Efros <ben@pc-doctor.com> */ +UNUSUAL_DEV( 0x0bc2, 0x3010, 0x0000, 0x0000, + "Seagate", + "FreeAgent Pro", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_SANE_SENSE ), + #ifdef CONFIG_USB_STORAGE_ISD200 UNUSUAL_DEV( 0x0bf6, 0xa001, 0x0100, 0x0110, "ATI", @@ -1410,6 +1461,22 @@ UNUSUAL_DEV( 0x0c0b, 0xa109, 0x0000, 0xffff, US_FL_SINGLE_LUN ), #endif +UNUSUAL_DEV( 0x0d49, 0x7310, 0x0000, 0x9999, + "Maxtor", + "USB to SATA", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_SANE_SENSE), + +/* + * Pete Zaitcev <zaitcev@yahoo.com>, bz#164688. + * The device blatantly ignores LUN and returns 1 in GetMaxLUN. + */ +UNUSUAL_DEV( 0x0c45, 0x1060, 0x0100, 0x0100, + "Unknown", + "Unknown", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_SINGLE_LUN ), + /* Submitted by: Nick Sillik <n.sillik@temple.edu> * Needed for OneTouch extension to usb-storage * @@ -1427,16 +1494,6 @@ UNUSUAL_DEV( 0x0c0b, 0xa109, 0x0000, 0xffff, 0), #endif -/* - * Pete Zaitcev <zaitcev@yahoo.com>, bz#164688. - * The device blatantly ignores LUN and returns 1 in GetMaxLUN. - */ -UNUSUAL_DEV( 0x0c45, 0x1060, 0x0100, 0x0100, - "Unknown", - "Unknown", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_SINGLE_LUN ), - /* Submitted by Joris Struyve <joris@struyve.be> */ UNUSUAL_DEV( 0x0d96, 0x410a, 0x0001, 0xffff, "Medion", @@ -1576,13 +1633,6 @@ UNUSUAL_DEV( 0x0fce, 0xe030, 0x0000, 0x0000, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_CAPACITY | US_FL_IGNORE_RESIDUE ), -/* Reported by Ricardo Barberis <ricardo@dattatec.com> */ -UNUSUAL_DEV( 0x0fce, 0xe092, 0x0000, 0x0000, - "Sony Ericsson", - "P1i", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_IGNORE_RESIDUE ), - /* Reported by Emmanuel Vasilakis <evas@forthnet.gr> */ UNUSUAL_DEV( 0x0fce, 0xe031, 0x0000, 0x0000, "Sony Ericsson", @@ -1590,6 +1640,13 @@ UNUSUAL_DEV( 0x0fce, 0xe031, 0x0000, 0x0000, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_IGNORE_RESIDUE | US_FL_FIX_CAPACITY ), +/* Reported by Ricardo Barberis <ricardo@dattatec.com> */ +UNUSUAL_DEV( 0x0fce, 0xe092, 0x0000, 0x0000, + "Sony Ericsson", + "P1i", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + /* Reported by Kevin Cernekee <kpc-usbdev@gelato.uiuc.edu> * Tested on hardware version 1.10. * Entry is needed only for the initializer function override. @@ -1602,6 +1659,12 @@ UNUSUAL_DEV( 0x1019, 0x0c55, 0x0000, 0x0110, US_SC_DEVICE, US_PR_DEVICE, usb_stor_ucr61s2b_init, 0 ), +UNUSUAL_DEV( 0x1058, 0x0704, 0x0000, 0x9999, + "Western Digital", + "External HDD", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_SANE_SENSE), + /* Reported by Fabio Venturi <f.venturi@tdnet.it> * The device reports a vendor-specific bDeviceClass. */ @@ -1991,10 +2054,10 @@ UNUSUAL_DEV( 0x14cd, 0x6600, 0x0201, 0x0201, * JMicron responds to USN and several other SCSI ioctls with a * residue that causes subsequent I/O requests to fail. */ UNUSUAL_DEV( 0x152d, 0x2329, 0x0100, 0x0100, - "JMicron", - "USB to ATA/ATAPI Bridge", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_IGNORE_RESIDUE ), + "JMicron", + "USB to ATA/ATAPI Bridge", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE | US_FL_SANE_SENSE ), /* Reported by Robert Schedel <r.schedel@yahoo.de> * Note: this is a 'super top' device like the above 14cd/6600 device */ diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 27016fd2cad1..06c735703f4a 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -113,6 +113,16 @@ static unsigned int delay_use = 5; module_param(delay_use, uint, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(delay_use, "seconds to delay before using a new device"); +static char *quirks; +module_param(quirks, charp, S_IRUGO); +MODULE_PARM_DESC(quirks, "supplemental list of device IDs and their quirks"); + +struct quirks_entry { + u16 vid, pid; + u32 fflags; +}; +static struct quirks_entry *quirks_list, *quirks_end; + /* * The entries in this table correspond, line for line, @@ -464,13 +474,31 @@ static int associate_dev(struct us_data *us, struct usb_interface *intf) US_DEBUGP("I/O buffer allocation failed\n"); return -ENOMEM; } + return 0; +} - us->sensebuf = kmalloc(US_SENSE_SIZE, GFP_KERNEL); - if (!us->sensebuf) { - US_DEBUGP("Sense buffer allocation failed\n"); - return -ENOMEM; +/* Adjust device flags based on the "quirks=" module parameter */ +static void adjust_quirks(struct us_data *us) +{ + u16 vid, pid; + struct quirks_entry *q; + unsigned int mask = (US_FL_FIX_CAPACITY | US_FL_IGNORE_DEVICE | + US_FL_NOT_LOCKABLE | US_FL_MAX_SECTORS_64 | + US_FL_IGNORE_RESIDUE | US_FL_SINGLE_LUN | + US_FL_NO_WP_DETECT); + + vid = le16_to_cpu(us->pusb_dev->descriptor.idVendor); + pid = le16_to_cpu(us->pusb_dev->descriptor.idProduct); + + for (q = quirks_list; q != quirks_end; ++q) { + if (q->vid == vid && q->pid == pid) { + us->fflags = (us->fflags & ~mask) | q->fflags; + dev_info(&us->pusb_intf->dev, "Quirks match for " + "vid %04x pid %04x: %x\n", + vid, pid, q->fflags); + break; + } } - return 0; } /* Find an unusual_dev descriptor (always succeeds in the current code) */ @@ -497,6 +525,7 @@ static int get_device_info(struct us_data *us, const struct usb_device_id *id) idesc->bInterfaceProtocol : unusual_dev->useTransport; us->fflags = USB_US_ORIG_FLAGS(id->driver_info); + adjust_quirks(us); if (us->fflags & US_FL_IGNORE_DEVICE) { printk(KERN_INFO USB_STORAGE "device ignored\n"); @@ -562,7 +591,7 @@ static int get_transport(struct us_data *us) case US_PR_CBI: us->transport_name = "Control/Bulk/Interrupt"; - us->transport = usb_stor_CBI_transport; + us->transport = usb_stor_CB_transport; us->transport_reset = usb_stor_CB_reset; us->max_lun = 7; break; @@ -675,19 +704,19 @@ static int get_protocol(struct us_data *us) case US_SC_8020: us->protocol_name = "8020i"; - us->proto_handler = usb_stor_ATAPI_command; + us->proto_handler = usb_stor_pad12_command; us->max_lun = 0; break; case US_SC_QIC: us->protocol_name = "QIC-157"; - us->proto_handler = usb_stor_qic157_command; + us->proto_handler = usb_stor_pad12_command; us->max_lun = 0; break; case US_SC_8070: us->protocol_name = "8070i"; - us->proto_handler = usb_stor_ATAPI_command; + us->proto_handler = usb_stor_pad12_command; us->max_lun = 0; break; @@ -840,8 +869,6 @@ static void dissociate_dev(struct us_data *us) { US_DEBUGP("-- %s\n", __func__); - kfree(us->sensebuf); - /* Free the device-related DMA-mapped buffers */ if (us->cr) usb_buffer_free(us->pusb_dev, sizeof(*us->cr), us->cr, @@ -1061,10 +1088,88 @@ static struct usb_driver usb_storage_driver = { .soft_unbind = 1, }; +/* Works only for digits and letters, but small and fast */ +#define TOLOWER(x) ((x) | 0x20) + +static void __init parse_quirks(void) +{ + int n, i; + char *p; + + if (!quirks) + return; + + /* Count the ':' characters to get 2 * the number of entries */ + n = 0; + for (p = quirks; *p; ++p) { + if (*p == ':') + ++n; + } + n /= 2; + if (n == 0) + return; /* Don't allocate 0 bytes */ + + quirks_list = kmalloc(n * sizeof(*quirks_list), GFP_KERNEL); + if (!quirks_list) + return; + + p = quirks; + quirks_end = quirks_list; + for (i = 0; i < n && *p; ++i) { + unsigned f = 0; + + /* Each entry consists of VID:PID:flags */ + quirks_end->vid = simple_strtoul(p, &p, 16); + if (*p != ':') + goto skip_to_next; + quirks_end->pid = simple_strtoul(p+1, &p, 16); + if (*p != ':') + goto skip_to_next; + + while (*++p && *p != ',') { + switch (TOLOWER(*p)) { + case 'c': + f |= US_FL_FIX_CAPACITY; + break; + case 'i': + f |= US_FL_IGNORE_DEVICE; + break; + case 'l': + f |= US_FL_NOT_LOCKABLE; + break; + case 'm': + f |= US_FL_MAX_SECTORS_64; + break; + case 'r': + f |= US_FL_IGNORE_RESIDUE; + break; + case 's': + f |= US_FL_SINGLE_LUN; + break; + case 'w': + f |= US_FL_NO_WP_DETECT; + break; + /* Ignore unrecognized flag characters */ + } + } + quirks_end->fflags = f; + ++quirks_end; + + skip_to_next: + /* Entries are separated by commas */ + while (*p) { + if (*p++ == ',') + break; + } + } /* for (i = 0; ...) */ +} + static int __init usb_stor_init(void) { int retval; + printk(KERN_INFO "Initializing USB Mass Storage driver...\n"); + parse_quirks(); /* register the driver, return usb_register return code if error */ retval = usb_register(&usb_storage_driver); diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h index a4ad73bd832d..e4674fc715e6 100644 --- a/drivers/usb/storage/usb.h +++ b/drivers/usb/storage/usb.h @@ -138,7 +138,6 @@ struct us_data { struct usb_ctrlrequest *cr; /* control requests */ struct usb_sg_request current_sg; /* scatter-gather req. */ unsigned char *iobuf; /* I/O buffer */ - unsigned char *sensebuf; /* sense data buffer */ dma_addr_t cr_dma; /* buffer DMA addresses */ dma_addr_t iobuf_dma; struct task_struct *ctl_thread; /* the control thread */ diff --git a/drivers/usb/wusbcore/rh.c b/drivers/usb/wusbcore/rh.c index 267a64325106..b1bb82db4abc 100644 --- a/drivers/usb/wusbcore/rh.c +++ b/drivers/usb/wusbcore/rh.c @@ -352,7 +352,7 @@ static int wusbhc_rh_get_port_status(struct wusbhc *wusbhc, u16 port_idx, u32 *_buf, u16 wLength) { int result = -EINVAL; - u16 *buf = (u16 *) _buf; + __le16 *buf = (__le16 *)_buf; d_fnstart(1, wusbhc->dev, "(wusbhc %p port_idx %u wLength %u)\n", wusbhc, port_idx, wLength); diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 0f13448c6f79..5611a4f1559b 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -362,7 +362,7 @@ endchoice config FB_ACORN bool "Acorn VIDC support" - depends on (FB = y) && ARM && (ARCH_ACORN || ARCH_CLPS7500) + depends on (FB = y) && ARM && ARCH_ACORN select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT @@ -2021,17 +2021,19 @@ config FB_COBALT depends on FB && MIPS_COBALT config FB_SH7760 - bool "SH7760/SH7763 LCDC support" - depends on FB && (CPU_SUBTYPE_SH7760 || CPU_SUBTYPE_SH7763) - select FB_CFB_FILLRECT - select FB_CFB_COPYAREA - select FB_CFB_IMAGEBLIT - help - Support for the SH7760/SH7763 integrated (D)STN/TFT LCD Controller. - Supports display resolutions up to 1024x1024 pixel, grayscale and - color operation, with depths ranging from 1 bpp to 8 bpp monochrome - and 8, 15 or 16 bpp color; 90 degrees clockwise display rotation for - panels <= 320 pixel horizontal resolution. + bool "SH7760/SH7763/SH7720/SH7721 LCDC support" + depends on FB && (CPU_SUBTYPE_SH7760 || CPU_SUBTYPE_SH7763 \ + || CPU_SUBTYPE_SH7720 || CPU_SUBTYPE_SH7721) + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + ---help--- + Support for the SH7760/SH7763/SH7720/SH7721 integrated + (D)STN/TFT LCD Controller. + Supports display resolutions up to 1024x1024 pixel, grayscale and + color operation, with depths ranging from 1 bpp to 8 bpp monochrome + and 8, 15 or 16 bpp color; 90 degrees clockwise display rotation for + panels <= 320 pixel horizontal resolution. config FB_VIRTUAL tristate "Virtual Frame Buffer support (ONLY FOR TESTING!)" @@ -2083,6 +2085,38 @@ config FB_METRONOME controller. The pre-release name for this device was 8track and could also have been called by some vendors as PVI-nnnn. +config FB_MB862XX + tristate "Fujitsu MB862xx GDC support" + depends on FB + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + ---help--- + Frame buffer driver for Fujitsu Carmine/Coral-P(A)/Lime controllers. + +config FB_MB862XX_PCI_GDC + bool "Carmine/Coral-P(A) GDC" + depends on PCI && FB_MB862XX + ---help--- + This enables framebuffer support for Fujitsu Carmine/Coral-P(A) + PCI graphics controller devices. + +config FB_MB862XX_LIME + bool "Lime GDC" + depends on FB_MB862XX + depends on OF && !FB_MB862XX_PCI_GDC + select FB_FOREIGN_ENDIAN + select FB_LITTLE_ENDIAN + ---help--- + Framebuffer support for Fujitsu Lime GDC on host CPU bus. + +config FB_PRE_INIT_FB + bool "Don't reinitialize, use bootloader's GDC/Display configuration" + depends on FB_MB862XX_LIME + ---help--- + Select this option if display contents should be inherited as set by + the bootloader. + source "drivers/video/omap/Kconfig" source "drivers/video/backlight/Kconfig" diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 248bddc8d0b0..e39e33e797da 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -122,6 +122,7 @@ obj-$(CONFIG_FB_SH_MOBILE_LCDC) += sh_mobile_lcdcfb.o obj-$(CONFIG_FB_OMAP) += omap/ obj-$(CONFIG_XEN_FBDEV_FRONTEND) += xen-fbfront.o obj-$(CONFIG_FB_CARMINE) += carminefb.o +obj-$(CONFIG_FB_MB862XX) += mb862xx/ # Platform or fallback drivers go here obj-$(CONFIG_FB_UVESA) += uvesafb.o diff --git a/drivers/video/amba-clcd.c b/drivers/video/amba-clcd.c index a7a1c891bfa2..2ac52fd8cc11 100644 --- a/drivers/video/amba-clcd.c +++ b/drivers/video/amba-clcd.c @@ -343,14 +343,14 @@ static int clcdfb_register(struct clcd_fb *fb) { int ret; - fb->clk = clk_get(&fb->dev->dev, "CLCDCLK"); + fb->clk = clk_get(&fb->dev->dev, NULL); if (IS_ERR(fb->clk)) { ret = PTR_ERR(fb->clk); goto out; } fb->fb.fix.mmio_start = fb->dev->res.start; - fb->fb.fix.mmio_len = SZ_4K; + fb->fb.fix.mmio_len = 4096; fb->regs = ioremap(fb->fb.fix.mmio_start, fb->fb.fix.mmio_len); if (!fb->regs) { diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c index f8d0a57a07cb..9a577a800db5 100644 --- a/drivers/video/atmel_lcdfb.c +++ b/drivers/video/atmel_lcdfb.c @@ -132,7 +132,7 @@ static void init_backlight(struct atmel_lcdfb_info *sinfo) bl = backlight_device_register("backlight", &sinfo->pdev->dev, sinfo, &atmel_lcdc_bl_ops); - if (IS_ERR(sinfo->backlight)) { + if (IS_ERR(bl)) { dev_err(&sinfo->pdev->dev, "error %ld on backlight register\n", PTR_ERR(bl)); return; diff --git a/drivers/video/backlight/backlight.c b/drivers/video/backlight/backlight.c index fab0bc874b58..0664fc032235 100644 --- a/drivers/video/backlight/backlight.c +++ b/drivers/video/backlight/backlight.c @@ -217,7 +217,7 @@ struct backlight_device *backlight_device_register(const char *name, new_bd->dev.class = backlight_class; new_bd->dev.parent = parent; new_bd->dev.release = bl_device_release; - strlcpy(new_bd->dev.bus_id, name, BUS_ID_SIZE); + dev_set_name(&new_bd->dev, name); dev_set_drvdata(&new_bd->dev, devdata); rc = device_register(&new_bd->dev); diff --git a/drivers/video/backlight/da903x.c b/drivers/video/backlight/da903x.c index 242c38250166..93bb4340cc64 100644 --- a/drivers/video/backlight/da903x.c +++ b/drivers/video/backlight/da903x.c @@ -119,6 +119,7 @@ static int da903x_backlight_probe(struct platform_device *pdev) default: dev_err(&pdev->dev, "invalid backlight device ID(%d)\n", pdev->id); + kfree(data); return -EINVAL; } @@ -130,6 +131,7 @@ static int da903x_backlight_probe(struct platform_device *pdev) data, &da903x_backlight_ops); if (IS_ERR(bl)) { dev_err(&pdev->dev, "failed to register backlight\n"); + kfree(data); return PTR_ERR(bl); } diff --git a/drivers/video/backlight/lcd.c b/drivers/video/backlight/lcd.c index 8e1731d3b228..b6449470106c 100644 --- a/drivers/video/backlight/lcd.c +++ b/drivers/video/backlight/lcd.c @@ -42,10 +42,13 @@ static int fb_notifier_callback(struct notifier_block *self, mutex_lock(&ld->ops_lock); if (!ld->ops->check_fb || ld->ops->check_fb(ld, evdata->info)) { - if (event == FB_EVENT_BLANK) - ld->ops->set_power(ld, *(int *)evdata->data); - else - ld->ops->set_mode(ld, evdata->data); + if (event == FB_EVENT_BLANK) { + if (ld->ops->set_power) + ld->ops->set_power(ld, *(int *)evdata->data); + } else { + if (ld->ops->set_mode) + ld->ops->set_mode(ld, evdata->data); + } } mutex_unlock(&ld->ops_lock); return 0; @@ -205,7 +208,7 @@ struct lcd_device *lcd_device_register(const char *name, struct device *parent, new_ld->dev.class = lcd_class; new_ld->dev.parent = parent; new_ld->dev.release = lcd_device_release; - strlcpy(new_ld->dev.bus_id, name, BUS_ID_SIZE); + dev_set_name(&new_ld->dev, name); dev_set_drvdata(&new_ld->dev, devdata); rc = device_register(&new_ld->dev); diff --git a/drivers/video/cirrusfb.c b/drivers/video/cirrusfb.c index 8a8760230bc7..a2aa6ddffbe2 100644 --- a/drivers/video/cirrusfb.c +++ b/drivers/video/cirrusfb.c @@ -2462,8 +2462,7 @@ static int __init cirrusfb_init(void) #ifndef MODULE static int __init cirrusfb_setup(char *options) { - char *this_opt, s[32]; - int i; + char *this_opt; DPRINTK("ENTER\n"); diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index b92947d62ad6..c136054c8e8a 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -81,9 +81,6 @@ #ifdef CONFIG_ATARI #include <asm/atariints.h> #endif -#ifdef CONFIG_MAC -#include <asm/macints.h> -#endif #if defined(__mc68000__) #include <asm/machdep.h> #include <asm/setup.h> @@ -160,8 +157,6 @@ static int fbcon_set_origin(struct vc_data *); /* # VBL ints between cursor state changes */ #define ATARI_CURSOR_BLINK_RATE (42) -#define MAC_CURSOR_BLINK_RATE (32) -#define DEFAULT_CURSOR_BLINK_RATE (20) static int vbl_cursor_cnt; static int fbcon_cursor_noblink; @@ -210,19 +205,6 @@ static void fbcon_start(void); static void fbcon_exit(void); static struct device *fbcon_device; -#ifdef CONFIG_MAC -/* - * On the Macintoy, there may or may not be a working VBL int. We need to probe - */ -static int vbl_detected; - -static irqreturn_t fb_vbl_detect(int irq, void *dummy) -{ - vbl_detected++; - return IRQ_HANDLED; -} -#endif - #ifdef CONFIG_FRAMEBUFFER_CONSOLE_ROTATION static inline void fbcon_set_rotation(struct fb_info *info) { @@ -421,7 +403,7 @@ static void fb_flashcursor(struct work_struct *work) release_console_sem(); } -#if defined(CONFIG_ATARI) || defined(CONFIG_MAC) +#ifdef CONFIG_ATARI static int cursor_blink_rate; static irqreturn_t fb_vbl_handler(int irq, void *dev_id) { @@ -949,9 +931,7 @@ static const char *fbcon_startup(void) struct fb_info *info = NULL; struct fbcon_ops *ops; int rows, cols; - int irqres; - irqres = 1; /* * If num_registered_fb is zero, this is a call for the dummy part. * The frame buffer devices weren't initialized yet. @@ -1040,56 +1020,11 @@ static const char *fbcon_startup(void) #ifdef CONFIG_ATARI if (MACH_IS_ATARI) { cursor_blink_rate = ATARI_CURSOR_BLINK_RATE; - irqres = - request_irq(IRQ_AUTO_4, fb_vbl_handler, + (void)request_irq(IRQ_AUTO_4, fb_vbl_handler, IRQ_TYPE_PRIO, "framebuffer vbl", info); } -#endif /* CONFIG_ATARI */ - -#ifdef CONFIG_MAC - /* - * On a Macintoy, the VBL interrupt may or may not be active. - * As interrupt based cursor is more reliable and race free, we - * probe for VBL interrupts. - */ - if (MACH_IS_MAC) { - int ct = 0; - /* - * Probe for VBL: set temp. handler ... - */ - irqres = request_irq(IRQ_MAC_VBL, fb_vbl_detect, 0, - "framebuffer vbl", info); - vbl_detected = 0; - - /* - * ... and spin for 20 ms ... - */ - while (!vbl_detected && ++ct < 1000) - udelay(20); - - if (ct == 1000) - printk - ("fbcon_startup: No VBL detected, using timer based cursor.\n"); - - free_irq(IRQ_MAC_VBL, fb_vbl_detect); - - if (vbl_detected) { - /* - * interrupt based cursor ok - */ - cursor_blink_rate = MAC_CURSOR_BLINK_RATE; - irqres = - request_irq(IRQ_MAC_VBL, fb_vbl_handler, 0, - "framebuffer vbl", info); - } else { - /* - * VBL not detected: fall through, use timer based cursor - */ - irqres = 1; - } - } -#endif /* CONFIG_MAC */ +#endif /* CONFIG_ATARI */ fbcon_add_cursor_timer(info); fbcon_has_exited = 0; @@ -3523,11 +3458,8 @@ static void fbcon_exit(void) return; #ifdef CONFIG_ATARI - free_irq(IRQ_AUTO_4, fb_vbl_handler); -#endif -#ifdef CONFIG_MAC - if (MACH_IS_MAC && vbl_detected) - free_irq(IRQ_MAC_VBL, fb_vbl_handler); + if (MACH_IS_ATARI) + free_irq(IRQ_AUTO_4, fb_vbl_handler); #endif kfree((void *)softback_buf); diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index 6048b55f2878..3c65b0d67617 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -230,7 +230,7 @@ static void fb_set_logo_directpalette(struct fb_info *info, greenshift = info->var.green.offset; blueshift = info->var.blue.offset; - for (i = 32; i < logo->clutsize; i++) + for (i = 32; i < 32 + logo->clutsize; i++) palette[i] = i << redshift | i << greenshift | i << blueshift; } @@ -1002,13 +1002,9 @@ fb_blank(struct fb_info *info, int blank) return ret; } -static long -fb_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) +static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, + unsigned long arg) { - struct inode *inode = file->f_path.dentry->d_inode; - int fbidx = iminor(inode); - struct fb_info *info; struct fb_ops *fb; struct fb_var_screeninfo var; struct fb_fix_screeninfo fix; @@ -1018,14 +1014,10 @@ fb_ioctl(struct file *file, unsigned int cmd, void __user *argp = (void __user *)arg; long ret = 0; - info = registered_fb[fbidx]; - mutex_lock(&info->lock); fb = info->fbops; - - if (!fb) { - mutex_unlock(&info->lock); + if (!fb) return -ENODEV; - } + switch (cmd) { case FBIOGET_VSCREENINFO: ret = copy_to_user(argp, &info->var, @@ -1126,6 +1118,21 @@ fb_ioctl(struct file *file, unsigned int cmd, else ret = fb->fb_ioctl(info, cmd, arg); } + return ret; +} + +static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +__acquires(&info->lock) +__releases(&info->lock) +{ + struct inode *inode = file->f_path.dentry->d_inode; + int fbidx = iminor(inode); + struct fb_info *info; + long ret; + + info = registered_fb[fbidx]; + mutex_lock(&info->lock); + ret = do_fb_ioctl(info, cmd, arg); mutex_unlock(&info->lock); return ret; } @@ -1157,8 +1164,8 @@ struct fb_cmap32 { compat_caddr_t transp; }; -static int fb_getput_cmap(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static int fb_getput_cmap(struct fb_info *info, unsigned int cmd, + unsigned long arg) { struct fb_cmap_user __user *cmap; struct fb_cmap32 __user *cmap32; @@ -1181,7 +1188,7 @@ static int fb_getput_cmap(struct inode *inode, struct file *file, put_user(compat_ptr(data), &cmap->transp)) return -EFAULT; - err = fb_ioctl(file, cmd, (unsigned long) cmap); + err = do_fb_ioctl(info, cmd, (unsigned long) cmap); if (!err) { if (copy_in_user(&cmap32->start, @@ -1223,8 +1230,8 @@ static int do_fscreeninfo_to_user(struct fb_fix_screeninfo *fix, return err; } -static int fb_get_fscreeninfo(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static int fb_get_fscreeninfo(struct fb_info *info, unsigned int cmd, + unsigned long arg) { mm_segment_t old_fs; struct fb_fix_screeninfo fix; @@ -1235,7 +1242,7 @@ static int fb_get_fscreeninfo(struct inode *inode, struct file *file, old_fs = get_fs(); set_fs(KERNEL_DS); - err = fb_ioctl(file, cmd, (unsigned long) &fix); + err = do_fb_ioctl(info, cmd, (unsigned long) &fix); set_fs(old_fs); if (!err) @@ -1244,8 +1251,10 @@ static int fb_get_fscreeninfo(struct inode *inode, struct file *file, return err; } -static long -fb_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +static long fb_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +__acquires(&info->lock) +__releases(&info->lock) { struct inode *inode = file->f_path.dentry->d_inode; int fbidx = iminor(inode); @@ -1262,16 +1271,16 @@ fb_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case FBIOPUT_CON2FBMAP: arg = (unsigned long) compat_ptr(arg); case FBIOBLANK: - mutex_unlock(&info->lock); - return fb_ioctl(file, cmd, arg); + ret = do_fb_ioctl(info, cmd, arg); + break; case FBIOGET_FSCREENINFO: - ret = fb_get_fscreeninfo(inode, file, cmd, arg); + ret = fb_get_fscreeninfo(info, cmd, arg); break; case FBIOGETCMAP: case FBIOPUTCMAP: - ret = fb_getput_cmap(inode, file, cmd, arg); + ret = fb_getput_cmap(info, cmd, arg); break; default: @@ -1286,6 +1295,8 @@ fb_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) static int fb_mmap(struct file *file, struct vm_area_struct * vma) +__acquires(&info->lock) +__releases(&info->lock) { int fbidx = iminor(file->f_path.dentry->d_inode); struct fb_info *info = registered_fb[fbidx]; @@ -1339,6 +1350,8 @@ fb_mmap(struct file *file, struct vm_area_struct * vma) static int fb_open(struct inode *inode, struct file *file) +__acquires(&info->lock) +__releases(&info->lock) { int fbidx = iminor(inode); struct fb_info *info; @@ -1374,6 +1387,8 @@ out: static int fb_release(struct inode *inode, struct file *file) +__acquires(&info->lock) +__releases(&info->lock) { struct fb_info * const info = file->private_data; diff --git a/drivers/video/macfb.c b/drivers/video/macfb.c index b790ddff76f9..d66887e8cbb1 100644 --- a/drivers/video/macfb.c +++ b/drivers/video/macfb.c @@ -36,7 +36,6 @@ #include <asm/irq.h> #include <asm/macintosh.h> #include <asm/io.h> -#include <asm/machw.h> /* Common DAC base address for the LC, RBV, Valkyrie, and IIvx */ #define DAC_BASE 0x50f24000 @@ -78,34 +77,34 @@ static int csc_setpalette (unsigned int regno, unsigned int red, unsigned int green, unsigned int blue, struct fb_info *fb_info); -static volatile struct { +static struct { unsigned char addr; /* Note: word-aligned */ char pad[3]; unsigned char lut; -} *valkyrie_cmap_regs; +} __iomem *valkyrie_cmap_regs; -static volatile struct { +static struct { unsigned char addr; unsigned char lut; -} *v8_brazil_cmap_regs; +} __iomem *v8_brazil_cmap_regs; -static volatile struct { +static struct { unsigned char addr; char pad1[3]; /* word aligned */ unsigned char lut; char pad2[3]; /* word aligned */ unsigned char cntl; /* a guess as to purpose */ -} *rbv_cmap_regs; +} __iomem *rbv_cmap_regs; -static volatile struct { +static struct { unsigned long reset; unsigned long pad1[3]; unsigned char pad2[3]; unsigned char lut; -} *dafb_cmap_regs; +} __iomem *dafb_cmap_regs; -static volatile struct { +static struct { unsigned char addr; /* OFFSET: 0x00 */ unsigned char pad1[15]; unsigned char lut; /* OFFSET: 0x10 */ @@ -114,16 +113,16 @@ static volatile struct { unsigned char pad3[7]; unsigned long vbl_addr; /* OFFSET: 0x28 */ unsigned int status2; /* OFFSET: 0x2C */ -} *civic_cmap_regs; +} __iomem *civic_cmap_regs; -static volatile struct { +static struct { char pad1[0x40]; unsigned char clut_waddr; /* 0x40 */ char pad2; unsigned char clut_data; /* 0x42 */ char pad3[0x3]; unsigned char clut_raddr; /* 0x46 */ -} *csc_cmap_regs; +} __iomem *csc_cmap_regs; /* We will leave these the way they are for the time being */ struct mdc_cmap_regs { @@ -164,7 +163,6 @@ static struct fb_var_screeninfo macfb_defined = { }; static struct fb_fix_screeninfo macfb_fix = { - .id = "Macintosh ", .type = FB_TYPE_PACKED_PIXELS, .accel = FB_ACCEL_NONE, }; @@ -508,10 +506,10 @@ static int csc_setpalette (unsigned int regno, unsigned int red, struct fb_info *info) { mdelay(1); - csc_cmap_regs->clut_waddr = regno; - csc_cmap_regs->clut_data = red; - csc_cmap_regs->clut_data = green; - csc_cmap_regs->clut_data = blue; + nubus_writeb(regno, &csc_cmap_regs->clut_waddr); + nubus_writeb(red, &csc_cmap_regs->clut_data); + nubus_writeb(green, &csc_cmap_regs->clut_data); + nubus_writeb(blue, &csc_cmap_regs->clut_data); return 0; } @@ -760,22 +758,22 @@ static int __init macfb_init(void) switch(ndev->dr_hw) { case NUBUS_DRHW_APPLE_MDC: - strcat( macfb_fix.id, "Display Card" ); + strcpy(macfb_fix.id, "Mac Disp. Card"); macfb_setpalette = mdc_setpalette; macfb_defined.activate = FB_ACTIVATE_NOW; break; case NUBUS_DRHW_APPLE_TFB: - strcat( macfb_fix.id, "Toby" ); + strcpy(macfb_fix.id, "Toby"); macfb_setpalette = toby_setpalette; macfb_defined.activate = FB_ACTIVATE_NOW; break; case NUBUS_DRHW_APPLE_JET: - strcat( macfb_fix.id, "Jet"); + strcpy(macfb_fix.id, "Jet"); macfb_setpalette = jet_setpalette; macfb_defined.activate = FB_ACTIVATE_NOW; break; default: - strcat( macfb_fix.id, "Generic NuBus" ); + strcpy(macfb_fix.id, "Generic NuBus"); break; } } @@ -786,21 +784,11 @@ static int __init macfb_init(void) if (!video_is_nubus) switch( mac_bi_data.id ) { - /* These don't have onboard video. Eventually, we may - be able to write separate framebuffer drivers for - them (tobyfb.c, hiresfb.c, etc, etc) */ - case MAC_MODEL_II: - case MAC_MODEL_IIX: - case MAC_MODEL_IICX: - case MAC_MODEL_IIFX: - strcat( macfb_fix.id, "Generic NuBus" ); - break; - /* Valkyrie Quadras */ case MAC_MODEL_Q630: /* I'm not sure about this one */ case MAC_MODEL_P588: - strcat( macfb_fix.id, "Valkyrie built-in" ); + strcpy(macfb_fix.id, "Valkyrie"); macfb_setpalette = valkyrie_setpalette; macfb_defined.activate = FB_ACTIVATE_NOW; valkyrie_cmap_regs = ioremap(DAC_BASE, 0x1000); @@ -823,7 +811,7 @@ static int __init macfb_init(void) case MAC_MODEL_Q700: case MAC_MODEL_Q900: case MAC_MODEL_Q950: - strcat( macfb_fix.id, "DAFB built-in" ); + strcpy(macfb_fix.id, "DAFB"); macfb_setpalette = dafb_setpalette; macfb_defined.activate = FB_ACTIVATE_NOW; dafb_cmap_regs = ioremap(DAFB_BASE, 0x1000); @@ -831,7 +819,7 @@ static int __init macfb_init(void) /* LC II uses the V8 framebuffer */ case MAC_MODEL_LCII: - strcat( macfb_fix.id, "V8 built-in" ); + strcpy(macfb_fix.id, "V8"); macfb_setpalette = v8_brazil_setpalette; macfb_defined.activate = FB_ACTIVATE_NOW; v8_brazil_cmap_regs = ioremap(DAC_BASE, 0x1000); @@ -843,7 +831,7 @@ static int __init macfb_init(void) case MAC_MODEL_IIVI: case MAC_MODEL_IIVX: case MAC_MODEL_P600: - strcat( macfb_fix.id, "Brazil built-in" ); + strcpy(macfb_fix.id, "Brazil"); macfb_setpalette = v8_brazil_setpalette; macfb_defined.activate = FB_ACTIVATE_NOW; v8_brazil_cmap_regs = ioremap(DAC_BASE, 0x1000); @@ -860,7 +848,7 @@ static int __init macfb_init(void) case MAC_MODEL_P460: macfb_setpalette = v8_brazil_setpalette; macfb_defined.activate = FB_ACTIVATE_NOW; - strcat( macfb_fix.id, "Sonora built-in" ); + strcpy(macfb_fix.id, "Sonora"); v8_brazil_cmap_regs = ioremap(DAC_BASE, 0x1000); break; @@ -871,7 +859,7 @@ static int __init macfb_init(void) case MAC_MODEL_IISI: macfb_setpalette = rbv_setpalette; macfb_defined.activate = FB_ACTIVATE_NOW; - strcat( macfb_fix.id, "RBV built-in" ); + strcpy(macfb_fix.id, "RBV"); rbv_cmap_regs = ioremap(DAC_BASE, 0x1000); break; @@ -880,7 +868,7 @@ static int __init macfb_init(void) case MAC_MODEL_C660: macfb_setpalette = civic_setpalette; macfb_defined.activate = FB_ACTIVATE_NOW; - strcat( macfb_fix.id, "Civic built-in" ); + strcpy(macfb_fix.id, "Civic"); civic_cmap_regs = ioremap(CIVIC_BASE, 0x1000); break; @@ -901,7 +889,7 @@ static int __init macfb_init(void) v8_brazil_cmap_regs = ioremap(DAC_BASE, 0x1000); } - strcat( macfb_fix.id, "LC built-in" ); + strcpy(macfb_fix.id, "LC"); break; /* We think this may be like the LC II */ case MAC_MODEL_CCL: @@ -911,18 +899,18 @@ static int __init macfb_init(void) v8_brazil_cmap_regs = ioremap(DAC_BASE, 0x1000); } - strcat( macfb_fix.id, "Color Classic built-in" ); + strcpy(macfb_fix.id, "Color Classic"); break; /* And we *do* mean "weirdos" */ case MAC_MODEL_TV: - strcat( macfb_fix.id, "Mac TV built-in" ); + strcpy(macfb_fix.id, "Mac TV"); break; /* These don't have colour, so no need to worry */ case MAC_MODEL_SE30: case MAC_MODEL_CLII: - strcat( macfb_fix.id, "Monochrome built-in" ); + strcpy(macfb_fix.id, "Monochrome"); break; /* Powerbooks are particularly difficult. Many of @@ -935,7 +923,7 @@ static int __init macfb_init(void) case MAC_MODEL_PB140: case MAC_MODEL_PB145: case MAC_MODEL_PB170: - strcat( macfb_fix.id, "DDC built-in" ); + strcpy(macfb_fix.id, "DDC"); break; /* Internal is GSC, External (if present) is ViSC */ @@ -945,13 +933,13 @@ static int __init macfb_init(void) case MAC_MODEL_PB180: case MAC_MODEL_PB210: case MAC_MODEL_PB230: - strcat( macfb_fix.id, "GSC built-in" ); + strcpy(macfb_fix.id, "GSC"); break; /* Internal is TIM, External is ViSC */ case MAC_MODEL_PB165C: case MAC_MODEL_PB180C: - strcat( macfb_fix.id, "TIM built-in" ); + strcpy(macfb_fix.id, "TIM"); break; /* Internal is CSC, External is Keystone+Ariel. */ @@ -963,12 +951,12 @@ static int __init macfb_init(void) case MAC_MODEL_PB280C: macfb_setpalette = csc_setpalette; macfb_defined.activate = FB_ACTIVATE_NOW; - strcat( macfb_fix.id, "CSC built-in" ); + strcpy(macfb_fix.id, "CSC"); csc_cmap_regs = ioremap(CSC_BASE, 0x1000); break; default: - strcat( macfb_fix.id, "Unknown/Unsupported built-in" ); + strcpy(macfb_fix.id, "Unknown"); break; } @@ -978,16 +966,23 @@ static int __init macfb_init(void) fb_info.pseudo_palette = pseudo_palette; fb_info.flags = FBINFO_DEFAULT; - fb_alloc_cmap(&fb_info.cmap, video_cmap_len, 0); + err = fb_alloc_cmap(&fb_info.cmap, video_cmap_len, 0); + if (err) + goto fail_unmap; err = register_framebuffer(&fb_info); - if (!err) - printk("fb%d: %s frame buffer device\n", - fb_info.node, fb_info.fix.id); - else { - iounmap(fb_info.screen_base); - iounmap_macfb(); - } + if (err) + goto fail_dealloc; + + printk("fb%d: %s frame buffer device\n", + fb_info.node, fb_info.fix.id); + return 0; + +fail_dealloc: + fb_dealloc_cmap(&fb_info.cmap); +fail_unmap: + iounmap(fb_info.screen_base); + iounmap_macfb(); return err; } diff --git a/drivers/video/mb862xx/Makefile b/drivers/video/mb862xx/Makefile new file mode 100644 index 000000000000..07664814bb1d --- /dev/null +++ b/drivers/video/mb862xx/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for the MB862xx framebuffer driver +# + +obj-$(CONFIG_FB_MB862XX) := mb862xxfb.o diff --git a/drivers/video/mb862xx/mb862xx_reg.h b/drivers/video/mb862xx/mb862xx_reg.h new file mode 100644 index 000000000000..2ba65e118500 --- /dev/null +++ b/drivers/video/mb862xx/mb862xx_reg.h @@ -0,0 +1,138 @@ +/* + * Fujitsu MB862xx Graphics Controller Registers/Bits + */ + +#ifndef _MB862XX_REG_H +#define _MB862XX_REG_H + +#ifdef MB862XX_MMIO_BOTTOM +#define MB862XX_MMIO_BASE 0x03fc0000 +#else +#define MB862XX_MMIO_BASE 0x01fc0000 +#endif +#define MB862XX_I2C_BASE 0x0000c000 +#define MB862XX_DISP_BASE 0x00010000 +#define MB862XX_CAP_BASE 0x00018000 +#define MB862XX_DRAW_BASE 0x00030000 +#define MB862XX_GEO_BASE 0x00038000 +#define MB862XX_PIO_BASE 0x00038000 +#define MB862XX_MMIO_SIZE 0x40000 + +/* Host interface/pio registers */ +#define GC_IST 0x00000020 +#define GC_IMASK 0x00000024 +#define GC_SRST 0x0000002c +#define GC_CCF 0x00000038 +#define GC_CID 0x000000f0 +#define GC_REVISION 0x00000084 + +#define GC_CCF_CGE_100 0x00000000 +#define GC_CCF_CGE_133 0x00040000 +#define GC_CCF_CGE_166 0x00080000 +#define GC_CCF_COT_100 0x00000000 +#define GC_CCF_COT_133 0x00010000 +#define GC_CID_CNAME_MSK 0x0000ff00 +#define GC_CID_VERSION_MSK 0x000000ff + +/* define enabled interrupts hereby */ +#define GC_INT_EN 0x00000000 + +/* Memory interface mode register */ +#define GC_MMR 0x0000fffc + +/* Display Controller registers */ +#define GC_DCM0 0x00000000 +#define GC_HTP 0x00000004 +#define GC_HDB_HDP 0x00000008 +#define GC_VSW_HSW_HSP 0x0000000c +#define GC_VTR 0x00000010 +#define GC_VDP_VSP 0x00000014 +#define GC_WY_WX 0x00000018 +#define GC_WH_WW 0x0000001c +#define GC_L0M 0x00000020 +#define GC_L0OA0 0x00000024 +#define GC_L0DA0 0x00000028 +#define GC_L0DY_L0DX 0x0000002c +#define GC_DCM1 0x00000100 +#define GC_L0EM 0x00000110 +#define GC_L0WY_L0WX 0x00000114 +#define GC_L0WH_L0WW 0x00000118 +#define GC_DCM2 0x00000104 +#define GC_DCM3 0x00000108 +#define GC_CPM_CUTC 0x000000a0 +#define GC_CUOA0 0x000000a4 +#define GC_CUY0_CUX0 0x000000a8 +#define GC_CUOA1 0x000000ac +#define GC_CUY1_CUX1 0x000000b0 +#define GC_L0PAL0 0x00000400 + +#define GC_CPM_CEN0 0x00100000 +#define GC_CPM_CEN1 0x00200000 + +#define GC_DCM01_ESY 0x00000004 +#define GC_DCM01_SC 0x00003f00 +#define GC_DCM01_RESV 0x00004000 +#define GC_DCM01_CKS 0x00008000 +#define GC_DCM01_L0E 0x00010000 +#define GC_DCM01_DEN 0x80000000 +#define GC_L0M_L0C_8 0x00000000 +#define GC_L0M_L0C_16 0x80000000 +#define GC_L0EM_L0EC_24 0x40000000 +#define GC_L0M_L0W_UNIT 64 + +#define GC_DISP_REFCLK_400 400 + +/* Carmine specific */ +#define MB86297_DRAW_BASE 0x00020000 +#define MB86297_DISP0_BASE 0x00100000 +#define MB86297_DISP1_BASE 0x00140000 +#define MB86297_WRBACK_BASE 0x00180000 +#define MB86297_CAP0_BASE 0x00200000 +#define MB86297_CAP1_BASE 0x00280000 +#define MB86297_DRAMCTRL_BASE 0x00300000 +#define MB86297_CTRL_BASE 0x00400000 +#define MB86297_I2C_BASE 0x00500000 + +#define GC_CTRL_STATUS 0x00000000 +#define GC_CTRL_INT_MASK 0x00000004 +#define GC_CTRL_CLK_ENABLE 0x0000000c +#define GC_CTRL_SOFT_RST 0x00000010 + +#define GC_CTRL_CLK_EN_DRAM 0x00000001 +#define GC_CTRL_CLK_EN_2D3D 0x00000002 +#define GC_CTRL_CLK_EN_DISP0 0x00000020 +#define GC_CTRL_CLK_EN_DISP1 0x00000040 + +#define GC_2D3D_REV 0x000004b4 +#define GC_RE_REVISION 0x24240200 + +/* define enabled interrupts hereby */ +#define GC_CARMINE_INT_EN 0x00000004 + +/* DRAM controller */ +#define GC_DCTL_MODE_ADD 0x00000000 +#define GC_DCTL_SETTIME1_EMODE 0x00000004 +#define GC_DCTL_REFRESH_SETTIME2 0x00000008 +#define GC_DCTL_RSV0_STATES 0x0000000C +#define GC_DCTL_RSV2_RSV1 0x00000010 +#define GC_DCTL_DDRIF2_DDRIF1 0x00000014 +#define GC_DCTL_IOCONT1_IOCONT0 0x00000024 + +#define GC_DCTL_STATES_MSK 0x0000000f +#define GC_DCTL_INIT_WAIT_CNT 3000 +#define GC_DCTL_INIT_WAIT_INTERVAL 1 + +/* DRAM ctrl values for Carmine PCI Eval. board */ +#define GC_EVB_DCTL_MODE_ADD 0x012105c3 +#define GC_EVB_DCTL_MODE_ADD_AFT_RST 0x002105c3 +#define GC_EVB_DCTL_SETTIME1_EMODE 0x47498000 +#define GC_EVB_DCTL_REFRESH_SETTIME2 0x00422a22 +#define GC_EVB_DCTL_RSV0_STATES 0x00200003 +#define GC_EVB_DCTL_RSV0_STATES_AFT_RST 0x00200002 +#define GC_EVB_DCTL_RSV2_RSV1 0x0000000f +#define GC_EVB_DCTL_DDRIF2_DDRIF1 0x00556646 +#define GC_EVB_DCTL_IOCONT1_IOCONT0 0x05550555 + +#define GC_DISP_REFCLK_533 533 + +#endif diff --git a/drivers/video/mb862xx/mb862xxfb.c b/drivers/video/mb862xx/mb862xxfb.c new file mode 100644 index 000000000000..38718d95fbb9 --- /dev/null +++ b/drivers/video/mb862xx/mb862xxfb.c @@ -0,0 +1,1061 @@ +/* + * drivers/mb862xx/mb862xxfb.c + * + * Fujitsu Carmine/Coral-P(A)/Lime framebuffer driver + * + * (C) 2008 Anatolij Gustschin <agust@denx.de> + * DENX Software Engineering + * + * 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. + * + */ + +#undef DEBUG + +#include <linux/fb.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#if defined(CONFIG_PPC_OF) +#include <linux/of_platform.h> +#endif +#include "mb862xxfb.h" +#include "mb862xx_reg.h" + +#define NR_PALETTE 256 +#define MB862XX_MEM_SIZE 0x1000000 +#define CORALP_MEM_SIZE 0x4000000 +#define CARMINE_MEM_SIZE 0x8000000 +#define DRV_NAME "mb862xxfb" + +#if defined(CONFIG_LWMON5) +static struct mb862xx_gc_mode lwmon5_gc_mode = { + /* Mode for Sharp LQ104V1DG61 TFT LCD Panel */ + { "640x480", 60, 640, 480, 40000, 48, 16, 32, 11, 96, 2, 0, 0, 0 }, + /* 16 bits/pixel, 32MB, 100MHz, SDRAM memory mode value */ + 16, 0x2000000, GC_CCF_COT_100, 0x414fb7f2 +}; +#endif + +#if defined(CONFIG_SOCRATES) +static struct mb862xx_gc_mode socrates_gc_mode = { + /* Mode for Prime View PM070WL4 TFT LCD Panel */ + { "800x480", 45, 800, 480, 40000, 86, 42, 33, 10, 128, 2, 0, 0, 0 }, + /* 16 bits/pixel, 16MB, 133MHz, SDRAM memory mode value */ + 16, 0x1000000, GC_CCF_COT_133, 0x4157ba63 +}; +#endif + +/* Helpers */ +static inline int h_total(struct fb_var_screeninfo *var) +{ + return var->xres + var->left_margin + + var->right_margin + var->hsync_len; +} + +static inline int v_total(struct fb_var_screeninfo *var) +{ + return var->yres + var->upper_margin + + var->lower_margin + var->vsync_len; +} + +static inline int hsp(struct fb_var_screeninfo *var) +{ + return var->xres + var->right_margin - 1; +} + +static inline int vsp(struct fb_var_screeninfo *var) +{ + return var->yres + var->lower_margin - 1; +} + +static inline int d_pitch(struct fb_var_screeninfo *var) +{ + return var->xres * var->bits_per_pixel / 8; +} + +static inline unsigned int chan_to_field(unsigned int chan, + struct fb_bitfield *bf) +{ + chan &= 0xffff; + chan >>= 16 - bf->length; + return chan << bf->offset; +} + +static int mb862xxfb_setcolreg(unsigned regno, + unsigned red, unsigned green, unsigned blue, + unsigned transp, struct fb_info *info) +{ + struct mb862xxfb_par *par = info->par; + unsigned int val; + + switch (info->fix.visual) { + case FB_VISUAL_TRUECOLOR: + if (regno < 16) { + val = chan_to_field(red, &info->var.red); + val |= chan_to_field(green, &info->var.green); + val |= chan_to_field(blue, &info->var.blue); + par->pseudo_palette[regno] = val; + } + break; + case FB_VISUAL_PSEUDOCOLOR: + if (regno < 256) { + val = (red >> 8) << 16; + val |= (green >> 8) << 8; + val |= blue >> 8; + outreg(disp, GC_L0PAL0 + (regno * 4), val); + } + break; + default: + return 1; /* unsupported type */ + } + return 0; +} + +static int mb862xxfb_check_var(struct fb_var_screeninfo *var, + struct fb_info *fbi) +{ + unsigned long tmp; + + if (fbi->dev) + dev_dbg(fbi->dev, "%s\n", __func__); + + /* check if these values fit into the registers */ + if (var->hsync_len > 255 || var->vsync_len > 255) + return -EINVAL; + + if ((var->xres + var->right_margin) >= 4096) + return -EINVAL; + + if ((var->yres + var->lower_margin) > 4096) + return -EINVAL; + + if (h_total(var) > 4096 || v_total(var) > 4096) + return -EINVAL; + + if (var->xres_virtual > 4096 || var->yres_virtual > 4096) + return -EINVAL; + + if (var->bits_per_pixel <= 8) + var->bits_per_pixel = 8; + else if (var->bits_per_pixel <= 16) + var->bits_per_pixel = 16; + else if (var->bits_per_pixel <= 32) + var->bits_per_pixel = 32; + + /* + * can cope with 8,16 or 24/32bpp if resulting + * pitch is divisible by 64 without remainder + */ + if (d_pitch(&fbi->var) % GC_L0M_L0W_UNIT) { + int r; + + var->bits_per_pixel = 0; + do { + var->bits_per_pixel += 8; + r = d_pitch(&fbi->var) % GC_L0M_L0W_UNIT; + } while (r && var->bits_per_pixel <= 32); + + if (d_pitch(&fbi->var) % GC_L0M_L0W_UNIT) + return -EINVAL; + } + + /* line length is going to be 128 bit aligned */ + tmp = (var->xres * var->bits_per_pixel) / 8; + if ((tmp & 15) != 0) + return -EINVAL; + + /* set r/g/b positions and validate bpp */ + switch (var->bits_per_pixel) { + case 8: + var->red.length = var->bits_per_pixel; + var->green.length = var->bits_per_pixel; + var->blue.length = var->bits_per_pixel; + var->red.offset = 0; + var->green.offset = 0; + var->blue.offset = 0; + var->transp.length = 0; + break; + case 16: + var->red.length = 5; + var->green.length = 5; + var->blue.length = 5; + var->red.offset = 10; + var->green.offset = 5; + var->blue.offset = 0; + var->transp.length = 0; + break; + case 24: + case 32: + var->transp.length = 8; + var->red.length = 8; + var->green.length = 8; + var->blue.length = 8; + var->transp.offset = 24; + var->red.offset = 16; + var->green.offset = 8; + var->blue.offset = 0; + break; + default: + return -EINVAL; + } + return 0; +} + +/* + * set display parameters + */ +static int mb862xxfb_set_par(struct fb_info *fbi) +{ + struct mb862xxfb_par *par = fbi->par; + unsigned long reg, sc; + + dev_dbg(par->dev, "%s\n", __func__); + + if (par->pre_init) + return 0; + + /* disp off */ + reg = inreg(disp, GC_DCM1); + reg &= ~GC_DCM01_DEN; + outreg(disp, GC_DCM1, reg); + + /* set display reference clock div. */ + sc = par->refclk / (1000000 / fbi->var.pixclock) - 1; + reg = inreg(disp, GC_DCM1); + reg &= ~(GC_DCM01_CKS | GC_DCM01_RESV | GC_DCM01_SC); + reg |= sc << 8; + outreg(disp, GC_DCM1, reg); + dev_dbg(par->dev, "SC 0x%lx\n", sc); + + /* disp dimension, format */ + reg = pack(d_pitch(&fbi->var) / GC_L0M_L0W_UNIT, + (fbi->var.yres - 1)); + if (fbi->var.bits_per_pixel == 16) + reg |= GC_L0M_L0C_16; + outreg(disp, GC_L0M, reg); + + if (fbi->var.bits_per_pixel == 32) { + reg = inreg(disp, GC_L0EM); + outreg(disp, GC_L0EM, reg | GC_L0EM_L0EC_24); + } + outreg(disp, GC_WY_WX, 0); + reg = pack(fbi->var.yres - 1, fbi->var.xres); + outreg(disp, GC_WH_WW, reg); + outreg(disp, GC_L0OA0, 0); + outreg(disp, GC_L0DA0, 0); + outreg(disp, GC_L0DY_L0DX, 0); + outreg(disp, GC_L0WY_L0WX, 0); + outreg(disp, GC_L0WH_L0WW, reg); + + /* both HW-cursors off */ + reg = inreg(disp, GC_CPM_CUTC); + reg &= ~(GC_CPM_CEN0 | GC_CPM_CEN1); + outreg(disp, GC_CPM_CUTC, reg); + + /* timings */ + reg = pack(fbi->var.xres - 1, fbi->var.xres - 1); + outreg(disp, GC_HDB_HDP, reg); + reg = pack((fbi->var.yres - 1), vsp(&fbi->var)); + outreg(disp, GC_VDP_VSP, reg); + reg = ((fbi->var.vsync_len - 1) << 24) | + pack((fbi->var.hsync_len - 1), hsp(&fbi->var)); + outreg(disp, GC_VSW_HSW_HSP, reg); + outreg(disp, GC_HTP, pack(h_total(&fbi->var) - 1, 0)); + outreg(disp, GC_VTR, pack(v_total(&fbi->var) - 1, 0)); + + /* display on */ + reg = inreg(disp, GC_DCM1); + reg |= GC_DCM01_DEN | GC_DCM01_L0E; + reg &= ~GC_DCM01_ESY; + outreg(disp, GC_DCM1, reg); + return 0; +} + +static int mb862xxfb_pan(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct mb862xxfb_par *par = info->par; + unsigned long reg; + + reg = pack(var->yoffset, var->xoffset); + outreg(disp, GC_L0WY_L0WX, reg); + + reg = pack(var->yres_virtual, var->xres_virtual); + outreg(disp, GC_L0WH_L0WW, reg); + return 0; +} + +static int mb862xxfb_blank(int mode, struct fb_info *fbi) +{ + struct mb862xxfb_par *par = fbi->par; + unsigned long reg; + + dev_dbg(fbi->dev, "blank mode=%d\n", mode); + + switch (mode) { + case FB_BLANK_POWERDOWN: + reg = inreg(disp, GC_DCM1); + reg &= ~GC_DCM01_DEN; + outreg(disp, GC_DCM1, reg); + break; + case FB_BLANK_UNBLANK: + reg = inreg(disp, GC_DCM1); + reg |= GC_DCM01_DEN; + outreg(disp, GC_DCM1, reg); + break; + case FB_BLANK_NORMAL: + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_HSYNC_SUSPEND: + default: + return 1; + } + return 0; +} + +/* framebuffer ops */ +static struct fb_ops mb862xxfb_ops = { + .owner = THIS_MODULE, + .fb_check_var = mb862xxfb_check_var, + .fb_set_par = mb862xxfb_set_par, + .fb_setcolreg = mb862xxfb_setcolreg, + .fb_blank = mb862xxfb_blank, + .fb_pan_display = mb862xxfb_pan, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, +}; + +/* initialize fb_info data */ +static int mb862xxfb_init_fbinfo(struct fb_info *fbi) +{ + struct mb862xxfb_par *par = fbi->par; + struct mb862xx_gc_mode *mode = par->gc_mode; + unsigned long reg; + + fbi->fbops = &mb862xxfb_ops; + fbi->pseudo_palette = par->pseudo_palette; + fbi->screen_base = par->fb_base; + fbi->screen_size = par->mapped_vram; + + strcpy(fbi->fix.id, DRV_NAME); + fbi->fix.smem_start = (unsigned long)par->fb_base_phys; + fbi->fix.smem_len = par->mapped_vram; + fbi->fix.mmio_start = (unsigned long)par->mmio_base_phys; + fbi->fix.mmio_len = par->mmio_len; + fbi->fix.accel = FB_ACCEL_NONE; + fbi->fix.type = FB_TYPE_PACKED_PIXELS; + fbi->fix.type_aux = 0; + fbi->fix.xpanstep = 1; + fbi->fix.ypanstep = 1; + fbi->fix.ywrapstep = 0; + + reg = inreg(disp, GC_DCM1); + if (reg & GC_DCM01_DEN && reg & GC_DCM01_L0E) { + /* get the disp mode from active display cfg */ + unsigned long sc = ((reg & GC_DCM01_SC) >> 8) + 1; + unsigned long hsp, vsp, ht, vt; + + dev_dbg(par->dev, "using bootloader's disp. mode\n"); + fbi->var.pixclock = (sc * 1000000) / par->refclk; + fbi->var.xres = (inreg(disp, GC_HDB_HDP) & 0x0fff) + 1; + reg = inreg(disp, GC_VDP_VSP); + fbi->var.yres = ((reg >> 16) & 0x0fff) + 1; + vsp = (reg & 0x0fff) + 1; + fbi->var.xres_virtual = fbi->var.xres; + fbi->var.yres_virtual = fbi->var.yres; + reg = inreg(disp, GC_L0EM); + if (reg & GC_L0EM_L0EC_24) { + fbi->var.bits_per_pixel = 32; + } else { + reg = inreg(disp, GC_L0M); + if (reg & GC_L0M_L0C_16) + fbi->var.bits_per_pixel = 16; + else + fbi->var.bits_per_pixel = 8; + } + reg = inreg(disp, GC_VSW_HSW_HSP); + fbi->var.hsync_len = ((reg & 0xff0000) >> 16) + 1; + fbi->var.vsync_len = ((reg & 0x3f000000) >> 24) + 1; + hsp = (reg & 0xffff) + 1; + ht = ((inreg(disp, GC_HTP) & 0xfff0000) >> 16) + 1; + fbi->var.right_margin = hsp - fbi->var.xres; + fbi->var.left_margin = ht - hsp - fbi->var.hsync_len; + vt = ((inreg(disp, GC_VTR) & 0xfff0000) >> 16) + 1; + fbi->var.lower_margin = vsp - fbi->var.yres; + fbi->var.upper_margin = vt - vsp - fbi->var.vsync_len; + } else if (mode) { + dev_dbg(par->dev, "using supplied mode\n"); + fb_videomode_to_var(&fbi->var, (struct fb_videomode *)mode); + fbi->var.bits_per_pixel = mode->def_bpp ? mode->def_bpp : 8; + } else { + int ret; + + ret = fb_find_mode(&fbi->var, fbi, "640x480-16@60", + NULL, 0, NULL, 16); + if (ret == 0 || ret == 4) { + dev_err(par->dev, + "failed to get initial mode\n"); + return -EINVAL; + } + } + + fbi->var.xoffset = 0; + fbi->var.yoffset = 0; + fbi->var.grayscale = 0; + fbi->var.nonstd = 0; + fbi->var.height = -1; + fbi->var.width = -1; + fbi->var.accel_flags = 0; + fbi->var.vmode = FB_VMODE_NONINTERLACED; + fbi->var.activate = FB_ACTIVATE_NOW; + fbi->flags = FBINFO_DEFAULT | +#ifdef __BIG_ENDIAN + FBINFO_FOREIGN_ENDIAN | +#endif + FBINFO_HWACCEL_XPAN | + FBINFO_HWACCEL_YPAN; + + /* check and possibly fix bpp */ + if ((fbi->fbops->fb_check_var)(&fbi->var, fbi)) + dev_err(par->dev, "check_var() failed on initial setup?\n"); + + fbi->fix.visual = fbi->var.bits_per_pixel == 8 ? + FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; + fbi->fix.line_length = (fbi->var.xres_virtual * + fbi->var.bits_per_pixel) / 8; + return 0; +} + +/* + * show some display controller and cursor registers + */ +static ssize_t mb862xxfb_show_dispregs(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct mb862xxfb_par *par = fbi->par; + char *ptr = buf; + unsigned int reg; + + for (reg = GC_DCM0; reg <= GC_L0DY_L0DX; reg += 4) + ptr += sprintf(ptr, "%08x = %08x\n", + reg, inreg(disp, reg)); + + for (reg = GC_CPM_CUTC; reg <= GC_CUY1_CUX1; reg += 4) + ptr += sprintf(ptr, "%08x = %08x\n", + reg, inreg(disp, reg)); + + for (reg = GC_DCM1; reg <= GC_L0WH_L0WW; reg += 4) + ptr += sprintf(ptr, "%08x = %08x\n", + reg, inreg(disp, reg)); + + return ptr - buf; +} + +static DEVICE_ATTR(dispregs, 0444, mb862xxfb_show_dispregs, NULL); + +irqreturn_t mb862xx_intr(int irq, void *dev_id) +{ + struct mb862xxfb_par *par = (struct mb862xxfb_par *) dev_id; + unsigned long reg_ist, mask; + + if (!par) + return IRQ_NONE; + + if (par->type == BT_CARMINE) { + /* Get Interrupt Status */ + reg_ist = inreg(ctrl, GC_CTRL_STATUS); + mask = inreg(ctrl, GC_CTRL_INT_MASK); + if (reg_ist == 0) + return IRQ_HANDLED; + + reg_ist &= mask; + if (reg_ist == 0) + return IRQ_HANDLED; + + /* Clear interrupt status */ + outreg(ctrl, 0x0, reg_ist); + } else { + /* Get status */ + reg_ist = inreg(host, GC_IST); + mask = inreg(host, GC_IMASK); + + reg_ist &= mask; + if (reg_ist == 0) + return IRQ_HANDLED; + + /* Clear status */ + outreg(host, GC_IST, ~reg_ist); + } + return IRQ_HANDLED; +} + +#if defined(CONFIG_FB_MB862XX_LIME) +/* + * GDC (Lime, Coral(B/Q), Mint, ...) on host bus + */ +static int mb862xx_gdc_init(struct mb862xxfb_par *par) +{ + unsigned long ccf, mmr; + unsigned long ver, rev; + + if (!par) + return -ENODEV; + +#if defined(CONFIG_FB_PRE_INIT_FB) + par->pre_init = 1; +#endif + par->host = par->mmio_base; + par->i2c = par->mmio_base + MB862XX_I2C_BASE; + par->disp = par->mmio_base + MB862XX_DISP_BASE; + par->cap = par->mmio_base + MB862XX_CAP_BASE; + par->draw = par->mmio_base + MB862XX_DRAW_BASE; + par->geo = par->mmio_base + MB862XX_GEO_BASE; + par->pio = par->mmio_base + MB862XX_PIO_BASE; + + par->refclk = GC_DISP_REFCLK_400; + + ver = inreg(host, GC_CID); + rev = inreg(pio, GC_REVISION); + if ((ver == 0x303) && (rev & 0xffffff00) == 0x20050100) { + dev_info(par->dev, "Fujitsu Lime v1.%d found\n", + (int)rev & 0xff); + par->type = BT_LIME; + ccf = par->gc_mode ? par->gc_mode->ccf : GC_CCF_COT_100; + mmr = par->gc_mode ? par->gc_mode->mmr : 0x414fb7f2; + } else { + dev_info(par->dev, "? GDC, CID/Rev.: 0x%lx/0x%lx \n", ver, rev); + return -ENODEV; + } + + if (!par->pre_init) { + outreg(host, GC_CCF, ccf); + udelay(200); + outreg(host, GC_MMR, mmr); + udelay(10); + } + + /* interrupt status */ + outreg(host, GC_IST, 0); + outreg(host, GC_IMASK, GC_INT_EN); + return 0; +} + +static int __devinit of_platform_mb862xx_probe(struct of_device *ofdev, + const struct of_device_id *id) +{ + struct device_node *np = ofdev->node; + struct device *dev = &ofdev->dev; + struct mb862xxfb_par *par; + struct fb_info *info; + struct resource res; + resource_size_t res_size; + unsigned long ret = -ENODEV; + + if (of_address_to_resource(np, 0, &res)) { + dev_err(dev, "Invalid address\n"); + return -ENXIO; + } + + info = framebuffer_alloc(sizeof(struct mb862xxfb_par), dev); + if (info == NULL) { + dev_err(dev, "cannot allocate framebuffer\n"); + return -ENOMEM; + } + + par = info->par; + par->info = info; + par->dev = dev; + + par->irq = irq_of_parse_and_map(np, 0); + if (par->irq == NO_IRQ) { + dev_err(dev, "failed to map irq\n"); + ret = -ENODEV; + goto fbrel; + } + + res_size = 1 + res.end - res.start; + par->res = request_mem_region(res.start, res_size, DRV_NAME); + if (par->res == NULL) { + dev_err(dev, "Cannot claim framebuffer/mmio\n"); + ret = -ENXIO; + goto irqdisp; + } + +#if defined(CONFIG_LWMON5) + par->gc_mode = &lwmon5_gc_mode; +#endif + +#if defined(CONFIG_SOCRATES) + par->gc_mode = &socrates_gc_mode; +#endif + + par->fb_base_phys = res.start; + par->mmio_base_phys = res.start + MB862XX_MMIO_BASE; + par->mmio_len = MB862XX_MMIO_SIZE; + if (par->gc_mode) + par->mapped_vram = par->gc_mode->max_vram; + else + par->mapped_vram = MB862XX_MEM_SIZE; + + par->fb_base = ioremap(par->fb_base_phys, par->mapped_vram); + if (par->fb_base == NULL) { + dev_err(dev, "Cannot map framebuffer\n"); + goto rel_reg; + } + + par->mmio_base = ioremap(par->mmio_base_phys, par->mmio_len); + if (par->mmio_base == NULL) { + dev_err(dev, "Cannot map registers\n"); + goto fb_unmap; + } + + dev_dbg(dev, "fb phys 0x%llx 0x%lx\n", + (u64)par->fb_base_phys, (ulong)par->mapped_vram); + dev_dbg(dev, "mmio phys 0x%llx 0x%lx, (irq = %d)\n", + (u64)par->mmio_base_phys, (ulong)par->mmio_len, par->irq); + + if (mb862xx_gdc_init(par)) + goto io_unmap; + + if (request_irq(par->irq, mb862xx_intr, IRQF_DISABLED, + DRV_NAME, (void *)par)) { + dev_err(dev, "Cannot request irq\n"); + goto io_unmap; + } + + mb862xxfb_init_fbinfo(info); + + if (fb_alloc_cmap(&info->cmap, NR_PALETTE, 0) < 0) { + dev_err(dev, "Could not allocate cmap for fb_info.\n"); + goto free_irq; + } + + if ((info->fbops->fb_set_par)(info)) + dev_err(dev, "set_var() failed on initial setup?\n"); + + if (register_framebuffer(info)) { + dev_err(dev, "failed to register framebuffer\n"); + goto rel_cmap; + } + + dev_set_drvdata(dev, info); + + if (device_create_file(dev, &dev_attr_dispregs)) + dev_err(dev, "Can't create sysfs regdump file\n"); + return 0; + +rel_cmap: + fb_dealloc_cmap(&info->cmap); +free_irq: + outreg(host, GC_IMASK, 0); + free_irq(par->irq, (void *)par); +io_unmap: + iounmap(par->mmio_base); +fb_unmap: + iounmap(par->fb_base); +rel_reg: + release_mem_region(res.start, res_size); +irqdisp: + irq_dispose_mapping(par->irq); +fbrel: + dev_set_drvdata(dev, NULL); + framebuffer_release(info); + return ret; +} + +static int __devexit of_platform_mb862xx_remove(struct of_device *ofdev) +{ + struct fb_info *fbi = dev_get_drvdata(&ofdev->dev); + struct mb862xxfb_par *par = fbi->par; + resource_size_t res_size = 1 + par->res->end - par->res->start; + unsigned long reg; + + dev_dbg(fbi->dev, "%s release\n", fbi->fix.id); + + /* display off */ + reg = inreg(disp, GC_DCM1); + reg &= ~(GC_DCM01_DEN | GC_DCM01_L0E); + outreg(disp, GC_DCM1, reg); + + /* disable interrupts */ + outreg(host, GC_IMASK, 0); + + free_irq(par->irq, (void *)par); + irq_dispose_mapping(par->irq); + + device_remove_file(&ofdev->dev, &dev_attr_dispregs); + + unregister_framebuffer(fbi); + fb_dealloc_cmap(&fbi->cmap); + + iounmap(par->mmio_base); + iounmap(par->fb_base); + + dev_set_drvdata(&ofdev->dev, NULL); + release_mem_region(par->res->start, res_size); + framebuffer_release(fbi); + return 0; +} + +/* + * common types + */ +static struct of_device_id __devinitdata of_platform_mb862xx_tbl[] = { + { .compatible = "fujitsu,MB86276", }, + { .compatible = "fujitsu,lime", }, + { .compatible = "fujitsu,MB86277", }, + { .compatible = "fujitsu,mint", }, + { .compatible = "fujitsu,MB86293", }, + { .compatible = "fujitsu,MB86294", }, + { .compatible = "fujitsu,coral", }, + { /* end */ } +}; + +static struct of_platform_driver of_platform_mb862xxfb_driver = { + .owner = THIS_MODULE, + .name = DRV_NAME, + .match_table = of_platform_mb862xx_tbl, + .probe = of_platform_mb862xx_probe, + .remove = __devexit_p(of_platform_mb862xx_remove), +}; +#endif + +#if defined(CONFIG_FB_MB862XX_PCI_GDC) +static int coralp_init(struct mb862xxfb_par *par) +{ + int cn, ver; + + par->host = par->mmio_base; + par->i2c = par->mmio_base + MB862XX_I2C_BASE; + par->disp = par->mmio_base + MB862XX_DISP_BASE; + par->cap = par->mmio_base + MB862XX_CAP_BASE; + par->draw = par->mmio_base + MB862XX_DRAW_BASE; + par->geo = par->mmio_base + MB862XX_GEO_BASE; + par->pio = par->mmio_base + MB862XX_PIO_BASE; + + par->refclk = GC_DISP_REFCLK_400; + + ver = inreg(host, GC_CID); + cn = (ver & GC_CID_CNAME_MSK) >> 8; + ver = ver & GC_CID_VERSION_MSK; + if (cn == 3) { + dev_info(par->dev, "Fujitsu Coral-%s GDC Rev.%d found\n",\ + (ver == 6) ? "P" : (ver == 8) ? "PA" : "?", + par->pdev->revision); + outreg(host, GC_CCF, GC_CCF_CGE_166 | GC_CCF_COT_133); + udelay(200); + outreg(host, GC_MMR, GC_MMR_CORALP_EVB_VAL); + udelay(10); + /* Clear interrupt status */ + outreg(host, GC_IST, 0); + } else { + return -ENODEV; + } + return 0; +} + +static int init_dram_ctrl(struct mb862xxfb_par *par) +{ + unsigned long i = 0; + + /* + * Set io mode first! Spec. says IC may be destroyed + * if not set to SSTL2/LVCMOS before init. + */ + outreg(dram_ctrl, GC_DCTL_IOCONT1_IOCONT0, GC_EVB_DCTL_IOCONT1_IOCONT0); + + /* DRAM init */ + outreg(dram_ctrl, GC_DCTL_MODE_ADD, GC_EVB_DCTL_MODE_ADD); + outreg(dram_ctrl, GC_DCTL_SETTIME1_EMODE, GC_EVB_DCTL_SETTIME1_EMODE); + outreg(dram_ctrl, GC_DCTL_REFRESH_SETTIME2, + GC_EVB_DCTL_REFRESH_SETTIME2); + outreg(dram_ctrl, GC_DCTL_RSV2_RSV1, GC_EVB_DCTL_RSV2_RSV1); + outreg(dram_ctrl, GC_DCTL_DDRIF2_DDRIF1, GC_EVB_DCTL_DDRIF2_DDRIF1); + outreg(dram_ctrl, GC_DCTL_RSV0_STATES, GC_EVB_DCTL_RSV0_STATES); + + /* DLL reset done? */ + while ((inreg(dram_ctrl, GC_DCTL_RSV0_STATES) & GC_DCTL_STATES_MSK)) { + udelay(GC_DCTL_INIT_WAIT_INTERVAL); + if (i++ > GC_DCTL_INIT_WAIT_CNT) { + dev_err(par->dev, "VRAM init failed.\n"); + return -EINVAL; + } + } + outreg(dram_ctrl, GC_DCTL_MODE_ADD, GC_EVB_DCTL_MODE_ADD_AFT_RST); + outreg(dram_ctrl, GC_DCTL_RSV0_STATES, GC_EVB_DCTL_RSV0_STATES_AFT_RST); + return 0; +} + +static int carmine_init(struct mb862xxfb_par *par) +{ + unsigned long reg; + + par->ctrl = par->mmio_base + MB86297_CTRL_BASE; + par->i2c = par->mmio_base + MB86297_I2C_BASE; + par->disp = par->mmio_base + MB86297_DISP0_BASE; + par->disp1 = par->mmio_base + MB86297_DISP1_BASE; + par->cap = par->mmio_base + MB86297_CAP0_BASE; + par->cap1 = par->mmio_base + MB86297_CAP1_BASE; + par->draw = par->mmio_base + MB86297_DRAW_BASE; + par->dram_ctrl = par->mmio_base + MB86297_DRAMCTRL_BASE; + par->wrback = par->mmio_base + MB86297_WRBACK_BASE; + + par->refclk = GC_DISP_REFCLK_533; + + /* warm up */ + reg = GC_CTRL_CLK_EN_DRAM | GC_CTRL_CLK_EN_2D3D | GC_CTRL_CLK_EN_DISP0; + outreg(ctrl, GC_CTRL_CLK_ENABLE, reg); + + /* check for engine module revision */ + if (inreg(draw, GC_2D3D_REV) == GC_RE_REVISION) + dev_info(par->dev, "Fujitsu Carmine GDC Rev.%d found\n", + par->pdev->revision); + else + goto err_init; + + reg &= ~GC_CTRL_CLK_EN_2D3D; + outreg(ctrl, GC_CTRL_CLK_ENABLE, reg); + + /* set up vram */ + if (init_dram_ctrl(par) < 0) + goto err_init; + + outreg(ctrl, GC_CTRL_INT_MASK, 0); + return 0; + +err_init: + outreg(ctrl, GC_CTRL_CLK_ENABLE, 0); + return -EINVAL; +} + +static inline int mb862xx_pci_gdc_init(struct mb862xxfb_par *par) +{ + switch (par->type) { + case BT_CORALP: + return coralp_init(par); + case BT_CARMINE: + return carmine_init(par); + default: + return -ENODEV; + } +} + +#define CHIP_ID(id) \ + { PCI_DEVICE(PCI_VENDOR_ID_FUJITSU_LIMITED, id) } + +static struct pci_device_id mb862xx_pci_tbl[] __devinitdata = { + /* MB86295/MB86296 */ + CHIP_ID(PCI_DEVICE_ID_FUJITSU_CORALP), + CHIP_ID(PCI_DEVICE_ID_FUJITSU_CORALPA), + /* MB86297 */ + CHIP_ID(PCI_DEVICE_ID_FUJITSU_CARMINE), + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, mb862xx_pci_tbl); + +static int __devinit mb862xx_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct mb862xxfb_par *par; + struct fb_info *info; + struct device *dev = &pdev->dev; + int ret; + + ret = pci_enable_device(pdev); + if (ret < 0) { + dev_err(dev, "Cannot enable PCI device\n"); + goto out; + } + + info = framebuffer_alloc(sizeof(struct mb862xxfb_par), dev); + if (!info) { + dev_err(dev, "framebuffer alloc failed\n"); + ret = -ENOMEM; + goto dis_dev; + } + + par = info->par; + par->info = info; + par->dev = dev; + par->pdev = pdev; + par->irq = pdev->irq; + + ret = pci_request_regions(pdev, DRV_NAME); + if (ret < 0) { + dev_err(dev, "Cannot reserve region(s) for PCI device\n"); + goto rel_fb; + } + + switch (pdev->device) { + case PCI_DEVICE_ID_FUJITSU_CORALP: + case PCI_DEVICE_ID_FUJITSU_CORALPA: + par->fb_base_phys = pci_resource_start(par->pdev, 0); + par->mapped_vram = CORALP_MEM_SIZE; + par->mmio_base_phys = par->fb_base_phys + MB862XX_MMIO_BASE; + par->mmio_len = MB862XX_MMIO_SIZE; + par->type = BT_CORALP; + break; + case PCI_DEVICE_ID_FUJITSU_CARMINE: + par->fb_base_phys = pci_resource_start(par->pdev, 2); + par->mmio_base_phys = pci_resource_start(par->pdev, 3); + par->mmio_len = pci_resource_len(par->pdev, 3); + par->mapped_vram = CARMINE_MEM_SIZE; + par->type = BT_CARMINE; + break; + default: + /* should never occur */ + goto rel_reg; + } + + par->fb_base = ioremap(par->fb_base_phys, par->mapped_vram); + if (par->fb_base == NULL) { + dev_err(dev, "Cannot map framebuffer\n"); + goto rel_reg; + } + + par->mmio_base = ioremap(par->mmio_base_phys, par->mmio_len); + if (par->mmio_base == NULL) { + dev_err(dev, "Cannot map registers\n"); + ret = -EIO; + goto fb_unmap; + } + + dev_dbg(dev, "fb phys 0x%llx 0x%lx\n", + (u64)par->fb_base_phys, (ulong)par->mapped_vram); + dev_dbg(dev, "mmio phys 0x%llx 0x%lx\n", + (u64)par->mmio_base_phys, (ulong)par->mmio_len); + + if (mb862xx_pci_gdc_init(par)) + goto io_unmap; + + if (request_irq(par->irq, mb862xx_intr, IRQF_DISABLED | IRQF_SHARED, + DRV_NAME, (void *)par)) { + dev_err(dev, "Cannot request irq\n"); + goto io_unmap; + } + + mb862xxfb_init_fbinfo(info); + + if (fb_alloc_cmap(&info->cmap, NR_PALETTE, 0) < 0) { + dev_err(dev, "Could not allocate cmap for fb_info.\n"); + ret = -ENOMEM; + goto free_irq; + } + + if ((info->fbops->fb_set_par)(info)) + dev_err(dev, "set_var() failed on initial setup?\n"); + + ret = register_framebuffer(info); + if (ret < 0) { + dev_err(dev, "failed to register framebuffer\n"); + goto rel_cmap; + } + + pci_set_drvdata(pdev, info); + + if (device_create_file(dev, &dev_attr_dispregs)) + dev_err(dev, "Can't create sysfs regdump file\n"); + + if (par->type == BT_CARMINE) + outreg(ctrl, GC_CTRL_INT_MASK, GC_CARMINE_INT_EN); + else + outreg(host, GC_IMASK, GC_INT_EN); + + return 0; + +rel_cmap: + fb_dealloc_cmap(&info->cmap); +free_irq: + free_irq(par->irq, (void *)par); +io_unmap: + iounmap(par->mmio_base); +fb_unmap: + iounmap(par->fb_base); +rel_reg: + pci_release_regions(pdev); +rel_fb: + framebuffer_release(info); +dis_dev: + pci_disable_device(pdev); +out: + return ret; +} + +static void __devexit mb862xx_pci_remove(struct pci_dev *pdev) +{ + struct fb_info *fbi = pci_get_drvdata(pdev); + struct mb862xxfb_par *par = fbi->par; + unsigned long reg; + + dev_dbg(fbi->dev, "%s release\n", fbi->fix.id); + + /* display off */ + reg = inreg(disp, GC_DCM1); + reg &= ~(GC_DCM01_DEN | GC_DCM01_L0E); + outreg(disp, GC_DCM1, reg); + + if (par->type == BT_CARMINE) { + outreg(ctrl, GC_CTRL_INT_MASK, 0); + outreg(ctrl, GC_CTRL_CLK_ENABLE, 0); + } else { + outreg(host, GC_IMASK, 0); + } + + device_remove_file(&pdev->dev, &dev_attr_dispregs); + + pci_set_drvdata(pdev, NULL); + unregister_framebuffer(fbi); + fb_dealloc_cmap(&fbi->cmap); + + free_irq(par->irq, (void *)par); + iounmap(par->mmio_base); + iounmap(par->fb_base); + + pci_release_regions(pdev); + framebuffer_release(fbi); + pci_disable_device(pdev); +} + +static struct pci_driver mb862xxfb_pci_driver = { + .name = DRV_NAME, + .id_table = mb862xx_pci_tbl, + .probe = mb862xx_pci_probe, + .remove = __devexit_p(mb862xx_pci_remove), +}; +#endif + +static int __devinit mb862xxfb_init(void) +{ + int ret = -ENODEV; + +#if defined(CONFIG_FB_MB862XX_LIME) + ret = of_register_platform_driver(&of_platform_mb862xxfb_driver); +#endif +#if defined(CONFIG_FB_MB862XX_PCI_GDC) + ret = pci_register_driver(&mb862xxfb_pci_driver); +#endif + return ret; +} + +static void __exit mb862xxfb_exit(void) +{ +#if defined(CONFIG_FB_MB862XX_LIME) + of_unregister_platform_driver(&of_platform_mb862xxfb_driver); +#endif +#if defined(CONFIG_FB_MB862XX_PCI_GDC) + pci_unregister_driver(&mb862xxfb_pci_driver); +#endif +} + +module_init(mb862xxfb_init); +module_exit(mb862xxfb_exit); + +MODULE_DESCRIPTION("Fujitsu MB862xx Framebuffer driver"); +MODULE_AUTHOR("Anatolij Gustschin <agust@denx.de>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/video/mb862xx/mb862xxfb.h b/drivers/video/mb862xx/mb862xxfb.h new file mode 100644 index 000000000000..c4c8f4dd2217 --- /dev/null +++ b/drivers/video/mb862xx/mb862xxfb.h @@ -0,0 +1,83 @@ +#ifndef __MB862XX_H__ +#define __MB862XX_H__ + +#define PCI_VENDOR_ID_FUJITSU_LIMITED 0x10cf +#define PCI_DEVICE_ID_FUJITSU_CORALP 0x2019 +#define PCI_DEVICE_ID_FUJITSU_CORALPA 0x201e +#define PCI_DEVICE_ID_FUJITSU_CARMINE 0x202b + +#define GC_MMR_CORALP_EVB_VAL 0x11d7fa13 + +enum gdctype { + BT_NONE, + BT_LIME, + BT_MINT, + BT_CORAL, + BT_CORALP, + BT_CARMINE, +}; + +struct mb862xx_gc_mode { + struct fb_videomode def_mode; /* mode of connected display */ + unsigned int def_bpp; /* default depth */ + unsigned long max_vram; /* connected SDRAM size */ + unsigned long ccf; /* gdc clk */ + unsigned long mmr; /* memory mode for SDRAM */ +}; + +/* private data */ +struct mb862xxfb_par { + struct fb_info *info; /* fb info head */ + struct device *dev; + struct pci_dev *pdev; + struct resource *res; /* framebuffer/mmio resource */ + + resource_size_t fb_base_phys; /* fb base, 36-bit PPC440EPx */ + resource_size_t mmio_base_phys; /* io base addr */ + void __iomem *fb_base; /* remapped framebuffer */ + void __iomem *mmio_base; /* remapped registers */ + size_t mapped_vram; /* length of remapped vram */ + size_t mmio_len; /* length of register region */ + + void __iomem *host; /* relocatable reg. bases */ + void __iomem *i2c; + void __iomem *disp; + void __iomem *disp1; + void __iomem *cap; + void __iomem *cap1; + void __iomem *draw; + void __iomem *geo; + void __iomem *pio; + void __iomem *ctrl; + void __iomem *dram_ctrl; + void __iomem *wrback; + + unsigned int irq; + unsigned int type; /* GDC type */ + unsigned int refclk; /* disp. reference clock */ + struct mb862xx_gc_mode *gc_mode; /* GDC mode init data */ + int pre_init; /* don't init display if 1 */ + + u32 pseudo_palette[16]; +}; + +#if defined(CONFIG_FB_MB862XX_LIME) && defined(CONFIG_FB_MB862XX_PCI_GDC) +#error "Select Lime GDC or CoralP/Carmine support, but not both together" +#endif +#if defined(CONFIG_FB_MB862XX_LIME) +#define gdc_read __raw_readl +#define gdc_write __raw_writel +#else +#define gdc_read readl +#define gdc_write writel +#endif + +#define inreg(type, off) \ + gdc_read((par->type + (off))) + +#define outreg(type, off, val) \ + gdc_write((val), (par->type + (off))) + +#define pack(a, b) (((a) << 16) | (b)) + +#endif diff --git a/drivers/video/omap/Makefile b/drivers/video/omap/Makefile index 99da8b6d2c36..ed13889c1162 100644 --- a/drivers/video/omap/Makefile +++ b/drivers/video/omap/Makefile @@ -23,7 +23,6 @@ objs-y$(CONFIG_MACH_OMAP_PALMZ71) += lcd_palmz71.o objs-$(CONFIG_ARCH_OMAP16XX)$(CONFIG_MACH_OMAP_INNOVATOR) += lcd_inn1610.o objs-$(CONFIG_ARCH_OMAP15XX)$(CONFIG_MACH_OMAP_INNOVATOR) += lcd_inn1510.o objs-y$(CONFIG_MACH_OMAP_OSK) += lcd_osk.o -objs-y$(CONFIG_MACH_SX1) += lcd_sx1.o omapfb-objs := $(objs-yy) diff --git a/drivers/video/omap/lcd_sx1.c b/drivers/video/omap/lcd_sx1.c deleted file mode 100644 index e55de201b8ff..000000000000 --- a/drivers/video/omap/lcd_sx1.c +++ /dev/null @@ -1,327 +0,0 @@ -/* - * LCD panel support for the Siemens SX1 mobile phone - * - * Current version : Vovan888@gmail.com, great help from FCA00000 - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/delay.h> -#include <linux/io.h> - -#include <mach/gpio.h> -#include <mach/omapfb.h> -#include <mach/mcbsp.h> -#include <mach/mux.h> - -/* - * OMAP310 GPIO registers - */ -#define GPIO_DATA_INPUT 0xfffce000 -#define GPIO_DATA_OUTPUT 0xfffce004 -#define GPIO_DIR_CONTROL 0xfffce008 -#define GPIO_INT_CONTROL 0xfffce00c -#define GPIO_INT_MASK 0xfffce010 -#define GPIO_INT_STATUS 0xfffce014 -#define GPIO_PIN_CONTROL 0xfffce018 - - -#define A_LCD_SSC_RD 3 -#define A_LCD_SSC_SD 7 -#define _A_LCD_RESET 9 -#define _A_LCD_SSC_CS 12 -#define _A_LCD_SSC_A0 13 - -#define DSP_REG 0xE1017024 - -const unsigned char INIT_1[12] = { - 0x1C, 0x02, 0x88, 0x00, 0x1E, 0xE0, 0x00, 0xDC, 0x00, 0x02, 0x00 -}; - -const unsigned char INIT_2[127] = { - 0x15, 0x00, 0x29, 0x00, 0x3E, 0x00, 0x51, 0x00, - 0x65, 0x00, 0x7A, 0x00, 0x8D, 0x00, 0xA1, 0x00, - 0xB6, 0x00, 0xC7, 0x00, 0xD8, 0x00, 0xEB, 0x00, - 0xFB, 0x00, 0x0B, 0x01, 0x1B, 0x01, 0x27, 0x01, - 0x34, 0x01, 0x41, 0x01, 0x4C, 0x01, 0x55, 0x01, - 0x5F, 0x01, 0x68, 0x01, 0x70, 0x01, 0x78, 0x01, - 0x7E, 0x01, 0x86, 0x01, 0x8C, 0x01, 0x94, 0x01, - 0x9B, 0x01, 0xA1, 0x01, 0xA4, 0x01, 0xA9, 0x01, - 0xAD, 0x01, 0xB2, 0x01, 0xB7, 0x01, 0xBC, 0x01, - 0xC0, 0x01, 0xC4, 0x01, 0xC8, 0x01, 0xCB, 0x01, - 0xCF, 0x01, 0xD2, 0x01, 0xD5, 0x01, 0xD8, 0x01, - 0xDB, 0x01, 0xE0, 0x01, 0xE3, 0x01, 0xE6, 0x01, - 0xE8, 0x01, 0xEB, 0x01, 0xEE, 0x01, 0xF1, 0x01, - 0xF3, 0x01, 0xF8, 0x01, 0xF9, 0x01, 0xFC, 0x01, - 0x00, 0x02, 0x03, 0x02, 0x07, 0x02, 0x09, 0x02, - 0x0E, 0x02, 0x13, 0x02, 0x1C, 0x02, 0x00 -}; - -const unsigned char INIT_3[15] = { - 0x14, 0x26, 0x33, 0x3D, 0x45, 0x4D, 0x53, 0x59, - 0x5E, 0x63, 0x67, 0x6D, 0x71, 0x78, 0xFF -}; - -static void epson_sendbyte(int flag, unsigned char byte) -{ - int i, shifter = 0x80; - - if (!flag) - gpio_set_value(_A_LCD_SSC_A0, 0); - mdelay(2); - gpio_set_value(A_LCD_SSC_RD, 1); - - gpio_set_value(A_LCD_SSC_SD, flag); - - OMAP_MCBSP_WRITE(OMAP1510_MCBSP3_BASE, PCR0, 0x2200); - OMAP_MCBSP_WRITE(OMAP1510_MCBSP3_BASE, PCR0, 0x2202); - for (i = 0; i < 8; i++) { - OMAP_MCBSP_WRITE(OMAP1510_MCBSP3_BASE, PCR0, 0x2200); - gpio_set_value(A_LCD_SSC_SD, shifter & byte); - OMAP_MCBSP_WRITE(OMAP1510_MCBSP3_BASE, PCR0, 0x2202); - shifter >>= 1; - } - gpio_set_value(_A_LCD_SSC_A0, 1); -} - -static void init_system(void) -{ - omap_mcbsp_request(OMAP_MCBSP3); - omap_mcbsp_stop(OMAP_MCBSP3); -} - -static void setup_GPIO(void) -{ - /* new wave */ - gpio_request(A_LCD_SSC_RD, "lcd_ssc_rd"); - gpio_request(A_LCD_SSC_SD, "lcd_ssc_sd"); - gpio_request(_A_LCD_RESET, "lcd_reset"); - gpio_request(_A_LCD_SSC_CS, "lcd_ssc_cs"); - gpio_request(_A_LCD_SSC_A0, "lcd_ssc_a0"); - - /* set GPIOs to output, with initial data */ - gpio_direction_output(A_LCD_SSC_RD, 1); - gpio_direction_output(A_LCD_SSC_SD, 0); - gpio_direction_output(_A_LCD_RESET, 0); - gpio_direction_output(_A_LCD_SSC_CS, 1); - gpio_direction_output(_A_LCD_SSC_A0, 1); -} - -static void display_init(void) -{ - int i; - - omap_cfg_reg(MCBSP3_CLKX); - - mdelay(2); - setup_GPIO(); - mdelay(2); - - /* reset LCD */ - gpio_set_value(A_LCD_SSC_SD, 1); - epson_sendbyte(0, 0x25); - - gpio_set_value(_A_LCD_RESET, 0); - mdelay(10); - gpio_set_value(_A_LCD_RESET, 1); - - gpio_set_value(_A_LCD_SSC_CS, 1); - mdelay(2); - gpio_set_value(_A_LCD_SSC_CS, 0); - - /* init LCD, phase 1 */ - epson_sendbyte(0, 0xCA); - for (i = 0; i < 10; i++) - epson_sendbyte(1, INIT_1[i]); - gpio_set_value(_A_LCD_SSC_CS, 1); - gpio_set_value(_A_LCD_SSC_CS, 0); - - /* init LCD phase 2 */ - epson_sendbyte(0, 0xCB); - for (i = 0; i < 125; i++) - epson_sendbyte(1, INIT_2[i]); - gpio_set_value(_A_LCD_SSC_CS, 1); - gpio_set_value(_A_LCD_SSC_CS, 0); - - /* init LCD phase 2a */ - epson_sendbyte(0, 0xCC); - for (i = 0; i < 14; i++) - epson_sendbyte(1, INIT_3[i]); - gpio_set_value(_A_LCD_SSC_CS, 1); - gpio_set_value(_A_LCD_SSC_CS, 0); - - /* init LCD phase 3 */ - epson_sendbyte(0, 0xBC); - epson_sendbyte(1, 0x08); - gpio_set_value(_A_LCD_SSC_CS, 1); - gpio_set_value(_A_LCD_SSC_CS, 0); - - /* init LCD phase 4 */ - epson_sendbyte(0, 0x07); - epson_sendbyte(1, 0x05); - gpio_set_value(_A_LCD_SSC_CS, 1); - gpio_set_value(_A_LCD_SSC_CS, 0); - - /* init LCD phase 5 */ - epson_sendbyte(0, 0x94); - gpio_set_value(_A_LCD_SSC_CS, 1); - gpio_set_value(_A_LCD_SSC_CS, 0); - - /* init LCD phase 6 */ - epson_sendbyte(0, 0xC6); - epson_sendbyte(1, 0x80); - gpio_set_value(_A_LCD_SSC_CS, 1); - mdelay(100); /* used to be 1000 */ - gpio_set_value(_A_LCD_SSC_CS, 0); - - /* init LCD phase 7 */ - epson_sendbyte(0, 0x16); - epson_sendbyte(1, 0x02); - epson_sendbyte(1, 0x00); - epson_sendbyte(1, 0xB1); - epson_sendbyte(1, 0x00); - gpio_set_value(_A_LCD_SSC_CS, 1); - gpio_set_value(_A_LCD_SSC_CS, 0); - - /* init LCD phase 8 */ - epson_sendbyte(0, 0x76); - epson_sendbyte(1, 0x00); - epson_sendbyte(1, 0x00); - epson_sendbyte(1, 0xDB); - epson_sendbyte(1, 0x00); - gpio_set_value(_A_LCD_SSC_CS, 1); - gpio_set_value(_A_LCD_SSC_CS, 0); - - /* init LCD phase 9 */ - epson_sendbyte(0, 0xAF); - gpio_set_value(_A_LCD_SSC_CS, 1); -} - -static int sx1_panel_init(struct lcd_panel *panel, struct omapfb_device *fbdev) -{ - return 0; -} - -static void sx1_panel_cleanup(struct lcd_panel *panel) -{ -} - -static void sx1_panel_disable(struct lcd_panel *panel) -{ - printk(KERN_INFO "SX1: LCD panel disable\n"); - sx1_setmmipower(0); - gpio_set_value(_A_LCD_SSC_CS, 1); - - epson_sendbyte(0, 0x25); - gpio_set_value(_A_LCD_SSC_CS, 0); - - epson_sendbyte(0, 0xAE); - gpio_set_value(_A_LCD_SSC_CS, 1); - mdelay(100); - gpio_set_value(_A_LCD_SSC_CS, 0); - - epson_sendbyte(0, 0x95); - gpio_set_value(_A_LCD_SSC_CS, 1); -} - -static int sx1_panel_enable(struct lcd_panel *panel) -{ - printk(KERN_INFO "lcd_sx1: LCD panel enable\n"); - init_system(); - display_init(); - - sx1_setmmipower(1); - sx1_setbacklight(0x18); - sx1_setkeylight (0x06); - return 0; -} - - -static unsigned long sx1_panel_get_caps(struct lcd_panel *panel) -{ - return 0; -} - -struct lcd_panel sx1_panel = { - .name = "sx1", - .config = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC | - OMAP_LCDC_INV_HSYNC | OMAP_LCDC_INV_PIX_CLOCK | - OMAP_LCDC_INV_OUTPUT_EN, - - .x_res = 176, - .y_res = 220, - .data_lines = 16, - .bpp = 16, - .hsw = 5, - .hfp = 5, - .hbp = 5, - .vsw = 2, - .vfp = 1, - .vbp = 1, - .pixel_clock = 1500, - - .init = sx1_panel_init, - .cleanup = sx1_panel_cleanup, - .enable = sx1_panel_enable, - .disable = sx1_panel_disable, - .get_caps = sx1_panel_get_caps, -}; - -static int sx1_panel_probe(struct platform_device *pdev) -{ - omapfb_register_panel(&sx1_panel); - return 0; -} - -static int sx1_panel_remove(struct platform_device *pdev) -{ - return 0; -} - -static int sx1_panel_suspend(struct platform_device *pdev, pm_message_t mesg) -{ - return 0; -} - -static int sx1_panel_resume(struct platform_device *pdev) -{ - return 0; -} - -struct platform_driver sx1_panel_driver = { - .probe = sx1_panel_probe, - .remove = sx1_panel_remove, - .suspend = sx1_panel_suspend, - .resume = sx1_panel_resume, - .driver = { - .name = "lcd_sx1", - .owner = THIS_MODULE, - }, -}; - -static int sx1_panel_drv_init(void) -{ - return platform_driver_register(&sx1_panel_driver); -} - -static void sx1_panel_drv_cleanup(void) -{ - platform_driver_unregister(&sx1_panel_driver); -} - -module_init(sx1_panel_drv_init); -module_exit(sx1_panel_drv_cleanup); diff --git a/drivers/video/output.c b/drivers/video/output.c index f2df5519c9c4..5e6439ae7394 100644 --- a/drivers/video/output.c +++ b/drivers/video/output.c @@ -96,7 +96,7 @@ struct output_device *video_output_register(const char *name, new_dev->props = op; new_dev->dev.class = &video_output_class; new_dev->dev.parent = dev; - strlcpy(new_dev->dev.bus_id,name, BUS_ID_SIZE); + dev_set_name(&new_dev->dev, name); dev_set_drvdata(&new_dev->dev, devdata); ret_code = device_register(&new_dev->dev); if (ret_code) { diff --git a/drivers/video/ps3fb.c b/drivers/video/ps3fb.c index 4b5d80771904..bd3e39baf7d2 100644 --- a/drivers/video/ps3fb.c +++ b/drivers/video/ps3fb.c @@ -460,12 +460,16 @@ static void ps3fb_sync_image(struct device *dev, u64 frame_offset, line_length |= (u64)src_line_length << 32; src_offset += GPU_FB_START; + + mutex_lock(&ps3_gpu_mutex); status = lv1_gpu_context_attribute(ps3fb.context_handle, L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT, dst_offset, GPU_IOIF + src_offset, L1GPU_FB_BLIT_WAIT_FOR_COMPLETION | (width << 16) | height, line_length); + mutex_unlock(&ps3_gpu_mutex); + if (status) dev_err(dev, "%s: lv1_gpu_context_attribute FB_BLIT failed: %d\n", @@ -784,15 +788,6 @@ static int ps3fb_wait_for_vsync(u32 crtc) return 0; } -static void ps3fb_flip_ctl(int on, void *data) -{ - struct ps3fb_priv *priv = data; - if (on) - atomic_dec_if_positive(&priv->ext_flip); - else - atomic_inc(&priv->ext_flip); -} - /* * ioctl @@ -1228,7 +1223,6 @@ static int __devinit ps3fb_probe(struct ps3_system_bus_device *dev) } ps3fb.task = task; - ps3av_register_flip_ctl(ps3fb_flip_ctl, &ps3fb); return 0; @@ -1258,10 +1252,9 @@ static int ps3fb_shutdown(struct ps3_system_bus_device *dev) dev_dbg(&dev->core, " -> %s:%d\n", __func__, __LINE__); - ps3fb_flip_ctl(0, &ps3fb); /* flip off */ + atomic_inc(&ps3fb.ext_flip); /* flip off */ ps3fb.dinfo->irq.mask = 0; - ps3av_register_flip_ctl(NULL, NULL); if (ps3fb.task) { struct task_struct *task = ps3fb.task; ps3fb.task = NULL; diff --git a/drivers/video/pxafb.c b/drivers/video/pxafb.c index 97204497d9f7..afe7a65c5603 100644 --- a/drivers/video/pxafb.c +++ b/drivers/video/pxafb.c @@ -69,9 +69,6 @@ #define LCCR3_INVALID_CONFIG_MASK (LCCR3_HSP | LCCR3_VSP |\ LCCR3_PCD | LCCR3_BPP) -static void (*pxafb_backlight_power)(int); -static void (*pxafb_lcd_power)(int, struct fb_var_screeninfo *); - static int pxafb_activate_var(struct fb_var_screeninfo *var, struct pxafb_info *); static void set_ctrlr_state(struct pxafb_info *fbi, u_int state); @@ -804,6 +801,9 @@ static int pxafb_smart_thread(void *arg) static int pxafb_smart_init(struct pxafb_info *fbi) { + if (!(fbi->lccr0 | LCCR0_LCDT)) + return 0; + fbi->smart_thread = kthread_run(pxafb_smart_thread, fbi, "lcd_refresh"); if (IS_ERR(fbi->smart_thread)) { @@ -811,6 +811,7 @@ static int pxafb_smart_init(struct pxafb_info *fbi) __func__); return PTR_ERR(fbi->smart_thread); } + return 0; } #else @@ -973,16 +974,16 @@ static inline void __pxafb_backlight_power(struct pxafb_info *fbi, int on) { pr_debug("pxafb: backlight o%s\n", on ? "n" : "ff"); - if (pxafb_backlight_power) - pxafb_backlight_power(on); + if (fbi->backlight_power) + fbi->backlight_power(on); } static inline void __pxafb_lcd_power(struct pxafb_info *fbi, int on) { pr_debug("pxafb: LCD power o%s\n", on ? "n" : "ff"); - if (pxafb_lcd_power) - pxafb_lcd_power(on, &fbi->fb.var); + if (fbi->lcd_power) + fbi->lcd_power(on, &fbi->fb.var); } static void pxafb_setup_gpio(struct pxafb_info *fbi) @@ -1372,7 +1373,7 @@ static void pxafb_decode_mach_info(struct pxafb_info *fbi, fbi->cmap_inverse = inf->cmap_inverse; fbi->cmap_static = inf->cmap_static; - switch (lcd_conn & 0xf) { + switch (lcd_conn & LCD_TYPE_MASK) { case LCD_TYPE_MONO_STN: fbi->lccr0 = LCCR0_CMS; break; @@ -1426,7 +1427,7 @@ static struct pxafb_info * __devinit pxafb_init_fbinfo(struct device *dev) memset(fbi, 0, sizeof(struct pxafb_info)); fbi->dev = dev; - fbi->clk = clk_get(dev, "LCDCLK"); + fbi->clk = clk_get(dev, NULL); if (IS_ERR(fbi->clk)) { kfree(fbi); return NULL; @@ -1745,8 +1746,7 @@ static int __devinit pxafb_probe(struct platform_device *dev) ret = -EINVAL; goto failed; } - pxafb_backlight_power = inf->pxafb_backlight_power; - pxafb_lcd_power = inf->pxafb_lcd_power; + fbi = pxafb_init_fbinfo(&dev->dev); if (!fbi) { /* only reason for pxafb_init_fbinfo to fail is kmalloc */ @@ -1755,6 +1755,9 @@ static int __devinit pxafb_probe(struct platform_device *dev) goto failed; } + fbi->backlight_power = inf->pxafb_backlight_power; + fbi->lcd_power = inf->pxafb_lcd_power; + r = platform_get_resource(dev, IORESOURCE_MEM, 0); if (r == NULL) { dev_err(&dev->dev, "no I/O memory resource defined\n"); diff --git a/drivers/video/pxafb.h b/drivers/video/pxafb.h index 31541b86f13d..d8eb93fa03a3 100644 --- a/drivers/video/pxafb.h +++ b/drivers/video/pxafb.h @@ -124,6 +124,9 @@ struct pxafb_info { struct notifier_block freq_transition; struct notifier_block freq_policy; #endif + + void (*lcd_power)(int, struct fb_var_screeninfo *); + void (*backlight_power)(int); }; #define TO_INF(ptr,member) container_of(ptr,struct pxafb_info,member) diff --git a/drivers/video/sa1100fb.c b/drivers/video/sa1100fb.c index c052bd4c0b06..076f946fa0f5 100644 --- a/drivers/video/sa1100fb.c +++ b/drivers/video/sa1100fb.c @@ -114,7 +114,7 @@ * - convert dma address types to dma_addr_t * - remove unused 'montype' stuff * - remove redundant zero inits of init_var after the initial - * memzero. + * memset. * - remove allow_modeset (acornfb idea does not belong here) * * 2001/05/28: <rmk@arm.linux.org.uk> diff --git a/drivers/video/sh7760fb.c b/drivers/video/sh7760fb.c index 8d0212da4514..653bdfee3057 100644 --- a/drivers/video/sh7760fb.c +++ b/drivers/video/sh7760fb.c @@ -13,6 +13,8 @@ * * Thanks to Siegfried Schaefer <s.schaefer at schaefer-edv.de> * for his original source and testing! + * + * sh7760_setcolreg get from drivers/video/sh_mobile_lcdcfb.c */ #include <linux/completion.h> @@ -53,29 +55,6 @@ static irqreturn_t sh7760fb_irq(int irq, void *data) return IRQ_HANDLED; } -static void sh7760fb_wait_vsync(struct fb_info *info) -{ - struct sh7760fb_par *par = info->par; - - if (par->pd->novsync) - return; - - iowrite16(ioread16(par->base + LDINTR) & ~VINT_CHECK, - par->base + LDINTR); - - if (par->irq < 0) { - /* poll for vert. retrace: status bit is sticky */ - while (!(ioread16(par->base + LDINTR) & VINT_CHECK)) - cpu_relax(); - } else { - /* a "wait_for_irq_event(par->irq)" would be extremely nice */ - init_completion(&par->vsync); - enable_irq(par->irq); - wait_for_completion(&par->vsync); - disable_irq_nosync(par->irq); - } -} - /* wait_for_lps - wait until power supply has reached a certain state. */ static int wait_for_lps(struct sh7760fb_par *par, int val) { @@ -117,55 +96,28 @@ static int sh7760fb_blank(int blank, struct fb_info *info) return wait_for_lps(par, lps); } -/* set color registers */ -static int sh7760fb_setcmap(struct fb_cmap *cmap, struct fb_info *info) +static int sh7760_setcolreg (u_int regno, + u_int red, u_int green, u_int blue, + u_int transp, struct fb_info *info) { - struct sh7760fb_par *par = info->par; - u32 s = cmap->start; - u32 l = cmap->len; - u16 *r = cmap->red; - u16 *g = cmap->green; - u16 *b = cmap->blue; - u32 col, tmo; - int ret; + u32 *palette = info->pseudo_palette; - ret = 0; + if (regno >= 16) + return -EINVAL; - sh7760fb_wait_vsync(info); + /* only FB_VISUAL_TRUECOLOR supported */ - /* request palette access */ - iowrite16(LDPALCR_PALEN, par->base + LDPALCR); + red >>= 16 - info->var.red.length; + green >>= 16 - info->var.green.length; + blue >>= 16 - info->var.blue.length; + transp >>= 16 - info->var.transp.length; - /* poll for access grant */ - tmo = 100; - while (!(ioread16(par->base + LDPALCR) & LDPALCR_PALS) && (--tmo)) - cpu_relax(); + palette[regno] = (red << info->var.red.offset) | + (green << info->var.green.offset) | + (blue << info->var.blue.offset) | + (transp << info->var.transp.offset); - if (!tmo) { - ret = 1; - dev_dbg(info->dev, "no palette access!\n"); - goto out; - } - - while (l && (s < 256)) { - col = ((*r) & 0xff) << 16; - col |= ((*g) & 0xff) << 8; - col |= ((*b) & 0xff); - col &= SH7760FB_PALETTE_MASK; - iowrite32(col, par->base + LDPR(s)); - - if (s < 16) - ((u32 *) (info->pseudo_palette))[s] = s; - - s++; - l--; - r++; - g++; - b++; - } -out: - iowrite16(0, par->base + LDPALCR); - return ret; + return 0; } static void encode_fix(struct fb_fix_screeninfo *fix, struct fb_info *info, @@ -406,7 +358,7 @@ static struct fb_ops sh7760fb_ops = { .owner = THIS_MODULE, .fb_blank = sh7760fb_blank, .fb_check_var = sh7760fb_check_var, - .fb_setcmap = sh7760fb_setcmap, + .fb_setcolreg = sh7760_setcolreg, .fb_set_par = sh7760fb_set_par, .fb_fillrect = cfb_fillrect, .fb_copyarea = cfb_copyarea, diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c index efff672fd7b8..c81ee00c54d7 100644 --- a/drivers/video/sh_mobile_lcdcfb.c +++ b/drivers/video/sh_mobile_lcdcfb.c @@ -35,6 +35,7 @@ struct sh_mobile_lcdc_chan { struct sh_mobile_lcdc_priv { void __iomem *base; #ifdef CONFIG_HAVE_CLK + struct clk *dot_clk; struct clk *clk; #endif unsigned long lddckr; @@ -207,6 +208,11 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) int k, m; int ret = 0; +#ifdef CONFIG_HAVE_CLK + clk_enable(priv->clk); + if (priv->dot_clk) + clk_enable(priv->dot_clk); +#endif /* reset */ lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) | LCDC_RESET); lcdc_wait_bit(priv, _LDCNT2R, LCDC_RESET, 0); @@ -371,6 +377,12 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv) /* stop the lcdc */ sh_mobile_lcdc_start_stop(priv, 0); + +#ifdef CONFIG_HAVE_CLK + if (priv->dot_clk) + clk_disable(priv->dot_clk); + clk_disable(priv->clk); +#endif } static int sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan *ch) @@ -413,9 +425,13 @@ static int sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan *ch) return -EINVAL; } -static int sh_mobile_lcdc_setup_clocks(struct device *dev, int clock_source, +static int sh_mobile_lcdc_setup_clocks(struct platform_device *pdev, + int clock_source, struct sh_mobile_lcdc_priv *priv) { +#ifdef CONFIG_HAVE_CLK + char clk_name[8]; +#endif char *str; int icksel; @@ -430,14 +446,20 @@ static int sh_mobile_lcdc_setup_clocks(struct device *dev, int clock_source, priv->lddckr = icksel << 16; #ifdef CONFIG_HAVE_CLK + snprintf(clk_name, sizeof(clk_name), "lcdc%d", pdev->id); + priv->clk = clk_get(&pdev->dev, clk_name); + if (IS_ERR(priv->clk)) { + dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name); + return PTR_ERR(priv->clk); + } + if (str) { - priv->clk = clk_get(dev, str); - if (IS_ERR(priv->clk)) { - dev_err(dev, "cannot get clock %s\n", str); - return PTR_ERR(priv->clk); + priv->dot_clk = clk_get(&pdev->dev, str); + if (IS_ERR(priv->dot_clk)) { + dev_err(&pdev->dev, "cannot get dot clock %s\n", str); + clk_put(priv->clk); + return PTR_ERR(priv->dot_clk); } - - clk_enable(priv->clk); } #endif @@ -587,8 +609,7 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev) goto err1; } - error = sh_mobile_lcdc_setup_clocks(&pdev->dev, - pdata->clock_source, priv); + error = sh_mobile_lcdc_setup_clocks(pdev, pdata->clock_source, priv); if (error) { dev_err(&pdev->dev, "unable to setup clocks\n"); goto err1; @@ -697,10 +718,9 @@ static int sh_mobile_lcdc_remove(struct platform_device *pdev) } #ifdef CONFIG_HAVE_CLK - if (priv->clk) { - clk_disable(priv->clk); - clk_put(priv->clk); - } + if (priv->dot_clk) + clk_put(priv->dot_clk); + clk_put(priv->clk); #endif if (priv->base) diff --git a/drivers/video/tmiofb.c b/drivers/video/tmiofb.c index 2a380011e9ba..7baf2dd12d50 100644 --- a/drivers/video/tmiofb.c +++ b/drivers/video/tmiofb.c @@ -222,6 +222,9 @@ static irqreturn_t tmiofb_irq(int irq, void *__info) unsigned int bbisc = tmio_ioread16(par->lcr + LCR_BBISC); + tmio_iowrite16(bbisc, par->lcr + LCR_BBISC); + +#ifdef CONFIG_FB_TMIO_ACCELL /* * We were in polling mode and now we got correct irq. * Switch back to IRQ-based sync of command FIFO @@ -231,9 +234,6 @@ static irqreturn_t tmiofb_irq(int irq, void *__info) par->use_polling = false; } - tmio_iowrite16(bbisc, par->lcr + LCR_BBISC); - -#ifdef CONFIG_FB_TMIO_ACCELL if (bbisc & 1) wake_up(&par->wait_acc); #endif @@ -938,7 +938,9 @@ static void tmiofb_dump_regs(struct platform_device *dev) static int tmiofb_suspend(struct platform_device *dev, pm_message_t state) { struct fb_info *info = platform_get_drvdata(dev); +#ifdef CONFIG_FB_TMIO_ACCELL struct tmiofb_par *par = info->par; +#endif struct mfd_cell *cell = dev->dev.platform_data; int retval = 0; @@ -950,12 +952,14 @@ static int tmiofb_suspend(struct platform_device *dev, pm_message_t state) info->fbops->fb_sync(info); +#ifdef CONFIG_FB_TMIO_ACCELL /* * The fb should be usable even if interrupts are disabled (and they are * during suspend/resume). Switch temporary to forced polling. */ printk(KERN_INFO "tmiofb: switching to polling\n"); par->use_polling = true; +#endif tmiofb_hw_stop(dev); if (cell->suspend) diff --git a/drivers/video/via/viafbdev.c b/drivers/video/via/viafbdev.c index 0132eae06f55..73ac754ad801 100644 --- a/drivers/video/via/viafbdev.c +++ b/drivers/video/via/viafbdev.c @@ -2036,30 +2036,30 @@ static int viafb_vt1636_proc_write(struct file *file, return count; } -static void viafb_init_proc(struct proc_dir_entry *viafb_entry) +static void viafb_init_proc(struct proc_dir_entry **viafb_entry) { struct proc_dir_entry *entry; - viafb_entry = proc_mkdir("viafb", NULL); + *viafb_entry = proc_mkdir("viafb", NULL); if (viafb_entry) { - entry = create_proc_entry("dvp0", 0, viafb_entry); + entry = create_proc_entry("dvp0", 0, *viafb_entry); if (entry) { entry->owner = THIS_MODULE; entry->read_proc = viafb_dvp0_proc_read; entry->write_proc = viafb_dvp0_proc_write; } - entry = create_proc_entry("dvp1", 0, viafb_entry); + entry = create_proc_entry("dvp1", 0, *viafb_entry); if (entry) { entry->owner = THIS_MODULE; entry->read_proc = viafb_dvp1_proc_read; entry->write_proc = viafb_dvp1_proc_write; } - entry = create_proc_entry("dfph", 0, viafb_entry); + entry = create_proc_entry("dfph", 0, *viafb_entry); if (entry) { entry->owner = THIS_MODULE; entry->read_proc = viafb_dfph_proc_read; entry->write_proc = viafb_dfph_proc_write; } - entry = create_proc_entry("dfpl", 0, viafb_entry); + entry = create_proc_entry("dfpl", 0, *viafb_entry); if (entry) { entry->owner = THIS_MODULE; entry->read_proc = viafb_dfpl_proc_read; @@ -2068,7 +2068,7 @@ static void viafb_init_proc(struct proc_dir_entry *viafb_entry) if (VT1636_LVDS == viaparinfo->chip_info->lvds_chip_info. lvds_chip_name || VT1636_LVDS == viaparinfo->chip_info->lvds_chip_info2.lvds_chip_name) { - entry = create_proc_entry("vt1636", 0, viafb_entry); + entry = create_proc_entry("vt1636", 0, *viafb_entry); if (entry) { entry->owner = THIS_MODULE; entry->read_proc = viafb_vt1636_proc_read; @@ -2087,6 +2087,7 @@ static void viafb_remove_proc(struct proc_dir_entry *viafb_entry) remove_proc_entry("dfpl", viafb_entry); remove_proc_entry("vt1636", viafb_entry); remove_proc_entry("vt1625", viafb_entry); + remove_proc_entry("viafb", NULL); } static int __devinit via_pci_probe(void) @@ -2348,7 +2349,7 @@ static int __devinit via_pci_probe(void) viafbinfo->node, viafbinfo->fix.id, default_var.xres, default_var.yres, default_var.bits_per_pixel); - viafb_init_proc(viaparinfo->proc_entry); + viafb_init_proc(&viaparinfo->proc_entry); viafb_init_dac(IGA2); return 0; } diff --git a/drivers/video/xen-fbfront.c b/drivers/video/xen-fbfront.c index a463b3dd837b..2493f05e9f61 100644 --- a/drivers/video/xen-fbfront.c +++ b/drivers/video/xen-fbfront.c @@ -668,7 +668,7 @@ static struct xenbus_device_id xenfb_ids[] = { { "" } }; -static struct xenbus_driver xenfb = { +static struct xenbus_driver xenfb_driver = { .name = "vfb", .owner = THIS_MODULE, .ids = xenfb_ids, @@ -687,12 +687,12 @@ static int __init xenfb_init(void) if (xen_initial_domain()) return -ENODEV; - return xenbus_register_frontend(&xenfb); + return xenbus_register_frontend(&xenfb_driver); } static void __exit xenfb_cleanup(void) { - xenbus_unregister_driver(&xenfb); + xenbus_unregister_driver(&xenfb_driver); } module_init(xenfb_init); diff --git a/drivers/video/xilinxfb.c b/drivers/video/xilinxfb.c index 5da3d2423cc0..40a3a2afbfe7 100644 --- a/drivers/video/xilinxfb.c +++ b/drivers/video/xilinxfb.c @@ -298,8 +298,9 @@ static int xilinxfb_assign(struct device *dev, unsigned long physaddr, /* Put a banner in the log (for DEBUG) */ dev_dbg(dev, "regs: phys=%lx, virt=%p\n", physaddr, drvdata->regs); - dev_dbg(dev, "fb: phys=%p, virt=%p, size=%x\n", - (void*)drvdata->fb_phys, drvdata->fb_virt, fbsize); + dev_dbg(dev, "fb: phys=%llx, virt=%p, size=%x\n", + (unsigned long long) drvdata->fb_phys, drvdata->fb_virt, + fbsize); return 0; /* success */ diff --git a/drivers/w1/masters/Kconfig b/drivers/w1/masters/Kconfig index c4493091c655..90616822cd20 100644 --- a/drivers/w1/masters/Kconfig +++ b/drivers/w1/masters/Kconfig @@ -36,7 +36,7 @@ config W1_MASTER_DS2482 config W1_MASTER_DS1WM tristate "Maxim DS1WM 1-wire busmaster" - depends on W1 && ARM + depends on W1 && ARM && HAVE_CLK help Say Y here to enable the DS1WM 1-wire driver, such as that in HP iPAQ devices like h5xxx, h2200, and ASIC3-based like @@ -52,5 +52,12 @@ config W1_MASTER_GPIO This support is also available as a module. If so, the module will be called w1-gpio.ko. +config HDQ_MASTER_OMAP + tristate "OMAP HDQ driver" + depends on ARCH_OMAP2430 || ARCH_OMAP34XX + help + Say Y here if you want support for the 1-wire or HDQ Interface + on an OMAP processor. + endmenu diff --git a/drivers/w1/masters/Makefile b/drivers/w1/masters/Makefile index 1420b5bbdda8..bc4714a75f3a 100644 --- a/drivers/w1/masters/Makefile +++ b/drivers/w1/masters/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_W1_MASTER_DS2490) += ds2490.o obj-$(CONFIG_W1_MASTER_DS2482) += ds2482.o obj-$(CONFIG_W1_MASTER_DS1WM) += ds1wm.o obj-$(CONFIG_W1_MASTER_GPIO) += w1-gpio.o +obj-$(CONFIG_HDQ_MASTER_OMAP) += omap_hdq.o diff --git a/drivers/w1/masters/omap_hdq.c b/drivers/w1/masters/omap_hdq.c new file mode 100644 index 000000000000..c973889110c8 --- /dev/null +++ b/drivers/w1/masters/omap_hdq.c @@ -0,0 +1,725 @@ +/* + * drivers/w1/masters/omap_hdq.c + * + * Copyright (C) 2007 Texas Instruments, Inc. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/io.h> + +#include <asm/irq.h> +#include <mach/hardware.h> + +#include "../w1.h" +#include "../w1_int.h" + +#define MOD_NAME "OMAP_HDQ:" + +#define OMAP_HDQ_REVISION 0x00 +#define OMAP_HDQ_TX_DATA 0x04 +#define OMAP_HDQ_RX_DATA 0x08 +#define OMAP_HDQ_CTRL_STATUS 0x0c +#define OMAP_HDQ_CTRL_STATUS_INTERRUPTMASK (1<<6) +#define OMAP_HDQ_CTRL_STATUS_CLOCKENABLE (1<<5) +#define OMAP_HDQ_CTRL_STATUS_GO (1<<4) +#define OMAP_HDQ_CTRL_STATUS_INITIALIZATION (1<<2) +#define OMAP_HDQ_CTRL_STATUS_DIR (1<<1) +#define OMAP_HDQ_CTRL_STATUS_MODE (1<<0) +#define OMAP_HDQ_INT_STATUS 0x10 +#define OMAP_HDQ_INT_STATUS_TXCOMPLETE (1<<2) +#define OMAP_HDQ_INT_STATUS_RXCOMPLETE (1<<1) +#define OMAP_HDQ_INT_STATUS_TIMEOUT (1<<0) +#define OMAP_HDQ_SYSCONFIG 0x14 +#define OMAP_HDQ_SYSCONFIG_SOFTRESET (1<<1) +#define OMAP_HDQ_SYSCONFIG_AUTOIDLE (1<<0) +#define OMAP_HDQ_SYSSTATUS 0x18 +#define OMAP_HDQ_SYSSTATUS_RESETDONE (1<<0) + +#define OMAP_HDQ_FLAG_CLEAR 0 +#define OMAP_HDQ_FLAG_SET 1 +#define OMAP_HDQ_TIMEOUT (HZ/5) + +#define OMAP_HDQ_MAX_USER 4 + +static DECLARE_WAIT_QUEUE_HEAD(hdq_wait_queue); +static int w1_id; + +struct hdq_data { + struct device *dev; + void __iomem *hdq_base; + /* lock status update */ + struct mutex hdq_mutex; + int hdq_usecount; + struct clk *hdq_ick; + struct clk *hdq_fck; + u8 hdq_irqstatus; + /* device lock */ + spinlock_t hdq_spinlock; + /* + * Used to control the call to omap_hdq_get and omap_hdq_put. + * HDQ Protocol: Write the CMD|REG_address first, followed by + * the data wrire or read. + */ + int init_trans; +}; + +static int __init omap_hdq_probe(struct platform_device *pdev); +static int omap_hdq_remove(struct platform_device *pdev); + +static struct platform_driver omap_hdq_driver = { + .probe = omap_hdq_probe, + .remove = omap_hdq_remove, + .driver = { + .name = "omap_hdq", + }, +}; + +static u8 omap_w1_read_byte(void *_hdq); +static void omap_w1_write_byte(void *_hdq, u8 byte); +static u8 omap_w1_reset_bus(void *_hdq); +static void omap_w1_search_bus(void *_hdq, struct w1_master *master_dev, + u8 search_type, w1_slave_found_callback slave_found); + + +static struct w1_bus_master omap_w1_master = { + .read_byte = omap_w1_read_byte, + .write_byte = omap_w1_write_byte, + .reset_bus = omap_w1_reset_bus, + .search = omap_w1_search_bus, +}; + +/* HDQ register I/O routines */ +static inline u8 hdq_reg_in(struct hdq_data *hdq_data, u32 offset) +{ + return __raw_readb(hdq_data->hdq_base + offset); +} + +static inline void hdq_reg_out(struct hdq_data *hdq_data, u32 offset, u8 val) +{ + __raw_writeb(val, hdq_data->hdq_base + offset); +} + +static inline u8 hdq_reg_merge(struct hdq_data *hdq_data, u32 offset, + u8 val, u8 mask) +{ + u8 new_val = (__raw_readb(hdq_data->hdq_base + offset) & ~mask) + | (val & mask); + __raw_writeb(new_val, hdq_data->hdq_base + offset); + + return new_val; +} + +/* + * Wait for one or more bits in flag change. + * HDQ_FLAG_SET: wait until any bit in the flag is set. + * HDQ_FLAG_CLEAR: wait until all bits in the flag are cleared. + * return 0 on success and -ETIMEDOUT in the case of timeout. + */ +static int hdq_wait_for_flag(struct hdq_data *hdq_data, u32 offset, + u8 flag, u8 flag_set, u8 *status) +{ + int ret = 0; + unsigned long timeout = jiffies + OMAP_HDQ_TIMEOUT; + + if (flag_set == OMAP_HDQ_FLAG_CLEAR) { + /* wait for the flag clear */ + while (((*status = hdq_reg_in(hdq_data, offset)) & flag) + && time_before(jiffies, timeout)) { + schedule_timeout_uninterruptible(1); + } + if (*status & flag) + ret = -ETIMEDOUT; + } else if (flag_set == OMAP_HDQ_FLAG_SET) { + /* wait for the flag set */ + while (!((*status = hdq_reg_in(hdq_data, offset)) & flag) + && time_before(jiffies, timeout)) { + schedule_timeout_uninterruptible(1); + } + if (!(*status & flag)) + ret = -ETIMEDOUT; + } else + return -EINVAL; + + return ret; +} + +/* write out a byte and fill *status with HDQ_INT_STATUS */ +static int hdq_write_byte(struct hdq_data *hdq_data, u8 val, u8 *status) +{ + int ret; + u8 tmp_status; + unsigned long irqflags; + + *status = 0; + + spin_lock_irqsave(&hdq_data->hdq_spinlock, irqflags); + /* clear interrupt flags via a dummy read */ + hdq_reg_in(hdq_data, OMAP_HDQ_INT_STATUS); + /* ISR loads it with new INT_STATUS */ + hdq_data->hdq_irqstatus = 0; + spin_unlock_irqrestore(&hdq_data->hdq_spinlock, irqflags); + + hdq_reg_out(hdq_data, OMAP_HDQ_TX_DATA, val); + + /* set the GO bit */ + hdq_reg_merge(hdq_data, OMAP_HDQ_CTRL_STATUS, OMAP_HDQ_CTRL_STATUS_GO, + OMAP_HDQ_CTRL_STATUS_DIR | OMAP_HDQ_CTRL_STATUS_GO); + /* wait for the TXCOMPLETE bit */ + ret = wait_event_timeout(hdq_wait_queue, + hdq_data->hdq_irqstatus, OMAP_HDQ_TIMEOUT); + if (ret == 0) { + dev_dbg(hdq_data->dev, "TX wait elapsed\n"); + goto out; + } + + *status = hdq_data->hdq_irqstatus; + /* check irqstatus */ + if (!(*status & OMAP_HDQ_INT_STATUS_TXCOMPLETE)) { + dev_dbg(hdq_data->dev, "timeout waiting for" + "TXCOMPLETE/RXCOMPLETE, %x", *status); + ret = -ETIMEDOUT; + goto out; + } + + /* wait for the GO bit return to zero */ + ret = hdq_wait_for_flag(hdq_data, OMAP_HDQ_CTRL_STATUS, + OMAP_HDQ_CTRL_STATUS_GO, + OMAP_HDQ_FLAG_CLEAR, &tmp_status); + if (ret) { + dev_dbg(hdq_data->dev, "timeout waiting GO bit" + "return to zero, %x", tmp_status); + } + +out: + return ret; +} + +/* HDQ Interrupt service routine */ +static irqreturn_t hdq_isr(int irq, void *_hdq) +{ + struct hdq_data *hdq_data = _hdq; + unsigned long irqflags; + + spin_lock_irqsave(&hdq_data->hdq_spinlock, irqflags); + hdq_data->hdq_irqstatus = hdq_reg_in(hdq_data, OMAP_HDQ_INT_STATUS); + spin_unlock_irqrestore(&hdq_data->hdq_spinlock, irqflags); + dev_dbg(hdq_data->dev, "hdq_isr: %x", hdq_data->hdq_irqstatus); + + if (hdq_data->hdq_irqstatus & + (OMAP_HDQ_INT_STATUS_TXCOMPLETE | OMAP_HDQ_INT_STATUS_RXCOMPLETE + | OMAP_HDQ_INT_STATUS_TIMEOUT)) { + /* wake up sleeping process */ + wake_up(&hdq_wait_queue); + } + + return IRQ_HANDLED; +} + +/* HDQ Mode: always return success */ +static u8 omap_w1_reset_bus(void *_hdq) +{ + return 0; +} + +/* W1 search callback function */ +static void omap_w1_search_bus(void *_hdq, struct w1_master *master_dev, + u8 search_type, w1_slave_found_callback slave_found) +{ + u64 module_id, rn_le, cs, id; + + if (w1_id) + module_id = w1_id; + else + module_id = 0x1; + + rn_le = cpu_to_le64(module_id); + /* + * HDQ might not obey truly the 1-wire spec. + * So calculate CRC based on module parameter. + */ + cs = w1_calc_crc8((u8 *)&rn_le, 7); + id = (cs << 56) | module_id; + + slave_found(master_dev, id); +} + +static int _omap_hdq_reset(struct hdq_data *hdq_data) +{ + int ret; + u8 tmp_status; + + hdq_reg_out(hdq_data, OMAP_HDQ_SYSCONFIG, OMAP_HDQ_SYSCONFIG_SOFTRESET); + /* + * Select HDQ mode & enable clocks. + * It is observed that INT flags can't be cleared via a read and GO/INIT + * won't return to zero if interrupt is disabled. So we always enable + * interrupt. + */ + hdq_reg_out(hdq_data, OMAP_HDQ_CTRL_STATUS, + OMAP_HDQ_CTRL_STATUS_CLOCKENABLE | + OMAP_HDQ_CTRL_STATUS_INTERRUPTMASK); + + /* wait for reset to complete */ + ret = hdq_wait_for_flag(hdq_data, OMAP_HDQ_SYSSTATUS, + OMAP_HDQ_SYSSTATUS_RESETDONE, OMAP_HDQ_FLAG_SET, &tmp_status); + if (ret) + dev_dbg(hdq_data->dev, "timeout waiting HDQ reset, %x", + tmp_status); + else { + hdq_reg_out(hdq_data, OMAP_HDQ_CTRL_STATUS, + OMAP_HDQ_CTRL_STATUS_CLOCKENABLE | + OMAP_HDQ_CTRL_STATUS_INTERRUPTMASK); + hdq_reg_out(hdq_data, OMAP_HDQ_SYSCONFIG, + OMAP_HDQ_SYSCONFIG_AUTOIDLE); + } + + return ret; +} + +/* Issue break pulse to the device */ +static int omap_hdq_break(struct hdq_data *hdq_data) +{ + int ret = 0; + u8 tmp_status; + unsigned long irqflags; + + ret = mutex_lock_interruptible(&hdq_data->hdq_mutex); + if (ret < 0) { + dev_dbg(hdq_data->dev, "Could not acquire mutex\n"); + ret = -EINTR; + goto rtn; + } + + spin_lock_irqsave(&hdq_data->hdq_spinlock, irqflags); + /* clear interrupt flags via a dummy read */ + hdq_reg_in(hdq_data, OMAP_HDQ_INT_STATUS); + /* ISR loads it with new INT_STATUS */ + hdq_data->hdq_irqstatus = 0; + spin_unlock_irqrestore(&hdq_data->hdq_spinlock, irqflags); + + /* set the INIT and GO bit */ + hdq_reg_merge(hdq_data, OMAP_HDQ_CTRL_STATUS, + OMAP_HDQ_CTRL_STATUS_INITIALIZATION | OMAP_HDQ_CTRL_STATUS_GO, + OMAP_HDQ_CTRL_STATUS_DIR | OMAP_HDQ_CTRL_STATUS_INITIALIZATION | + OMAP_HDQ_CTRL_STATUS_GO); + + /* wait for the TIMEOUT bit */ + ret = wait_event_timeout(hdq_wait_queue, + hdq_data->hdq_irqstatus, OMAP_HDQ_TIMEOUT); + if (ret == 0) { + dev_dbg(hdq_data->dev, "break wait elapsed\n"); + ret = -EINTR; + goto out; + } + + tmp_status = hdq_data->hdq_irqstatus; + /* check irqstatus */ + if (!(tmp_status & OMAP_HDQ_INT_STATUS_TIMEOUT)) { + dev_dbg(hdq_data->dev, "timeout waiting for TIMEOUT, %x", + tmp_status); + ret = -ETIMEDOUT; + goto out; + } + /* + * wait for both INIT and GO bits rerurn to zero. + * zero wait time expected for interrupt mode. + */ + ret = hdq_wait_for_flag(hdq_data, OMAP_HDQ_CTRL_STATUS, + OMAP_HDQ_CTRL_STATUS_INITIALIZATION | + OMAP_HDQ_CTRL_STATUS_GO, OMAP_HDQ_FLAG_CLEAR, + &tmp_status); + if (ret) + dev_dbg(hdq_data->dev, "timeout waiting INIT&GO bits" + "return to zero, %x", tmp_status); + +out: + mutex_unlock(&hdq_data->hdq_mutex); +rtn: + return ret; +} + +static int hdq_read_byte(struct hdq_data *hdq_data, u8 *val) +{ + int ret = 0; + u8 status; + unsigned long timeout = jiffies + OMAP_HDQ_TIMEOUT; + + ret = mutex_lock_interruptible(&hdq_data->hdq_mutex); + if (ret < 0) { + ret = -EINTR; + goto rtn; + } + + if (!hdq_data->hdq_usecount) { + ret = -EINVAL; + goto out; + } + + if (!(hdq_data->hdq_irqstatus & OMAP_HDQ_INT_STATUS_RXCOMPLETE)) { + hdq_reg_merge(hdq_data, OMAP_HDQ_CTRL_STATUS, + OMAP_HDQ_CTRL_STATUS_DIR | OMAP_HDQ_CTRL_STATUS_GO, + OMAP_HDQ_CTRL_STATUS_DIR | OMAP_HDQ_CTRL_STATUS_GO); + /* + * The RX comes immediately after TX. It + * triggers another interrupt before we + * sleep. So we have to wait for RXCOMPLETE bit. + */ + while (!(hdq_data->hdq_irqstatus + & OMAP_HDQ_INT_STATUS_RXCOMPLETE) + && time_before(jiffies, timeout)) { + schedule_timeout_uninterruptible(1); + } + hdq_reg_merge(hdq_data, OMAP_HDQ_CTRL_STATUS, 0, + OMAP_HDQ_CTRL_STATUS_DIR); + status = hdq_data->hdq_irqstatus; + /* check irqstatus */ + if (!(status & OMAP_HDQ_INT_STATUS_RXCOMPLETE)) { + dev_dbg(hdq_data->dev, "timeout waiting for" + "RXCOMPLETE, %x", status); + ret = -ETIMEDOUT; + goto out; + } + } + /* the data is ready. Read it in! */ + *val = hdq_reg_in(hdq_data, OMAP_HDQ_RX_DATA); +out: + mutex_unlock(&hdq_data->hdq_mutex); +rtn: + return 0; + +} + +/* Enable clocks and set the controller to HDQ mode */ +static int omap_hdq_get(struct hdq_data *hdq_data) +{ + int ret = 0; + + ret = mutex_lock_interruptible(&hdq_data->hdq_mutex); + if (ret < 0) { + ret = -EINTR; + goto rtn; + } + + if (OMAP_HDQ_MAX_USER == hdq_data->hdq_usecount) { + dev_dbg(hdq_data->dev, "attempt to exceed the max use count"); + ret = -EINVAL; + goto out; + } else { + hdq_data->hdq_usecount++; + try_module_get(THIS_MODULE); + if (1 == hdq_data->hdq_usecount) { + if (clk_enable(hdq_data->hdq_ick)) { + dev_dbg(hdq_data->dev, "Can not enable ick\n"); + ret = -ENODEV; + goto clk_err; + } + if (clk_enable(hdq_data->hdq_fck)) { + dev_dbg(hdq_data->dev, "Can not enable fck\n"); + clk_disable(hdq_data->hdq_ick); + ret = -ENODEV; + goto clk_err; + } + + /* make sure HDQ is out of reset */ + if (!(hdq_reg_in(hdq_data, OMAP_HDQ_SYSSTATUS) & + OMAP_HDQ_SYSSTATUS_RESETDONE)) { + ret = _omap_hdq_reset(hdq_data); + if (ret) + /* back up the count */ + hdq_data->hdq_usecount--; + } else { + /* select HDQ mode & enable clocks */ + hdq_reg_out(hdq_data, OMAP_HDQ_CTRL_STATUS, + OMAP_HDQ_CTRL_STATUS_CLOCKENABLE | + OMAP_HDQ_CTRL_STATUS_INTERRUPTMASK); + hdq_reg_out(hdq_data, OMAP_HDQ_SYSCONFIG, + OMAP_HDQ_SYSCONFIG_AUTOIDLE); + hdq_reg_in(hdq_data, OMAP_HDQ_INT_STATUS); + } + } + } + +clk_err: + clk_put(hdq_data->hdq_ick); + clk_put(hdq_data->hdq_fck); +out: + mutex_unlock(&hdq_data->hdq_mutex); +rtn: + return ret; +} + +/* Disable clocks to the module */ +static int omap_hdq_put(struct hdq_data *hdq_data) +{ + int ret = 0; + + ret = mutex_lock_interruptible(&hdq_data->hdq_mutex); + if (ret < 0) + return -EINTR; + + if (0 == hdq_data->hdq_usecount) { + dev_dbg(hdq_data->dev, "attempt to decrement use count" + "when it is zero"); + ret = -EINVAL; + } else { + hdq_data->hdq_usecount--; + module_put(THIS_MODULE); + if (0 == hdq_data->hdq_usecount) { + clk_disable(hdq_data->hdq_ick); + clk_disable(hdq_data->hdq_fck); + } + } + mutex_unlock(&hdq_data->hdq_mutex); + + return ret; +} + +/* Read a byte of data from the device */ +static u8 omap_w1_read_byte(void *_hdq) +{ + struct hdq_data *hdq_data = _hdq; + u8 val = 0; + int ret; + + ret = hdq_read_byte(hdq_data, &val); + if (ret) { + ret = mutex_lock_interruptible(&hdq_data->hdq_mutex); + if (ret < 0) { + dev_dbg(hdq_data->dev, "Could not acquire mutex\n"); + return -EINTR; + } + hdq_data->init_trans = 0; + mutex_unlock(&hdq_data->hdq_mutex); + omap_hdq_put(hdq_data); + return -1; + } + + /* Write followed by a read, release the module */ + if (hdq_data->init_trans) { + ret = mutex_lock_interruptible(&hdq_data->hdq_mutex); + if (ret < 0) { + dev_dbg(hdq_data->dev, "Could not acquire mutex\n"); + return -EINTR; + } + hdq_data->init_trans = 0; + mutex_unlock(&hdq_data->hdq_mutex); + omap_hdq_put(hdq_data); + } + + return val; +} + +/* Write a byte of data to the device */ +static void omap_w1_write_byte(void *_hdq, u8 byte) +{ + struct hdq_data *hdq_data = _hdq; + int ret; + u8 status; + + /* First write to initialize the transfer */ + if (hdq_data->init_trans == 0) + omap_hdq_get(hdq_data); + + ret = mutex_lock_interruptible(&hdq_data->hdq_mutex); + if (ret < 0) { + dev_dbg(hdq_data->dev, "Could not acquire mutex\n"); + return; + } + hdq_data->init_trans++; + mutex_unlock(&hdq_data->hdq_mutex); + + ret = hdq_write_byte(hdq_data, byte, &status); + if (ret == 0) { + dev_dbg(hdq_data->dev, "TX failure:Ctrl status %x\n", status); + return; + } + + /* Second write, data transfered. Release the module */ + if (hdq_data->init_trans > 1) { + omap_hdq_put(hdq_data); + ret = mutex_lock_interruptible(&hdq_data->hdq_mutex); + if (ret < 0) { + dev_dbg(hdq_data->dev, "Could not acquire mutex\n"); + return; + } + hdq_data->init_trans = 0; + mutex_unlock(&hdq_data->hdq_mutex); + } + + return; +} + +static int __init omap_hdq_probe(struct platform_device *pdev) +{ + struct hdq_data *hdq_data; + struct resource *res; + int ret, irq; + u8 rev; + + hdq_data = kmalloc(sizeof(*hdq_data), GFP_KERNEL); + if (!hdq_data) { + dev_dbg(&pdev->dev, "unable to allocate memory\n"); + ret = -ENOMEM; + goto err_kmalloc; + } + + hdq_data->dev = &pdev->dev; + platform_set_drvdata(pdev, hdq_data); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_dbg(&pdev->dev, "unable to get resource\n"); + ret = -ENXIO; + goto err_resource; + } + + hdq_data->hdq_base = ioremap(res->start, SZ_4K); + if (!hdq_data->hdq_base) { + dev_dbg(&pdev->dev, "ioremap failed\n"); + ret = -EINVAL; + goto err_ioremap; + } + + /* get interface & functional clock objects */ + hdq_data->hdq_ick = clk_get(&pdev->dev, "hdq_ick"); + hdq_data->hdq_fck = clk_get(&pdev->dev, "hdq_fck"); + + if (IS_ERR(hdq_data->hdq_ick) || IS_ERR(hdq_data->hdq_fck)) { + dev_dbg(&pdev->dev, "Can't get HDQ clock objects\n"); + if (IS_ERR(hdq_data->hdq_ick)) { + ret = PTR_ERR(hdq_data->hdq_ick); + goto err_clk; + } + if (IS_ERR(hdq_data->hdq_fck)) { + ret = PTR_ERR(hdq_data->hdq_fck); + clk_put(hdq_data->hdq_ick); + goto err_clk; + } + } + + hdq_data->hdq_usecount = 0; + mutex_init(&hdq_data->hdq_mutex); + + if (clk_enable(hdq_data->hdq_ick)) { + dev_dbg(&pdev->dev, "Can not enable ick\n"); + ret = -ENODEV; + goto err_intfclk; + } + + if (clk_enable(hdq_data->hdq_fck)) { + dev_dbg(&pdev->dev, "Can not enable fck\n"); + ret = -ENODEV; + goto err_fnclk; + } + + rev = hdq_reg_in(hdq_data, OMAP_HDQ_REVISION); + dev_info(&pdev->dev, "OMAP HDQ Hardware Rev %c.%c. Driver in %s mode\n", + (rev >> 4) + '0', (rev & 0x0f) + '0', "Interrupt"); + + spin_lock_init(&hdq_data->hdq_spinlock); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + ret = -ENXIO; + goto err_irq; + } + + ret = request_irq(irq, hdq_isr, IRQF_DISABLED, "omap_hdq", hdq_data); + if (ret < 0) { + dev_dbg(&pdev->dev, "could not request irq\n"); + goto err_irq; + } + + omap_hdq_break(hdq_data); + + /* don't clock the HDQ until it is needed */ + clk_disable(hdq_data->hdq_ick); + clk_disable(hdq_data->hdq_fck); + + omap_w1_master.data = hdq_data; + + ret = w1_add_master_device(&omap_w1_master); + if (ret) { + dev_dbg(&pdev->dev, "Failure in registering w1 master\n"); + goto err_w1; + } + + return 0; + +err_w1: +err_irq: + clk_disable(hdq_data->hdq_fck); + +err_fnclk: + clk_disable(hdq_data->hdq_ick); + +err_intfclk: + clk_put(hdq_data->hdq_ick); + clk_put(hdq_data->hdq_fck); + +err_clk: + iounmap(hdq_data->hdq_base); + +err_ioremap: +err_resource: + platform_set_drvdata(pdev, NULL); + kfree(hdq_data); + +err_kmalloc: + return ret; + +} + +static int omap_hdq_remove(struct platform_device *pdev) +{ + struct hdq_data *hdq_data = platform_get_drvdata(pdev); + + mutex_lock(&hdq_data->hdq_mutex); + + if (hdq_data->hdq_usecount) { + dev_dbg(&pdev->dev, "removed when use count is not zero\n"); + return -EBUSY; + } + + mutex_unlock(&hdq_data->hdq_mutex); + + /* remove module dependency */ + clk_put(hdq_data->hdq_ick); + clk_put(hdq_data->hdq_fck); + free_irq(INT_24XX_HDQ_IRQ, hdq_data); + platform_set_drvdata(pdev, NULL); + iounmap(hdq_data->hdq_base); + kfree(hdq_data); + + return 0; +} + +static int __init +omap_hdq_init(void) +{ + return platform_driver_register(&omap_hdq_driver); +} +module_init(omap_hdq_init); + +static void __exit +omap_hdq_exit(void) +{ + platform_driver_unregister(&omap_hdq_driver); +} +module_exit(omap_hdq_exit); + +module_param(w1_id, int, S_IRUSR); +MODULE_PARM_DESC(w1_id, "1-wire id for the slave detection"); + +MODULE_AUTHOR("Texas Instruments"); +MODULE_DESCRIPTION("HDQ driver Library"); +MODULE_LICENSE("GPL"); diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig index 3df29a122f84..8d0b1fb1e52e 100644 --- a/drivers/w1/slaves/Kconfig +++ b/drivers/w1/slaves/Kconfig @@ -44,4 +44,11 @@ config W1_SLAVE_DS2760 If you are unsure, say N. +config W1_SLAVE_BQ27000 + tristate "BQ27000 slave support" + depends on W1 + help + Say Y here if you want to use a hdq + bq27000 slave support. + endmenu diff --git a/drivers/w1/slaves/Makefile b/drivers/w1/slaves/Makefile index a8eb7524df1d..990f400b6d22 100644 --- a/drivers/w1/slaves/Makefile +++ b/drivers/w1/slaves/Makefile @@ -6,4 +6,4 @@ obj-$(CONFIG_W1_SLAVE_THERM) += w1_therm.o obj-$(CONFIG_W1_SLAVE_SMEM) += w1_smem.o obj-$(CONFIG_W1_SLAVE_DS2433) += w1_ds2433.o obj-$(CONFIG_W1_SLAVE_DS2760) += w1_ds2760.o - +obj-$(CONFIG_W1_SLAVE_BQ27000) += w1_bq27000.o diff --git a/drivers/w1/slaves/w1_bq27000.c b/drivers/w1/slaves/w1_bq27000.c new file mode 100644 index 000000000000..8f4c91f6c680 --- /dev/null +++ b/drivers/w1/slaves/w1_bq27000.c @@ -0,0 +1,123 @@ +/* + * drivers/w1/slaves/w1_bq27000.c + * + * Copyright (C) 2007 Texas Instruments, Inc. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/types.h> +#include <linux/platform_device.h> +#include <linux/mutex.h> + +#include "../w1.h" +#include "../w1_int.h" +#include "../w1_family.h" + +#define HDQ_CMD_READ (0) +#define HDQ_CMD_WRITE (1<<7) + +static int F_ID; + +void w1_bq27000_write(struct device *dev, u8 buf, u8 reg) +{ + struct w1_slave *sl = container_of(dev, struct w1_slave, dev); + + if (!dev) { + pr_info("Could not obtain slave dev ptr\n"); + return; + } + + w1_write_8(sl->master, HDQ_CMD_WRITE | reg); + w1_write_8(sl->master, buf); +} +EXPORT_SYMBOL(w1_bq27000_write); + +int w1_bq27000_read(struct device *dev, u8 reg) +{ + u8 val; + struct w1_slave *sl = container_of(dev, struct w1_slave, dev); + + if (!dev) + return 0; + + w1_write_8(sl->master, HDQ_CMD_READ | reg); + val = w1_read_8(sl->master); + + return val; +} +EXPORT_SYMBOL(w1_bq27000_read); + +static int w1_bq27000_add_slave(struct w1_slave *sl) +{ + int ret; + int id = 1; + struct platform_device *pdev; + + pdev = platform_device_alloc("bq27000-battery", id); + if (!pdev) { + ret = -ENOMEM; + return ret; + } + pdev->dev.parent = &sl->dev; + + ret = platform_device_add(pdev); + if (ret) + goto pdev_add_failed; + + dev_set_drvdata(&sl->dev, pdev); + + goto success; + +pdev_add_failed: + platform_device_unregister(pdev); +success: + return ret; +} + +static void w1_bq27000_remove_slave(struct w1_slave *sl) +{ + struct platform_device *pdev = dev_get_drvdata(&sl->dev); + + platform_device_unregister(pdev); +} + +static struct w1_family_ops w1_bq27000_fops = { + .add_slave = w1_bq27000_add_slave, + .remove_slave = w1_bq27000_remove_slave, +}; + +static struct w1_family w1_bq27000_family = { + .fid = 1, + .fops = &w1_bq27000_fops, +}; + +static int __init w1_bq27000_init(void) +{ + if (F_ID) + w1_bq27000_family.fid = F_ID; + + return w1_register_family(&w1_bq27000_family); +} + +static void __exit w1_bq27000_exit(void) +{ + w1_unregister_family(&w1_bq27000_family); +} + + +module_init(w1_bq27000_init); +module_exit(w1_bq27000_exit); + +module_param(F_ID, int, S_IRUSR); +MODULE_PARM_DESC(F_ID, "1-wire slave FID for BQ device"); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Texas Instruments Ltd"); +MODULE_DESCRIPTION("HDQ/1-wire slave driver bq27000 battery monitor chip"); diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c index 3b615d4022ee..acc7e3b7fe17 100644 --- a/drivers/w1/w1.c +++ b/drivers/w1/w1.c @@ -197,7 +197,7 @@ struct device_driver w1_master_driver = { struct device w1_master_device = { .parent = NULL, .bus = &w1_bus_type, - .bus_id = "w1 bus master", + .init_name = "w1 bus master", .driver = &w1_master_driver, .release = &w1_master_release }; @@ -211,7 +211,7 @@ static struct device_driver w1_slave_driver = { struct device w1_slave_device = { .parent = NULL, .bus = &w1_bus_type, - .bus_id = "w1 bus slave", + .init_name = "w1 bus slave", .driver = &w1_slave_driver, .release = &w1_slave_release }; @@ -573,7 +573,7 @@ static int w1_uevent(struct device *dev, struct kobj_uevent_env *env) } dev_dbg(dev, "Hotplug event for %s %s, bus_id=%s.\n", - event_owner, name, dev->bus_id); + event_owner, name, dev_name(dev)); if (dev->driver != &w1_slave_driver || !sl) return 0; @@ -605,8 +605,7 @@ static int __w1_attach_slave_device(struct w1_slave *sl) sl->dev.bus = &w1_bus_type; sl->dev.release = &w1_slave_release; - snprintf(&sl->dev.bus_id[0], sizeof(sl->dev.bus_id), - "%02x-%012llx", + dev_set_name(&sl->dev, "%02x-%012llx", (unsigned int) sl->reg_num.family, (unsigned long long) sl->reg_num.id); snprintf(&sl->name[0], sizeof(sl->name), @@ -615,13 +614,13 @@ static int __w1_attach_slave_device(struct w1_slave *sl) (unsigned long long) sl->reg_num.id); dev_dbg(&sl->dev, "%s: registering %s as %p.\n", __func__, - &sl->dev.bus_id[0], sl); + dev_name(&sl->dev), sl); err = device_register(&sl->dev); if (err < 0) { dev_err(&sl->dev, "Device registration [%s] failed. err=%d\n", - sl->dev.bus_id, err); + dev_name(&sl->dev), err); return err; } @@ -630,7 +629,7 @@ static int __w1_attach_slave_device(struct w1_slave *sl) if (err < 0) { dev_err(&sl->dev, "sysfs file creation for [%s] failed. err=%d\n", - sl->dev.bus_id, err); + dev_name(&sl->dev), err); goto out_unreg; } @@ -639,7 +638,7 @@ static int __w1_attach_slave_device(struct w1_slave *sl) if (err < 0) { dev_err(&sl->dev, "sysfs file creation for [%s] failed. err=%d\n", - sl->dev.bus_id, err); + dev_name(&sl->dev), err); goto out_rem1; } @@ -648,7 +647,7 @@ static int __w1_attach_slave_device(struct w1_slave *sl) ((err = sl->family->fops->add_slave(sl)) < 0)) { dev_err(&sl->dev, "sysfs file creation for [%s] failed. err=%d\n", - sl->dev.bus_id, err); + dev_name(&sl->dev), err); goto out_rem2; } diff --git a/drivers/w1/w1.h b/drivers/w1/w1.h index cdaa6fffbfc7..97304bd83ec9 100644 --- a/drivers/w1/w1.h +++ b/drivers/w1/w1.h @@ -206,6 +206,7 @@ void w1_slave_detach(struct w1_slave *sl); u8 w1_triplet(struct w1_master *dev, int bdir); void w1_write_8(struct w1_master *, u8); +u8 w1_read_8(struct w1_master *); int w1_reset_bus(struct w1_master *); u8 w1_calc_crc8(u8 *, int); void w1_write_block(struct w1_master *, const u8 *, int); diff --git a/drivers/w1/w1_int.c b/drivers/w1/w1_int.c index a3a54567bfba..4a46ed58ece9 100644 --- a/drivers/w1/w1_int.c +++ b/drivers/w1/w1_int.c @@ -75,8 +75,7 @@ static struct w1_master * w1_alloc_dev(u32 id, int slave_count, int slave_ttl, mutex_init(&dev->mutex); memcpy(&dev->dev, device, sizeof(struct device)); - snprintf(dev->dev.bus_id, sizeof(dev->dev.bus_id), - "w1_bus_master%u", dev->id); + dev_set_name(&dev->dev, "w1_bus_master%u", dev->id); snprintf(dev->name, sizeof(dev->name), "w1_bus_master%u", dev->id); dev->driver = driver; diff --git a/drivers/w1/w1_io.c b/drivers/w1/w1_io.c index f4f82f1f486e..0d15b0eaf79a 100644 --- a/drivers/w1/w1_io.c +++ b/drivers/w1/w1_io.c @@ -217,7 +217,7 @@ u8 w1_triplet(struct w1_master *dev, int bdir) * @param dev the master device * @return the byte read */ -static u8 w1_read_8(struct w1_master * dev) +u8 w1_read_8(struct w1_master *dev) { int i; u8 res = 0; @@ -230,6 +230,7 @@ static u8 w1_read_8(struct w1_master * dev) return res; } +EXPORT_SYMBOL_GPL(w1_read_8); /** * Writes a series of bytes. diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 1a22fe782a27..4fd3fa5546b1 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -67,11 +67,11 @@ config AT91RM9200_WATCHDOG system when the timeout is reached. config AT91SAM9X_WATCHDOG - tristate "AT91SAM9X watchdog" - depends on WATCHDOG && (ARCH_AT91SAM9260 || ARCH_AT91SAM9261) + tristate "AT91SAM9X / AT91CAP9 watchdog" + depends on ARCH_AT91 && !ARCH_AT91RM9200 help - Watchdog timer embedded into AT91SAM9X chips. This will reboot your - system when the timeout is reached. + Watchdog timer embedded into AT91SAM9X and AT91CAP9 chips. This will + reboot your system when the timeout is reached. config 21285_WATCHDOG tristate "DC21285 watchdog" diff --git a/drivers/watchdog/at91sam9_wdt.c b/drivers/watchdog/at91sam9_wdt.c index b4babfc31586..b1da287f90ec 100644 --- a/drivers/watchdog/at91sam9_wdt.c +++ b/drivers/watchdog/at91sam9_wdt.c @@ -30,7 +30,7 @@ #include <linux/bitops.h> #include <linux/uaccess.h> -#include <asm/arch/at91_wdt.h> +#include <mach/at91_wdt.h> #define DRV_NAME "AT91SAM9 Watchdog" diff --git a/drivers/watchdog/booke_wdt.c b/drivers/watchdog/booke_wdt.c index c3b78a76f173..225398fd5049 100644 --- a/drivers/watchdog/booke_wdt.c +++ b/drivers/watchdog/booke_wdt.c @@ -42,8 +42,10 @@ u32 booke_wdt_period = WDT_PERIOD_DEFAULT; #ifdef CONFIG_FSL_BOOKE #define WDTP(x) ((((63-x)&0x3)<<30)|(((63-x)&0x3c)<<15)) +#define WDTP_MASK (WDTP(0)) #else #define WDTP(x) (TCR_WP(x)) +#define WDTP_MASK (TCR_WP_MASK) #endif static DEFINE_SPINLOCK(booke_wdt_lock); @@ -65,6 +67,7 @@ static void __booke_wdt_enable(void *data) /* clear status before enabling watchdog */ __booke_wdt_ping(NULL); val = mfspr(SPRN_TCR); + val &= ~WDTP_MASK; val |= (TCR_WIE|TCR_WRC(WRC_CHIP)|WDTP(booke_wdt_period)); mtspr(SPRN_TCR, val); @@ -114,7 +117,7 @@ static long booke_wdt_ioctl(struct file *file, case WDIOC_SETTIMEOUT: if (get_user(booke_wdt_period, p)) return -EFAULT; - mtspr(SPRN_TCR, (mfspr(SPRN_TCR) & ~WDTP(0)) | + mtspr(SPRN_TCR, (mfspr(SPRN_TCR) & ~WDTP_MASK) | WDTP(booke_wdt_period)); return 0; case WDIOC_GETTIMEOUT: diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c index f7f6ce82a5e2..e31925ee8346 100644 --- a/drivers/watchdog/s3c2410_wdt.c +++ b/drivers/watchdog/s3c2410_wdt.c @@ -42,7 +42,7 @@ #undef S3C_VA_WATCHDOG #define S3C_VA_WATCHDOG (0) -#include <asm/plat-s3c/regs-watchdog.h> +#include <plat/regs-watchdog.h> #define PFX "s3c2410-wdt: " diff --git a/drivers/watchdog/sa1100_wdt.c b/drivers/watchdog/sa1100_wdt.c index ed01e4c2beff..d6fbb4657210 100644 --- a/drivers/watchdog/sa1100_wdt.c +++ b/drivers/watchdog/sa1100_wdt.c @@ -27,6 +27,7 @@ #include <linux/init.h> #include <linux/bitops.h> #include <linux/uaccess.h> +#include <linux/timex.h> #ifdef CONFIG_ARCH_PXA #include <mach/pxa-regs.h> diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c index 8c83abc73400..526c191e84ea 100644 --- a/drivers/xen/balloon.c +++ b/drivers/xen/balloon.c @@ -41,7 +41,6 @@ #include <linux/pagemap.h> #include <linux/highmem.h> #include <linux/mutex.h> -#include <linux/highmem.h> #include <linux/list.h> #include <linux/sysdev.h> @@ -123,14 +122,7 @@ static struct timer_list balloon_timer; static void scrub_page(struct page *page) { #ifdef CONFIG_XEN_SCRUB_PAGES - if (PageHighMem(page)) { - void *v = kmap(page); - clear_page(v); - kunmap(v); - } else { - void *v = page_address(page); - clear_page(v); - } + clear_highpage(page); #endif } diff --git a/drivers/xen/xenbus/xenbus_probe.c b/drivers/xen/xenbus/xenbus_probe.c index 7f24a98a446f..b2a03184a246 100644 --- a/drivers/xen/xenbus/xenbus_probe.c +++ b/drivers/xen/xenbus/xenbus_probe.c @@ -99,15 +99,15 @@ static int xenbus_uevent(struct device *_dev, struct kobj_uevent_env *env) } /* device/<type>/<id> => <type>-<id> */ -static int frontend_bus_id(char bus_id[BUS_ID_SIZE], const char *nodename) +static int frontend_bus_id(char bus_id[XEN_BUS_ID_SIZE], const char *nodename) { nodename = strchr(nodename, '/'); - if (!nodename || strlen(nodename + 1) >= BUS_ID_SIZE) { + if (!nodename || strlen(nodename + 1) >= XEN_BUS_ID_SIZE) { printk(KERN_WARNING "XENBUS: bad frontend %s\n", nodename); return -EINVAL; } - strlcpy(bus_id, nodename + 1, BUS_ID_SIZE); + strlcpy(bus_id, nodename + 1, XEN_BUS_ID_SIZE); if (!strchr(bus_id, '/')) { printk(KERN_WARNING "XENBUS: bus_id %s no slash\n", bus_id); return -EINVAL; @@ -460,6 +460,7 @@ int xenbus_probe_node(struct xen_bus_type *bus, const char *type, const char *nodename) { + char devname[XEN_BUS_ID_SIZE]; int err; struct xenbus_device *xendev; size_t stringlen; @@ -494,10 +495,12 @@ int xenbus_probe_node(struct xen_bus_type *bus, xendev->dev.bus = &bus->bus; xendev->dev.release = xenbus_dev_release; - err = bus->get_bus_id(xendev->dev.bus_id, xendev->nodename); + err = bus->get_bus_id(devname, xendev->nodename); if (err) goto fail; + dev_set_name(&xendev->dev, devname); + /* Register with generic device framework. */ err = device_register(&xendev->dev); if (err) @@ -611,7 +614,7 @@ void xenbus_dev_changed(const char *node, struct xen_bus_type *bus) { int exists, rootlen; struct xenbus_device *dev; - char type[BUS_ID_SIZE]; + char type[XEN_BUS_ID_SIZE]; const char *p, *root; if (char_count(node, '/') < 2) @@ -625,8 +628,8 @@ void xenbus_dev_changed(const char *node, struct xen_bus_type *bus) /* backend/<type>/... or device/<type>/... */ p = strchr(node, '/') + 1; - snprintf(type, BUS_ID_SIZE, "%.*s", (int)strcspn(p, "/"), p); - type[BUS_ID_SIZE-1] = '\0'; + snprintf(type, XEN_BUS_ID_SIZE, "%.*s", (int)strcspn(p, "/"), p); + type[XEN_BUS_ID_SIZE-1] = '\0'; rootlen = strsep_len(node, '/', bus->levels); if (rootlen < 0) @@ -674,7 +677,7 @@ static int suspend_dev(struct device *dev, void *data) err = drv->suspend(xdev); if (err) printk(KERN_WARNING - "xenbus: suspend %s failed: %i\n", dev->bus_id, err); + "xenbus: suspend %s failed: %i\n", dev_name(dev), err); return 0; } @@ -695,7 +698,7 @@ static int suspend_cancel_dev(struct device *dev, void *data) if (err) printk(KERN_WARNING "xenbus: suspend_cancel %s failed: %i\n", - dev->bus_id, err); + dev_name(dev), err); return 0; } @@ -717,7 +720,7 @@ static int resume_dev(struct device *dev, void *data) if (err) { printk(KERN_WARNING "xenbus: resume (talk_to_otherend) %s failed: %i\n", - dev->bus_id, err); + dev_name(dev), err); return err; } @@ -728,7 +731,7 @@ static int resume_dev(struct device *dev, void *data) if (err) { printk(KERN_WARNING "xenbus: resume %s failed: %i\n", - dev->bus_id, err); + dev_name(dev), err); return err; } } @@ -737,7 +740,7 @@ static int resume_dev(struct device *dev, void *data) if (err) { printk(KERN_WARNING "xenbus_probe: resume (watch_otherend) %s failed: " - "%d.\n", dev->bus_id, err); + "%d.\n", dev_name(dev), err); return err; } diff --git a/drivers/xen/xenbus/xenbus_probe.h b/drivers/xen/xenbus/xenbus_probe.h index e09b19415a40..6c5e3185a6a2 100644 --- a/drivers/xen/xenbus/xenbus_probe.h +++ b/drivers/xen/xenbus/xenbus_probe.h @@ -34,6 +34,8 @@ #ifndef _XENBUS_PROBE_H #define _XENBUS_PROBE_H +#define XEN_BUS_ID_SIZE 20 + #ifdef CONFIG_XEN_BACKEND extern void xenbus_backend_suspend(int (*fn)(struct device *, void *)); extern void xenbus_backend_resume(int (*fn)(struct device *, void *)); @@ -52,7 +54,7 @@ struct xen_bus_type { char *root; unsigned int levels; - int (*get_bus_id)(char bus_id[BUS_ID_SIZE], const char *nodename); + int (*get_bus_id)(char bus_id[XEN_BUS_ID_SIZE], const char *nodename); int (*probe)(const char *type, const char *dir); struct bus_type bus; }; |