From e496b617b40f2abf6d49803f56aa1344ce1b9177 Mon Sep 17 00:00:00 2001 From: Harvey Harrison Date: Wed, 7 Jan 2009 16:22:37 -0800 Subject: PCI: __FUNCTION__ is gcc-specific, use __func__ Signed-off-by: Harvey Harrison Signed-off-by: Jesse Barnes --- drivers/pci/pci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 6d6120007af4..5737b8a9a732 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -657,7 +657,7 @@ static int pci_save_pcie_state(struct pci_dev *dev) save_state = pci_find_saved_cap(dev, PCI_CAP_ID_EXP); if (!save_state) { - dev_err(&dev->dev, "buffer not found in %s\n", __FUNCTION__); + dev_err(&dev->dev, "buffer not found in %s\n", __func__); return -ENOMEM; } cap = (u16 *)&save_state->data[0]; @@ -700,7 +700,7 @@ static int pci_save_pcix_state(struct pci_dev *dev) save_state = pci_find_saved_cap(dev, PCI_CAP_ID_PCIX); if (!save_state) { - dev_err(&dev->dev, "buffer not found in %s\n", __FUNCTION__); + dev_err(&dev->dev, "buffer not found in %s\n", __func__); return -ENOMEM; } -- cgit v1.2.3 From 1bf83e558cb29d163f4bc6decbc3800ecf4db195 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 13 Jan 2009 14:38:34 +0100 Subject: PCI: PCIe portdrv: Use driver data to simplify code PCI Express port driver extension, as defined by struct pcie_port_device_ext in portdrv.h, is allocated and initialized, but never used (it also is never freed). Extend it to hold the PCI Express port type as well as the port interrupt mode, change its name and use it to simplify the code in portdrv_core.c . Additionally, remove the redundant interrupt_mode member of struct pcie_device defined in include/linux/pcieport_if.h . Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/pci/pcie/portdrv.h | 5 ++- drivers/pci/pcie/portdrv_core.c | 95 ++++++++++++++++------------------------- include/linux/pcieport_if.h | 1 - 3 files changed, 39 insertions(+), 62 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h index 2529f3f2ea5a..b0dcbc73415e 100644 --- a/drivers/pci/pcie/portdrv.h +++ b/drivers/pci/pcie/portdrv.h @@ -28,8 +28,9 @@ #define get_descriptor_id(type, service) (((type - 4) << 4) | service) -struct pcie_port_device_ext { - int interrupt_mode; /* [0:INTx | 1:MSI | 2:MSI-X] */ +struct pcie_port_data { + int port_type; /* Type of the port */ + int port_irq_mode; /* [0:INTx | 1:MSI | 2:MSI-X] */ }; extern struct bus_type pcie_port_bus_type; diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 8b3f8c18032f..273e97619bce 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -15,10 +15,9 @@ #include #include +#include "../pci.h" #include "portdrv.h" -extern int pcie_mch_quirk; /* MSI-quirk Indicator */ - /** * release_pcie_device - free PCI Express port service device structure * @dev: Port service device to release @@ -31,28 +30,6 @@ static void release_pcie_device(struct device *dev) kfree(to_pcie_device(dev)); } -static int is_msi_quirked(struct pci_dev *dev) -{ - int port_type, quirk = 0; - u16 reg16; - - pci_read_config_word(dev, - pci_find_capability(dev, PCI_CAP_ID_EXP) + - PCIE_CAPABILITIES_REG, ®16); - port_type = (reg16 >> 4) & PORT_TYPE_MASK; - switch(port_type) { - case PCIE_RC_PORT: - if (pcie_mch_quirk == 1) - quirk = 1; - break; - case PCIE_SW_UPSTREAM_PORT: - case PCIE_SW_DOWNSTREAM_PORT: - default: - break; - } - return quirk; -} - /** * assign_interrupt_mode - choose interrupt mode for PCI Express port services * (INTx, MSI-X, MSI) and set up vectors @@ -64,6 +41,7 @@ static int is_msi_quirked(struct pci_dev *dev) */ static int assign_interrupt_mode(struct pci_dev *dev, int *vectors, int mask) { + struct pcie_port_data *port_data = pci_get_drvdata(dev); int i, pos, nvec, status = -EINVAL; int interrupt_mode = PCIE_PORT_INTx_MODE; @@ -75,7 +53,7 @@ static int assign_interrupt_mode(struct pci_dev *dev, int *vectors, int mask) } /* Check MSI quirk */ - if (is_msi_quirked(dev)) + if (port_data->port_type == PCIE_RC_PORT && pcie_mch_quirk) return interrupt_mode; /* Select MSI-X over MSI if supported */ @@ -132,13 +110,11 @@ static int get_port_device_capability(struct pci_dev *dev) pos + PCIE_SLOT_CAPABILITIES_REG, ®32); if (reg32 & SLOT_HP_CAPABLE_MASK) services |= PCIE_PORT_SERVICE_HP; - } - /* PME Capable - root port capability */ - if (((reg16 >> 4) & PORT_TYPE_MASK) == PCIE_RC_PORT) - services |= PCIE_PORT_SERVICE_PME; - + } + /* AER capable */ if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR)) services |= PCIE_PORT_SERVICE_AER; + /* VC support */ if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_VC)) services |= PCIE_PORT_SERVICE_VC; @@ -152,15 +128,15 @@ static int get_port_device_capability(struct pci_dev *dev) * @port_type: Type of the port * @service_type: Type of service to associate with the service device * @irq: Interrupt vector to associate with the service device - * @irq_mode: Interrupt mode of the service (INTx, MSI-X, MSI) */ static void pcie_device_init(struct pci_dev *parent, struct pcie_device *dev, - int port_type, int service_type, int irq, int irq_mode) + int service_type, int irq) { + struct pcie_port_data *port_data = pci_get_drvdata(parent); struct device *device; + int port_type = port_data->port_type; dev->port = parent; - dev->interrupt_mode = irq_mode; dev->irq = irq; dev->id.vendor = parent->vendor; dev->id.device = parent->device; @@ -185,10 +161,9 @@ static void pcie_device_init(struct pci_dev *parent, struct pcie_device *dev, * @port_type: Type of the port * @service_type: Type of service to associate with the service device * @irq: Interrupt vector to associate with the service device - * @irq_mode: Interrupt mode of the service (INTx, MSI-X, MSI) */ static struct pcie_device* alloc_pcie_device(struct pci_dev *parent, - int port_type, int service_type, int irq, int irq_mode) + int service_type, int irq) { struct pcie_device *device; @@ -196,7 +171,7 @@ static struct pcie_device* alloc_pcie_device(struct pci_dev *parent, if (!device) return NULL; - pcie_device_init(parent, device, port_type, service_type, irq,irq_mode); + pcie_device_init(parent, device, service_type, irq); return device; } @@ -230,39 +205,36 @@ int pcie_port_device_probe(struct pci_dev *dev) */ int pcie_port_device_register(struct pci_dev *dev) { - struct pcie_port_device_ext *p_ext; - int status, type, capabilities, irq_mode, i; + struct pcie_port_data *port_data; + int status, capabilities, irq_mode, i; int vectors[PCIE_PORT_DEVICE_MAXSERVICES]; u16 reg16; - /* Allocate port device extension */ - if (!(p_ext = kmalloc(sizeof(struct pcie_port_device_ext), GFP_KERNEL))) + port_data = kzalloc(sizeof(*port_data), GFP_KERNEL); + if (!port_data) return -ENOMEM; - - pci_set_drvdata(dev, p_ext); + pci_set_drvdata(dev, port_data); /* Get port type */ pci_read_config_word(dev, pci_find_capability(dev, PCI_CAP_ID_EXP) + PCIE_CAPABILITIES_REG, ®16); - type = (reg16 >> 4) & PORT_TYPE_MASK; + port_data->port_type = (reg16 >> 4) & PORT_TYPE_MASK; - /* Now get port services */ capabilities = get_port_device_capability(dev); + /* Root ports are capable of generating PME too */ + if (port_data->port_type == PCIE_RC_PORT) + capabilities |= PCIE_PORT_SERVICE_PME; + irq_mode = assign_interrupt_mode(dev, vectors, capabilities); - p_ext->interrupt_mode = irq_mode; + port_data->port_irq_mode = irq_mode; /* Allocate child services if any */ for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) { struct pcie_device *child; if (capabilities & (1 << i)) { - child = alloc_pcie_device( - dev, /* parent */ - type, /* port type */ - i, /* service type */ - vectors[i], /* irq */ - irq_mode /* interrupt mode */); + child = alloc_pcie_device(dev, i, vectors[i]); if (child) { status = device_register(&child->device); if (status) { @@ -349,25 +321,30 @@ static int remove_iter(struct device *dev, void *data) */ void pcie_port_device_remove(struct pci_dev *dev) { - struct device *device; - unsigned long device_addr; - int interrupt_mode = PCIE_PORT_INTx_MODE; + struct pcie_port_data *port_data = pci_get_drvdata(dev); int status; do { + unsigned long device_addr; + status = device_for_each_child(&dev->dev, &device_addr, remove_iter); if (status) { - device = (struct device*)device_addr; - interrupt_mode = (to_pcie_device(device))->interrupt_mode; + struct device *device = (struct device*)device_addr; put_device(device); device_unregister(device); } } while (status); - /* Switch to INTx by default if MSI enabled */ - if (interrupt_mode == PCIE_PORT_MSIX_MODE) + + switch (port_data->port_irq_mode) { + case PCIE_PORT_MSIX_MODE: pci_disable_msix(dev); - else if (interrupt_mode == PCIE_PORT_MSI_MODE) + break; + case PCIE_PORT_MSI_MODE: pci_disable_msi(dev); + break; + } + + kfree(port_data); } /** diff --git a/include/linux/pcieport_if.h b/include/linux/pcieport_if.h index 6cd91e3f9820..194409af1037 100644 --- a/include/linux/pcieport_if.h +++ b/include/linux/pcieport_if.h @@ -36,7 +36,6 @@ struct pcie_port_service_id { struct pcie_device { int irq; /* Service IRQ/MSI/MSI-X Vector */ - int interrupt_mode; /* [0:INTx | 1:MSI | 2:MSI-X] */ struct pcie_port_service_id id; /* Service ID */ struct pci_dev *port; /* Root/Upstream/Downstream Port */ void *priv_data; /* Service Private Data */ -- cgit v1.2.3 From 90e9cd50f7feeddc911325c8a8c1b7e1fccc6599 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 13 Jan 2009 14:39:39 +0100 Subject: PCI: PCIe portdrv: Aviod using service devices with wrong interrupts The PCI Express port driver should not attempt to register service devices that require the ability to generate interrupts if generating interrupts is not possible. Namely, if the port has no interrupt pin configured and we cannot set up MSI or MSI-X for it, there is no way it can generate interrupts and in such a case the port services that rely on interrupts (PME, PCIe HP, AER) should not be enabled for it. Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/pci/pcie/portdrv_core.c | 41 ++++++++++++++++++++++++++++------------- include/linux/pcieport_if.h | 1 + 2 files changed, 29 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 273e97619bce..265eba033a4a 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -43,7 +43,7 @@ static int assign_interrupt_mode(struct pci_dev *dev, int *vectors, int mask) { struct pcie_port_data *port_data = pci_get_drvdata(dev); int i, pos, nvec, status = -EINVAL; - int interrupt_mode = PCIE_PORT_INTx_MODE; + int interrupt_mode = PCIE_PORT_NO_IRQ; /* Set INTx as default */ for (i = 0, nvec = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) { @@ -51,7 +51,9 @@ static int assign_interrupt_mode(struct pci_dev *dev, int *vectors, int mask) nvec++; vectors[i] = dev->irq; } - + if (dev->pin) + interrupt_mode = PCIE_PORT_INTx_MODE; + /* Check MSI quirk */ if (port_data->port_type == PCIE_RC_PORT && pcie_mch_quirk) return interrupt_mode; @@ -141,7 +143,7 @@ static void pcie_device_init(struct pci_dev *parent, struct pcie_device *dev, dev->id.vendor = parent->vendor; dev->id.device = parent->device; dev->id.port_type = port_type; - dev->id.service_type = (1 << service_type); + dev->id.service_type = service_type; /* Initialize generic device interface */ device = &dev->device; @@ -232,19 +234,32 @@ int pcie_port_device_register(struct pci_dev *dev) /* Allocate child services if any */ for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) { struct pcie_device *child; + int service = 1 << i; - if (capabilities & (1 << i)) { - child = alloc_pcie_device(dev, i, vectors[i]); - if (child) { - status = device_register(&child->device); - if (status) { - kfree(child); - continue; - } - get_device(&child->device); - } + if (!(capabilities & service)) + continue; + + /* + * Don't use service devices that require interrupts if there is + * no way to generate them. + */ + if (irq_mode == PCIE_PORT_NO_IRQ + && service != PCIE_PORT_SERVICE_VC) + continue; + + child = alloc_pcie_device(dev, service, vectors[i]); + if (!child) + continue; + + status = device_register(&child->device); + if (status) { + kfree(child); + continue; } + + get_device(&child->device); } + return 0; } diff --git a/include/linux/pcieport_if.h b/include/linux/pcieport_if.h index 194409af1037..8e1ae1fd92f6 100644 --- a/include/linux/pcieport_if.h +++ b/include/linux/pcieport_if.h @@ -22,6 +22,7 @@ #define PCIE_PORT_SERVICE_VC 8 /* Virtual Channel */ /* Root/Upstream/Downstream Port's Interrupt Mode */ +#define PCIE_PORT_NO_IRQ (-1) #define PCIE_PORT_INTx_MODE 0 #define PCIE_PORT_MSI_MODE 1 #define PCIE_PORT_MSIX_MODE 2 -- cgit v1.2.3 From f118c0c3cff4fed39bde1863f9d59850719645cc Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 13 Jan 2009 14:42:01 +0100 Subject: PCI: PCIe portdrv: Do not enable port device before setting up interrupts The PCI Express port driver calls pci_enable_device() before setting up interrupts, which is wrong, because if there is an interrupt pin configured for the port, pci_enable_device() will likely set up an interrupt link for it. However, this shouldn't be done if either MSI or MSI-X interrupt mode is chosen for the port. The solution is to call pci_enable_device() after setting up interrupts, because in that case the interrupt link won't be set up if MSI or MSI-X are enabled. Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/pci/pcie/portdrv_core.c | 38 ++++++++++++++++++++++++++++---------- drivers/pci/pcie/portdrv_pci.c | 11 +++-------- 2 files changed, 31 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 265eba033a4a..91ecbc43155f 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -208,7 +208,7 @@ int pcie_port_device_probe(struct pci_dev *dev) int pcie_port_device_register(struct pci_dev *dev) { struct pcie_port_data *port_data; - int status, capabilities, irq_mode, i; + int status, capabilities, irq_mode, i, nr_serv; int vectors[PCIE_PORT_DEVICE_MAXSERVICES]; u16 reg16; @@ -229,24 +229,32 @@ int pcie_port_device_register(struct pci_dev *dev) capabilities |= PCIE_PORT_SERVICE_PME; irq_mode = assign_interrupt_mode(dev, vectors, capabilities); + if (irq_mode == PCIE_PORT_NO_IRQ) { + /* + * Don't use service devices that require interrupts if there is + * no way to generate them. + */ + if (!(capabilities & PCIE_PORT_SERVICE_VC)) { + status = -ENODEV; + goto Error; + } + capabilities = PCIE_PORT_SERVICE_VC; + } port_data->port_irq_mode = irq_mode; + status = pci_enable_device(dev); + if (status) + goto Error; + pci_set_master(dev); + /* Allocate child services if any */ - for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) { + for (i = 0, nr_serv = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) { struct pcie_device *child; int service = 1 << i; if (!(capabilities & service)) continue; - /* - * Don't use service devices that require interrupts if there is - * no way to generate them. - */ - if (irq_mode == PCIE_PORT_NO_IRQ - && service != PCIE_PORT_SERVICE_VC) - continue; - child = alloc_pcie_device(dev, service, vectors[i]); if (!child) continue; @@ -258,9 +266,19 @@ int pcie_port_device_register(struct pci_dev *dev) } get_device(&child->device); + nr_serv++; + } + if (!nr_serv) { + pci_disable_device(dev); + status = -ENODEV; + goto Error; } return 0; + + Error: + kfree(port_data); + return status; } #ifdef CONFIG_PM diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index 5ea566e20b37..d17611f390bd 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -82,18 +82,13 @@ static int __devinit pcie_portdrv_probe (struct pci_dev *dev, if (status) return status; - if (pci_enable_device(dev) < 0) - return -ENODEV; - - pci_set_master(dev); if (!dev->irq && dev->pin) { dev_warn(&dev->dev, "device [%04x:%04x] has invalid IRQ; " "check vendor BIOS\n", dev->vendor, dev->device); } - if (pcie_port_device_register(dev)) { - pci_disable_device(dev); - return -ENOMEM; - } + status = pcie_port_device_register(dev); + if (status) + return status; pcie_portdrv_save_config(dev); -- cgit v1.2.3 From 87d2e2ecf6026efa64b01f7f71802b20da736d35 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 13 Jan 2009 14:43:07 +0100 Subject: PCI: PCIe portdrv: Remove unnecessary function The function pcie_portdrv_save_config() in portdrv_pci.c is not necessary. Remove it. Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/pci/pcie/portdrv_pci.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index d17611f390bd..94d0e2af9bad 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -32,11 +32,6 @@ MODULE_LICENSE("GPL"); /* global data */ static const char device_name[] = "pcieport-driver"; -static int pcie_portdrv_save_config(struct pci_dev *dev) -{ - return pci_save_state(dev); -} - static int pcie_portdrv_restore_config(struct pci_dev *dev) { int retval; @@ -90,7 +85,7 @@ static int __devinit pcie_portdrv_probe (struct pci_dev *dev, if (status) return status; - pcie_portdrv_save_config(dev); + pci_save_state(dev); return 0; } -- cgit v1.2.3 From 0516c8bcd25293f438573101c439ce25a18916ad Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 13 Jan 2009 14:44:19 +0100 Subject: PCI: PCIe portdrv: Simplily probe callback of service drivers The second argument of the ->probe() callback in struct pcie_port_service_driver is unnecessary and never used. Remove it. Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/pciehp_acpi.c | 3 +-- drivers/pci/hotplug/pciehp_core.c | 2 +- drivers/pci/pcie/aer/aerdrv.c | 6 ++---- drivers/pci/pcie/portdrv_core.c | 2 +- include/linux/pcieport_if.h | 3 +-- 5 files changed, 6 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/pciehp_acpi.c b/drivers/pci/hotplug/pciehp_acpi.c index 438d795f9fe3..ad8835758a17 100644 --- a/drivers/pci/hotplug/pciehp_acpi.c +++ b/drivers/pci/hotplug/pciehp_acpi.c @@ -82,8 +82,7 @@ static int __initdata acpi_slot_detected; static struct list_head __initdata dummy_slots = LIST_HEAD_INIT(dummy_slots); /* Dummy driver for dumplicate name detection */ -static int __init dummy_probe(struct pcie_device *dev, - const struct pcie_port_service_id *id) +static int __init dummy_probe(struct pcie_device *dev) { int pos; u32 slot_cap; diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index 681e3912b821..3429b21dbb53 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -401,7 +401,7 @@ static int get_cur_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_spe return 0; } -static int pciehp_probe(struct pcie_device *dev, const struct pcie_port_service_id *id) +static int pciehp_probe(struct pcie_device *dev) { int rc; struct controller *ctrl; diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c index e390707661dd..57c41204c549 100644 --- a/drivers/pci/pcie/aer/aerdrv.c +++ b/drivers/pci/pcie/aer/aerdrv.c @@ -38,8 +38,7 @@ MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); -static int __devinit aer_probe (struct pcie_device *dev, - const struct pcie_port_service_id *id ); +static int __devinit aer_probe (struct pcie_device *dev); static void aer_remove(struct pcie_device *dev); static int aer_suspend(struct pcie_device *dev, pm_message_t state) {return 0;} @@ -207,8 +206,7 @@ static void aer_remove(struct pcie_device *dev) * * Invoked when PCI Express bus loads AER service driver. **/ -static int __devinit aer_probe (struct pcie_device *dev, - const struct pcie_port_service_id *id ) +static int __devinit aer_probe (struct pcie_device *dev) { int status; struct aer_rpc *rpc; diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 91ecbc43155f..682524b0c93a 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -402,7 +402,7 @@ static int pcie_port_probe_service(struct device *dev) return -ENODEV; pciedev = to_pcie_device(dev); - status = driver->probe(pciedev, driver->id_table); + status = driver->probe(pciedev); if (!status) { dev_printk(KERN_DEBUG, dev, "service driver %s loaded\n", driver->name); diff --git a/include/linux/pcieport_if.h b/include/linux/pcieport_if.h index 8e1ae1fd92f6..59e90b8a7839 100644 --- a/include/linux/pcieport_if.h +++ b/include/linux/pcieport_if.h @@ -56,8 +56,7 @@ static inline void* get_service_data(struct pcie_device *dev) struct pcie_port_service_driver { const char *name; - int (*probe) (struct pcie_device *dev, - const struct pcie_port_service_id *id); + int (*probe) (struct pcie_device *dev); void (*remove) (struct pcie_device *dev); int (*suspend) (struct pcie_device *dev, pm_message_t state); int (*resume) (struct pcie_device *dev); -- cgit v1.2.3 From 22106368c999246c414610dcaacd485e741605b1 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 13 Jan 2009 14:46:46 +0100 Subject: PCI: PCIe portdrv: Remove struct pcie_port_service_id The PCI Express port driver uses 'struct pcie_port_service_id' for matching port service devices and drivers, but this structure contains fields that duplicate information from the port device itself (vendor, device, subvendor, subdevice) and fields that are not used by any existing port service driver (class, class_mask, drvier_data). Also, both existing port service drivers (AER and PCIe HP) don't even use the vendor and device fields for device matching. Therefore 'struct pcie_port_service_id' can be removed altogether and the only useful members of it (port_type, service) can be introduced directly into the port service device and port service driver structures. That simplifies the code quite a bit and reduces its size. Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/pciehp_acpi.c | 13 ++----------- drivers/pci/hotplug/pciehp_core.c | 12 ++---------- drivers/pci/pcie/aer/aerdrv.c | 16 ++-------------- drivers/pci/pcie/aer/aerdrv_core.c | 10 +++++----- drivers/pci/pcie/portdrv.h | 5 ----- drivers/pci/pcie/portdrv_bus.c | 18 ++++++++++-------- drivers/pci/pcie/portdrv_core.c | 5 +---- include/linux/pcieport_if.h | 17 ++++++++--------- 8 files changed, 30 insertions(+), 66 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/pciehp_acpi.c b/drivers/pci/hotplug/pciehp_acpi.c index ad8835758a17..21734c311529 100644 --- a/drivers/pci/hotplug/pciehp_acpi.c +++ b/drivers/pci/hotplug/pciehp_acpi.c @@ -67,16 +67,6 @@ static int __init parse_detect_mode(void) return PCIEHP_DETECT_DEFAULT; } -static struct pcie_port_service_id __initdata port_pci_ids[] = { - { - .vendor = PCI_ANY_ID, - .device = PCI_ANY_ID, - .port_type = PCIE_ANY_PORT, - .service_type = PCIE_PORT_SERVICE_HP, - .driver_data = 0, - }, { /* end: all zeroes */ } -}; - static int __initdata dup_slot_id; static int __initdata acpi_slot_detected; static struct list_head __initdata dummy_slots = LIST_HEAD_INIT(dummy_slots); @@ -110,7 +100,8 @@ static int __init dummy_probe(struct pcie_device *dev) static struct pcie_port_service_driver __initdata dummy_driver = { .name = "pciehp_dummy", - .id_table = port_pci_ids, + .port_type = PCIE_ANY_PORT, + .service = PCIE_PORT_SERVICE_HP, .probe = dummy_probe, }; diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index 3429b21dbb53..3d21bbba3308 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -505,18 +505,10 @@ static int pciehp_resume (struct pcie_device *dev) } #endif -static struct pcie_port_service_id port_pci_ids[] = { { - .vendor = PCI_ANY_ID, - .device = PCI_ANY_ID, - .port_type = PCIE_ANY_PORT, - .service_type = PCIE_PORT_SERVICE_HP, - .driver_data = 0, - }, { /* end: all zeroes */ } -}; - static struct pcie_port_service_driver hpdriver_portdrv = { .name = PCIE_MODULE_NAME, - .id_table = &port_pci_ids[0], + .port_type = PCIE_ANY_PORT, + .service = PCIE_PORT_SERVICE_HP, .probe = pciehp_probe, .remove = pciehp_remove, diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c index 57c41204c549..e11c03194063 100644 --- a/drivers/pci/pcie/aer/aerdrv.c +++ b/drivers/pci/pcie/aer/aerdrv.c @@ -48,19 +48,6 @@ static pci_ers_result_t aer_error_detected(struct pci_dev *dev, static void aer_error_resume(struct pci_dev *dev); static pci_ers_result_t aer_root_reset(struct pci_dev *dev); -/* - * PCI Express bus's AER Root service driver data structure - */ -static struct pcie_port_service_id aer_id[] = { - { - .vendor = PCI_ANY_ID, - .device = PCI_ANY_ID, - .port_type = PCIE_RC_PORT, - .service_type = PCIE_PORT_SERVICE_AER, - }, - { /* end: all zeroes */ } -}; - static struct pci_error_handlers aer_error_handlers = { .error_detected = aer_error_detected, .resume = aer_error_resume, @@ -68,7 +55,8 @@ static struct pci_error_handlers aer_error_handlers = { static struct pcie_port_service_driver aerdriver = { .name = "aer", - .id_table = &aer_id[0], + .port_type = PCIE_ANY_PORT, + .service = PCIE_PORT_SERVICE_AER, .probe = aer_probe, .remove = aer_remove, diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c index 382575007382..307452f30035 100644 --- a/drivers/pci/pcie/aer/aerdrv_core.c +++ b/drivers/pci/pcie/aer/aerdrv_core.c @@ -351,21 +351,21 @@ static int find_aer_service_iter(struct device *device, void *data) { struct device_driver *driver; struct pcie_port_service_driver *service_driver; - struct pcie_device *pcie_dev; struct find_aer_service_data *result; result = (struct find_aer_service_data *) data; if (device->bus == &pcie_port_bus_type) { - pcie_dev = to_pcie_device(device); - if (pcie_dev->id.port_type == PCIE_SW_DOWNSTREAM_PORT) + struct pcie_port_data *port_data; + + port_data = pci_get_drvdata(to_pcie_device(device)->port); + if (port_data->port_type == PCIE_SW_DOWNSTREAM_PORT) result->is_downstream = 1; driver = device->driver; if (driver) { service_driver = to_service_driver(driver); - if (service_driver->id_table->service_type == - PCIE_PORT_SERVICE_AER) { + if (service_driver->service == PCIE_PORT_SERVICE_AER) { result->aer_driver = service_driver; return 1; } diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h index b0dcbc73415e..ad4d082a0344 100644 --- a/drivers/pci/pcie/portdrv.h +++ b/drivers/pci/pcie/portdrv.h @@ -28,11 +28,6 @@ #define get_descriptor_id(type, service) (((type - 4) << 4) | service) -struct pcie_port_data { - int port_type; /* Type of the port */ - int port_irq_mode; /* [0:INTx | 1:MSI | 2:MSI-X] */ -}; - extern struct bus_type pcie_port_bus_type; extern int pcie_port_device_probe(struct pci_dev *dev); extern int pcie_port_device_register(struct pci_dev *dev); diff --git a/drivers/pci/pcie/portdrv_bus.c b/drivers/pci/pcie/portdrv_bus.c index eec89b767f9f..ef3a4eeaebb4 100644 --- a/drivers/pci/pcie/portdrv_bus.c +++ b/drivers/pci/pcie/portdrv_bus.c @@ -26,20 +26,22 @@ EXPORT_SYMBOL_GPL(pcie_port_bus_type); static int pcie_port_bus_match(struct device *dev, struct device_driver *drv) { struct pcie_device *pciedev; + struct pcie_port_data *port_data; struct pcie_port_service_driver *driver; if (drv->bus != &pcie_port_bus_type || dev->bus != &pcie_port_bus_type) return 0; - + pciedev = to_pcie_device(dev); driver = to_service_driver(drv); - if ( (driver->id_table->vendor != PCI_ANY_ID && - driver->id_table->vendor != pciedev->id.vendor) || - (driver->id_table->device != PCI_ANY_ID && - driver->id_table->device != pciedev->id.device) || - (driver->id_table->port_type != PCIE_ANY_PORT && - driver->id_table->port_type != pciedev->id.port_type) || - driver->id_table->service_type != pciedev->id.service_type ) + + if (driver->service != pciedev->service) + return 0; + + port_data = pci_get_drvdata(pciedev->port); + + if (driver->port_type != PCIE_ANY_PORT + && driver->port_type != port_data->port_type) return 0; return 1; diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 682524b0c93a..843d9e30dd3b 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -140,10 +140,7 @@ static void pcie_device_init(struct pci_dev *parent, struct pcie_device *dev, dev->port = parent; dev->irq = irq; - dev->id.vendor = parent->vendor; - dev->id.device = parent->device; - dev->id.port_type = port_type; - dev->id.service_type = service_type; + dev->service = service_type; /* Initialize generic device interface */ device = &dev->device; diff --git a/include/linux/pcieport_if.h b/include/linux/pcieport_if.h index 59e90b8a7839..a3832079508e 100644 --- a/include/linux/pcieport_if.h +++ b/include/linux/pcieport_if.h @@ -27,18 +27,15 @@ #define PCIE_PORT_MSI_MODE 1 #define PCIE_PORT_MSIX_MODE 2 -struct pcie_port_service_id { - __u32 vendor, device; /* Vendor and device ID or PCI_ANY_ID*/ - __u32 subvendor, subdevice; /* Subsystem ID's or PCI_ANY_ID */ - __u32 class, class_mask; /* (class,subclass,prog-if) triplet */ - __u32 port_type, service_type; /* Port Entity */ - kernel_ulong_t driver_data; +struct pcie_port_data { + int port_type; /* Type of the port */ + int port_irq_mode; /* [0:INTx | 1:MSI | 2:MSI-X] */ }; struct pcie_device { int irq; /* Service IRQ/MSI/MSI-X Vector */ - struct pcie_port_service_id id; /* Service ID */ - struct pci_dev *port; /* Root/Upstream/Downstream Port */ + struct pci_dev *port; /* Root/Upstream/Downstream Port */ + u32 service; /* Port service this device represents */ void *priv_data; /* Service Private Data */ struct device device; /* Generic Device Interface */ }; @@ -67,7 +64,9 @@ struct pcie_port_service_driver { /* Link Reset Capability - AER service driver specific */ pci_ers_result_t (*reset_link) (struct pci_dev *dev); - const struct pcie_port_service_id *id_table; + int port_type; /* Type of the port this driver can handle */ + u32 service; /* Port service this device represents */ + struct device_driver driver; }; #define to_service_driver(d) \ -- cgit v1.2.3 From a447b772826fde2a3abfd9bb943dee8750994c55 Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Sun, 25 Jan 2009 23:53:56 +0100 Subject: PCI: struct device - replace bus_id with dev_name(), dev_set_name() More dev_set_name conversion. Acked-by: Greg Kroah-Hartman Signed-off-by: Kay Sievers Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/cpqphp_sysfs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/cpqphp_sysfs.c b/drivers/pci/hotplug/cpqphp_sysfs.c index a13abf55d784..8450f4a6568a 100644 --- a/drivers/pci/hotplug/cpqphp_sysfs.c +++ b/drivers/pci/hotplug/cpqphp_sysfs.c @@ -225,7 +225,8 @@ void cpqhp_shutdown_debugfs(void) void cpqhp_create_debugfs_files(struct controller *ctrl) { - ctrl->dentry = debugfs_create_file(ctrl->pci_dev->dev.bus_id, S_IRUGO, root, ctrl, &debug_ops); + ctrl->dentry = debugfs_create_file(dev_name(&ctrl->pci_dev->dev), + S_IRUGO, root, ctrl, &debug_ops); } void cpqhp_remove_debugfs_files(struct controller *ctrl) -- cgit v1.2.3 From a52e2e3513d4beafe8fe8699f1519b021c2d05ba Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 24 Jan 2009 00:21:14 +0100 Subject: PCI/MSI: Introduce pci_msix_table_size() Introduce new function pci_msix_table_size() returning the size of the MSI-X table of given PCI device or 0 if the device doesn't support MSI-X. Signed-off-by: Rafael J. Wysocki Reviewed-by: Hidetoshi Seto Signed-off-by: Jesse Barnes --- drivers/pci/msi.c | 24 +++++++++++++++++++----- include/linux/pci.h | 5 +++++ 2 files changed, 24 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index baba2eb5367d..08aedd5875b0 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -674,6 +674,23 @@ static int msi_free_irqs(struct pci_dev* dev) return 0; } +/** + * pci_msix_table_size - return the number of device's MSI-X table entries + * @dev: pointer to the pci_dev data structure of MSI-X device function + */ +int pci_msix_table_size(struct pci_dev *dev) +{ + int pos; + u16 control; + + pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); + if (!pos) + return 0; + + pci_read_config_word(dev, msi_control_reg(pos), &control); + return multi_msix_capable(control); +} + /** * pci_enable_msix - configure device's MSI-X capability structure * @dev: pointer to the pci_dev data structure of MSI-X device function @@ -691,9 +708,8 @@ static int msi_free_irqs(struct pci_dev* dev) **/ int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec) { - int status, pos, nr_entries; + int status, nr_entries; int i, j; - u16 control; if (!entries) return -EINVAL; @@ -702,9 +718,7 @@ int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec) if (status) return status; - pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); - pci_read_config_word(dev, msi_control_reg(pos), &control); - nr_entries = multi_msix_capable(control); + nr_entries = pci_msix_table_size(dev); if (nvec > nr_entries) return -EINVAL; diff --git a/include/linux/pci.h b/include/linux/pci.h index 7bd624bfdcfd..b5d6d0e0f1cb 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -799,6 +799,10 @@ static inline void pci_msi_shutdown(struct pci_dev *dev) static inline void pci_disable_msi(struct pci_dev *dev) { } +static inline int pci_msix_table_size(struct pci_dev *dev) +{ + return 0; +} static inline int pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries, int nvec) { @@ -823,6 +827,7 @@ static inline int pci_msi_enabled(void) extern int pci_enable_msi(struct pci_dev *dev); extern void pci_msi_shutdown(struct pci_dev *dev); extern void pci_disable_msi(struct pci_dev *dev); +extern int pci_msix_table_size(struct pci_dev *dev); extern int pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries, int nvec); extern void pci_msix_shutdown(struct pci_dev *dev); -- cgit v1.2.3 From b43d451385ef833e0696032aac2629da04d46c59 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 24 Jan 2009 00:23:22 +0100 Subject: PCI/PCIe portdrv: Fix allocation of interrupts If MSI-X interrupt mode is used by the PCI Express port driver, too many vectors are allocated and it is not ensured that the right vectors will be used for the right services. Namely, the PCI Express specification states that both PCI Express native PME and PCI Express hotplug will always use the same MSI or MSI-X message for signalling interrupts, which implies that the same vector will be used by both of them. Also, the VC service does not use interrupts at all. Moreover, is not clear which of the vectors allocated by pci_enable_msix() in the current code will be used for PME and hotplug and which of them will be used for AER if all of these services are configured. For these reasons, rework the allocation of interrupts for PCI Express ports so that if MSI-X are enabled, the right vectors will be used for the right purposes. Signed-off-by: Rafael J. Wysocki Reviewed-by: Hidetoshi Seto Signed-off-by: Jesse Barnes --- drivers/pci/pcie/portdrv.h | 6 ++ drivers/pci/pcie/portdrv_core.c | 206 ++++++++++++++++++++++++++++++++-------- include/linux/pcieport_if.h | 12 ++- 3 files changed, 181 insertions(+), 43 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h index ad4d082a0344..5b818bd835ef 100644 --- a/drivers/pci/pcie/portdrv.h +++ b/drivers/pci/pcie/portdrv.h @@ -25,6 +25,12 @@ #define PCIE_CAPABILITIES_REG 0x2 #define PCIE_SLOT_CAPABILITIES_REG 0x14 #define PCIE_PORT_DEVICE_MAXSERVICES 4 +#define PCIE_PORT_MSI_VECTOR_MASK 0x1f +/* + * According to the PCI Express Base Specification 2.0, the indices of the MSI-X + * table entires used by port services must not exceed 31 + */ +#define PCIE_PORT_MAX_MSIX_ENTRIES 32 #define get_descriptor_id(type, service) (((type - 4) << 4) | service) diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 843d9e30dd3b..3aea92a92928 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -30,6 +30,152 @@ static void release_pcie_device(struct device *dev) kfree(to_pcie_device(dev)); } +/** + * pcie_port_msix_add_entry - add entry to given array of MSI-X entries + * @entries: Array of MSI-X entries + * @new_entry: Index of the entry to add to the array + * @nr_entries: Number of entries aleady in the array + * + * Return value: Position of the added entry in the array + */ +static int pcie_port_msix_add_entry( + struct msix_entry *entries, int new_entry, int nr_entries) +{ + int j; + + for (j = 0; j < nr_entries; j++) + if (entries[j].entry == new_entry) + return j; + + entries[j].entry = new_entry; + return j; +} + +/** + * pcie_port_enable_msix - try to set up MSI-X as interrupt mode for given port + * @dev: PCI Express port to handle + * @vectors: Array of interrupt vectors to populate + * @mask: Bitmask of port capabilities returned by get_port_device_capability() + * + * Return value: 0 on success, error code on failure + */ +static int pcie_port_enable_msix(struct pci_dev *dev, int *vectors, int mask) +{ + struct msix_entry *msix_entries; + int idx[PCIE_PORT_DEVICE_MAXSERVICES]; + int nr_entries, status, pos, i, nvec; + u16 reg16; + u32 reg32; + + nr_entries = pci_msix_table_size(dev); + if (!nr_entries) + return -EINVAL; + if (nr_entries > PCIE_PORT_MAX_MSIX_ENTRIES) + nr_entries = PCIE_PORT_MAX_MSIX_ENTRIES; + + msix_entries = kzalloc(sizeof(*msix_entries) * nr_entries, GFP_KERNEL); + if (!msix_entries) + return -ENOMEM; + + /* + * Allocate as many entries as the port wants, so that we can check + * which of them will be useful. Moreover, if nr_entries is correctly + * equal to the number of entries this port actually uses, we'll happily + * go through without any tricks. + */ + for (i = 0; i < nr_entries; i++) + msix_entries[i].entry = i; + + status = pci_enable_msix(dev, msix_entries, nr_entries); + if (status) + goto Exit; + + for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) + idx[i] = -1; + status = -EIO; + nvec = 0; + + if (mask & (PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP)) { + int entry; + + /* + * The code below follows the PCI Express Base Specification 2.0 + * stating in Section 6.1.6 that "PME and Hot-Plug Event + * interrupts (when both are implemented) always share the same + * MSI or MSI-X vector, as indicated by the Interrupt Message + * Number field in the PCI Express Capabilities register", where + * according to Section 7.8.2 of the specification "For MSI-X, + * the value in this field indicates which MSI-X Table entry is + * used to generate the interrupt message." + */ + pos = pci_find_capability(dev, PCI_CAP_ID_EXP); + pci_read_config_word(dev, pos + PCIE_CAPABILITIES_REG, ®16); + entry = (reg16 >> 9) & PCIE_PORT_MSI_VECTOR_MASK; + if (entry >= nr_entries) + goto Error; + + i = pcie_port_msix_add_entry(msix_entries, entry, nvec); + if (i == nvec) + nvec++; + + idx[PCIE_PORT_SERVICE_PME_SHIFT] = i; + idx[PCIE_PORT_SERVICE_HP_SHIFT] = i; + } + + if (mask & PCIE_PORT_SERVICE_AER) { + int entry; + + /* + * The code below follows Section 7.10.10 of the PCI Express + * Base Specification 2.0 stating that bits 31-27 of the Root + * Error Status Register contain a value indicating which of the + * MSI/MSI-X vectors assigned to the port is going to be used + * for AER, where "For MSI-X, the value in this register + * indicates which MSI-X Table entry is used to generate the + * interrupt message." + */ + pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); + pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, ®32); + entry = reg32 >> 27; + if (entry >= nr_entries) + goto Error; + + i = pcie_port_msix_add_entry(msix_entries, entry, nvec); + if (i == nvec) + nvec++; + + idx[PCIE_PORT_SERVICE_AER_SHIFT] = i; + } + + /* + * If nvec is equal to the allocated number of entries, we can just use + * what we have. Otherwise, the port has some extra entries not for the + * services we know and we need to work around that. + */ + if (nvec == nr_entries) { + status = 0; + } else { + /* Drop the temporary MSI-X setup */ + pci_disable_msix(dev); + + /* Now allocate the MSI-X vectors for real */ + status = pci_enable_msix(dev, msix_entries, nvec); + if (status) + goto Exit; + } + + for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) + vectors[i] = idx[i] >= 0 ? msix_entries[idx[i]].vector : -1; + + Exit: + kfree(msix_entries); + return status; + + Error: + pci_disable_msix(dev); + goto Exit; +} + /** * assign_interrupt_mode - choose interrupt mode for PCI Express port services * (INTx, MSI-X, MSI) and set up vectors @@ -42,49 +188,31 @@ static void release_pcie_device(struct device *dev) static int assign_interrupt_mode(struct pci_dev *dev, int *vectors, int mask) { struct pcie_port_data *port_data = pci_get_drvdata(dev); - int i, pos, nvec, status = -EINVAL; - int interrupt_mode = PCIE_PORT_NO_IRQ; - - /* Set INTx as default */ - for (i = 0, nvec = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) { - if (mask & (1 << i)) - nvec++; - vectors[i] = dev->irq; - } - if (dev->pin) - interrupt_mode = PCIE_PORT_INTx_MODE; + int irq, interrupt_mode = PCIE_PORT_NO_IRQ; + int i; /* Check MSI quirk */ if (port_data->port_type == PCIE_RC_PORT && pcie_mch_quirk) - return interrupt_mode; + goto Fallback; + + /* Try to use MSI-X if supported */ + if (!pcie_port_enable_msix(dev, vectors, mask)) + return PCIE_PORT_MSIX_MODE; + + /* We're not going to use MSI-X, so try MSI and fall back to INTx */ + if (!pci_enable_msi(dev)) + interrupt_mode = PCIE_PORT_MSI_MODE; + + Fallback: + if (interrupt_mode == PCIE_PORT_NO_IRQ && dev->pin) + interrupt_mode = PCIE_PORT_INTx_MODE; + + irq = interrupt_mode != PCIE_PORT_NO_IRQ ? dev->irq : -1; + for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) + vectors[i] = irq; + + vectors[PCIE_PORT_SERVICE_VC_SHIFT] = -1; - /* Select MSI-X over MSI if supported */ - pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); - if (pos) { - struct msix_entry msix_entries[PCIE_PORT_DEVICE_MAXSERVICES] = - {{0, 0}, {0, 1}, {0, 2}, {0, 3}}; - status = pci_enable_msix(dev, msix_entries, nvec); - if (!status) { - int j = 0; - - interrupt_mode = PCIE_PORT_MSIX_MODE; - for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) { - if (mask & (1 << i)) - vectors[i] = msix_entries[j++].vector; - } - } - } - if (status) { - pos = pci_find_capability(dev, PCI_CAP_ID_MSI); - if (pos) { - status = pci_enable_msi(dev); - if (!status) { - interrupt_mode = PCIE_PORT_MSI_MODE; - for (i = 0;i < PCIE_PORT_DEVICE_MAXSERVICES;i++) - vectors[i] = dev->irq; - } - } - } return interrupt_mode; } diff --git a/include/linux/pcieport_if.h b/include/linux/pcieport_if.h index a3832079508e..5d2afcfa6bc1 100644 --- a/include/linux/pcieport_if.h +++ b/include/linux/pcieport_if.h @@ -16,10 +16,14 @@ #define PCIE_ANY_PORT 7 /* Service Type */ -#define PCIE_PORT_SERVICE_PME 1 /* Power Management Event */ -#define PCIE_PORT_SERVICE_AER 2 /* Advanced Error Reporting */ -#define PCIE_PORT_SERVICE_HP 4 /* Native Hotplug */ -#define PCIE_PORT_SERVICE_VC 8 /* Virtual Channel */ +#define PCIE_PORT_SERVICE_PME_SHIFT 0 /* Power Management Event */ +#define PCIE_PORT_SERVICE_PME (1 << PCIE_PORT_SERVICE_PME_SHIFT) +#define PCIE_PORT_SERVICE_AER_SHIFT 1 /* Advanced Error Reporting */ +#define PCIE_PORT_SERVICE_AER (1 << PCIE_PORT_SERVICE_AER_SHIFT) +#define PCIE_PORT_SERVICE_HP_SHIFT 2 /* Native Hotplug */ +#define PCIE_PORT_SERVICE_HP (1 << PCIE_PORT_SERVICE_HP_SHIFT) +#define PCIE_PORT_SERVICE_VC_SHIFT 3 /* Virtual Channel */ +#define PCIE_PORT_SERVICE_VC (1 << PCIE_PORT_SERVICE_VC_SHIFT) /* Root/Upstream/Downstream Port's Interrupt Mode */ #define PCIE_PORT_NO_IRQ (-1) -- cgit v1.2.3 From 11df1f05514beaf0269484191007dbc8d47e0e6f Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Mon, 19 Jan 2009 11:31:00 +1100 Subject: PCI/MSI: Use #ifdefs instead of weak functions Weak functions aren't all they're cracked up to be. They lead to incorrect binaries with some toolchains, they require us to have empty functions we otherwise wouldn't, and the unused code is not elided (as of gcc 4.3.2 anyway). So replace the weak MSI arch hooks with the #define foo foo idiom. We no longer need empty versions of arch_setup/teardown_msi_irq(). This is less source (by 1 line!), and results in smaller binaries too: text data bss dec hex filename 9354300 1693916 678424 11726640 b2ef30 build/powerpc/vmlinux-before 9354052 1693852 678424 11726328 b2edf8 build/powerpc/vmlinux-after Also smaller on x86_64 and arm (iop13xx). Signed-off-by: Michael Ellerman Signed-off-by: Jesse Barnes --- arch/powerpc/include/asm/pci.h | 4 ++++ arch/x86/include/asm/pci.h | 3 +++ drivers/pci/msi.c | 26 +++++++++----------------- 3 files changed, 16 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/arch/powerpc/include/asm/pci.h b/arch/powerpc/include/asm/pci.h index 3548159a1beb..ba17d5d90a49 100644 --- a/arch/powerpc/include/asm/pci.h +++ b/arch/powerpc/include/asm/pci.h @@ -114,6 +114,10 @@ extern int pci_domain_nr(struct pci_bus *bus); /* Decide whether to display the domain number in /proc */ extern int pci_proc_domain(struct pci_bus *bus); +/* MSI arch hooks */ +#define arch_setup_msi_irqs arch_setup_msi_irqs +#define arch_teardown_msi_irqs arch_teardown_msi_irqs +#define arch_msi_check_device arch_msi_check_device struct vm_area_struct; /* Map a range of PCI memory or I/O space for a device into user space */ diff --git a/arch/x86/include/asm/pci.h b/arch/x86/include/asm/pci.h index a977de23cb4d..a0301bfeb954 100644 --- a/arch/x86/include/asm/pci.h +++ b/arch/x86/include/asm/pci.h @@ -86,6 +86,9 @@ static inline void early_quirks(void) { } extern void pci_iommu_alloc(void); +/* MSI arch hook */ +#define arch_setup_msi_irqs arch_setup_msi_irqs + #endif /* __KERNEL__ */ #ifdef CONFIG_X86_32 diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 08aedd5875b0..33adf323f064 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -27,20 +27,15 @@ static int pci_msi_enable = 1; /* Arch hooks */ -int __attribute__ ((weak)) -arch_msi_check_device(struct pci_dev *dev, int nvec, int type) +#ifndef arch_msi_check_device +int arch_msi_check_device(struct pci_dev *dev, int nvec, int type) { return 0; } +#endif -int __attribute__ ((weak)) -arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *entry) -{ - return 0; -} - -int __attribute__ ((weak)) -arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) +#ifndef arch_setup_msi_irqs +int arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) { struct msi_desc *entry; int ret; @@ -53,14 +48,10 @@ arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) return 0; } +#endif -void __attribute__ ((weak)) arch_teardown_msi_irq(unsigned int irq) -{ - return; -} - -void __attribute__ ((weak)) -arch_teardown_msi_irqs(struct pci_dev *dev) +#ifndef arch_teardown_msi_irqs +void arch_teardown_msi_irqs(struct pci_dev *dev) { struct msi_desc *entry; @@ -69,6 +60,7 @@ arch_teardown_msi_irqs(struct pci_dev *dev) arch_teardown_msi_irq(entry->irq); } } +#endif static void __msi_set_enable(struct pci_dev *dev, int pos, int enable) { -- cgit v1.2.3 From 2b56313448bb8efad3af19f211d988c8352ac04d Mon Sep 17 00:00:00 2001 From: Yu Zhao Date: Wed, 28 Jan 2009 18:27:21 +0800 Subject: PCI: check if a bus is added when removing it When removing a bus, 'is_added' should be checked to make sure the bus has been successfully added by pci_bus_add_child() who will sets 'is_added'. Signed-off-by: Yu Zhao Signed-off-by: Jesse Barnes --- drivers/pci/remove.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index 042e08924421..caf8e1eae45e 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -71,6 +71,9 @@ void pci_remove_bus(struct pci_bus *pci_bus) down_write(&pci_bus_sem); list_del(&pci_bus->node); up_write(&pci_bus_sem); + if (!pci_bus->is_added) + return; + pci_remove_legacy_files(pci_bus); device_remove_file(&pci_bus->dev, &dev_attr_cpuaffinity); device_remove_file(&pci_bus->dev, &dev_attr_cpulistaffinity); -- cgit v1.2.3 From 1c35b8e538cb6259accb215099cdb673310cad84 Mon Sep 17 00:00:00 2001 From: Frank Seidel Date: Fri, 6 Feb 2009 10:23:36 +0100 Subject: PCI: add missing KERN_* constants to printks According to kerneljanitors todo list all printk calls (beginning a new line) should have an according KERN_* constant. Those are the missing pieces here for the pci subsystem. Signed-off-by: Frank Seidel Reviewed-by: Kenji Kaneshige Tested-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/pciehp.h | 10 +++++----- drivers/pci/hotplug/shpchp.h | 10 +++++----- drivers/pci/intel-iommu.c | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index 39ae37589fda..1f887f64e493 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -46,10 +46,10 @@ extern int pciehp_force; extern struct workqueue_struct *pciehp_wq; #define dbg(format, arg...) \ - do { \ - if (pciehp_debug) \ - printk("%s: " format, MY_NAME , ## arg); \ - } while (0) +do { \ + if (pciehp_debug) \ + printk(KERN_DEBUG "%s: " format, MY_NAME , ## arg); \ +} while (0) #define err(format, arg...) \ printk(KERN_ERR "%s: " format, MY_NAME , ## arg) #define info(format, arg...) \ @@ -60,7 +60,7 @@ extern struct workqueue_struct *pciehp_wq; #define ctrl_dbg(ctrl, format, arg...) \ do { \ if (pciehp_debug) \ - dev_printk(, &ctrl->pcie->device, \ + dev_printk(KERN_DEBUG, &ctrl->pcie->device, \ format, ## arg); \ } while (0) #define ctrl_err(ctrl, format, arg...) \ diff --git a/drivers/pci/hotplug/shpchp.h b/drivers/pci/hotplug/shpchp.h index 6aba0b6cf2e0..974e924ca96d 100644 --- a/drivers/pci/hotplug/shpchp.h +++ b/drivers/pci/hotplug/shpchp.h @@ -48,10 +48,10 @@ extern int shpchp_debug; extern struct workqueue_struct *shpchp_wq; #define dbg(format, arg...) \ - do { \ - if (shpchp_debug) \ - printk("%s: " format, MY_NAME , ## arg); \ - } while (0) +do { \ + if (shpchp_debug) \ + printk(KERN_DEBUG "%s: " format, MY_NAME , ## arg); \ +} while (0) #define err(format, arg...) \ printk(KERN_ERR "%s: " format, MY_NAME , ## arg) #define info(format, arg...) \ @@ -62,7 +62,7 @@ extern struct workqueue_struct *shpchp_wq; #define ctrl_dbg(ctrl, format, arg...) \ do { \ if (shpchp_debug) \ - dev_printk(, &ctrl->pci_dev->dev, \ + dev_printk(KERN_DEBUG, &ctrl->pci_dev->dev, \ format, ## arg); \ } while (0) #define ctrl_err(ctrl, format, arg...) \ diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index f3f686581a90..b548937d4746 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -1970,7 +1970,7 @@ static inline void iommu_prepare_isa(void) ret = iommu_prepare_identity_map(pdev, 0, 16*1024*1024); if (ret) - printk("IOMMU: Failed to create 0-64M identity map, " + printk(KERN_ERR "IOMMU: Failed to create 0-64M identity map, " "floppy might not work\n"); } -- cgit v1.2.3 From 0b3e7388e3b438500aaa0630879ce536747a47ca Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 8 Feb 2009 22:45:24 +0100 Subject: PCI: introduce missing kfree Error handling code following a kmalloc should free the allocated data. Since the subsequent code that could provoke an error does not use the allocated data, the allocation is just moved below it. The semantic match that finds the problem is as follows: (http://www.emn.fr/x-info/coccinelle/) // @r exists@ local idexpression x; statement S; expression E; identifier f,l; position p1,p2; expression *ptr != NULL; @@ ( if ((x@p1 = \(kmalloc\|kzalloc\|kcalloc\)(...)) == NULL) S | x@p1 = \(kmalloc\|kzalloc\|kcalloc\)(...); ... if (x == NULL) S ) <... when != x when != if (...) { <+...x...+> } x->f = E ...> ( return \(0\|<+...x...+>\|ptr\); | return@p2 ...; ) @script:python@ p1 << r.p1; p2 << r.p2; @@ print "* file: %s kmalloc %s return %s" % (p1[0].file,p1[0].line,p2[0].line) // Signed-off-by: Julia Lawall Reviewed-by: Matthew Wilcox Reviewed-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/pciehp_acpi.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/pciehp_acpi.c b/drivers/pci/hotplug/pciehp_acpi.c index 21734c311529..96048010e7d9 100644 --- a/drivers/pci/hotplug/pciehp_acpi.c +++ b/drivers/pci/hotplug/pciehp_acpi.c @@ -79,14 +79,15 @@ static int __init dummy_probe(struct pcie_device *dev) struct slot *slot, *tmp; struct pci_dev *pdev = dev->port; struct pci_bus *pbus = pdev->subordinate; - if (!(slot = kzalloc(sizeof(*slot), GFP_KERNEL))) - return -ENOMEM; /* Note: pciehp_detect_mode != PCIEHP_DETECT_ACPI here */ if (pciehp_get_hp_hw_control_from_firmware(pdev)) return -ENODEV; if (!(pos = pci_find_capability(pdev, PCI_CAP_ID_EXP))) return -ENODEV; pci_read_config_dword(pdev, pos + PCI_EXP_SLTCAP, &slot_cap); + slot = kzalloc(sizeof(*slot), GFP_KERNEL); + if (!slot) + return -ENOMEM; slot->number = slot_cap >> 19; list_for_each_entry(tmp, &dummy_slots, slot_list) { if (tmp->number == slot->number) -- cgit v1.2.3 From 81b840cd27e3ee9af67b6e05a4847868f74fce69 Mon Sep 17 00:00:00 2001 From: Kenji Kaneshige Date: Tue, 3 Feb 2009 15:06:13 +0900 Subject: PCI: pciehp: fix possible endless loop in pcie_isr Fix possible endless loop in pcie_isr. Currently, pcie_isr() (interrupt service routine of pciehp) can end up in an endless loop if the Slot Status register is set again immediately after being cleared. According to the past discussion (see below URL) this case can happen if the power fault detected bit is set during handling. http://sourceforge.net/mailarchive/message.php?msg_id=20051130135409.A14918%40unix-os.sc.intel.com Signed-off-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/pciehp_hpc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 7a16c6897bb9..c1a312f24060 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -672,10 +672,11 @@ static irqreturn_t pcie_isr(int irq, void *dev_id) detected &= (PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD | PCI_EXP_SLTSTA_MRLSC | PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_CC); + detected &= ~intr_loc; intr_loc |= detected; if (!intr_loc) return IRQ_NONE; - if (detected && pciehp_writew(ctrl, PCI_EXP_SLTSTA, detected)) { + if (detected && pciehp_writew(ctrl, PCI_EXP_SLTSTA, intr_loc)) { ctrl_err(ctrl, "%s: Cannot write to SLOTSTATUS\n", __func__); return IRQ_NONE; -- cgit v1.2.3 From 99f0169c17f334a11b0ace91188501c612f3e1e6 Mon Sep 17 00:00:00 2001 From: Kenji Kaneshige Date: Tue, 3 Feb 2009 15:06:16 +0900 Subject: PCI: pciehp: enable software notification on empty slots Current pciehp disables software notification of adapter presence changed event and MRL changed event when slot is turned off. Because of this, there is no way to detect those events on empty slots in the current pciehp implementation. According to the past discussion(*), this behavior was introduced to prevent endless loop that could happen if pcie_isr() runs after power fault is detected on a certain platform whose stickey power-fault bit remains on till the slot is powered on again. (*) http://sourceforge.net/mailarchive/message.php?msg_id=20051130135409.A14918%40unix-os.sc.intel.com I think this endless loop can be avoided using one bit flag that indicates power fault had been detected, instead of disabling software notification of adapter present changed event and MRL changed event. With this patch, we can enable software notification mechanism of presence changed and MRL changed event on the empty slots again. Signed-off-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/pciehp.h | 1 + drivers/pci/hotplug/pciehp_hpc.c | 31 +++++++++++-------------------- 2 files changed, 12 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index 1f887f64e493..2bf8d28062e8 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -112,6 +112,7 @@ struct controller { unsigned int no_cmd_complete:1; unsigned int link_active_reporting:1; unsigned int notification_enabled:1; + unsigned int power_fault_detected; }; #define INT_BUTTON_IGNORE 0 diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index c1a312f24060..07bd32151146 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -548,23 +548,21 @@ static int hpc_power_on_slot(struct slot * slot) slot_cmd = POWER_ON; cmd_mask = PCI_EXP_SLTCTL_PCC; - /* Enable detection that we turned off at slot power-off time */ if (!pciehp_poll_mode) { - slot_cmd |= (PCI_EXP_SLTCTL_PFDE | PCI_EXP_SLTCTL_MRLSCE | - PCI_EXP_SLTCTL_PDCE); - cmd_mask |= (PCI_EXP_SLTCTL_PFDE | PCI_EXP_SLTCTL_MRLSCE | - PCI_EXP_SLTCTL_PDCE); + /* Enable power fault detection turned off at power off time */ + slot_cmd |= PCI_EXP_SLTCTL_PFDE; + cmd_mask |= PCI_EXP_SLTCTL_PFDE; } retval = pcie_write_cmd(ctrl, slot_cmd, cmd_mask); - if (retval) { ctrl_err(ctrl, "Write %x command failed!\n", slot_cmd); - return -1; + return retval; } ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, ctrl->cap_base + PCI_EXP_SLTCTL, slot_cmd); + ctrl->power_fault_detected = 0; return retval; } @@ -621,18 +619,10 @@ static int hpc_power_off_slot(struct slot * slot) slot_cmd = POWER_OFF; cmd_mask = PCI_EXP_SLTCTL_PCC; - /* - * If we get MRL or presence detect interrupts now, the isr - * will notice the sticky power-fault bit too and issue power - * indicator change commands. This will lead to an endless loop - * of command completions, since the power-fault bit remains on - * till the slot is powered on again. - */ if (!pciehp_poll_mode) { - slot_cmd &= ~(PCI_EXP_SLTCTL_PFDE | PCI_EXP_SLTCTL_MRLSCE | - PCI_EXP_SLTCTL_PDCE); - cmd_mask |= (PCI_EXP_SLTCTL_PFDE | PCI_EXP_SLTCTL_MRLSCE | - PCI_EXP_SLTCTL_PDCE); + /* Disable power fault detection */ + slot_cmd &= ~PCI_EXP_SLTCTL_PFDE; + cmd_mask |= PCI_EXP_SLTCTL_PFDE; } retval = pcie_write_cmd(ctrl, slot_cmd, cmd_mask); @@ -710,9 +700,10 @@ static irqreturn_t pcie_isr(int irq, void *dev_id) pciehp_handle_presence_change(p_slot); /* Check Power Fault Detected */ - if (intr_loc & PCI_EXP_SLTSTA_PFD) + if ((intr_loc & PCI_EXP_SLTSTA_PFD) && !ctrl->power_fault_detected) { + ctrl->power_fault_detected = 1; pciehp_handle_power_fault(p_slot); - + } return IRQ_HANDLED; } -- cgit v1.2.3 From 6a82e21823058eea95325005b79f3b8c9492460f Mon Sep 17 00:00:00 2001 From: Kenji Kaneshige Date: Tue, 3 Feb 2009 15:06:18 +0900 Subject: PCI: pciehp: make cmd_busy flag one bit The cmd_busy field in struct controller takes only two values 0 or 1. So it should be one bit. Signed-off-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/pciehp.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index 2bf8d28062e8..0a368547e633 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -108,7 +108,7 @@ struct controller { u32 slot_cap; u8 cap_base; struct timer_list poll_timer; - int cmd_busy; + unsigned int cmd_busy:1; unsigned int no_cmd_complete:1; unsigned int link_active_reporting:1; unsigned int notification_enabled:1; -- cgit v1.2.3 From 62795041418dd63cd9ff6ff7bbdf1d1c513c189b Mon Sep 17 00:00:00 2001 From: Alex Chiang Date: Wed, 4 Feb 2009 11:25:22 -0700 Subject: PCI: enhance physical slot debug information Convert usages of pr_debug to dev_dbg and add physical slot name. Note that we use dev_dbg on the struct pci_bus and still manually print out the PCI slot number (instead of calling dev_dbg on a pci_dev) because a struct pci_bus with empty physical slots will not have any pci_devs. Reviewed-by: Andrew Patterson Signed-off-by: Alex Chiang Signed-off-by: Jesse Barnes --- drivers/pci/slot.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c index 5a8ccb4f604d..21189447e545 100644 --- a/drivers/pci/slot.c +++ b/drivers/pci/slot.c @@ -1,8 +1,8 @@ /* * drivers/pci/slot.c * Copyright (C) 2006 Matthew Wilcox - * Copyright (C) 2006-2008 Hewlett-Packard Development Company, L.P. - * Alex Chiang + * Copyright (C) 2006-2009 Hewlett-Packard Development Company, L.P. + * Alex Chiang */ #include @@ -52,8 +52,8 @@ static void pci_slot_release(struct kobject *kobj) struct pci_dev *dev; struct pci_slot *slot = to_pci_slot(kobj); - pr_debug("%s: releasing pci_slot on %x:%d\n", __func__, - slot->bus->number, slot->number); + dev_dbg(&slot->bus->dev, "dev %02x, released physical slot %s\n", + slot->number, pci_slot_name(slot)); list_for_each_entry(dev, &slot->bus->devices, bus_list) if (PCI_SLOT(dev->devfn) == slot->number) @@ -248,9 +248,8 @@ placeholder: if (PCI_SLOT(dev->devfn) == slot_nr) dev->slot = slot; - /* Don't care if debug printk has a -1 for slot_nr */ - pr_debug("%s: created pci_slot on %04x:%02x:%02x\n", - __func__, pci_domain_nr(parent), parent->number, slot_nr); + dev_dbg(&parent->dev, "dev %02x, created physical slot %s\n", + slot_nr, pci_slot_name(slot)); out: kfree(slot_name); @@ -299,9 +298,8 @@ EXPORT_SYMBOL_GPL(pci_renumber_slot); */ void pci_destroy_slot(struct pci_slot *slot) { - pr_debug("%s: dec refcount to %d on %04x:%02x:%02x\n", __func__, - atomic_read(&slot->kobj.kref.refcount) - 1, - pci_domain_nr(slot->bus), slot->bus->number, slot->number); + dev_dbg(&slot->bus->dev, "dev %02x, dec refcount to %d\n", + slot->number, atomic_read(&slot->kobj.kref.refcount) - 1); down_write(&pci_bus_sem); kobject_put(&slot->kobj); -- cgit v1.2.3 From 4c9c16867e4980fbd7d1fcc9516c9269ecb4d06f Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 8 Dec 2008 16:19:14 +0100 Subject: PCI quirk: don't mark one netmos as class other Let it stay as serial, since it doesn't have subdevice in the form of 0x00PS. Signed-off-by: Jiri Slaby Signed-off-by: Jesse Barnes --- drivers/pci/quirks.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 92b9efe9bcaf..5aa2afb23ef9 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -1664,9 +1664,13 @@ static void __devinit quirk_netmos(struct pci_dev *dev) * of parallel ports and is the number of serial ports. */ switch (dev->device) { + case PCI_DEVICE_ID_NETMOS_9835: + /* Well, this rule doesn't hold for the following 9835 device */ + if (dev->subsystem_vendor == PCI_VENDOR_ID_IBM && + dev->subsystem_device == 0x0299) + return; case PCI_DEVICE_ID_NETMOS_9735: case PCI_DEVICE_ID_NETMOS_9745: - case PCI_DEVICE_ID_NETMOS_9835: case PCI_DEVICE_ID_NETMOS_9845: case PCI_DEVICE_ID_NETMOS_9855: if ((dev->class >> 8) == PCI_CLASS_COMMUNICATION_SERIAL && -- cgit v1.2.3 From 5fe5db05f64d0d10b563b1c13b58e4a52b190686 Mon Sep 17 00:00:00 2001 From: Sheng Yang Date: Mon, 9 Feb 2009 14:53:47 +0800 Subject: PCI: Speed up device reset function For all devices need to do function level reset, currently we need wait for at least 200ms, which can be too long if we have lots of devices... The patch checked pending bit before msleep() to skip some unnecessary sleeping interval. Signed-off-by: Sheng Yang Signed-off-by: Jesse Barnes --- drivers/pci/pci.c | 46 +++++++++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 5737b8a9a732..0b3e20f1b6f7 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2028,18 +2028,24 @@ static int __pcie_flr(struct pci_dev *dev, int probe) pci_block_user_cfg_access(dev); /* Wait for Transaction Pending bit clean */ + pci_read_config_word(dev, exppos + PCI_EXP_DEVSTA, &status); + if (!(status & PCI_EXP_DEVSTA_TRPND)) + goto transaction_done; + msleep(100); pci_read_config_word(dev, exppos + PCI_EXP_DEVSTA, &status); - if (status & PCI_EXP_DEVSTA_TRPND) { - dev_info(&dev->dev, "Busy after 100ms while trying to reset; " + if (!(status & PCI_EXP_DEVSTA_TRPND)) + goto transaction_done; + + dev_info(&dev->dev, "Busy after 100ms while trying to reset; " "sleeping for 1 second\n"); - ssleep(1); - pci_read_config_word(dev, exppos + PCI_EXP_DEVSTA, &status); - if (status & PCI_EXP_DEVSTA_TRPND) - dev_info(&dev->dev, "Still busy after 1s; " + ssleep(1); + pci_read_config_word(dev, exppos + PCI_EXP_DEVSTA, &status); + if (status & PCI_EXP_DEVSTA_TRPND) + dev_info(&dev->dev, "Still busy after 1s; " "proceeding with reset anyway\n"); - } +transaction_done: pci_write_config_word(dev, exppos + PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_BCR_FLR); mdelay(100); @@ -2066,18 +2072,24 @@ static int __pci_af_flr(struct pci_dev *dev, int probe) pci_block_user_cfg_access(dev); /* Wait for Transaction Pending bit clean */ + pci_read_config_byte(dev, cappos + PCI_AF_STATUS, &status); + if (!(status & PCI_AF_STATUS_TP)) + goto transaction_done; + 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"); - } + if (!(status & PCI_AF_STATUS_TP)) + goto transaction_done; + + 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"); + +transaction_done: pci_write_config_byte(dev, cappos + PCI_AF_CTRL, PCI_AF_CTRL_FLR); mdelay(100); -- cgit v1.2.3 From 63f10f0f6df4e4e860b790d64bebfde85b540b0a Mon Sep 17 00:00:00 2001 From: Kenji Kaneshige Date: Mon, 9 Feb 2009 15:59:29 +0900 Subject: PCI/ACPI: move _OSC code to pci_root.c Move PCI _OSC management code from drivers/pci/pci-acpi.c to drivers/acpi/pci_root.c. The benefits are - We no longer need struct osc_data and its management code (contents are moved to struct acpi_pci_root). This simplify the code, and we no longer care about kmalloc() failure. - We can make pci_acpi_osc_support() be a static function, which is called only from drivers/acpi/pci_root.c. Signed-off-by: Kenji Kaneshige Reviewed-by: Andrew Patterson Tested-by: Andrew Patterson Acked-by: Alex Chiang Signed-off-by: Jesse Barnes --- drivers/acpi/pci_root.c | 180 ++++++++++++++++++++++++++++++++++++++- drivers/pci/pci-acpi.c | 215 ----------------------------------------------- include/linux/pci-acpi.h | 1 - 3 files changed, 178 insertions(+), 218 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 5b38a026d122..979eccc82c5b 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -66,11 +66,18 @@ struct acpi_pci_root { struct acpi_device * device; struct acpi_pci_id id; struct pci_bus *bus; + + u32 osc_support_set; /* _OSC state of support bits */ + u32 osc_control_set; /* _OSC state of control bits */ + u32 osc_control_qry; /* the latest _OSC query result */ + + u32 osc_queried:1; /* has _OSC control been queried? */ }; static LIST_HEAD(acpi_pci_roots); static struct acpi_pci_driver *sub_driver; +static DEFINE_MUTEX(osc_lock); int acpi_pci_register_driver(struct acpi_pci_driver *driver) { @@ -185,6 +192,175 @@ static void acpi_pci_bridge_scan(struct acpi_device *device) } } +static u8 OSC_UUID[16] = {0x5B, 0x4D, 0xDB, 0x33, 0xF7, 0x1F, 0x1C, 0x40, + 0x96, 0x57, 0x74, 0x41, 0xC0, 0x3D, 0xD7, 0x66}; + +static acpi_status acpi_pci_run_osc(acpi_handle handle, + const u32 *capbuf, u32 *retval) +{ + acpi_status status; + struct acpi_object_list input; + union acpi_object in_params[4]; + struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; + union acpi_object *out_obj; + u32 errors; + + /* Setting up input parameters */ + input.count = 4; + input.pointer = in_params; + in_params[0].type = ACPI_TYPE_BUFFER; + in_params[0].buffer.length = 16; + in_params[0].buffer.pointer = OSC_UUID; + in_params[1].type = ACPI_TYPE_INTEGER; + in_params[1].integer.value = 1; + in_params[2].type = ACPI_TYPE_INTEGER; + in_params[2].integer.value = 3; + in_params[3].type = ACPI_TYPE_BUFFER; + in_params[3].buffer.length = 12; + in_params[3].buffer.pointer = (u8 *)capbuf; + + status = acpi_evaluate_object(handle, "_OSC", &input, &output); + if (ACPI_FAILURE(status)) + return status; + + if (!output.length) + return AE_NULL_OBJECT; + + out_obj = output.pointer; + if (out_obj->type != ACPI_TYPE_BUFFER) { + printk(KERN_DEBUG "_OSC evaluation returned wrong type\n"); + status = AE_TYPE; + goto out_kfree; + } + /* 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 failed\n"); + if (errors & OSC_INVALID_UUID_ERROR) + printk(KERN_DEBUG "_OSC invalid UUID\n"); + if (errors & OSC_INVALID_REVISION_ERROR) + printk(KERN_DEBUG "_OSC invalid revision\n"); + if (errors & OSC_CAPABILITIES_MASK_ERROR) { + if (capbuf[OSC_QUERY_TYPE] & OSC_QUERY_ENABLE) + goto out_success; + printk(KERN_DEBUG + "Firmware did not grant requested _OSC control\n"); + status = AE_SUPPORT; + goto out_kfree; + } + status = AE_ERROR; + goto out_kfree; + } +out_success: + *retval = *((u32 *)(out_obj->buffer.pointer + 8)); + status = AE_OK; + +out_kfree: + kfree(output.pointer); + return status; +} + +static acpi_status acpi_pci_query_osc(struct acpi_pci_root *root, u32 flags) +{ + acpi_status status; + u32 support_set, result, capbuf[3]; + + /* do _OSC query for all possible controls */ + support_set = root->osc_support_set | (flags & OSC_SUPPORT_MASKS); + capbuf[OSC_QUERY_TYPE] = OSC_QUERY_ENABLE; + capbuf[OSC_SUPPORT_TYPE] = support_set; + capbuf[OSC_CONTROL_TYPE] = OSC_CONTROL_MASKS; + + status = acpi_pci_run_osc(root->device->handle, capbuf, &result); + if (ACPI_SUCCESS(status)) { + root->osc_support_set = support_set; + root->osc_control_qry = result; + root->osc_queried = 1; + } + return status; +} + +static acpi_status acpi_pci_osc_support(struct acpi_pci_root *root, u32 flags) +{ + acpi_status status; + acpi_handle tmp; + + status = acpi_get_handle(root->device->handle, "_OSC", &tmp); + if (ACPI_FAILURE(status)) + return status; + mutex_lock(&osc_lock); + status = acpi_pci_query_osc(root, flags); + mutex_unlock(&osc_lock); + return status; +} + +static struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle) +{ + struct acpi_pci_root *root; + list_for_each_entry(root, &acpi_pci_roots, node) { + if (root->device->handle == handle) + return root; + } + return NULL; +} + +/** + * pci_osc_control_set - commit requested control to Firmware + * @handle: acpi_handle for the target ACPI object + * @flags: driver's requested control bits + * + * Attempt to take control from Firmware on requested control bits. + **/ +acpi_status pci_osc_control_set(acpi_handle handle, u32 flags) +{ + acpi_status status; + u32 control_req, result, capbuf[3]; + acpi_handle tmp; + struct acpi_pci_root *root; + + status = acpi_get_handle(handle, "_OSC", &tmp); + if (ACPI_FAILURE(status)) + return status; + + control_req = (flags & OSC_CONTROL_MASKS); + if (!control_req) + return AE_TYPE; + + root = acpi_pci_find_root(handle); + if (!root) + return AE_NOT_EXIST; + + mutex_lock(&osc_lock); + /* No need to evaluate _OSC if the control was already granted. */ + if ((root->osc_control_set & control_req) == control_req) + goto out; + + /* Need to query controls first before requesting them */ + if (!root->osc_queried) { + status = acpi_pci_query_osc(root, root->osc_support_set); + if (ACPI_FAILURE(status)) + goto out; + } + if ((root->osc_control_qry & control_req) != control_req) { + printk(KERN_DEBUG + "Firmware did not grant requested _OSC control\n"); + status = AE_SUPPORT; + goto out; + } + + capbuf[OSC_QUERY_TYPE] = 0; + capbuf[OSC_SUPPORT_TYPE] = root->osc_support_set; + capbuf[OSC_CONTROL_TYPE] = root->osc_control_set | control_req; + status = acpi_pci_run_osc(handle, capbuf, &result); + if (ACPI_SUCCESS(status)) + root->osc_control_set = result; +out: + mutex_unlock(&osc_lock); + return status; +} +EXPORT_SYMBOL(pci_osc_control_set); + static int __devinit acpi_pci_root_add(struct acpi_device *device) { int result = 0; @@ -217,7 +393,7 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device) * 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); + acpi_pci_osc_support(root, flags); /* * Segment @@ -353,7 +529,7 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device) if (pci_msi_enabled()) flags |= OSC_MSI_SUPPORT; if (flags != base_flags) - pci_acpi_osc_support(device->handle, flags); + acpi_pci_osc_support(root, flags); end: if (result) { diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index deea8a187eb8..fac5eddcefd2 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -18,221 +18,6 @@ #include #include "pci.h" -struct acpi_osc_data { - acpi_handle handle; - u32 support_set; - u32 control_set; - u32 control_query; - int is_queried; - struct list_head sibiling; -}; -static LIST_HEAD(acpi_osc_data_list); - -struct acpi_osc_args { - u32 capbuf[3]; -}; - -static DEFINE_MUTEX(pci_acpi_lock); - -static struct acpi_osc_data *acpi_get_osc_data(acpi_handle handle) -{ - struct acpi_osc_data *data; - - list_for_each_entry(data, &acpi_osc_data_list, sibiling) { - if (data->handle == handle) - return data; - } - data = kzalloc(sizeof(*data), GFP_KERNEL); - if (!data) - return NULL; - INIT_LIST_HEAD(&data->sibiling); - data->handle = handle; - list_add_tail(&data->sibiling, &acpi_osc_data_list); - return data; -} - -static u8 OSC_UUID[16] = {0x5B, 0x4D, 0xDB, 0x33, 0xF7, 0x1F, 0x1C, 0x40, - 0x96, 0x57, 0x74, 0x41, 0xC0, 0x3D, 0xD7, 0x66}; - -static acpi_status acpi_run_osc(acpi_handle handle, - struct acpi_osc_args *osc_args, u32 *retval) -{ - acpi_status status; - struct acpi_object_list input; - union acpi_object in_params[4]; - struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; - union acpi_object *out_obj; - u32 errors, flags = osc_args->capbuf[OSC_QUERY_TYPE]; - - /* Setting up input parameters */ - input.count = 4; - input.pointer = in_params; - in_params[0].type = ACPI_TYPE_BUFFER; - in_params[0].buffer.length = 16; - in_params[0].buffer.pointer = OSC_UUID; - in_params[1].type = ACPI_TYPE_INTEGER; - in_params[1].integer.value = 1; - in_params[2].type = ACPI_TYPE_INTEGER; - in_params[2].integer.value = 3; - in_params[3].type = ACPI_TYPE_BUFFER; - in_params[3].buffer.length = 12; - in_params[3].buffer.pointer = (u8 *)osc_args->capbuf; - - status = acpi_evaluate_object(handle, "_OSC", &input, &output); - if (ACPI_FAILURE(status)) - return status; - - if (!output.length) - return AE_NULL_OBJECT; - - out_obj = output.pointer; - if (out_obj->type != ACPI_TYPE_BUFFER) { - printk(KERN_DEBUG "Evaluate _OSC returns wrong type\n"); - status = AE_TYPE; - goto out_kfree; - } - /* 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 (errors & OSC_INVALID_UUID_ERROR) - printk(KERN_DEBUG "_OSC invalid UUID\n"); - if (errors & OSC_INVALID_REVISION_ERROR) - printk(KERN_DEBUG "_OSC invalid revision\n"); - if (errors & OSC_CAPABILITIES_MASK_ERROR) { - if (flags & OSC_QUERY_ENABLE) - goto out_success; - printk(KERN_DEBUG "_OSC FW not grant req. control\n"); - status = AE_SUPPORT; - goto out_kfree; - } - status = AE_ERROR; - goto out_kfree; - } -out_success: - *retval = *((u32 *)(out_obj->buffer.pointer + 8)); - status = AE_OK; - -out_kfree: - kfree(output.pointer); - return status; -} - -static acpi_status __acpi_query_osc(u32 flags, struct acpi_osc_data *osc_data) -{ - acpi_status status; - u32 support_set, result; - struct acpi_osc_args osc_args; - - /* do _OSC query for all possible controls */ - support_set = osc_data->support_set | (flags & OSC_SUPPORT_MASKS); - osc_args.capbuf[OSC_QUERY_TYPE] = OSC_QUERY_ENABLE; - osc_args.capbuf[OSC_SUPPORT_TYPE] = support_set; - osc_args.capbuf[OSC_CONTROL_TYPE] = OSC_CONTROL_MASKS; - - status = acpi_run_osc(osc_data->handle, &osc_args, &result); - if (ACPI_SUCCESS(status)) { - osc_data->support_set = support_set; - osc_data->control_query = result; - osc_data->is_queried = 1; - } - - return status; -} - -/* - * 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) -{ - acpi_status status; - acpi_handle tmp; - struct acpi_osc_data *osc_data; - int rc = 0; - - status = acpi_get_handle(handle, "_OSC", &tmp); - if (ACPI_FAILURE(status)) - 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); -out: - mutex_unlock(&pci_acpi_lock); - return rc; -} - -/** - * pci_osc_control_set - commit requested control to Firmware - * @handle: acpi_handle for the target ACPI object - * @flags: driver's requested control bits - * - * Attempt to take control from Firmware on requested control bits. - **/ -acpi_status pci_osc_control_set(acpi_handle handle, u32 flags) -{ - acpi_status status; - u32 control_req, control_set, result; - acpi_handle tmp; - struct acpi_osc_data *osc_data; - struct acpi_osc_args osc_args; - - status = acpi_get_handle(handle, "_OSC", &tmp); - if (ACPI_FAILURE(status)) - return status; - - 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"); - status = AE_ERROR; - goto out; - } - - control_req = (flags & OSC_CONTROL_MASKS); - if (!control_req) { - status = AE_TYPE; - goto out; - } - - /* No need to evaluate _OSC if the control was already granted. */ - if ((osc_data->control_set & control_req) == control_req) - goto out; - - if (!osc_data->is_queried) { - status = __acpi_query_osc(osc_data->support_set, osc_data); - if (ACPI_FAILURE(status)) - goto out; - } - - if ((osc_data->control_query & control_req) != control_req) { - status = AE_SUPPORT; - goto out; - } - - control_set = osc_data->control_set | control_req; - osc_args.capbuf[OSC_QUERY_TYPE] = 0; - osc_args.capbuf[OSC_SUPPORT_TYPE] = osc_data->support_set; - osc_args.capbuf[OSC_CONTROL_TYPE] = control_set; - status = acpi_run_osc(handle, &osc_args, &result); - if (ACPI_SUCCESS(status)) - osc_data->control_set = result; -out: - mutex_unlock(&pci_acpi_lock); - return status; -} -EXPORT_SYMBOL(pci_osc_control_set); - /* * _SxD returns the D-state with the highest power * (lowest D-state number) supported in the S-state "x". diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h index 042c166f65d5..65cb103b21db 100644 --- a/include/linux/pci-acpi.h +++ b/include/linux/pci-acpi.h @@ -50,7 +50,6 @@ #ifdef CONFIG_ACPI extern acpi_status pci_osc_control_set(acpi_handle handle, u32 flags); -int pci_acpi_osc_support(acpi_handle handle, u32 flags); static inline acpi_handle acpi_find_root_bridge_handle(struct pci_dev *pdev) { /* Find root host bridge */ -- cgit v1.2.3 From 9f5404d8ea90bfa4d58a3936e5a3d0d28cecf60f Mon Sep 17 00:00:00 2001 From: Kenji Kaneshige Date: Mon, 9 Feb 2009 16:00:04 +0900 Subject: PCI/ACPI: rename pci_osc_control_set() - Rename pci_osc_control_set() to acpi_pci_osc_control_set() according to the other API names in drivers/acpi/pci_root.c. - Move _OSC related definitions to include/linux/acpi.h because _OSC related API is implemented in drivers/acpi/pci_root.c now. Signed-off-by: Kenji Kaneshige Reviewed-by: Andrew Patterson Tested-by: Andrew Patterson Signed-off-by: Jesse Barnes --- drivers/acpi/pci_root.c | 6 ++--- drivers/pci/hotplug/acpi_pcihp.c | 5 ++--- drivers/pci/pcie/aer/aerdrv_acpi.c | 2 +- include/linux/acpi.h | 34 ++++++++++++++++++++++++++++ include/linux/pci-acpi.h | 45 -------------------------------------- 5 files changed, 40 insertions(+), 52 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 979eccc82c5b..196f97d00956 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -306,13 +306,13 @@ static struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle) } /** - * pci_osc_control_set - commit requested control to Firmware + * acpi_pci_osc_control_set - commit requested control to Firmware * @handle: acpi_handle for the target ACPI object * @flags: driver's requested control bits * * Attempt to take control from Firmware on requested control bits. **/ -acpi_status pci_osc_control_set(acpi_handle handle, u32 flags) +acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 flags) { acpi_status status; u32 control_req, result, capbuf[3]; @@ -359,7 +359,7 @@ out: mutex_unlock(&osc_lock); return status; } -EXPORT_SYMBOL(pci_osc_control_set); +EXPORT_SYMBOL(acpi_pci_osc_control_set); static int __devinit acpi_pci_root_add(struct acpi_device *device) { diff --git a/drivers/pci/hotplug/acpi_pcihp.c b/drivers/pci/hotplug/acpi_pcihp.c index 1c1141801060..f47bc74be567 100644 --- a/drivers/pci/hotplug/acpi_pcihp.c +++ b/drivers/pci/hotplug/acpi_pcihp.c @@ -30,9 +30,8 @@ #include #include #include +#include #include -#include -#include #define MY_NAME "acpi_pcihp" @@ -408,7 +407,7 @@ int acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev, u32 flags) acpi_get_name(handle, ACPI_FULL_PATHNAME, &string); dbg("Trying to get hotplug control for %s\n", (char *)string.pointer); - status = pci_osc_control_set(handle, flags); + status = acpi_pci_osc_control_set(handle, flags); if (ACPI_SUCCESS(status)) goto got_one; kfree(string.pointer); diff --git a/drivers/pci/pcie/aer/aerdrv_acpi.c b/drivers/pci/pcie/aer/aerdrv_acpi.c index ebce26c37049..8edb2f300e8f 100644 --- a/drivers/pci/pcie/aer/aerdrv_acpi.c +++ b/drivers/pci/pcie/aer/aerdrv_acpi.c @@ -38,7 +38,7 @@ int aer_osc_setup(struct pcie_device *pciedev) handle = acpi_find_root_bridge_handle(pdev); if (handle) { - status = pci_osc_control_set(handle, + status = acpi_pci_osc_control_set(handle, OSC_PCI_EXPRESS_AER_CONTROL | OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL); } diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 6fce2fc2d124..2a3b189e3e26 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -256,6 +256,40 @@ void __init acpi_no_s4_hw_signature(void); void __init acpi_old_suspend_ordering(void); void __init acpi_s4_no_nvs(void); #endif /* CONFIG_PM_SLEEP */ + +#define OSC_QUERY_TYPE 0 +#define OSC_SUPPORT_TYPE 1 +#define OSC_CONTROL_TYPE 2 +#define OSC_SUPPORT_MASKS 0x1f + +/* _OSC DW0 Definition */ +#define OSC_QUERY_ENABLE 1 +#define OSC_REQUEST_ERROR 2 +#define OSC_INVALID_UUID_ERROR 4 +#define OSC_INVALID_REVISION_ERROR 8 +#define OSC_CAPABILITIES_MASK_ERROR 16 + +/* _OSC DW1 Definition (OS Support Fields) */ +#define OSC_EXT_PCI_CONFIG_SUPPORT 1 +#define OSC_ACTIVE_STATE_PWR_SUPPORT 2 +#define OSC_CLOCK_PWR_CAPABILITY_SUPPORT 4 +#define OSC_PCI_SEGMENT_GROUPS_SUPPORT 8 +#define OSC_MSI_SUPPORT 16 + +/* _OSC DW1 Definition (OS Control Fields) */ +#define OSC_PCI_EXPRESS_NATIVE_HP_CONTROL 1 +#define OSC_SHPC_NATIVE_HP_CONTROL 2 +#define OSC_PCI_EXPRESS_PME_CONTROL 4 +#define OSC_PCI_EXPRESS_AER_CONTROL 8 +#define OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL 16 + +#define OSC_CONTROL_MASKS (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL | \ + OSC_SHPC_NATIVE_HP_CONTROL | \ + OSC_PCI_EXPRESS_PME_CONTROL | \ + OSC_PCI_EXPRESS_AER_CONTROL | \ + OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL) + +extern acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 flags); #else /* CONFIG_ACPI */ static inline int early_acpi_boot_init(void) diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h index 65cb103b21db..20480b9f10c8 100644 --- a/include/linux/pci-acpi.h +++ b/include/linux/pci-acpi.h @@ -10,46 +10,7 @@ #include -#define OSC_QUERY_TYPE 0 -#define OSC_SUPPORT_TYPE 1 -#define OSC_CONTROL_TYPE 2 -#define OSC_SUPPORT_MASKS 0x1f - -/* - * _OSC DW0 Definition - */ -#define OSC_QUERY_ENABLE 1 -#define OSC_REQUEST_ERROR 2 -#define OSC_INVALID_UUID_ERROR 4 -#define OSC_INVALID_REVISION_ERROR 8 -#define OSC_CAPABILITIES_MASK_ERROR 16 - -/* - * _OSC DW1 Definition (OS Support Fields) - */ -#define OSC_EXT_PCI_CONFIG_SUPPORT 1 -#define OSC_ACTIVE_STATE_PWR_SUPPORT 2 -#define OSC_CLOCK_PWR_CAPABILITY_SUPPORT 4 -#define OSC_PCI_SEGMENT_GROUPS_SUPPORT 8 -#define OSC_MSI_SUPPORT 16 - -/* - * _OSC DW1 Definition (OS Control Fields) - */ -#define OSC_PCI_EXPRESS_NATIVE_HP_CONTROL 1 -#define OSC_SHPC_NATIVE_HP_CONTROL 2 -#define OSC_PCI_EXPRESS_PME_CONTROL 4 -#define OSC_PCI_EXPRESS_AER_CONTROL 8 -#define OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL 16 - -#define OSC_CONTROL_MASKS (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL | \ - OSC_SHPC_NATIVE_HP_CONTROL | \ - OSC_PCI_EXPRESS_PME_CONTROL | \ - OSC_PCI_EXPRESS_AER_CONTROL | \ - OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL) - #ifdef CONFIG_ACPI -extern acpi_status pci_osc_control_set(acpi_handle handle, u32 flags); static inline acpi_handle acpi_find_root_bridge_handle(struct pci_dev *pdev) { /* Find root host bridge */ @@ -69,12 +30,6 @@ static inline acpi_handle acpi_pci_get_bridge_handle(struct pci_bus *pbus) return acpi_get_pci_rootbridge_handle(seg, busnr); } #else -#if !defined(AE_ERROR) -typedef u32 acpi_status; -#define AE_ERROR (acpi_status) (0x0001) -#endif -static inline acpi_status pci_osc_control_set(acpi_handle handle, u32 flags) -{return AE_ERROR;} static inline acpi_handle acpi_find_root_bridge_handle(struct pci_dev *pdev) { return NULL; } #endif -- cgit v1.2.3 From 35e1801ea637810830e653ffe7ff62c7048ae03a Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Wed, 11 Feb 2009 21:13:45 +0100 Subject: PCI hotplug: shpchp: fix bus number check to avoid false positive With for (busnr = 0; busnr <= end; busnr++) { ... } busnr reaches end + 1 after the loop. So fix the "no busses available" check to look for just busnr > end rather than >=. Signed-off-by: Roel Kluin Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/shpchp_pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/shpchp_pci.c b/drivers/pci/hotplug/shpchp_pci.c index 138f161becc0..aa315e52529b 100644 --- a/drivers/pci/hotplug/shpchp_pci.c +++ b/drivers/pci/hotplug/shpchp_pci.c @@ -137,7 +137,7 @@ int __ref shpchp_configure_device(struct slot *p_slot) busnr)) break; } - if (busnr >= end) { + if (busnr > end) { ctrl_err(ctrl, "No free bus for hot-added bridge\n"); pci_dev_put(dev); -- cgit v1.2.3 From b5fbf53324f65646154e172af350674d5a2a1629 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Wed, 11 Feb 2009 22:27:02 +1100 Subject: PCI/MSI: Allow arch code to return the number of MSI-X available There is code in msix_capability_init() which, when the requested number of MSI-X couldn't be allocated, calculates how many MSI-X /could/ be allocated and returns that to the driver. That allows the driver to then make a second request, with a number of MSIs that should succeed. The current code requires the arch code to setup as many msi_descs as it can, and then return to the generic code. On some platforms the arch code may already know how many MSI-X it can allocate, before it sets up any of the msi_descs. So change the logic such that if the arch code returns a positive error code, that is taken to be the number of MSI-X that could be allocated. If the error code is negative we still calculate the number available using the old method. Because it's a little subtle, make sure the error return code from arch_setup_msi_irq() is always negative. That way only implementations of arch_setup_msi_irqs() need to be careful about returning a positive error code. Signed-off-by: Michael Ellerman Signed-off-by: Jesse Barnes --- drivers/pci/msi.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 33adf323f064..dceea56f7342 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -42,8 +42,10 @@ int arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) list_for_each_entry(entry, &dev->msi_list, list) { ret = arch_setup_msi_irq(dev, entry); - if (ret) + if (ret < 0) return ret; + if (ret > 0) + return -ENOSPC; } return 0; @@ -487,7 +489,9 @@ static int msix_capability_init(struct pci_dev *dev, } ret = arch_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSIX); - if (ret) { + if (ret < 0) { + /* If we had some success report the number of irqs + * we succeeded in setting up. */ int avail = 0; list_for_each_entry(entry, &dev->msi_list, list) { if (entry->irq != 0) { @@ -495,14 +499,13 @@ static int msix_capability_init(struct pci_dev *dev, } } - msi_free_irqs(dev); + if (avail != 0) + ret = avail; + } - /* If we had some success report the number of irqs - * we succeeded in setting up. - */ - if (avail == 0) - avail = ret; - return avail; + if (ret) { + msi_free_irqs(dev); + return ret; } i = 0; -- cgit v1.2.3 From c48f1670f42b71f39f4a3bfba01ffb691cc9206c Mon Sep 17 00:00:00 2001 From: "akpm@linux-foundation.org" Date: Tue, 3 Feb 2009 15:45:26 -0800 Subject: PCI: constify pci_bus_add_devices() drivers/pci/hotplug/fakephp.c:283: warning: passing argument 1 of 'pci_bus_add_devices' discards qualifiers from pointer target type Signed-off-by: Andrew Morton Signed-off-by: Jesse Barnes --- drivers/pci/bus.c | 2 +- include/linux/pci.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 52b54f053be0..118c77778d29 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -133,7 +133,7 @@ int pci_bus_add_child(struct pci_bus *bus) * * Call hotplug for each new devices. */ -void pci_bus_add_devices(struct pci_bus *bus) +void pci_bus_add_devices(const struct pci_bus *bus) { struct pci_dev *dev; struct pci_bus *child; diff --git a/include/linux/pci.h b/include/linux/pci.h index b5d6d0e0f1cb..a1af2fe00639 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -528,7 +528,7 @@ void pcibios_update_irq(struct pci_dev *, int irq); /* Generic PCI functions used internally */ extern struct pci_bus *pci_find_bus(int domain, int busnr); -void pci_bus_add_devices(struct pci_bus *bus); +void pci_bus_add_devices(const struct pci_bus *bus); struct pci_bus *pci_scan_bus_parented(struct device *parent, int bus, struct pci_ops *ops, void *sysdata); static inline struct pci_bus * __devinit pci_scan_bus(int bus, struct pci_ops *ops, -- cgit v1.2.3 From ea7415512a07add2b09c070c9a5d1950833cf9b3 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Wed, 18 Feb 2009 10:44:29 -0800 Subject: PCI: constify pci_bus_assign_resources() drivers/pci/hotplug/fakephp.c: In function 'pci_rescan_bus': drivers/pci/hotplug/fakephp.c:271: warning: passing argument 1 of 'pci_bus_assign_resources' discards qualifiers from pointer target type Signed-off-by: Andrew Morton Signed-off-by: Jesse Barnes --- drivers/pci/setup-bus.c | 4 ++-- include/linux/pci.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 704608945780..170a3eda9dd3 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -27,7 +27,7 @@ #include -static void pbus_assign_resources_sorted(struct pci_bus *bus) +static void pbus_assign_resources_sorted(const struct pci_bus *bus) { struct pci_dev *dev; struct resource *res; @@ -495,7 +495,7 @@ void __ref pci_bus_size_bridges(struct pci_bus *bus) } EXPORT_SYMBOL(pci_bus_size_bridges); -void __ref pci_bus_assign_resources(struct pci_bus *bus) +void __ref pci_bus_assign_resources(const struct pci_bus *bus) { struct pci_bus *b; struct pci_dev *dev; diff --git a/include/linux/pci.h b/include/linux/pci.h index a1af2fe00639..7baf2a5db12a 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -708,7 +708,7 @@ ssize_t pci_write_vpd(struct pci_dev *dev, loff_t pos, size_t count, const void int pci_vpd_truncate(struct pci_dev *dev, size_t size); /* Helper functions for low-level code (drivers/pci/setup-[bus,res].c) */ -void pci_bus_assign_resources(struct pci_bus *bus); +void pci_bus_assign_resources(const struct pci_bus *bus); void pci_bus_size_bridges(struct pci_bus *bus); int pci_claim_resource(struct pci_dev *, int); void pci_assign_unassigned_resources(void); -- cgit v1.2.3 From 10a0ef39fbd1d484c2bbc1ffd83d57ecef209140 Mon Sep 17 00:00:00 2001 From: Ivan Kokshaysky Date: Tue, 17 Feb 2009 13:46:53 +0300 Subject: PCI/alpha: pci sysfs resources This closes http://bugzilla.kernel.org/show_bug.cgi?id=10893 which is a showstopper for X development on alpha. The generic HAVE_PCI_MMAP code (drivers/pci-sysfs.c) is not very useful since we have to deal with three different types of MMIO address spaces: sparse and dense mappings for old ev4/ev5 machines and "normal" 1:1 MMIO space (bwx) for ev56 and later. Also "write combine" mappings are meaningless on alpha - roughly speaking, alpha does write combining, IO reordering and other optimizations by default, unless user splits IO accesses with memory barriers. I think the cleanest way to deal with resource files on alpha is to convert the default no-op pci_create_resource_files() and pci_remove_resource_files() for !HAVE_PCI_MMAP case into __weak functions and override them with alpha specific ones. Another alpha hook is needed for "legacy_" resource files to handle sparse addressing (pci_adjust_legacy_attr). With the "standard" resourceN files on ev56/ev6 libpciaccess works "out of the box". Handling of resourceN_sparse/resourceN_dense files on older machines obviously requires some userland work. Sparse/dense stuff has been tested on sx164 (pca56/pyxis, normally uses bwx IO) with the kernel hacked into "cia compatible" mode. Signed-off-by: Ivan Kokshaysky Signed-off-by: Jesse Barnes --- arch/alpha/include/asm/pci.h | 14 ++ arch/alpha/kernel/Makefile | 2 +- arch/alpha/kernel/pci-sysfs.c | 366 ++++++++++++++++++++++++++++++++++++++++++ drivers/pci/pci-sysfs.c | 19 ++- 4 files changed, 398 insertions(+), 3 deletions(-) create mode 100644 arch/alpha/kernel/pci-sysfs.c (limited to 'drivers') diff --git a/arch/alpha/include/asm/pci.h b/arch/alpha/include/asm/pci.h index 2a14302c17a3..cb04eaa6ba33 100644 --- a/arch/alpha/include/asm/pci.h +++ b/arch/alpha/include/asm/pci.h @@ -273,4 +273,18 @@ struct pci_dev *alpha_gendev_to_pci(struct device *dev); extern struct pci_dev *isa_bridge; +extern int pci_legacy_read(struct pci_bus *bus, loff_t port, u32 *val, + size_t count); +extern int pci_legacy_write(struct pci_bus *bus, loff_t port, u32 val, + size_t count); +extern int pci_mmap_legacy_page_range(struct pci_bus *bus, + struct vm_area_struct *vma, + enum pci_mmap_state mmap_state); +extern void pci_adjust_legacy_attr(struct pci_bus *bus, + enum pci_mmap_state mmap_type); +#define HAVE_PCI_LEGACY 1 + +extern int pci_create_resource_files(struct pci_dev *dev); +extern void pci_remove_resource_files(struct pci_dev *dev); + #endif /* __ALPHA_PCI_H */ diff --git a/arch/alpha/kernel/Makefile b/arch/alpha/kernel/Makefile index b4697759a123..a427538252f8 100644 --- a/arch/alpha/kernel/Makefile +++ b/arch/alpha/kernel/Makefile @@ -12,7 +12,7 @@ obj-y := entry.o traps.o process.o init_task.o osf_sys.o irq.o \ obj-$(CONFIG_VGA_HOSE) += console.o obj-$(CONFIG_SMP) += smp.o -obj-$(CONFIG_PCI) += pci.o pci_iommu.o +obj-$(CONFIG_PCI) += pci.o pci_iommu.o pci-sysfs.o obj-$(CONFIG_SRM_ENV) += srm_env.o obj-$(CONFIG_MODULES) += module.o diff --git a/arch/alpha/kernel/pci-sysfs.c b/arch/alpha/kernel/pci-sysfs.c new file mode 100644 index 000000000000..6ea822e7f724 --- /dev/null +++ b/arch/alpha/kernel/pci-sysfs.c @@ -0,0 +1,366 @@ +/* + * arch/alpha/kernel/pci-sysfs.c + * + * Copyright (C) 2009 Ivan Kokshaysky + * + * Alpha PCI resource files. + * + * Loosely based on generic HAVE_PCI_MMAP implementation in + * drivers/pci/pci-sysfs.c + */ + +#include +#include + +static int hose_mmap_page_range(struct pci_controller *hose, + struct vm_area_struct *vma, + enum pci_mmap_state mmap_type, int sparse) +{ + unsigned long base; + + if (mmap_type == pci_mmap_mem) + base = sparse ? hose->sparse_mem_base : hose->dense_mem_base; + else + base = sparse ? hose->sparse_io_base : hose->dense_io_base; + + vma->vm_pgoff += base >> PAGE_SHIFT; + vma->vm_flags |= (VM_IO | VM_RESERVED); + + return io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, + vma->vm_end - vma->vm_start, + vma->vm_page_prot); +} + +static int __pci_mmap_fits(struct pci_dev *pdev, int num, + struct vm_area_struct *vma, int sparse) +{ + unsigned long nr, start, size; + int shift = sparse ? 5 : 0; + + nr = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; + start = vma->vm_pgoff; + size = ((pci_resource_len(pdev, num) - 1) >> (PAGE_SHIFT - shift)) + 1; + + if (start < size && size - start >= nr) + return 1; + WARN(1, "process \"%s\" tried to map%s 0x%08lx-0x%08lx on %s BAR %d " + "(size 0x%08lx)\n", + current->comm, sparse ? " sparse" : "", start, start + nr, + pci_name(pdev), num, size); + return 0; +} + +/** + * pci_mmap_resource - map a PCI resource into user memory space + * @kobj: kobject for mapping + * @attr: struct bin_attribute for the file being mapped + * @vma: struct vm_area_struct passed into the mmap + * @sparse: address space type + * + * Use the bus mapping routines to map a PCI resource into userspace. + */ +static int pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr, + struct vm_area_struct *vma, int sparse) +{ + struct pci_dev *pdev = to_pci_dev(container_of(kobj, + struct device, kobj)); + struct resource *res = (struct resource *)attr->private; + enum pci_mmap_state mmap_type; + struct pci_bus_region bar; + int i; + + for (i = 0; i < PCI_ROM_RESOURCE; i++) + if (res == &pdev->resource[i]) + break; + if (i >= PCI_ROM_RESOURCE) + return -ENODEV; + + if (!__pci_mmap_fits(pdev, i, vma, sparse)) + return -EINVAL; + + if (iomem_is_exclusive(res->start)) + return -EINVAL; + + pcibios_resource_to_bus(pdev, &bar, res); + vma->vm_pgoff += bar.start >> (PAGE_SHIFT - (sparse ? 5 : 0)); + mmap_type = res->flags & IORESOURCE_MEM ? pci_mmap_mem : pci_mmap_io; + + return hose_mmap_page_range(pdev->sysdata, vma, mmap_type, sparse); +} + +static int pci_mmap_resource_sparse(struct kobject *kobj, + struct bin_attribute *attr, + struct vm_area_struct *vma) +{ + return pci_mmap_resource(kobj, attr, vma, 1); +} + +static int pci_mmap_resource_dense(struct kobject *kobj, + struct bin_attribute *attr, + struct vm_area_struct *vma) +{ + return pci_mmap_resource(kobj, attr, vma, 0); +} + +/** + * pci_remove_resource_files - cleanup resource files + * @dev: dev to cleanup + * + * If we created resource files for @dev, remove them from sysfs and + * free their resources. + */ +void pci_remove_resource_files(struct pci_dev *pdev) +{ + int i; + + for (i = 0; i < PCI_ROM_RESOURCE; i++) { + struct bin_attribute *res_attr; + + res_attr = pdev->res_attr[i]; + if (res_attr) { + sysfs_remove_bin_file(&pdev->dev.kobj, res_attr); + kfree(res_attr); + } + + res_attr = pdev->res_attr_wc[i]; + if (res_attr) { + sysfs_remove_bin_file(&pdev->dev.kobj, res_attr); + kfree(res_attr); + } + } +} + +static int sparse_mem_mmap_fits(struct pci_dev *pdev, int num) +{ + struct pci_bus_region bar; + struct pci_controller *hose = pdev->sysdata; + long dense_offset; + unsigned long sparse_size; + + pcibios_resource_to_bus(pdev, &bar, &pdev->resource[num]); + + /* All core logic chips have 4G sparse address space, except + CIA which has 16G (see xxx_SPARSE_MEM and xxx_DENSE_MEM + definitions in asm/core_xxx.h files). This corresponds + to 128M or 512M of the bus space. */ + dense_offset = (long)(hose->dense_mem_base - hose->sparse_mem_base); + sparse_size = dense_offset >= 0x400000000UL ? 0x20000000 : 0x8000000; + + return bar.end < sparse_size; +} + +static int pci_create_one_attr(struct pci_dev *pdev, int num, char *name, + char *suffix, struct bin_attribute *res_attr, + unsigned long sparse) +{ + size_t size = pci_resource_len(pdev, num); + + sprintf(name, "resource%d%s", num, suffix); + res_attr->mmap = sparse ? pci_mmap_resource_sparse : + pci_mmap_resource_dense; + res_attr->attr.name = name; + res_attr->attr.mode = S_IRUSR | S_IWUSR; + res_attr->size = sparse ? size << 5 : size; + res_attr->private = &pdev->resource[num]; + return sysfs_create_bin_file(&pdev->dev.kobj, res_attr); +} + +static int pci_create_attr(struct pci_dev *pdev, int num) +{ + /* allocate attribute structure, piggyback attribute name */ + int retval, nlen1, nlen2 = 0, res_count = 1; + unsigned long sparse_base, dense_base; + struct bin_attribute *attr; + struct pci_controller *hose = pdev->sysdata; + char *suffix, *attr_name; + + suffix = ""; /* Assume bwx machine, normal resourceN files. */ + nlen1 = 10; + + if (pdev->resource[num].flags & IORESOURCE_MEM) { + sparse_base = hose->sparse_mem_base; + dense_base = hose->dense_mem_base; + if (sparse_base && !sparse_mem_mmap_fits(pdev, num)) { + sparse_base = 0; + suffix = "_dense"; + nlen1 = 16; /* resourceN_dense */ + } + } else { + sparse_base = hose->sparse_io_base; + dense_base = hose->dense_io_base; + } + + if (sparse_base) { + suffix = "_sparse"; + nlen1 = 17; + if (dense_base) { + nlen2 = 16; /* resourceN_dense */ + res_count = 2; + } + } + + attr = kzalloc(sizeof(*attr) * res_count + nlen1 + nlen2, GFP_ATOMIC); + if (!attr) + return -ENOMEM; + + /* Create bwx, sparse or single dense file */ + attr_name = (char *)(attr + res_count); + pdev->res_attr[num] = attr; + retval = pci_create_one_attr(pdev, num, attr_name, suffix, attr, + sparse_base); + if (retval || res_count == 1) + return retval; + + /* Create dense file */ + attr_name += nlen1; + attr++; + pdev->res_attr_wc[num] = attr; + return pci_create_one_attr(pdev, num, attr_name, "_dense", attr, 0); +} + +/** + * pci_create_resource_files - create resource files in sysfs for @dev + * @dev: dev in question + * + * Walk the resources in @dev creating files for each resource available. + */ +int pci_create_resource_files(struct pci_dev *pdev) +{ + int i; + int retval; + + /* Expose the PCI resources from this device as files */ + for (i = 0; i < PCI_ROM_RESOURCE; i++) { + + /* skip empty resources */ + if (!pci_resource_len(pdev, i)) + continue; + + retval = pci_create_attr(pdev, i); + if (retval) { + pci_remove_resource_files(pdev); + return retval; + } + } + return 0; +} + +/* Legacy I/O bus mapping stuff. */ + +static int __legacy_mmap_fits(struct pci_controller *hose, + struct vm_area_struct *vma, + unsigned long res_size, int sparse) +{ + unsigned long nr, start, size; + + nr = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; + start = vma->vm_pgoff; + size = ((res_size - 1) >> PAGE_SHIFT) + 1; + + if (start < size && size - start >= nr) + return 1; + WARN(1, "process \"%s\" tried to map%s 0x%08lx-0x%08lx on hose %d " + "(size 0x%08lx)\n", + current->comm, sparse ? " sparse" : "", start, start + nr, + hose->index, size); + return 0; +} + +static inline int has_sparse(struct pci_controller *hose, + enum pci_mmap_state mmap_type) +{ + unsigned long base; + + base = (mmap_type == pci_mmap_mem) ? hose->sparse_mem_base : + hose->sparse_io_base; + + return base != 0; +} + +int pci_mmap_legacy_page_range(struct pci_bus *bus, struct vm_area_struct *vma, + enum pci_mmap_state mmap_type) +{ + struct pci_controller *hose = bus->sysdata; + int sparse = has_sparse(hose, mmap_type); + unsigned long res_size; + + res_size = (mmap_type == pci_mmap_mem) ? bus->legacy_mem->size : + bus->legacy_io->size; + if (!__legacy_mmap_fits(hose, vma, res_size, sparse)) + return -EINVAL; + + return hose_mmap_page_range(hose, vma, mmap_type, sparse); +} + +/** + * pci_adjust_legacy_attr - adjustment of legacy file attributes + * @b: bus to create files under + * @mmap_type: I/O port or memory + * + * Adjust file name and size for sparse mappings. + */ +void pci_adjust_legacy_attr(struct pci_bus *bus, enum pci_mmap_state mmap_type) +{ + struct pci_controller *hose = bus->sysdata; + + if (!has_sparse(hose, mmap_type)) + return; + + if (mmap_type == pci_mmap_mem) { + bus->legacy_mem->attr.name = "legacy_mem_sparse"; + bus->legacy_mem->size <<= 5; + } else { + bus->legacy_io->attr.name = "legacy_io_sparse"; + bus->legacy_io->size <<= 5; + } + return; +} + +/* Legacy I/O bus read/write functions */ +int pci_legacy_read(struct pci_bus *bus, loff_t port, u32 *val, size_t size) +{ + struct pci_controller *hose = bus->sysdata; + + port += hose->io_space->start; + + switch(size) { + case 1: + *((u8 *)val) = inb(port); + return 1; + case 2: + if (port & 1) + return -EINVAL; + *((u16 *)val) = inw(port); + return 2; + case 4: + if (port & 3) + return -EINVAL; + *((u32 *)val) = inl(port); + return 4; + } + return -EINVAL; +} + +int pci_legacy_write(struct pci_bus *bus, loff_t port, u32 val, size_t size) +{ + struct pci_controller *hose = bus->sysdata; + + port += hose->io_space->start; + + switch(size) { + case 1: + outb(port, val); + return 1; + case 2: + if (port & 1) + return -EINVAL; + outw(port, val); + return 2; + case 4: + if (port & 3) + return -EINVAL; + outl(port, val); + return 4; + } + return -EINVAL; +} diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index dfc4e0ddf241..1c8929801400 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -492,6 +492,19 @@ pci_mmap_legacy_io(struct kobject *kobj, struct bin_attribute *attr, return pci_mmap_legacy_page_range(bus, vma, pci_mmap_io); } +/** + * pci_adjust_legacy_attr - adjustment of legacy file attributes + * @b: bus to create files under + * @mmap_type: I/O port or memory + * + * Stub implementation. Can be overridden by arch if necessary. + */ +void __weak +pci_adjust_legacy_attr(struct pci_bus *b, enum pci_mmap_state mmap_type) +{ + return; +} + /** * pci_create_legacy_files - create legacy I/O port and memory files * @b: bus to create files under @@ -518,6 +531,7 @@ void pci_create_legacy_files(struct pci_bus *b) b->legacy_io->read = pci_read_legacy_io; b->legacy_io->write = pci_write_legacy_io; b->legacy_io->mmap = pci_mmap_legacy_io; + pci_adjust_legacy_attr(b, pci_mmap_io); error = device_create_bin_file(&b->dev, b->legacy_io); if (error) goto legacy_io_err; @@ -528,6 +542,7 @@ void pci_create_legacy_files(struct pci_bus *b) b->legacy_mem->size = 1024*1024; b->legacy_mem->attr.mode = S_IRUSR | S_IWUSR; b->legacy_mem->mmap = pci_mmap_legacy_mem; + pci_adjust_legacy_attr(b, pci_mmap_mem); error = device_create_bin_file(&b->dev, b->legacy_mem); if (error) goto legacy_mem_err; @@ -719,8 +734,8 @@ static int pci_create_resource_files(struct pci_dev *pdev) return 0; } #else /* !HAVE_PCI_MMAP */ -static inline int pci_create_resource_files(struct pci_dev *dev) { return 0; } -static inline void pci_remove_resource_files(struct pci_dev *dev) { return; } +int __weak pci_create_resource_files(struct pci_dev *dev) { return 0; } +void __weak pci_remove_resource_files(struct pci_dev *dev) { return; } #endif /* HAVE_PCI_MMAP */ /** -- cgit v1.2.3 From ae40582e9959cdb7bfe4b918be8e3d19f9511798 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 20 Feb 2009 20:16:07 -0800 Subject: PCI: pcie_portdriver: fix pcie_port_device_remove pcie_port_device_remove currently calls the remove method of port drivers twice. Ouch! We are calling device_for_each_child multiple times for no apparent reason. So make it simple. Place put_device and device_unregister into remove_iter, and throw out the rest. Only call device_for_each_child once. The code is simpler and actually works! Signed-off-by: Eric W. Biederman Signed-off-by: Jesse Barnes --- drivers/pci/pcie/portdrv_core.c | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 3aea92a92928..569af0015fce 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -456,16 +456,9 @@ int pcie_port_device_resume(struct pci_dev *dev) static int remove_iter(struct device *dev, void *data) { - struct pcie_port_service_driver *service_driver; - if (dev->bus == &pcie_port_bus_type) { - if (dev->driver) { - service_driver = to_service_driver(dev->driver); - if (service_driver->remove) - service_driver->remove(to_pcie_device(dev)); - } - *(unsigned long*)data = (unsigned long)dev; - return 1; + put_device(dev); + device_unregister(dev); } return 0; } @@ -480,18 +473,8 @@ static int remove_iter(struct device *dev, void *data) void pcie_port_device_remove(struct pci_dev *dev) { struct pcie_port_data *port_data = pci_get_drvdata(dev); - int status; - - do { - unsigned long device_addr; - status = device_for_each_child(&dev->dev, &device_addr, remove_iter); - if (status) { - struct device *device = (struct device*)device_addr; - put_device(device); - device_unregister(device); - } - } while (status); + device_for_each_child(&dev->dev, NULL, remove_iter); switch (port_data->port_irq_mode) { case PCIE_PORT_MSIX_MODE: -- cgit v1.2.3 From 3a3c244c9a355105bc193fde873c73727bf87192 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sun, 15 Feb 2009 22:32:48 +0100 Subject: PCI: PCIe portdrv: Implement pm object Implement pm object for the PCI Express port driver in order to use the new power management framework and reduce the code size. Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/pciehp_core.c | 4 ++-- drivers/pci/pcie/aer/aerdrv.c | 6 ------ drivers/pci/pcie/portdrv.h | 4 ++-- drivers/pci/pcie/portdrv_core.c | 14 ++++++-------- drivers/pci/pcie/portdrv_pci.c | 31 +++++++++++++++---------------- include/linux/pcieport_if.h | 2 +- 6 files changed, 26 insertions(+), 35 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index 3d21bbba3308..fb254b2454de 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -475,7 +475,7 @@ static void pciehp_remove (struct pcie_device *dev) } #ifdef CONFIG_PM -static int pciehp_suspend (struct pcie_device *dev, pm_message_t state) +static int pciehp_suspend (struct pcie_device *dev) { dev_info(&dev->device, "%s ENTRY\n", __func__); return 0; @@ -503,7 +503,7 @@ static int pciehp_resume (struct pcie_device *dev) } return 0; } -#endif +#endif /* PM */ static struct pcie_port_service_driver hpdriver_portdrv = { .name = PCIE_MODULE_NAME, diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c index e11c03194063..32ade5af927e 100644 --- a/drivers/pci/pcie/aer/aerdrv.c +++ b/drivers/pci/pcie/aer/aerdrv.c @@ -40,9 +40,6 @@ MODULE_LICENSE("GPL"); static int __devinit aer_probe (struct pcie_device *dev); static void aer_remove(struct pcie_device *dev); -static int aer_suspend(struct pcie_device *dev, pm_message_t state) -{return 0;} -static int aer_resume(struct pcie_device *dev) {return 0;} static pci_ers_result_t aer_error_detected(struct pci_dev *dev, enum pci_channel_state error); static void aer_error_resume(struct pci_dev *dev); @@ -61,9 +58,6 @@ static struct pcie_port_service_driver aerdriver = { .probe = aer_probe, .remove = aer_remove, - .suspend = aer_suspend, - .resume = aer_resume, - .err_handler = &aer_error_handlers, .reset_link = aer_root_reset, diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h index 5b818bd835ef..17ad53868f9f 100644 --- a/drivers/pci/pcie/portdrv.h +++ b/drivers/pci/pcie/portdrv.h @@ -38,8 +38,8 @@ extern struct bus_type pcie_port_bus_type; extern int pcie_port_device_probe(struct pci_dev *dev); extern int pcie_port_device_register(struct pci_dev *dev); #ifdef CONFIG_PM -extern int pcie_port_device_suspend(struct pci_dev *dev, pm_message_t state); -extern int pcie_port_device_resume(struct pci_dev *dev); +extern int pcie_port_device_suspend(struct device *dev); +extern int pcie_port_device_resume(struct device *dev); #endif extern void pcie_port_device_remove(struct pci_dev *dev); extern int __must_check pcie_port_bus_register(void); diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 569af0015fce..5a5bfe7cdf5f 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -410,13 +410,12 @@ int pcie_port_device_register(struct pci_dev *dev) static int suspend_iter(struct device *dev, void *data) { struct pcie_port_service_driver *service_driver; - pm_message_t state = * (pm_message_t *) data; if ((dev->bus == &pcie_port_bus_type) && (dev->driver)) { service_driver = to_service_driver(dev->driver); if (service_driver->suspend) - service_driver->suspend(to_pcie_device(dev), state); + service_driver->suspend(to_pcie_device(dev)); } return 0; } @@ -424,11 +423,10 @@ static int suspend_iter(struct device *dev, void *data) /** * pcie_port_device_suspend - suspend port services associated with a PCIe port * @dev: PCI Express port to handle - * @state: Representation of system power management transition in progress */ -int pcie_port_device_suspend(struct pci_dev *dev, pm_message_t state) +int pcie_port_device_suspend(struct device *dev) { - return device_for_each_child(&dev->dev, &state, suspend_iter); + return device_for_each_child(dev, NULL, suspend_iter); } static int resume_iter(struct device *dev, void *data) @@ -448,11 +446,11 @@ static int resume_iter(struct device *dev, void *data) * pcie_port_device_suspend - resume port services associated with a PCIe port * @dev: PCI Express port to handle */ -int pcie_port_device_resume(struct pci_dev *dev) +int pcie_port_device_resume(struct device *dev) { - return device_for_each_child(&dev->dev, NULL, resume_iter); + return device_for_each_child(dev, NULL, resume_iter); } -#endif +#endif /* PM */ static int remove_iter(struct device *dev, void *data) { diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index 94d0e2af9bad..a61f4930d676 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -44,21 +44,21 @@ static int pcie_portdrv_restore_config(struct pci_dev *dev) } #ifdef CONFIG_PM -static int pcie_portdrv_suspend(struct pci_dev *dev, pm_message_t state) -{ - return pcie_port_device_suspend(dev, state); +static struct dev_pm_ops pcie_portdrv_pm_ops = { + .suspend = pcie_port_device_suspend, + .resume = pcie_port_device_resume, + .freeze = pcie_port_device_suspend, + .thaw = pcie_port_device_resume, + .poweroff = pcie_port_device_suspend, + .restore = pcie_port_device_resume, +}; -} +#define PCIE_PORTDRV_PM_OPS (&pcie_portdrv_pm_ops) -static int pcie_portdrv_resume(struct pci_dev *dev) -{ - pci_set_master(dev); - return pcie_port_device_resume(dev); -} -#else -#define pcie_portdrv_suspend NULL -#define pcie_portdrv_resume NULL -#endif +#else /* !PM */ + +#define PCIE_PORTDRV_PM_OPS NULL +#endif /* !PM */ /* * pcie_portdrv_probe - Probe PCI-Express port devices @@ -268,10 +268,9 @@ static struct pci_driver pcie_portdriver = { .probe = pcie_portdrv_probe, .remove = pcie_portdrv_remove, - .suspend = pcie_portdrv_suspend, - .resume = pcie_portdrv_resume, - .err_handler = &pcie_portdrv_err_handler, + + .driver.pm = PCIE_PORTDRV_PM_OPS, }; static int __init pcie_portdrv_init(void) diff --git a/include/linux/pcieport_if.h b/include/linux/pcieport_if.h index 5d2afcfa6bc1..b4c79545330b 100644 --- a/include/linux/pcieport_if.h +++ b/include/linux/pcieport_if.h @@ -59,7 +59,7 @@ struct pcie_port_service_driver { const char *name; int (*probe) (struct pcie_device *dev); void (*remove) (struct pcie_device *dev); - int (*suspend) (struct pcie_device *dev, pm_message_t state); + int (*suspend) (struct pcie_device *dev); int (*resume) (struct pcie_device *dev); /* Service Error Recovery Handler */ -- cgit v1.2.3 From 267efd7eec5eca62f32f8c9bc1721b578d5da963 Mon Sep 17 00:00:00 2001 From: Kenji Kaneshige Date: Tue, 17 Feb 2009 14:13:20 +0900 Subject: PCI hotplug: fix wrong assumption in acpi_get_hp_params_from_firmware Current acpi_get_hp_params_from_firmware() has a assumption that pci_bus->self is NULL on the root pci bus. But it might not true on some platforms. Because of this wrong assumption, current acpi_get_hp_params_from_firmware() might cause endless loop. We must check pci_bus->parent instead. Signed-off-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/acpi_pcihp.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/acpi_pcihp.c b/drivers/pci/hotplug/acpi_pcihp.c index f47bc74be567..09a84402986d 100644 --- a/drivers/pci/hotplug/acpi_pcihp.c +++ b/drivers/pci/hotplug/acpi_pcihp.c @@ -332,19 +332,14 @@ acpi_status acpi_get_hp_params_from_firmware(struct pci_bus *bus, { acpi_status status = AE_NOT_FOUND; acpi_handle handle, phandle; - struct pci_bus *pbus = bus; - struct pci_dev *pdev; + struct pci_bus *pbus; - do { - pdev = pbus->self; - if (!pdev) { - handle = acpi_get_pci_rootbridge_handle( - pci_domain_nr(pbus), pbus->number); + handle = NULL; + for (pbus = bus; pbus; pbus = pbus->parent) { + handle = acpi_pci_get_bridge_handle(pbus); + if (handle) break; - } - handle = DEVICE_ACPI_HANDLE(&(pdev->dev)); - pbus = pbus->parent; - } while (!handle); + } /* * _HPP settings apply to all child buses, until another _HPP is -- cgit v1.2.3 From d391f00f0e7fb6d883c6724b31a1799e19a584c5 Mon Sep 17 00:00:00 2001 From: Kenji Kaneshige Date: Tue, 17 Feb 2009 14:13:59 +0900 Subject: PCI hotplug: fix wrong assumption in acpi_get_hp_hw_control_from_firmware Current acpi_get_hp_hw_control_from_firmware() has a assumption that pci_bus->self is NULL on a PCI root bus. But it might not be true on some platforms. Because of this wrong assumption, current acpi_get_hp_hw_control_from_firmware() might cause endless loop. We must check pci_bus->parent instead. Signed-off-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/acpi_pcihp.c | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/acpi_pcihp.c b/drivers/pci/hotplug/acpi_pcihp.c index 09a84402986d..fbc63d5e459f 100644 --- a/drivers/pci/hotplug/acpi_pcihp.c +++ b/drivers/pci/hotplug/acpi_pcihp.c @@ -372,12 +372,10 @@ EXPORT_SYMBOL_GPL(acpi_get_hp_params_from_firmware); * * Attempt to take hotplug control from firmware. */ -int acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev, u32 flags) +int acpi_get_hp_hw_control_from_firmware(struct pci_dev *pdev, u32 flags) { acpi_status status; acpi_handle chandle, handle; - struct pci_dev *pdev = dev; - struct pci_bus *parent; struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL }; flags &= (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL | @@ -409,26 +407,18 @@ int acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev, u32 flags) string = (struct acpi_buffer){ ACPI_ALLOCATE_BUFFER, NULL }; } - pdev = dev; - handle = DEVICE_ACPI_HANDLE(&dev->dev); - while (!handle) { + handle = DEVICE_ACPI_HANDLE(&pdev->dev); + if (!handle) { /* * This hotplug controller was not listed in the ACPI name * space at all. Try to get acpi handle of parent pci bus. */ - if (!pdev || !pdev->bus->parent) - break; - parent = pdev->bus->parent; - dbg("Could not find %s in acpi namespace, trying parent\n", - pci_name(pdev)); - if (!parent->self) - /* Parent must be a host bridge */ - handle = acpi_get_pci_rootbridge_handle( - pci_domain_nr(parent), - parent->number); - else - handle = DEVICE_ACPI_HANDLE(&(parent->self->dev)); - pdev = parent->self; + struct pci_bus *pbus; + for (pbus = pdev->bus; pbus; pbus = pbus->parent) { + handle = acpi_pci_get_bridge_handle(pbus); + if (handle) + break; + } } while (handle) { @@ -447,13 +437,13 @@ int acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev, u32 flags) } dbg("Cannot get control of hotplug hardware for pci %s\n", - pci_name(dev)); + pci_name(pdev)); kfree(string.pointer); return -ENODEV; got_one: - dbg("Gained control for hotplug HW for pci %s (%s)\n", pci_name(dev), - (char *)string.pointer); + dbg("Gained control for hotplug HW for pci %s (%s)\n", + pci_name(pdev), (char *)string.pointer); kfree(string.pointer); return 0; } -- cgit v1.2.3 From 151ab36a2ea0b3181d103f7244636e0d16e685de Mon Sep 17 00:00:00 2001 From: Kenji Kaneshige Date: Tue, 17 Feb 2009 14:14:36 +0900 Subject: PCI: fix wrong assumption in pci_find_upstream_pcie_bridge Current pci_find_upstream_pcie_bridge() has a wrong assumption that pci_bus->self is NULL on the root pci bus. But it might not true on some platforms. Because of this wrong assumption, current pci_find_upstream_pcie_bridge() might cause endless loop. We must check pci_bus->parent instead. Signed-off-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/search.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/search.c b/drivers/pci/search.c index 5af8bd538149..710d4ea69568 100644 --- a/drivers/pci/search.c +++ b/drivers/pci/search.c @@ -29,7 +29,7 @@ pci_find_upstream_pcie_bridge(struct pci_dev *pdev) if (pdev->is_pcie) return NULL; while (1) { - if (!pdev->bus->self) + if (!pdev->bus->parent) break; pdev = pdev->bus->self; /* a p2p bridge */ -- cgit v1.2.3 From f92d4e29d785f1d4217dee7f1ae6ff7140547ed5 Mon Sep 17 00:00:00 2001 From: Kenji Kaneshige Date: Tue, 17 Feb 2009 14:15:16 +0900 Subject: PCI: fix wrong assumption in pci_read_bridge_bases Current pci_read_bridge_bases() has an assumption that pci_bus->self is NULL on the pci root bus (It checks pci_bus->self to see if the pci bus is root bus). But is might not true on some platforms. We must check pci_bus->parent instead. Signed-off-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/probe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 55ec44a27e89..23362e8c696f 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -287,7 +287,7 @@ void __devinit pci_read_bridge_bases(struct pci_bus *child) struct resource *res; int i; - if (!dev) /* It's a host bus, nothing to read */ + if (!child->parent) /* It's a host bus, nothing to read */ return; if (dev->transparent) { -- cgit v1.2.3 From c2a3072e010943ac749794622f26b3ef54de25be Mon Sep 17 00:00:00 2001 From: Kenji Kaneshige Date: Tue, 17 Feb 2009 14:15:45 +0900 Subject: PCI: fix wrong assumption in pci_get_interrupt_pin Current pci_get_interrupt_pin() seems to have an assumption that pci_bus->self is NULL on the root pci bus. But it might not be true on some platforms. Because of this wrong assumption, current pci_get_interrupt_pin() might cause endless loop. We must check pci_bus->parent instead. Signed-off-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 0b3e20f1b6f7..0cfed9e28ea1 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1484,7 +1484,7 @@ pci_get_interrupt_pin(struct pci_dev *dev, struct pci_dev **bridge) if (!pin) return -1; - while (dev->bus->self) { + while (dev->bus->parent) { pin = pci_swizzle_interrupt_pin(dev, pin); dev = dev->bus->self; } -- cgit v1.2.3 From c74d724462d1845535667f4d3f720e02e3432e53 Mon Sep 17 00:00:00 2001 From: Kenji Kaneshige Date: Tue, 17 Feb 2009 14:16:13 +0900 Subject: PCI: fix wrong assumption in pci_common_swizzle Current pci_common_swizzle() seems to have a assumption that pci_bus->self is NULL on the pci root bus. But it might not be true on some platforms. Because of this wrong assumption, pci_common_swizzle() might cause endless loop. We must check pci_bus->parent instead. Signed-off-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 0cfed9e28ea1..8310dc2f943b 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1504,7 +1504,7 @@ u8 pci_common_swizzle(struct pci_dev *dev, u8 *pinp) { u8 pin = *pinp; - while (dev->bus->self) { + while (dev->bus->parent) { pin = pci_swizzle_interrupt_pin(dev, pin); dev = dev->bus->self; } -- cgit v1.2.3 From 0994375e9614f78657031e04e30019b9cdb62795 Mon Sep 17 00:00:00 2001 From: Chris Wright Date: Mon, 23 Feb 2009 21:52:23 -0800 Subject: PCI: add remove_id sysfs entry This adds a remove_id sysfs entry to allow users of new_id to later remove the added dynid. One use case is management tools that want to dynamically bind/unbind devices to pci-stub driver while devices are assigned to KVM guests. Rather than having to track which driver was originally bound to the driver, a mangement tool can simply: Guest uses device Signed-off-by: Chris Wright Signed-off-by: Jesse Barnes --- Documentation/ABI/testing/sysfs-bus-pci | 16 +++++++ drivers/pci/pci-driver.c | 80 ++++++++++++++++++++++++++++++++- 2 files changed, 94 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci index e638e15a8895..3d29793a0ea2 100644 --- a/Documentation/ABI/testing/sysfs-bus-pci +++ b/Documentation/ABI/testing/sysfs-bus-pci @@ -41,6 +41,22 @@ Description: for the device and attempt to bind to it. For example: # echo "8086 10f5" > /sys/bus/pci/drivers/foo/new_id +What: /sys/bus/pci/drivers/.../remove_id +Date: February 2009 +Contact: Chris Wright +Description: + Writing a device ID to this file will remove an ID + that was dynamically added via the new_id sysfs entry. + The format for the device ID is: + VVVV DDDD SVVV SDDD CCCC MMMM. That is Vendor ID, Device + ID, Subsystem Vendor ID, Subsystem Device ID, Class, + and Class Mask. The Vendor ID and Device ID fields are + required, the rest are optional. After successfully + removing an ID, the driver will no longer support the + device. This is useful to ensure auto probing won't + match the driver to the device. For example: + # echo "8086 10f5" > /sys/bus/pci/drivers/foo/remove_id + What: /sys/bus/pci/devices/.../vpd Date: February 2008 Contact: Ben Hutchings diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 93eac1423585..87a5ddbb324f 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -99,6 +99,52 @@ store_new_id(struct device_driver *driver, const char *buf, size_t count) } static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id); +/** + * store_remove_id - remove a PCI device ID from this driver + * @driver: target device driver + * @buf: buffer for scanning device ID data + * @count: input size + * + * Removes a dynamic pci device ID to this driver. + */ +static ssize_t +store_remove_id(struct device_driver *driver, const char *buf, size_t count) +{ + struct pci_dynid *dynid, *n; + struct pci_driver *pdrv = to_pci_driver(driver); + __u32 vendor, device, subvendor = PCI_ANY_ID, + subdevice = PCI_ANY_ID, class = 0, class_mask = 0; + int fields = 0; + int retval = -ENODEV; + + fields = sscanf(buf, "%x %x %x %x %x %x", + &vendor, &device, &subvendor, &subdevice, + &class, &class_mask); + if (fields < 2) + return -EINVAL; + + spin_lock(&pdrv->dynids.lock); + list_for_each_entry_safe(dynid, n, &pdrv->dynids.list, node) { + struct pci_device_id *id = &dynid->id; + if ((id->vendor == vendor) && + (id->device == device) && + (subvendor == PCI_ANY_ID || id->subvendor == subvendor) && + (subdevice == PCI_ANY_ID || id->subdevice == subdevice) && + !((id->class ^ class) & class_mask)) { + list_del(&dynid->node); + kfree(dynid); + retval = 0; + break; + } + } + spin_unlock(&pdrv->dynids.lock); + + if (retval) + return retval; + return count; +} +static DRIVER_ATTR(remove_id, S_IWUSR, NULL, store_remove_id); + static void pci_free_dynids(struct pci_driver *drv) { @@ -125,6 +171,20 @@ static void pci_remove_newid_file(struct pci_driver *drv) { driver_remove_file(&drv->driver, &driver_attr_new_id); } + +static int +pci_create_removeid_file(struct pci_driver *drv) +{ + int error = 0; + if (drv->probe != NULL) + error = driver_create_file(&drv->driver,&driver_attr_remove_id); + return error; +} + +static void pci_remove_removeid_file(struct pci_driver *drv) +{ + driver_remove_file(&drv->driver, &driver_attr_remove_id); +} #else /* !CONFIG_HOTPLUG */ static inline void pci_free_dynids(struct pci_driver *drv) {} static inline int pci_create_newid_file(struct pci_driver *drv) @@ -132,6 +192,11 @@ static inline int pci_create_newid_file(struct pci_driver *drv) return 0; } static inline void pci_remove_newid_file(struct pci_driver *drv) {} +static inline int pci_create_removeid_file(struct pci_driver *drv) +{ + return 0; +} +static inline void pci_remove_removeid_file(struct pci_driver *drv) {} #endif /** @@ -852,13 +917,23 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner, /* register with core */ error = driver_register(&drv->driver); if (error) - return error; + goto out; error = pci_create_newid_file(drv); if (error) - driver_unregister(&drv->driver); + goto out_newid; + error = pci_create_removeid_file(drv); + if (error) + goto out_removeid; +out: return error; + +out_removeid: + pci_remove_newid_file(drv); +out_newid: + driver_unregister(&drv->driver); + goto out; } /** @@ -874,6 +949,7 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner, void pci_unregister_driver(struct pci_driver *drv) { + pci_remove_removeid_file(drv); pci_remove_newid_file(drv); driver_unregister(&drv->driver); pci_free_dynids(drv); -- cgit v1.2.3 From 24d27553390c69d11cdbd930d635193956fc295f Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Tue, 17 Mar 2009 08:54:06 -0400 Subject: PCI MSI: Replace 'type' with 'is_msix' By changing from a 5-bit field to a 1-bit field, we free up some bits that can be used by a later patch. Also rearrange the fields for better packing on 64-bit platforms (reducing the size of msi_desc from 72 bytes to 64 bytes). Signed-off-by: Matthew Wilcox Signed-off-by: Jesse Barnes --- drivers/pci/msi.c | 115 ++++++++++++++++++---------------------------------- include/linux/msi.h | 4 +- 2 files changed, 41 insertions(+), 78 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index dceea56f7342..b3db4388f974 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -111,20 +111,10 @@ static void msix_flush_writes(struct irq_desc *desc) entry = get_irq_desc_msi(desc); BUG_ON(!entry || !entry->dev); - switch (entry->msi_attrib.type) { - case PCI_CAP_ID_MSI: - /* nothing to do */ - break; - case PCI_CAP_ID_MSIX: - { + if (entry->msi_attrib.is_msix) { int offset = entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET; readl(entry->mask_base + offset); - break; - } - default: - BUG(); - break; } } @@ -143,32 +133,23 @@ static int msi_set_mask_bits(struct irq_desc *desc, u32 mask, u32 flag) entry = get_irq_desc_msi(desc); BUG_ON(!entry || !entry->dev); - switch (entry->msi_attrib.type) { - case PCI_CAP_ID_MSI: - if (entry->msi_attrib.maskbit) { - int pos; - u32 mask_bits; - - pos = (long)entry->mask_base; - pci_read_config_dword(entry->dev, pos, &mask_bits); - mask_bits &= ~(mask); - mask_bits |= flag & mask; - pci_write_config_dword(entry->dev, pos, mask_bits); - } else { - return 0; - } - break; - case PCI_CAP_ID_MSIX: - { + if (entry->msi_attrib.is_msix) { int offset = entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET; writel(flag, entry->mask_base + offset); readl(entry->mask_base + offset); - break; - } - default: - BUG(); - break; + } else { + int pos; + u32 mask_bits; + + if (!entry->msi_attrib.maskbit) + return 0; + + pos = (long)entry->mask_base; + pci_read_config_dword(entry->dev, pos, &mask_bits); + mask_bits &= ~mask; + mask_bits |= flag & mask; + pci_write_config_dword(entry->dev, pos, mask_bits); } entry->msi_attrib.masked = !!flag; return 1; @@ -177,9 +158,14 @@ static int msi_set_mask_bits(struct irq_desc *desc, u32 mask, u32 flag) void read_msi_msg_desc(struct irq_desc *desc, struct msi_msg *msg) { struct msi_desc *entry = get_irq_desc_msi(desc); - switch(entry->msi_attrib.type) { - case PCI_CAP_ID_MSI: - { + if (entry->msi_attrib.is_msix) { + void __iomem *base = entry->mask_base + + entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE; + + msg->address_lo = readl(base + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET); + msg->address_hi = readl(base + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET); + msg->data = readl(base + PCI_MSIX_ENTRY_DATA_OFFSET); + } else { struct pci_dev *dev = entry->dev; int pos = entry->msi_attrib.pos; u16 data; @@ -195,21 +181,6 @@ void read_msi_msg_desc(struct irq_desc *desc, struct msi_msg *msg) pci_read_config_word(dev, msi_data_reg(pos, 0), &data); } msg->data = data; - break; - } - case PCI_CAP_ID_MSIX: - { - void __iomem *base; - base = entry->mask_base + - entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE; - - msg->address_lo = readl(base + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET); - msg->address_hi = readl(base + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET); - msg->data = readl(base + PCI_MSIX_ENTRY_DATA_OFFSET); - break; - } - default: - BUG(); } } @@ -223,9 +194,17 @@ void read_msi_msg(unsigned int irq, struct msi_msg *msg) void write_msi_msg_desc(struct irq_desc *desc, struct msi_msg *msg) { struct msi_desc *entry = get_irq_desc_msi(desc); - switch (entry->msi_attrib.type) { - case PCI_CAP_ID_MSI: - { + if (entry->msi_attrib.is_msix) { + void __iomem *base; + base = entry->mask_base + + entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE; + + writel(msg->address_lo, + base + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET); + writel(msg->address_hi, + base + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET); + writel(msg->data, base + PCI_MSIX_ENTRY_DATA_OFFSET); + } else { struct pci_dev *dev = entry->dev; int pos = entry->msi_attrib.pos; @@ -240,23 +219,6 @@ void write_msi_msg_desc(struct irq_desc *desc, struct msi_msg *msg) pci_write_config_word(dev, msi_data_reg(pos, 0), msg->data); } - break; - } - case PCI_CAP_ID_MSIX: - { - void __iomem *base; - base = entry->mask_base + - entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE; - - writel(msg->address_lo, - base + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET); - writel(msg->address_hi, - base + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET); - writel(msg->data, base + PCI_MSIX_ENTRY_DATA_OFFSET); - break; - } - default: - BUG(); } entry->msg = *msg; } @@ -393,7 +355,7 @@ static int msi_capability_init(struct pci_dev *dev) if (!entry) return -ENOMEM; - entry->msi_attrib.type = PCI_CAP_ID_MSI; + entry->msi_attrib.is_msix = 0; entry->msi_attrib.is_64 = is_64bit_address(control); entry->msi_attrib.entry_nr = 0; entry->msi_attrib.maskbit = is_mask_bit_support(control); @@ -475,7 +437,7 @@ static int msix_capability_init(struct pci_dev *dev, break; j = entries[i].entry; - entry->msi_attrib.type = PCI_CAP_ID_MSIX; + entry->msi_attrib.is_msix = 1; entry->msi_attrib.is_64 = 1; entry->msi_attrib.entry_nr = j; entry->msi_attrib.maskbit = 1; @@ -619,12 +581,13 @@ void pci_msi_shutdown(struct pci_dev* dev) struct irq_desc *desc = irq_to_desc(dev->irq); msi_set_mask_bits(desc, mask, ~mask); } - if (!entry->dev || entry->msi_attrib.type != PCI_CAP_ID_MSI) + if (!entry->dev || entry->msi_attrib.is_msix) return; /* Restore dev->irq to its default pin-assertion irq */ dev->irq = entry->msi_attrib.default_irq; } + void pci_disable_msi(struct pci_dev* dev) { struct msi_desc *entry; @@ -635,7 +598,7 @@ void pci_disable_msi(struct pci_dev* dev) pci_msi_shutdown(dev); entry = list_entry(dev->msi_list.next, struct msi_desc, list); - if (!entry->dev || entry->msi_attrib.type != PCI_CAP_ID_MSI) + if (!entry->dev || entry->msi_attrib.is_msix) return; msi_free_irqs(dev); @@ -654,7 +617,7 @@ static int msi_free_irqs(struct pci_dev* dev) arch_teardown_msi_irqs(dev); list_for_each_entry_safe(entry, tmp, &dev->msi_list, list) { - if (entry->msi_attrib.type == PCI_CAP_ID_MSIX) { + if (entry->msi_attrib.is_msix) { writel(1, entry->mask_base + entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET); diff --git a/include/linux/msi.h b/include/linux/msi.h index d2b8a1e8ca11..9c5ce214fbf4 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -20,13 +20,13 @@ extern void write_msi_msg(unsigned int irq, struct msi_msg *msg); struct msi_desc { struct { - __u8 type : 5; /* {0: unused, 5h:MSI, 11h:MSI-X} */ + __u8 is_msix : 1; __u8 maskbit : 1; /* mask-pending bit supported ? */ __u8 masked : 1; __u8 is_64 : 1; /* Address size: 0=32bit 1=64bit */ __u8 pos; /* Location of the msi capability */ - __u32 maskbits_mask; /* mask bits mask */ __u16 entry_nr; /* specific enabled entry */ + __u32 maskbits_mask; /* mask bits mask */ unsigned default_irq; /* default pre-assigned irq */ }msi_attrib; -- cgit v1.2.3 From 379f5327a86f7822a51ec7d088a085167724df75 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Tue, 17 Mar 2009 08:54:07 -0400 Subject: PCI MSI: msi_desc->dev is always initialised By passing the pci_dev into alloc_msi_entry() we can be sure that the ->dev entry is always assigned and so we don't need to check it. Also, we used kzalloc() so we don't need to initialise ->irq to 0. Signed-off-by: Matthew Wilcox Signed-off-by: Jesse Barnes --- drivers/pci/msi.c | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index b3db4388f974..a658c0f34e16 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -110,7 +110,7 @@ static void msix_flush_writes(struct irq_desc *desc) struct msi_desc *entry; entry = get_irq_desc_msi(desc); - BUG_ON(!entry || !entry->dev); + BUG_ON(!entry); if (entry->msi_attrib.is_msix) { int offset = entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET; @@ -132,7 +132,7 @@ static int msi_set_mask_bits(struct irq_desc *desc, u32 mask, u32 flag) struct msi_desc *entry; entry = get_irq_desc_msi(desc); - BUG_ON(!entry || !entry->dev); + BUG_ON(!entry); if (entry->msi_attrib.is_msix) { int offset = entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET; @@ -248,19 +248,16 @@ void unmask_msi_irq(unsigned int irq) static int msi_free_irqs(struct pci_dev* dev); -static struct msi_desc* alloc_msi_entry(void) +static struct msi_desc *alloc_msi_entry(struct pci_dev *dev) { - struct msi_desc *entry; - - entry = kzalloc(sizeof(struct msi_desc), GFP_KERNEL); - if (!entry) + struct msi_desc *desc = kzalloc(sizeof(*desc), GFP_KERNEL); + if (!desc) return NULL; - INIT_LIST_HEAD(&entry->list); - entry->irq = 0; - entry->dev = NULL; + INIT_LIST_HEAD(&desc->list); + desc->dev = dev; - return entry; + return desc; } static void pci_intx_for_msi(struct pci_dev *dev, int enable) @@ -351,7 +348,7 @@ static int msi_capability_init(struct pci_dev *dev) pos = pci_find_capability(dev, PCI_CAP_ID_MSI); pci_read_config_word(dev, msi_control_reg(pos), &control); /* MSI Entry Initialization */ - entry = alloc_msi_entry(); + entry = alloc_msi_entry(dev); if (!entry) return -ENOMEM; @@ -362,7 +359,6 @@ static int msi_capability_init(struct pci_dev *dev) entry->msi_attrib.masked = 1; entry->msi_attrib.default_irq = dev->irq; /* Save IOAPIC IRQ */ entry->msi_attrib.pos = pos; - entry->dev = dev; if (entry->msi_attrib.maskbit) { unsigned int base, maskbits, temp; @@ -432,7 +428,7 @@ static int msix_capability_init(struct pci_dev *dev, /* MSI-X Table Initialization */ for (i = 0; i < nvec; i++) { - entry = alloc_msi_entry(); + entry = alloc_msi_entry(dev); if (!entry) break; @@ -444,7 +440,6 @@ static int msix_capability_init(struct pci_dev *dev, entry->msi_attrib.masked = 1; entry->msi_attrib.default_irq = dev->irq; entry->msi_attrib.pos = pos; - entry->dev = dev; entry->mask_base = base; list_add_tail(&entry->list, &dev->msi_list); @@ -581,7 +576,7 @@ void pci_msi_shutdown(struct pci_dev* dev) struct irq_desc *desc = irq_to_desc(dev->irq); msi_set_mask_bits(desc, mask, ~mask); } - if (!entry->dev || entry->msi_attrib.is_msix) + if (entry->msi_attrib.is_msix) return; /* Restore dev->irq to its default pin-assertion irq */ @@ -598,7 +593,7 @@ void pci_disable_msi(struct pci_dev* dev) pci_msi_shutdown(dev); entry = list_entry(dev->msi_list.next, struct msi_desc, list); - if (!entry->dev || entry->msi_attrib.is_msix) + if (entry->msi_attrib.is_msix) return; msi_free_irqs(dev); -- cgit v1.2.3 From 264d9caaa1c574c0274b019a810abfe957391005 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Tue, 17 Mar 2009 08:54:08 -0400 Subject: PCI MSI: Use mask_pos instead of mask_base when appropriate MSI interrupts have a mask_pos where MSI-X have a mask_base. Use a transparent union to get rid of some ugly casts. Signed-off-by: Matthew Wilcox Signed-off-by: Jesse Barnes --- drivers/pci/msi.c | 5 ++--- include/linux/msi.h | 5 ++++- 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index a658c0f34e16..fcde04df6dfe 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -145,7 +145,7 @@ static int msi_set_mask_bits(struct irq_desc *desc, u32 mask, u32 flag) if (!entry->msi_attrib.maskbit) return 0; - pos = (long)entry->mask_base; + pos = entry->mask_pos; pci_read_config_dword(entry->dev, pos, &mask_bits); mask_bits &= ~mask; mask_bits |= flag & mask; @@ -363,8 +363,7 @@ static int msi_capability_init(struct pci_dev *dev) unsigned int base, maskbits, temp; base = msi_mask_bits_reg(pos, entry->msi_attrib.is_64); - entry->mask_base = (void __iomem *)(long)base; - + entry->mask_pos = base; /* All MSIs are unmasked by default, Mask them all */ pci_read_config_dword(dev, base, &maskbits); temp = msi_mask((control & PCI_MSI_FLAGS_QMASK) >> 1); diff --git a/include/linux/msi.h b/include/linux/msi.h index 9c5ce214fbf4..5025ca4d91e4 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -33,7 +33,10 @@ struct msi_desc { unsigned int irq; struct list_head list; - void __iomem *mask_base; + union { + void __iomem *mask_base; + u8 mask_pos; + }; struct pci_dev *dev; /* Last set MSI message */ -- cgit v1.2.3 From f2440d9acbe866b917b16cc0f927366341ce9215 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Tue, 17 Mar 2009 08:54:09 -0400 Subject: PCI MSI: Refactor interrupt masking code Since most of the callers already know whether they have an MSI or an MSI-X capability, split msi_set_mask_bits() into msi_mask_irq() and msix_mask_irq(). The only callers which don't (mask_msi_irq() and unmask_msi_irq()) can share code in msi_set_mask_bit(). This then becomes the only caller of msix_flush_writes(), so we can inline it. The flushing read can be to any address that belongs to the device, so we can eliminate the calculation too. We can also get rid of maskbits_mask from struct msi_desc and simply recalculate it on the rare occasion that we need it. The single-bit 'masked' element is replaced by a copy of the 32-bit 'masked' register, so this patch does not affect the size of msi_desc. Signed-off-by: Matthew Wilcox Signed-off-by: Jesse Barnes --- drivers/pci/msi.c | 155 +++++++++++++++++++++++++--------------------------- include/linux/msi.h | 5 +- 2 files changed, 77 insertions(+), 83 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index fcde04df6dfe..adcc78242571 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -105,17 +105,14 @@ static inline __attribute_const__ u32 msi_mask(unsigned x) return (1 << (1 << x)) - 1; } -static void msix_flush_writes(struct irq_desc *desc) +static inline __attribute_const__ u32 msi_capable_mask(u16 control) { - struct msi_desc *entry; + return msi_mask((control >> 1) & 7); +} - entry = get_irq_desc_msi(desc); - BUG_ON(!entry); - if (entry->msi_attrib.is_msix) { - int offset = entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE + - PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET; - readl(entry->mask_base + offset); - } +static inline __attribute_const__ u32 msi_enabled_mask(u16 control) +{ + return msi_mask((control >> 4) & 7); } /* @@ -127,32 +124,57 @@ static void msix_flush_writes(struct irq_desc *desc) * Returns 1 if it succeeded in masking the interrupt and 0 if the device * doesn't support MSI masking. */ -static int msi_set_mask_bits(struct irq_desc *desc, u32 mask, u32 flag) +static void msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag) { - struct msi_desc *entry; + u32 mask_bits = desc->masked; - entry = get_irq_desc_msi(desc); - BUG_ON(!entry); - if (entry->msi_attrib.is_msix) { - int offset = entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE + - PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET; - writel(flag, entry->mask_base + offset); - readl(entry->mask_base + offset); - } else { - int pos; - u32 mask_bits; + if (!desc->msi_attrib.maskbit) + return; + + mask_bits &= ~mask; + mask_bits |= flag; + pci_write_config_dword(desc->dev, desc->mask_pos, mask_bits); + desc->masked = mask_bits; +} + +/* + * This internal function does not flush PCI writes to the device. + * All users must ensure that they read from the device before either + * assuming that the device state is up to date, or returning out of this + * file. This saves a few milliseconds when initialising devices with lots + * of MSI-X interrupts. + */ +static void msix_mask_irq(struct msi_desc *desc, u32 flag) +{ + u32 mask_bits = desc->masked; + unsigned offset = desc->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET; + mask_bits &= ~1; + mask_bits |= flag; + writel(mask_bits, desc->mask_base + offset); + desc->masked = mask_bits; +} - if (!entry->msi_attrib.maskbit) - return 0; +static void msi_set_mask_bit(unsigned irq, u32 flag) +{ + struct msi_desc *desc = get_irq_msi(irq); - pos = entry->mask_pos; - pci_read_config_dword(entry->dev, pos, &mask_bits); - mask_bits &= ~mask; - mask_bits |= flag & mask; - pci_write_config_dword(entry->dev, pos, mask_bits); + if (desc->msi_attrib.is_msix) { + msix_mask_irq(desc, flag); + readl(desc->mask_base); /* Flush write to device */ + } else { + msi_mask_irq(desc, 1, flag); } - entry->msi_attrib.masked = !!flag; - return 1; +} + +void mask_msi_irq(unsigned int irq) +{ + msi_set_mask_bit(irq, 1); +} + +void unmask_msi_irq(unsigned int irq) +{ + msi_set_mask_bit(irq, 0); } void read_msi_msg_desc(struct irq_desc *desc, struct msi_msg *msg) @@ -230,22 +252,6 @@ void write_msi_msg(unsigned int irq, struct msi_msg *msg) write_msi_msg_desc(desc, msg); } -void mask_msi_irq(unsigned int irq) -{ - struct irq_desc *desc = irq_to_desc(irq); - - msi_set_mask_bits(desc, 1, 1); - msix_flush_writes(desc); -} - -void unmask_msi_irq(unsigned int irq) -{ - struct irq_desc *desc = irq_to_desc(irq); - - msi_set_mask_bits(desc, 1, 0); - msix_flush_writes(desc); -} - static int msi_free_irqs(struct pci_dev* dev); static struct msi_desc *alloc_msi_entry(struct pci_dev *dev) @@ -281,13 +287,9 @@ static void __pci_restore_msi_state(struct pci_dev *dev) pci_intx_for_msi(dev, 0); msi_set_enable(dev, 0); write_msi_msg(dev->irq, &entry->msg); - if (entry->msi_attrib.maskbit) { - struct irq_desc *desc = irq_to_desc(dev->irq); - msi_set_mask_bits(desc, entry->msi_attrib.maskbits_mask, - entry->msi_attrib.masked); - } pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &control); + msi_mask_irq(entry, msi_capable_mask(control), entry->masked); control &= ~PCI_MSI_FLAGS_QSIZE; control |= PCI_MSI_FLAGS_ENABLE; pci_write_config_word(dev, pos + PCI_MSI_FLAGS, control); @@ -307,9 +309,8 @@ static void __pci_restore_msix_state(struct pci_dev *dev) msix_set_enable(dev, 0); list_for_each_entry(entry, &dev->msi_list, list) { - struct irq_desc *desc = irq_to_desc(entry->irq); write_msi_msg(entry->irq, &entry->msg); - msi_set_mask_bits(desc, 1, entry->msi_attrib.masked); + msix_mask_irq(entry, entry->masked); } BUG_ON(list_empty(&dev->msi_list)); @@ -342,6 +343,7 @@ static int msi_capability_init(struct pci_dev *dev) struct msi_desc *entry; int pos, ret; u16 control; + unsigned mask; msi_set_enable(dev, 0); /* Ensure msi is disabled as I set it up */ @@ -356,21 +358,16 @@ static int msi_capability_init(struct pci_dev *dev) entry->msi_attrib.is_64 = is_64bit_address(control); entry->msi_attrib.entry_nr = 0; entry->msi_attrib.maskbit = is_mask_bit_support(control); - entry->msi_attrib.masked = 1; entry->msi_attrib.default_irq = dev->irq; /* Save IOAPIC IRQ */ entry->msi_attrib.pos = pos; - if (entry->msi_attrib.maskbit) { - unsigned int base, maskbits, temp; - - base = msi_mask_bits_reg(pos, entry->msi_attrib.is_64); - entry->mask_pos = base; - /* All MSIs are unmasked by default, Mask them all */ - pci_read_config_dword(dev, base, &maskbits); - temp = msi_mask((control & PCI_MSI_FLAGS_QMASK) >> 1); - maskbits |= temp; - pci_write_config_dword(dev, base, maskbits); - entry->msi_attrib.maskbits_mask = temp; - } + + entry->mask_pos = msi_mask_bits_reg(pos, entry->msi_attrib.is_64); + /* All MSIs are unmasked by default, Mask them all */ + if (entry->msi_attrib.maskbit) + pci_read_config_dword(dev, entry->mask_pos, &entry->masked); + mask = msi_capable_mask(control); + msi_mask_irq(entry, mask, mask); + list_add_tail(&entry->list, &dev->msi_list); /* Configure MSI capability structure */ @@ -435,11 +432,12 @@ static int msix_capability_init(struct pci_dev *dev, entry->msi_attrib.is_msix = 1; entry->msi_attrib.is_64 = 1; entry->msi_attrib.entry_nr = j; - entry->msi_attrib.maskbit = 1; - entry->msi_attrib.masked = 1; entry->msi_attrib.default_irq = dev->irq; entry->msi_attrib.pos = pos; entry->mask_base = base; + entry->masked = readl(base + j * PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET); + msix_mask_irq(entry, 1); list_add_tail(&entry->list, &dev->msi_list); } @@ -556,9 +554,11 @@ int pci_enable_msi(struct pci_dev* dev) } EXPORT_SYMBOL(pci_enable_msi); -void pci_msi_shutdown(struct pci_dev* dev) +void pci_msi_shutdown(struct pci_dev *dev) { - struct msi_desc *entry; + struct msi_desc *desc; + u32 mask; + u16 ctrl; if (!pci_msi_enable || !dev || !dev->msi_enabled) return; @@ -568,18 +568,13 @@ void pci_msi_shutdown(struct pci_dev* dev) dev->msi_enabled = 0; BUG_ON(list_empty(&dev->msi_list)); - entry = list_entry(dev->msi_list.next, struct msi_desc, list); - /* Return the the pci reset with msi irqs unmasked */ - if (entry->msi_attrib.maskbit) { - u32 mask = entry->msi_attrib.maskbits_mask; - struct irq_desc *desc = irq_to_desc(dev->irq); - msi_set_mask_bits(desc, mask, ~mask); - } - if (entry->msi_attrib.is_msix) - return; + desc = list_first_entry(&dev->msi_list, struct msi_desc, list); + pci_read_config_word(dev, desc->msi_attrib.pos + PCI_MSI_FLAGS, &ctrl); + mask = msi_capable_mask(ctrl); + msi_mask_irq(desc, mask, ~mask); /* Restore dev->irq to its default pin-assertion irq */ - dev->irq = entry->msi_attrib.default_irq; + dev->irq = desc->msi_attrib.default_irq; } void pci_disable_msi(struct pci_dev* dev) diff --git a/include/linux/msi.h b/include/linux/msi.h index 5025ca4d91e4..37c1bbe546e5 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -22,14 +22,13 @@ struct msi_desc { struct { __u8 is_msix : 1; __u8 maskbit : 1; /* mask-pending bit supported ? */ - __u8 masked : 1; __u8 is_64 : 1; /* Address size: 0=32bit 1=64bit */ __u8 pos; /* Location of the msi capability */ __u16 entry_nr; /* specific enabled entry */ - __u32 maskbits_mask; /* mask bits mask */ unsigned default_irq; /* default pre-assigned irq */ - }msi_attrib; + } msi_attrib; + u32 masked; /* mask bits */ unsigned int irq; struct list_head list; -- cgit v1.2.3 From 1c8d7b0a562da06d3ebe83f01b1ed553205d1ae4 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Tue, 17 Mar 2009 08:54:10 -0400 Subject: PCI MSI: Add support for multiple MSI Add the new API pci_enable_msi_block() to allow drivers to request multiple MSI and reimplement pci_enable_msi in terms of pci_enable_msi_block. Ensure that the architecture back ends don't have to know about multiple MSI. Signed-off-by: Matthew Wilcox Signed-off-by: Jesse Barnes --- Documentation/PCI/MSI-HOWTO.txt | 45 +++++++++++++++++--- arch/powerpc/kernel/msi.c | 4 ++ arch/x86/kernel/io_apic.c | 4 ++ drivers/pci/msi.c | 91 +++++++++++++++++++++++++++++------------ drivers/pci/msi.h | 6 --- include/linux/msi.h | 1 + include/linux/pci.h | 6 ++- 7 files changed, 116 insertions(+), 41 deletions(-) (limited to 'drivers') diff --git a/Documentation/PCI/MSI-HOWTO.txt b/Documentation/PCI/MSI-HOWTO.txt index 1c02431f1d1a..9494f6dc38eb 100644 --- a/Documentation/PCI/MSI-HOWTO.txt +++ b/Documentation/PCI/MSI-HOWTO.txt @@ -94,15 +94,48 @@ This function should be called before the driver calls request_irq() since enabling MSIs disables the pin-based IRQ and the driver will not receive interrupts on the old interrupt. -4.2.2 pci_disable_msi +4.2.2 pci_enable_msi_block + +int pci_enable_msi_block(struct pci_dev *dev, int count) + +This variation on the above call allows a device driver to request multiple +MSIs. The MSI specification only allows interrupts to be allocated in +powers of two, up to a maximum of 2^5 (32). + +If this function returns 0, it has succeeded in allocating at least as many +interrupts as the driver requested (it may have allocated more in order +to satisfy the power-of-two requirement). In this case, the function +enables MSI on this device and updates dev->irq to be the lowest of +the new interrupts assigned to it. The other interrupts assigned to +the device are in the range dev->irq to dev->irq + count - 1. + +If this function returns a negative number, it indicates an error and +the driver should not attempt to request any more MSI interrupts for +this device. If this function returns a positive number, it will be +less than 'count' and indicate the number of interrupts that could have +been allocated. In neither case will the irq value have been +updated, nor will the device have been switched into MSI mode. + +The device driver must decide what action to take if +pci_enable_msi_block() returns a value less than the number asked for. +Some devices can make use of fewer interrupts than the maximum they +request; in this case the driver should call pci_enable_msi_block() +again. Note that it is not guaranteed to succeed, even when the +'count' has been reduced to the value returned from a previous call to +pci_enable_msi_block(). This is because there are multiple constraints +on the number of vectors that can be allocated; pci_enable_msi_block() +will return as soon as it finds any constraint that doesn't allow the +call to succeed. + +4.2.3 pci_disable_msi void pci_disable_msi(struct pci_dev *dev) -This function should be used to undo the effect of pci_enable_msi(). -Calling it restores dev->irq to the pin-based interrupt number and frees -the previously allocated message signaled interrupt(s). The interrupt -may subsequently be assigned to another device, so drivers should not -cache the value of dev->irq. +This function should be used to undo the effect of pci_enable_msi() or +pci_enable_msi_block(). Calling it restores dev->irq to the pin-based +interrupt number and frees the previously allocated message signaled +interrupt(s). The interrupt may subsequently be assigned to another +device, so drivers should not cache the value of dev->irq. A device driver must always call free_irq() on the interrupt(s) for which it has called request_irq() before calling this function. diff --git a/arch/powerpc/kernel/msi.c b/arch/powerpc/kernel/msi.c index 3bb7d3dd28be..0c16e2a854e5 100644 --- a/arch/powerpc/kernel/msi.c +++ b/arch/powerpc/kernel/msi.c @@ -19,6 +19,10 @@ int arch_msi_check_device(struct pci_dev* dev, int nvec, int type) return -ENOSYS; } + /* PowerPC doesn't support multiple MSI yet */ + if (type == PCI_CAP_ID_MSI && nvec > 1) + return 1; + if (ppc_md.msi_check_device) { pr_debug("msi: Using platform check routine.\n"); return ppc_md.msi_check_device(dev, nvec, type); diff --git a/arch/x86/kernel/io_apic.c b/arch/x86/kernel/io_apic.c index bc7ac4da90d7..a09549a6321b 100644 --- a/arch/x86/kernel/io_apic.c +++ b/arch/x86/kernel/io_apic.c @@ -3510,6 +3510,10 @@ int arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) int index = 0; #endif + /* x86 doesn't support multiple MSI yet */ + if (type == PCI_CAP_ID_MSI && nvec > 1) + return 1; + irq_want = nr_irqs_gsi; sub_handle = 0; list_for_each_entry(msidesc, &dev->msi_list, list) { diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index adcc78242571..6f2e6295e773 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -40,6 +40,13 @@ int arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) struct msi_desc *entry; int ret; + /* + * If an architecture wants to support multiple MSI, it needs to + * override arch_setup_msi_irqs() + */ + if (type == PCI_CAP_ID_MSI && nvec > 1) + return 1; + list_for_each_entry(entry, &dev->msi_list, list) { ret = arch_setup_msi_irq(dev, entry); if (ret < 0) @@ -58,8 +65,12 @@ void arch_teardown_msi_irqs(struct pci_dev *dev) struct msi_desc *entry; list_for_each_entry(entry, &dev->msi_list, list) { - if (entry->irq != 0) - arch_teardown_msi_irq(entry->irq); + int i, nvec; + if (entry->irq == 0) + continue; + nvec = 1 << entry->msi_attrib.multiple; + for (i = 0; i < nvec; i++) + arch_teardown_msi_irq(entry->irq + i); } } #endif @@ -163,7 +174,8 @@ static void msi_set_mask_bit(unsigned irq, u32 flag) msix_mask_irq(desc, flag); readl(desc->mask_base); /* Flush write to device */ } else { - msi_mask_irq(desc, 1, flag); + unsigned offset = irq - desc->dev->irq; + msi_mask_irq(desc, 1 << offset, flag << offset); } } @@ -229,6 +241,12 @@ void write_msi_msg_desc(struct irq_desc *desc, struct msi_msg *msg) } else { struct pci_dev *dev = entry->dev; int pos = entry->msi_attrib.pos; + u16 msgctl; + + pci_read_config_word(dev, msi_control_reg(pos), &msgctl); + msgctl &= ~PCI_MSI_FLAGS_QSIZE; + msgctl |= entry->msi_attrib.multiple << 4; + pci_write_config_word(dev, msi_control_reg(pos), msgctl); pci_write_config_dword(dev, msi_lower_address_reg(pos), msg->address_lo); @@ -291,7 +309,7 @@ static void __pci_restore_msi_state(struct pci_dev *dev) pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &control); msi_mask_irq(entry, msi_capable_mask(control), entry->masked); control &= ~PCI_MSI_FLAGS_QSIZE; - control |= PCI_MSI_FLAGS_ENABLE; + control |= (entry->msi_attrib.multiple << 4) | PCI_MSI_FLAGS_ENABLE; pci_write_config_word(dev, pos + PCI_MSI_FLAGS, control); } @@ -332,13 +350,15 @@ EXPORT_SYMBOL_GPL(pci_restore_msi_state); /** * msi_capability_init - configure device's MSI capability structure * @dev: pointer to the pci_dev data structure of MSI device function + * @nvec: number of interrupts to allocate * - * Setup the MSI capability structure of device function with a single - * MSI irq, regardless of device function is capable of handling - * multiple messages. A return of zero indicates the successful setup - * of an entry zero with the new MSI irq or non-zero for otherwise. - **/ -static int msi_capability_init(struct pci_dev *dev) + * Setup the MSI capability structure of the device with the requested + * number of interrupts. A return value of zero indicates the successful + * setup of an entry with the new MSI irq. A negative return value indicates + * an error, and a positive return value indicates the number of interrupts + * which could have been allocated. + */ +static int msi_capability_init(struct pci_dev *dev, int nvec) { struct msi_desc *entry; int pos, ret; @@ -371,7 +391,7 @@ static int msi_capability_init(struct pci_dev *dev) list_add_tail(&entry->list, &dev->msi_list); /* Configure MSI capability structure */ - ret = arch_setup_msi_irqs(dev, 1, PCI_CAP_ID_MSI); + ret = arch_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSI); if (ret) { msi_free_irqs(dev); return ret; @@ -524,35 +544,48 @@ static int pci_msi_check_device(struct pci_dev* dev, int nvec, int type) } /** - * pci_enable_msi - configure device's MSI capability structure - * @dev: pointer to the pci_dev data structure of MSI device function + * pci_enable_msi_block - configure device's MSI capability structure + * @dev: device to configure + * @nvec: number of interrupts to configure * - * Setup the MSI capability structure of device function with - * a single MSI irq upon its software driver call to request for - * MSI mode enabled on its hardware device function. A return of zero - * indicates the successful setup of an entry zero with the new MSI - * irq or non-zero for otherwise. - **/ -int pci_enable_msi(struct pci_dev* dev) + * Allocate IRQs for a device with the MSI capability. + * This function returns a negative errno if an error occurs. If it + * is unable to allocate the number of interrupts requested, it returns + * the number of interrupts it might be able to allocate. If it successfully + * allocates at least the number of interrupts requested, it returns 0 and + * updates the @dev's irq member to the lowest new interrupt number; the + * other interrupt numbers allocated to this device are consecutive. + */ +int pci_enable_msi_block(struct pci_dev *dev, unsigned int nvec) { - int status; + int status, pos, maxvec; + u16 msgctl; + + pos = pci_find_capability(dev, PCI_CAP_ID_MSI); + if (!pos) + return -EINVAL; + pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &msgctl); + maxvec = 1 << ((msgctl & PCI_MSI_FLAGS_QMASK) >> 1); + if (nvec > maxvec) + return maxvec; - status = pci_msi_check_device(dev, 1, PCI_CAP_ID_MSI); + status = pci_msi_check_device(dev, nvec, PCI_CAP_ID_MSI); if (status) return status; WARN_ON(!!dev->msi_enabled); - /* Check whether driver already requested for MSI-X irqs */ + /* Check whether driver already requested MSI-X irqs */ if (dev->msix_enabled) { dev_info(&dev->dev, "can't enable MSI " "(MSI-X already enabled)\n"); return -EINVAL; } - status = msi_capability_init(dev); + + status = msi_capability_init(dev, nvec); return status; } -EXPORT_SYMBOL(pci_enable_msi); +EXPORT_SYMBOL(pci_enable_msi_block); void pci_msi_shutdown(struct pci_dev *dev) { @@ -599,8 +632,12 @@ static int msi_free_irqs(struct pci_dev* dev) struct msi_desc *entry, *tmp; list_for_each_entry(entry, &dev->msi_list, list) { - if (entry->irq) - BUG_ON(irq_has_action(entry->irq)); + int i, nvec; + if (!entry->irq) + continue; + nvec = 1 << entry->msi_attrib.multiple; + for (i = 0; i < nvec; i++) + BUG_ON(irq_has_action(entry->irq + i)); } arch_teardown_msi_irqs(dev); diff --git a/drivers/pci/msi.h b/drivers/pci/msi.h index 3898f5237144..71f4df2ef654 100644 --- a/drivers/pci/msi.h +++ b/drivers/pci/msi.h @@ -20,14 +20,8 @@ #define msi_mask_bits_reg(base, is64bit) \ ( (is64bit == 1) ? base+PCI_MSI_MASK_BIT : base+PCI_MSI_MASK_BIT-4) #define msi_disable(control) control &= ~PCI_MSI_FLAGS_ENABLE -#define multi_msi_capable(control) \ - (1 << ((control & PCI_MSI_FLAGS_QMASK) >> 1)) -#define multi_msi_enable(control, num) \ - control |= (((num >> 1) << 4) & PCI_MSI_FLAGS_QSIZE); #define is_64bit_address(control) (!!(control & PCI_MSI_FLAGS_64BIT)) #define is_mask_bit_support(control) (!!(control & PCI_MSI_FLAGS_MASKBIT)) -#define msi_enable(control, num) multi_msi_enable(control, num); \ - control |= PCI_MSI_FLAGS_ENABLE #define msix_table_offset_reg(base) (base + 0x04) #define msix_pba_offset_reg(base) (base + 0x08) diff --git a/include/linux/msi.h b/include/linux/msi.h index 37c1bbe546e5..6991ab5b24d1 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -21,6 +21,7 @@ extern void write_msi_msg(unsigned int irq, struct msi_msg *msg); struct msi_desc { struct { __u8 is_msix : 1; + __u8 multiple: 3; /* log2 number of messages */ __u8 maskbit : 1; /* mask-pending bit supported ? */ __u8 is_64 : 1; /* Address size: 0=32bit 1=64bit */ __u8 pos; /* Location of the msi capability */ diff --git a/include/linux/pci.h b/include/linux/pci.h index 7baf2a5db12a..1f6c5ddaae36 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -789,7 +789,7 @@ struct msix_entry { #ifndef CONFIG_PCI_MSI -static inline int pci_enable_msi(struct pci_dev *dev) +static inline int pci_enable_msi_block(struct pci_dev *dev, unsigned int nvec) { return -1; } @@ -824,7 +824,7 @@ static inline int pci_msi_enabled(void) return 0; } #else -extern int pci_enable_msi(struct pci_dev *dev); +extern int pci_enable_msi_block(struct pci_dev *dev, unsigned int nvec); extern void pci_msi_shutdown(struct pci_dev *dev); extern void pci_disable_msi(struct pci_dev *dev); extern int pci_msix_table_size(struct pci_dev *dev); @@ -846,6 +846,8 @@ static inline int pcie_aspm_enabled(void) extern int pcie_aspm_enabled(void); #endif +#define pci_enable_msi(pdev) pci_enable_msi_block(pdev, 1) + #ifdef CONFIG_HT_IRQ /* The functions a driver should call */ int ht_create_irq(struct pci_dev *dev, int idx); -- cgit v1.2.3 From 32a9a682bef2f6fce7026bd94d1ce20028b0e52d Mon Sep 17 00:00:00 2001 From: Yuji Shimada Date: Mon, 16 Mar 2009 17:13:39 +0900 Subject: PCI: allow assignment of memory resources with a specified alignment This patch allows memory resources to be assigned with a specified alignment at boot-time or run-time. The patch is useful when we use PCI pass-through, because page-aligned memory resources are required to securely share PCI resources with guest drivers. If you want to assign the resource at boot time, please set "pci=resource_alignment=" boot parameter. This is format of "pci=resource_alignment=" boot parameter: [@][:]:.[; ...] Specifies alignment and device to reassign aligned memory resources. If is not specified, PAGE_SIZE is used as alignment. PCI-PCI bridge can be specified, if resource windows need to be expanded. This is example: pci=resource_alignment=20@07:00.0;18@0f:00.0;00:1d.7 If you want to assign the resource at run-time, please set "/sys/bus/pci/resource_alignment" file, and hot-remove the device and hot-add the device. For this purpose, fakephp or PCI hotplug interfaces can be used. The format of "/sys/bus/pci/resource_alignment" file is the same with boot parameter. You can use "," instead of ";". For example: # cd /sys/bus/pci # echo -n 20@12:00.0 > resource_alignment # echo 1 > devices/0000:12:00.0/remove # echo 1 > rescan Reviewed-by: Alex Chiang Reviewed-by: Yu Zhao Signed-off-by: Yuji Shimada Signed-off-by: Jesse Barnes --- Documentation/kernel-parameters.txt | 9 +++ drivers/pci/pci.c | 120 ++++++++++++++++++++++++++++++++++++ drivers/pci/pci.h | 6 ++ drivers/pci/quirks.c | 60 ++++++++++++++++++ drivers/pci/setup-res.c | 15 +++++ 5 files changed, 210 insertions(+) (limited to 'drivers') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index c7c441e7930e..1754fedc531c 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1760,6 +1760,15 @@ and is between 256 and 4096 characters. It is defined in the file cbmemsize=nn[KMG] The fixed amount of bus space which is reserved for the CardBus bridge's memory window. The default value is 64 megabytes. + resource_alignment= + Format: + [@][:]:.[; ...] + Specifies alignment and device to reassign + aligned memory resources. + If is not specified, + PAGE_SIZE is used as alignment. + PCI-PCI bridge can be specified, if resource + windows need to be expanded. pcie_aspm= [PCIE] Forcibly enable or disable PCIe Active State Power Management. diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 8310dc2f943b..a35a8b2ba631 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -20,6 +20,8 @@ #include #include #include /* isa_dma_bridge_buggy */ +#include +#include #include "pci.h" unsigned int pci_pm_d3_delay = PCI_PM_D3_WAIT; @@ -2370,6 +2372,121 @@ int pci_resource_bar(struct pci_dev *dev, int resno, enum pci_bar_type *type) return 0; } +#define RESOURCE_ALIGNMENT_PARAM_SIZE COMMAND_LINE_SIZE +static char resource_alignment_param[RESOURCE_ALIGNMENT_PARAM_SIZE] = {0}; +spinlock_t resource_alignment_lock = SPIN_LOCK_UNLOCKED; + +/** + * pci_specified_resource_alignment - get resource alignment specified by user. + * @dev: the PCI device to get + * + * RETURNS: Resource alignment if it is specified. + * Zero if it is not specified. + */ +resource_size_t pci_specified_resource_alignment(struct pci_dev *dev) +{ + int seg, bus, slot, func, align_order, count; + resource_size_t align = 0; + char *p; + + spin_lock(&resource_alignment_lock); + p = resource_alignment_param; + while (*p) { + count = 0; + if (sscanf(p, "%d%n", &align_order, &count) == 1 && + p[count] == '@') { + p += count + 1; + } else { + align_order = -1; + } + if (sscanf(p, "%x:%x:%x.%x%n", + &seg, &bus, &slot, &func, &count) != 4) { + seg = 0; + if (sscanf(p, "%x:%x.%x%n", + &bus, &slot, &func, &count) != 3) { + /* Invalid format */ + printk(KERN_ERR "PCI: Can't parse resource_alignment parameter: %s\n", + p); + break; + } + } + p += count; + if (seg == pci_domain_nr(dev->bus) && + bus == dev->bus->number && + slot == PCI_SLOT(dev->devfn) && + func == PCI_FUNC(dev->devfn)) { + if (align_order == -1) { + align = PAGE_SIZE; + } else { + align = 1 << align_order; + } + /* Found */ + break; + } + if (*p != ';' && *p != ',') { + /* End of param or invalid format */ + break; + } + p++; + } + spin_unlock(&resource_alignment_lock); + return align; +} + +/** + * pci_is_reassigndev - check if specified PCI is target device to reassign + * @dev: the PCI device to check + * + * RETURNS: non-zero for PCI device is a target device to reassign, + * or zero is not. + */ +int pci_is_reassigndev(struct pci_dev *dev) +{ + return (pci_specified_resource_alignment(dev) != 0); +} + +ssize_t pci_set_resource_alignment_param(const char *buf, size_t count) +{ + if (count > RESOURCE_ALIGNMENT_PARAM_SIZE - 1) + count = RESOURCE_ALIGNMENT_PARAM_SIZE - 1; + spin_lock(&resource_alignment_lock); + strncpy(resource_alignment_param, buf, count); + resource_alignment_param[count] = '\0'; + spin_unlock(&resource_alignment_lock); + return count; +} + +ssize_t pci_get_resource_alignment_param(char *buf, size_t size) +{ + size_t count; + spin_lock(&resource_alignment_lock); + count = snprintf(buf, size, "%s", resource_alignment_param); + spin_unlock(&resource_alignment_lock); + return count; +} + +static ssize_t pci_resource_alignment_show(struct bus_type *bus, char *buf) +{ + return pci_get_resource_alignment_param(buf, PAGE_SIZE); +} + +static ssize_t pci_resource_alignment_store(struct bus_type *bus, + const char *buf, size_t count) +{ + return pci_set_resource_alignment_param(buf, count); +} + +BUS_ATTR(resource_alignment, 0644, pci_resource_alignment_show, + pci_resource_alignment_store); + +static int __init pci_resource_alignment_sysfs_init(void) +{ + return bus_create_file(&pci_bus_type, + &bus_attr_resource_alignment); +} + +late_initcall(pci_resource_alignment_sysfs_init); + static void __devinit pci_no_domains(void) { #ifdef CONFIG_PCI_DOMAINS @@ -2418,6 +2535,9 @@ static int __init pci_setup(char *str) pci_cardbus_io_size = memparse(str + 9, &str); } else if (!strncmp(str, "cbmemsize=", 10)) { pci_cardbus_mem_size = memparse(str + 10, &str); + } else if (!strncmp(str, "resource_alignment=", 19)) { + pci_set_resource_alignment_param(str + 19, + strlen(str + 19)); } else { printk(KERN_ERR "PCI: Unknown option `%s'\n", str); diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 07c0aa5275e6..2cd1cba7236f 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -195,4 +195,10 @@ static inline int pci_ari_enabled(struct pci_bus *bus) return bus->self && bus->self->ari_enabled; } +#ifdef CONFIG_PCI_QUIRKS +extern int pci_is_reassigndev(struct pci_dev *dev); +resource_size_t pci_specified_resource_alignment(struct pci_dev *dev); +extern void pci_disable_bridge_window(struct pci_dev *dev); +#endif + #endif /* DRIVERS_PCI_H */ diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 5aa2afb23ef9..50233818a763 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "pci.h" int isa_dma_bridge_buggy; @@ -34,6 +35,65 @@ int pcie_mch_quirk; EXPORT_SYMBOL(pcie_mch_quirk); #ifdef CONFIG_PCI_QUIRKS +/* + * This quirk function disables the device and releases resources + * which is specified by kernel's boot parameter 'pci=resource_alignment='. + * It also rounds up size to specified alignment. + * Later on, the kernel will assign page-aligned memory resource back + * to that device. + */ +static void __devinit quirk_resource_alignment(struct pci_dev *dev) +{ + int i; + struct resource *r; + resource_size_t align, size; + + if (!pci_is_reassigndev(dev)) + return; + + if (dev->hdr_type == PCI_HEADER_TYPE_NORMAL && + (dev->class >> 8) == PCI_CLASS_BRIDGE_HOST) { + dev_warn(&dev->dev, + "Can't reassign resources to host bridge.\n"); + return; + } + + dev_info(&dev->dev, "Disabling device and release resources.\n"); + pci_disable_device(dev); + + align = pci_specified_resource_alignment(dev); + for (i=0; i < PCI_BRIDGE_RESOURCES; i++) { + r = &dev->resource[i]; + if (!(r->flags & IORESOURCE_MEM)) + continue; + size = resource_size(r); + if (size < align) { + size = align; + dev_info(&dev->dev, + "Rounding up size of resource #%d to %#llx.\n", + i, (unsigned long long)size); + } + r->end = size - 1; + r->start = 0; + } + /* Need to disable bridge's resource window, + * to enable the kernel to reassign new resource + * window later on. + */ + if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE && + (dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) { + for (i = PCI_BRIDGE_RESOURCES; i < PCI_NUM_RESOURCES; i++) { + r = &dev->resource[i]; + if (!(r->flags & IORESOURCE_MEM)) + continue; + r->end = resource_size(r) - 1; + r->start = 0; + } + pci_disable_bridge_window(dev); + } +} +DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, quirk_resource_alignment); + /* The Mellanox Tavor device gives false positive parity errors * Mark this device with a broken_parity_status, to allow * PCI scanning code to "skip" this now blacklisted device. diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 32e8d88a4619..3039fcb86afc 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -120,6 +120,21 @@ int pci_claim_resource(struct pci_dev *dev, int resource) return err; } +#ifdef CONFIG_PCI_QUIRKS +void pci_disable_bridge_window(struct pci_dev *dev) +{ + dev_dbg(&dev->dev, "Disabling bridge window.\n"); + + /* MMIO Base/Limit */ + pci_write_config_dword(dev, PCI_MEMORY_BASE, 0x0000fff0); + + /* Prefetchable MMIO Base/Limit */ + pci_write_config_dword(dev, PCI_PREF_LIMIT_UPPER32, 0); + pci_write_config_dword(dev, PCI_PREF_MEMORY_BASE, 0x0000fff0); + pci_write_config_dword(dev, PCI_PREF_BASE_UPPER32, 0xffffffff); +} +#endif /* CONFIG_PCI_QUIRKS */ + int pci_assign_resource(struct pci_dev *dev, int resno) { struct pci_bus *bus = dev->bus; -- cgit v1.2.3 From 6a3b3e26803fc823058fbb05abb5e0d92a52e1bd Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sun, 15 Mar 2009 20:14:37 +0100 Subject: PCI: Use kzalloc() in pci_create_bus() Signed-off-by: Geert Uytterhoeven Signed-off-by: Jesse Barnes --- drivers/pci/probe.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 23362e8c696f..9e7d642e66b0 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1114,7 +1114,7 @@ struct pci_bus * pci_create_bus(struct device *parent, if (!b) return NULL; - dev = kmalloc(sizeof(*dev), GFP_KERNEL); + dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev){ kfree(b); return NULL; @@ -1133,7 +1133,6 @@ struct pci_bus * pci_create_bus(struct device *parent, list_add_tail(&b->node, &pci_root_buses); up_write(&pci_bus_sem); - memset(dev, 0, sizeof(*dev)); dev->parent = parent; dev->release = pci_release_bus_bridge_dev; dev_set_name(dev, "pci%04x:%02x", pci_domain_nr(b), bus); -- cgit v1.2.3 From 9efb5fe1b80a45130ba659ba755f24534c83301b Mon Sep 17 00:00:00 2001 From: Alex Chiang Date: Mon, 9 Mar 2009 12:08:15 -0600 Subject: PCI: PCIe portdrv: eliminate double kfree in remove path Commit 55633af3 (PCIe portdrv: Use driver data to simplify code) added a kfree of the driver private data in pcie_port_device_remove but forgot to remove the old kfree from pcie_portdrv_remove. Acked-by: Rafael J. Wysocki Signed-off-by: Alex Chiang Signed-off-by: Jesse Barnes --- drivers/pci/pcie/portdrv_pci.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index a61f4930d676..b924e2463f85 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -94,7 +94,6 @@ static void pcie_portdrv_remove (struct pci_dev *dev) { pcie_port_device_remove(dev); pci_disable_device(dev); - kfree(pci_get_drvdata(dev)); } static int error_detected_iter(struct device *device, void *data) -- cgit v1.2.3 From 745be2e700cdddd5da4e402854a484242c3628df Mon Sep 17 00:00:00 2001 From: Alex Chiang Date: Sat, 7 Mar 2009 21:46:49 -0700 Subject: PCIe: portdrv: call pci_disable_device during remove The PCIe port driver calls pci_enable_device when registering ports, but never calls pci_disable_device during removal. Acked-by: Rafael J. Wysocki Signed-off-by: Alex Chiang Signed-off-by: Jesse Barnes --- drivers/pci/pcie/portdrv_core.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 5a5bfe7cdf5f..e39982503863 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -473,6 +473,7 @@ void pcie_port_device_remove(struct pci_dev *dev) struct pcie_port_data *port_data = pci_get_drvdata(dev); device_for_each_child(&dev->dev, NULL, remove_iter); + pci_disable_device(dev); switch (port_data->port_irq_mode) { case PCIE_PORT_MSIX_MODE: -- cgit v1.2.3 From dfadd9edff498d767008edc6b2a6e86a7a19934d Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Sun, 8 Mar 2009 21:35:37 -0700 Subject: PCI/x86: detect host bridge config space size w/o using quirks Many host bridges support a 4k config space, so check them directy instead of using quirks to add them. We only need to do this extra check for host bridges at this point, because only host bridges are known to have extended address space without also having a PCI-X/PCI-E caps. Other devices with this property could be done with quirks (if there are any). As a bonus, we can remove the quirks for AMD host bridges with family 10h and 11h since they're not needed any more. With this patch, we can get correct pci cfg size of new Intel CPUs/IOHs with host bridges. Signed-off-by: Yinghai Lu Acked-by: H. Peter Anvin Reviewed-by: Matthew Wilcox Cc: Signed-off-by: Jesse Barnes --- arch/x86/pci/fixup.c | 20 -------------------- drivers/pci/probe.c | 9 ++++++++- 2 files changed, 8 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/arch/x86/pci/fixup.c b/arch/x86/pci/fixup.c index 7d388d5cf548..096b0ed0713e 100644 --- a/arch/x86/pci/fixup.c +++ b/arch/x86/pci/fixup.c @@ -494,26 +494,6 @@ static void __devinit pci_siemens_interrupt_controller(struct pci_dev *dev) DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SIEMENS, 0x0015, pci_siemens_interrupt_controller); -/* - * Regular PCI devices have 256 bytes, but AMD Family 10h/11h CPUs have - * 4096 bytes configuration space for each function of their processor - * configuration space. - */ -static void amd_cpu_pci_cfg_space_size(struct pci_dev *dev) -{ - dev->cfg_size = pci_cfg_space_size_ext(dev); -} -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1200, amd_cpu_pci_cfg_space_size); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1201, amd_cpu_pci_cfg_space_size); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1202, amd_cpu_pci_cfg_space_size); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1203, amd_cpu_pci_cfg_space_size); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1204, amd_cpu_pci_cfg_space_size); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1300, amd_cpu_pci_cfg_space_size); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1301, amd_cpu_pci_cfg_space_size); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1302, amd_cpu_pci_cfg_space_size); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1303, amd_cpu_pci_cfg_space_size); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1304, amd_cpu_pci_cfg_space_size); - /* * SB600: Disable BAR1 on device 14.0 to avoid HPET resources from * confusing the PCI engine: diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 9e7d642e66b0..579a56c8181f 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -847,6 +847,11 @@ int pci_cfg_space_size(struct pci_dev *dev) { int pos; u32 status; + u16 class; + + class = dev->class >> 8; + if (class == PCI_CLASS_BRIDGE_HOST) + return pci_cfg_space_size_ext(dev); pos = pci_find_capability(dev, PCI_CAP_ID_EXP); if (!pos) { @@ -936,7 +941,6 @@ static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn) dev->multifunction = !!(hdr_type & 0x80); dev->vendor = l & 0xffff; dev->device = (l >> 16) & 0xffff; - dev->cfg_size = pci_cfg_space_size(dev); dev->error_state = pci_channel_io_normal; set_pcie_port_type(dev); @@ -952,6 +956,9 @@ static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn) return NULL; } + /* need to have dev->class ready */ + dev->cfg_size = pci_cfg_space_size(dev); + return dev; } -- cgit v1.2.3 From 217f45de3d2f68b6d0b646bb4f2a155a71648396 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Wed, 4 Mar 2009 05:57:05 +0000 Subject: PCI: expose boot VGA device via sysfs. X really would like to know which VGA device was considered the boot device by the system. The x86 PCI fixups have support for discovering this but we provide no way to expose it to userspace. This adds a sysfs file per VGA class device which has the value 0 for non the boot device or unknown, and 1 if the VGA device is the boot device. Acked-by: Greg Kroah-Hartman Signed-off-by: Dave Airlie Signed-off-by: Jesse Barnes --- drivers/pci/pci-sysfs.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 1c8929801400..ec7a175dcbaf 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -240,6 +240,17 @@ struct device_attribute pci_dev_attrs[] = { __ATTR_NULL, }; +static ssize_t +boot_vga_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct pci_dev *pdev = to_pci_dev(dev); + + return sprintf(buf, "%u\n", + !!(pdev->resource[PCI_ROM_RESOURCE].flags & + IORESOURCE_ROM_SHADOW)); +} +struct device_attribute vga_attr = __ATTR_RO(boot_vga); + static ssize_t pci_read_config(struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) @@ -899,18 +910,27 @@ int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev) pdev->rom_attr = attr; } + if ((pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA) { + retval = device_create_file(&pdev->dev, &vga_attr); + if (retval) + goto err_rom_file; + } + /* add platform-specific attributes */ retval = pcibios_add_platform_entries(pdev); if (retval) - goto err_rom_file; + goto err_vga_file; /* add sysfs entries for various capabilities */ retval = pci_create_capabilities_sysfs(pdev); if (retval) - goto err_rom_file; + goto err_vga_file; return 0; +err_vga_file: + if ((pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA) + device_remove_file(&pdev->dev, &vga_attr); err_rom_file: if (rom_size) { sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr); -- cgit v1.2.3 From 8293b0f629095efbe7c7e3f9b437f8c040c19eb5 Mon Sep 17 00:00:00 2001 From: David O'Shea Date: Mon, 2 Mar 2009 09:51:13 +0100 Subject: PCI: Compaq Evo D510 SMBus quirk using USB instead of VGA On the Compaq Evo D510 SFF/CMT, a PCI quirk activated the SMBus device based on detection of the on-board VGA controller, but the on-board VGA is disabled if an AGP card is inserted, so look for one of the USB controllers instead. Signed-off-by: David O'Shea Signed-off-by: Jean Delvare Signed-off-by: Jesse Barnes --- drivers/pci/quirks.c | 9 +++++++-- include/linux/pci_ids.h | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 50233818a763..7ddcfc65e790 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -1186,10 +1186,15 @@ static void __init asus_hides_smbus_hostbridge(struct pci_dev *dev) * its on-board VGA controller */ asus_hides_smbus = 1; } - else if (dev->device == PCI_DEVICE_ID_INTEL_82845G_IG) + else if (dev->device == PCI_DEVICE_ID_INTEL_82801DB_2) switch(dev->subsystem_device) { case 0x00b8: /* Compaq Evo D510 CMT */ case 0x00b9: /* Compaq Evo D510 SFF */ + /* Motherboard doesn't have Host bridge + * subvendor/subdevice IDs and on-board VGA + * controller is disabled if an AGP card is + * inserted, therefore checking USB UHCI + * Controller #1 */ asus_hides_smbus = 1; } else if (dev->device == PCI_DEVICE_ID_INTEL_82815_CGC) @@ -1214,7 +1219,7 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82855GM_HB, as DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82915GM_HB, asus_hides_smbus_hostbridge); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82810_IG3, asus_hides_smbus_hostbridge); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82845G_IG, asus_hides_smbus_hostbridge); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_2, asus_hides_smbus_hostbridge); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82815_CGC, asus_hides_smbus_hostbridge); static void asus_hides_smbus_lpc(struct pci_dev *dev) diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index aca8c458aa8a..3ddf8beabdf8 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2373,6 +2373,7 @@ #define PCI_DEVICE_ID_INTEL_82801CA_12 0x248c #define PCI_DEVICE_ID_INTEL_82801DB_0 0x24c0 #define PCI_DEVICE_ID_INTEL_82801DB_1 0x24c1 +#define PCI_DEVICE_ID_INTEL_82801DB_2 0x24c2 #define PCI_DEVICE_ID_INTEL_82801DB_3 0x24c3 #define PCI_DEVICE_ID_INTEL_82801DB_5 0x24c5 #define PCI_DEVICE_ID_INTEL_82801DB_6 0x24c6 -- cgit v1.2.3 From d1b054da8f599905f3c18a218961dcf17f9d5f13 Mon Sep 17 00:00:00 2001 From: Yu Zhao Date: Fri, 20 Mar 2009 11:25:11 +0800 Subject: PCI: initialize and release SR-IOV capability If a device has the SR-IOV capability, initialize it (set the ARI Capable Hierarchy in the lowest numbered PF if necessary; calculate the System Page Size for the VF MMIO, probe the VF Offset, Stride and BARs). A lock for the VF bus allocation is also initialized if a PF is the lowest numbered PF. Reviewed-by: Matthew Wilcox Signed-off-by: Yu Zhao Signed-off-by: Jesse Barnes --- drivers/pci/Kconfig | 10 +++ drivers/pci/Makefile | 2 + drivers/pci/iov.c | 182 +++++++++++++++++++++++++++++++++++++++++++++++ drivers/pci/pci.c | 7 ++ drivers/pci/pci.h | 37 ++++++++++ drivers/pci/probe.c | 4 ++ include/linux/pci.h | 11 +++ include/linux/pci_regs.h | 33 +++++++++ 8 files changed, 286 insertions(+) create mode 100644 drivers/pci/iov.c (limited to 'drivers') diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 2a4501dd2515..fdc864f9cf23 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -59,3 +59,13 @@ config HT_IRQ This allows native hypertransport devices to use interrupts. If unsure say Y. + +config PCI_IOV + bool "PCI IOV support" + depends on PCI + help + I/O Virtualization is a PCI feature supported by some devices + which allows them to create virtual devices which share their + physical resources. + + If unsure, say N. diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 3d07ce24f6a8..ba6af162fd39 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -29,6 +29,8 @@ obj-$(CONFIG_DMAR) += dmar.o iova.o intel-iommu.o obj-$(CONFIG_INTR_REMAP) += dmar.o intr_remapping.o +obj-$(CONFIG_PCI_IOV) += iov.o + # # Some architectures use the generic PCI setup functions # diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c new file mode 100644 index 000000000000..66cc414ed15f --- /dev/null +++ b/drivers/pci/iov.c @@ -0,0 +1,182 @@ +/* + * drivers/pci/iov.c + * + * Copyright (C) 2009 Intel Corporation, Yu Zhao + * + * PCI Express I/O Virtualization (IOV) support. + * Single Root IOV 1.0 + */ + +#include +#include +#include +#include +#include "pci.h" + + +static int sriov_init(struct pci_dev *dev, int pos) +{ + int i; + int rc; + int nres; + u32 pgsz; + u16 ctrl, total, offset, stride; + struct pci_sriov *iov; + struct resource *res; + struct pci_dev *pdev; + + if (dev->pcie_type != PCI_EXP_TYPE_RC_END && + dev->pcie_type != PCI_EXP_TYPE_ENDPOINT) + return -ENODEV; + + pci_read_config_word(dev, pos + PCI_SRIOV_CTRL, &ctrl); + if (ctrl & PCI_SRIOV_CTRL_VFE) { + pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, 0); + ssleep(1); + } + + pci_read_config_word(dev, pos + PCI_SRIOV_TOTAL_VF, &total); + if (!total) + return 0; + + ctrl = 0; + list_for_each_entry(pdev, &dev->bus->devices, bus_list) + if (pdev->is_physfn) + goto found; + + pdev = NULL; + if (pci_ari_enabled(dev->bus)) + ctrl |= PCI_SRIOV_CTRL_ARI; + +found: + pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, ctrl); + pci_write_config_word(dev, pos + PCI_SRIOV_NUM_VF, total); + pci_read_config_word(dev, pos + PCI_SRIOV_VF_OFFSET, &offset); + pci_read_config_word(dev, pos + PCI_SRIOV_VF_STRIDE, &stride); + if (!offset || (total > 1 && !stride)) + return -EIO; + + pci_read_config_dword(dev, pos + PCI_SRIOV_SUP_PGSIZE, &pgsz); + i = PAGE_SHIFT > 12 ? PAGE_SHIFT - 12 : 0; + pgsz &= ~((1 << i) - 1); + if (!pgsz) + return -EIO; + + pgsz &= ~(pgsz - 1); + pci_write_config_dword(dev, pos + PCI_SRIOV_SYS_PGSIZE, pgsz); + + nres = 0; + for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { + res = dev->resource + PCI_IOV_RESOURCES + i; + i += __pci_read_base(dev, pci_bar_unknown, res, + pos + PCI_SRIOV_BAR + i * 4); + if (!res->flags) + continue; + if (resource_size(res) & (PAGE_SIZE - 1)) { + rc = -EIO; + goto failed; + } + res->end = res->start + resource_size(res) * total - 1; + nres++; + } + + iov = kzalloc(sizeof(*iov), GFP_KERNEL); + if (!iov) { + rc = -ENOMEM; + goto failed; + } + + iov->pos = pos; + iov->nres = nres; + iov->ctrl = ctrl; + iov->total = total; + iov->offset = offset; + iov->stride = stride; + iov->pgsz = pgsz; + iov->self = dev; + pci_read_config_dword(dev, pos + PCI_SRIOV_CAP, &iov->cap); + pci_read_config_byte(dev, pos + PCI_SRIOV_FUNC_LINK, &iov->link); + + if (pdev) + iov->dev = pci_dev_get(pdev); + else { + iov->dev = dev; + mutex_init(&iov->lock); + } + + dev->sriov = iov; + dev->is_physfn = 1; + + return 0; + +failed: + for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { + res = dev->resource + PCI_IOV_RESOURCES + i; + res->flags = 0; + } + + return rc; +} + +static void sriov_release(struct pci_dev *dev) +{ + if (dev == dev->sriov->dev) + mutex_destroy(&dev->sriov->lock); + else + pci_dev_put(dev->sriov->dev); + + kfree(dev->sriov); + dev->sriov = NULL; +} + +/** + * pci_iov_init - initialize the IOV capability + * @dev: the PCI device + * + * Returns 0 on success, or negative on failure. + */ +int pci_iov_init(struct pci_dev *dev) +{ + int pos; + + if (!dev->is_pcie) + return -ENODEV; + + pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV); + if (pos) + return sriov_init(dev, pos); + + return -ENODEV; +} + +/** + * pci_iov_release - release resources used by the IOV capability + * @dev: the PCI device + */ +void pci_iov_release(struct pci_dev *dev) +{ + if (dev->is_physfn) + sriov_release(dev); +} + +/** + * pci_iov_resource_bar - get position of the SR-IOV BAR + * @dev: the PCI device + * @resno: the resource number + * @type: the BAR type to be filled in + * + * Returns position of the BAR encapsulated in the SR-IOV capability. + */ +int pci_iov_resource_bar(struct pci_dev *dev, int resno, + enum pci_bar_type *type) +{ + if (resno < PCI_IOV_RESOURCES || resno > PCI_IOV_RESOURCE_END) + return 0; + + BUG_ON(!dev->is_physfn); + + *type = pci_bar_unknown; + + return dev->sriov->pos + PCI_SRIOV_BAR + + 4 * (resno - PCI_IOV_RESOURCES); +} diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index a35a8b2ba631..2b3201ec2b05 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2360,12 +2360,19 @@ int pci_select_bars(struct pci_dev *dev, unsigned long flags) */ int pci_resource_bar(struct pci_dev *dev, int resno, enum pci_bar_type *type) { + int reg; + if (resno < PCI_ROM_RESOURCE) { *type = pci_bar_unknown; return PCI_BASE_ADDRESS_0 + 4 * resno; } else if (resno == PCI_ROM_RESOURCE) { *type = pci_bar_mem32; return dev->rom_base_reg; + } else if (resno < PCI_BRIDGE_RESOURCES) { + /* device specific resource */ + reg = pci_iov_resource_bar(dev, resno, type); + if (reg) + return reg; } dev_err(&dev->dev, "BAR: invalid resource #%d\n", resno); diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 2cd1cba7236f..7d5327c986f5 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -201,4 +201,41 @@ resource_size_t pci_specified_resource_alignment(struct pci_dev *dev); extern void pci_disable_bridge_window(struct pci_dev *dev); #endif +/* Single Root I/O Virtualization */ +struct pci_sriov { + int pos; /* capability position */ + int nres; /* number of resources */ + u32 cap; /* SR-IOV Capabilities */ + u16 ctrl; /* SR-IOV Control */ + u16 total; /* total VFs associated with the PF */ + u16 offset; /* first VF Routing ID offset */ + u16 stride; /* following VF stride */ + u32 pgsz; /* page size for BAR alignment */ + u8 link; /* Function Dependency Link */ + struct pci_dev *dev; /* lowest numbered PF */ + struct pci_dev *self; /* this PF */ + struct mutex lock; /* lock for VF bus */ +}; + +#ifdef CONFIG_PCI_IOV +extern int pci_iov_init(struct pci_dev *dev); +extern void pci_iov_release(struct pci_dev *dev); +extern int pci_iov_resource_bar(struct pci_dev *dev, int resno, + enum pci_bar_type *type); +#else +static inline int pci_iov_init(struct pci_dev *dev) +{ + return -ENODEV; +} +static inline void pci_iov_release(struct pci_dev *dev) + +{ +} +static inline int pci_iov_resource_bar(struct pci_dev *dev, int resno, + enum pci_bar_type *type) +{ + return 0; +} +#endif /* CONFIG_PCI_IOV */ + #endif /* DRIVERS_PCI_H */ diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 579a56c8181f..0471f6ea1466 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -785,6 +785,7 @@ static int pci_setup_device(struct pci_dev * dev) static void pci_release_capabilities(struct pci_dev *dev) { pci_vpd_release(dev); + pci_iov_release(dev); } /** @@ -979,6 +980,9 @@ static void pci_init_capabilities(struct pci_dev *dev) /* Alternative Routing-ID Forwarding */ pci_enable_ari(dev); + + /* Single Root I/O Virtualization */ + pci_iov_init(dev); } void pci_device_add(struct pci_dev *dev, struct pci_bus *bus) diff --git a/include/linux/pci.h b/include/linux/pci.h index 1f6c5ddaae36..8ce2f2d9ab63 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -93,6 +93,12 @@ enum { /* #6: expansion ROM resource */ PCI_ROM_RESOURCE, + /* device specific resources */ +#ifdef CONFIG_PCI_IOV + PCI_IOV_RESOURCES, + PCI_IOV_RESOURCE_END = PCI_IOV_RESOURCES + PCI_SRIOV_NUM_BARS - 1, +#endif + /* resources assigned to buses behind the bridge */ #define PCI_BRIDGE_RESOURCE_NUM 4 @@ -180,6 +186,7 @@ struct pci_cap_saved_state { struct pcie_link_state; struct pci_vpd; +struct pci_sriov; /* * The pci_dev structure is used to describe PCI devices. @@ -257,6 +264,7 @@ struct pci_dev { unsigned int is_managed:1; unsigned int is_pcie:1; unsigned int state_saved:1; + unsigned int is_physfn:1; pci_dev_flags_t dev_flags; atomic_t enable_cnt; /* pci_enable_device has been called */ @@ -270,6 +278,9 @@ struct pci_dev { struct list_head msi_list; #endif struct pci_vpd *vpd; +#ifdef CONFIG_PCI_IOV + struct pci_sriov *sriov; /* SR-IOV capability related */ +#endif }; extern struct pci_dev *alloc_pci_dev(void); diff --git a/include/linux/pci_regs.h b/include/linux/pci_regs.h index b647a4df59fc..d4e663877f45 100644 --- a/include/linux/pci_regs.h +++ b/include/linux/pci_regs.h @@ -375,6 +375,7 @@ #define PCI_EXP_TYPE_UPSTREAM 0x5 /* Upstream Port */ #define PCI_EXP_TYPE_DOWNSTREAM 0x6 /* Downstream Port */ #define PCI_EXP_TYPE_PCI_BRIDGE 0x7 /* PCI/PCI-X Bridge */ +#define PCI_EXP_TYPE_RC_END 0x9 /* Root Complex Integrated Endpoint */ #define PCI_EXP_FLAGS_SLOT 0x0100 /* Slot implemented */ #define PCI_EXP_FLAGS_IRQ 0x3e00 /* Interrupt message number */ #define PCI_EXP_DEVCAP 4 /* Device capabilities */ @@ -498,6 +499,7 @@ #define PCI_EXT_CAP_ID_DSN 3 #define PCI_EXT_CAP_ID_PWR 4 #define PCI_EXT_CAP_ID_ARI 14 +#define PCI_EXT_CAP_ID_SRIOV 16 /* Advanced Error Reporting */ #define PCI_ERR_UNCOR_STATUS 4 /* Uncorrectable Error Status */ @@ -615,4 +617,35 @@ #define PCI_ARI_CTRL_ACS 0x0002 /* ACS Function Groups Enable */ #define PCI_ARI_CTRL_FG(x) (((x) >> 4) & 7) /* Function Group */ +/* Single Root I/O Virtualization */ +#define PCI_SRIOV_CAP 0x04 /* SR-IOV Capabilities */ +#define PCI_SRIOV_CAP_VFM 0x01 /* VF Migration Capable */ +#define PCI_SRIOV_CAP_INTR(x) ((x) >> 21) /* Interrupt Message Number */ +#define PCI_SRIOV_CTRL 0x08 /* SR-IOV Control */ +#define PCI_SRIOV_CTRL_VFE 0x01 /* VF Enable */ +#define PCI_SRIOV_CTRL_VFM 0x02 /* VF Migration Enable */ +#define PCI_SRIOV_CTRL_INTR 0x04 /* VF Migration Interrupt Enable */ +#define PCI_SRIOV_CTRL_MSE 0x08 /* VF Memory Space Enable */ +#define PCI_SRIOV_CTRL_ARI 0x10 /* ARI Capable Hierarchy */ +#define PCI_SRIOV_STATUS 0x0a /* SR-IOV Status */ +#define PCI_SRIOV_STATUS_VFM 0x01 /* VF Migration Status */ +#define PCI_SRIOV_INITIAL_VF 0x0c /* Initial VFs */ +#define PCI_SRIOV_TOTAL_VF 0x0e /* Total VFs */ +#define PCI_SRIOV_NUM_VF 0x10 /* Number of VFs */ +#define PCI_SRIOV_FUNC_LINK 0x12 /* Function Dependency Link */ +#define PCI_SRIOV_VF_OFFSET 0x14 /* First VF Offset */ +#define PCI_SRIOV_VF_STRIDE 0x16 /* Following VF Stride */ +#define PCI_SRIOV_VF_DID 0x1a /* VF Device ID */ +#define PCI_SRIOV_SUP_PGSIZE 0x1c /* Supported Page Sizes */ +#define PCI_SRIOV_SYS_PGSIZE 0x20 /* System Page Size */ +#define PCI_SRIOV_BAR 0x24 /* VF BAR0 */ +#define PCI_SRIOV_NUM_BARS 6 /* Number of VF BARs */ +#define PCI_SRIOV_VFM 0x3c /* VF Migration State Array Offset*/ +#define PCI_SRIOV_VFM_BIR(x) ((x) & 7) /* State BIR */ +#define PCI_SRIOV_VFM_OFFSET(x) ((x) & ~7) /* State Offset */ +#define PCI_SRIOV_VFM_UA 0x0 /* Inactive.Unavailable */ +#define PCI_SRIOV_VFM_MI 0x1 /* Dormant.MigrateIn */ +#define PCI_SRIOV_VFM_MO 0x2 /* Active.MigrateOut */ +#define PCI_SRIOV_VFM_AV 0x3 /* Active.Available */ + #endif /* LINUX_PCI_REGS_H */ -- cgit v1.2.3 From 8c5cdb6adc6688b9b8fd82ea4a5cf4674dabad79 Mon Sep 17 00:00:00 2001 From: Yu Zhao Date: Fri, 20 Mar 2009 11:25:12 +0800 Subject: PCI: restore saved SR-IOV state Restore the volatile registers in the SR-IOV capability after the D3->D0 transition. Reviewed-by: Matthew Wilcox Signed-off-by: Yu Zhao Signed-off-by: Jesse Barnes --- drivers/pci/iov.c | 29 +++++++++++++++++++++++++++++ drivers/pci/pci.c | 1 + drivers/pci/pci.h | 4 ++++ 3 files changed, 34 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 66cc414ed15f..b121e47402fa 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -129,6 +129,25 @@ static void sriov_release(struct pci_dev *dev) dev->sriov = NULL; } +static void sriov_restore_state(struct pci_dev *dev) +{ + int i; + u16 ctrl; + struct pci_sriov *iov = dev->sriov; + + pci_read_config_word(dev, iov->pos + PCI_SRIOV_CTRL, &ctrl); + if (ctrl & PCI_SRIOV_CTRL_VFE) + return; + + for (i = PCI_IOV_RESOURCES; i <= PCI_IOV_RESOURCE_END; i++) + pci_update_resource(dev, i); + + pci_write_config_dword(dev, iov->pos + PCI_SRIOV_SYS_PGSIZE, iov->pgsz); + pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); + if (iov->ctrl & PCI_SRIOV_CTRL_VFE) + msleep(100); +} + /** * pci_iov_init - initialize the IOV capability * @dev: the PCI device @@ -180,3 +199,13 @@ int pci_iov_resource_bar(struct pci_dev *dev, int resno, return dev->sriov->pos + PCI_SRIOV_BAR + 4 * (resno - PCI_IOV_RESOURCES); } + +/** + * pci_restore_iov_state - restore the state of the IOV capability + * @dev: the PCI device + */ +void pci_restore_iov_state(struct pci_dev *dev) +{ + if (dev->is_physfn) + sriov_restore_state(dev); +} diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 2b3201ec2b05..676bbcbc272b 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -775,6 +775,7 @@ pci_restore_state(struct pci_dev *dev) } pci_restore_pcix_state(dev); pci_restore_msi_state(dev); + pci_restore_iov_state(dev); return 0; } diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 7d5327c986f5..fd5ea4d445e8 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -222,6 +222,7 @@ extern int pci_iov_init(struct pci_dev *dev); extern void pci_iov_release(struct pci_dev *dev); extern int pci_iov_resource_bar(struct pci_dev *dev, int resno, enum pci_bar_type *type); +extern void pci_restore_iov_state(struct pci_dev *dev); #else static inline int pci_iov_init(struct pci_dev *dev) { @@ -236,6 +237,9 @@ static inline int pci_iov_resource_bar(struct pci_dev *dev, int resno, { return 0; } +static inline void pci_restore_iov_state(struct pci_dev *dev) +{ +} #endif /* CONFIG_PCI_IOV */ #endif /* DRIVERS_PCI_H */ -- cgit v1.2.3 From a28724b0fb909d247229a70761c90bb37b13366a Mon Sep 17 00:00:00 2001 From: Yu Zhao Date: Fri, 20 Mar 2009 11:25:13 +0800 Subject: PCI: reserve bus range for SR-IOV device Reserve the bus number range used by the Virtual Function when pcibios_assign_all_busses() returns true. Reviewed-by: Matthew Wilcox Signed-off-by: Yu Zhao Signed-off-by: Jesse Barnes --- drivers/pci/iov.c | 36 ++++++++++++++++++++++++++++++++++++ drivers/pci/pci.h | 5 +++++ drivers/pci/probe.c | 3 +++ 3 files changed, 44 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index b121e47402fa..5ddfc09a8d3f 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -14,6 +14,18 @@ #include "pci.h" +static inline u8 virtfn_bus(struct pci_dev *dev, int id) +{ + return dev->bus->number + ((dev->devfn + dev->sriov->offset + + dev->sriov->stride * id) >> 8); +} + +static inline u8 virtfn_devfn(struct pci_dev *dev, int id) +{ + return (dev->devfn + dev->sriov->offset + + dev->sriov->stride * id) & 0xff; +} + static int sriov_init(struct pci_dev *dev, int pos) { int i; @@ -209,3 +221,27 @@ void pci_restore_iov_state(struct pci_dev *dev) if (dev->is_physfn) sriov_restore_state(dev); } + +/** + * pci_iov_bus_range - find bus range used by Virtual Function + * @bus: the PCI bus + * + * Returns max number of buses (exclude current one) used by Virtual + * Functions. + */ +int pci_iov_bus_range(struct pci_bus *bus) +{ + int max = 0; + u8 busnr; + struct pci_dev *dev; + + list_for_each_entry(dev, &bus->devices, bus_list) { + if (!dev->is_physfn) + continue; + busnr = virtfn_bus(dev, dev->sriov->total - 1); + if (busnr > max) + max = busnr; + } + + return max ? max - bus->number : 0; +} diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index fd5ea4d445e8..5c29cb2b8e63 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -223,6 +223,7 @@ extern void pci_iov_release(struct pci_dev *dev); extern int pci_iov_resource_bar(struct pci_dev *dev, int resno, enum pci_bar_type *type); extern void pci_restore_iov_state(struct pci_dev *dev); +extern int pci_iov_bus_range(struct pci_bus *bus); #else static inline int pci_iov_init(struct pci_dev *dev) { @@ -240,6 +241,10 @@ static inline int pci_iov_resource_bar(struct pci_dev *dev, int resno, static inline void pci_restore_iov_state(struct pci_dev *dev) { } +static inline int pci_iov_bus_range(struct pci_bus *bus) +{ + return 0; +} #endif /* CONFIG_PCI_IOV */ #endif /* DRIVERS_PCI_H */ diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 0471f6ea1466..0ecdaea3915f 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1085,6 +1085,9 @@ unsigned int __devinit pci_scan_child_bus(struct pci_bus *bus) for (devfn = 0; devfn < 0x100; devfn += 8) pci_scan_slot(bus, devfn); + /* Reserve buses for SR-IOV capability. */ + max += pci_iov_bus_range(bus); + /* * After performing arch-dependent fixup of the bus, look behind * all PCI-to-PCI bridges on this bus. -- cgit v1.2.3 From 480b93b7837fb3cf0579a42f4953ac463a5b9e1e Mon Sep 17 00:00:00 2001 From: Yu Zhao Date: Fri, 20 Mar 2009 11:25:14 +0800 Subject: PCI: centralize device setup code Move the device setup stuff into pci_setup_device() which will be used to setup the Virtual Function later. Reviewed-by: Matthew Wilcox Signed-off-by: Yu Zhao Signed-off-by: Jesse Barnes --- drivers/pci/pci.h | 1 + drivers/pci/probe.c | 78 +++++++++++++++++++++++++++-------------------------- 2 files changed, 41 insertions(+), 38 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 5c29cb2b8e63..f4fc10fc5872 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -178,6 +178,7 @@ enum pci_bar_type { pci_bar_mem64, /* A 64-bit memory BAR */ }; +extern int pci_setup_device(struct pci_dev *dev); extern int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, struct resource *res, unsigned int reg); extern int pci_resource_bar(struct pci_dev *dev, int resno, diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 0ecdaea3915f..943c49a7842c 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -674,6 +674,19 @@ static void pci_read_irq(struct pci_dev *dev) dev->irq = irq; } +static void set_pcie_port_type(struct pci_dev *pdev) +{ + int pos; + u16 reg16; + + pos = pci_find_capability(pdev, PCI_CAP_ID_EXP); + if (!pos) + return; + pdev->is_pcie = 1; + pci_read_config_word(pdev, pos + PCI_EXP_FLAGS, ®16); + pdev->pcie_type = (reg16 & PCI_EXP_FLAGS_TYPE) >> 4; +} + #define LEGACY_IO_RESOURCE (IORESOURCE_IO | IORESOURCE_PCI_FIXED) /** @@ -683,12 +696,34 @@ static void pci_read_irq(struct pci_dev *dev) * Initialize the device structure with information about the device's * vendor,class,memory and IO-space addresses,IRQ lines etc. * Called at initialisation of the PCI subsystem and by CardBus services. - * Returns 0 on success and -1 if unknown type of device (not normal, bridge - * or CardBus). + * Returns 0 on success and negative if unknown type of device (not normal, + * bridge or CardBus). */ -static int pci_setup_device(struct pci_dev * dev) +int pci_setup_device(struct pci_dev *dev) { u32 class; + u8 hdr_type; + struct pci_slot *slot; + + if (pci_read_config_byte(dev, PCI_HEADER_TYPE, &hdr_type)) + return -EIO; + + dev->sysdata = dev->bus->sysdata; + dev->dev.parent = dev->bus->bridge; + dev->dev.bus = &pci_bus_type; + dev->hdr_type = hdr_type & 0x7f; + dev->multifunction = !!(hdr_type & 0x80); + dev->cfg_size = pci_cfg_space_size(dev); + dev->error_state = pci_channel_io_normal; + set_pcie_port_type(dev); + + list_for_each_entry(slot, &dev->bus->slots, list) + if (PCI_SLOT(dev->devfn) == slot->number) + dev->slot = slot; + + /* Assume 32-bit PCI; let 64-bit PCI cards (which are far rarer) + set this higher, assuming the system even supports it. */ + dev->dma_mask = 0xffffffff; dev_set_name(&dev->dev, "%04x:%02x:%02x.%d", pci_domain_nr(dev->bus), dev->bus->number, PCI_SLOT(dev->devfn), @@ -708,7 +743,6 @@ static int pci_setup_device(struct pci_dev * dev) /* Early fixups, before probing the BARs */ pci_fixup_device(pci_fixup_early, dev); - class = dev->class >> 8; switch (dev->hdr_type) { /* header type */ case PCI_HEADER_TYPE_NORMAL: /* standard header */ @@ -770,7 +804,7 @@ static int pci_setup_device(struct pci_dev * dev) default: /* unknown header */ dev_err(&dev->dev, "unknown header type %02x, " "ignoring device\n", dev->hdr_type); - return -1; + return -EIO; bad: dev_err(&dev->dev, "ignoring class %02x (doesn't match header " @@ -804,19 +838,6 @@ static void pci_release_dev(struct device *dev) kfree(pci_dev); } -static void set_pcie_port_type(struct pci_dev *pdev) -{ - int pos; - u16 reg16; - - pos = pci_find_capability(pdev, PCI_CAP_ID_EXP); - if (!pos) - return; - pdev->is_pcie = 1; - pci_read_config_word(pdev, pos + PCI_EXP_FLAGS, ®16); - pdev->pcie_type = (reg16 & PCI_EXP_FLAGS_TYPE) >> 4; -} - /** * pci_cfg_space_size - get the configuration space size of the PCI device. * @dev: PCI device @@ -897,9 +918,7 @@ EXPORT_SYMBOL(alloc_pci_dev); static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn) { struct pci_dev *dev; - struct pci_slot *slot; u32 l; - u8 hdr_type; int delay = 1; if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, &l)) @@ -926,33 +945,16 @@ static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn) } } - if (pci_bus_read_config_byte(bus, devfn, PCI_HEADER_TYPE, &hdr_type)) - return NULL; - dev = alloc_pci_dev(); if (!dev) return NULL; dev->bus = bus; - dev->sysdata = bus->sysdata; - dev->dev.parent = bus->bridge; - dev->dev.bus = &pci_bus_type; dev->devfn = devfn; - dev->hdr_type = hdr_type & 0x7f; - dev->multifunction = !!(hdr_type & 0x80); dev->vendor = l & 0xffff; dev->device = (l >> 16) & 0xffff; - dev->error_state = pci_channel_io_normal; - set_pcie_port_type(dev); - - list_for_each_entry(slot, &bus->slots, list) - if (PCI_SLOT(devfn) == slot->number) - dev->slot = slot; - /* Assume 32-bit PCI; let 64-bit PCI cards (which are far rarer) - set this higher, assuming the system even supports it. */ - dev->dma_mask = 0xffffffff; - if (pci_setup_device(dev) < 0) { + if (pci_setup_device(dev)) { kfree(dev); return NULL; } -- cgit v1.2.3 From dd7cc44d0bcec5e9c42fe52e88dc254ae62eac8d Mon Sep 17 00:00:00 2001 From: Yu Zhao Date: Fri, 20 Mar 2009 11:25:15 +0800 Subject: PCI: add SR-IOV API for Physical Function driver Add or remove the Virtual Function when the SR-IOV is enabled or disabled by the device driver. This can happen anytime rather than only at the device probe stage. Reviewed-by: Matthew Wilcox Signed-off-by: Yu Zhao Signed-off-by: Jesse Barnes --- drivers/pci/iov.c | 314 ++++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/pci/pci.h | 2 + include/linux/pci.h | 19 +++- 3 files changed, 334 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 5ddfc09a8d3f..d0ff8ad8f7ba 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -13,6 +13,7 @@ #include #include "pci.h" +#define VIRTFN_ID_LEN 16 static inline u8 virtfn_bus(struct pci_dev *dev, int id) { @@ -26,6 +27,284 @@ static inline u8 virtfn_devfn(struct pci_dev *dev, int id) dev->sriov->stride * id) & 0xff; } +static struct pci_bus *virtfn_add_bus(struct pci_bus *bus, int busnr) +{ + int rc; + struct pci_bus *child; + + if (bus->number == busnr) + return bus; + + child = pci_find_bus(pci_domain_nr(bus), busnr); + if (child) + return child; + + child = pci_add_new_bus(bus, NULL, busnr); + if (!child) + return NULL; + + child->subordinate = busnr; + child->dev.parent = bus->bridge; + rc = pci_bus_add_child(child); + if (rc) { + pci_remove_bus(child); + return NULL; + } + + return child; +} + +static void virtfn_remove_bus(struct pci_bus *bus, int busnr) +{ + struct pci_bus *child; + + if (bus->number == busnr) + return; + + child = pci_find_bus(pci_domain_nr(bus), busnr); + BUG_ON(!child); + + if (list_empty(&child->devices)) + pci_remove_bus(child); +} + +static int virtfn_add(struct pci_dev *dev, int id, int reset) +{ + int i; + int rc; + u64 size; + char buf[VIRTFN_ID_LEN]; + struct pci_dev *virtfn; + struct resource *res; + struct pci_sriov *iov = dev->sriov; + + virtfn = alloc_pci_dev(); + if (!virtfn) + return -ENOMEM; + + mutex_lock(&iov->dev->sriov->lock); + virtfn->bus = virtfn_add_bus(dev->bus, virtfn_bus(dev, id)); + if (!virtfn->bus) { + kfree(virtfn); + mutex_unlock(&iov->dev->sriov->lock); + return -ENOMEM; + } + virtfn->devfn = virtfn_devfn(dev, id); + virtfn->vendor = dev->vendor; + pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_DID, &virtfn->device); + pci_setup_device(virtfn); + virtfn->dev.parent = dev->dev.parent; + + for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { + res = dev->resource + PCI_IOV_RESOURCES + i; + if (!res->parent) + continue; + virtfn->resource[i].name = pci_name(virtfn); + virtfn->resource[i].flags = res->flags; + size = resource_size(res); + do_div(size, iov->total); + virtfn->resource[i].start = res->start + size * id; + virtfn->resource[i].end = virtfn->resource[i].start + size - 1; + rc = request_resource(res, &virtfn->resource[i]); + BUG_ON(rc); + } + + if (reset) + pci_execute_reset_function(virtfn); + + pci_device_add(virtfn, virtfn->bus); + mutex_unlock(&iov->dev->sriov->lock); + + virtfn->physfn = pci_dev_get(dev); + virtfn->is_virtfn = 1; + + rc = pci_bus_add_device(virtfn); + if (rc) + goto failed1; + sprintf(buf, "virtfn%u", id); + rc = sysfs_create_link(&dev->dev.kobj, &virtfn->dev.kobj, buf); + if (rc) + goto failed1; + rc = sysfs_create_link(&virtfn->dev.kobj, &dev->dev.kobj, "physfn"); + if (rc) + goto failed2; + + kobject_uevent(&virtfn->dev.kobj, KOBJ_CHANGE); + + return 0; + +failed2: + sysfs_remove_link(&dev->dev.kobj, buf); +failed1: + pci_dev_put(dev); + mutex_lock(&iov->dev->sriov->lock); + pci_remove_bus_device(virtfn); + virtfn_remove_bus(dev->bus, virtfn_bus(dev, id)); + mutex_unlock(&iov->dev->sriov->lock); + + return rc; +} + +static void virtfn_remove(struct pci_dev *dev, int id, int reset) +{ + char buf[VIRTFN_ID_LEN]; + struct pci_bus *bus; + struct pci_dev *virtfn; + struct pci_sriov *iov = dev->sriov; + + bus = pci_find_bus(pci_domain_nr(dev->bus), virtfn_bus(dev, id)); + if (!bus) + return; + + virtfn = pci_get_slot(bus, virtfn_devfn(dev, id)); + if (!virtfn) + return; + + pci_dev_put(virtfn); + + if (reset) { + device_release_driver(&virtfn->dev); + pci_execute_reset_function(virtfn); + } + + sprintf(buf, "virtfn%u", id); + sysfs_remove_link(&dev->dev.kobj, buf); + sysfs_remove_link(&virtfn->dev.kobj, "physfn"); + + mutex_lock(&iov->dev->sriov->lock); + pci_remove_bus_device(virtfn); + virtfn_remove_bus(dev->bus, virtfn_bus(dev, id)); + mutex_unlock(&iov->dev->sriov->lock); + + pci_dev_put(dev); +} + +static int sriov_enable(struct pci_dev *dev, int nr_virtfn) +{ + int rc; + int i, j; + int nres; + u16 offset, stride, initial; + struct resource *res; + struct pci_dev *pdev; + struct pci_sriov *iov = dev->sriov; + + if (!nr_virtfn) + return 0; + + if (iov->nr_virtfn) + return -EINVAL; + + pci_read_config_word(dev, iov->pos + PCI_SRIOV_INITIAL_VF, &initial); + if (initial > iov->total || + (!(iov->cap & PCI_SRIOV_CAP_VFM) && (initial != iov->total))) + return -EIO; + + if (nr_virtfn < 0 || nr_virtfn > iov->total || + (!(iov->cap & PCI_SRIOV_CAP_VFM) && (nr_virtfn > initial))) + return -EINVAL; + + pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, nr_virtfn); + pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_OFFSET, &offset); + pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_STRIDE, &stride); + if (!offset || (nr_virtfn > 1 && !stride)) + return -EIO; + + nres = 0; + for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { + res = dev->resource + PCI_IOV_RESOURCES + i; + if (res->parent) + nres++; + } + if (nres != iov->nres) { + dev_err(&dev->dev, "not enough MMIO resources for SR-IOV\n"); + return -ENOMEM; + } + + iov->offset = offset; + iov->stride = stride; + + if (virtfn_bus(dev, nr_virtfn - 1) > dev->bus->subordinate) { + dev_err(&dev->dev, "SR-IOV: bus number out of range\n"); + return -ENOMEM; + } + + if (iov->link != dev->devfn) { + pdev = pci_get_slot(dev->bus, iov->link); + if (!pdev) + return -ENODEV; + + pci_dev_put(pdev); + + if (!pdev->is_physfn) + return -ENODEV; + + rc = sysfs_create_link(&dev->dev.kobj, + &pdev->dev.kobj, "dep_link"); + if (rc) + return rc; + } + + iov->ctrl |= PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE; + pci_block_user_cfg_access(dev); + pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); + msleep(100); + pci_unblock_user_cfg_access(dev); + + iov->initial = initial; + if (nr_virtfn < initial) + initial = nr_virtfn; + + for (i = 0; i < initial; i++) { + rc = virtfn_add(dev, i, 0); + if (rc) + goto failed; + } + + kobject_uevent(&dev->dev.kobj, KOBJ_CHANGE); + iov->nr_virtfn = nr_virtfn; + + return 0; + +failed: + for (j = 0; j < i; j++) + virtfn_remove(dev, j, 0); + + iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE); + pci_block_user_cfg_access(dev); + pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); + ssleep(1); + pci_unblock_user_cfg_access(dev); + + if (iov->link != dev->devfn) + sysfs_remove_link(&dev->dev.kobj, "dep_link"); + + return rc; +} + +static void sriov_disable(struct pci_dev *dev) +{ + int i; + struct pci_sriov *iov = dev->sriov; + + if (!iov->nr_virtfn) + return; + + for (i = 0; i < iov->nr_virtfn; i++) + virtfn_remove(dev, i, 0); + + iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE); + pci_block_user_cfg_access(dev); + pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); + ssleep(1); + pci_unblock_user_cfg_access(dev); + + if (iov->link != dev->devfn) + sysfs_remove_link(&dev->dev.kobj, "dep_link"); + + iov->nr_virtfn = 0; +} + static int sriov_init(struct pci_dev *dev, int pos) { int i; @@ -132,6 +411,8 @@ failed: static void sriov_release(struct pci_dev *dev) { + BUG_ON(dev->sriov->nr_virtfn); + if (dev == dev->sriov->dev) mutex_destroy(&dev->sriov->lock); else @@ -155,6 +436,7 @@ static void sriov_restore_state(struct pci_dev *dev) pci_update_resource(dev, i); pci_write_config_dword(dev, iov->pos + PCI_SRIOV_SYS_PGSIZE, iov->pgsz); + pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, iov->nr_virtfn); pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); if (iov->ctrl & PCI_SRIOV_CTRL_VFE) msleep(100); @@ -245,3 +527,35 @@ int pci_iov_bus_range(struct pci_bus *bus) return max ? max - bus->number : 0; } + +/** + * pci_enable_sriov - enable the SR-IOV capability + * @dev: the PCI device + * + * Returns 0 on success, or negative on failure. + */ +int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn) +{ + might_sleep(); + + if (!dev->is_physfn) + return -ENODEV; + + return sriov_enable(dev, nr_virtfn); +} +EXPORT_SYMBOL_GPL(pci_enable_sriov); + +/** + * pci_disable_sriov - disable the SR-IOV capability + * @dev: the PCI device + */ +void pci_disable_sriov(struct pci_dev *dev) +{ + might_sleep(); + + if (!dev->is_physfn) + return; + + sriov_disable(dev); +} +EXPORT_SYMBOL_GPL(pci_disable_sriov); diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index f4fc10fc5872..0f1c7d103509 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -209,6 +209,8 @@ struct pci_sriov { u32 cap; /* SR-IOV Capabilities */ u16 ctrl; /* SR-IOV Control */ u16 total; /* total VFs associated with the PF */ + u16 initial; /* initial VFs associated with the PF */ + u16 nr_virtfn; /* number of VFs available */ u16 offset; /* first VF Routing ID offset */ u16 stride; /* following VF stride */ u32 pgsz; /* page size for BAR alignment */ diff --git a/include/linux/pci.h b/include/linux/pci.h index 8ce2f2d9ab63..c2e491e04063 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -265,6 +265,7 @@ struct pci_dev { unsigned int is_pcie:1; unsigned int state_saved:1; unsigned int is_physfn:1; + unsigned int is_virtfn:1; pci_dev_flags_t dev_flags; atomic_t enable_cnt; /* pci_enable_device has been called */ @@ -279,7 +280,10 @@ struct pci_dev { #endif struct pci_vpd *vpd; #ifdef CONFIG_PCI_IOV - struct pci_sriov *sriov; /* SR-IOV capability related */ + union { + struct pci_sriov *sriov; /* SR-IOV capability related */ + struct pci_dev *physfn; /* the PF this VF is associated with */ + }; #endif }; @@ -1212,5 +1216,18 @@ int pci_ext_cfg_avail(struct pci_dev *dev); void __iomem *pci_ioremap_bar(struct pci_dev *pdev, int bar); +#ifdef CONFIG_PCI_IOV +extern int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn); +extern void pci_disable_sriov(struct pci_dev *dev); +#else +static inline int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn) +{ + return -ENODEV; +} +static inline void pci_disable_sriov(struct pci_dev *dev) +{ +} +#endif + #endif /* __KERNEL__ */ #endif /* LINUX_PCI_H */ -- cgit v1.2.3 From 74bb1bcc7dbbc9ddef773bf3395d7ff92aaaad2e Mon Sep 17 00:00:00 2001 From: Yu Zhao Date: Fri, 20 Mar 2009 11:25:16 +0800 Subject: PCI: handle SR-IOV Virtual Function Migration Add or remove a Virtual Function after receiving a Migrate In or Out Request. Reviewed-by: Matthew Wilcox Signed-off-by: Yu Zhao Signed-off-by: Jesse Barnes --- drivers/pci/iov.c | 119 ++++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/pci/pci.h | 4 ++ include/linux/pci.h | 6 +++ 3 files changed, 129 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index d0ff8ad8f7ba..7227efc760db 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -179,6 +179,97 @@ static void virtfn_remove(struct pci_dev *dev, int id, int reset) pci_dev_put(dev); } +static int sriov_migration(struct pci_dev *dev) +{ + u16 status; + struct pci_sriov *iov = dev->sriov; + + if (!iov->nr_virtfn) + return 0; + + if (!(iov->cap & PCI_SRIOV_CAP_VFM)) + return 0; + + pci_read_config_word(dev, iov->pos + PCI_SRIOV_STATUS, &status); + if (!(status & PCI_SRIOV_STATUS_VFM)) + return 0; + + schedule_work(&iov->mtask); + + return 1; +} + +static void sriov_migration_task(struct work_struct *work) +{ + int i; + u8 state; + u16 status; + struct pci_sriov *iov = container_of(work, struct pci_sriov, mtask); + + for (i = iov->initial; i < iov->nr_virtfn; i++) { + state = readb(iov->mstate + i); + if (state == PCI_SRIOV_VFM_MI) { + writeb(PCI_SRIOV_VFM_AV, iov->mstate + i); + state = readb(iov->mstate + i); + if (state == PCI_SRIOV_VFM_AV) + virtfn_add(iov->self, i, 1); + } else if (state == PCI_SRIOV_VFM_MO) { + virtfn_remove(iov->self, i, 1); + writeb(PCI_SRIOV_VFM_UA, iov->mstate + i); + state = readb(iov->mstate + i); + if (state == PCI_SRIOV_VFM_AV) + virtfn_add(iov->self, i, 0); + } + } + + pci_read_config_word(iov->self, iov->pos + PCI_SRIOV_STATUS, &status); + status &= ~PCI_SRIOV_STATUS_VFM; + pci_write_config_word(iov->self, iov->pos + PCI_SRIOV_STATUS, status); +} + +static int sriov_enable_migration(struct pci_dev *dev, int nr_virtfn) +{ + int bir; + u32 table; + resource_size_t pa; + struct pci_sriov *iov = dev->sriov; + + if (nr_virtfn <= iov->initial) + return 0; + + pci_read_config_dword(dev, iov->pos + PCI_SRIOV_VFM, &table); + bir = PCI_SRIOV_VFM_BIR(table); + if (bir > PCI_STD_RESOURCE_END) + return -EIO; + + table = PCI_SRIOV_VFM_OFFSET(table); + if (table + nr_virtfn > pci_resource_len(dev, bir)) + return -EIO; + + pa = pci_resource_start(dev, bir) + table; + iov->mstate = ioremap(pa, nr_virtfn); + if (!iov->mstate) + return -ENOMEM; + + INIT_WORK(&iov->mtask, sriov_migration_task); + + iov->ctrl |= PCI_SRIOV_CTRL_VFM | PCI_SRIOV_CTRL_INTR; + pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); + + return 0; +} + +static void sriov_disable_migration(struct pci_dev *dev) +{ + struct pci_sriov *iov = dev->sriov; + + iov->ctrl &= ~(PCI_SRIOV_CTRL_VFM | PCI_SRIOV_CTRL_INTR); + pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); + + cancel_work_sync(&iov->mtask); + iounmap(iov->mstate); +} + static int sriov_enable(struct pci_dev *dev, int nr_virtfn) { int rc; @@ -261,6 +352,12 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn) goto failed; } + if (iov->cap & PCI_SRIOV_CAP_VFM) { + rc = sriov_enable_migration(dev, nr_virtfn); + if (rc) + goto failed; + } + kobject_uevent(&dev->dev.kobj, KOBJ_CHANGE); iov->nr_virtfn = nr_virtfn; @@ -290,6 +387,9 @@ static void sriov_disable(struct pci_dev *dev) if (!iov->nr_virtfn) return; + if (iov->cap & PCI_SRIOV_CAP_VFM) + sriov_disable_migration(dev); + for (i = 0; i < iov->nr_virtfn; i++) virtfn_remove(dev, i, 0); @@ -559,3 +659,22 @@ void pci_disable_sriov(struct pci_dev *dev) sriov_disable(dev); } EXPORT_SYMBOL_GPL(pci_disable_sriov); + +/** + * pci_sriov_migration - notify SR-IOV core of Virtual Function Migration + * @dev: the PCI device + * + * Returns IRQ_HANDLED if the IRQ is handled, or IRQ_NONE if not. + * + * Physical Function driver is responsible to register IRQ handler using + * VF Migration Interrupt Message Number, and call this function when the + * interrupt is generated by the hardware. + */ +irqreturn_t pci_sriov_migration(struct pci_dev *dev) +{ + if (!dev->is_physfn) + return IRQ_NONE; + + return sriov_migration(dev) ? IRQ_HANDLED : IRQ_NONE; +} +EXPORT_SYMBOL_GPL(pci_sriov_migration); diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 0f1c7d103509..22dcfdb75d91 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -1,6 +1,8 @@ #ifndef DRIVERS_PCI_H #define DRIVERS_PCI_H +#include + #define PCI_CFG_SPACE_SIZE 256 #define PCI_CFG_SPACE_EXP_SIZE 4096 @@ -218,6 +220,8 @@ struct pci_sriov { struct pci_dev *dev; /* lowest numbered PF */ struct pci_dev *self; /* this PF */ struct mutex lock; /* lock for VF bus */ + struct work_struct mtask; /* VF Migration task */ + u8 __iomem *mstate; /* VF Migration State Array */ }; #ifdef CONFIG_PCI_IOV diff --git a/include/linux/pci.h b/include/linux/pci.h index c2e491e04063..1216843412da 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -52,6 +52,7 @@ #include #include #include +#include /* Include the ID list */ #include @@ -1219,6 +1220,7 @@ void __iomem *pci_ioremap_bar(struct pci_dev *pdev, int bar); #ifdef CONFIG_PCI_IOV extern int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn); extern void pci_disable_sriov(struct pci_dev *dev); +extern irqreturn_t pci_sriov_migration(struct pci_dev *dev); #else static inline int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn) { @@ -1227,6 +1229,10 @@ static inline int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn) static inline void pci_disable_sriov(struct pci_dev *dev) { } +static inline irqreturn_t pci_sriov_migration(struct pci_dev *dev) +{ + return IRQ_NONE; +} #endif #endif /* __KERNEL__ */ -- cgit v1.2.3 From 90bdb3117f4209baa6d712b126f0e7791b24dc3f Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Fri, 20 Mar 2009 14:56:00 -0600 Subject: PCI: don't scan existing devices pci_scan_single_device is supposed to add newly discovered devices to pci_bus->devices, but doesn't check to see if the device has already been added. This can cause problems if we ever want to use this interface to rescan the PCI bus. If the device is already added, just return it. Signed-off-by: Trent Piepho Signed-off-by: Alex Chiang Signed-off-by: Jesse Barnes --- drivers/pci/probe.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 943c49a7842c..140b9deb482c 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1019,6 +1019,12 @@ struct pci_dev *__ref pci_scan_single_device(struct pci_bus *bus, int devfn) { struct pci_dev *dev; + dev = pci_get_slot(bus, devfn); + if (dev) { + pci_dev_put(dev); + return dev; + } + dev = pci_scan_device(bus, devfn); if (!dev) return NULL; -- cgit v1.2.3 From 1b69dfc649e6658fc38499cf704750d74cabc73d Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Fri, 20 Mar 2009 14:56:05 -0600 Subject: PCI: pci_scan_slot() returns newly found devices pci_scan_slot() has been rewritten to be less complex and will now return the number of *new* devices found. Existing callers need not worry because they already assume that they can't call pci_scan_slot() on an already-scanned slot. Thus, there is no semantic change for existing callers: returning newly found devices (this patch) is exactly equal to returning all found devices (before this patch). This patch adds some more groundwork to allow us to rescan the PCI bus during runtime to discover newly added devices. Signed-off-by: Trent Piepho Reviewed-by: Alex Chiang Signed-off-by: Alex Chiang Signed-off-by: Jesse Barnes --- drivers/pci/probe.c | 40 ++++++++++++++++------------------------ 1 file changed, 16 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 140b9deb482c..f3aabdf28f84 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1043,35 +1043,27 @@ EXPORT_SYMBOL(pci_scan_single_device); * Scan a PCI slot on the specified PCI bus for devices, adding * discovered devices to the @bus->devices list. New devices * will not have is_added set. + * + * Returns the number of new devices found. */ int pci_scan_slot(struct pci_bus *bus, int devfn) { - int func, nr = 0; - int scan_all_fns; - - scan_all_fns = pcibios_scan_all_fns(bus, devfn); - - for (func = 0; func < 8; func++, devfn++) { - struct pci_dev *dev; - - dev = pci_scan_single_device(bus, devfn); - if (dev) { - nr++; + int fn, nr = 0; + struct pci_dev *dev; - /* - * If this is a single function device, - * don't scan past the first function. - */ - if (!dev->multifunction) { - if (func > 0) { - dev->multifunction = 1; - } else { - break; - } + dev = pci_scan_single_device(bus, devfn); + if (dev && !dev->is_added) /* new device? */ + nr++; + + if ((dev && dev->multifunction) || + (!dev && pcibios_scan_all_fns(bus, devfn))) { + for (fn = 1; fn < 8; fn++) { + dev = pci_scan_single_device(bus, devfn + fn); + if (dev) { + if (!dev->is_added) + nr++; + dev->multifunction = 1; } - } else { - if (func == 0 && !scan_all_fns) - break; } } -- cgit v1.2.3 From 74710ded8e16fc8dacbb702a5bac1a493d88549a Mon Sep 17 00:00:00 2001 From: Alex Chiang Date: Fri, 20 Mar 2009 14:56:10 -0600 Subject: PCI: always scan child buses While scanning bridges, we stop our scan if we encounter a bus that we've seen before, to work around some buggy chipsets. This is a good idea, but prevents us from fully scanning the PCI bus at a future time (to find newly hot-added devices, for example). Change the logic so that we skip _re-adding_ an existing bus that we've seen before, but also allow the scan to descend to all child buses. Now that we're potentially scanning our child buses again, we also need to be sure not to attempt re-initializing their BARs so we avoid that. This patch lays the groundwork to allow the user to issue a rescan of the PCI bus at any time. Signed-off-by: Alex Chiang Signed-off-by: Jesse Barnes --- drivers/pci/probe.c | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index f3aabdf28f84..f69256c63b2b 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -511,21 +511,21 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, /* * If we already got to this bus through a different bridge, - * ignore it. This can happen with the i450NX chipset. + * don't re-add it. This can happen with the i450NX chipset. + * + * However, we continue to descend down the hierarchy and + * scan remaining child buses. */ - if (pci_find_bus(pci_domain_nr(bus), busnr)) { - dev_info(&dev->dev, "bus %04x:%02x already known\n", - pci_domain_nr(bus), busnr); - goto out; + child = pci_find_bus(pci_domain_nr(bus), busnr); + if (!child) { + child = pci_add_new_bus(bus, dev, busnr); + if (!child) + goto out; + child->primary = buses & 0xFF; + child->subordinate = (buses >> 16) & 0xFF; + child->bridge_ctl = bctl; } - child = pci_add_new_bus(bus, dev, busnr); - if (!child) - goto out; - child->primary = buses & 0xFF; - child->subordinate = (buses >> 16) & 0xFF; - child->bridge_ctl = bctl; - cmax = pci_scan_child_bus(child); if (cmax > max) max = cmax; @@ -1092,8 +1092,14 @@ unsigned int __devinit pci_scan_child_bus(struct pci_bus *bus) * After performing arch-dependent fixup of the bus, look behind * all PCI-to-PCI bridges on this bus. */ - pr_debug("PCI: Fixups for bus %04x:%02x\n", pci_domain_nr(bus), bus->number); - pcibios_fixup_bus(bus); + if (!bus->is_added) { + pr_debug("PCI: Fixups for bus %04x:%02x\n", + pci_domain_nr(bus), bus->number); + pcibios_fixup_bus(bus); + if (pci_is_root_bus(bus)) + bus->is_added = 1; + } + for (pass=0; pass < 2; pass++) list_for_each_entry(dev, &bus->devices, bus_list) { if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || -- cgit v1.2.3 From b73e97d95c168cbc19bd1208c894077f25931ba1 Mon Sep 17 00:00:00 2001 From: Alex Chiang Date: Fri, 20 Mar 2009 14:56:15 -0600 Subject: PCI: do not initialize bridges more than once In preparation for PCI core hotplug, we need to ensure that we do not attempt to re-initialize bridges that have already been initialized. We only need to worry about non-root buses, since we will not allow root bus removal. Reported-by: Kenji Kaneshige Signed-off-by: Alex Chiang Signed-off-by: Jesse Barnes --- drivers/pci/setup-bus.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 170a3eda9dd3..334285a8e237 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -144,6 +144,9 @@ static void pci_setup_bridge(struct pci_bus *bus) struct pci_bus_region region; u32 l, bu, lu, io_upper16; + if (!pci_is_root_bus(bus) && bus->is_added) + return; + dev_info(&bridge->dev, "PCI bridge, secondary bus %04x:%02x\n", pci_domain_nr(bus), bus->number); -- cgit v1.2.3 From 9dd90cafa7a712d283e2e0c625b022e19f746762 Mon Sep 17 00:00:00 2001 From: Alex Chiang Date: Fri, 20 Mar 2009 14:56:20 -0600 Subject: PCI: do not enable bridges more than once In preparation for PCI core hotplug, we need to ensure that we do not attempt to re-enable bridges that have already been enabled. Reported-by: Kenji Kaneshige Signed-off-by: Alex Chiang Signed-off-by: Jesse Barnes --- drivers/pci/bus.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 118c77778d29..68f91a252595 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -184,8 +184,10 @@ void pci_enable_bridges(struct pci_bus *bus) list_for_each_entry(dev, &bus->devices, bus_list) { if (dev->subordinate) { - retval = pci_enable_device(dev); - pci_set_master(dev); + if (atomic_read(&dev->enable_cnt) == 0) { + retval = pci_enable_device(dev); + pci_set_master(dev); + } pci_enable_bridges(dev->subordinate); } } -- cgit v1.2.3 From 3ed4fd96b3188406ac5357d9290bcffa08c65cf6 Mon Sep 17 00:00:00 2001 From: Alex Chiang Date: Fri, 20 Mar 2009 14:56:25 -0600 Subject: PCI: Introduce pci_rescan_bus() This API is used by the PCI core to rescan a bus and rediscover newly added devices. Over time, it is expected that the various PCI hotplug drivers will migrate to this interface and away from the old pci_do_scan_bus() interface. Signed-off-by: Alex Chiang Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/fakephp.c | 6 +++--- drivers/pci/probe.c | 32 ++++++++++++++++++++++++++++++++ include/linux/pci.h | 3 +++ 3 files changed, 38 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/fakephp.c b/drivers/pci/hotplug/fakephp.c index d8649e127298..16063745766e 100644 --- a/drivers/pci/hotplug/fakephp.c +++ b/drivers/pci/hotplug/fakephp.c @@ -245,12 +245,12 @@ static int pci_rescan_slot(struct pci_dev *temp) /** - * pci_rescan_bus - Rescan PCI bus + * pci_rescan_bus_local - fakephp version of rescan PCI bus * @bus: the PCI bus to rescan * * Call pci_rescan_slot for each possible function of the bus. */ -static void pci_rescan_bus(const struct pci_bus *bus) +static void pci_rescan_bus_local(const struct pci_bus *bus) { unsigned int devfn; struct pci_dev *dev; @@ -291,7 +291,7 @@ static void pci_rescan_buses(const struct list_head *list) const struct list_head *l; list_for_each(l,list) { const struct pci_bus *b = pci_bus_b(l); - pci_rescan_bus(b); + pci_rescan_bus_local(b); pci_rescan_buses(&b->children); } } diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index f69256c63b2b..60a8e5fec6c5 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1212,6 +1212,38 @@ struct pci_bus * __devinit pci_scan_bus_parented(struct device *parent, EXPORT_SYMBOL(pci_scan_bus_parented); #ifdef CONFIG_HOTPLUG +/** + * pci_rescan_bus - scan a PCI bus for devices. + * @bus: PCI bus to scan + * + * Scan a PCI bus and child buses for new devices, adds them, + * and enables them. + * + * Returns the max number of subordinate bus discovered. + */ +unsigned int __devinit pci_rescan_bus(struct pci_bus *bus) +{ + unsigned int max; + struct pci_dev *dev; + + max = pci_scan_child_bus(bus); + + up_read(&pci_bus_sem); + list_for_each_entry(dev, &bus->devices, bus_list) + if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || + dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) + if (dev->subordinate) + pci_bus_size_bridges(dev->subordinate); + down_read(&pci_bus_sem); + + pci_bus_assign_resources(bus); + pci_enable_bridges(bus); + pci_bus_add_devices(bus); + + return max; +} +EXPORT_SYMBOL_GPL(pci_rescan_bus); + EXPORT_SYMBOL(pci_add_new_bus); EXPORT_SYMBOL(pci_scan_slot); EXPORT_SYMBOL(pci_scan_bridge); diff --git a/include/linux/pci.h b/include/linux/pci.h index 50d94388e87c..6fb335b0d74f 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -726,6 +726,9 @@ int pci_back_from_sleep(struct pci_dev *dev); /* Functions for PCI Hotplug drivers to use */ int pci_bus_find_capability(struct pci_bus *bus, unsigned int devfn, int cap); +#ifdef CONFIG_HOTPLUG +unsigned int pci_rescan_bus(struct pci_bus *bus); +#endif /* Vital product data routines */ ssize_t pci_read_vpd(struct pci_dev *dev, loff_t pos, size_t count, void *buf); -- cgit v1.2.3 From 705b1aaa823e800490f157cd9366ad8cff385f5f Mon Sep 17 00:00:00 2001 From: Alex Chiang Date: Fri, 20 Mar 2009 14:56:31 -0600 Subject: PCI: Introduce /sys/bus/pci/rescan This interface allows the user to force a rescan of all PCI buses in system, and rediscover devices that have been removed earlier. pci_bus_attrs implementation from Trent Piepho. Thanks to Vegard Nossum for discovering locking issues with the sysfs interface. Cc: Trent Piepho Signed-off-by: Alex Chiang Signed-off-by: Jesse Barnes --- Documentation/ABI/testing/sysfs-bus-pci | 9 +++++++++ drivers/pci/pci-driver.c | 1 + drivers/pci/pci-sysfs.c | 26 ++++++++++++++++++++++++++ drivers/pci/pci.h | 6 ++++++ drivers/pci/probe.c | 4 ++-- 5 files changed, 44 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci index d175a2a5fac2..e6ad047cb3c2 100644 --- a/Documentation/ABI/testing/sysfs-bus-pci +++ b/Documentation/ABI/testing/sysfs-bus-pci @@ -57,6 +57,15 @@ Description: match the driver to the device. For example: # echo "8086 10f5" > /sys/bus/pci/drivers/foo/remove_id +What: /sys/bus/pci/rescan +Date: January 2009 +Contact: Linux PCI developers +Description: + Writing a non-zero value to this attribute will + force a rescan of all PCI buses in the system, and + re-discover previously removed devices. + Depends on CONFIG_HOTPLUG. + What: /sys/bus/pci/devices/.../vpd Date: February 2008 Contact: Ben Hutchings diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 87a5ddbb324f..95d198570290 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -1049,6 +1049,7 @@ struct bus_type pci_bus_type = { .remove = pci_device_remove, .shutdown = pci_device_shutdown, .dev_attrs = pci_dev_attrs, + .bus_attrs = pci_bus_attrs, .pm = PCI_PM_OPS_PTR, }; diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index ec7a175dcbaf..be7468a5eb72 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -219,6 +219,32 @@ msi_bus_store(struct device *dev, struct device_attribute *attr, return count; } +#ifdef CONFIG_HOTPLUG +static DEFINE_MUTEX(pci_remove_rescan_mutex); +static ssize_t bus_rescan_store(struct bus_type *bus, const char *buf, + size_t count) +{ + unsigned long val; + struct pci_bus *b = NULL; + + if (strict_strtoul(buf, 0, &val) < 0) + return -EINVAL; + + if (val) { + mutex_lock(&pci_remove_rescan_mutex); + while ((b = pci_find_next_bus(b)) != NULL) + pci_rescan_bus(b); + mutex_unlock(&pci_remove_rescan_mutex); + } + return count; +} + +struct bus_attribute pci_bus_attrs[] = { + __ATTR(rescan, (S_IWUSR|S_IWGRP), NULL, bus_rescan_store), + __ATTR_NULL +}; +#endif + struct device_attribute pci_dev_attrs[] = { __ATTR_RO(resource), __ATTR_RO(vendor), diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 22dcfdb75d91..45833a5bca61 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -138,6 +138,12 @@ extern int pcie_mch_quirk; extern struct device_attribute pci_dev_attrs[]; extern struct device_attribute dev_attr_cpuaffinity; extern struct device_attribute dev_attr_cpulistaffinity; +#ifdef CONFIG_HOTPLUG +extern struct bus_attribute pci_bus_attrs[]; +#else +#define pci_bus_attrs NULL +#endif + /** * pci_match_one_device - Tell if a PCI device structure has a matching diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 60a8e5fec6c5..56c71e585f3d 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1228,13 +1228,13 @@ unsigned int __devinit pci_rescan_bus(struct pci_bus *bus) max = pci_scan_child_bus(bus); - up_read(&pci_bus_sem); + down_read(&pci_bus_sem); list_for_each_entry(dev, &bus->devices, bus_list) if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) if (dev->subordinate) pci_bus_size_bridges(dev->subordinate); - down_read(&pci_bus_sem); + up_read(&pci_bus_sem); pci_bus_assign_resources(bus); pci_enable_bridges(bus); -- cgit v1.2.3 From 77c27c7b49d69d45ccb94e481653f024f1ac6650 Mon Sep 17 00:00:00 2001 From: Alex Chiang Date: Fri, 20 Mar 2009 14:56:36 -0600 Subject: PCI: Introduce /sys/bus/pci/devices/.../remove This patch adds an attribute named "remove" to a PCI device's sysfs directory. Writing a non-zero value to this attribute will remove the PCI device and any children of it. Trent Piepho wrote the original implementation and documentation. Thanks to Vegard Nossum for testing under kmemcheck and finding locking issues with the sysfs interface. Cc: Trent Piepho Tested-by: Vegard Nossum Signed-off-by: Alex Chiang Signed-off-by: Jesse Barnes --- Documentation/ABI/testing/sysfs-bus-pci | 8 ++++++++ Documentation/filesystems/sysfs-pci.txt | 10 +++++++++ drivers/pci/pci-sysfs.c | 36 +++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+) (limited to 'drivers') diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci index e6ad047cb3c2..daa791a9e85e 100644 --- a/Documentation/ABI/testing/sysfs-bus-pci +++ b/Documentation/ABI/testing/sysfs-bus-pci @@ -66,6 +66,14 @@ Description: re-discover previously removed devices. Depends on CONFIG_HOTPLUG. +What: /sys/bus/pci/devices/.../remove +Date: January 2009 +Contact: Linux PCI developers +Description: + Writing a non-zero value to this attribute will + hot-remove the PCI device and any of its children. + Depends on CONFIG_HOTPLUG. + What: /sys/bus/pci/devices/.../vpd Date: February 2008 Contact: Ben Hutchings diff --git a/Documentation/filesystems/sysfs-pci.txt b/Documentation/filesystems/sysfs-pci.txt index 9f8740ca3f3b..26e4b8bc53ee 100644 --- a/Documentation/filesystems/sysfs-pci.txt +++ b/Documentation/filesystems/sysfs-pci.txt @@ -12,6 +12,7 @@ that support it. For example, a given bus might look like this: | |-- enable | |-- irq | |-- local_cpus + | |-- remove | |-- resource | |-- resource0 | |-- resource1 @@ -36,6 +37,7 @@ files, each with their own function. enable Whether the device is enabled (ascii, rw) irq IRQ number (ascii, ro) local_cpus nearby CPU mask (cpumask, ro) + remove remove device from kernel's list (ascii, wo) resource PCI resource host addresses (ascii, ro) resource0..N PCI resource N, if present (binary, mmap) resource0_wc..N_wc PCI WC map resource N, if prefetchable (binary, mmap) @@ -46,6 +48,7 @@ files, each with their own function. ro - read only file rw - file is readable and writable + wo - write only file mmap - file is mmapable ascii - file contains ascii text binary - file contains binary data @@ -73,6 +76,13 @@ that the device must be enabled for a rom read to return data succesfully. In the event a driver is not bound to the device, it can be enabled using the 'enable' file, documented above. +The 'remove' file is used to remove the PCI device, by writing a non-zero +integer to the file. This does not involve any kind of hot-plug functionality, +e.g. powering off the device. The device is removed from the kernel's list of +PCI devices, the sysfs directory for it is removed, and the device will be +removed from any drivers attached to it. Removal of PCI root buses is +disallowed. + Accessing legacy resources through sysfs ---------------------------------------- diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index be7468a5eb72..e16990ecc024 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -243,6 +243,39 @@ struct bus_attribute pci_bus_attrs[] = { __ATTR(rescan, (S_IWUSR|S_IWGRP), NULL, bus_rescan_store), __ATTR_NULL }; + +static void remove_callback(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + + mutex_lock(&pci_remove_rescan_mutex); + pci_remove_bus_device(pdev); + mutex_unlock(&pci_remove_rescan_mutex); +} + +static ssize_t +remove_store(struct device *dev, struct device_attribute *dummy, + const char *buf, size_t count) +{ + int ret = 0; + unsigned long val; + struct pci_dev *pdev = to_pci_dev(dev); + + if (strict_strtoul(buf, 0, &val) < 0) + return -EINVAL; + + if (pci_is_root_bus(pdev->bus)) + return -EBUSY; + + /* An attribute cannot be unregistered by one of its own methods, + * so we have to use this roundabout approach. + */ + if (val) + ret = device_schedule_callback(dev, remove_callback); + if (ret) + count = ret; + return count; +} #endif struct device_attribute pci_dev_attrs[] = { @@ -263,6 +296,9 @@ struct device_attribute pci_dev_attrs[] = { __ATTR(broken_parity_status,(S_IRUGO|S_IWUSR), broken_parity_status_show,broken_parity_status_store), __ATTR(msi_bus, 0644, msi_bus_show, msi_bus_store), +#ifdef CONFIG_HOTPLUG + __ATTR(remove, (S_IWUSR|S_IWGRP), NULL, remove_store), +#endif __ATTR_NULL, }; -- cgit v1.2.3 From 738a6396c223b486304dda778119dbbca563f019 Mon Sep 17 00:00:00 2001 From: Alex Chiang Date: Fri, 20 Mar 2009 14:56:41 -0600 Subject: PCI: Introduce /sys/bus/pci/devices/.../rescan This interface allows the user to force a rescan of the device's parent bus and all subordinate buses, and rediscover devices removed earlier from this part of the device tree. Cc: Trent Piepho Signed-off-by: Alex Chiang Signed-off-by: Jesse Barnes --- Documentation/ABI/testing/sysfs-bus-pci | 10 ++++++++++ drivers/pci/pci-sysfs.c | 19 +++++++++++++++++++ 2 files changed, 29 insertions(+) (limited to 'drivers') diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci index daa791a9e85e..97ad190e13af 100644 --- a/Documentation/ABI/testing/sysfs-bus-pci +++ b/Documentation/ABI/testing/sysfs-bus-pci @@ -74,6 +74,16 @@ Description: hot-remove the PCI device and any of its children. Depends on CONFIG_HOTPLUG. +What: /sys/bus/pci/devices/.../rescan +Date: January 2009 +Contact: Linux PCI developers +Description: + Writing a non-zero value to this attribute will + force a rescan of the device's parent bus and all + child buses, and re-discover devices removed earlier + from this part of the device tree. + Depends on CONFIG_HOTPLUG. + What: /sys/bus/pci/devices/.../vpd Date: February 2008 Contact: Ben Hutchings diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index e16990ecc024..e9a8706a6401 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -244,6 +244,24 @@ struct bus_attribute pci_bus_attrs[] = { __ATTR_NULL }; +static ssize_t +dev_rescan_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long val; + struct pci_dev *pdev = to_pci_dev(dev); + + if (strict_strtoul(buf, 0, &val) < 0) + return -EINVAL; + + if (val) { + mutex_lock(&pci_remove_rescan_mutex); + pci_rescan_bus(pdev->bus); + mutex_unlock(&pci_remove_rescan_mutex); + } + return count; +} + static void remove_callback(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); @@ -298,6 +316,7 @@ struct device_attribute pci_dev_attrs[] = { __ATTR(msi_bus, 0644, msi_bus_show, msi_bus_store), #ifdef CONFIG_HOTPLUG __ATTR(remove, (S_IWUSR|S_IWGRP), NULL, remove_store), + __ATTR(rescan, (S_IWUSR|S_IWGRP), NULL, dev_rescan_store), #endif __ATTR_NULL, }; -- cgit v1.2.3 From 83dbf66f04b96e65c6c18436c16d40f9cf8630aa Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Fri, 20 Mar 2009 14:56:46 -0600 Subject: PCI Hotplug: restore fakephp interface with complete reimplementation A complete re-implementation of fakephp is necessary if it is to present its former interface (pre-2.6.27, when it broke). The reason is that PCI hotplug drivers call pci_hp_register(), which enforces the rule that only one /sys/bus/pci/slots/ file may be created per physical slot. The change breaks the old fakephp's assumption that it could create a file per function. So we re-implement fakephp to avoid using the standard PCI hotplug API so that we can restore the old fakephp user interface. It puts entries in /sys/bus/pci/slots with the names of all PCI devices/functions, exactly symmetrical to what is shown in /sys/bus/pci/devices. Each slots/ entry has a "power" attribute, which works the same way as the fakephp driver's power attribute has worked. There are a few improvements over old fakephp, which couldn't handle PCI devices being added or removed via a means outside of fakephp's knowledge. If a device was added another way, old fakephp didn't notice and didn't create the fake slot for it. If a device was removed another way, old fakephp didn't delete the fake slot for it (and accessing the stale slot caused an oops). The new implementation overcomes these limitations. As a consequence, removing a bridge with other devices behind it now works as well, which is something else old fakephp couldn't do previously. This duplicates a tiny bit of the code in the PCI core that does this same function. Re-using that code ends up being more complex than duplicating it, and it makes code in the PCI core more ugly just to support this legacy fakephp interface compatibility layer. Reviewed-by: James Cameron Signed-off-by: Trent Piepho Signed-off-by: Alex Chiang Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/Makefile | 2 +- drivers/pci/hotplug/fakephp.c | 395 ----------------------------------- drivers/pci/hotplug/legacy_fakephp.c | 162 ++++++++++++++ 3 files changed, 163 insertions(+), 396 deletions(-) delete mode 100644 drivers/pci/hotplug/fakephp.c create mode 100644 drivers/pci/hotplug/legacy_fakephp.c (limited to 'drivers') diff --git a/drivers/pci/hotplug/Makefile b/drivers/pci/hotplug/Makefile index 2aa117c8cd87..3314fe881509 100644 --- a/drivers/pci/hotplug/Makefile +++ b/drivers/pci/hotplug/Makefile @@ -20,7 +20,7 @@ obj-$(CONFIG_HOTPLUG_PCI_RPA_DLPAR) += rpadlpar_io.o obj-$(CONFIG_HOTPLUG_PCI_SGI) += sgi_hotplug.o # Link this last so it doesn't claim devices that have a real hotplug driver -obj-$(CONFIG_HOTPLUG_PCI_FAKE) += fakephp.o +obj-$(CONFIG_HOTPLUG_PCI_FAKE) += legacy_fakephp.o pci_hotplug-objs := pci_hotplug_core.o diff --git a/drivers/pci/hotplug/fakephp.c b/drivers/pci/hotplug/fakephp.c deleted file mode 100644 index 16063745766e..000000000000 --- a/drivers/pci/hotplug/fakephp.c +++ /dev/null @@ -1,395 +0,0 @@ -/* - * Fake PCI Hot Plug Controller Driver - * - * Copyright (C) 2003 Greg Kroah-Hartman - * Copyright (C) 2003 IBM Corp. - * Copyright (C) 2003 Rolf Eike Beer - * - * Based on ideas and code from: - * Vladimir Kondratiev - * Rolf Eike Beer - * - * All rights reserved. - * - * 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. - * - * Send feedback to - */ - -/* - * - * This driver will "emulate" removing PCI devices from the system. If - * the "power" file is written to with "0" then the specified PCI device - * will be completely removed from the kernel. - * - * WARNING, this does NOT turn off the power to the PCI device. This is - * a "logical" removal, not a physical or electrical removal. - * - * Use this module at your own risk, you have been warned! - * - * Enabling PCI devices is left as an exercise for the reader... - * - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include "../pci.h" - -#if !defined(MODULE) - #define MY_NAME "fakephp" -#else - #define MY_NAME THIS_MODULE->name -#endif - -#define dbg(format, arg...) \ - do { \ - if (debug) \ - printk(KERN_DEBUG "%s: " format, \ - MY_NAME , ## arg); \ - } while (0) -#define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME , ## arg) -#define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME , ## arg) - -#define DRIVER_AUTHOR "Greg Kroah-Hartman " -#define DRIVER_DESC "Fake PCI Hot Plug Controller Driver" - -struct dummy_slot { - struct list_head node; - struct hotplug_slot *slot; - struct pci_dev *dev; - struct work_struct remove_work; - unsigned long removed; -}; - -static int debug; -static int dup_slots; -static LIST_HEAD(slot_list); -static struct workqueue_struct *dummyphp_wq; - -static void pci_rescan_worker(struct work_struct *work); -static DECLARE_WORK(pci_rescan_work, pci_rescan_worker); - -static int enable_slot (struct hotplug_slot *slot); -static int disable_slot (struct hotplug_slot *slot); - -static struct hotplug_slot_ops dummy_hotplug_slot_ops = { - .owner = THIS_MODULE, - .enable_slot = enable_slot, - .disable_slot = disable_slot, -}; - -static void dummy_release(struct hotplug_slot *slot) -{ - struct dummy_slot *dslot = slot->private; - - list_del(&dslot->node); - kfree(dslot->slot->info); - kfree(dslot->slot); - pci_dev_put(dslot->dev); - kfree(dslot); -} - -#define SLOT_NAME_SIZE 8 - -static int add_slot(struct pci_dev *dev) -{ - struct dummy_slot *dslot; - struct hotplug_slot *slot; - char name[SLOT_NAME_SIZE]; - int retval = -ENOMEM; - static int count = 1; - - slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL); - if (!slot) - goto error; - - slot->info = kzalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL); - if (!slot->info) - goto error_slot; - - slot->info->power_status = 1; - slot->info->max_bus_speed = PCI_SPEED_UNKNOWN; - slot->info->cur_bus_speed = PCI_SPEED_UNKNOWN; - - dslot = kzalloc(sizeof(struct dummy_slot), GFP_KERNEL); - if (!dslot) - goto error_info; - - if (dup_slots) - snprintf(name, SLOT_NAME_SIZE, "fake"); - else - snprintf(name, SLOT_NAME_SIZE, "fake%d", count++); - dbg("slot->name = %s\n", name); - slot->ops = &dummy_hotplug_slot_ops; - slot->release = &dummy_release; - slot->private = dslot; - - retval = pci_hp_register(slot, dev->bus, PCI_SLOT(dev->devfn), name); - if (retval) { - err("pci_hp_register failed with error %d\n", retval); - goto error_dslot; - } - - dbg("slot->name = %s\n", hotplug_slot_name(slot)); - dslot->slot = slot; - dslot->dev = pci_dev_get(dev); - list_add (&dslot->node, &slot_list); - return retval; - -error_dslot: - kfree(dslot); -error_info: - kfree(slot->info); -error_slot: - kfree(slot); -error: - return retval; -} - -static int __init pci_scan_buses(void) -{ - struct pci_dev *dev = NULL; - int lastslot = 0; - - while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { - if (PCI_FUNC(dev->devfn) > 0 && - lastslot == PCI_SLOT(dev->devfn)) - continue; - lastslot = PCI_SLOT(dev->devfn); - add_slot(dev); - } - - return 0; -} - -static void remove_slot(struct dummy_slot *dslot) -{ - int retval; - - dbg("removing slot %s\n", hotplug_slot_name(dslot->slot)); - retval = pci_hp_deregister(dslot->slot); - if (retval) - err("Problem unregistering a slot %s\n", - hotplug_slot_name(dslot->slot)); -} - -/* called from the single-threaded workqueue handler to remove a slot */ -static void remove_slot_worker(struct work_struct *work) -{ - struct dummy_slot *dslot = - container_of(work, struct dummy_slot, remove_work); - remove_slot(dslot); -} - -/** - * pci_rescan_slot - Rescan slot - * @temp: Device template. Should be set: bus and devfn. - * - * Tries hard not to re-enable already existing devices; - * also handles scanning of subfunctions. - */ -static int pci_rescan_slot(struct pci_dev *temp) -{ - struct pci_bus *bus = temp->bus; - struct pci_dev *dev; - int func; - u8 hdr_type; - int count = 0; - - if (!pci_read_config_byte(temp, PCI_HEADER_TYPE, &hdr_type)) { - temp->hdr_type = hdr_type & 0x7f; - if ((dev = pci_get_slot(bus, temp->devfn)) != NULL) - pci_dev_put(dev); - else { - dev = pci_scan_single_device(bus, temp->devfn); - if (dev) { - dbg("New device on %s function %x:%x\n", - bus->name, temp->devfn >> 3, - temp->devfn & 7); - count++; - } - } - /* multifunction device? */ - if (!(hdr_type & 0x80)) - return count; - - /* continue scanning for other functions */ - for (func = 1, temp->devfn++; func < 8; func++, temp->devfn++) { - if (pci_read_config_byte(temp, PCI_HEADER_TYPE, &hdr_type)) - continue; - temp->hdr_type = hdr_type & 0x7f; - - if ((dev = pci_get_slot(bus, temp->devfn)) != NULL) - pci_dev_put(dev); - else { - dev = pci_scan_single_device(bus, temp->devfn); - if (dev) { - dbg("New device on %s function %x:%x\n", - bus->name, temp->devfn >> 3, - temp->devfn & 7); - count++; - } - } - } - } - - return count; -} - - -/** - * pci_rescan_bus_local - fakephp version of rescan PCI bus - * @bus: the PCI bus to rescan - * - * Call pci_rescan_slot for each possible function of the bus. - */ -static void pci_rescan_bus_local(const struct pci_bus *bus) -{ - unsigned int devfn; - struct pci_dev *dev; - int retval; - int found = 0; - dev = alloc_pci_dev(); - if (!dev) - return; - - dev->bus = (struct pci_bus*)bus; - dev->sysdata = bus->sysdata; - for (devfn = 0; devfn < 0x100; devfn += 8) { - dev->devfn = devfn; - found += pci_rescan_slot(dev); - } - - if (found) { - pci_bus_assign_resources(bus); - list_for_each_entry(dev, &bus->devices, bus_list) { - /* Skip already-added devices */ - if (dev->is_added) - continue; - retval = pci_bus_add_device(dev); - if (retval) - dev_err(&dev->dev, - "Error adding device, continuing\n"); - else - add_slot(dev); - } - pci_bus_add_devices(bus); - } - kfree(dev); -} - -/* recursively scan all buses */ -static void pci_rescan_buses(const struct list_head *list) -{ - const struct list_head *l; - list_for_each(l,list) { - const struct pci_bus *b = pci_bus_b(l); - pci_rescan_bus_local(b); - pci_rescan_buses(&b->children); - } -} - -/* initiate rescan of all pci buses */ -static inline void pci_rescan(void) { - pci_rescan_buses(&pci_root_buses); -} - -/* called from the single-threaded workqueue handler to rescan all pci buses */ -static void pci_rescan_worker(struct work_struct *work) -{ - pci_rescan(); -} - -static int enable_slot(struct hotplug_slot *hotplug_slot) -{ - /* mis-use enable_slot for rescanning of the pci bus */ - cancel_work_sync(&pci_rescan_work); - queue_work(dummyphp_wq, &pci_rescan_work); - return 0; -} - -static int disable_slot(struct hotplug_slot *slot) -{ - struct dummy_slot *dslot; - struct pci_dev *dev; - int func; - - if (!slot) - return -ENODEV; - dslot = slot->private; - - dbg("%s - physical_slot = %s\n", __func__, hotplug_slot_name(slot)); - - for (func = 7; func >= 0; func--) { - dev = pci_get_slot(dslot->dev->bus, dslot->dev->devfn + func); - if (!dev) - continue; - - if (test_and_set_bit(0, &dslot->removed)) { - dbg("Slot already scheduled for removal\n"); - pci_dev_put(dev); - return -ENODEV; - } - - /* remove the device from the pci core */ - pci_remove_bus_device(dev); - - /* queue work item to blow away this sysfs entry and other - * parts. - */ - INIT_WORK(&dslot->remove_work, remove_slot_worker); - queue_work(dummyphp_wq, &dslot->remove_work); - - pci_dev_put(dev); - } - return 0; -} - -static void cleanup_slots (void) -{ - struct list_head *tmp; - struct list_head *next; - struct dummy_slot *dslot; - - destroy_workqueue(dummyphp_wq); - list_for_each_safe (tmp, next, &slot_list) { - dslot = list_entry (tmp, struct dummy_slot, node); - remove_slot(dslot); - } - -} - -static int __init dummyphp_init(void) -{ - info(DRIVER_DESC "\n"); - - dummyphp_wq = create_singlethread_workqueue(MY_NAME); - if (!dummyphp_wq) - return -ENOMEM; - - return pci_scan_buses(); -} - - -static void __exit dummyphp_exit(void) -{ - cleanup_slots(); -} - -module_init(dummyphp_init); -module_exit(dummyphp_exit); - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); -module_param(debug, bool, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); -module_param(dup_slots, bool, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(dup_slots, "Force duplicate slot names for debugging"); diff --git a/drivers/pci/hotplug/legacy_fakephp.c b/drivers/pci/hotplug/legacy_fakephp.c new file mode 100644 index 000000000000..2dc7828df480 --- /dev/null +++ b/drivers/pci/hotplug/legacy_fakephp.c @@ -0,0 +1,162 @@ +/* Works like the fakephp driver used to, except a little better. + * + * - It's possible to remove devices with subordinate busses. + * - New PCI devices that appear via any method, not just a fakephp triggered + * rescan, will be noticed. + * - Devices that are removed via any method, not just a fakephp triggered + * removal, will also be noticed. + * + * Uses nothing from the pci-hotplug subsystem. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "../pci.h" + +struct legacy_slot { + struct kobject kobj; + struct pci_dev *dev; + struct list_head list; +}; + +static LIST_HEAD(legacy_list); + +static ssize_t legacy_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct legacy_slot *slot = container_of(kobj, typeof(*slot), kobj); + strcpy(buf, "1\n"); + return 2; +} + +static void remove_callback(void *data) +{ + pci_remove_bus_device((struct pci_dev *)data); +} + +static ssize_t legacy_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t len) +{ + struct legacy_slot *slot = container_of(kobj, typeof(*slot), kobj); + unsigned long val; + + if (strict_strtoul(buf, 0, &val) < 0) + return -EINVAL; + + if (val) + pci_rescan_bus(slot->dev->bus); + else + sysfs_schedule_callback(&slot->dev->dev.kobj, remove_callback, + slot->dev, THIS_MODULE); + return len; +} + +static struct attribute *legacy_attrs[] = { + &(struct attribute){ .name = "power", .mode = 0644 }, + NULL, +}; + +static void legacy_release(struct kobject *kobj) +{ + struct legacy_slot *slot = container_of(kobj, typeof(*slot), kobj); + + pci_dev_put(slot->dev); + kfree(slot); +} + +static struct kobj_type legacy_ktype = { + .sysfs_ops = &(struct sysfs_ops){ + .store = legacy_store, .show = legacy_show + }, + .release = &legacy_release, + .default_attrs = legacy_attrs, +}; + +static int legacy_add_slot(struct pci_dev *pdev) +{ + struct legacy_slot *slot = kzalloc(sizeof(*slot), GFP_KERNEL); + + if (!slot) + return -ENOMEM; + + if (kobject_init_and_add(&slot->kobj, &legacy_ktype, + &pci_slots_kset->kobj, "%s", + pdev->dev.bus_id)) { + dev_warn(&pdev->dev, "Failed to created legacy fake slot\n"); + return -EINVAL; + } + slot->dev = pci_dev_get(pdev); + + list_add(&slot->list, &legacy_list); + + return 0; +} + +static int legacy_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct pci_dev *pdev = to_pci_dev(data); + + if (action == BUS_NOTIFY_ADD_DEVICE) { + legacy_add_slot(pdev); + } else if (action == BUS_NOTIFY_DEL_DEVICE) { + struct legacy_slot *slot; + + list_for_each_entry(slot, &legacy_list, list) + if (slot->dev == pdev) + goto found; + + dev_warn(&pdev->dev, "Missing legacy fake slot?"); + return -ENODEV; +found: + kobject_del(&slot->kobj); + list_del(&slot->list); + kobject_put(&slot->kobj); + } + + return 0; +} + +static struct notifier_block legacy_notifier = { + .notifier_call = legacy_notify +}; + +static int __init init_legacy(void) +{ + struct pci_dev *pdev = NULL; + + /* Add existing devices */ + while ((pdev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pdev))) + legacy_add_slot(pdev); + + /* Be alerted of any new ones */ + bus_register_notifier(&pci_bus_type, &legacy_notifier); + return 0; +} +module_init(init_legacy); + +static void __exit remove_legacy(void) +{ + struct legacy_slot *slot, *tmp; + + bus_unregister_notifier(&pci_bus_type, &legacy_notifier); + + list_for_each_entry_safe(slot, tmp, &legacy_list, list) { + list_del(&slot->list); + kobject_del(&slot->kobj); + kobject_put(&slot->kobj); + } +} +module_exit(remove_legacy); + + +MODULE_AUTHOR("Trent Piepho "); +MODULE_DESCRIPTION("Legacy version of the fakephp interface"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 8ffd25454738fb9ed76ee18cc0f180fb0b360401 Mon Sep 17 00:00:00 2001 From: Alex Chiang Date: Fri, 20 Mar 2009 14:56:51 -0600 Subject: PCI Hotplug: rename legacy_fakephp to fakephp We wanted to replace fakephp wholesale, so rename legacy_fakephp back to fakephp. Yes, this is a silly commit, but it produces a much easier patch to read and review. Signed-off-by: Alex Chiang Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/Makefile | 2 +- drivers/pci/hotplug/fakephp.c | 162 +++++++++++++++++++++++++++++++++++ drivers/pci/hotplug/legacy_fakephp.c | 162 ----------------------------------- 3 files changed, 163 insertions(+), 163 deletions(-) create mode 100644 drivers/pci/hotplug/fakephp.c delete mode 100644 drivers/pci/hotplug/legacy_fakephp.c (limited to 'drivers') diff --git a/drivers/pci/hotplug/Makefile b/drivers/pci/hotplug/Makefile index 3314fe881509..2aa117c8cd87 100644 --- a/drivers/pci/hotplug/Makefile +++ b/drivers/pci/hotplug/Makefile @@ -20,7 +20,7 @@ obj-$(CONFIG_HOTPLUG_PCI_RPA_DLPAR) += rpadlpar_io.o obj-$(CONFIG_HOTPLUG_PCI_SGI) += sgi_hotplug.o # Link this last so it doesn't claim devices that have a real hotplug driver -obj-$(CONFIG_HOTPLUG_PCI_FAKE) += legacy_fakephp.o +obj-$(CONFIG_HOTPLUG_PCI_FAKE) += fakephp.o pci_hotplug-objs := pci_hotplug_core.o diff --git a/drivers/pci/hotplug/fakephp.c b/drivers/pci/hotplug/fakephp.c new file mode 100644 index 000000000000..2dc7828df480 --- /dev/null +++ b/drivers/pci/hotplug/fakephp.c @@ -0,0 +1,162 @@ +/* Works like the fakephp driver used to, except a little better. + * + * - It's possible to remove devices with subordinate busses. + * - New PCI devices that appear via any method, not just a fakephp triggered + * rescan, will be noticed. + * - Devices that are removed via any method, not just a fakephp triggered + * removal, will also be noticed. + * + * Uses nothing from the pci-hotplug subsystem. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "../pci.h" + +struct legacy_slot { + struct kobject kobj; + struct pci_dev *dev; + struct list_head list; +}; + +static LIST_HEAD(legacy_list); + +static ssize_t legacy_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct legacy_slot *slot = container_of(kobj, typeof(*slot), kobj); + strcpy(buf, "1\n"); + return 2; +} + +static void remove_callback(void *data) +{ + pci_remove_bus_device((struct pci_dev *)data); +} + +static ssize_t legacy_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t len) +{ + struct legacy_slot *slot = container_of(kobj, typeof(*slot), kobj); + unsigned long val; + + if (strict_strtoul(buf, 0, &val) < 0) + return -EINVAL; + + if (val) + pci_rescan_bus(slot->dev->bus); + else + sysfs_schedule_callback(&slot->dev->dev.kobj, remove_callback, + slot->dev, THIS_MODULE); + return len; +} + +static struct attribute *legacy_attrs[] = { + &(struct attribute){ .name = "power", .mode = 0644 }, + NULL, +}; + +static void legacy_release(struct kobject *kobj) +{ + struct legacy_slot *slot = container_of(kobj, typeof(*slot), kobj); + + pci_dev_put(slot->dev); + kfree(slot); +} + +static struct kobj_type legacy_ktype = { + .sysfs_ops = &(struct sysfs_ops){ + .store = legacy_store, .show = legacy_show + }, + .release = &legacy_release, + .default_attrs = legacy_attrs, +}; + +static int legacy_add_slot(struct pci_dev *pdev) +{ + struct legacy_slot *slot = kzalloc(sizeof(*slot), GFP_KERNEL); + + if (!slot) + return -ENOMEM; + + if (kobject_init_and_add(&slot->kobj, &legacy_ktype, + &pci_slots_kset->kobj, "%s", + pdev->dev.bus_id)) { + dev_warn(&pdev->dev, "Failed to created legacy fake slot\n"); + return -EINVAL; + } + slot->dev = pci_dev_get(pdev); + + list_add(&slot->list, &legacy_list); + + return 0; +} + +static int legacy_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct pci_dev *pdev = to_pci_dev(data); + + if (action == BUS_NOTIFY_ADD_DEVICE) { + legacy_add_slot(pdev); + } else if (action == BUS_NOTIFY_DEL_DEVICE) { + struct legacy_slot *slot; + + list_for_each_entry(slot, &legacy_list, list) + if (slot->dev == pdev) + goto found; + + dev_warn(&pdev->dev, "Missing legacy fake slot?"); + return -ENODEV; +found: + kobject_del(&slot->kobj); + list_del(&slot->list); + kobject_put(&slot->kobj); + } + + return 0; +} + +static struct notifier_block legacy_notifier = { + .notifier_call = legacy_notify +}; + +static int __init init_legacy(void) +{ + struct pci_dev *pdev = NULL; + + /* Add existing devices */ + while ((pdev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pdev))) + legacy_add_slot(pdev); + + /* Be alerted of any new ones */ + bus_register_notifier(&pci_bus_type, &legacy_notifier); + return 0; +} +module_init(init_legacy); + +static void __exit remove_legacy(void) +{ + struct legacy_slot *slot, *tmp; + + bus_unregister_notifier(&pci_bus_type, &legacy_notifier); + + list_for_each_entry_safe(slot, tmp, &legacy_list, list) { + list_del(&slot->list); + kobject_del(&slot->kobj); + kobject_put(&slot->kobj); + } +} +module_exit(remove_legacy); + + +MODULE_AUTHOR("Trent Piepho "); +MODULE_DESCRIPTION("Legacy version of the fakephp interface"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pci/hotplug/legacy_fakephp.c b/drivers/pci/hotplug/legacy_fakephp.c deleted file mode 100644 index 2dc7828df480..000000000000 --- a/drivers/pci/hotplug/legacy_fakephp.c +++ /dev/null @@ -1,162 +0,0 @@ -/* Works like the fakephp driver used to, except a little better. - * - * - It's possible to remove devices with subordinate busses. - * - New PCI devices that appear via any method, not just a fakephp triggered - * rescan, will be noticed. - * - Devices that are removed via any method, not just a fakephp triggered - * removal, will also be noticed. - * - * Uses nothing from the pci-hotplug subsystem. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "../pci.h" - -struct legacy_slot { - struct kobject kobj; - struct pci_dev *dev; - struct list_head list; -}; - -static LIST_HEAD(legacy_list); - -static ssize_t legacy_show(struct kobject *kobj, struct attribute *attr, - char *buf) -{ - struct legacy_slot *slot = container_of(kobj, typeof(*slot), kobj); - strcpy(buf, "1\n"); - return 2; -} - -static void remove_callback(void *data) -{ - pci_remove_bus_device((struct pci_dev *)data); -} - -static ssize_t legacy_store(struct kobject *kobj, struct attribute *attr, - const char *buf, size_t len) -{ - struct legacy_slot *slot = container_of(kobj, typeof(*slot), kobj); - unsigned long val; - - if (strict_strtoul(buf, 0, &val) < 0) - return -EINVAL; - - if (val) - pci_rescan_bus(slot->dev->bus); - else - sysfs_schedule_callback(&slot->dev->dev.kobj, remove_callback, - slot->dev, THIS_MODULE); - return len; -} - -static struct attribute *legacy_attrs[] = { - &(struct attribute){ .name = "power", .mode = 0644 }, - NULL, -}; - -static void legacy_release(struct kobject *kobj) -{ - struct legacy_slot *slot = container_of(kobj, typeof(*slot), kobj); - - pci_dev_put(slot->dev); - kfree(slot); -} - -static struct kobj_type legacy_ktype = { - .sysfs_ops = &(struct sysfs_ops){ - .store = legacy_store, .show = legacy_show - }, - .release = &legacy_release, - .default_attrs = legacy_attrs, -}; - -static int legacy_add_slot(struct pci_dev *pdev) -{ - struct legacy_slot *slot = kzalloc(sizeof(*slot), GFP_KERNEL); - - if (!slot) - return -ENOMEM; - - if (kobject_init_and_add(&slot->kobj, &legacy_ktype, - &pci_slots_kset->kobj, "%s", - pdev->dev.bus_id)) { - dev_warn(&pdev->dev, "Failed to created legacy fake slot\n"); - return -EINVAL; - } - slot->dev = pci_dev_get(pdev); - - list_add(&slot->list, &legacy_list); - - return 0; -} - -static int legacy_notify(struct notifier_block *nb, - unsigned long action, void *data) -{ - struct pci_dev *pdev = to_pci_dev(data); - - if (action == BUS_NOTIFY_ADD_DEVICE) { - legacy_add_slot(pdev); - } else if (action == BUS_NOTIFY_DEL_DEVICE) { - struct legacy_slot *slot; - - list_for_each_entry(slot, &legacy_list, list) - if (slot->dev == pdev) - goto found; - - dev_warn(&pdev->dev, "Missing legacy fake slot?"); - return -ENODEV; -found: - kobject_del(&slot->kobj); - list_del(&slot->list); - kobject_put(&slot->kobj); - } - - return 0; -} - -static struct notifier_block legacy_notifier = { - .notifier_call = legacy_notify -}; - -static int __init init_legacy(void) -{ - struct pci_dev *pdev = NULL; - - /* Add existing devices */ - while ((pdev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pdev))) - legacy_add_slot(pdev); - - /* Be alerted of any new ones */ - bus_register_notifier(&pci_bus_type, &legacy_notifier); - return 0; -} -module_init(init_legacy); - -static void __exit remove_legacy(void) -{ - struct legacy_slot *slot, *tmp; - - bus_unregister_notifier(&pci_bus_type, &legacy_notifier); - - list_for_each_entry_safe(slot, tmp, &legacy_list, list) { - list_del(&slot->list); - kobject_del(&slot->kobj); - kobject_put(&slot->kobj); - } -} -module_exit(remove_legacy); - - -MODULE_AUTHOR("Trent Piepho "); -MODULE_DESCRIPTION("Legacy version of the fakephp interface"); -MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 853346e4354c948b50a6fb0002f8af2cf5fbf2ae Mon Sep 17 00:00:00 2001 From: Yu Zhao Date: Sat, 21 Mar 2009 22:05:11 +0800 Subject: PCI: fix conflict between SR-IOV and config space sizing New pci_cfg_space_size() needs invalid pdev->class, put it in the right place in the pci_setup_device(). Signed-off-by: Yu Zhao Signed-off-by: Jesse Barnes --- drivers/pci/probe.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 56c71e585f3d..e2f3dd098cfa 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -713,7 +713,6 @@ int pci_setup_device(struct pci_dev *dev) dev->dev.bus = &pci_bus_type; dev->hdr_type = hdr_type & 0x7f; dev->multifunction = !!(hdr_type & 0x80); - dev->cfg_size = pci_cfg_space_size(dev); dev->error_state = pci_channel_io_normal; set_pcie_port_type(dev); @@ -738,6 +737,9 @@ int pci_setup_device(struct pci_dev *dev) dev_dbg(&dev->dev, "found [%04x:%04x] class %06x header type %02x\n", dev->vendor, dev->device, class, dev->hdr_type); + /* need to have dev->class ready */ + dev->cfg_size = pci_cfg_space_size(dev); + /* "Unknown power state" */ dev->current_state = PCI_UNKNOWN; @@ -959,9 +961,6 @@ static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn) return NULL; } - /* need to have dev->class ready */ - dev->cfg_size = pci_cfg_space_size(dev); - return dev; } -- cgit v1.2.3 From 7ae0567fd3f4f51d55c4c638ecc6836347992de2 Mon Sep 17 00:00:00 2001 From: Kenji Kaneshige Date: Thu, 26 Mar 2009 16:49:52 +0900 Subject: PCI: fix kernel oops on bridge removal Fix the following kernel oops problem that happens when removing PCI bridge with pciehp loaded. It should also occur with other hotplug driver that is implemented as a bridge's driver. [ 459.997257] pciehp 0000:2f:04.0:pcie24: unloading service driver pciehp [ 459.997495] general protection fault: 0000 [#1] SMP [ 459.997737] last sysfs file: /sys/devices/pci0000:00/0000:00:04.0/0000:2e:00.0/0000:2f:04.0/remove [ 459.997964] CPU 4 [ 459.998129] Modules linked in: pciehp ipv6 autofs4 hidp rfcomm l2cap bluetooth sunrpc cpufreq_ondemand acpi_cpufreq dm_mirror dm_region_hash dm_log dm_multipath scsi_dh dm_mod sbs sbshc battery ac parport_pc lp parport mptspi mptscsih mptbase scsi_transport_spi e1000e sg sr_mod cdrom button serio_raw i2c_i801 i2c_core shpchp pcspkr ata_piix libata megaraid_sas sd_mod scsi_mod crc_t10dif ext3 jbd uhci_hcd ohci_hcd ehci_hcd [last unloaded: microcode] [ 459.998129] Pid: 56, comm: events/4 Not tainted 2.6.29-rc8-kk #1 PRIMERGY [ 459.998129] RIP: 0010:[] [] pci_slot_release+0x37/0x100 [ 459.998129] RSP: 0018:ffff88083b3bf9e0 EFLAGS: 00010246 [ 459.998129] RAX: ffff88083adc5158 RBX: ffff880836c1bc80 RCX: 6b6b6b6b6b6b6b6b [ 459.998129] RDX: 0000000000000000 RSI: ffffffff803a77f0 RDI: ffff880836c1bc48 [ 459.998129] RBP: ffff88083b3bfa00 R08: 0000000000000002 R09: 0000000000000000 [ 459.998129] R10: 0000000000000000 R11: 0000000000000000 R12: ffff880836c1bc48 [ 459.998129] R13: ffff880836c1bc20 R14: ffff880836c1bc48 R15: ffff880836d1ec38 [ 459.998129] FS: 0000000000000000(0000) GS:ffff88083ccc3770(0000) knlGS:0000000000000000 [ 459.998129] CS: 0010 DS: 0018 ES: 0018 CR0: 000000008005003b [ 459.998129] CR2: 00007f1562f1d558 CR3: 0000000838090000 CR4: 00000000000006e0 [ 459.998129] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 459.998129] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 [ 459.998129] Process events/4 (pid: 56, threadinfo ffff88083b3be000, task ffff88083b3b3e40) [ 459.998129] Stack: [ 459.998129] ffff880836c1bc80 ffff880836c1bc48 ffffffff80793320 ffff88083b0d0960 [ 459.998129] ffff88083b3bfa30 ffffffff803a788a ffff880836c1bc80 ffffffff803a77f0 [ 459.998129] ffff880836c1bc20 ffff880836d1ec38 ffff88083b3bfa50 ffffffff803a8ce7 [ 459.998129] Call Trace: [ 459.998129] [] kobject_release+0x9a/0x290 [ 459.998129] [] ? kobject_release+0x0/0x290 [ 459.998129] [] kref_put+0x37/0x80 [ 459.998129] [] kobject_put+0x27/0x60 [ 459.998129] [] ? pci_destroy_slot+0x3c/0xc0 [ 459.998129] [] pci_destroy_slot+0x45/0xc0 [ 459.998129] [] pci_hp_deregister+0x13d/0x210 [ 459.998129] [] cleanup_slots+0x2d/0x80 [pciehp] [ 459.998129] [] pciehp_remove+0x15/0x30 [pciehp] [ 459.998129] [] pcie_port_remove_service+0x69/0x90 [ 459.998129] [] __device_release_driver+0x59/0x90 [ 459.998129] [] device_release_driver+0x2b/0x40 [ 459.998129] [] bus_remove_device+0xa6/0x120 [ 459.998129] [] device_del+0x12b/0x190 [ 459.998129] [] ? remove_iter+0x0/0x40 [ 459.998129] [] device_unregister+0x26/0x70 [ 459.998129] [] remove_iter+0x2f/0x40 [ 459.998129] [] device_for_each_child+0x33/0x60 [ 459.998129] [] ? sysfs_schedule_callback_work+0x0/0x50 [ 459.998129] [] pcie_port_device_remove+0x30/0x80 [ 459.998129] [] pcie_portdrv_remove+0x11/0x20 [ 459.998129] [] pci_device_remove+0x32/0x70 [ 459.998129] [] __device_release_driver+0x59/0x90 [ 459.998129] [] device_release_driver+0x2b/0x40 [ 459.998129] [] bus_remove_device+0xa6/0x120 [ 459.998129] [] device_del+0x12b/0x190 [ 459.998129] [] device_unregister+0x26/0x70 [ 459.998129] [] pci_stop_dev+0x49/0x60 [ 459.998129] [] pci_remove_bus_device+0x40/0xc0 [ 459.998129] [] remove_callback+0x29/0x40 [ 459.998129] [] sysfs_schedule_callback_work+0x1f/0x50 [ 459.998129] [] run_workqueue+0x15a/0x230 [ 459.998129] [] ? run_workqueue+0x108/0x230 [ 459.998129] [] worker_thread+0x9f/0x100 [ 459.998129] [] ? autoremove_wake_function+0x0/0x40 [ 459.998129] [] ? worker_thread+0x0/0x100 [ 459.998129] [] kthread+0x4d/0x80 [ 459.998129] [] child_rip+0xa/0x20 [ 459.998129] [] ? restore_args+0x0/0x30 [ 459.998129] [] ? kthread+0x0/0x80 [ 459.998129] [] ? child_rip+0x0/0x20 [ 459.998129] Code: 56 49 89 fe 41 55 4c 8d 6f d8 41 54 53 74 09 f6 05 b8 05 c7 00 08 75 72 49 8b 45 00 48 8b 48 28 eb 05 66 90 48 89 f1 49 8b 45 00 <48> 8b 31 48 83 c0 28 0f 18 0e 48 39 c1 74 1c 8b 41 38 41 0f b6 [ 459.998129] RIP [] pci_slot_release+0x37/0x100 [ 459.998129] RSP [ 460.018595] ---[ end trace 5a08d2095374aedc ]--- The pci_remove_bus_device() removes all buses and devices under the bridge, and then removes the bridge. So the remove() callback of the hotplug drivers implemented as a bridge's driver is executed after the struct pci_bus of the bridge's secondary bus is removed. The remove() callback of those driver unregisters the slot using pci_destroy_slot(), and slot's release callback refers to the the struct pci_bus that was already freed. This is the cause of the kernel oops. This patch solves the problem by stopping bus drivers before removing the bridge and its child bus and devices. Acked-by: Alex Chiang Signed-off-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/remove.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index caf8e1eae45e..86503c14ce7e 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -95,6 +95,7 @@ EXPORT_SYMBOL(pci_remove_bus); */ void pci_remove_bus_device(struct pci_dev *dev) { + pci_stop_bus_device(dev); if (dev->subordinate) { struct pci_bus *b = dev->subordinate; -- cgit v1.2.3 From 7bb2cb3e90dc49be1cd14956c155451499c857a7 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Thu, 26 Mar 2009 18:34:33 +1100 Subject: PCI: update fakephp for bus_id removal Get rid of a new use of bus_id that snuck in. Signed-off-by: Stephen Rothwell Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/fakephp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/fakephp.c b/drivers/pci/hotplug/fakephp.c index 2dc7828df480..6151389fd903 100644 --- a/drivers/pci/hotplug/fakephp.c +++ b/drivers/pci/hotplug/fakephp.c @@ -18,6 +18,7 @@ #include #include #include +#include #include "../pci.h" struct legacy_slot { @@ -88,7 +89,7 @@ static int legacy_add_slot(struct pci_dev *pdev) if (kobject_init_and_add(&slot->kobj, &legacy_ktype, &pci_slots_kset->kobj, "%s", - pdev->dev.bus_id)) { + dev_name(&pdev->dev))) { dev_warn(&pdev->dev, "Failed to created legacy fake slot\n"); return -EINVAL; } -- cgit v1.2.3 From 898585172fa729513d8636257b44bd1cfd279096 Mon Sep 17 00:00:00 2001 From: Yu Zhao Date: Mon, 16 Feb 2009 02:55:47 +0800 Subject: PCI: save and restore PCIe 2.0 registers PCIe 2.0 defines several new registers (Device Control 2, Link Control 2, and Slot Control 2). Save and retore them in pci_save_pcie_state() and pci_restore_pcie_state(). Signed-off-by: Yu Zhao Signed-off-by: Jesse Barnes --- drivers/pci/pci.c | 11 ++++++++++- include/linux/pci_regs.h | 2 ++ 2 files changed, 12 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 676bbcbc272b..59569b8cf1d5 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -647,6 +647,8 @@ pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state) EXPORT_SYMBOL(pci_choose_state); +#define PCI_EXP_SAVE_REGS 7 + static int pci_save_pcie_state(struct pci_dev *dev) { int pos, i = 0; @@ -668,6 +670,9 @@ static int pci_save_pcie_state(struct pci_dev *dev) pci_read_config_word(dev, pos + PCI_EXP_LNKCTL, &cap[i++]); pci_read_config_word(dev, pos + PCI_EXP_SLTCTL, &cap[i++]); pci_read_config_word(dev, pos + PCI_EXP_RTCTL, &cap[i++]); + pci_read_config_word(dev, pos + PCI_EXP_DEVCTL2, &cap[i++]); + pci_read_config_word(dev, pos + PCI_EXP_LNKCTL2, &cap[i++]); + pci_read_config_word(dev, pos + PCI_EXP_SLTCTL2, &cap[i++]); return 0; } @@ -688,6 +693,9 @@ static void pci_restore_pcie_state(struct pci_dev *dev) pci_write_config_word(dev, pos + PCI_EXP_LNKCTL, cap[i++]); pci_write_config_word(dev, pos + PCI_EXP_SLTCTL, cap[i++]); pci_write_config_word(dev, pos + PCI_EXP_RTCTL, cap[i++]); + pci_write_config_word(dev, pos + PCI_EXP_DEVCTL2, cap[i++]); + pci_write_config_word(dev, pos + PCI_EXP_LNKCTL2, cap[i++]); + pci_write_config_word(dev, pos + PCI_EXP_SLTCTL2, cap[i++]); } @@ -1372,7 +1380,8 @@ void pci_allocate_cap_save_buffers(struct pci_dev *dev) { int error; - error = pci_add_cap_save_buffer(dev, PCI_CAP_ID_EXP, 4 * sizeof(u16)); + error = pci_add_cap_save_buffer(dev, PCI_CAP_ID_EXP, + PCI_EXP_SAVE_REGS * sizeof(u16)); if (error) dev_err(&dev->dev, "unable to preallocate PCI Express save buffer\n"); diff --git a/include/linux/pci_regs.h b/include/linux/pci_regs.h index d4e663877f45..e4d08c1b2e0b 100644 --- a/include/linux/pci_regs.h +++ b/include/linux/pci_regs.h @@ -488,6 +488,8 @@ #define PCI_EXP_DEVCAP2_ARI 0x20 /* Alternative Routing-ID */ #define PCI_EXP_DEVCTL2 40 /* Device Control 2 */ #define PCI_EXP_DEVCTL2_ARI 0x20 /* Alternative Routing-ID */ +#define PCI_EXP_LNKCTL2 48 /* Link Control 2 */ +#define PCI_EXP_SLTCTL2 56 /* Slot Control 2 */ /* Extended Capabilities (PCI-X 2.0 and Express) */ #define PCI_EXT_CAP_ID(header) (header & 0x0000ffff) -- cgit v1.2.3 From de7453065d5d5243686467998f1fcc58d20e0a7c Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 20 Mar 2009 19:29:41 -0700 Subject: PCI: don't enable too much HT MSI mapping Impact: fix bug Prakash reported that his c51-mcp51 system ondie sound card doesn't work MSI but if he hack out the HT-MSI on mcp51, the MSI will work well with sound card. This patch reworks nv_msi_ht_cap_quirk() and will only avoid enabling ht_msi on devices following that root device. Reported-by: Prakash Punnoor Signed-off-by: Yinghai Lu Signed-off-by: Jesse Barnes --- drivers/pci/quirks.c | 118 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 82 insertions(+), 36 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 7ddcfc65e790..faf02dd00015 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -2147,6 +2147,65 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_15, nvenet_msi_disable); +static int __devinit ht_check_msi_mapping(struct pci_dev *dev) +{ + int pos, ttl = 48; + int found = 0; + + /* check if there is HT MSI cap or enabled on this device */ + pos = pci_find_ht_capability(dev, HT_CAPTYPE_MSI_MAPPING); + while (pos && ttl--) { + u8 flags; + + if (found < 1) + found = 1; + if (pci_read_config_byte(dev, pos + HT_MSI_FLAGS, + &flags) == 0) { + if (flags & HT_MSI_FLAGS_ENABLE) { + if (found < 2) { + found = 2; + break; + } + } + } + pos = pci_find_next_ht_capability(dev, pos, + HT_CAPTYPE_MSI_MAPPING); + } + + return found; +} + +static int __devinit host_bridge_with_leaf(struct pci_dev *host_bridge) +{ + struct pci_dev *dev; + int pos; + int i, dev_no; + int found = 0; + + dev_no = host_bridge->devfn >> 3; + for (i = dev_no + 1; i < 0x20; i++) { + dev = pci_get_slot(host_bridge->bus, PCI_DEVFN(i, 0)); + if (!dev) + continue; + + /* found next host bridge ?*/ + pos = pci_find_ht_capability(dev, HT_CAPTYPE_SLAVE); + if (pos != 0) { + pci_dev_put(dev); + break; + } + + if (ht_check_msi_mapping(dev)) { + found = 1; + pci_dev_put(dev); + break; + } + pci_dev_put(dev); + } + + return found; +} + static void __devinit nv_ht_enable_msi_mapping(struct pci_dev *dev) { struct pci_dev *host_bridge; @@ -2171,6 +2230,10 @@ static void __devinit nv_ht_enable_msi_mapping(struct pci_dev *dev) if (!found) return; + /* don't enable host_bridge with leaf directly here */ + if (host_bridge == dev && host_bridge_with_leaf(host_bridge)) + goto out; + /* root did that ! */ if (msi_ht_cap_enabled(host_bridge)) goto out; @@ -2201,44 +2264,12 @@ static void __devinit ht_disable_msi_mapping(struct pci_dev *dev) } } -static int __devinit ht_check_msi_mapping(struct pci_dev *dev) -{ - int pos, ttl = 48; - int found = 0; - - /* check if there is HT MSI cap or enabled on this device */ - pos = pci_find_ht_capability(dev, HT_CAPTYPE_MSI_MAPPING); - while (pos && ttl--) { - u8 flags; - - if (found < 1) - found = 1; - if (pci_read_config_byte(dev, pos + HT_MSI_FLAGS, - &flags) == 0) { - if (flags & HT_MSI_FLAGS_ENABLE) { - if (found < 2) { - found = 2; - break; - } - } - } - pos = pci_find_next_ht_capability(dev, pos, - HT_CAPTYPE_MSI_MAPPING); - } - - return found; -} - -static void __devinit nv_msi_ht_cap_quirk(struct pci_dev *dev) +static void __devinit __nv_msi_ht_cap_quirk(struct pci_dev *dev, int all) { struct pci_dev *host_bridge; int pos; int found; - /* Enabling HT MSI mapping on this device breaks MCP51 */ - if (dev->device == 0x270) - return; - /* check if there is HT MSI cap or enabled on this device */ found = ht_check_msi_mapping(dev); @@ -2262,7 +2293,10 @@ static void __devinit nv_msi_ht_cap_quirk(struct pci_dev *dev) /* Host bridge is to HT */ if (found == 1) { /* it is not enabled, try to enable it */ - nv_ht_enable_msi_mapping(dev); + if (all) + ht_enable_msi_mapping(dev); + else + nv_ht_enable_msi_mapping(dev); } return; } @@ -2274,8 +2308,20 @@ static void __devinit nv_msi_ht_cap_quirk(struct pci_dev *dev) /* Host bridge is not to HT, disable HT MSI mapping on this device */ ht_disable_msi_mapping(dev); } -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, nv_msi_ht_cap_quirk); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AL, PCI_ANY_ID, nv_msi_ht_cap_quirk); + +static void __devinit nv_msi_ht_cap_quirk_all(struct pci_dev *dev) +{ + return __nv_msi_ht_cap_quirk(dev, 1); +} + +static void __devinit nv_msi_ht_cap_quirk_leaf(struct pci_dev *dev) +{ + return __nv_msi_ht_cap_quirk(dev, 0); +} + +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, nv_msi_ht_cap_quirk_leaf); + +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AL, PCI_ANY_ID, nv_msi_ht_cap_quirk_all); static void __devinit quirk_msi_intx_disable_bug(struct pci_dev *dev) { -- cgit v1.2.3 From 79f1bc06dbb05f222756d6df4a9ff95588c9cc06 Mon Sep 17 00:00:00 2001 From: Alexander Beregalov Date: Sat, 28 Mar 2009 23:37:27 -0700 Subject: ni5010: convert to net_device_ops Signed-off-by: Alexander Beregalov Signed-off-by: David S. Miller --- drivers/net/ni5010.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ni5010.c b/drivers/net/ni5010.c index 539e18ab485c..2a8da476ab3d 100644 --- a/drivers/net/ni5010.c +++ b/drivers/net/ni5010.c @@ -189,6 +189,17 @@ static void __init trigger_irq(int ioaddr) outb(MM_EN_XMT|MM_MUX, IE_MMODE); /* Start transmission */ } +static const struct net_device_ops ni5010_netdev_ops = { + .ndo_open = ni5010_open, + .ndo_stop = ni5010_close, + .ndo_start_xmit = ni5010_send_packet, + .ndo_set_multicast_list = ni5010_set_multicast_list, + .ndo_tx_timeout = ni5010_timeout, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_mac_address = eth_mac_addr, + .ndo_change_mtu = eth_change_mtu, +}; + /* * This is the real probe routine. Linux has a history of friendly device * probes on the ISA bus. A good device probes avoids doing writes, and @@ -328,13 +339,8 @@ static int __init ni5010_probe1(struct net_device *dev, int ioaddr) outb(0, IE_RBUF); /* set buffer byte 0 to 0 again */ } printk("-> bufsize rcv/xmt=%d/%d\n", bufsize_rcv, NI5010_BUFSIZE); - memset(netdev_priv(dev), 0, sizeof(struct ni5010_local)); - dev->open = ni5010_open; - dev->stop = ni5010_close; - dev->hard_start_xmit = ni5010_send_packet; - dev->set_multicast_list = ni5010_set_multicast_list; - dev->tx_timeout = ni5010_timeout; + dev->netdev_ops = &ni5010_netdev_ops; dev->watchdog_timeo = HZ/20; dev->flags &= ~IFF_MULTICAST; /* Multicast doesn't work */ -- cgit v1.2.3 From 3e8af307bfe3b6318a1aaaf8ce18d0af7ddf2ea2 Mon Sep 17 00:00:00 2001 From: Alexander Beregalov Date: Sat, 28 Mar 2009 23:40:05 -0700 Subject: dmascc: fix incomplete conversion to network_device_ops drivers/net/hamradio/dmascc.c:587: error: 'struct net_device' has no member named 'set_mac_address' Signed-off-by: Alexander Beregalov Signed-off-by: David S. Miller --- drivers/net/hamradio/dmascc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/hamradio/dmascc.c b/drivers/net/hamradio/dmascc.c index 881bf818bb48..7459b3ac77a9 100644 --- a/drivers/net/hamradio/dmascc.c +++ b/drivers/net/hamradio/dmascc.c @@ -445,6 +445,7 @@ static const struct net_device_ops scc_netdev_ops = { .ndo_stop = scc_close, .ndo_start_xmit = scc_send_packet, .ndo_do_ioctl = scc_ioctl, + .ndo_set_mac_address = scc_set_mac_address, }; static int __init setup_adapter(int card_base, int type, int n) @@ -584,7 +585,6 @@ static int __init setup_adapter(int card_base, int type, int n) dev->irq = irq; dev->netdev_ops = &scc_netdev_ops; dev->header_ops = &ax25_header_ops; - dev->set_mac_address = scc_set_mac_address; } if (register_netdev(info->dev[0])) { printk(KERN_ERR "dmascc: could not register %s\n", -- cgit v1.2.3 From e7557af56a576762a655f1aaaded253ad14c5958 Mon Sep 17 00:00:00 2001 From: Harvey Harrison Date: Sat, 28 Mar 2009 15:38:31 +0000 Subject: netpoll: store local and remote ip in net-endian Allows for the removal of byteswapping in some places and the removal of HIPQUAD (replaced by %pI4). Signed-off-by: Harvey Harrison Signed-off-by: David S. Miller --- drivers/net/netconsole.c | 10 ++++------ include/linux/netpoll.h | 2 +- net/core/netpoll.c | 31 +++++++++++++++---------------- 3 files changed, 20 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c index d304d38cd5d1..eceadf787a67 100644 --- a/drivers/net/netconsole.c +++ b/drivers/net/netconsole.c @@ -294,14 +294,12 @@ static ssize_t show_remote_port(struct netconsole_target *nt, char *buf) static ssize_t show_local_ip(struct netconsole_target *nt, char *buf) { - return snprintf(buf, PAGE_SIZE, "%d.%d.%d.%d\n", - HIPQUAD(nt->np.local_ip)); + return snprintf(buf, PAGE_SIZE, "%pI4\n", &nt->np.local_ip); } static ssize_t show_remote_ip(struct netconsole_target *nt, char *buf) { - return snprintf(buf, PAGE_SIZE, "%d.%d.%d.%d\n", - HIPQUAD(nt->np.remote_ip)); + return snprintf(buf, PAGE_SIZE, "%pI4\n", &nt->np.remote_ip); } static ssize_t show_local_mac(struct netconsole_target *nt, char *buf) @@ -438,7 +436,7 @@ static ssize_t store_local_ip(struct netconsole_target *nt, return -EINVAL; } - nt->np.local_ip = ntohl(in_aton(buf)); + nt->np.local_ip = in_aton(buf); return strnlen(buf, count); } @@ -454,7 +452,7 @@ static ssize_t store_remote_ip(struct netconsole_target *nt, return -EINVAL; } - nt->np.remote_ip = ntohl(in_aton(buf)); + nt->np.remote_ip = in_aton(buf); return strnlen(buf, count); } diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h index de99025f2c5d..2524267210d3 100644 --- a/include/linux/netpoll.h +++ b/include/linux/netpoll.h @@ -18,7 +18,7 @@ struct netpoll { const char *name; void (*rx_hook)(struct netpoll *, int, char *, int); - u32 local_ip, remote_ip; + __be32 local_ip, remote_ip; u16 local_port, remote_port; u8 remote_mac[ETH_ALEN]; }; diff --git a/net/core/netpoll.c b/net/core/netpoll.c index 755414cd49d1..b5873bdff612 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -345,8 +345,8 @@ void netpoll_send_udp(struct netpoll *np, const char *msg, int len) udph->dest = htons(np->remote_port); udph->len = htons(udp_len); udph->check = 0; - udph->check = csum_tcpudp_magic(htonl(np->local_ip), - htonl(np->remote_ip), + udph->check = csum_tcpudp_magic(np->local_ip, + np->remote_ip, udp_len, IPPROTO_UDP, csum_partial(udph, udp_len, 0)); if (udph->check == 0) @@ -365,8 +365,8 @@ void netpoll_send_udp(struct netpoll *np, const char *msg, int len) iph->ttl = 64; iph->protocol = IPPROTO_UDP; iph->check = 0; - put_unaligned(htonl(np->local_ip), &(iph->saddr)); - put_unaligned(htonl(np->remote_ip), &(iph->daddr)); + put_unaligned(np->local_ip, &(iph->saddr)); + put_unaligned(np->remote_ip, &(iph->daddr)); iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); eth = (struct ethhdr *) skb_push(skb, ETH_HLEN); @@ -424,7 +424,7 @@ static void arp_reply(struct sk_buff *skb) memcpy(&tip, arp_ptr, 4); /* Should we ignore arp? */ - if (tip != htonl(np->local_ip) || + if (tip != np->local_ip || ipv4_is_loopback(tip) || ipv4_is_multicast(tip)) return; @@ -533,9 +533,9 @@ int __netpoll_rx(struct sk_buff *skb) goto out; if (checksum_udp(skb, uh, ulen, iph->saddr, iph->daddr)) goto out; - if (np->local_ip && np->local_ip != ntohl(iph->daddr)) + if (np->local_ip && np->local_ip != iph->daddr) goto out; - if (np->remote_ip && np->remote_ip != ntohl(iph->saddr)) + if (np->remote_ip && np->remote_ip != iph->saddr) goto out; if (np->local_port && np->local_port != ntohs(uh->dest)) goto out; @@ -560,14 +560,14 @@ void netpoll_print_options(struct netpoll *np) { printk(KERN_INFO "%s: local port %d\n", np->name, np->local_port); - printk(KERN_INFO "%s: local IP %d.%d.%d.%d\n", - np->name, HIPQUAD(np->local_ip)); + printk(KERN_INFO "%s: local IP %pI4\n", + np->name, &np->local_ip); printk(KERN_INFO "%s: interface %s\n", np->name, np->dev_name); printk(KERN_INFO "%s: remote port %d\n", np->name, np->remote_port); - printk(KERN_INFO "%s: remote IP %d.%d.%d.%d\n", - np->name, HIPQUAD(np->remote_ip)); + printk(KERN_INFO "%s: remote IP %pI4\n", + np->name, &np->remote_ip); printk(KERN_INFO "%s: remote ethernet address %pM\n", np->name, np->remote_mac); } @@ -589,7 +589,7 @@ int netpoll_parse_options(struct netpoll *np, char *opt) if ((delim = strchr(cur, '/')) == NULL) goto parse_failed; *delim = 0; - np->local_ip = ntohl(in_aton(cur)); + np->local_ip = in_aton(cur); cur = delim; } cur++; @@ -618,7 +618,7 @@ int netpoll_parse_options(struct netpoll *np, char *opt) if ((delim = strchr(cur, '/')) == NULL) goto parse_failed; *delim = 0; - np->remote_ip = ntohl(in_aton(cur)); + np->remote_ip = in_aton(cur); cur = delim + 1; if (*cur != 0) { @@ -759,10 +759,9 @@ int netpoll_setup(struct netpoll *np) goto release; } - np->local_ip = ntohl(in_dev->ifa_list->ifa_local); + np->local_ip = in_dev->ifa_list->ifa_local; rcu_read_unlock(); - printk(KERN_INFO "%s: local IP %d.%d.%d.%d\n", - np->name, HIPQUAD(np->local_ip)); + printk(KERN_INFO "%s: local IP %pI4\n", np->name, &np->local_ip); } if (np->rx_hook) { -- cgit v1.2.3 From ee76db5e9e9896312f001790855a798472440328 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sun, 29 Mar 2009 01:19:37 -0700 Subject: gianfar: Fix use-after-of_node_put() in gfar_of_init(). We can't put 'mdio' until after we've used it in the fsl_pq_mdio_bus_name() call. Signed-off-by: David S. Miller --- drivers/net/gianfar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c index 6a38800be3f1..65f55877be95 100644 --- a/drivers/net/gianfar.c +++ b/drivers/net/gianfar.c @@ -289,9 +289,9 @@ static int gfar_of_init(struct net_device *dev) id = of_get_property(phy, "reg", NULL); of_node_put(phy); - of_node_put(mdio); fsl_pq_mdio_bus_name(bus_name, mdio); + of_node_put(mdio); snprintf(priv->phy_bus_id, sizeof(priv->phy_bus_id), "%s:%02x", bus_name, *id); } -- cgit v1.2.3 From 129dd9677b30a07bb832247dfe8d6089f1ac61a0 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sun, 29 Mar 2009 01:20:18 -0700 Subject: ucc_geth: Fix use-after-of_node_put() in ucc_geth_probe(). We can't put 'mdio' until after we've used it in the fsl_pq_mdio_bus_name() call. Also fix error return values. Signed-off-by: David S. Miller --- drivers/net/ucc_geth.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ucc_geth.c b/drivers/net/ucc_geth.c index 86a479f61c0c..933fcfbf35e1 100644 --- a/drivers/net/ucc_geth.c +++ b/drivers/net/ucc_geth.c @@ -3648,15 +3648,16 @@ static int ucc_geth_probe(struct of_device* ofdev, const struct of_device_id *ma mdio = of_get_parent(phy); if (mdio == NULL) - return -1; + return -ENODEV; err = of_address_to_resource(mdio, 0, &res); - of_node_put(mdio); - - if (err) - return -1; + if (err) { + of_node_put(mdio); + return err; + } fsl_pq_mdio_bus_name(bus_name, mdio); + of_node_put(mdio); snprintf(ug_info->phy_bus_id, sizeof(ug_info->phy_bus_id), "%s:%02x", bus_name, *prop); } -- cgit v1.2.3 From 4099e01224e2afcaeea439cd92db3e7cf6e0f84f Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sun, 29 Mar 2009 01:39:41 -0700 Subject: niu: Add GRO support. Signed-off-by: David S. Miller --- drivers/net/niu.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/niu.c b/drivers/net/niu.c index 50c11126a3db..02c37e2f08a9 100644 --- a/drivers/net/niu.c +++ b/drivers/net/niu.c @@ -3441,7 +3441,8 @@ static int niu_rx_pkt_ignore(struct niu *np, struct rx_ring_info *rp) return num_rcr; } -static int niu_process_rx_pkt(struct niu *np, struct rx_ring_info *rp) +static int niu_process_rx_pkt(struct napi_struct *napi, struct niu *np, + struct rx_ring_info *rp) { unsigned int index = rp->rcr_index; struct sk_buff *skb; @@ -3518,7 +3519,7 @@ static int niu_process_rx_pkt(struct niu *np, struct rx_ring_info *rp) skb->protocol = eth_type_trans(skb, np->dev); skb_record_rx_queue(skb, rp->rx_channel); - netif_receive_skb(skb); + napi_gro_receive(napi, skb); return num_rcr; } @@ -3706,7 +3707,8 @@ static inline void niu_sync_rx_discard_stats(struct niu *np, } } -static int niu_rx_work(struct niu *np, struct rx_ring_info *rp, int budget) +static int niu_rx_work(struct napi_struct *napi, struct niu *np, + struct rx_ring_info *rp, int budget) { int qlen, rcr_done = 0, work_done = 0; struct rxdma_mailbox *mbox = rp->mbox; @@ -3728,7 +3730,7 @@ static int niu_rx_work(struct niu *np, struct rx_ring_info *rp, int budget) rcr_done = work_done = 0; qlen = min(qlen, budget); while (work_done < qlen) { - rcr_done += niu_process_rx_pkt(np, rp); + rcr_done += niu_process_rx_pkt(napi, np, rp); work_done++; } @@ -3776,7 +3778,7 @@ static int niu_poll_core(struct niu *np, struct niu_ldg *lp, int budget) if (rx_vec & (1 << rp->rx_channel)) { int this_work_done; - this_work_done = niu_rx_work(np, rp, + this_work_done = niu_rx_work(&lp->napi, np, rp, budget); budget -= this_work_done; -- cgit v1.2.3 From 1383bdb98c01bbd28d72336d1bf614ce79114d29 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sun, 29 Mar 2009 01:39:49 -0700 Subject: tg3: Add GRO support. Signed-off-by: David S. Miller --- drivers/net/tg3.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index f7efcecc4108..1205c2a22657 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c @@ -4392,7 +4392,7 @@ static void tg3_recycle_rx(struct tg3 *tp, u32 opaque_key, #if TG3_VLAN_TAG_USED static int tg3_vlan_rx(struct tg3 *tp, struct sk_buff *skb, u16 vlan_tag) { - return vlan_hwaccel_receive_skb(skb, tp->vlgrp, vlan_tag); + return vlan_gro_receive(&tp->napi, tp->vlgrp, vlan_tag, skb); } #endif @@ -4539,7 +4539,7 @@ static int tg3_rx(struct tg3 *tp, int budget) desc->err_vlan & RXD_VLAN_MASK); } else #endif - netif_receive_skb(skb); + napi_gro_receive(&tp->napi, skb); received++; budget--; -- cgit v1.2.3 From 321dee6e8b235c496f0a068a72d8df9a4e13ceb9 Mon Sep 17 00:00:00 2001 From: Alexander Beregalov Date: Sun, 29 Mar 2009 13:52:21 -0700 Subject: wireless: remove duplicated .ndo_set_mac_address Signed-off-by: Alexander Beregalov Signed-off-by: David S. Miller --- drivers/net/wireless/airo.c | 2 -- drivers/net/wireless/ipw2x00/ipw2200.c | 1 - drivers/net/wireless/prism54/islpci_dev.c | 1 - drivers/net/wireless/zd1201.c | 1 - 4 files changed, 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c index 7e80aba8a148..93302c0a36b0 100644 --- a/drivers/net/wireless/airo.c +++ b/drivers/net/wireless/airo.c @@ -2752,7 +2752,6 @@ static const struct net_device_ops airo_netdev_ops = { .ndo_set_mac_address = airo_set_mac_address, .ndo_do_ioctl = airo_ioctl, .ndo_change_mtu = airo_change_mtu, - .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; @@ -2765,7 +2764,6 @@ static const struct net_device_ops mpi_netdev_ops = { .ndo_set_mac_address = airo_set_mac_address, .ndo_do_ioctl = airo_ioctl, .ndo_change_mtu = airo_change_mtu, - .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/wireless/ipw2x00/ipw2200.c b/drivers/net/wireless/ipw2x00/ipw2200.c index b3449948a25a..4a92af1d7877 100644 --- a/drivers/net/wireless/ipw2x00/ipw2200.c +++ b/drivers/net/wireless/ipw2x00/ipw2200.c @@ -11593,7 +11593,6 @@ static const struct net_device_ops ipw_netdev_ops = { .ndo_set_mac_address = ipw_net_set_mac_address, .ndo_start_xmit = ieee80211_xmit, .ndo_change_mtu = ieee80211_change_mtu, - .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/wireless/prism54/islpci_dev.c b/drivers/net/wireless/prism54/islpci_dev.c index 166ed9584601..e26d7b3ceab5 100644 --- a/drivers/net/wireless/prism54/islpci_dev.c +++ b/drivers/net/wireless/prism54/islpci_dev.c @@ -803,7 +803,6 @@ static const struct net_device_ops islpci_netdev_ops = { .ndo_tx_timeout = islpci_eth_tx_timeout, .ndo_set_mac_address = prism54_set_mac_address, .ndo_change_mtu = eth_change_mtu, - .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/wireless/zd1201.c b/drivers/net/wireless/zd1201.c index 9b244c96b221..5fabd9c0f07a 100644 --- a/drivers/net/wireless/zd1201.c +++ b/drivers/net/wireless/zd1201.c @@ -1725,7 +1725,6 @@ static const struct net_device_ops zd1201_netdev_ops = { .ndo_set_multicast_list = zd1201_set_multicast, .ndo_set_mac_address = zd1201_set_mac_address, .ndo_change_mtu = eth_change_mtu, - .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; -- cgit v1.2.3 From 3a35ce7dcefe9e80a00603a195269fbaf6e7d901 Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Thu, 22 Jan 2009 16:42:57 +0100 Subject: virtio: fix BAD_RING, START_US and END_USE macros Impact: cleanup fix BAD_RING, START_US and END_USE macros When these macros aren't called with a variable named vq as first argument, this would result in a build failure. Signed-off-by: Roel Kluin Signed-off-by: Rusty Russell --- drivers/virtio/virtio_ring.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index 5777196bf6c9..12273bf9b3b2 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -23,15 +23,15 @@ #ifdef DEBUG /* For development, we want to crash whenever the ring is screwed. */ -#define BAD_RING(vq, fmt...) \ - do { dev_err(&vq->vq.vdev->dev, fmt); BUG(); } while(0) -#define START_USE(vq) \ - do { if ((vq)->in_use) panic("in_use = %i\n", (vq)->in_use); (vq)->in_use = __LINE__; mb(); } while(0) -#define END_USE(vq) \ - do { BUG_ON(!(vq)->in_use); (vq)->in_use = 0; mb(); } while(0) +#define BAD_RING(_vq, fmt...) \ + do { dev_err(&_vq->vq.vdev->dev, fmt); BUG(); } while(0) +#define START_USE(_vq) \ + do { if ((_vq)->in_use) panic("in_use = %i\n", (_vq)->in_use); (_vq)->in_use = __LINE__; mb(); } while(0) +#define END_USE(_vq) \ + do { BUG_ON(!(_vq)->in_use); (_vq)->in_use = 0; mb(); } while(0) #else -#define BAD_RING(vq, fmt...) \ - do { dev_err(&vq->vq.vdev->dev, fmt); (vq)->broken = true; } while(0) +#define BAD_RING(_vq, fmt...) \ + do { dev_err(&_vq->vq.vdev->dev, fmt); (_vq)->broken = true; } while(0) #define START_USE(vq) #define END_USE(vq) #endif -- cgit v1.2.3 From c5f841f1780dad7efb7eca092f60742d47f47d25 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 30 Mar 2009 21:55:22 -0600 Subject: virtio: more neatening of virtio_ring macros. Impact: cleanup Roel Kluin drew attention to these macros with his patch: here I neaten them a little further: 1) Add a comment on what START_USE and END_USE are checking, 2) Brackets around _vq in BAD_RING, 3) Neaten formatting for START_USE so it's less than 80 cols. Signed-off-by: Rusty Russell --- drivers/virtio/virtio_ring.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index 12273bf9b3b2..5c52369ab9bb 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -24,9 +24,15 @@ #ifdef DEBUG /* For development, we want to crash whenever the ring is screwed. */ #define BAD_RING(_vq, fmt...) \ - do { dev_err(&_vq->vq.vdev->dev, fmt); BUG(); } while(0) -#define START_USE(_vq) \ - do { if ((_vq)->in_use) panic("in_use = %i\n", (_vq)->in_use); (_vq)->in_use = __LINE__; mb(); } while(0) + do { dev_err(&(_vq)->vq.vdev->dev, fmt); BUG(); } while(0) +/* Caller is supposed to guarantee no reentry. */ +#define START_USE(_vq) \ + do { \ + if ((_vq)->in_use) \ + panic("in_use = %i\n", (_vq)->in_use); \ + (_vq)->in_use = __LINE__; \ + mb(); \ + } while(0) #define END_USE(_vq) \ do { BUG_ON(!(_vq)->in_use); (_vq)->in_use = 0; mb(); } while(0) #else -- cgit v1.2.3 From 6afbdd059c27330eccbd85943354f94c2b83a7fe Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 30 Mar 2009 21:55:23 -0600 Subject: lguest: fix spurious BUG_ON() on invalid guest stack. Impact: fix crash on misbehaving guest gpte_addr() contains a BUG_ON(), insisting that the present flag is set. We need to return before we call it if that isn't the case. Signed-off-by: Rusty Russell Cc: stable@kernel.org --- drivers/lguest/page_tables.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/lguest/page_tables.c b/drivers/lguest/page_tables.c index 576a8318221c..82ff484bd8c8 100644 --- a/drivers/lguest/page_tables.c +++ b/drivers/lguest/page_tables.c @@ -373,8 +373,10 @@ unsigned long guest_pa(struct lg_cpu *cpu, unsigned long vaddr) /* First step: get the top-level Guest page table entry. */ gpgd = lgread(cpu, gpgd_addr(cpu, vaddr), pgd_t); /* Toplevel not present? We can't map it in. */ - if (!(pgd_flags(gpgd) & _PAGE_PRESENT)) + if (!(pgd_flags(gpgd) & _PAGE_PRESENT)) { kill_guest(cpu, "Bad address %#lx", vaddr); + return -1UL; + } gpte = lgread(cpu, gpte_addr(gpgd, vaddr), pte_t); if (!(pte_flags(gpte) & _PAGE_PRESENT)) -- cgit v1.2.3 From 4cd8b5e2a159f18a1507f1187b44a1acbfa6341b Mon Sep 17 00:00:00 2001 From: Matias Zabaljauregui Date: Sat, 14 Mar 2009 13:37:52 -0200 Subject: lguest: use KVM hypercalls Impact: cleanup This patch allow us to use KVM hypercalls Signed-off-by: Matias Zabaljauregui Signed-off-by: Rusty Russell --- arch/x86/include/asm/lguest_hcall.h | 24 ++--------- arch/x86/lguest/boot.c | 78 ++++++++++++++++++++++------------- arch/x86/lguest/i386_head.S | 4 +- drivers/lguest/interrupts_and_traps.c | 7 ++-- drivers/lguest/lguest_device.c | 4 +- drivers/lguest/x86/core.c | 62 +++++++++++++++++++++++++++- 6 files changed, 122 insertions(+), 57 deletions(-) (limited to 'drivers') diff --git a/arch/x86/include/asm/lguest_hcall.h b/arch/x86/include/asm/lguest_hcall.h index 43894428c3c2..0f4ee7148afe 100644 --- a/arch/x86/include/asm/lguest_hcall.h +++ b/arch/x86/include/asm/lguest_hcall.h @@ -26,36 +26,20 @@ #ifndef __ASSEMBLY__ #include +#include /*G:031 But first, how does our Guest contact the Host to ask for privileged * operations? There are two ways: the direct way is to make a "hypercall", * to make requests of the Host Itself. * - * Our hypercall mechanism uses the highest unused trap code (traps 32 and - * above are used by real hardware interrupts). Fifteen hypercalls are + * We use the KVM hypercall mechanism. Eighteen hypercalls are * available: the hypercall number is put in the %eax register, and the - * arguments (when required) are placed in %edx, %ebx and %ecx. If a return + * arguments (when required) are placed in %ebx, %ecx and %edx. If a return * value makes sense, it's returned in %eax. * * Grossly invalid calls result in Sudden Death at the hands of the vengeful * Host, rather than returning failure. This reflects Winston Churchill's * definition of a gentleman: "someone who is only rude intentionally". */ -static inline unsigned long -hcall(unsigned long call, - unsigned long arg1, unsigned long arg2, unsigned long arg3) -{ - /* "int" is the Intel instruction to trigger a trap. */ - asm volatile("int $" __stringify(LGUEST_TRAP_ENTRY) - /* The call in %eax (aka "a") might be overwritten */ - : "=a"(call) - /* The arguments are in %eax, %edx, %ebx & %ecx */ - : "a"(call), "d"(arg1), "b"(arg2), "c"(arg3) - /* "memory" means this might write somewhere in memory. - * This isn't true for all calls, but it's safe to tell - * gcc that it might happen so it doesn't get clever. */ - : "memory"); - return call; -} /*:*/ /* Can't use our min() macro here: needs to be a constant */ @@ -64,7 +48,7 @@ hcall(unsigned long call, #define LHCALL_RING_SIZE 64 struct hcall_args { /* These map directly onto eax, ebx, ecx, edx in struct lguest_regs */ - unsigned long arg0, arg2, arg3, arg1; + unsigned long arg0, arg1, arg2, arg3; }; #endif /* !__ASSEMBLY__ */ diff --git a/arch/x86/lguest/boot.c b/arch/x86/lguest/boot.c index 7822d29b02c7..5be9293961ba 100644 --- a/arch/x86/lguest/boot.c +++ b/arch/x86/lguest/boot.c @@ -107,7 +107,7 @@ static void async_hcall(unsigned long call, unsigned long arg1, local_irq_save(flags); if (lguest_data.hcall_status[next_call] != 0xFF) { /* Table full, so do normal hcall which will flush table. */ - hcall(call, arg1, arg2, arg3); + kvm_hypercall3(call, arg1, arg2, arg3); } else { lguest_data.hcalls[next_call].arg0 = call; lguest_data.hcalls[next_call].arg1 = arg1; @@ -134,13 +134,32 @@ static void async_hcall(unsigned long call, unsigned long arg1, * * So, when we're in lazy mode, we call async_hcall() to store the call for * future processing: */ -static void lazy_hcall(unsigned long call, +static void lazy_hcall1(unsigned long call, + unsigned long arg1) +{ + if (paravirt_get_lazy_mode() == PARAVIRT_LAZY_NONE) + kvm_hypercall1(call, arg1); + else + async_hcall(call, arg1, 0, 0); +} + +static void lazy_hcall2(unsigned long call, + unsigned long arg1, + unsigned long arg2) +{ + if (paravirt_get_lazy_mode() == PARAVIRT_LAZY_NONE) + kvm_hypercall2(call, arg1, arg2); + else + async_hcall(call, arg1, arg2, 0); +} + +static void lazy_hcall3(unsigned long call, unsigned long arg1, unsigned long arg2, unsigned long arg3) { if (paravirt_get_lazy_mode() == PARAVIRT_LAZY_NONE) - hcall(call, arg1, arg2, arg3); + kvm_hypercall3(call, arg1, arg2, arg3); else async_hcall(call, arg1, arg2, arg3); } @@ -150,7 +169,7 @@ static void lazy_hcall(unsigned long call, static void lguest_leave_lazy_mode(void) { paravirt_leave_lazy(paravirt_get_lazy_mode()); - hcall(LHCALL_FLUSH_ASYNC, 0, 0, 0); + kvm_hypercall0(LHCALL_FLUSH_ASYNC); } /*G:033 @@ -229,7 +248,7 @@ static void lguest_write_idt_entry(gate_desc *dt, /* Keep the local copy up to date. */ native_write_idt_entry(dt, entrynum, g); /* Tell Host about this new entry. */ - hcall(LHCALL_LOAD_IDT_ENTRY, entrynum, desc[0], desc[1]); + kvm_hypercall3(LHCALL_LOAD_IDT_ENTRY, entrynum, desc[0], desc[1]); } /* Changing to a different IDT is very rare: we keep the IDT up-to-date every @@ -241,7 +260,7 @@ static void lguest_load_idt(const struct desc_ptr *desc) struct desc_struct *idt = (void *)desc->address; for (i = 0; i < (desc->size+1)/8; i++) - hcall(LHCALL_LOAD_IDT_ENTRY, i, idt[i].a, idt[i].b); + kvm_hypercall3(LHCALL_LOAD_IDT_ENTRY, i, idt[i].a, idt[i].b); } /* @@ -261,8 +280,8 @@ static void lguest_load_idt(const struct desc_ptr *desc) */ static void lguest_load_gdt(const struct desc_ptr *desc) { - BUG_ON((desc->size+1)/8 != GDT_ENTRIES); - hcall(LHCALL_LOAD_GDT, __pa(desc->address), GDT_ENTRIES, 0); + BUG_ON((desc->size + 1) / 8 != GDT_ENTRIES); + kvm_hypercall2(LHCALL_LOAD_GDT, __pa(desc->address), GDT_ENTRIES); } /* For a single GDT entry which changes, we do the lazy thing: alter our GDT, @@ -272,7 +291,7 @@ static void lguest_write_gdt_entry(struct desc_struct *dt, int entrynum, const void *desc, int type) { native_write_gdt_entry(dt, entrynum, desc, type); - hcall(LHCALL_LOAD_GDT, __pa(dt), GDT_ENTRIES, 0); + kvm_hypercall2(LHCALL_LOAD_GDT, __pa(dt), GDT_ENTRIES); } /* OK, I lied. There are three "thread local storage" GDT entries which change @@ -284,7 +303,7 @@ static void lguest_load_tls(struct thread_struct *t, unsigned int cpu) * can't handle us removing entries we're currently using. So we clear * the GS register here: if it's needed it'll be reloaded anyway. */ lazy_load_gs(0); - lazy_hcall(LHCALL_LOAD_TLS, __pa(&t->tls_array), cpu, 0); + lazy_hcall2(LHCALL_LOAD_TLS, __pa(&t->tls_array), cpu); } /*G:038 That's enough excitement for now, back to ploughing through each of @@ -382,7 +401,7 @@ static void lguest_cpuid(unsigned int *ax, unsigned int *bx, static unsigned long current_cr0; static void lguest_write_cr0(unsigned long val) { - lazy_hcall(LHCALL_TS, val & X86_CR0_TS, 0, 0); + lazy_hcall1(LHCALL_TS, val & X86_CR0_TS); current_cr0 = val; } @@ -396,7 +415,7 @@ static unsigned long lguest_read_cr0(void) * the vowels have been optimized out. */ static void lguest_clts(void) { - lazy_hcall(LHCALL_TS, 0, 0, 0); + lazy_hcall1(LHCALL_TS, 0); current_cr0 &= ~X86_CR0_TS; } @@ -418,7 +437,7 @@ static bool cr3_changed = false; static void lguest_write_cr3(unsigned long cr3) { lguest_data.pgdir = cr3; - lazy_hcall(LHCALL_NEW_PGTABLE, cr3, 0, 0); + lazy_hcall1(LHCALL_NEW_PGTABLE, cr3); cr3_changed = true; } @@ -493,7 +512,7 @@ static void lguest_write_cr4(unsigned long val) static void lguest_pte_update(struct mm_struct *mm, unsigned long addr, pte_t *ptep) { - lazy_hcall(LHCALL_SET_PTE, __pa(mm->pgd), addr, ptep->pte_low); + lazy_hcall3(LHCALL_SET_PTE, __pa(mm->pgd), addr, ptep->pte_low); } static void lguest_set_pte_at(struct mm_struct *mm, unsigned long addr, @@ -509,8 +528,8 @@ static void lguest_set_pte_at(struct mm_struct *mm, unsigned long addr, static void lguest_set_pmd(pmd_t *pmdp, pmd_t pmdval) { *pmdp = pmdval; - lazy_hcall(LHCALL_SET_PMD, __pa(pmdp)&PAGE_MASK, - (__pa(pmdp)&(PAGE_SIZE-1))/4, 0); + lazy_hcall2(LHCALL_SET_PMD, __pa(pmdp) & PAGE_MASK, + (__pa(pmdp) & (PAGE_SIZE - 1)) / 4); } /* There are a couple of legacy places where the kernel sets a PTE, but we @@ -526,7 +545,7 @@ static void lguest_set_pte(pte_t *ptep, pte_t pteval) { *ptep = pteval; if (cr3_changed) - lazy_hcall(LHCALL_FLUSH_TLB, 1, 0, 0); + lazy_hcall1(LHCALL_FLUSH_TLB, 1); } /* Unfortunately for Lguest, the pv_mmu_ops for page tables were based on @@ -542,7 +561,7 @@ static void lguest_set_pte(pte_t *ptep, pte_t pteval) static void lguest_flush_tlb_single(unsigned long addr) { /* Simply set it to zero: if it was not, it will fault back in. */ - lazy_hcall(LHCALL_SET_PTE, lguest_data.pgdir, addr, 0); + lazy_hcall3(LHCALL_SET_PTE, lguest_data.pgdir, addr, 0); } /* This is what happens after the Guest has removed a large number of entries. @@ -550,7 +569,7 @@ static void lguest_flush_tlb_single(unsigned long addr) * have changed, ie. virtual addresses below PAGE_OFFSET. */ static void lguest_flush_tlb_user(void) { - lazy_hcall(LHCALL_FLUSH_TLB, 0, 0, 0); + lazy_hcall1(LHCALL_FLUSH_TLB, 0); } /* This is called when the kernel page tables have changed. That's not very @@ -558,7 +577,7 @@ static void lguest_flush_tlb_user(void) * slow), so it's worth separating this from the user flushing above. */ static void lguest_flush_tlb_kernel(void) { - lazy_hcall(LHCALL_FLUSH_TLB, 1, 0, 0); + lazy_hcall1(LHCALL_FLUSH_TLB, 1); } /* @@ -695,7 +714,7 @@ static int lguest_clockevent_set_next_event(unsigned long delta, } /* Please wake us this far in the future. */ - hcall(LHCALL_SET_CLOCKEVENT, delta, 0, 0); + kvm_hypercall1(LHCALL_SET_CLOCKEVENT, delta); return 0; } @@ -706,7 +725,7 @@ static void lguest_clockevent_set_mode(enum clock_event_mode mode, case CLOCK_EVT_MODE_UNUSED: case CLOCK_EVT_MODE_SHUTDOWN: /* A 0 argument shuts the clock down. */ - hcall(LHCALL_SET_CLOCKEVENT, 0, 0, 0); + kvm_hypercall0(LHCALL_SET_CLOCKEVENT); break; case CLOCK_EVT_MODE_ONESHOT: /* This is what we expect. */ @@ -781,8 +800,8 @@ static void lguest_time_init(void) static void lguest_load_sp0(struct tss_struct *tss, struct thread_struct *thread) { - lazy_hcall(LHCALL_SET_STACK, __KERNEL_DS|0x1, thread->sp0, - THREAD_SIZE/PAGE_SIZE); + lazy_hcall3(LHCALL_SET_STACK, __KERNEL_DS | 0x1, thread->sp0, + THREAD_SIZE / PAGE_SIZE); } /* Let's just say, I wouldn't do debugging under a Guest. */ @@ -855,7 +874,7 @@ static void set_lguest_basic_apic_ops(void) /* STOP! Until an interrupt comes in. */ static void lguest_safe_halt(void) { - hcall(LHCALL_HALT, 0, 0, 0); + kvm_hypercall0(LHCALL_HALT); } /* The SHUTDOWN hypercall takes a string to describe what's happening, and @@ -865,7 +884,8 @@ static void lguest_safe_halt(void) * rather than virtual addresses, so we use __pa() here. */ static void lguest_power_off(void) { - hcall(LHCALL_SHUTDOWN, __pa("Power down"), LGUEST_SHUTDOWN_POWEROFF, 0); + kvm_hypercall2(LHCALL_SHUTDOWN, __pa("Power down"), + LGUEST_SHUTDOWN_POWEROFF); } /* @@ -875,7 +895,7 @@ static void lguest_power_off(void) */ static int lguest_panic(struct notifier_block *nb, unsigned long l, void *p) { - hcall(LHCALL_SHUTDOWN, __pa(p), LGUEST_SHUTDOWN_POWEROFF, 0); + kvm_hypercall2(LHCALL_SHUTDOWN, __pa(p), LGUEST_SHUTDOWN_POWEROFF); /* The hcall won't return, but to keep gcc happy, we're "done". */ return NOTIFY_DONE; } @@ -916,7 +936,7 @@ static __init int early_put_chars(u32 vtermno, const char *buf, int count) len = sizeof(scratch) - 1; scratch[len] = '\0'; memcpy(scratch, buf, len); - hcall(LHCALL_NOTIFY, __pa(scratch), 0, 0); + kvm_hypercall1(LHCALL_NOTIFY, __pa(scratch)); /* This routine returns the number of bytes actually written. */ return len; @@ -926,7 +946,7 @@ static __init int early_put_chars(u32 vtermno, const char *buf, int count) * Launcher to reboot us. */ static void lguest_restart(char *reason) { - hcall(LHCALL_SHUTDOWN, __pa(reason), LGUEST_SHUTDOWN_RESTART, 0); + kvm_hypercall2(LHCALL_SHUTDOWN, __pa(reason), LGUEST_SHUTDOWN_RESTART); } /*G:050 diff --git a/arch/x86/lguest/i386_head.S b/arch/x86/lguest/i386_head.S index 10b9bd35a8ff..f79541989471 100644 --- a/arch/x86/lguest/i386_head.S +++ b/arch/x86/lguest/i386_head.S @@ -27,8 +27,8 @@ ENTRY(lguest_entry) /* We make the "initialization" hypercall now to tell the Host about * us, and also find out where it put our page tables. */ movl $LHCALL_LGUEST_INIT, %eax - movl $lguest_data - __PAGE_OFFSET, %edx - int $LGUEST_TRAP_ENTRY + movl $lguest_data - __PAGE_OFFSET, %ebx + .byte 0x0f,0x01,0xc1 /* KVM_HYPERCALL */ /* Set up the initial stack so we can run C code. */ movl $(init_thread_union+THREAD_SIZE),%esp diff --git a/drivers/lguest/interrupts_and_traps.c b/drivers/lguest/interrupts_and_traps.c index 415fab0125ac..504091da1737 100644 --- a/drivers/lguest/interrupts_and_traps.c +++ b/drivers/lguest/interrupts_and_traps.c @@ -288,9 +288,10 @@ static int direct_trap(unsigned int num) /* The Host needs to see page faults (for shadow paging and to save the * fault address), general protection faults (in/out emulation) and - * device not available (TS handling), and of course, the hypercall - * trap. */ - return num != 14 && num != 13 && num != 7 && num != LGUEST_TRAP_ENTRY; + * device not available (TS handling), invalid opcode fault (kvm hcall), + * and of course, the hypercall trap. */ + return num != 14 && num != 13 && num != 7 && + num != 6 && num != LGUEST_TRAP_ENTRY; } /*:*/ diff --git a/drivers/lguest/lguest_device.c b/drivers/lguest/lguest_device.c index 8132533d71f9..df44d962626d 100644 --- a/drivers/lguest/lguest_device.c +++ b/drivers/lguest/lguest_device.c @@ -161,7 +161,7 @@ static void set_status(struct virtio_device *vdev, u8 status) /* We set the status. */ to_lgdev(vdev)->desc->status = status; - hcall(LHCALL_NOTIFY, (max_pfn<priv; - hcall(LHCALL_NOTIFY, lvq->config.pfn << PAGE_SHIFT, 0, 0); + kvm_hypercall1(LHCALL_NOTIFY, lvq->config.pfn << PAGE_SHIFT); } /* An extern declaration inside a C file is bad form. Don't do it. */ diff --git a/drivers/lguest/x86/core.c b/drivers/lguest/x86/core.c index bf7942327bda..a6b717644be0 100644 --- a/drivers/lguest/x86/core.c +++ b/drivers/lguest/x86/core.c @@ -290,6 +290,57 @@ static int emulate_insn(struct lg_cpu *cpu) return 1; } +/* Our hypercalls mechanism used to be based on direct software interrupts. + * After Anthony's "Refactor hypercall infrastructure" kvm patch, we decided to + * change over to using kvm hypercalls. + * + * KVM_HYPERCALL is actually a "vmcall" instruction, which generates an invalid + * opcode fault (fault 6) on non-VT cpus, so the easiest solution seemed to be + * an *emulation approach*: if the fault was really produced by an hypercall + * (is_hypercall() does exactly this check), we can just call the corresponding + * hypercall host implementation function. + * + * But these invalid opcode faults are notably slower than software interrupts. + * So we implemented the *patching (or rewriting) approach*: every time we hit + * the KVM_HYPERCALL opcode in Guest code, we patch it to the old "int 0x1f" + * opcode, so next time the Guest calls this hypercall it will use the + * faster trap mechanism. + * + * Matias even benchmarked it to convince you: this shows the average cycle + * cost of a hypercall. For each alternative solution mentioned above we've + * made 5 runs of the benchmark: + * + * 1) direct software interrupt: 2915, 2789, 2764, 2721, 2898 + * 2) emulation technique: 3410, 3681, 3466, 3392, 3780 + * 3) patching (rewrite) technique: 2977, 2975, 2891, 2637, 2884 + * + * One two-line function is worth a 20% hypercall speed boost! + */ +static void rewrite_hypercall(struct lg_cpu *cpu) +{ + /* This are the opcodes we use to patch the Guest. The opcode for "int + * $0x1f" is "0xcd 0x1f" but vmcall instruction is 3 bytes long, so we + * complete the sequence with a NOP (0x90). */ + u8 insn[3] = {0xcd, 0x1f, 0x90}; + + __lgwrite(cpu, guest_pa(cpu, cpu->regs->eip), insn, sizeof(insn)); +} + +static bool is_hypercall(struct lg_cpu *cpu) +{ + u8 insn[3]; + + /* This must be the Guest kernel trying to do something. + * The bottom two bits of the CS segment register are the privilege + * level. */ + if ((cpu->regs->cs & 3) != GUEST_PL) + return false; + + /* Is it a vmcall? */ + __lgread(cpu, insn, guest_pa(cpu, cpu->regs->eip), sizeof(insn)); + return insn[0] == 0x0f && insn[1] == 0x01 && insn[2] == 0xc1; +} + /*H:050 Once we've re-enabled interrupts, we look at why the Guest exited. */ void lguest_arch_handle_trap(struct lg_cpu *cpu) { @@ -337,7 +388,7 @@ void lguest_arch_handle_trap(struct lg_cpu *cpu) break; case 32 ... 255: /* These values mean a real interrupt occurred, in which case - * the Host handler has already been run. We just do a + * the Host handler has already been run. We just do a * friendly check if another process should now be run, then * return to run the Guest again */ cond_resched(); @@ -347,6 +398,15 @@ void lguest_arch_handle_trap(struct lg_cpu *cpu) * up the pointer now to indicate a hypercall is pending. */ cpu->hcall = (struct hcall_args *)cpu->regs; return; + case 6: + /* kvm hypercalls trigger an invalid opcode fault (6). + * We need to check if ring == GUEST_PL and + * faulting instruction == vmcall. */ + if (is_hypercall(cpu)) { + rewrite_hypercall(cpu); + return; + } + break; } /* We didn't handle the trap, so it needs to go to the Guest. */ -- cgit v1.2.3 From df1693abc42e34bbc4351e179dbe66c28a94efb8 Mon Sep 17 00:00:00 2001 From: Matias Zabaljauregui Date: Wed, 18 Mar 2009 13:38:35 -0300 Subject: lguest: use bool instead of int Impact: clean up Rusty told me, some time ago, that he had become a fan of "bool". So, here are some replacements. Signed-off-by: Matias Zabaljauregui Signed-off-by: Rusty Russell --- drivers/lguest/core.c | 4 ++-- drivers/lguest/interrupts_and_traps.c | 21 +++++++++++---------- drivers/lguest/lg.h | 8 ++++---- drivers/lguest/page_tables.c | 18 +++++++++--------- drivers/lguest/segments.c | 2 +- 5 files changed, 27 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/lguest/core.c b/drivers/lguest/core.c index 60156dfdc608..4845fb3cf74b 100644 --- a/drivers/lguest/core.c +++ b/drivers/lguest/core.c @@ -152,8 +152,8 @@ static void unmap_switcher(void) * code. We have to check that the range is below the pfn_limit the Launcher * gave us. We have to make sure that addr + len doesn't give us a false * positive by overflowing, too. */ -int lguest_address_ok(const struct lguest *lg, - unsigned long addr, unsigned long len) +bool lguest_address_ok(const struct lguest *lg, + unsigned long addr, unsigned long len) { return (addr+len) / PAGE_SIZE < lg->pfn_limit && (addr+len >= addr); } diff --git a/drivers/lguest/interrupts_and_traps.c b/drivers/lguest/interrupts_and_traps.c index 504091da1737..6e99adbe1946 100644 --- a/drivers/lguest/interrupts_and_traps.c +++ b/drivers/lguest/interrupts_and_traps.c @@ -34,7 +34,7 @@ static int idt_type(u32 lo, u32 hi) } /* An IDT entry can't be used unless the "present" bit is set. */ -static int idt_present(u32 lo, u32 hi) +static bool idt_present(u32 lo, u32 hi) { return (hi & 0x8000); } @@ -60,7 +60,8 @@ static void push_guest_stack(struct lg_cpu *cpu, unsigned long *gstack, u32 val) * We set up the stack just like the CPU does for a real interrupt, so it's * identical for the Guest (and the standard "iret" instruction will undo * it). */ -static void set_guest_interrupt(struct lg_cpu *cpu, u32 lo, u32 hi, int has_err) +static void set_guest_interrupt(struct lg_cpu *cpu, u32 lo, u32 hi, + bool has_err) { unsigned long gstack, origstack; u32 eflags, ss, irq_enable; @@ -184,7 +185,7 @@ void maybe_do_interrupt(struct lg_cpu *cpu) /* set_guest_interrupt() takes the interrupt descriptor and a * flag to say whether this interrupt pushes an error code onto * the stack as well: virtual interrupts never do. */ - set_guest_interrupt(cpu, idt->a, idt->b, 0); + set_guest_interrupt(cpu, idt->a, idt->b, false); } /* Every time we deliver an interrupt, we update the timestamp in the @@ -244,26 +245,26 @@ void free_interrupts(void) /*H:220 Now we've got the routines to deliver interrupts, delivering traps like * page fault is easy. The only trick is that Intel decided that some traps * should have error codes: */ -static int has_err(unsigned int trap) +static bool has_err(unsigned int trap) { return (trap == 8 || (trap >= 10 && trap <= 14) || trap == 17); } /* deliver_trap() returns true if it could deliver the trap. */ -int deliver_trap(struct lg_cpu *cpu, unsigned int num) +bool deliver_trap(struct lg_cpu *cpu, unsigned int num) { /* Trap numbers are always 8 bit, but we set an impossible trap number * for traps inside the Switcher, so check that here. */ if (num >= ARRAY_SIZE(cpu->arch.idt)) - return 0; + return false; /* Early on the Guest hasn't set the IDT entries (or maybe it put a * bogus one in): if we fail here, the Guest will be killed. */ if (!idt_present(cpu->arch.idt[num].a, cpu->arch.idt[num].b)) - return 0; + return false; set_guest_interrupt(cpu, cpu->arch.idt[num].a, cpu->arch.idt[num].b, has_err(num)); - return 1; + return true; } /*H:250 Here's the hard part: returning to the Host every time a trap happens @@ -279,12 +280,12 @@ int deliver_trap(struct lg_cpu *cpu, unsigned int num) * * This routine indicates if a particular trap number could be delivered * directly. */ -static int direct_trap(unsigned int num) +static bool direct_trap(unsigned int num) { /* Hardware interrupts don't go to the Guest at all (except system * call). */ if (num >= FIRST_EXTERNAL_VECTOR && !could_be_syscall(num)) - return 0; + return false; /* The Host needs to see page faults (for shadow paging and to save the * fault address), general protection faults (in/out emulation) and diff --git a/drivers/lguest/lg.h b/drivers/lguest/lg.h index f2c641e0bdde..ac8a4a3741b8 100644 --- a/drivers/lguest/lg.h +++ b/drivers/lguest/lg.h @@ -109,8 +109,8 @@ struct lguest extern struct mutex lguest_lock; /* core.c: */ -int lguest_address_ok(const struct lguest *lg, - unsigned long addr, unsigned long len); +bool lguest_address_ok(const struct lguest *lg, + unsigned long addr, unsigned long len); void __lgread(struct lg_cpu *, void *, unsigned long, unsigned); void __lgwrite(struct lg_cpu *, unsigned long, const void *, unsigned); @@ -140,7 +140,7 @@ int run_guest(struct lg_cpu *cpu, unsigned long __user *user); /* interrupts_and_traps.c: */ void maybe_do_interrupt(struct lg_cpu *cpu); -int deliver_trap(struct lg_cpu *cpu, unsigned int num); +bool deliver_trap(struct lg_cpu *cpu, unsigned int num); void load_guest_idt_entry(struct lg_cpu *cpu, unsigned int i, u32 low, u32 hi); void guest_set_stack(struct lg_cpu *cpu, u32 seg, u32 esp, unsigned int pages); @@ -173,7 +173,7 @@ void guest_pagetable_flush_user(struct lg_cpu *cpu); void guest_set_pte(struct lg_cpu *cpu, unsigned long gpgdir, unsigned long vaddr, pte_t val); void map_switcher_in_guest(struct lg_cpu *cpu, struct lguest_pages *pages); -int demand_page(struct lg_cpu *cpu, unsigned long cr2, int errcode); +bool demand_page(struct lg_cpu *cpu, unsigned long cr2, int errcode); void pin_page(struct lg_cpu *cpu, unsigned long vaddr); unsigned long guest_pa(struct lg_cpu *cpu, unsigned long vaddr); void page_table_guest_data_init(struct lg_cpu *cpu); diff --git a/drivers/lguest/page_tables.c b/drivers/lguest/page_tables.c index 82ff484bd8c8..a059cf9980f7 100644 --- a/drivers/lguest/page_tables.c +++ b/drivers/lguest/page_tables.c @@ -199,7 +199,7 @@ static void check_gpgd(struct lg_cpu *cpu, pgd_t gpgd) * * If we fixed up the fault (ie. we mapped the address), this routine returns * true. Otherwise, it was a real fault and we need to tell the Guest. */ -int demand_page(struct lg_cpu *cpu, unsigned long vaddr, int errcode) +bool demand_page(struct lg_cpu *cpu, unsigned long vaddr, int errcode) { pgd_t gpgd; pgd_t *spgd; @@ -211,7 +211,7 @@ int demand_page(struct lg_cpu *cpu, unsigned long vaddr, int errcode) gpgd = lgread(cpu, gpgd_addr(cpu, vaddr), pgd_t); /* Toplevel not present? We can't map it in. */ if (!(pgd_flags(gpgd) & _PAGE_PRESENT)) - return 0; + return false; /* Now look at the matching shadow entry. */ spgd = spgd_addr(cpu, cpu->cpu_pgd, vaddr); @@ -222,7 +222,7 @@ int demand_page(struct lg_cpu *cpu, unsigned long vaddr, int errcode) * simple for this corner case. */ if (!ptepage) { kill_guest(cpu, "out of memory allocating pte page"); - return 0; + return false; } /* We check that the Guest pgd is OK. */ check_gpgd(cpu, gpgd); @@ -238,16 +238,16 @@ int demand_page(struct lg_cpu *cpu, unsigned long vaddr, int errcode) /* If this page isn't in the Guest page tables, we can't page it in. */ if (!(pte_flags(gpte) & _PAGE_PRESENT)) - return 0; + return false; /* Check they're not trying to write to a page the Guest wants * read-only (bit 2 of errcode == write). */ if ((errcode & 2) && !(pte_flags(gpte) & _PAGE_RW)) - return 0; + return false; /* User access to a kernel-only page? (bit 3 == user access) */ if ((errcode & 4) && !(pte_flags(gpte) & _PAGE_USER)) - return 0; + return false; /* Check that the Guest PTE flags are OK, and the page number is below * the pfn_limit (ie. not mapping the Launcher binary). */ @@ -283,7 +283,7 @@ int demand_page(struct lg_cpu *cpu, unsigned long vaddr, int errcode) * manipulated, the result returned and the code complete. A small * delay and a trace of alliteration are the only indications the Guest * has that a page fault occurred at all. */ - return 1; + return true; } /*H:360 @@ -296,7 +296,7 @@ int demand_page(struct lg_cpu *cpu, unsigned long vaddr, int errcode) * * This is a quick version which answers the question: is this virtual address * mapped by the shadow page tables, and is it writable? */ -static int page_writable(struct lg_cpu *cpu, unsigned long vaddr) +static bool page_writable(struct lg_cpu *cpu, unsigned long vaddr) { pgd_t *spgd; unsigned long flags; @@ -304,7 +304,7 @@ static int page_writable(struct lg_cpu *cpu, unsigned long vaddr) /* Look at the current top level entry: is it present? */ spgd = spgd_addr(cpu, cpu->cpu_pgd, vaddr); if (!(pgd_flags(*spgd) & _PAGE_PRESENT)) - return 0; + return false; /* Check the flags on the pte entry itself: it must be present and * writable. */ diff --git a/drivers/lguest/segments.c b/drivers/lguest/segments.c index ec6aa3f1c36b..4f15439b7f12 100644 --- a/drivers/lguest/segments.c +++ b/drivers/lguest/segments.c @@ -45,7 +45,7 @@ * "Task State Segment" which controls all kinds of delicate things. The * LGUEST_CS and LGUEST_DS entries are reserved for the Switcher, and the * the Guest can't be trusted to deal with double faults. */ -static int ignored_gdt(unsigned int num) +static bool ignored_gdt(unsigned int num) { return (num == GDT_ENTRY_TSS || num == GDT_ENTRY_LGUEST_CS -- cgit v1.2.3 From aa85ea5b89c36c51200d795dd788139bd9b8cf50 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 30 Mar 2009 22:05:15 -0600 Subject: cpumask: use new cpumask_ functions in core code. Impact: cleanup Time to clean up remaining laggards using the old cpu_ functions. Signed-off-by: Rusty Russell Cc: Greg Kroah-Hartman Cc: Ingo Molnar Cc: Trond.Myklebust@netapp.com --- drivers/base/cpu.c | 2 +- include/linux/cpuset.h | 4 ++-- kernel/workqueue.c | 6 +++--- mm/allocpercpu.c | 2 +- mm/vmstat.c | 2 +- net/sunrpc/svc.c | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index 5b257a57bc57..e62a4ccea54d 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -119,7 +119,7 @@ static ssize_t print_cpus_map(char *buf, const struct cpumask *map) #define print_cpus_func(type) \ static ssize_t print_cpus_##type(struct sysdev_class *class, char *buf) \ { \ - return print_cpus_map(buf, &cpu_##type##_map); \ + return print_cpus_map(buf, cpu_##type##_mask); \ } \ static struct sysdev_class_attribute attr_##type##_map = \ _SYSDEV_CLASS_ATTR(type, 0444, print_cpus_##type, NULL) diff --git a/include/linux/cpuset.h b/include/linux/cpuset.h index 90c6074a36ca..2e0d79678deb 100644 --- a/include/linux/cpuset.h +++ b/include/linux/cpuset.h @@ -90,12 +90,12 @@ static inline void cpuset_init_smp(void) {} static inline void cpuset_cpus_allowed(struct task_struct *p, struct cpumask *mask) { - *mask = cpu_possible_map; + cpumask_copy(mask, cpu_possible_mask); } static inline void cpuset_cpus_allowed_locked(struct task_struct *p, struct cpumask *mask) { - *mask = cpu_possible_map; + cpumask_copy(mask, cpu_possible_mask); } static inline nodemask_t cpuset_mems_allowed(struct task_struct *p) diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 1f0c509b40d3..9aedd9fd825b 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -416,7 +416,7 @@ void flush_workqueue(struct workqueue_struct *wq) might_sleep(); lock_map_acquire(&wq->lockdep_map); lock_map_release(&wq->lockdep_map); - for_each_cpu_mask_nr(cpu, *cpu_map) + for_each_cpu(cpu, cpu_map) flush_cpu_workqueue(per_cpu_ptr(wq->cpu_wq, cpu)); } EXPORT_SYMBOL_GPL(flush_workqueue); @@ -547,7 +547,7 @@ static void wait_on_work(struct work_struct *work) wq = cwq->wq; cpu_map = wq_cpu_map(wq); - for_each_cpu_mask_nr(cpu, *cpu_map) + for_each_cpu(cpu, cpu_map) wait_on_cpu_work(per_cpu_ptr(wq->cpu_wq, cpu), work); } @@ -911,7 +911,7 @@ void destroy_workqueue(struct workqueue_struct *wq) list_del(&wq->list); spin_unlock(&workqueue_lock); - for_each_cpu_mask_nr(cpu, *cpu_map) + for_each_cpu(cpu, cpu_map) cleanup_workqueue_thread(per_cpu_ptr(wq->cpu_wq, cpu)); cpu_maps_update_done(); diff --git a/mm/allocpercpu.c b/mm/allocpercpu.c index 1882923bc706..139d5b7b6621 100644 --- a/mm/allocpercpu.c +++ b/mm/allocpercpu.c @@ -143,7 +143,7 @@ void free_percpu(void *__pdata) { if (unlikely(!__pdata)) return; - __percpu_depopulate_mask(__pdata, &cpu_possible_map); + __percpu_depopulate_mask(__pdata, cpu_possible_mask); kfree(__percpu_disguise(__pdata)); } EXPORT_SYMBOL_GPL(free_percpu); diff --git a/mm/vmstat.c b/mm/vmstat.c index 91149746bb8d..8cd81ea1ddc1 100644 --- a/mm/vmstat.c +++ b/mm/vmstat.c @@ -27,7 +27,7 @@ static void sum_vm_events(unsigned long *ret, const struct cpumask *cpumask) memset(ret, 0, NR_VM_EVENT_ITEMS * sizeof(unsigned long)); - for_each_cpu_mask_nr(cpu, *cpumask) { + for_each_cpu(cpu, cpumask) { struct vm_event_state *this = &per_cpu(vm_event_states, cpu); for (i = 0; i < NR_VM_EVENT_ITEMS; i++) diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index c51fed4d1af1..bb507e2bb94d 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -312,7 +312,7 @@ svc_pool_map_set_cpumask(struct task_struct *task, unsigned int pidx) switch (m->mode) { case SVC_POOL_PERCPU: { - set_cpus_allowed_ptr(task, &cpumask_of_cpu(node)); + set_cpus_allowed_ptr(task, cpumask_of(node)); break; } case SVC_POOL_PERNODE: -- cgit v1.2.3 From bb75efddeaca89f8a67fd82cdcbaaf436cf17ca9 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 29 Mar 2009 17:12:22 +0100 Subject: oprofile: Thou shalt not call __exit functions from __init functions Impact: fix ref to discarded function `buffer_sync_cleanup' referenced in section `.init.text' of arch/arm/oprofile/built-in.o: defined in discarded section `.exit.text' of arch/arm/oprofile/built-in.o Signed-off-by: Russell King Signed-off-by: Rusty Russell --- drivers/oprofile/buffer_sync.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/oprofile/buffer_sync.c b/drivers/oprofile/buffer_sync.c index c3ea5fa7d05a..2c9aa49e43cd 100644 --- a/drivers/oprofile/buffer_sync.c +++ b/drivers/oprofile/buffer_sync.c @@ -574,7 +574,7 @@ int __init buffer_sync_init(void) return 0; } -void __exit buffer_sync_cleanup(void) +void buffer_sync_cleanup(void) { free_cpumask_var(marked_cpus); } -- cgit v1.2.3 From eeafda70bf2807544e96fa4e52b2433cd470ff46 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Sun, 29 Mar 2009 12:30:05 -0700 Subject: PCI: fix HT MSI mapping fix Impact: fix bug This patch reworks the nv_msi_ht_cap_quirk() and will only try to avoid to enable ht_msi on device following that root dev, and don't touch that root dev, but only do that trick with end_device on the chain. Reported-by: Prakash Punnoor Tested-by: Prakash Punnoor Signed-off-by: Yinghai Lu Signed-off-by: Jesse Barnes --- drivers/pci/quirks.c | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index faf02dd00015..9b2f0d96900d 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -2206,6 +2206,33 @@ static int __devinit host_bridge_with_leaf(struct pci_dev *host_bridge) return found; } +#define PCI_HT_CAP_SLAVE_CTRL0 4 /* link control */ +#define PCI_HT_CAP_SLAVE_CTRL1 8 /* link control to */ + +static int __devinit is_end_of_ht_chain(struct pci_dev *dev) +{ + int pos, ctrl_off; + int end = 0; + u16 flags, ctrl; + + pos = pci_find_ht_capability(dev, HT_CAPTYPE_SLAVE); + + if (!pos) + goto out; + + pci_read_config_word(dev, pos + PCI_CAP_FLAGS, &flags); + + ctrl_off = ((flags >> 10) & 1) ? + PCI_HT_CAP_SLAVE_CTRL0 : PCI_HT_CAP_SLAVE_CTRL1; + pci_read_config_word(dev, pos + ctrl_off, &ctrl); + + if (ctrl & (1 << 6)) + end = 1; + +out: + return end; +} + static void __devinit nv_ht_enable_msi_mapping(struct pci_dev *dev) { struct pci_dev *host_bridge; @@ -2230,8 +2257,9 @@ static void __devinit nv_ht_enable_msi_mapping(struct pci_dev *dev) if (!found) return; - /* don't enable host_bridge with leaf directly here */ - if (host_bridge == dev && host_bridge_with_leaf(host_bridge)) + /* don't enable end_device/host_bridge with leaf directly here */ + if (host_bridge == dev && is_end_of_ht_chain(host_bridge) && + host_bridge_with_leaf(host_bridge)) goto out; /* root did that ! */ -- cgit v1.2.3 From 9202add67454c6a6f9508f41e733edce705dc56e Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Mon, 30 Mar 2009 21:46:40 +0200 Subject: hwmon: (ds1621) Reorder code statements Reorder the ds1621 driver code so that we can get rid of forward function declarations. Signed-off-by: Jean Delvare Cc: Aurelien Jarno --- drivers/hwmon/ds1621.c | 123 +++++++++++++++++++++++-------------------------- 1 file changed, 57 insertions(+), 66 deletions(-) (limited to 'drivers') diff --git a/drivers/hwmon/ds1621.c b/drivers/hwmon/ds1621.c index 7415381601c3..e688798e115f 100644 --- a/drivers/hwmon/ds1621.c +++ b/drivers/hwmon/ds1621.c @@ -81,34 +81,6 @@ struct ds1621_data { u8 conf; /* Register encoding, combined */ }; -static int ds1621_probe(struct i2c_client *client, - const struct i2c_device_id *id); -static int ds1621_detect(struct i2c_client *client, int kind, - struct i2c_board_info *info); -static void ds1621_init_client(struct i2c_client *client); -static int ds1621_remove(struct i2c_client *client); -static struct ds1621_data *ds1621_update_client(struct device *dev); - -static const struct i2c_device_id ds1621_id[] = { - { "ds1621", ds1621 }, - { "ds1625", ds1621 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, ds1621_id); - -/* This is the driver that will be inserted */ -static struct i2c_driver ds1621_driver = { - .class = I2C_CLASS_HWMON, - .driver = { - .name = "ds1621", - }, - .probe = ds1621_probe, - .remove = ds1621_remove, - .id_table = ds1621_id, - .detect = ds1621_detect, - .address_data = &addr_data, -}; - /* All registers are word-sized, except for the configuration register. DS1621 uses a high-byte first convention, which is exactly opposite to the SMBus standard. */ @@ -146,6 +118,45 @@ static void ds1621_init_client(struct i2c_client *client) i2c_smbus_write_byte(client, DS1621_COM_START); } +static struct ds1621_data *ds1621_update_client(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ds1621_data *data = i2c_get_clientdata(client); + u8 new_conf; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + int i; + + dev_dbg(&client->dev, "Starting ds1621 update\n"); + + data->conf = ds1621_read_value(client, DS1621_REG_CONF); + + for (i = 0; i < ARRAY_SIZE(data->temp); i++) + data->temp[i] = ds1621_read_value(client, + DS1621_REG_TEMP[i]); + + /* reset alarms if necessary */ + new_conf = data->conf; + if (data->temp[0] > data->temp[1]) /* input > min */ + new_conf &= ~DS1621_ALARM_TEMP_LOW; + if (data->temp[0] < data->temp[2]) /* input < max */ + new_conf &= ~DS1621_ALARM_TEMP_HIGH; + if (data->conf != new_conf) + ds1621_write_value(client, DS1621_REG_CONF, + new_conf); + + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + + return data; +} + static ssize_t show_temp(struct device *dev, struct device_attribute *da, char *buf) { @@ -294,45 +305,25 @@ static int ds1621_remove(struct i2c_client *client) return 0; } +static const struct i2c_device_id ds1621_id[] = { + { "ds1621", ds1621 }, + { "ds1625", ds1621 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ds1621_id); -static struct ds1621_data *ds1621_update_client(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct ds1621_data *data = i2c_get_clientdata(client); - u8 new_conf; - - mutex_lock(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ + HZ / 2) - || !data->valid) { - int i; - - dev_dbg(&client->dev, "Starting ds1621 update\n"); - - data->conf = ds1621_read_value(client, DS1621_REG_CONF); - - for (i = 0; i < ARRAY_SIZE(data->temp); i++) - data->temp[i] = ds1621_read_value(client, - DS1621_REG_TEMP[i]); - - /* reset alarms if necessary */ - new_conf = data->conf; - if (data->temp[0] > data->temp[1]) /* input > min */ - new_conf &= ~DS1621_ALARM_TEMP_LOW; - if (data->temp[0] < data->temp[2]) /* input < max */ - new_conf &= ~DS1621_ALARM_TEMP_HIGH; - if (data->conf != new_conf) - ds1621_write_value(client, DS1621_REG_CONF, - new_conf); - - data->last_updated = jiffies; - data->valid = 1; - } - - mutex_unlock(&data->update_lock); - - return data; -} +/* This is the driver that will be inserted */ +static struct i2c_driver ds1621_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "ds1621", + }, + .probe = ds1621_probe, + .remove = ds1621_remove, + .id_table = ds1621_id, + .detect = ds1621_detect, + .address_data = &addr_data, +}; static int __init ds1621_init(void) { -- cgit v1.2.3 From 594592dc6f68356a3b7278eb4d260a50a66f0a06 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Mon, 30 Mar 2009 21:46:40 +0200 Subject: hwmon: (ds1621) Clean up register access Fix a few oddities in how the ds1621 driver accesses the registers: * We don't need a wrapper to access the configuration register. * Check for error before calling swab16. Error checking isn't complete yet, but that's a start. * Device-specific read functions should never be called during detection, as by definition we don't know what device we are talking to at that point. * Likewise, don't assume that register reads succeed during detection. Signed-off-by: Jean Delvare Cc: Aurelien Jarno --- drivers/hwmon/ds1621.c | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/hwmon/ds1621.c b/drivers/hwmon/ds1621.c index e688798e115f..fe160c54b959 100644 --- a/drivers/hwmon/ds1621.c +++ b/drivers/hwmon/ds1621.c @@ -81,28 +81,27 @@ struct ds1621_data { u8 conf; /* Register encoding, combined */ }; -/* All registers are word-sized, except for the configuration register. +/* Temperature registers are word-sized. DS1621 uses a high-byte first convention, which is exactly opposite to the SMBus standard. */ -static int ds1621_read_value(struct i2c_client *client, u8 reg) +static int ds1621_read_temp(struct i2c_client *client, u8 reg) { - if (reg == DS1621_REG_CONF) - return i2c_smbus_read_byte_data(client, reg); - else - return swab16(i2c_smbus_read_word_data(client, reg)); + int ret; + + ret = i2c_smbus_read_word_data(client, reg); + if (ret < 0) + return ret; + return swab16(ret); } -static int ds1621_write_value(struct i2c_client *client, u8 reg, u16 value) +static int ds1621_write_temp(struct i2c_client *client, u8 reg, u16 value) { - if (reg == DS1621_REG_CONF) - return i2c_smbus_write_byte_data(client, reg, value); - else - return i2c_smbus_write_word_data(client, reg, swab16(value)); + return i2c_smbus_write_word_data(client, reg, swab16(value)); } static void ds1621_init_client(struct i2c_client *client) { - int reg = ds1621_read_value(client, DS1621_REG_CONF); + int reg = i2c_smbus_read_byte_data(client, DS1621_REG_CONF); /* switch to continuous conversion mode */ reg &= ~ DS1621_REG_CONFIG_1SHOT; @@ -112,7 +111,7 @@ static void ds1621_init_client(struct i2c_client *client) else if (polarity == 1) reg |= DS1621_REG_CONFIG_POLARITY; - ds1621_write_value(client, DS1621_REG_CONF, reg); + i2c_smbus_write_byte_data(client, DS1621_REG_CONF, reg); /* start conversion */ i2c_smbus_write_byte(client, DS1621_COM_START); @@ -132,11 +131,11 @@ static struct ds1621_data *ds1621_update_client(struct device *dev) dev_dbg(&client->dev, "Starting ds1621 update\n"); - data->conf = ds1621_read_value(client, DS1621_REG_CONF); + data->conf = i2c_smbus_read_byte_data(client, DS1621_REG_CONF); for (i = 0; i < ARRAY_SIZE(data->temp); i++) - data->temp[i] = ds1621_read_value(client, - DS1621_REG_TEMP[i]); + data->temp[i] = ds1621_read_temp(client, + DS1621_REG_TEMP[i]); /* reset alarms if necessary */ new_conf = data->conf; @@ -145,8 +144,8 @@ static struct ds1621_data *ds1621_update_client(struct device *dev) if (data->temp[0] < data->temp[2]) /* input < max */ new_conf &= ~DS1621_ALARM_TEMP_HIGH; if (data->conf != new_conf) - ds1621_write_value(client, DS1621_REG_CONF, - new_conf); + i2c_smbus_write_byte_data(client, DS1621_REG_CONF, + new_conf); data->last_updated = jiffies; data->valid = 1; @@ -176,8 +175,8 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *da, mutex_lock(&data->update_lock); data->temp[attr->index] = val; - ds1621_write_value(client, DS1621_REG_TEMP[attr->index], - data->temp[attr->index]); + ds1621_write_temp(client, DS1621_REG_TEMP[attr->index], + data->temp[attr->index]); mutex_unlock(&data->update_lock); return count; } @@ -239,13 +238,14 @@ static int ds1621_detect(struct i2c_client *client, int kind, /* The NVB bit should be low if no EEPROM write has been requested during the latest 10ms, which is highly improbable in our case. */ - conf = ds1621_read_value(client, DS1621_REG_CONF); - if (conf & DS1621_REG_CONFIG_NVB) + conf = i2c_smbus_read_byte_data(client, DS1621_REG_CONF); + if (conf < 0 || conf & DS1621_REG_CONFIG_NVB) return -ENODEV; /* The 7 lowest bits of a temperature should always be 0. */ for (i = 0; i < ARRAY_SIZE(DS1621_REG_TEMP); i++) { - temp = ds1621_read_value(client, DS1621_REG_TEMP[i]); - if (temp & 0x007f) + temp = i2c_smbus_read_word_data(client, + DS1621_REG_TEMP[i]); + if (temp < 0 || (temp & 0x7f00)) return -ENODEV; } } -- cgit v1.2.3 From e4879e28abd67b894fb9d2db0afd08f1945670ba Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Mon, 30 Mar 2009 21:46:40 +0200 Subject: hwmon: (ds1621) Avoid unneeded register access Register access over SMBus isn't cheap, so avoid register access where possible: * Only write back the configuration register if it changed. * Don't refresh the register cache when we don't have to. Signed-off-by: Jean Delvare Cc: Aurelien Jarno --- drivers/hwmon/ds1621.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/hwmon/ds1621.c b/drivers/hwmon/ds1621.c index fe160c54b959..53f88f511816 100644 --- a/drivers/hwmon/ds1621.c +++ b/drivers/hwmon/ds1621.c @@ -101,17 +101,20 @@ static int ds1621_write_temp(struct i2c_client *client, u8 reg, u16 value) static void ds1621_init_client(struct i2c_client *client) { - int reg = i2c_smbus_read_byte_data(client, DS1621_REG_CONF); + u8 conf, new_conf; + + new_conf = conf = i2c_smbus_read_byte_data(client, DS1621_REG_CONF); /* switch to continuous conversion mode */ - reg &= ~ DS1621_REG_CONFIG_1SHOT; + new_conf &= ~DS1621_REG_CONFIG_1SHOT; /* setup output polarity */ if (polarity == 0) - reg &= ~DS1621_REG_CONFIG_POLARITY; + new_conf &= ~DS1621_REG_CONFIG_POLARITY; else if (polarity == 1) - reg |= DS1621_REG_CONFIG_POLARITY; + new_conf |= DS1621_REG_CONFIG_POLARITY; - i2c_smbus_write_byte_data(client, DS1621_REG_CONF, reg); + if (conf != new_conf) + i2c_smbus_write_byte_data(client, DS1621_REG_CONF, new_conf); /* start conversion */ i2c_smbus_write_byte(client, DS1621_COM_START); @@ -170,7 +173,7 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *da, { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); struct i2c_client *client = to_i2c_client(dev); - struct ds1621_data *data = ds1621_update_client(dev); + struct ds1621_data *data = i2c_get_clientdata(client); u16 val = LM75_TEMP_TO_REG(simple_strtol(buf, NULL, 10)); mutex_lock(&data->update_lock); -- cgit v1.2.3 From 2b8cf3e8c0638687a7a28a7517e673f855623e3b Mon Sep 17 00:00:00 2001 From: Frank Seidel Date: Mon, 30 Mar 2009 21:46:41 +0200 Subject: hwmon: (hdaps) Allow inversion of separate axis Fix for kernel.org bug #7154: hdaps inversion of each axis. This version is based on the work from Michael Ruoss . Signed-off-by: Frank Seidel Signed-off-by: Jean Delvare --- drivers/hwmon/hdaps.c | 64 ++++++++++++++++++++++++++------------------------- 1 file changed, 33 insertions(+), 31 deletions(-) (limited to 'drivers') diff --git a/drivers/hwmon/hdaps.c b/drivers/hwmon/hdaps.c index a4d92d246d52..dd058e06e344 100644 --- a/drivers/hwmon/hdaps.c +++ b/drivers/hwmon/hdaps.c @@ -65,6 +65,10 @@ #define HDAPS_INPUT_FUZZ 4 /* input event threshold */ #define HDAPS_INPUT_FLAT 4 +#define HDAPS_X_AXIS (1 << 0) +#define HDAPS_Y_AXIS (1 << 1) +#define HDAPS_BOTH_AXES (HDAPS_X_AXIS | HDAPS_Y_AXIS) + static struct platform_device *pdev; static struct input_polled_dev *hdaps_idev; static unsigned int hdaps_invert; @@ -182,11 +186,11 @@ static int __hdaps_read_pair(unsigned int port1, unsigned int port2, km_activity = inb(HDAPS_PORT_KMACT); __device_complete(); - /* if hdaps_invert is set, negate the two values */ - if (hdaps_invert) { + /* hdaps_invert is a bitvector to negate the axes */ + if (hdaps_invert & HDAPS_X_AXIS) *x = -*x; + if (hdaps_invert & HDAPS_Y_AXIS) *y = -*y; - } return 0; } @@ -436,7 +440,8 @@ static ssize_t hdaps_invert_store(struct device *dev, { int invert; - if (sscanf(buf, "%d", &invert) != 1 || (invert != 1 && invert != 0)) + if (sscanf(buf, "%d", &invert) != 1 || + invert < 0 || invert > HDAPS_BOTH_AXES) return -EINVAL; hdaps_invert = invert; @@ -483,56 +488,52 @@ static int __init hdaps_dmi_match(const struct dmi_system_id *id) /* hdaps_dmi_match_invert - found an inverted match. */ static int __init hdaps_dmi_match_invert(const struct dmi_system_id *id) { - hdaps_invert = 1; - printk(KERN_INFO "hdaps: inverting axis readings.\n"); + hdaps_invert = (unsigned long)id->driver_data; + printk(KERN_INFO "hdaps: inverting axis (%u) readings.\n", + hdaps_invert); return hdaps_dmi_match(id); } -#define HDAPS_DMI_MATCH_NORMAL(vendor, model) { \ - .ident = vendor " " model, \ - .callback = hdaps_dmi_match, \ - .matches = { \ - DMI_MATCH(DMI_BOARD_VENDOR, vendor), \ - DMI_MATCH(DMI_PRODUCT_VERSION, model) \ - } \ -} - -#define HDAPS_DMI_MATCH_INVERT(vendor, model) { \ +#define HDAPS_DMI_MATCH_INVERT(vendor, model, axes) { \ .ident = vendor " " model, \ .callback = hdaps_dmi_match_invert, \ + .driver_data = (void *)axes, \ .matches = { \ DMI_MATCH(DMI_BOARD_VENDOR, vendor), \ DMI_MATCH(DMI_PRODUCT_VERSION, model) \ } \ } +#define HDAPS_DMI_MATCH_NORMAL(vendor, model) \ + HDAPS_DMI_MATCH_INVERT(vendor, model, 0) + /* Note that HDAPS_DMI_MATCH_NORMAL("ThinkPad T42") would match "ThinkPad T42p", so the order of the entries matters. If your ThinkPad is not recognized, please update to latest BIOS. This is especially the case for some R52 ThinkPads. */ static struct dmi_system_id __initdata hdaps_whitelist[] = { - HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad R50p"), + HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad R50p", HDAPS_BOTH_AXES), HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R50"), HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R51"), HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R52"), - HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R61i"), - HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R61"), - HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T41p"), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R61i", HDAPS_BOTH_AXES), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R61", HDAPS_BOTH_AXES), + HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T41p", HDAPS_BOTH_AXES), HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T41"), - HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T42p"), + HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T42p", HDAPS_BOTH_AXES), HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T42"), HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T43"), - HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T60"), - HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T61p"), - HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T61"), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T60", HDAPS_BOTH_AXES), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T61p", HDAPS_BOTH_AXES), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T61", HDAPS_BOTH_AXES), HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad X40"), HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad X41"), - HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X60"), - HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X61s"), - HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X61"), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X60", HDAPS_BOTH_AXES), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X61s", HDAPS_BOTH_AXES), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X61", HDAPS_BOTH_AXES), HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad Z60m"), - HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad Z61m"), - HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad Z61p"), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad Z61m", HDAPS_BOTH_AXES), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad Z61p", HDAPS_BOTH_AXES), { .ident = NULL } }; @@ -627,8 +628,9 @@ static void __exit hdaps_exit(void) module_init(hdaps_init); module_exit(hdaps_exit); -module_param_named(invert, hdaps_invert, bool, 0); -MODULE_PARM_DESC(invert, "invert data along each axis"); +module_param_named(invert, hdaps_invert, int, 0); +MODULE_PARM_DESC(invert, "invert data along each axis. 1 invert x-axis, " + "2 invert y-axis, 3 invert both axes."); MODULE_AUTHOR("Robert Love"); MODULE_DESCRIPTION("IBM Hard Drive Active Protection System (HDAPS) driver"); -- cgit v1.2.3 From b6a33fe2cc1b44851174967943fe5989f7e0550f Mon Sep 17 00:00:00 2001 From: Frank Seidel Date: Mon, 30 Mar 2009 21:46:42 +0200 Subject: hwmon: (hdaps) Fix Thinkpad X41 axis inversion Fix for kernel.org bug #7154: hdaps inversion of actual Thinkpad X41's Y-axis. Signed-off-by: Frank Seidel Signed-off-by: Jean Delvare --- drivers/hwmon/hdaps.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hwmon/hdaps.c b/drivers/hwmon/hdaps.c index dd058e06e344..d3612a1f1981 100644 --- a/drivers/hwmon/hdaps.c +++ b/drivers/hwmon/hdaps.c @@ -527,7 +527,7 @@ static struct dmi_system_id __initdata hdaps_whitelist[] = { HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T61p", HDAPS_BOTH_AXES), HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T61", HDAPS_BOTH_AXES), HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad X40"), - HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad X41"), + HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad X41", HDAPS_Y_AXIS), HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X60", HDAPS_BOTH_AXES), HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X61s", HDAPS_BOTH_AXES), HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X61", HDAPS_BOTH_AXES), -- cgit v1.2.3 From 1704b26ee3fd89c76724cbea238e951dc019faca Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Mon, 30 Mar 2009 21:46:42 +0200 Subject: hwmon: (w83627ehf) Invert fan pin variables logic Use positive logic for fan pin variables (variable is set if pin is used for fan), instead of negative logic which is error prone. Signed-off-by: Jean Delvare Cc: Gong Jun --- drivers/hwmon/w83627ehf.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c index feae743ba991..18432e34dc7a 100644 --- a/drivers/hwmon/w83627ehf.c +++ b/drivers/hwmon/w83627ehf.c @@ -1317,8 +1317,8 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) /* fan4 and fan5 share some pins with the GPIO and serial flash */ - fan5pin = superio_inb(sio_data->sioreg, 0x24) & 0x2; - fan4pin = superio_inb(sio_data->sioreg, 0x29) & 0x6; + fan5pin = !(superio_inb(sio_data->sioreg, 0x24) & 0x2); + fan4pin = !(superio_inb(sio_data->sioreg, 0x29) & 0x6); superio_exit(sio_data->sioreg); /* It looks like fan4 and fan5 pins can be alternatively used @@ -1329,9 +1329,9 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) data->has_fan = 0x07; /* fan1, fan2 and fan3 */ i = w83627ehf_read_value(data, W83627EHF_REG_FANDIV1); - if ((i & (1 << 2)) && (!fan4pin)) + if ((i & (1 << 2)) && fan4pin) data->has_fan |= (1 << 3); - if (!(i & (1 << 1)) && (!fan5pin)) + if (!(i & (1 << 1)) && fan5pin) data->has_fan |= (1 << 4); /* Read fan clock dividers immediately */ -- cgit v1.2.3 From 237c8d2f54ff12bd4fea1a9d18a94ae5810271d3 Mon Sep 17 00:00:00 2001 From: Gong Jun Date: Mon, 30 Mar 2009 21:46:42 +0200 Subject: hwmon: (w83627ehf) Add support for W83667HG Add initial support for the Nuvoton W83667HG chip to the w83627ehf driver. It has been tested on ASUS P5QL PRO by Gong Jun. At the moment there is still a usability issue which is that only in6 or temp3 can be present on the W83667HG, so the driver shouldn't expose both. This will be addressed later. Signed-off-by: Gong Jun Acked-by: David Hubbard Signed-off-by: Jean Delvare --- Documentation/hwmon/w83627ehf | 29 ++++++++---- drivers/hwmon/Kconfig | 4 +- drivers/hwmon/w83627ehf.c | 106 +++++++++++++++++++++++++++--------------- 3 files changed, 92 insertions(+), 47 deletions(-) (limited to 'drivers') diff --git a/Documentation/hwmon/w83627ehf b/Documentation/hwmon/w83627ehf index d6e1ae30fa6e..b6eb59384bb3 100644 --- a/Documentation/hwmon/w83627ehf +++ b/Documentation/hwmon/w83627ehf @@ -2,30 +2,40 @@ Kernel driver w83627ehf ======================= Supported chips: - * Winbond W83627EHF/EHG/DHG (ISA access ONLY) + * Winbond W83627EHF/EHG (ISA access ONLY) Prefix: 'w83627ehf' Addresses scanned: ISA address retrieved from Super I/O registers Datasheet: - http://www.winbond-usa.com/products/winbond_products/pdfs/PCIC/W83627EHF_%20W83627EHGb.pdf - DHG datasheet confidential. + http://www.nuvoton.com.tw/NR/rdonlyres/A6A258F0-F0C9-4F97-81C0-C4D29E7E943E/0/W83627EHF.pdf + * Winbond W83627DHG + Prefix: 'w83627dhg' + Addresses scanned: ISA address retrieved from Super I/O registers + Datasheet: + http://www.nuvoton.com.tw/NR/rdonlyres/7885623D-A487-4CF9-A47F-30C5F73D6FE6/0/W83627DHG.pdf + * Winbond W83667HG + Prefix: 'w83667hg' + Addresses scanned: ISA address retrieved from Super I/O registers + Datasheet: not available Authors: Jean Delvare Yuan Mu (Winbond) Rudolf Marek David Hubbard + Gong Jun Description ----------- -This driver implements support for the Winbond W83627EHF, W83627EHG, and -W83627DHG super I/O chips. We will refer to them collectively as Winbond chips. +This driver implements support for the Winbond W83627EHF, W83627EHG, +W83627DHG and W83667HG super I/O chips. We will refer to them collectively +as Winbond chips. The chips implement three temperature sensors, five fan rotation speed sensors, ten analog voltage sensors (only nine for the 627DHG), one -VID (6 pins for the 627EHF/EHG, 8 pins for the 627DHG), alarms with beep -warnings (control unimplemented), and some automatic fan regulation -strategies (plus manual fan control mode). +VID (6 pins for the 627EHF/EHG, 8 pins for the 627DHG and 667HG), alarms +with beep warnings (control unimplemented), and some automatic fan +regulation strategies (plus manual fan control mode). Temperatures are measured in degrees Celsius and measurement resolution is 1 degC for temp1 and 0.5 degC for temp2 and temp3. An alarm is triggered when @@ -54,7 +64,8 @@ follows: temp1 -> pwm1 temp2 -> pwm2 temp3 -> pwm3 -prog -> pwm4 (the programmable setting is not supported by the driver) +prog -> pwm4 (not on 667HG; the programmable setting is not supported by + the driver) /sys files ---------- diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index b4eea0292c1a..0eb4170cc4af 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -827,7 +827,7 @@ config SENSORS_W83627HF will be called w83627hf. config SENSORS_W83627EHF - tristate "Winbond W83627EHF/DHG" + tristate "Winbond W83627EHF/EHG/DHG, W83667HG" select HWMON_VID help If you say yes here you get support for the hardware @@ -838,6 +838,8 @@ config SENSORS_W83627EHF chip suited for specific Intel processors that use PECI such as the Core 2 Duo. + This driver also supports the W83667HG chip. + This driver can also be built as a module. If so, the module will be called w83627ehf. diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c index 18432e34dc7a..20a9332959bb 100644 --- a/drivers/hwmon/w83627ehf.c +++ b/drivers/hwmon/w83627ehf.c @@ -36,6 +36,7 @@ w83627ehf 10 5 4 3 0x8850 0x88 0x5ca3 0x8860 0xa1 w83627dhg 9 5 4 3 0xa020 0xc1 0x5ca3 + w83667hg 9 5 3 3 0xa510 0xc1 0x5ca3 */ #include @@ -52,12 +53,13 @@ #include #include "lm75.h" -enum kinds { w83627ehf, w83627dhg }; +enum kinds { w83627ehf, w83627dhg, w83667hg }; /* used to set data->name = w83627ehf_device_names[data->sio_kind] */ static const char * w83627ehf_device_names[] = { "w83627ehf", "w83627dhg", + "w83667hg", }; static unsigned short force_id; @@ -71,6 +73,7 @@ MODULE_PARM_DESC(force_id, "Override the detected device ID"); */ #define W83627EHF_LD_HWM 0x0b +#define W83667HG_LD_VID 0x0d #define SIO_REG_LDSEL 0x07 /* Logical device select */ #define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */ @@ -83,6 +86,7 @@ MODULE_PARM_DESC(force_id, "Override the detected device ID"); #define SIO_W83627EHF_ID 0x8850 #define SIO_W83627EHG_ID 0x8860 #define SIO_W83627DHG_ID 0xa020 +#define SIO_W83667HG_ID 0xa510 #define SIO_ID_MASK 0xFFF0 static inline void @@ -289,6 +293,7 @@ struct w83627ehf_data { u8 pwm_mode[4]; /* 0->DC variable voltage, 1->PWM variable duty cycle */ u8 pwm_enable[4]; /* 1->manual 2->thermal cruise (also called SmartFan I) */ + u8 pwm_num; /* number of pwm */ u8 pwm[4]; u8 target_temp[4]; u8 tolerance[4]; @@ -1192,7 +1197,7 @@ static void w83627ehf_device_remove_files(struct device *dev) device_remove_file(dev, &sda_fan_div[i].dev_attr); device_remove_file(dev, &sda_fan_min[i].dev_attr); } - for (i = 0; i < 4; i++) { + for (i = 0; i < data->pwm_num; i++) { device_remove_file(dev, &sda_pwm[i].dev_attr); device_remove_file(dev, &sda_pwm_mode[i].dev_attr); device_remove_file(dev, &sda_pwm_enable[i].dev_attr); @@ -1272,8 +1277,10 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) data->name = w83627ehf_device_names[sio_data->kind]; platform_set_drvdata(pdev, data); - /* 627EHG and 627EHF have 10 voltage inputs; DHG has 9 */ - data->in_num = (sio_data->kind == w83627dhg) ? 9 : 10; + /* 627EHG and 627EHF have 10 voltage inputs; 627DHG and 667HG have 9 */ + data->in_num = (sio_data->kind == w83627ehf) ? 10 : 9; + /* 667HG has 3 pwms */ + data->pwm_num = (sio_data->kind == w83667hg) ? 3 : 4; /* Initialize the chip */ w83627ehf_init_device(data); @@ -1281,44 +1288,64 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) data->vrm = vid_which_vrm(); superio_enter(sio_data->sioreg); /* Read VID value */ - superio_select(sio_data->sioreg, W83627EHF_LD_HWM); - if (superio_inb(sio_data->sioreg, SIO_REG_VID_CTRL) & 0x80) { - /* Set VID input sensibility if needed. In theory the BIOS - should have set it, but in practice it's not always the - case. We only do it for the W83627EHF/EHG because the - W83627DHG is more complex in this respect. */ - if (sio_data->kind == w83627ehf) { - en_vrm10 = superio_inb(sio_data->sioreg, - SIO_REG_EN_VRM10); - if ((en_vrm10 & 0x08) && data->vrm == 90) { - dev_warn(dev, "Setting VID input voltage to " - "TTL\n"); - superio_outb(sio_data->sioreg, SIO_REG_EN_VRM10, - en_vrm10 & ~0x08); - } else if (!(en_vrm10 & 0x08) && data->vrm == 100) { - dev_warn(dev, "Setting VID input voltage to " - "VRM10\n"); - superio_outb(sio_data->sioreg, SIO_REG_EN_VRM10, - en_vrm10 | 0x08); - } - } - - data->vid = superio_inb(sio_data->sioreg, SIO_REG_VID_DATA); - if (sio_data->kind == w83627ehf) /* 6 VID pins only */ - data->vid &= 0x3f; - + if (sio_data->kind == w83667hg) { + /* W83667HG has different pins for VID input and output, so + we can get the VID input values directly at logical device D + 0xe3. */ + superio_select(sio_data->sioreg, W83667HG_LD_VID); + data->vid = superio_inb(sio_data->sioreg, 0xe3); err = device_create_file(dev, &dev_attr_cpu0_vid); if (err) goto exit_release; } else { - dev_info(dev, "VID pins in output mode, CPU VID not " - "available\n"); + superio_select(sio_data->sioreg, W83627EHF_LD_HWM); + if (superio_inb(sio_data->sioreg, SIO_REG_VID_CTRL) & 0x80) { + /* Set VID input sensibility if needed. In theory the + BIOS should have set it, but in practice it's not + always the case. We only do it for the W83627EHF/EHG + because the W83627DHG is more complex in this + respect. */ + if (sio_data->kind == w83627ehf) { + en_vrm10 = superio_inb(sio_data->sioreg, + SIO_REG_EN_VRM10); + if ((en_vrm10 & 0x08) && data->vrm == 90) { + dev_warn(dev, "Setting VID input " + "voltage to TTL\n"); + superio_outb(sio_data->sioreg, + SIO_REG_EN_VRM10, + en_vrm10 & ~0x08); + } else if (!(en_vrm10 & 0x08) + && data->vrm == 100) { + dev_warn(dev, "Setting VID input " + "voltage to VRM10\n"); + superio_outb(sio_data->sioreg, + SIO_REG_EN_VRM10, + en_vrm10 | 0x08); + } + } + + data->vid = superio_inb(sio_data->sioreg, + SIO_REG_VID_DATA); + if (sio_data->kind == w83627ehf) /* 6 VID pins only */ + data->vid &= 0x3f; + + err = device_create_file(dev, &dev_attr_cpu0_vid); + if (err) + goto exit_release; + } else { + dev_info(dev, "VID pins in output mode, CPU VID not " + "available\n"); + } } /* fan4 and fan5 share some pins with the GPIO and serial flash */ - - fan5pin = !(superio_inb(sio_data->sioreg, 0x24) & 0x2); - fan4pin = !(superio_inb(sio_data->sioreg, 0x29) & 0x6); + if (sio_data->kind == w83667hg) { + fan5pin = superio_inb(sio_data->sioreg, 0x27) & 0x20; + fan4pin = superio_inb(sio_data->sioreg, 0x27) & 0x40; + } else { + fan5pin = !(superio_inb(sio_data->sioreg, 0x24) & 0x02); + fan4pin = !(superio_inb(sio_data->sioreg, 0x29) & 0x06); + } superio_exit(sio_data->sioreg); /* It looks like fan4 and fan5 pins can be alternatively used @@ -1344,7 +1371,7 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) goto exit_remove; /* if fan4 is enabled create the sf3 files for it */ - if (data->has_fan & (1 << 3)) + if ((data->has_fan & (1 << 3)) && data->pwm_num >= 4) for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++) { if ((err = device_create_file(dev, &sda_sf3_arrays_fan4[i].dev_attr))) @@ -1372,7 +1399,7 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) || (err = device_create_file(dev, &sda_fan_min[i].dev_attr))) goto exit_remove; - if (i < 4 && /* w83627ehf only has 4 pwm */ + if (i < data->pwm_num && ((err = device_create_file(dev, &sda_pwm[i].dev_attr)) || (err = device_create_file(dev, @@ -1442,6 +1469,7 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr, static const char __initdata sio_name_W83627EHF[] = "W83627EHF"; static const char __initdata sio_name_W83627EHG[] = "W83627EHG"; static const char __initdata sio_name_W83627DHG[] = "W83627DHG"; + static const char __initdata sio_name_W83667HG[] = "W83667HG"; u16 val; const char *sio_name; @@ -1466,6 +1494,10 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr, sio_data->kind = w83627dhg; sio_name = sio_name_W83627DHG; break; + case SIO_W83667HG_ID: + sio_data->kind = w83667hg; + sio_name = sio_name_W83667HG; + break; default: if (val != 0xffff) pr_debug(DRVNAME ": unsupported chip ID: 0x%04x\n", -- cgit v1.2.3 From a157d06d4d70318a0818552095071d7430dd5d34 Mon Sep 17 00:00:00 2001 From: Gong Jun Date: Mon, 30 Mar 2009 21:46:43 +0200 Subject: hwmon: (w83627ehf) Only expose in6 or temp3 on the W83667HG The pin for in6 and temp3 is shared on the W83667HG, so only one of these features can be supported on any given system. Let the driver select which one depending on the temp3 disabled bit. Signed-off-by: Gong Jun Signed-off-by: Jean Delvare --- drivers/hwmon/w83627ehf.c | 60 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c index 20a9332959bb..e64b42058b21 100644 --- a/drivers/hwmon/w83627ehf.c +++ b/drivers/hwmon/w83627ehf.c @@ -303,6 +303,9 @@ struct w83627ehf_data { u8 vid; u8 vrm; + + u8 temp3_disable; + u8 in6_skip; }; struct w83627ehf_sio_data { @@ -871,25 +874,37 @@ show_temp_type(struct device *dev, struct device_attribute *attr, char *buf) return sprintf(buf, "%d\n", (int)data->temp_type[nr]); } -static struct sensor_device_attribute sda_temp[] = { +static struct sensor_device_attribute sda_temp_input[] = { SENSOR_ATTR(temp1_input, S_IRUGO, show_temp1, NULL, 0), SENSOR_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 0), SENSOR_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 1), +}; + +static struct sensor_device_attribute sda_temp_max[] = { SENSOR_ATTR(temp1_max, S_IRUGO | S_IWUSR, show_temp1_max, store_temp1_max, 0), SENSOR_ATTR(temp2_max, S_IRUGO | S_IWUSR, show_temp_max, store_temp_max, 0), SENSOR_ATTR(temp3_max, S_IRUGO | S_IWUSR, show_temp_max, store_temp_max, 1), +}; + +static struct sensor_device_attribute sda_temp_max_hyst[] = { SENSOR_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR, show_temp1_max_hyst, store_temp1_max_hyst, 0), SENSOR_ATTR(temp2_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst, store_temp_max_hyst, 0), SENSOR_ATTR(temp3_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst, store_temp_max_hyst, 1), +}; + +static struct sensor_device_attribute sda_temp_alarm[] = { SENSOR_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 4), SENSOR_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 5), SENSOR_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 13), +}; + +static struct sensor_device_attribute sda_temp_type[] = { SENSOR_ATTR(temp1_type, S_IRUGO, show_temp_type, NULL, 0), SENSOR_ATTR(temp2_type, S_IRUGO, show_temp_type, NULL, 1), SENSOR_ATTR(temp3_type, S_IRUGO, show_temp_type, NULL, 2), @@ -1186,6 +1201,8 @@ static void w83627ehf_device_remove_files(struct device *dev) for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++) device_remove_file(dev, &sda_sf3_arrays_fan4[i].dev_attr); for (i = 0; i < data->in_num; i++) { + if ((i == 6) && data->in6_skip) + continue; device_remove_file(dev, &sda_in_input[i].dev_attr); device_remove_file(dev, &sda_in_alarm[i].dev_attr); device_remove_file(dev, &sda_in_min[i].dev_attr); @@ -1204,8 +1221,15 @@ static void w83627ehf_device_remove_files(struct device *dev) device_remove_file(dev, &sda_target_temp[i].dev_attr); device_remove_file(dev, &sda_tolerance[i].dev_attr); } - for (i = 0; i < ARRAY_SIZE(sda_temp); i++) - device_remove_file(dev, &sda_temp[i].dev_attr); + for (i = 0; i < 3; i++) { + if ((i == 2) && data->temp3_disable) + continue; + device_remove_file(dev, &sda_temp_input[i].dev_attr); + device_remove_file(dev, &sda_temp_max[i].dev_attr); + device_remove_file(dev, &sda_temp_max_hyst[i].dev_attr); + device_remove_file(dev, &sda_temp_alarm[i].dev_attr); + device_remove_file(dev, &sda_temp_type[i].dev_attr); + } device_remove_file(dev, &dev_attr_name); device_remove_file(dev, &dev_attr_cpu0_vid); @@ -1227,6 +1251,8 @@ static inline void __devinit w83627ehf_init_device(struct w83627ehf_data *data) for (i = 0; i < 2; i++) { tmp = w83627ehf_read_value(data, W83627EHF_REG_TEMP_CONFIG[i]); + if ((i == 1) && data->temp3_disable) + continue; if (tmp & 0x01) w83627ehf_write_value(data, W83627EHF_REG_TEMP_CONFIG[i], @@ -1282,6 +1308,13 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) /* 667HG has 3 pwms */ data->pwm_num = (sio_data->kind == w83667hg) ? 3 : 4; + /* Check temp3 configuration bit for 667HG */ + if (sio_data->kind == w83667hg) { + data->temp3_disable = w83627ehf_read_value(data, + W83627EHF_REG_TEMP_CONFIG[1]) & 0x01; + data->in6_skip = !data->temp3_disable; + } + /* Initialize the chip */ w83627ehf_init_device(data); @@ -1378,7 +1411,9 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) goto exit_remove; } - for (i = 0; i < data->in_num; i++) + for (i = 0; i < data->in_num; i++) { + if ((i == 6) && data->in6_skip) + continue; if ((err = device_create_file(dev, &sda_in_input[i].dev_attr)) || (err = device_create_file(dev, &sda_in_alarm[i].dev_attr)) @@ -1387,6 +1422,7 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) || (err = device_create_file(dev, &sda_in_max[i].dev_attr))) goto exit_remove; + } for (i = 0; i < 5; i++) { if (data->has_fan & (1 << i)) { @@ -1414,9 +1450,21 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) } } - for (i = 0; i < ARRAY_SIZE(sda_temp); i++) - if ((err = device_create_file(dev, &sda_temp[i].dev_attr))) + for (i = 0; i < 3; i++) { + if ((i == 2) && data->temp3_disable) + continue; + if ((err = device_create_file(dev, + &sda_temp_input[i].dev_attr)) + || (err = device_create_file(dev, + &sda_temp_max[i].dev_attr)) + || (err = device_create_file(dev, + &sda_temp_max_hyst[i].dev_attr)) + || (err = device_create_file(dev, + &sda_temp_alarm[i].dev_attr)) + || (err = device_create_file(dev, + &sda_temp_type[i].dev_attr))) goto exit_remove; + } err = device_create_file(dev, &dev_attr_name); if (err) -- cgit v1.2.3 From fb4504fe84b09cbf49fda19e6630a1003d79656a Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Mon, 30 Mar 2009 21:46:43 +0200 Subject: Move the pcf8591 driver to hwmon Directory drivers/i2c/chips is going away, so drivers there must find new homes. For the pcf8591 driver, the best choice seems to be the hwmon subsystem. While the Philips PCF8591 device isn't a typical hardware monitoring chip, its DAC interface is compatible with the hwmon one, so it fits somewhat. If a better subsystem is ever created for ADC/DAC chips, the driver could be moved there. Signed-off-by: Jean Delvare Cc: Aurelien Jarno --- Documentation/hwmon/pcf8591 | 90 +++++++++++ Documentation/i2c/chips/pcf8591 | 90 ----------- drivers/hwmon/Kconfig | 14 ++ drivers/hwmon/Makefile | 1 + drivers/hwmon/pcf8591.c | 325 ++++++++++++++++++++++++++++++++++++++++ drivers/i2c/chips/Kconfig | 13 -- drivers/i2c/chips/Makefile | 1 - drivers/i2c/chips/pcf8591.c | 325 ---------------------------------------- 8 files changed, 430 insertions(+), 429 deletions(-) create mode 100644 Documentation/hwmon/pcf8591 delete mode 100644 Documentation/i2c/chips/pcf8591 create mode 100644 drivers/hwmon/pcf8591.c delete mode 100644 drivers/i2c/chips/pcf8591.c (limited to 'drivers') diff --git a/Documentation/hwmon/pcf8591 b/Documentation/hwmon/pcf8591 new file mode 100644 index 000000000000..5628fcf4207f --- /dev/null +++ b/Documentation/hwmon/pcf8591 @@ -0,0 +1,90 @@ +Kernel driver pcf8591 +===================== + +Supported chips: + * Philips PCF8591 + Prefix: 'pcf8591' + Addresses scanned: I2C 0x48 - 0x4f + Datasheet: Publicly available at the Philips Semiconductor website + http://www.semiconductors.philips.com/pip/PCF8591P.html + +Authors: + Aurelien Jarno + valuable contributions by Jan M. Sendler , + Jean Delvare + + +Description +----------- +The PCF8591 is an 8-bit A/D and D/A converter (4 analog inputs and one +analog output) for the I2C bus produced by Philips Semiconductors. It +is designed to provide a byte I2C interface to up to 4 separate devices. + +The PCF8591 has 4 analog inputs programmable as single-ended or +differential inputs : +- mode 0 : four single ended inputs + Pins AIN0 to AIN3 are single ended inputs for channels 0 to 3 + +- mode 1 : three differential inputs + Pins AIN3 is the common negative differential input + Pins AIN0 to AIN2 are positive differential inputs for channels 0 to 2 + +- mode 2 : single ended and differential mixed + Pins AIN0 and AIN1 are single ended inputs for channels 0 and 1 + Pins AIN2 is the positive differential input for channel 3 + Pins AIN3 is the negative differential input for channel 3 + +- mode 3 : two differential inputs + Pins AIN0 is the positive differential input for channel 0 + Pins AIN1 is the negative differential input for channel 0 + Pins AIN2 is the positive differential input for channel 1 + Pins AIN3 is the negative differential input for channel 1 + +See the datasheet for details. + +Module parameters +----------------- + +* input_mode int + + Analog input mode: + 0 = four single ended inputs + 1 = three differential inputs + 2 = single ended and differential mixed + 3 = two differential inputs + + +Accessing PCF8591 via /sys interface +------------------------------------- + +! Be careful ! +The PCF8591 is plainly impossible to detect ! Stupid chip. +So every chip with address in the interval [48..4f] is +detected as PCF8591. If you have other chips in this address +range, the workaround is to load this module after the one +for your others chips. + +On detection (i.e. insmod, modprobe et al.), directories are being +created for each detected PCF8591: + +/sys/bus/devices/<0>-<1>/ +where <0> is the bus the chip was detected on (e. g. i2c-0) +and <1> the chip address ([48..4f]) + +Inside these directories, there are such files: +in0, in1, in2, in3, out0_enable, out0_output, name + +Name contains chip name. + +The in0, in1, in2 and in3 files are RO. Reading gives the value of the +corresponding channel. Depending on the current analog inputs configuration, +files in2 and/or in3 do not exist. Values range are from 0 to 255 for single +ended inputs and -128 to +127 for differential inputs (8-bit ADC). + +The out0_enable file is RW. Reading gives "1" for analog output enabled and +"0" for analog output disabled. Writing accepts "0" and "1" accordingly. + +The out0_output file is RW. Writing a number between 0 and 255 (8-bit DAC), send +the value to the digital-to-analog converter. Note that a voltage will +only appears on AOUT pin if aout0_enable equals 1. Reading returns the last +value written. diff --git a/Documentation/i2c/chips/pcf8591 b/Documentation/i2c/chips/pcf8591 deleted file mode 100644 index 5628fcf4207f..000000000000 --- a/Documentation/i2c/chips/pcf8591 +++ /dev/null @@ -1,90 +0,0 @@ -Kernel driver pcf8591 -===================== - -Supported chips: - * Philips PCF8591 - Prefix: 'pcf8591' - Addresses scanned: I2C 0x48 - 0x4f - Datasheet: Publicly available at the Philips Semiconductor website - http://www.semiconductors.philips.com/pip/PCF8591P.html - -Authors: - Aurelien Jarno - valuable contributions by Jan M. Sendler , - Jean Delvare - - -Description ------------ -The PCF8591 is an 8-bit A/D and D/A converter (4 analog inputs and one -analog output) for the I2C bus produced by Philips Semiconductors. It -is designed to provide a byte I2C interface to up to 4 separate devices. - -The PCF8591 has 4 analog inputs programmable as single-ended or -differential inputs : -- mode 0 : four single ended inputs - Pins AIN0 to AIN3 are single ended inputs for channels 0 to 3 - -- mode 1 : three differential inputs - Pins AIN3 is the common negative differential input - Pins AIN0 to AIN2 are positive differential inputs for channels 0 to 2 - -- mode 2 : single ended and differential mixed - Pins AIN0 and AIN1 are single ended inputs for channels 0 and 1 - Pins AIN2 is the positive differential input for channel 3 - Pins AIN3 is the negative differential input for channel 3 - -- mode 3 : two differential inputs - Pins AIN0 is the positive differential input for channel 0 - Pins AIN1 is the negative differential input for channel 0 - Pins AIN2 is the positive differential input for channel 1 - Pins AIN3 is the negative differential input for channel 1 - -See the datasheet for details. - -Module parameters ------------------ - -* input_mode int - - Analog input mode: - 0 = four single ended inputs - 1 = three differential inputs - 2 = single ended and differential mixed - 3 = two differential inputs - - -Accessing PCF8591 via /sys interface -------------------------------------- - -! Be careful ! -The PCF8591 is plainly impossible to detect ! Stupid chip. -So every chip with address in the interval [48..4f] is -detected as PCF8591. If you have other chips in this address -range, the workaround is to load this module after the one -for your others chips. - -On detection (i.e. insmod, modprobe et al.), directories are being -created for each detected PCF8591: - -/sys/bus/devices/<0>-<1>/ -where <0> is the bus the chip was detected on (e. g. i2c-0) -and <1> the chip address ([48..4f]) - -Inside these directories, there are such files: -in0, in1, in2, in3, out0_enable, out0_output, name - -Name contains chip name. - -The in0, in1, in2 and in3 files are RO. Reading gives the value of the -corresponding channel. Depending on the current analog inputs configuration, -files in2 and/or in3 do not exist. Values range are from 0 to 255 for single -ended inputs and -128 to +127 for differential inputs (8-bit ADC). - -The out0_enable file is RW. Reading gives "1" for analog output enabled and -"0" for analog output disabled. Writing accepts "0" and "1" accordingly. - -The out0_output file is RW. Writing a number between 0 and 255 (8-bit DAC), send -the value to the digital-to-analog converter. Note that a voltage will -only appears on AOUT pin if aout0_enable equals 1. Reading returns the last -value written. diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 0eb4170cc4af..9a22816b37dd 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -635,6 +635,20 @@ config SENSORS_PC87427 This driver can also be built as a module. If so, the module will be called pc87427. +config SENSORS_PCF8591 + tristate "Philips PCF8591 ADC/DAC" + depends on I2C + default n + help + If you say yes here you get support for Philips PCF8591 4-channel + ADC, 1-channel DAC chips. + + This driver can also be built as a module. If so, the module + will be called pcf8591. + + These devices are hard to detect and rarely found on mainstream + hardware. If unsure, say N. + config SENSORS_SIS5595 tristate "Silicon Integrated Systems Corp. SiS5595" depends on PCI diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 2e80f37f39eb..e332d6267920 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -70,6 +70,7 @@ obj-$(CONFIG_SENSORS_MAX1619) += max1619.o obj-$(CONFIG_SENSORS_MAX6650) += max6650.o obj-$(CONFIG_SENSORS_PC87360) += pc87360.o obj-$(CONFIG_SENSORS_PC87427) += pc87427.o +obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o obj-$(CONFIG_SENSORS_SIS5595) += sis5595.o obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o diff --git a/drivers/hwmon/pcf8591.c b/drivers/hwmon/pcf8591.c new file mode 100644 index 000000000000..1d7ffebd679d --- /dev/null +++ b/drivers/hwmon/pcf8591.c @@ -0,0 +1,325 @@ +/* + Copyright (C) 2001-2004 Aurelien Jarno + Ported to Linux 2.6 by Aurelien Jarno with + the help of Jean Delvare + + 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 +#include +#include +#include +#include + +/* Addresses to scan */ +static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c, + 0x4d, 0x4e, 0x4f, I2C_CLIENT_END }; + +/* Insmod parameters */ +I2C_CLIENT_INSMOD_1(pcf8591); + +static int input_mode; +module_param(input_mode, int, 0); +MODULE_PARM_DESC(input_mode, + "Analog input mode:\n" + " 0 = four single ended inputs\n" + " 1 = three differential inputs\n" + " 2 = single ended and differential mixed\n" + " 3 = two differential inputs\n"); + +/* The PCF8591 control byte + 7 6 5 4 3 2 1 0 + | 0 |AOEF| AIP | 0 |AINC| AICH | */ + +/* Analog Output Enable Flag (analog output active if 1) */ +#define PCF8591_CONTROL_AOEF 0x40 + +/* Analog Input Programming + 0x00 = four single ended inputs + 0x10 = three differential inputs + 0x20 = single ended and differential mixed + 0x30 = two differential inputs */ +#define PCF8591_CONTROL_AIP_MASK 0x30 + +/* Autoincrement Flag (switch on if 1) */ +#define PCF8591_CONTROL_AINC 0x04 + +/* Channel selection + 0x00 = channel 0 + 0x01 = channel 1 + 0x02 = channel 2 + 0x03 = channel 3 */ +#define PCF8591_CONTROL_AICH_MASK 0x03 + +/* Initial values */ +#define PCF8591_INIT_CONTROL ((input_mode << 4) | PCF8591_CONTROL_AOEF) +#define PCF8591_INIT_AOUT 0 /* DAC out = 0 */ + +/* Conversions */ +#define REG_TO_SIGNED(reg) (((reg) & 0x80)?((reg) - 256):(reg)) + +struct pcf8591_data { + struct mutex update_lock; + + u8 control; + u8 aout; +}; + +static void pcf8591_init_client(struct i2c_client *client); +static int pcf8591_read_channel(struct device *dev, int channel); + +/* following are the sysfs callback functions */ +#define show_in_channel(channel) \ +static ssize_t show_in##channel##_input(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return sprintf(buf, "%d\n", pcf8591_read_channel(dev, channel));\ +} \ +static DEVICE_ATTR(in##channel##_input, S_IRUGO, \ + show_in##channel##_input, NULL); + +show_in_channel(0); +show_in_channel(1); +show_in_channel(2); +show_in_channel(3); + +static ssize_t show_out0_ouput(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct pcf8591_data *data = i2c_get_clientdata(to_i2c_client(dev)); + return sprintf(buf, "%d\n", data->aout * 10); +} + +static ssize_t set_out0_output(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int value; + struct i2c_client *client = to_i2c_client(dev); + struct pcf8591_data *data = i2c_get_clientdata(client); + if ((value = (simple_strtoul(buf, NULL, 10) + 5) / 10) <= 255) { + data->aout = value; + i2c_smbus_write_byte_data(client, data->control, data->aout); + return count; + } + return -EINVAL; +} + +static DEVICE_ATTR(out0_output, S_IWUSR | S_IRUGO, + show_out0_ouput, set_out0_output); + +static ssize_t show_out0_enable(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct pcf8591_data *data = i2c_get_clientdata(to_i2c_client(dev)); + return sprintf(buf, "%u\n", !(!(data->control & PCF8591_CONTROL_AOEF))); +} + +static ssize_t set_out0_enable(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct pcf8591_data *data = i2c_get_clientdata(client); + unsigned long val = simple_strtoul(buf, NULL, 10); + + mutex_lock(&data->update_lock); + if (val) + data->control |= PCF8591_CONTROL_AOEF; + else + data->control &= ~PCF8591_CONTROL_AOEF; + i2c_smbus_write_byte(client, data->control); + mutex_unlock(&data->update_lock); + return count; +} + +static DEVICE_ATTR(out0_enable, S_IWUSR | S_IRUGO, + show_out0_enable, set_out0_enable); + +static struct attribute *pcf8591_attributes[] = { + &dev_attr_out0_enable.attr, + &dev_attr_out0_output.attr, + &dev_attr_in0_input.attr, + &dev_attr_in1_input.attr, + NULL +}; + +static const struct attribute_group pcf8591_attr_group = { + .attrs = pcf8591_attributes, +}; + +static struct attribute *pcf8591_attributes_opt[] = { + &dev_attr_in2_input.attr, + &dev_attr_in3_input.attr, + NULL +}; + +static const struct attribute_group pcf8591_attr_group_opt = { + .attrs = pcf8591_attributes_opt, +}; + +/* + * Real code + */ + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int pcf8591_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 + | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) + return -ENODEV; + + /* Now, we would do the remaining detection. But the PCF8591 is plainly + impossible to detect! Stupid chip. */ + + strlcpy(info->type, "pcf8591", I2C_NAME_SIZE); + + return 0; +} + +static int pcf8591_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct pcf8591_data *data; + int err; + + if (!(data = kzalloc(sizeof(struct pcf8591_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + /* Initialize the PCF8591 chip */ + pcf8591_init_client(client); + + /* Register sysfs hooks */ + err = sysfs_create_group(&client->dev.kobj, &pcf8591_attr_group); + if (err) + goto exit_kfree; + + /* Register input2 if not in "two differential inputs" mode */ + if (input_mode != 3) { + if ((err = device_create_file(&client->dev, + &dev_attr_in2_input))) + goto exit_sysfs_remove; + } + + /* Register input3 only in "four single ended inputs" mode */ + if (input_mode == 0) { + if ((err = device_create_file(&client->dev, + &dev_attr_in3_input))) + goto exit_sysfs_remove; + } + + return 0; + +exit_sysfs_remove: + sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group_opt); + sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group); +exit_kfree: + kfree(data); +exit: + return err; +} + +static int pcf8591_remove(struct i2c_client *client) +{ + sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group_opt); + sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group); + kfree(i2c_get_clientdata(client)); + return 0; +} + +/* Called when we have found a new PCF8591. */ +static void pcf8591_init_client(struct i2c_client *client) +{ + struct pcf8591_data *data = i2c_get_clientdata(client); + data->control = PCF8591_INIT_CONTROL; + data->aout = PCF8591_INIT_AOUT; + + i2c_smbus_write_byte_data(client, data->control, data->aout); + + /* The first byte transmitted contains the conversion code of the + previous read cycle. FLUSH IT! */ + i2c_smbus_read_byte(client); +} + +static int pcf8591_read_channel(struct device *dev, int channel) +{ + u8 value; + struct i2c_client *client = to_i2c_client(dev); + struct pcf8591_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + if ((data->control & PCF8591_CONTROL_AICH_MASK) != channel) { + data->control = (data->control & ~PCF8591_CONTROL_AICH_MASK) + | channel; + i2c_smbus_write_byte(client, data->control); + + /* The first byte transmitted contains the conversion code of + the previous read cycle. FLUSH IT! */ + i2c_smbus_read_byte(client); + } + value = i2c_smbus_read_byte(client); + + mutex_unlock(&data->update_lock); + + if ((channel == 2 && input_mode == 2) || + (channel != 3 && (input_mode == 1 || input_mode == 3))) + return (10 * REG_TO_SIGNED(value)); + else + return (10 * value); +} + +static const struct i2c_device_id pcf8591_id[] = { + { "pcf8591", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pcf8591_id); + +static struct i2c_driver pcf8591_driver = { + .driver = { + .name = "pcf8591", + }, + .probe = pcf8591_probe, + .remove = pcf8591_remove, + .id_table = pcf8591_id, + + .class = I2C_CLASS_HWMON, /* Nearest choice */ + .detect = pcf8591_detect, + .address_data = &addr_data, +}; + +static int __init pcf8591_init(void) +{ + if (input_mode < 0 || input_mode > 3) { + printk(KERN_WARNING "pcf8591: invalid input_mode (%d)\n", + input_mode); + input_mode = 0; + } + return i2c_add_driver(&pcf8591_driver); +} + +static void __exit pcf8591_exit(void) +{ + i2c_del_driver(&pcf8591_driver); +} + +MODULE_AUTHOR("Aurelien Jarno "); +MODULE_DESCRIPTION("PCF8591 driver"); +MODULE_LICENSE("GPL"); + +module_init(pcf8591_init); +module_exit(pcf8591_exit); diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig index c80312c1f382..8f8c81eb0aee 100644 --- a/drivers/i2c/chips/Kconfig +++ b/drivers/i2c/chips/Kconfig @@ -64,19 +64,6 @@ config SENSORS_PCA9539 This driver is deprecated and will be dropped soon. Use drivers/gpio/pca953x.c instead. -config SENSORS_PCF8591 - tristate "Philips PCF8591" - depends on EXPERIMENTAL - default n - help - If you say yes here you get support for Philips PCF8591 chips. - - This driver can also be built as a module. If so, the module - will be called pcf8591. - - These devices are hard to detect and rarely found on mainstream - hardware. If unsure, say N. - config SENSORS_MAX6875 tristate "Maxim MAX6875 Power supply supervisor" depends on EXPERIMENTAL diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile index d142f238a2de..55a376037183 100644 --- a/drivers/i2c/chips/Makefile +++ b/drivers/i2c/chips/Makefile @@ -15,7 +15,6 @@ obj-$(CONFIG_SENSORS_MAX6875) += max6875.o 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_SENSORS_TSL2550) += tsl2550.o ifeq ($(CONFIG_I2C_DEBUG_CHIP),y) diff --git a/drivers/i2c/chips/pcf8591.c b/drivers/i2c/chips/pcf8591.c deleted file mode 100644 index 16ce3e193776..000000000000 --- a/drivers/i2c/chips/pcf8591.c +++ /dev/null @@ -1,325 +0,0 @@ -/* - Copyright (C) 2001-2004 Aurelien Jarno - Ported to Linux 2.6 by Aurelien Jarno with - the help of Jean Delvare - - 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 -#include -#include -#include -#include - -/* Addresses to scan */ -static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c, - 0x4d, 0x4e, 0x4f, I2C_CLIENT_END }; - -/* Insmod parameters */ -I2C_CLIENT_INSMOD_1(pcf8591); - -static int input_mode; -module_param(input_mode, int, 0); -MODULE_PARM_DESC(input_mode, - "Analog input mode:\n" - " 0 = four single ended inputs\n" - " 1 = three differential inputs\n" - " 2 = single ended and differential mixed\n" - " 3 = two differential inputs\n"); - -/* The PCF8591 control byte - 7 6 5 4 3 2 1 0 - | 0 |AOEF| AIP | 0 |AINC| AICH | */ - -/* Analog Output Enable Flag (analog output active if 1) */ -#define PCF8591_CONTROL_AOEF 0x40 - -/* Analog Input Programming - 0x00 = four single ended inputs - 0x10 = three differential inputs - 0x20 = single ended and differential mixed - 0x30 = two differential inputs */ -#define PCF8591_CONTROL_AIP_MASK 0x30 - -/* Autoincrement Flag (switch on if 1) */ -#define PCF8591_CONTROL_AINC 0x04 - -/* Channel selection - 0x00 = channel 0 - 0x01 = channel 1 - 0x02 = channel 2 - 0x03 = channel 3 */ -#define PCF8591_CONTROL_AICH_MASK 0x03 - -/* Initial values */ -#define PCF8591_INIT_CONTROL ((input_mode << 4) | PCF8591_CONTROL_AOEF) -#define PCF8591_INIT_AOUT 0 /* DAC out = 0 */ - -/* Conversions */ -#define REG_TO_SIGNED(reg) (((reg) & 0x80)?((reg) - 256):(reg)) - -struct pcf8591_data { - struct mutex update_lock; - - u8 control; - u8 aout; -}; - -static void pcf8591_init_client(struct i2c_client *client); -static int pcf8591_read_channel(struct device *dev, int channel); - -/* following are the sysfs callback functions */ -#define show_in_channel(channel) \ -static ssize_t show_in##channel##_input(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return sprintf(buf, "%d\n", pcf8591_read_channel(dev, channel));\ -} \ -static DEVICE_ATTR(in##channel##_input, S_IRUGO, \ - show_in##channel##_input, NULL); - -show_in_channel(0); -show_in_channel(1); -show_in_channel(2); -show_in_channel(3); - -static ssize_t show_out0_ouput(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct pcf8591_data *data = i2c_get_clientdata(to_i2c_client(dev)); - return sprintf(buf, "%d\n", data->aout * 10); -} - -static ssize_t set_out0_output(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - unsigned int value; - struct i2c_client *client = to_i2c_client(dev); - struct pcf8591_data *data = i2c_get_clientdata(client); - if ((value = (simple_strtoul(buf, NULL, 10) + 5) / 10) <= 255) { - data->aout = value; - i2c_smbus_write_byte_data(client, data->control, data->aout); - return count; - } - return -EINVAL; -} - -static DEVICE_ATTR(out0_output, S_IWUSR | S_IRUGO, - show_out0_ouput, set_out0_output); - -static ssize_t show_out0_enable(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct pcf8591_data *data = i2c_get_clientdata(to_i2c_client(dev)); - return sprintf(buf, "%u\n", !(!(data->control & PCF8591_CONTROL_AOEF))); -} - -static ssize_t set_out0_enable(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct pcf8591_data *data = i2c_get_clientdata(client); - unsigned long val = simple_strtoul(buf, NULL, 10); - - mutex_lock(&data->update_lock); - if (val) - data->control |= PCF8591_CONTROL_AOEF; - else - data->control &= ~PCF8591_CONTROL_AOEF; - i2c_smbus_write_byte(client, data->control); - mutex_unlock(&data->update_lock); - return count; -} - -static DEVICE_ATTR(out0_enable, S_IWUSR | S_IRUGO, - show_out0_enable, set_out0_enable); - -static struct attribute *pcf8591_attributes[] = { - &dev_attr_out0_enable.attr, - &dev_attr_out0_output.attr, - &dev_attr_in0_input.attr, - &dev_attr_in1_input.attr, - NULL -}; - -static const struct attribute_group pcf8591_attr_group = { - .attrs = pcf8591_attributes, -}; - -static struct attribute *pcf8591_attributes_opt[] = { - &dev_attr_in2_input.attr, - &dev_attr_in3_input.attr, - NULL -}; - -static const struct attribute_group pcf8591_attr_group_opt = { - .attrs = pcf8591_attributes_opt, -}; - -/* - * Real code - */ - -/* Return 0 if detection is successful, -ENODEV otherwise */ -static int pcf8591_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 - | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) - return -ENODEV; - - /* Now, we would do the remaining detection. But the PCF8591 is plainly - impossible to detect! Stupid chip. */ - - strlcpy(info->type, "pcf8591", I2C_NAME_SIZE); - - return 0; -} - -static int pcf8591_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct pcf8591_data *data; - int err; - - if (!(data = kzalloc(sizeof(struct pcf8591_data), GFP_KERNEL))) { - err = -ENOMEM; - goto exit; - } - - i2c_set_clientdata(client, data); - mutex_init(&data->update_lock); - - /* Initialize the PCF8591 chip */ - pcf8591_init_client(client); - - /* Register sysfs hooks */ - err = sysfs_create_group(&client->dev.kobj, &pcf8591_attr_group); - if (err) - goto exit_kfree; - - /* Register input2 if not in "two differential inputs" mode */ - if (input_mode != 3) { - if ((err = device_create_file(&client->dev, - &dev_attr_in2_input))) - goto exit_sysfs_remove; - } - - /* Register input3 only in "four single ended inputs" mode */ - if (input_mode == 0) { - if ((err = device_create_file(&client->dev, - &dev_attr_in3_input))) - goto exit_sysfs_remove; - } - - return 0; - -exit_sysfs_remove: - sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group_opt); - sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group); -exit_kfree: - kfree(data); -exit: - return err; -} - -static int pcf8591_remove(struct i2c_client *client) -{ - sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group_opt); - sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group); - kfree(i2c_get_clientdata(client)); - return 0; -} - -/* Called when we have found a new PCF8591. */ -static void pcf8591_init_client(struct i2c_client *client) -{ - struct pcf8591_data *data = i2c_get_clientdata(client); - data->control = PCF8591_INIT_CONTROL; - data->aout = PCF8591_INIT_AOUT; - - i2c_smbus_write_byte_data(client, data->control, data->aout); - - /* The first byte transmitted contains the conversion code of the - previous read cycle. FLUSH IT! */ - i2c_smbus_read_byte(client); -} - -static int pcf8591_read_channel(struct device *dev, int channel) -{ - u8 value; - struct i2c_client *client = to_i2c_client(dev); - struct pcf8591_data *data = i2c_get_clientdata(client); - - mutex_lock(&data->update_lock); - - if ((data->control & PCF8591_CONTROL_AICH_MASK) != channel) { - data->control = (data->control & ~PCF8591_CONTROL_AICH_MASK) - | channel; - i2c_smbus_write_byte(client, data->control); - - /* The first byte transmitted contains the conversion code of - the previous read cycle. FLUSH IT! */ - i2c_smbus_read_byte(client); - } - value = i2c_smbus_read_byte(client); - - mutex_unlock(&data->update_lock); - - if ((channel == 2 && input_mode == 2) || - (channel != 3 && (input_mode == 1 || input_mode == 3))) - return (10 * REG_TO_SIGNED(value)); - else - return (10 * value); -} - -static const struct i2c_device_id pcf8591_id[] = { - { "pcf8591", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, pcf8591_id); - -static struct i2c_driver pcf8591_driver = { - .driver = { - .name = "pcf8591", - }, - .probe = pcf8591_probe, - .remove = pcf8591_remove, - .id_table = pcf8591_id, - - .class = I2C_CLASS_HWMON, /* Nearest choice */ - .detect = pcf8591_detect, - .address_data = &addr_data, -}; - -static int __init pcf8591_init(void) -{ - if (input_mode < 0 || input_mode > 3) { - printk(KERN_WARNING "pcf8591: invalid input_mode (%d)\n", - input_mode); - input_mode = 0; - } - return i2c_add_driver(&pcf8591_driver); -} - -static void __exit pcf8591_exit(void) -{ - i2c_del_driver(&pcf8591_driver); -} - -MODULE_AUTHOR("Aurelien Jarno "); -MODULE_DESCRIPTION("PCF8591 driver"); -MODULE_LICENSE("GPL"); - -module_init(pcf8591_init); -module_exit(pcf8591_exit); -- cgit v1.2.3 From e7a19c5624c66afa8118b10cd59f87ee407646bc Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Mon, 30 Mar 2009 21:46:44 +0200 Subject: dmi: Let dmi_walk() users pass private data At the moment, dmi_walk() lacks flexibility, users can't pass data to the callback function. Add a pointer for private data to make this function more flexible. Signed-off-by: Jean Delvare Cc: Hans de Goede Cc: Matthew Garrett Cc: Roland Dreier --- drivers/firmware/dmi_scan.c | 18 +++++++++++------- drivers/hwmon/fschmd.c | 4 ++-- drivers/platform/x86/dell-laptop.c | 4 ++-- drivers/watchdog/hpwdt.c | 4 ++-- include/linux/dmi.h | 7 ++++--- 5 files changed, 21 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c index 8f0f7c449305..5f1b5400d96a 100644 --- a/drivers/firmware/dmi_scan.c +++ b/drivers/firmware/dmi_scan.c @@ -68,7 +68,8 @@ static char * __init dmi_string(const struct dmi_header *dm, u8 s) * pointing to completely the wrong place for example */ static void dmi_table(u8 *buf, int len, int num, - void (*decode)(const struct dmi_header *)) + void (*decode)(const struct dmi_header *, void *), + void *private_data) { u8 *data = buf; int i = 0; @@ -89,7 +90,7 @@ static void dmi_table(u8 *buf, int len, int num, while ((data - buf < len - 1) && (data[0] || data[1])) data++; if (data - buf < len - 1) - decode(dm); + decode(dm, private_data); data += 2; i++; } @@ -99,7 +100,8 @@ static u32 dmi_base; static u16 dmi_len; static u16 dmi_num; -static int __init dmi_walk_early(void (*decode)(const struct dmi_header *)) +static int __init dmi_walk_early(void (*decode)(const struct dmi_header *, + void *)) { u8 *buf; @@ -107,7 +109,7 @@ static int __init dmi_walk_early(void (*decode)(const struct dmi_header *)) if (buf == NULL) return -1; - dmi_table(buf, dmi_len, dmi_num, decode); + dmi_table(buf, dmi_len, dmi_num, decode, NULL); dmi_iounmap(buf, dmi_len); return 0; @@ -295,7 +297,7 @@ static void __init dmi_save_extended_devices(const struct dmi_header *dm) * and machine entries. For 2.5 we should pull the smbus controller info * out of here. */ -static void __init dmi_decode(const struct dmi_header *dm) +static void __init dmi_decode(const struct dmi_header *dm, void *dummy) { switch(dm->type) { case 0: /* BIOS Information */ @@ -598,10 +600,12 @@ int dmi_get_year(int field) /** * dmi_walk - Walk the DMI table and get called back for every record * @decode: Callback function + * @private_data: Private data to be passed to the callback function * * Returns -1 when the DMI table can't be reached, 0 on success. */ -int dmi_walk(void (*decode)(const struct dmi_header *)) +int dmi_walk(void (*decode)(const struct dmi_header *, void *), + void *private_data) { u8 *buf; @@ -612,7 +616,7 @@ int dmi_walk(void (*decode)(const struct dmi_header *)) if (buf == NULL) return -1; - dmi_table(buf, dmi_len, dmi_num, decode); + dmi_table(buf, dmi_len, dmi_num, decode, private_data); iounmap(buf); return 0; diff --git a/drivers/hwmon/fschmd.c b/drivers/hwmon/fschmd.c index d07f4ef75092..b557f2ebd9ae 100644 --- a/drivers/hwmon/fschmd.c +++ b/drivers/hwmon/fschmd.c @@ -856,7 +856,7 @@ static struct file_operations watchdog_fops = { /* DMI decode routine to read voltage scaling factors from special DMI tables, which are available on FSC machines with an fscher or later chip. */ -static void fschmd_dmi_decode(const struct dmi_header *header) +static void fschmd_dmi_decode(const struct dmi_header *header, void *dummy) { int i, mult[3] = { 0 }, offset[3] = { 0 }, vref = 0, found = 0; @@ -991,7 +991,7 @@ static int fschmd_probe(struct i2c_client *client, /* Read the special DMI table for fscher and newer chips */ if ((kind == fscher || kind >= fschrc) && dmi_vref == -1) { - dmi_walk(fschmd_dmi_decode); + dmi_walk(fschmd_dmi_decode, NULL); if (dmi_vref == -1) { dev_warn(&client->dev, "Couldn't get voltage scaling factors from " diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index 16e11c2ee19a..af9f43021172 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -103,7 +103,7 @@ static void parse_da_table(const struct dmi_header *dm) da_num_tokens += tokens; } -static void find_tokens(const struct dmi_header *dm) +static void find_tokens(const struct dmi_header *dm, void *dummy) { switch (dm->type) { case 0xd4: /* Indexed IO */ @@ -356,7 +356,7 @@ static int __init dell_init(void) if (!dmi_check_system(dell_device_table)) return -ENODEV; - dmi_walk(find_tokens); + dmi_walk(find_tokens, NULL); if (!da_tokens) { printk(KERN_INFO "dell-laptop: Unable to find dmi tokens\n"); diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c index 6cf155d6b350..3137361ccbfe 100644 --- a/drivers/watchdog/hpwdt.c +++ b/drivers/watchdog/hpwdt.c @@ -380,7 +380,7 @@ asm(".text \n\t" * This function checks whether or not a SMBIOS/DMI record is * the 64bit CRU info or not */ -static void __devinit dmi_find_cru(const struct dmi_header *dm) +static void __devinit dmi_find_cru(const struct dmi_header *dm, void *dummy) { struct smbios_cru64_info *smbios_cru64_ptr; unsigned long cru_physical_address; @@ -403,7 +403,7 @@ static int __devinit detect_cru_service(void) { cru_rom_addr = NULL; - dmi_walk(dmi_find_cru); + dmi_walk(dmi_find_cru, NULL); /* if cru_rom_addr has been set then we found a CRU service */ return ((cru_rom_addr != NULL) ? 0 : -ENODEV); diff --git a/include/linux/dmi.h b/include/linux/dmi.h index d741b9ceb0e0..bb5489c82c99 100644 --- a/include/linux/dmi.h +++ b/include/linux/dmi.h @@ -47,7 +47,8 @@ extern int dmi_get_year(int field); extern int dmi_name_in_vendors(const char *str); extern int dmi_name_in_serial(const char *str); extern int dmi_available; -extern int dmi_walk(void (*decode)(const struct dmi_header *)); +extern int dmi_walk(void (*decode)(const struct dmi_header *, void *), + void *private_data); extern bool dmi_match(enum dmi_field f, const char *str); #else @@ -61,8 +62,8 @@ static inline int dmi_get_year(int year) { return 0; } static inline int dmi_name_in_vendors(const char *s) { return 0; } static inline int dmi_name_in_serial(const char *s) { return 0; } #define dmi_available 0 -static inline int dmi_walk(void (*decode)(const struct dmi_header *)) - { return -1; } +static inline int dmi_walk(void (*decode)(const struct dmi_header *, void *), + void *private_data) { return -1; } static inline bool dmi_match(enum dmi_field f, const char *str) { return false; } static inline const struct dmi_system_id * -- cgit v1.2.3 From fa5bfab7128e58c31448fca83a288a86e7d476cc Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 30 Mar 2009 21:46:44 +0200 Subject: i2c-i801: Instantiate FSC hardware montioring chips Detect various FSC hwmon IC's based on DMI tables and then let the i2c-i801 driver instantiate the i2c client devices. Note that some of the info in the added table is indentical for all rows, still this is kept in the table to keep the code general and thus (hopefully) easily extensible in the future. Signed-off-by: Hans de Goede Signed-off-by: Jean Delvare --- drivers/i2c/busses/i2c-i801.c | 77 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 230238df56c4..10411848fd70 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -65,6 +65,7 @@ #include #include #include +#include /* I801 SMBus address offsets */ #define SMBHSTSTS (0 + i801_smba) @@ -616,10 +617,81 @@ static void __init input_apanel_init(void) static void __init input_apanel_init(void) {} #endif +#if defined CONFIG_SENSORS_FSCHMD || defined CONFIG_SENSORS_FSCHMD_MODULE +struct dmi_onboard_device_info { + const char *name; + u8 type; + unsigned short i2c_addr; + const char *i2c_type; +}; + +static struct dmi_onboard_device_info __devinitdata dmi_devices[] = { + { "Syleus", DMI_DEV_TYPE_OTHER, 0x73, "fscsyl" }, + { "Hermes", DMI_DEV_TYPE_OTHER, 0x73, "fscher" }, + { "Hades", DMI_DEV_TYPE_OTHER, 0x73, "fschds" }, +}; + +static void __devinit dmi_check_onboard_device(u8 type, const char *name, + struct i2c_adapter *adap) +{ + int i; + struct i2c_board_info info; + + for (i = 0; i < ARRAY_SIZE(dmi_devices); i++) { + /* & ~0x80, ignore enabled/disabled bit */ + if ((type & ~0x80) != dmi_devices[i].type) + continue; + if (strcmp(name, dmi_devices[i].name)) + continue; + + memset(&info, 0, sizeof(struct i2c_board_info)); + info.addr = dmi_devices[i].i2c_addr; + strlcpy(info.type, dmi_devices[i].i2c_type, I2C_NAME_SIZE); + i2c_new_device(adap, &info); + break; + } +} + +/* We use our own function to check for onboard devices instead of + dmi_find_device() as some buggy BIOS's have the devices we are interested + in marked as disabled */ +static void __devinit dmi_check_onboard_devices(const struct dmi_header *dm, + void *adap) +{ + int i, count; + + if (dm->type != 10) + return; + + count = (dm->length - sizeof(struct dmi_header)) / 2; + for (i = 0; i < count; i++) { + const u8 *d = (char *)(dm + 1) + (i * 2); + const char *name = ((char *) dm) + dm->length; + u8 type = d[0]; + u8 s = d[1]; + + if (!s) + continue; + s--; + while (s > 0 && name[0]) { + name += strlen(name) + 1; + s--; + } + if (name[0] == 0) /* Bogus string reference */ + continue; + + dmi_check_onboard_device(type, name, adap); + } +} +#endif + static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id *id) { unsigned char temp; int err; +#if defined CONFIG_SENSORS_FSCHMD || defined CONFIG_SENSORS_FSCHMD_MODULE + const char *vendor; +#endif I801_dev = dev; i801_features = 0; @@ -712,6 +784,11 @@ static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id i2c_new_device(&i801_adapter, &info); } #endif +#if defined CONFIG_SENSORS_FSCHMD || defined CONFIG_SENSORS_FSCHMD_MODULE + vendor = dmi_get_system_info(DMI_BOARD_VENDOR); + if (vendor && !strcmp(vendor, "FUJITSU SIEMENS")) + dmi_walk(dmi_check_onboard_devices, &i801_adapter); +#endif return 0; -- cgit v1.2.3 From c69ab2b78efbe388bb0fc5d885b718ff4668cceb Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 30 Mar 2009 21:46:45 +0200 Subject: hwmon: (fschmd) Add support for the FSC Syleus IC Many thanks to Fujitsu Siemens Computers for providing docs and a machine to test the driver on. Signed-off-by: Hans de Goede Signed-off-by: Jean Delvare --- drivers/hwmon/Kconfig | 7 +- drivers/hwmon/fschmd.c | 214 ++++++++++++++++++++++++++++++++++++------------- 2 files changed, 161 insertions(+), 60 deletions(-) (limited to 'drivers') diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 9a22816b37dd..41e3f861c472 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -343,11 +343,12 @@ config SENSORS_FSCPOS will be called fscpos. config SENSORS_FSCHMD - tristate "FSC Poseidon, Scylla, Hermes, Heimdall and Heracles" + tristate "Fujitsu Siemens Computers sensor chips" depends on X86 && I2C help - If you say yes here you get support for various Fujitsu Siemens - Computers sensor chips, including support for the integrated + If you say yes here you get support for the following Fujitsu + Siemens Computers (FSC) sensor chips: Poseidon, Scylla, Hermes, + Heimdall, Heracles and Syleus including support for the integrated watchdog. This is a merged driver for FSC sensor chips replacing the fscpos, diff --git a/drivers/hwmon/fschmd.c b/drivers/hwmon/fschmd.c index b557f2ebd9ae..ac515bd6b1e7 100644 --- a/drivers/hwmon/fschmd.c +++ b/drivers/hwmon/fschmd.c @@ -1,6 +1,6 @@ /* fschmd.c * - * Copyright (C) 2007,2008 Hans de Goede + * Copyright (C) 2007 - 2009 Hans de Goede * * 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 @@ -19,7 +19,7 @@ /* * Merged Fujitsu Siemens hwmon driver, supporting the Poseidon, Hermes, - * Scylla, Heracles and Heimdall chips + * Scylla, Heracles, Heimdall and Syleus chips * * Based on the original 2.4 fscscy, 2.6 fscpos, 2.6 fscher and 2.6 * (candidate) fschmd drivers: @@ -56,7 +56,7 @@ static int nowayout = WATCHDOG_NOWAYOUT; module_param(nowayout, int, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); -I2C_CLIENT_INSMOD_5(fscpos, fscher, fscscy, fschrc, fschmd); +I2C_CLIENT_INSMOD_6(fscpos, fscher, fscscy, fschrc, fschmd, fscsyl); /* * The FSCHMD registers and other defines @@ -75,9 +75,12 @@ I2C_CLIENT_INSMOD_5(fscpos, fscher, fscscy, fschrc, fschmd); #define FSCHMD_CONTROL_ALERT_LED 0x01 /* watchdog */ -#define FSCHMD_REG_WDOG_PRESET 0x28 -#define FSCHMD_REG_WDOG_STATE 0x23 -#define FSCHMD_REG_WDOG_CONTROL 0x21 +static const u8 FSCHMD_REG_WDOG_CONTROL[6] = + { 0x21, 0x21, 0x21, 0x21, 0x21, 0x28 }; +static const u8 FSCHMD_REG_WDOG_STATE[6] = + { 0x23, 0x23, 0x23, 0x23, 0x23, 0x29 }; +static const u8 FSCHMD_REG_WDOG_PRESET[6] = + { 0x28, 0x28, 0x28, 0x28, 0x28, 0x2a }; #define FSCHMD_WDOG_CONTROL_TRIGGER 0x10 #define FSCHMD_WDOG_CONTROL_STARTED 0x10 /* the same as trigger */ @@ -87,70 +90,88 @@ I2C_CLIENT_INSMOD_5(fscpos, fscher, fscscy, fschrc, fschmd); #define FSCHMD_WDOG_STATE_CARDRESET 0x02 /* voltages, weird order is to keep the same order as the old drivers */ -static const u8 FSCHMD_REG_VOLT[3] = { 0x45, 0x42, 0x48 }; +static const u8 FSCHMD_REG_VOLT[6][6] = { + { 0x45, 0x42, 0x48 }, /* pos */ + { 0x45, 0x42, 0x48 }, /* her */ + { 0x45, 0x42, 0x48 }, /* scy */ + { 0x45, 0x42, 0x48 }, /* hrc */ + { 0x45, 0x42, 0x48 }, /* hmd */ + { 0x21, 0x20, 0x22, 0x23, 0x24, 0x25 }, /* syl */ +}; + +static const int FSCHMD_NO_VOLT_SENSORS[6] = { 3, 3, 3, 3, 3, 6 }; /* minimum pwm at which the fan is driven (pwm can by increased depending on the temp. Notice that for the scy some fans share there minimum speed. Also notice that with the scy the sensor order is different than with the other chips, this order was in the 2.4 driver and kept for consistency. */ -static const u8 FSCHMD_REG_FAN_MIN[5][6] = { +static const u8 FSCHMD_REG_FAN_MIN[6][7] = { { 0x55, 0x65 }, /* pos */ { 0x55, 0x65, 0xb5 }, /* her */ { 0x65, 0x65, 0x55, 0xa5, 0x55, 0xa5 }, /* scy */ { 0x55, 0x65, 0xa5, 0xb5 }, /* hrc */ { 0x55, 0x65, 0xa5, 0xb5, 0xc5 }, /* hmd */ + { 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb4 }, /* syl */ }; /* actual fan speed */ -static const u8 FSCHMD_REG_FAN_ACT[5][6] = { +static const u8 FSCHMD_REG_FAN_ACT[6][7] = { { 0x0e, 0x6b, 0xab }, /* pos */ { 0x0e, 0x6b, 0xbb }, /* her */ { 0x6b, 0x6c, 0x0e, 0xab, 0x5c, 0xbb }, /* scy */ { 0x0e, 0x6b, 0xab, 0xbb }, /* hrc */ { 0x5b, 0x6b, 0xab, 0xbb, 0xcb }, /* hmd */ + { 0x57, 0x67, 0x77, 0x87, 0x97, 0xa7, 0xb7 }, /* syl */ }; /* fan status registers */ -static const u8 FSCHMD_REG_FAN_STATE[5][6] = { +static const u8 FSCHMD_REG_FAN_STATE[6][7] = { { 0x0d, 0x62, 0xa2 }, /* pos */ { 0x0d, 0x62, 0xb2 }, /* her */ { 0x62, 0x61, 0x0d, 0xa2, 0x52, 0xb2 }, /* scy */ { 0x0d, 0x62, 0xa2, 0xb2 }, /* hrc */ { 0x52, 0x62, 0xa2, 0xb2, 0xc2 }, /* hmd */ + { 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0, 0xb0 }, /* syl */ }; /* fan ripple / divider registers */ -static const u8 FSCHMD_REG_FAN_RIPPLE[5][6] = { +static const u8 FSCHMD_REG_FAN_RIPPLE[6][7] = { { 0x0f, 0x6f, 0xaf }, /* pos */ { 0x0f, 0x6f, 0xbf }, /* her */ { 0x6f, 0x6f, 0x0f, 0xaf, 0x0f, 0xbf }, /* scy */ { 0x0f, 0x6f, 0xaf, 0xbf }, /* hrc */ { 0x5f, 0x6f, 0xaf, 0xbf, 0xcf }, /* hmd */ + { 0x56, 0x66, 0x76, 0x86, 0x96, 0xa6, 0xb6 }, /* syl */ }; -static const int FSCHMD_NO_FAN_SENSORS[5] = { 3, 3, 6, 4, 5 }; +static const int FSCHMD_NO_FAN_SENSORS[6] = { 3, 3, 6, 4, 5, 7 }; /* Fan status register bitmasks */ #define FSCHMD_FAN_ALARM 0x04 /* called fault by FSC! */ -#define FSCHMD_FAN_NOT_PRESENT 0x08 /* not documented */ +#define FSCHMD_FAN_NOT_PRESENT 0x08 +#define FSCHMD_FAN_DISABLED 0x80 /* actual temperature registers */ -static const u8 FSCHMD_REG_TEMP_ACT[5][5] = { +static const u8 FSCHMD_REG_TEMP_ACT[6][11] = { { 0x64, 0x32, 0x35 }, /* pos */ { 0x64, 0x32, 0x35 }, /* her */ { 0x64, 0xD0, 0x32, 0x35 }, /* scy */ { 0x64, 0x32, 0x35 }, /* hrc */ { 0x70, 0x80, 0x90, 0xd0, 0xe0 }, /* hmd */ + { 0x58, 0x68, 0x78, 0x88, 0x98, 0xa8, /* syl */ + 0xb8, 0xc8, 0xd8, 0xe8, 0xf8 }, }; /* temperature state registers */ -static const u8 FSCHMD_REG_TEMP_STATE[5][5] = { +static const u8 FSCHMD_REG_TEMP_STATE[6][11] = { { 0x71, 0x81, 0x91 }, /* pos */ { 0x71, 0x81, 0x91 }, /* her */ { 0x71, 0xd1, 0x81, 0x91 }, /* scy */ { 0x71, 0x81, 0x91 }, /* hrc */ { 0x71, 0x81, 0x91, 0xd1, 0xe1 }, /* hmd */ + { 0x59, 0x69, 0x79, 0x89, 0x99, 0xa9, /* syl */ + 0xb9, 0xc9, 0xd9, 0xe9, 0xf9 }, }; /* temperature high limit registers, FSC does not document these. Proven to be @@ -158,24 +179,30 @@ static const u8 FSCHMD_REG_TEMP_STATE[5][5] = { in the fscscy 2.4 driver. FSC has confirmed that the fschmd has registers at these addresses, but doesn't want to confirm they are the same as with the fscher?? */ -static const u8 FSCHMD_REG_TEMP_LIMIT[5][5] = { +static const u8 FSCHMD_REG_TEMP_LIMIT[6][11] = { { 0, 0, 0 }, /* pos */ { 0x76, 0x86, 0x96 }, /* her */ { 0x76, 0xd6, 0x86, 0x96 }, /* scy */ { 0x76, 0x86, 0x96 }, /* hrc */ { 0x76, 0x86, 0x96, 0xd6, 0xe6 }, /* hmd */ + { 0x5a, 0x6a, 0x7a, 0x8a, 0x9a, 0xaa, /* syl */ + 0xba, 0xca, 0xda, 0xea, 0xfa }, }; /* These were found through experimenting with an fscher, currently they are not used, but we keep them around for future reference. + On the fscsyl AUTOP1 lives at 0x#c (so 0x5c for fan1, 0x6c for fan2, etc), + AUTOP2 lives at 0x#e, and 0x#1 is a bitmask defining which temps influence + the fan speed. static const u8 FSCHER_REG_TEMP_AUTOP1[] = { 0x73, 0x83, 0x93 }; static const u8 FSCHER_REG_TEMP_AUTOP2[] = { 0x75, 0x85, 0x95 }; */ -static const int FSCHMD_NO_TEMP_SENSORS[5] = { 3, 3, 4, 3, 5 }; +static const int FSCHMD_NO_TEMP_SENSORS[6] = { 3, 3, 4, 3, 5, 11 }; /* temp status register bitmasks */ #define FSCHMD_TEMP_WORKING 0x01 #define FSCHMD_TEMP_ALERT 0x02 +#define FSCHMD_TEMP_DISABLED 0x80 /* there only really is an alarm if the sensor is working and alert == 1 */ #define FSCHMD_TEMP_ALARM_MASK \ (FSCHMD_TEMP_WORKING | FSCHMD_TEMP_ALERT) @@ -201,6 +228,7 @@ static const struct i2c_device_id fschmd_id[] = { { "fscscy", fscscy }, { "fschrc", fschrc }, { "fschmd", fschmd }, + { "fscsyl", fscsyl }, { } }; MODULE_DEVICE_TABLE(i2c, fschmd_id); @@ -242,14 +270,14 @@ struct fschmd_data { u8 watchdog_control; /* watchdog control register */ u8 watchdog_state; /* watchdog status register */ u8 watchdog_preset; /* watchdog counter preset on trigger val */ - u8 volt[3]; /* 12, 5, battery voltage */ - u8 temp_act[5]; /* temperature */ - u8 temp_status[5]; /* status of sensor */ - u8 temp_max[5]; /* high temp limit, notice: undocumented! */ - u8 fan_act[6]; /* fans revolutions per second */ - u8 fan_status[6]; /* fan status */ - u8 fan_min[6]; /* fan min value for rps */ - u8 fan_ripple[6]; /* divider for rps */ + u8 volt[6]; /* voltage */ + u8 temp_act[11]; /* temperature */ + u8 temp_status[11]; /* status of sensor */ + u8 temp_max[11]; /* high temp limit, notice: undocumented! */ + u8 fan_act[7]; /* fans revolutions per second */ + u8 fan_status[7]; /* fan status */ + u8 fan_min[7]; /* fan min value for rps */ + u8 fan_ripple[7]; /* divider for rps */ }; /* Global variables to hold information read from special DMI tables, which are @@ -257,8 +285,8 @@ struct fschmd_data { protect these with a lock as they are only modified from our attach function which always gets called with the i2c-core lock held and never accessed before the attach function is done with them. */ -static int dmi_mult[3] = { 490, 200, 100 }; -static int dmi_offset[3] = { 0, 0, 0 }; +static int dmi_mult[6] = { 490, 200, 100, 100, 200, 100 }; +static int dmi_offset[6] = { 0, 0, 0, 0, 0, 0 }; static int dmi_vref = -1; /* Somewhat ugly :( global data pointer list with all fschmd devices, so that @@ -450,10 +478,11 @@ static ssize_t show_pwm_auto_point1_pwm(struct device *dev, struct device_attribute *devattr, char *buf) { int index = to_sensor_dev_attr(devattr)->index; - int val = fschmd_update_device(dev)->fan_min[index]; + struct fschmd_data *data = fschmd_update_device(dev); + int val = data->fan_min[index]; - /* 0 = allow turning off, 1-255 = 50-100% */ - if (val) + /* 0 = allow turning off (except on the syl), 1-255 = 50-100% */ + if (val || data->kind == fscsyl - 1) val = val / 2 + 128; return sprintf(buf, "%d\n", val); @@ -466,8 +495,8 @@ static ssize_t store_pwm_auto_point1_pwm(struct device *dev, struct fschmd_data *data = dev_get_drvdata(dev); unsigned long v = simple_strtoul(buf, NULL, 10); - /* register: 0 = allow turning off, 1-255 = 50-100% */ - if (v) { + /* reg: 0 = allow turning off (except on the syl), 1-255 = 50-100% */ + if (v || data->kind == fscsyl - 1) { v = SENSORS_LIMIT(v, 128, 255); v = (v - 128) * 2 + 1; } @@ -522,11 +551,15 @@ static ssize_t store_alert_led(struct device *dev, return count; } +static DEVICE_ATTR(alert_led, 0644, show_alert_led, store_alert_led); + static struct sensor_device_attribute fschmd_attr[] = { SENSOR_ATTR(in0_input, 0444, show_in_value, NULL, 0), SENSOR_ATTR(in1_input, 0444, show_in_value, NULL, 1), SENSOR_ATTR(in2_input, 0444, show_in_value, NULL, 2), - SENSOR_ATTR(alert_led, 0644, show_alert_led, store_alert_led, 0), + SENSOR_ATTR(in3_input, 0444, show_in_value, NULL, 3), + SENSOR_ATTR(in4_input, 0444, show_in_value, NULL, 4), + SENSOR_ATTR(in5_input, 0444, show_in_value, NULL, 5), }; static struct sensor_device_attribute fschmd_temp_attr[] = { @@ -550,6 +583,30 @@ static struct sensor_device_attribute fschmd_temp_attr[] = { SENSOR_ATTR(temp5_max, 0644, show_temp_max, store_temp_max, 4), SENSOR_ATTR(temp5_fault, 0444, show_temp_fault, NULL, 4), SENSOR_ATTR(temp5_alarm, 0444, show_temp_alarm, NULL, 4), + SENSOR_ATTR(temp6_input, 0444, show_temp_value, NULL, 5), + SENSOR_ATTR(temp6_max, 0644, show_temp_max, store_temp_max, 5), + SENSOR_ATTR(temp6_fault, 0444, show_temp_fault, NULL, 5), + SENSOR_ATTR(temp6_alarm, 0444, show_temp_alarm, NULL, 5), + SENSOR_ATTR(temp7_input, 0444, show_temp_value, NULL, 6), + SENSOR_ATTR(temp7_max, 0644, show_temp_max, store_temp_max, 6), + SENSOR_ATTR(temp7_fault, 0444, show_temp_fault, NULL, 6), + SENSOR_ATTR(temp7_alarm, 0444, show_temp_alarm, NULL, 6), + SENSOR_ATTR(temp8_input, 0444, show_temp_value, NULL, 7), + SENSOR_ATTR(temp8_max, 0644, show_temp_max, store_temp_max, 7), + SENSOR_ATTR(temp8_fault, 0444, show_temp_fault, NULL, 7), + SENSOR_ATTR(temp8_alarm, 0444, show_temp_alarm, NULL, 7), + SENSOR_ATTR(temp9_input, 0444, show_temp_value, NULL, 8), + SENSOR_ATTR(temp9_max, 0644, show_temp_max, store_temp_max, 8), + SENSOR_ATTR(temp9_fault, 0444, show_temp_fault, NULL, 8), + SENSOR_ATTR(temp9_alarm, 0444, show_temp_alarm, NULL, 8), + SENSOR_ATTR(temp10_input, 0444, show_temp_value, NULL, 9), + SENSOR_ATTR(temp10_max, 0644, show_temp_max, store_temp_max, 9), + SENSOR_ATTR(temp10_fault, 0444, show_temp_fault, NULL, 9), + SENSOR_ATTR(temp10_alarm, 0444, show_temp_alarm, NULL, 9), + SENSOR_ATTR(temp11_input, 0444, show_temp_value, NULL, 10), + SENSOR_ATTR(temp11_max, 0644, show_temp_max, store_temp_max, 10), + SENSOR_ATTR(temp11_fault, 0444, show_temp_fault, NULL, 10), + SENSOR_ATTR(temp11_alarm, 0444, show_temp_alarm, NULL, 10), }; static struct sensor_device_attribute fschmd_fan_attr[] = { @@ -589,6 +646,12 @@ static struct sensor_device_attribute fschmd_fan_attr[] = { SENSOR_ATTR(fan6_fault, 0444, show_fan_fault, NULL, 5), SENSOR_ATTR(pwm6_auto_point1_pwm, 0644, show_pwm_auto_point1_pwm, store_pwm_auto_point1_pwm, 5), + SENSOR_ATTR(fan7_input, 0444, show_fan_value, NULL, 6), + SENSOR_ATTR(fan7_div, 0644, show_fan_div, store_fan_div, 6), + SENSOR_ATTR(fan7_alarm, 0444, show_fan_alarm, NULL, 6), + SENSOR_ATTR(fan7_fault, 0444, show_fan_fault, NULL, 6), + SENSOR_ATTR(pwm7_auto_point1_pwm, 0644, show_pwm_auto_point1_pwm, + store_pwm_auto_point1_pwm, 6), }; @@ -624,10 +687,11 @@ static int watchdog_set_timeout(struct fschmd_data *data, int timeout) data->watchdog_preset = DIV_ROUND_UP(timeout, resolution); /* Write new timeout value */ - i2c_smbus_write_byte_data(data->client, FSCHMD_REG_WDOG_PRESET, - data->watchdog_preset); + i2c_smbus_write_byte_data(data->client, + FSCHMD_REG_WDOG_PRESET[data->kind], data->watchdog_preset); /* Write new control register, do not trigger! */ - i2c_smbus_write_byte_data(data->client, FSCHMD_REG_WDOG_CONTROL, + i2c_smbus_write_byte_data(data->client, + FSCHMD_REG_WDOG_CONTROL[data->kind], data->watchdog_control & ~FSCHMD_WDOG_CONTROL_TRIGGER); ret = data->watchdog_preset * resolution; @@ -662,8 +726,9 @@ static int watchdog_trigger(struct fschmd_data *data) } data->watchdog_control |= FSCHMD_WDOG_CONTROL_TRIGGER; - i2c_smbus_write_byte_data(data->client, FSCHMD_REG_WDOG_CONTROL, - data->watchdog_control); + i2c_smbus_write_byte_data(data->client, + FSCHMD_REG_WDOG_CONTROL[data->kind], + data->watchdog_control); leave: mutex_unlock(&data->watchdog_lock); return ret; @@ -682,7 +747,8 @@ static int watchdog_stop(struct fschmd_data *data) data->watchdog_control &= ~FSCHMD_WDOG_CONTROL_STARTED; /* Don't store the stop flag in our watchdog control register copy, as its a write only bit (read always returns 0) */ - i2c_smbus_write_byte_data(data->client, FSCHMD_REG_WDOG_CONTROL, + i2c_smbus_write_byte_data(data->client, + FSCHMD_REG_WDOG_CONTROL[data->kind], data->watchdog_control | FSCHMD_WDOG_CONTROL_STOP); leave: mutex_unlock(&data->watchdog_lock); @@ -912,6 +978,15 @@ static void fschmd_dmi_decode(const struct dmi_header *header, void *dummy) dmi_mult[i] = mult[i] * 10; dmi_offset[i] = offset[i] * 10; } + /* According to the docs there should be separate dmi entries + for the mult's and offsets of in3-5 of the syl, but on + my test machine these are not present */ + dmi_mult[3] = dmi_mult[2]; + dmi_mult[4] = dmi_mult[1]; + dmi_mult[5] = dmi_mult[2]; + dmi_offset[3] = dmi_offset[2]; + dmi_offset[4] = dmi_offset[1]; + dmi_offset[5] = dmi_offset[2]; dmi_vref = vref; } } @@ -920,8 +995,6 @@ static int fschmd_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; - const char * const client_names[5] = { "fscpos", "fscher", "fscscy", - "fschrc", "fschmd" }; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; @@ -948,11 +1021,13 @@ static int fschmd_detect(struct i2c_client *client, int kind, kind = fschrc; else if (!strcmp(id, "HMD")) kind = fschmd; + else if (!strcmp(id, "SYL")) + kind = fscsyl; else return -ENODEV; } - strlcpy(info->type, client_names[kind - 1], I2C_NAME_SIZE); + strlcpy(info->type, fschmd_id[kind - 1].name, I2C_NAME_SIZE); return 0; } @@ -961,8 +1036,8 @@ static int fschmd_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct fschmd_data *data; - const char * const names[5] = { "Poseidon", "Hermes", "Scylla", - "Heracles", "Heimdall" }; + const char * const names[6] = { "Poseidon", "Hermes", "Scylla", + "Heracles", "Heimdall", "Syleus" }; const int watchdog_minors[] = { WATCHDOG_MINOR, 212, 213, 214, 215 }; int i, err; enum chips kind = id->driver_data; @@ -1000,21 +1075,25 @@ static int fschmd_probe(struct i2c_client *client, } } + /* i2c kind goes from 1-6, we want from 0-5 to address arrays */ + data->kind = kind - 1; + /* Read in some never changing registers */ data->revision = i2c_smbus_read_byte_data(client, FSCHMD_REG_REVISION); data->global_control = i2c_smbus_read_byte_data(client, FSCHMD_REG_CONTROL); data->watchdog_control = i2c_smbus_read_byte_data(client, - FSCHMD_REG_WDOG_CONTROL); + FSCHMD_REG_WDOG_CONTROL[data->kind]); data->watchdog_state = i2c_smbus_read_byte_data(client, - FSCHMD_REG_WDOG_STATE); + FSCHMD_REG_WDOG_STATE[data->kind]); data->watchdog_preset = i2c_smbus_read_byte_data(client, - FSCHMD_REG_WDOG_PRESET); + FSCHMD_REG_WDOG_PRESET[data->kind]); - /* i2c kind goes from 1-5, we want from 0-4 to address arrays */ - data->kind = kind - 1; + err = device_create_file(&client->dev, &dev_attr_alert_led); + if (err) + goto exit_detach; - for (i = 0; i < ARRAY_SIZE(fschmd_attr); i++) { + for (i = 0; i < FSCHMD_NO_VOLT_SENSORS[data->kind]; i++) { err = device_create_file(&client->dev, &fschmd_attr[i].dev_attr); if (err) @@ -1027,6 +1106,16 @@ static int fschmd_probe(struct i2c_client *client, show_temp_max) continue; + if (kind == fscsyl) { + if (i % 4 == 0) + data->temp_status[i / 4] = + i2c_smbus_read_byte_data(client, + FSCHMD_REG_TEMP_STATE + [data->kind][i / 4]); + if (data->temp_status[i / 4] & FSCHMD_TEMP_DISABLED) + continue; + } + err = device_create_file(&client->dev, &fschmd_temp_attr[i].dev_attr); if (err) @@ -1040,6 +1129,16 @@ static int fschmd_probe(struct i2c_client *client, "pwm3_auto_point1_pwm")) continue; + if (kind == fscsyl) { + if (i % 5 == 0) + data->fan_status[i / 5] = + i2c_smbus_read_byte_data(client, + FSCHMD_REG_FAN_STATE + [data->kind][i / 5]); + if (data->fan_status[i / 5] & FSCHMD_FAN_DISABLED) + continue; + } + err = device_create_file(&client->dev, &fschmd_fan_attr[i].dev_attr); if (err) @@ -1126,7 +1225,8 @@ static int fschmd_remove(struct i2c_client *client) if (data->hwmon_dev) hwmon_device_unregister(data->hwmon_dev); - for (i = 0; i < ARRAY_SIZE(fschmd_attr); i++) + device_remove_file(&client->dev, &dev_attr_alert_led); + for (i = 0; i < (FSCHMD_NO_VOLT_SENSORS[data->kind]); i++) device_remove_file(&client->dev, &fschmd_attr[i].dev_attr); for (i = 0; i < (FSCHMD_NO_TEMP_SENSORS[data->kind] * 4); i++) device_remove_file(&client->dev, @@ -1171,7 +1271,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); + data->temp_status[i]); } for (i = 0; i < FSCHMD_NO_FAN_SENSORS[data->kind]; i++) { @@ -1193,12 +1293,12 @@ static struct fschmd_data *fschmd_update_device(struct device *dev) data->fan_act[i]) i2c_smbus_write_byte_data(client, FSCHMD_REG_FAN_STATE[data->kind][i], - FSCHMD_FAN_ALARM); + data->fan_status[i]); } - for (i = 0; i < 3; i++) + for (i = 0; i < FSCHMD_NO_VOLT_SENSORS[data->kind]; i++) data->volt[i] = i2c_smbus_read_byte_data(client, - FSCHMD_REG_VOLT[i]); + FSCHMD_REG_VOLT[data->kind][i]); data->last_updated = jiffies; data->valid = 1; @@ -1220,8 +1320,8 @@ static void __exit fschmd_exit(void) } MODULE_AUTHOR("Hans de Goede "); -MODULE_DESCRIPTION("FSC Poseidon, Hermes, Scylla, Heracles and " - "Heimdall driver"); +MODULE_DESCRIPTION("FSC Poseidon, Hermes, Scylla, Heracles, Heimdall and " + "Syleus driver"); MODULE_LICENSE("GPL"); module_init(fschmd_init); -- cgit v1.2.3 From de15f093e666ccd542f6f7a0e3e917166a07ab44 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 30 Mar 2009 21:46:45 +0200 Subject: hwmon: (fschmd) Add support for the FSC Hades IC Add support for the Hades to the FSC hwmon driver. Signed-off-by: Hans de Goede Signed-off-by: Jean Delvare --- drivers/hwmon/Kconfig | 4 ++-- drivers/hwmon/fschmd.c | 57 ++++++++++++++++++++++++++++++-------------------- 2 files changed, 36 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 41e3f861c472..51ff9b3d7ea2 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -348,8 +348,8 @@ config SENSORS_FSCHMD help If you say yes here you get support for the following Fujitsu Siemens Computers (FSC) sensor chips: Poseidon, Scylla, Hermes, - Heimdall, Heracles and Syleus including support for the integrated - watchdog. + Heimdall, Heracles, Hades and Syleus including support for the + integrated watchdog. This is a merged driver for FSC sensor chips replacing the fscpos, fscscy and fscher drivers and adding support for several other FSC diff --git a/drivers/hwmon/fschmd.c b/drivers/hwmon/fschmd.c index ac515bd6b1e7..ea955edde87e 100644 --- a/drivers/hwmon/fschmd.c +++ b/drivers/hwmon/fschmd.c @@ -19,7 +19,7 @@ /* * Merged Fujitsu Siemens hwmon driver, supporting the Poseidon, Hermes, - * Scylla, Heracles, Heimdall and Syleus chips + * Scylla, Heracles, Heimdall, Hades and Syleus chips * * Based on the original 2.4 fscscy, 2.6 fscpos, 2.6 fscher and 2.6 * (candidate) fschmd drivers: @@ -56,7 +56,7 @@ static int nowayout = WATCHDOG_NOWAYOUT; module_param(nowayout, int, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); -I2C_CLIENT_INSMOD_6(fscpos, fscher, fscscy, fschrc, fschmd, fscsyl); +I2C_CLIENT_INSMOD_7(fscpos, fscher, fscscy, fschrc, fschmd, fschds, fscsyl); /* * The FSCHMD registers and other defines @@ -75,12 +75,12 @@ I2C_CLIENT_INSMOD_6(fscpos, fscher, fscscy, fschrc, fschmd, fscsyl); #define FSCHMD_CONTROL_ALERT_LED 0x01 /* watchdog */ -static const u8 FSCHMD_REG_WDOG_CONTROL[6] = - { 0x21, 0x21, 0x21, 0x21, 0x21, 0x28 }; -static const u8 FSCHMD_REG_WDOG_STATE[6] = - { 0x23, 0x23, 0x23, 0x23, 0x23, 0x29 }; -static const u8 FSCHMD_REG_WDOG_PRESET[6] = - { 0x28, 0x28, 0x28, 0x28, 0x28, 0x2a }; +static const u8 FSCHMD_REG_WDOG_CONTROL[7] = + { 0x21, 0x21, 0x21, 0x21, 0x21, 0x28, 0x28 }; +static const u8 FSCHMD_REG_WDOG_STATE[7] = + { 0x23, 0x23, 0x23, 0x23, 0x23, 0x29, 0x29 }; +static const u8 FSCHMD_REG_WDOG_PRESET[7] = + { 0x28, 0x28, 0x28, 0x28, 0x28, 0x2a, 0x2a }; #define FSCHMD_WDOG_CONTROL_TRIGGER 0x10 #define FSCHMD_WDOG_CONTROL_STARTED 0x10 /* the same as trigger */ @@ -90,61 +90,66 @@ static const u8 FSCHMD_REG_WDOG_PRESET[6] = #define FSCHMD_WDOG_STATE_CARDRESET 0x02 /* voltages, weird order is to keep the same order as the old drivers */ -static const u8 FSCHMD_REG_VOLT[6][6] = { +static const u8 FSCHMD_REG_VOLT[7][6] = { { 0x45, 0x42, 0x48 }, /* pos */ { 0x45, 0x42, 0x48 }, /* her */ { 0x45, 0x42, 0x48 }, /* scy */ { 0x45, 0x42, 0x48 }, /* hrc */ { 0x45, 0x42, 0x48 }, /* hmd */ + { 0x21, 0x20, 0x22 }, /* hds */ { 0x21, 0x20, 0x22, 0x23, 0x24, 0x25 }, /* syl */ }; -static const int FSCHMD_NO_VOLT_SENSORS[6] = { 3, 3, 3, 3, 3, 6 }; +static const int FSCHMD_NO_VOLT_SENSORS[7] = { 3, 3, 3, 3, 3, 3, 6 }; /* minimum pwm at which the fan is driven (pwm can by increased depending on the temp. Notice that for the scy some fans share there minimum speed. Also notice that with the scy the sensor order is different than with the other chips, this order was in the 2.4 driver and kept for consistency. */ -static const u8 FSCHMD_REG_FAN_MIN[6][7] = { +static const u8 FSCHMD_REG_FAN_MIN[7][7] = { { 0x55, 0x65 }, /* pos */ { 0x55, 0x65, 0xb5 }, /* her */ { 0x65, 0x65, 0x55, 0xa5, 0x55, 0xa5 }, /* scy */ { 0x55, 0x65, 0xa5, 0xb5 }, /* hrc */ { 0x55, 0x65, 0xa5, 0xb5, 0xc5 }, /* hmd */ + { 0x55, 0x65, 0xa5, 0xb5, 0xc5 }, /* hds */ { 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb4 }, /* syl */ }; /* actual fan speed */ -static const u8 FSCHMD_REG_FAN_ACT[6][7] = { +static const u8 FSCHMD_REG_FAN_ACT[7][7] = { { 0x0e, 0x6b, 0xab }, /* pos */ { 0x0e, 0x6b, 0xbb }, /* her */ { 0x6b, 0x6c, 0x0e, 0xab, 0x5c, 0xbb }, /* scy */ { 0x0e, 0x6b, 0xab, 0xbb }, /* hrc */ { 0x5b, 0x6b, 0xab, 0xbb, 0xcb }, /* hmd */ + { 0x5b, 0x6b, 0xab, 0xbb, 0xcb }, /* hds */ { 0x57, 0x67, 0x77, 0x87, 0x97, 0xa7, 0xb7 }, /* syl */ }; /* fan status registers */ -static const u8 FSCHMD_REG_FAN_STATE[6][7] = { +static const u8 FSCHMD_REG_FAN_STATE[7][7] = { { 0x0d, 0x62, 0xa2 }, /* pos */ { 0x0d, 0x62, 0xb2 }, /* her */ { 0x62, 0x61, 0x0d, 0xa2, 0x52, 0xb2 }, /* scy */ { 0x0d, 0x62, 0xa2, 0xb2 }, /* hrc */ { 0x52, 0x62, 0xa2, 0xb2, 0xc2 }, /* hmd */ + { 0x52, 0x62, 0xa2, 0xb2, 0xc2 }, /* hds */ { 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0, 0xb0 }, /* syl */ }; /* fan ripple / divider registers */ -static const u8 FSCHMD_REG_FAN_RIPPLE[6][7] = { +static const u8 FSCHMD_REG_FAN_RIPPLE[7][7] = { { 0x0f, 0x6f, 0xaf }, /* pos */ { 0x0f, 0x6f, 0xbf }, /* her */ { 0x6f, 0x6f, 0x0f, 0xaf, 0x0f, 0xbf }, /* scy */ { 0x0f, 0x6f, 0xaf, 0xbf }, /* hrc */ { 0x5f, 0x6f, 0xaf, 0xbf, 0xcf }, /* hmd */ + { 0x5f, 0x6f, 0xaf, 0xbf, 0xcf }, /* hds */ { 0x56, 0x66, 0x76, 0x86, 0x96, 0xa6, 0xb6 }, /* syl */ }; -static const int FSCHMD_NO_FAN_SENSORS[6] = { 3, 3, 6, 4, 5, 7 }; +static const int FSCHMD_NO_FAN_SENSORS[7] = { 3, 3, 6, 4, 5, 5, 7 }; /* Fan status register bitmasks */ #define FSCHMD_FAN_ALARM 0x04 /* called fault by FSC! */ @@ -153,23 +158,25 @@ static const int FSCHMD_NO_FAN_SENSORS[6] = { 3, 3, 6, 4, 5, 7 }; /* actual temperature registers */ -static const u8 FSCHMD_REG_TEMP_ACT[6][11] = { +static const u8 FSCHMD_REG_TEMP_ACT[7][11] = { { 0x64, 0x32, 0x35 }, /* pos */ { 0x64, 0x32, 0x35 }, /* her */ { 0x64, 0xD0, 0x32, 0x35 }, /* scy */ { 0x64, 0x32, 0x35 }, /* hrc */ { 0x70, 0x80, 0x90, 0xd0, 0xe0 }, /* hmd */ + { 0x70, 0x80, 0x90, 0xd0, 0xe0 }, /* hds */ { 0x58, 0x68, 0x78, 0x88, 0x98, 0xa8, /* syl */ 0xb8, 0xc8, 0xd8, 0xe8, 0xf8 }, }; /* temperature state registers */ -static const u8 FSCHMD_REG_TEMP_STATE[6][11] = { +static const u8 FSCHMD_REG_TEMP_STATE[7][11] = { { 0x71, 0x81, 0x91 }, /* pos */ { 0x71, 0x81, 0x91 }, /* her */ { 0x71, 0xd1, 0x81, 0x91 }, /* scy */ { 0x71, 0x81, 0x91 }, /* hrc */ { 0x71, 0x81, 0x91, 0xd1, 0xe1 }, /* hmd */ + { 0x71, 0x81, 0x91, 0xd1, 0xe1 }, /* hds */ { 0x59, 0x69, 0x79, 0x89, 0x99, 0xa9, /* syl */ 0xb9, 0xc9, 0xd9, 0xe9, 0xf9 }, }; @@ -179,12 +186,13 @@ static const u8 FSCHMD_REG_TEMP_STATE[6][11] = { in the fscscy 2.4 driver. FSC has confirmed that the fschmd has registers at these addresses, but doesn't want to confirm they are the same as with the fscher?? */ -static const u8 FSCHMD_REG_TEMP_LIMIT[6][11] = { +static const u8 FSCHMD_REG_TEMP_LIMIT[7][11] = { { 0, 0, 0 }, /* pos */ { 0x76, 0x86, 0x96 }, /* her */ { 0x76, 0xd6, 0x86, 0x96 }, /* scy */ { 0x76, 0x86, 0x96 }, /* hrc */ { 0x76, 0x86, 0x96, 0xd6, 0xe6 }, /* hmd */ + { 0x76, 0x86, 0x96, 0xd6, 0xe6 }, /* hds */ { 0x5a, 0x6a, 0x7a, 0x8a, 0x9a, 0xaa, /* syl */ 0xba, 0xca, 0xda, 0xea, 0xfa }, }; @@ -197,7 +205,7 @@ static const u8 FSCHMD_REG_TEMP_LIMIT[6][11] = { static const u8 FSCHER_REG_TEMP_AUTOP1[] = { 0x73, 0x83, 0x93 }; static const u8 FSCHER_REG_TEMP_AUTOP2[] = { 0x75, 0x85, 0x95 }; */ -static const int FSCHMD_NO_TEMP_SENSORS[6] = { 3, 3, 4, 3, 5, 11 }; +static const int FSCHMD_NO_TEMP_SENSORS[7] = { 3, 3, 4, 3, 5, 5, 11 }; /* temp status register bitmasks */ #define FSCHMD_TEMP_WORKING 0x01 @@ -228,6 +236,7 @@ static const struct i2c_device_id fschmd_id[] = { { "fscscy", fscscy }, { "fschrc", fschrc }, { "fschmd", fschmd }, + { "fschds", fschds }, { "fscsyl", fscsyl }, { } }; @@ -1021,6 +1030,8 @@ static int fschmd_detect(struct i2c_client *client, int kind, kind = fschrc; else if (!strcmp(id, "HMD")) kind = fschmd; + else if (!strcmp(id, "HDS")) + kind = fschds; else if (!strcmp(id, "SYL")) kind = fscsyl; else @@ -1036,8 +1047,8 @@ static int fschmd_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct fschmd_data *data; - const char * const names[6] = { "Poseidon", "Hermes", "Scylla", - "Heracles", "Heimdall", "Syleus" }; + const char * const names[7] = { "Poseidon", "Hermes", "Scylla", + "Heracles", "Heimdall", "Hades", "Syleus" }; const int watchdog_minors[] = { WATCHDOG_MINOR, 212, 213, 214, 215 }; int i, err; enum chips kind = id->driver_data; @@ -1320,8 +1331,8 @@ static void __exit fschmd_exit(void) } MODULE_AUTHOR("Hans de Goede "); -MODULE_DESCRIPTION("FSC Poseidon, Hermes, Scylla, Heracles, Heimdall and " - "Syleus driver"); +MODULE_DESCRIPTION("FSC Poseidon, Hermes, Scylla, Heracles, Heimdall, Hades " + "and Syleus driver"); MODULE_LICENSE("GPL"); module_init(fschmd_init); -- cgit v1.2.3 From 99b76233803beab302123d243eea9e41149804f3 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Wed, 25 Mar 2009 22:48:06 +0300 Subject: proc 2/2: remove struct proc_dir_entry::owner Setting ->owner as done currently (pde->owner = THIS_MODULE) is racy as correctly noted at bug #12454. Someone can lookup entry with NULL ->owner, thus not pinning enything, and release it later resulting in module refcount underflow. We can keep ->owner and supply it at registration time like ->proc_fops and ->data. But this leaves ->owner as easy-manipulative field (just one C assignment) and somebody will forget to unpin previous/pin current module when switching ->owner. ->proc_fops is declared as "const" which should give some thoughts. ->read_proc/->write_proc were just fixed to not require ->owner for protection. rmmod'ed directories will be empty and return "." and ".." -- no harm. And directories with tricky enough readdir and lookup shouldn't be modular. We definitely don't want such modular code. Removing ->owner will also make PDE smaller. So, let's nuke it. Kudos to Jeff Layton for reminding about this, let's say, oversight. http://bugzilla.kernel.org/show_bug.cgi?id=12454 Signed-off-by: Alexey Dobriyan --- Documentation/DocBook/procfs_example.c | 9 --------- arch/alpha/kernel/srm_env.c | 5 ----- arch/blackfin/mm/sram-alloc.c | 1 - arch/ia64/kernel/palinfo.c | 2 -- arch/ia64/sn/kernel/sn2/prominfo_proc.c | 9 ++------- arch/powerpc/kernel/rtas_flash.c | 1 - arch/sparc/kernel/led.c | 1 - arch/x86/kernel/cpu/mtrr/if.c | 10 +--------- drivers/acpi/ac.c | 1 - drivers/acpi/battery.c | 1 - drivers/acpi/button.c | 3 --- drivers/acpi/fan.c | 2 -- drivers/acpi/processor_core.c | 2 -- drivers/acpi/sbs.c | 1 - drivers/acpi/thermal.c | 2 -- drivers/acpi/video.c | 5 ----- drivers/block/ps3vram.c | 2 -- drivers/char/ipmi/ipmi_msghandler.c | 12 ++++------- drivers/char/ipmi/ipmi_si_intf.c | 6 +++--- drivers/input/input.c | 2 -- drivers/isdn/hardware/eicon/divasi.c | 1 - drivers/media/video/cpia.c | 4 +--- drivers/message/i2o/i2o_proc.c | 2 -- drivers/net/bonding/bond_main.c | 35 ++------------------------------- drivers/net/irda/vlsi_ir.c | 7 ------- drivers/net/wireless/airo.c | 1 - drivers/platform/x86/asus_acpi.c | 3 --- drivers/platform/x86/thinkpad_acpi.c | 2 -- drivers/platform/x86/toshiba_acpi.c | 3 --- drivers/rtc/rtc-proc.c | 10 ++-------- drivers/s390/block/dasd_proc.c | 2 -- drivers/scsi/scsi_devinfo.c | 2 -- drivers/scsi/scsi_proc.c | 3 --- drivers/video/via/viafbdev.c | 5 ----- fs/afs/proc.c | 1 - fs/cifs/cifs_debug.c | 1 - fs/jfs/jfs_debug.c | 1 - fs/nfs/client.c | 2 -- fs/proc/inode.c | 19 +++--------------- fs/proc/proc_tty.c | 1 - fs/reiserfs/procfs.c | 5 +---- include/linux/ipmi_smi.h | 2 +- include/linux/proc_fs.h | 4 ---- net/appletalk/atalk_proc.c | 1 - net/atm/mpoa_proc.c | 1 - net/atm/proc.c | 1 - net/can/bcm.c | 4 ---- net/can/proc.c | 2 -- net/core/pktgen.c | 1 - net/irda/irproc.c | 1 - net/llc/llc_proc.c | 1 - net/sctp/protocol.c | 8 ++------ net/sunrpc/cache.c | 4 ---- net/sunrpc/stats.c | 10 ++-------- sound/core/info.c | 31 ++--------------------------- 55 files changed, 26 insertions(+), 232 deletions(-) (limited to 'drivers') diff --git a/Documentation/DocBook/procfs_example.c b/Documentation/DocBook/procfs_example.c index 8c6396e4bf31..a5b11793b1e0 100644 --- a/Documentation/DocBook/procfs_example.c +++ b/Documentation/DocBook/procfs_example.c @@ -117,9 +117,6 @@ static int __init init_procfs_example(void) rv = -ENOMEM; goto out; } - - example_dir->owner = THIS_MODULE; - /* create jiffies using convenience function */ jiffies_file = create_proc_read_entry("jiffies", 0444, example_dir, @@ -130,8 +127,6 @@ static int __init init_procfs_example(void) goto no_jiffies; } - jiffies_file->owner = THIS_MODULE; - /* create foo and bar files using same callback * functions */ @@ -146,7 +141,6 @@ static int __init init_procfs_example(void) foo_file->data = &foo_data; foo_file->read_proc = proc_read_foobar; foo_file->write_proc = proc_write_foobar; - foo_file->owner = THIS_MODULE; bar_file = create_proc_entry("bar", 0644, example_dir); if(bar_file == NULL) { @@ -159,7 +153,6 @@ static int __init init_procfs_example(void) bar_file->data = &bar_data; bar_file->read_proc = proc_read_foobar; bar_file->write_proc = proc_write_foobar; - bar_file->owner = THIS_MODULE; /* create symlink */ symlink = proc_symlink("jiffies_too", example_dir, @@ -169,8 +162,6 @@ static int __init init_procfs_example(void) goto no_symlink; } - symlink->owner = THIS_MODULE; - /* everything OK */ printk(KERN_INFO "%s %s initialised\n", MODULE_NAME, MODULE_VERS); diff --git a/arch/alpha/kernel/srm_env.c b/arch/alpha/kernel/srm_env.c index 78ad7cd1bbd6..d12af472e1c0 100644 --- a/arch/alpha/kernel/srm_env.c +++ b/arch/alpha/kernel/srm_env.c @@ -218,7 +218,6 @@ srm_env_init(void) BASE_DIR); goto cleanup; } - base_dir->owner = THIS_MODULE; /* * Create per-name subdirectory @@ -229,7 +228,6 @@ srm_env_init(void) BASE_DIR, NAMED_DIR); goto cleanup; } - named_dir->owner = THIS_MODULE; /* * Create per-number subdirectory @@ -241,7 +239,6 @@ srm_env_init(void) goto cleanup; } - numbered_dir->owner = THIS_MODULE; /* * Create all named nodes @@ -254,7 +251,6 @@ srm_env_init(void) goto cleanup; entry->proc_entry->data = (void *) entry; - entry->proc_entry->owner = THIS_MODULE; entry->proc_entry->read_proc = srm_env_read; entry->proc_entry->write_proc = srm_env_write; @@ -275,7 +271,6 @@ srm_env_init(void) entry->id = var_num; entry->proc_entry->data = (void *) entry; - entry->proc_entry->owner = THIS_MODULE; entry->proc_entry->read_proc = srm_env_read; entry->proc_entry->write_proc = srm_env_write; } diff --git a/arch/blackfin/mm/sram-alloc.c b/arch/blackfin/mm/sram-alloc.c index 834cab7438a8..530d1393a232 100644 --- a/arch/blackfin/mm/sram-alloc.c +++ b/arch/blackfin/mm/sram-alloc.c @@ -854,7 +854,6 @@ static int __init sram_proc_init(void) printk(KERN_WARNING "unable to create /proc/sram\n"); return -1; } - ptr->owner = THIS_MODULE; ptr->read_proc = sram_proc_read; return 0; } diff --git a/arch/ia64/kernel/palinfo.c b/arch/ia64/kernel/palinfo.c index e5c57f413ca2..a4f19c70aadd 100644 --- a/arch/ia64/kernel/palinfo.c +++ b/arch/ia64/kernel/palinfo.c @@ -1002,8 +1002,6 @@ create_palinfo_proc_entries(unsigned int cpu) *pdir = create_proc_read_entry( palinfo_entries[j].name, 0, cpu_dir, palinfo_read_entry, (void *)f.value); - if (*pdir) - (*pdir)->owner = THIS_MODULE; pdir++; } } diff --git a/arch/ia64/sn/kernel/sn2/prominfo_proc.c b/arch/ia64/sn/kernel/sn2/prominfo_proc.c index 4dcce3d0e04c..e63328818643 100644 --- a/arch/ia64/sn/kernel/sn2/prominfo_proc.c +++ b/arch/ia64/sn/kernel/sn2/prominfo_proc.c @@ -225,7 +225,6 @@ static struct proc_dir_entry *sgi_prominfo_entry; int __init prominfo_init(void) { struct proc_dir_entry **entp; - struct proc_dir_entry *p; cnodeid_t cnodeid; unsigned long nasid; int size; @@ -246,14 +245,10 @@ int __init prominfo_init(void) sprintf(name, "node%d", cnodeid); *entp = proc_mkdir(name, sgi_prominfo_entry); nasid = cnodeid_to_nasid(cnodeid); - p = create_proc_read_entry("fit", 0, *entp, read_fit_entry, + create_proc_read_entry("fit", 0, *entp, read_fit_entry, (void *)nasid); - if (p) - p->owner = THIS_MODULE; - p = create_proc_read_entry("version", 0, *entp, + create_proc_read_entry("version", 0, *entp, read_version_entry, (void *)nasid); - if (p) - p->owner = THIS_MODULE; entp++; } diff --git a/arch/powerpc/kernel/rtas_flash.c b/arch/powerpc/kernel/rtas_flash.c index 149cb112cd1a..13011a96a977 100644 --- a/arch/powerpc/kernel/rtas_flash.c +++ b/arch/powerpc/kernel/rtas_flash.c @@ -669,7 +669,6 @@ static void remove_flash_pde(struct proc_dir_entry *dp) { if (dp) { kfree(dp->data); - dp->owner = NULL; remove_proc_entry(dp->name, dp->parent); } } diff --git a/arch/sparc/kernel/led.c b/arch/sparc/kernel/led.c index adaaed4ea2fb..00d034ea2164 100644 --- a/arch/sparc/kernel/led.c +++ b/arch/sparc/kernel/led.c @@ -126,7 +126,6 @@ static int __init led_init(void) led = proc_create("led", 0, NULL, &led_proc_fops); if (!led) return -ENOMEM; - led->owner = THIS_MODULE; printk(KERN_INFO "led: version %s, Lars Kotthoff \n", diff --git a/arch/x86/kernel/cpu/mtrr/if.c b/arch/x86/kernel/cpu/mtrr/if.c index 4c4214690dd1..fb73a52913a4 100644 --- a/arch/x86/kernel/cpu/mtrr/if.c +++ b/arch/x86/kernel/cpu/mtrr/if.c @@ -377,10 +377,6 @@ static const struct file_operations mtrr_fops = { .release = mtrr_close, }; - -static struct proc_dir_entry *proc_root_mtrr; - - static int mtrr_seq_show(struct seq_file *seq, void *offset) { char factor; @@ -423,11 +419,7 @@ static int __init mtrr_if_init(void) (!cpu_has(c, X86_FEATURE_CENTAUR_MCR))) return -ENODEV; - proc_root_mtrr = - proc_create("mtrr", S_IWUSR | S_IRUGO, NULL, &mtrr_fops); - - if (proc_root_mtrr) - proc_root_mtrr->owner = THIS_MODULE; + proc_create("mtrr", S_IWUSR | S_IRUGO, NULL, &mtrr_fops); return 0; } diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c index 9b917dac7732..88e42abf5d88 100644 --- a/drivers/acpi/ac.c +++ b/drivers/acpi/ac.c @@ -191,7 +191,6 @@ static int acpi_ac_add_fs(struct acpi_device *device) acpi_ac_dir); if (!acpi_device_dir(device)) return -ENODEV; - acpi_device_dir(device)->owner = THIS_MODULE; } /* 'state' [R] */ diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index 69cbc57c2d1c..3bcb5bfc45d3 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -760,7 +760,6 @@ static int acpi_battery_add_fs(struct acpi_device *device) acpi_battery_dir); if (!acpi_device_dir(device)) return -ENODEV; - acpi_device_dir(device)->owner = THIS_MODULE; } for (i = 0; i < ACPI_BATTERY_NUMFILES; ++i) { diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index 171fd914f435..c2f06069dcd4 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -200,12 +200,10 @@ static int acpi_button_add_fs(struct acpi_device *device) if (!entry) return -ENODEV; - entry->owner = THIS_MODULE; acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), entry); if (!acpi_device_dir(device)) return -ENODEV; - acpi_device_dir(device)->owner = THIS_MODULE; /* 'info' [R] */ entry = proc_create_data(ACPI_BUTTON_FILE_INFO, @@ -522,7 +520,6 @@ static int __init acpi_button_init(void) acpi_button_dir = proc_mkdir(ACPI_BUTTON_CLASS, acpi_root_dir); if (!acpi_button_dir) return -ENODEV; - acpi_button_dir->owner = THIS_MODULE; result = acpi_bus_register_driver(&acpi_button_driver); if (result < 0) { remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir); diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c index eaaee1660bdf..8a02944bf92d 100644 --- a/drivers/acpi/fan.c +++ b/drivers/acpi/fan.c @@ -193,7 +193,6 @@ static int acpi_fan_add_fs(struct acpi_device *device) acpi_fan_dir); if (!acpi_device_dir(device)) return -ENODEV; - acpi_device_dir(device)->owner = THIS_MODULE; } /* 'status' [R/W] */ @@ -347,7 +346,6 @@ static int __init acpi_fan_init(void) acpi_fan_dir = proc_mkdir(ACPI_FAN_CLASS, acpi_root_dir); if (!acpi_fan_dir) return -ENODEV; - acpi_fan_dir->owner = THIS_MODULE; #endif result = acpi_bus_register_driver(&acpi_fan_driver); diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index 0cc2fd31e376..fa2f7422d23d 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -359,7 +359,6 @@ static int acpi_processor_add_fs(struct acpi_device *device) if (!acpi_device_dir(device)) return -ENODEV; } - acpi_device_dir(device)->owner = THIS_MODULE; /* 'info' [R] */ entry = proc_create_data(ACPI_PROCESSOR_FILE_INFO, @@ -1137,7 +1136,6 @@ static int __init acpi_processor_init(void) acpi_processor_dir = proc_mkdir(ACPI_PROCESSOR_CLASS, acpi_root_dir); if (!acpi_processor_dir) return -ENOMEM; - acpi_processor_dir->owner = THIS_MODULE; /* * Check whether the system is DMI table. If yes, OSPM diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c index 6050ce481873..59afd52ccc12 100644 --- a/drivers/acpi/sbs.c +++ b/drivers/acpi/sbs.c @@ -488,7 +488,6 @@ acpi_sbs_add_fs(struct proc_dir_entry **dir, if (!*dir) { return -ENODEV; } - (*dir)->owner = THIS_MODULE; } /* 'info' [R] */ diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 99e6f1f8ea45..c11f9aeca706 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -1506,7 +1506,6 @@ static int acpi_thermal_add_fs(struct acpi_device *device) acpi_thermal_dir); if (!acpi_device_dir(device)) return -ENODEV; - acpi_device_dir(device)->owner = THIS_MODULE; } /* 'state' [R] */ @@ -1875,7 +1874,6 @@ static int __init acpi_thermal_init(void) acpi_thermal_dir = proc_mkdir(ACPI_THERMAL_CLASS, acpi_root_dir); if (!acpi_thermal_dir) return -ENODEV; - acpi_thermal_dir->owner = THIS_MODULE; result = acpi_bus_register_driver(&acpi_thermal_driver); if (result < 0) { diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index bb5ed059114a..67cc36dc9b82 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -1125,8 +1125,6 @@ static int acpi_video_device_add_fs(struct acpi_device *device) if (!device_dir) return -ENOMEM; - device_dir->owner = THIS_MODULE; - /* 'info' [R] */ entry = proc_create_data("info", S_IRUGO, device_dir, &acpi_video_device_info_fops, acpi_driver_data(device)); @@ -1403,8 +1401,6 @@ static int acpi_video_bus_add_fs(struct acpi_device *device) if (!device_dir) return -ENOMEM; - device_dir->owner = THIS_MODULE; - /* 'info' [R] */ entry = proc_create_data("info", S_IRUGO, device_dir, &acpi_video_bus_info_fops, @@ -2131,7 +2127,6 @@ static int __init acpi_video_init(void) acpi_video_dir = proc_mkdir(ACPI_VIDEO_CLASS, acpi_root_dir); if (!acpi_video_dir) return -ENODEV; - acpi_video_dir->owner = THIS_MODULE; result = acpi_bus_register_driver(&acpi_video_bus); if (result < 0) { diff --git a/drivers/block/ps3vram.c b/drivers/block/ps3vram.c index 393ed6760d78..8eddef373a91 100644 --- a/drivers/block/ps3vram.c +++ b/drivers/block/ps3vram.c @@ -551,8 +551,6 @@ static void __devinit ps3vram_proc_init(struct ps3_system_bus_device *dev) dev_warn(&dev->core, "failed to create /proc entry\n"); return; } - - pde->owner = THIS_MODULE; pde->data = priv; } diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 7a88dfd4427b..e93fc8d22fb2 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -1944,7 +1944,7 @@ static int stat_file_read_proc(char *page, char **start, off_t off, int ipmi_smi_add_proc_entry(ipmi_smi_t smi, char *name, read_proc_t *read_proc, - void *data, struct module *owner) + void *data) { int rv = 0; #ifdef CONFIG_PROC_FS @@ -1970,7 +1970,6 @@ int ipmi_smi_add_proc_entry(ipmi_smi_t smi, char *name, } else { file->data = data; file->read_proc = read_proc; - file->owner = owner; mutex_lock(&smi->proc_entry_lock); /* Stick it on the list. */ @@ -1993,23 +1992,21 @@ static int add_proc_entries(ipmi_smi_t smi, int num) smi->proc_dir = proc_mkdir(smi->proc_dir_name, proc_ipmi_root); if (!smi->proc_dir) rv = -ENOMEM; - else - smi->proc_dir->owner = THIS_MODULE; if (rv == 0) rv = ipmi_smi_add_proc_entry(smi, "stats", stat_file_read_proc, - smi, THIS_MODULE); + smi); if (rv == 0) rv = ipmi_smi_add_proc_entry(smi, "ipmb", ipmb_file_read_proc, - smi, THIS_MODULE); + smi); if (rv == 0) rv = ipmi_smi_add_proc_entry(smi, "version", version_file_read_proc, - smi, THIS_MODULE); + smi); #endif /* CONFIG_PROC_FS */ return rv; @@ -4265,7 +4262,6 @@ static int ipmi_init_msghandler(void) return -ENOMEM; } - proc_ipmi_root->owner = THIS_MODULE; #endif /* CONFIG_PROC_FS */ setup_timer(&ipmi_timer, ipmi_timeout, 0); diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index 3000135f2ead..e58ea4cd55ce 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -2899,7 +2899,7 @@ static int try_smi_init(struct smi_info *new_smi) rv = ipmi_smi_add_proc_entry(new_smi->intf, "type", type_file_read_proc, - new_smi, THIS_MODULE); + new_smi); if (rv) { printk(KERN_ERR "ipmi_si: Unable to create proc entry: %d\n", @@ -2909,7 +2909,7 @@ static int try_smi_init(struct smi_info *new_smi) rv = ipmi_smi_add_proc_entry(new_smi->intf, "si_stats", stat_file_read_proc, - new_smi, THIS_MODULE); + new_smi); if (rv) { printk(KERN_ERR "ipmi_si: Unable to create proc entry: %d\n", @@ -2919,7 +2919,7 @@ static int try_smi_init(struct smi_info *new_smi) rv = ipmi_smi_add_proc_entry(new_smi->intf, "params", param_read_proc, - new_smi, THIS_MODULE); + new_smi); if (rv) { printk(KERN_ERR "ipmi_si: Unable to create proc entry: %d\n", diff --git a/drivers/input/input.c b/drivers/input/input.c index 1730d7331a5d..ec3db3ade118 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -903,8 +903,6 @@ static int __init input_proc_init(void) if (!proc_bus_input_dir) return -ENOMEM; - proc_bus_input_dir->owner = THIS_MODULE; - entry = proc_create("devices", 0, proc_bus_input_dir, &input_devices_fileops); if (!entry) diff --git a/drivers/isdn/hardware/eicon/divasi.c b/drivers/isdn/hardware/eicon/divasi.c index f4969fe0a055..69e71ebe7841 100644 --- a/drivers/isdn/hardware/eicon/divasi.c +++ b/drivers/isdn/hardware/eicon/divasi.c @@ -118,7 +118,6 @@ static int DIVA_INIT_FUNCTION create_um_idi_proc(void) return (0); um_idi_proc_entry->read_proc = um_idi_proc_read; - um_idi_proc_entry->owner = THIS_MODULE; return (1); } diff --git a/drivers/media/video/cpia.c b/drivers/media/video/cpia.c index c3b0c8c63c76..43ab0adf3b61 100644 --- a/drivers/media/video/cpia.c +++ b/drivers/media/video/cpia.c @@ -1381,9 +1381,7 @@ static void proc_cpia_create(void) { cpia_proc_root = proc_mkdir("cpia", NULL); - if (cpia_proc_root) - cpia_proc_root->owner = THIS_MODULE; - else + if (!cpia_proc_root) LOG("Unable to initialise /proc/cpia\n"); } diff --git a/drivers/message/i2o/i2o_proc.c b/drivers/message/i2o/i2o_proc.c index 9a36b5a7de57..7045c45da9b1 100644 --- a/drivers/message/i2o/i2o_proc.c +++ b/drivers/message/i2o/i2o_proc.c @@ -2037,8 +2037,6 @@ static int __init i2o_proc_fs_create(void) if (!i2o_proc_dir_root) return -1; - i2o_proc_dir_root->owner = THIS_MODULE; - list_for_each_entry(c, &i2o_controllers, list) i2o_proc_iop_add(i2o_proc_dir_root, c); diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 9c326a50a3ee..99610f358c40 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -3444,25 +3444,12 @@ static void bond_remove_proc_entry(struct bonding *bond) */ static void bond_create_proc_dir(void) { - int len = strlen(DRV_NAME); - - for (bond_proc_dir = init_net.proc_net->subdir; bond_proc_dir; - bond_proc_dir = bond_proc_dir->next) { - if ((bond_proc_dir->namelen == len) && - !memcmp(bond_proc_dir->name, DRV_NAME, len)) { - break; - } - } - if (!bond_proc_dir) { bond_proc_dir = proc_mkdir(DRV_NAME, init_net.proc_net); - if (bond_proc_dir) { - bond_proc_dir->owner = THIS_MODULE; - } else { + if (!bond_proc_dir) printk(KERN_WARNING DRV_NAME ": Warning: cannot create /proc/net/%s\n", DRV_NAME); - } } } @@ -3471,25 +3458,7 @@ static void bond_create_proc_dir(void) */ static void bond_destroy_proc_dir(void) { - struct proc_dir_entry *de; - - if (!bond_proc_dir) { - return; - } - - /* verify that the /proc dir is empty */ - for (de = bond_proc_dir->subdir; de; de = de->next) { - /* ignore . and .. */ - if (*(de->name) != '.') { - break; - } - } - - if (de) { - if (bond_proc_dir->owner == THIS_MODULE) { - bond_proc_dir->owner = NULL; - } - } else { + if (bond_proc_dir) { remove_proc_entry(DRV_NAME, init_net.proc_net); bond_proc_dir = NULL; } diff --git a/drivers/net/irda/vlsi_ir.c b/drivers/net/irda/vlsi_ir.c index 1243bc8e0035..ac0e4b6b6b66 100644 --- a/drivers/net/irda/vlsi_ir.c +++ b/drivers/net/irda/vlsi_ir.c @@ -1871,13 +1871,6 @@ static int __init vlsi_mod_init(void) * without procfs - it's not required for the driver to work. */ vlsi_proc_root = proc_mkdir(PROC_DIR, NULL); - if (vlsi_proc_root) { - /* protect registered procdir against module removal. - * Because we are in the module init path there's no race - * window after create_proc_entry (and no barrier needed). - */ - vlsi_proc_root->owner = THIS_MODULE; - } ret = pci_register_driver(&vlsi_irda_driver); diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c index 7e80aba8a148..31b1cc2b778a 100644 --- a/drivers/net/wireless/airo.c +++ b/drivers/net/wireless/airo.c @@ -4494,7 +4494,6 @@ static int setup_proc_entry( struct net_device *dev, goto fail; apriv->proc_entry->uid = proc_uid; apriv->proc_entry->gid = proc_gid; - apriv->proc_entry->owner = THIS_MODULE; /* Setup the StatsDelta */ entry = proc_create_data("StatsDelta", diff --git a/drivers/platform/x86/asus_acpi.c b/drivers/platform/x86/asus_acpi.c index d63f26e666a4..ba1f7497e4b9 100644 --- a/drivers/platform/x86/asus_acpi.c +++ b/drivers/platform/x86/asus_acpi.c @@ -987,7 +987,6 @@ asus_proc_add(char *name, proc_writefunc *writefunc, proc->write_proc = writefunc; proc->read_proc = readfunc; proc->data = acpi_driver_data(device); - proc->owner = THIS_MODULE; proc->uid = asus_uid; proc->gid = asus_gid; return 0; @@ -1020,7 +1019,6 @@ static int asus_hotk_add_fs(struct acpi_device *device) if (proc) { proc->read_proc = proc_read_info; proc->data = acpi_driver_data(device); - proc->owner = THIS_MODULE; proc->uid = asus_uid; proc->gid = asus_gid; } else { @@ -1436,7 +1434,6 @@ static int __init asus_acpi_init(void) printk(KERN_ERR "Asus ACPI: Unable to create /proc entry\n"); return -ENODEV; } - asus_proc_dir->owner = THIS_MODULE; result = acpi_bus_register_driver(&asus_hotk_driver); if (result < 0) { diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index d2433204a40c..3dad27a385d3 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -6992,7 +6992,6 @@ static int __init ibm_init(struct ibm_init_struct *iibm) ret = -ENODEV; goto err_out; } - entry->owner = THIS_MODULE; entry->data = ibm; entry->read_proc = &dispatch_procfs_read; if (ibm->write) @@ -7405,7 +7404,6 @@ static int __init thinkpad_acpi_module_init(void) thinkpad_acpi_module_exit(); return -ENODEV; } - proc_dir->owner = THIS_MODULE; ret = platform_driver_register(&tpacpi_pdriver); if (ret) { diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 40e60fc2e596..9f187265db8e 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -679,8 +679,6 @@ static acpi_status __init add_device(void) toshiba_proc_dir, (read_proc_t *) dispatch_read, item); - if (proc) - proc->owner = THIS_MODULE; if (proc && item->write_func) proc->write_proc = (write_proc_t *) dispatch_write; } @@ -772,7 +770,6 @@ static int __init toshiba_acpi_init(void) toshiba_acpi_exit(); return -ENODEV; } else { - toshiba_proc_dir->owner = THIS_MODULE; status = add_device(); if (ACPI_FAILURE(status)) { toshiba_acpi_exit(); diff --git a/drivers/rtc/rtc-proc.c b/drivers/rtc/rtc-proc.c index 0c6257a034ff..c086fc30a84c 100644 --- a/drivers/rtc/rtc-proc.c +++ b/drivers/rtc/rtc-proc.c @@ -105,14 +105,8 @@ static const struct file_operations rtc_proc_fops = { void rtc_proc_add_device(struct rtc_device *rtc) { - if (rtc->id == 0) { - struct proc_dir_entry *ent; - - ent = proc_create_data("driver/rtc", 0, NULL, - &rtc_proc_fops, rtc); - if (ent) - ent->owner = rtc->owner; - } + if (rtc->id == 0) + proc_create_data("driver/rtc", 0, NULL, &rtc_proc_fops, rtc); } void rtc_proc_del_device(struct rtc_device *rtc) diff --git a/drivers/s390/block/dasd_proc.c b/drivers/s390/block/dasd_proc.c index 2080ba6a69b0..654daa3cdfda 100644 --- a/drivers/s390/block/dasd_proc.c +++ b/drivers/s390/block/dasd_proc.c @@ -320,7 +320,6 @@ dasd_proc_init(void) dasd_proc_root_entry = proc_mkdir("dasd", NULL); if (!dasd_proc_root_entry) goto out_nodasd; - dasd_proc_root_entry->owner = THIS_MODULE; dasd_devices_entry = proc_create("devices", S_IFREG | S_IRUGO | S_IWUSR, dasd_proc_root_entry, @@ -334,7 +333,6 @@ dasd_proc_init(void) goto out_nostatistics; dasd_statistics_entry->read_proc = dasd_statistics_read; dasd_statistics_entry->write_proc = dasd_statistics_write; - dasd_statistics_entry->owner = THIS_MODULE; return 0; out_nostatistics: diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c index 099b5455bbce..b13481369642 100644 --- a/drivers/scsi/scsi_devinfo.c +++ b/drivers/scsi/scsi_devinfo.c @@ -596,8 +596,6 @@ int __init scsi_init_devinfo(void) error = -ENOMEM; goto out; } - - p->owner = THIS_MODULE; #endif /* CONFIG_SCSI_PROC_FS */ out: diff --git a/drivers/scsi/scsi_proc.c b/drivers/scsi/scsi_proc.c index 82f7b2dd08a2..77fbddb507fd 100644 --- a/drivers/scsi/scsi_proc.c +++ b/drivers/scsi/scsi_proc.c @@ -115,8 +115,6 @@ void scsi_proc_hostdir_add(struct scsi_host_template *sht) if (!sht->proc_dir) printk(KERN_ERR "%s: proc_mkdir failed for %s\n", __func__, sht->proc_name); - else - sht->proc_dir->owner = sht->module; } mutex_unlock(&global_host_template_mutex); } @@ -163,7 +161,6 @@ void scsi_proc_host_add(struct Scsi_Host *shost) } p->write_proc = proc_scsi_write_proc; - p->owner = sht->module; } /** diff --git a/drivers/video/via/viafbdev.c b/drivers/video/via/viafbdev.c index 37b433a08ce8..e327b84820d2 100644 --- a/drivers/video/via/viafbdev.c +++ b/drivers/video/via/viafbdev.c @@ -2059,25 +2059,21 @@ static void viafb_init_proc(struct proc_dir_entry **viafb_entry) if (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); 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); 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); if (entry) { - entry->owner = THIS_MODULE; entry->read_proc = viafb_dfpl_proc_read; entry->write_proc = viafb_dfpl_proc_write; } @@ -2086,7 +2082,6 @@ static void viafb_init_proc(struct proc_dir_entry **viafb_entry) viaparinfo->chip_info->lvds_chip_info2.lvds_chip_name) { entry = create_proc_entry("vt1636", 0, *viafb_entry); if (entry) { - entry->owner = THIS_MODULE; entry->read_proc = viafb_vt1636_proc_read; entry->write_proc = viafb_vt1636_proc_write; } diff --git a/fs/afs/proc.c b/fs/afs/proc.c index 7578c1ab9e0b..8630615e57fe 100644 --- a/fs/afs/proc.c +++ b/fs/afs/proc.c @@ -146,7 +146,6 @@ int afs_proc_init(void) proc_afs = proc_mkdir("fs/afs", NULL); if (!proc_afs) goto error_dir; - proc_afs->owner = THIS_MODULE; p = proc_create("cells", 0, proc_afs, &afs_proc_cells_fops); if (!p) diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index 877e4d9a1159..7f19fefd3d45 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c @@ -404,7 +404,6 @@ cifs_proc_init(void) if (proc_fs_cifs == NULL) return; - proc_fs_cifs->owner = THIS_MODULE; proc_create("DebugData", 0, proc_fs_cifs, &cifs_debug_data_proc_fops); #ifdef CONFIG_CIFS_STATS diff --git a/fs/jfs/jfs_debug.c b/fs/jfs/jfs_debug.c index 6a73de84bcef..dd824d9b0b1a 100644 --- a/fs/jfs/jfs_debug.c +++ b/fs/jfs/jfs_debug.c @@ -90,7 +90,6 @@ void jfs_proc_init(void) if (!(base = proc_mkdir("fs/jfs", NULL))) return; - base->owner = THIS_MODULE; for (i = 0; i < NPROCENT; i++) proc_create(Entries[i].name, 0, base, Entries[i].proc_fops); diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 574158ae2398..2277421656e7 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -1606,8 +1606,6 @@ int __init nfs_fs_proc_init(void) if (!proc_fs_nfs) goto error_0; - proc_fs_nfs->owner = THIS_MODULE; - /* a file of servers with which we're dealing */ p = proc_create("servers", S_IFREG|S_IRUGO, proc_fs_nfs, &nfs_server_list_fops); diff --git a/fs/proc/inode.c b/fs/proc/inode.c index e11dc22c6511..d78ade305541 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -58,11 +58,8 @@ static void proc_delete_inode(struct inode *inode) /* Let go of any associated proc directory entry */ de = PROC_I(inode)->pde; - if (de) { - if (de->owner) - module_put(de->owner); + if (de) de_put(de); - } if (PROC_I(inode)->sysctl) sysctl_head_put(PROC_I(inode)->sysctl); clear_inode(inode); @@ -449,12 +446,9 @@ struct inode *proc_get_inode(struct super_block *sb, unsigned int ino, { struct inode * inode; - if (!try_module_get(de->owner)) - goto out_mod; - inode = iget_locked(sb, ino); if (!inode) - goto out_ino; + return NULL; if (inode->i_state & I_NEW) { inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; PROC_I(inode)->fd = 0; @@ -485,16 +479,9 @@ struct inode *proc_get_inode(struct super_block *sb, unsigned int ino, } } unlock_new_inode(inode); - } else { - module_put(de->owner); + } else de_put(de); - } return inode; - -out_ino: - module_put(de->owner); -out_mod: - return NULL; } int proc_fill_super(struct super_block *s) diff --git a/fs/proc/proc_tty.c b/fs/proc/proc_tty.c index d153946d6d15..4a9e0f65ae60 100644 --- a/fs/proc/proc_tty.c +++ b/fs/proc/proc_tty.c @@ -152,7 +152,6 @@ void proc_tty_register_driver(struct tty_driver *driver) if (!ent) return; ent->read_proc = driver->ops->read_proc; - ent->owner = driver->owner; ent->data = driver; driver->proc_entry = ent; diff --git a/fs/reiserfs/procfs.c b/fs/reiserfs/procfs.c index d5066400638a..9229e5514a4e 100644 --- a/fs/reiserfs/procfs.c +++ b/fs/reiserfs/procfs.c @@ -492,7 +492,6 @@ int reiserfs_proc_info_init(struct super_block *sb) spin_lock_init(&__PINFO(sb).lock); REISERFS_SB(sb)->procdir = proc_mkdir(b, proc_info_root); if (REISERFS_SB(sb)->procdir) { - REISERFS_SB(sb)->procdir->owner = THIS_MODULE; REISERFS_SB(sb)->procdir->data = sb; add_file(sb, "version", show_version); add_file(sb, "super", show_super); @@ -556,9 +555,7 @@ int reiserfs_proc_info_global_init(void) { if (proc_info_root == NULL) { proc_info_root = proc_mkdir(proc_info_root_name, NULL); - if (proc_info_root) { - proc_info_root->owner = THIS_MODULE; - } else { + if (!proc_info_root) { reiserfs_warning(NULL, "cannot create /proc/%s", proc_info_root_name); return 1; diff --git a/include/linux/ipmi_smi.h b/include/linux/ipmi_smi.h index 62b73668b602..f7c9c75a2775 100644 --- a/include/linux/ipmi_smi.h +++ b/include/linux/ipmi_smi.h @@ -230,6 +230,6 @@ static inline void ipmi_free_smi_msg(struct ipmi_smi_msg *msg) automatically be dstroyed when the interface is destroyed. */ int ipmi_smi_add_proc_entry(ipmi_smi_t smi, char *name, read_proc_t *read_proc, - void *data, struct module *owner); + void *data); #endif /* __LINUX_IPMI_SMI_H */ diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h index b8bdb96eff78..fbfa3d44d33d 100644 --- a/include/linux/proc_fs.h +++ b/include/linux/proc_fs.h @@ -41,9 +41,6 @@ enum { * while parent/subdir create the directory structure (every * /proc file has a parent, but "subdir" is NULL for all * non-directory entries). - * - * "owner" is used to protect module - * from unloading while proc_dir_entry is in use */ typedef int (read_proc_t)(char *page, char **start, off_t off, @@ -70,7 +67,6 @@ struct proc_dir_entry { * somewhere. */ const struct file_operations *proc_fops; - struct module *owner; struct proc_dir_entry *next, *parent, *subdir; void *data; read_proc_t *read_proc; diff --git a/net/appletalk/atalk_proc.c b/net/appletalk/atalk_proc.c index 162199a2d74f..fd8e0847b254 100644 --- a/net/appletalk/atalk_proc.c +++ b/net/appletalk/atalk_proc.c @@ -281,7 +281,6 @@ int __init atalk_proc_init(void) atalk_proc_dir = proc_mkdir("atalk", init_net.proc_net); if (!atalk_proc_dir) goto out; - atalk_proc_dir->owner = THIS_MODULE; p = proc_create("interface", S_IRUGO, atalk_proc_dir, &atalk_seq_interface_fops); diff --git a/net/atm/mpoa_proc.c b/net/atm/mpoa_proc.c index 4990541ef5da..1a0f5ccea9c4 100644 --- a/net/atm/mpoa_proc.c +++ b/net/atm/mpoa_proc.c @@ -281,7 +281,6 @@ int mpc_proc_init(void) printk(KERN_ERR "Unable to initialize /proc/atm/%s\n", STAT_FILE_NAME); return -ENOMEM; } - p->owner = THIS_MODULE; return 0; } diff --git a/net/atm/proc.c b/net/atm/proc.c index 49487b313f22..e7b3b273907d 100644 --- a/net/atm/proc.c +++ b/net/atm/proc.c @@ -476,7 +476,6 @@ int __init atm_proc_init(void) atm_proc_root, e->proc_fops); if (!dirent) goto err_out_remove; - dirent->owner = THIS_MODULE; e->dirent = dirent; } ret = 0; diff --git a/net/can/bcm.c b/net/can/bcm.c index b7c7d4651136..95d7f32643ae 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -1604,10 +1604,6 @@ static int __init bcm_module_init(void) /* create /proc/net/can-bcm directory */ proc_dir = proc_mkdir("can-bcm", init_net.proc_net); - - if (proc_dir) - proc_dir->owner = THIS_MODULE; - return 0; } diff --git a/net/can/proc.c b/net/can/proc.c index 520fef5e5398..1463653dbe34 100644 --- a/net/can/proc.c +++ b/net/can/proc.c @@ -473,8 +473,6 @@ void can_init_proc(void) return; } - can_dir->owner = THIS_MODULE; - /* own procfs entries from the AF_CAN core */ pde_version = can_create_proc_readentry(CAN_PROC_VERSION, 0644, can_proc_read_version, NULL); diff --git a/net/core/pktgen.c b/net/core/pktgen.c index 32d419f5ac98..3779c1438c11 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -3806,7 +3806,6 @@ static int __init pg_init(void) pg_proc_dir = proc_mkdir(PG_PROC_DIR, init_net.proc_net); if (!pg_proc_dir) return -ENODEV; - pg_proc_dir->owner = THIS_MODULE; pe = proc_create(PGCTRL, 0600, pg_proc_dir, &pktgen_fops); if (pe == NULL) { diff --git a/net/irda/irproc.c b/net/irda/irproc.c index 88e80a312732..8ff1861649e8 100644 --- a/net/irda/irproc.c +++ b/net/irda/irproc.c @@ -70,7 +70,6 @@ void __init irda_proc_register(void) proc_irda = proc_mkdir("irda", init_net.proc_net); if (proc_irda == NULL) return; - proc_irda->owner = THIS_MODULE; for (i = 0; i < ARRAY_SIZE(irda_dirs); i++) d = proc_create(irda_dirs[i].name, 0, proc_irda, diff --git a/net/llc/llc_proc.c b/net/llc/llc_proc.c index b58bd7c6cdf8..d208b3396d94 100644 --- a/net/llc/llc_proc.c +++ b/net/llc/llc_proc.c @@ -236,7 +236,6 @@ int __init llc_proc_init(void) llc_proc_dir = proc_mkdir("llc", init_net.proc_net); if (!llc_proc_dir) goto out; - llc_proc_dir->owner = THIS_MODULE; p = proc_create("socket", S_IRUGO, llc_proc_dir, &llc_seq_socket_fops); if (!p) diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index cb198af8887c..8eb3e61cb701 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -106,12 +106,8 @@ static __init int sctp_proc_init(void) goto out_nomem; #ifdef CONFIG_PROC_FS if (!proc_net_sctp) { - struct proc_dir_entry *ent; - ent = proc_mkdir("sctp", init_net.proc_net); - if (ent) { - ent->owner = THIS_MODULE; - proc_net_sctp = ent; - } else + proc_net_sctp = proc_mkdir("sctp", init_net.proc_net); + if (!proc_net_sctp) goto out_free_percpu; } diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index 4735caad26ed..20029a79a5de 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c @@ -313,7 +313,6 @@ static int create_cache_proc_entries(struct cache_detail *cd) cd->proc_ent = proc_mkdir(cd->name, proc_net_rpc); if (cd->proc_ent == NULL) goto out_nomem; - cd->proc_ent->owner = cd->owner; cd->channel_ent = cd->content_ent = NULL; p = proc_create_data("flush", S_IFREG|S_IRUSR|S_IWUSR, @@ -321,7 +320,6 @@ static int create_cache_proc_entries(struct cache_detail *cd) cd->flush_ent = p; if (p == NULL) goto out_nomem; - p->owner = cd->owner; if (cd->cache_request || cd->cache_parse) { p = proc_create_data("channel", S_IFREG|S_IRUSR|S_IWUSR, @@ -329,7 +327,6 @@ static int create_cache_proc_entries(struct cache_detail *cd) cd->channel_ent = p; if (p == NULL) goto out_nomem; - p->owner = cd->owner; } if (cd->cache_show) { p = proc_create_data("content", S_IFREG|S_IRUSR|S_IWUSR, @@ -337,7 +334,6 @@ static int create_cache_proc_entries(struct cache_detail *cd) cd->content_ent = p; if (p == NULL) goto out_nomem; - p->owner = cd->owner; } return 0; out_nomem: diff --git a/net/sunrpc/stats.c b/net/sunrpc/stats.c index 085372ef4feb..1ef6e46d9da2 100644 --- a/net/sunrpc/stats.c +++ b/net/sunrpc/stats.c @@ -262,14 +262,8 @@ void rpc_proc_init(void) { dprintk("RPC: registering /proc/net/rpc\n"); - if (!proc_net_rpc) { - struct proc_dir_entry *ent; - ent = proc_mkdir("rpc", init_net.proc_net); - if (ent) { - ent->owner = THIS_MODULE; - proc_net_rpc = ent; - } - } + if (!proc_net_rpc) + proc_net_rpc = proc_mkdir("rpc", init_net.proc_net); } void diff --git a/sound/core/info.c b/sound/core/info.c index 70fa87189f36..35df614f6c55 100644 --- a/sound/core/info.c +++ b/sound/core/info.c @@ -154,11 +154,6 @@ EXPORT_SYMBOL(snd_seq_root); struct snd_info_entry *snd_oss_root; #endif -static inline void snd_info_entry_prepare(struct proc_dir_entry *de) -{ - de->owner = THIS_MODULE; -} - static void snd_remove_proc_entry(struct proc_dir_entry *parent, struct proc_dir_entry *de) { @@ -522,32 +517,11 @@ static const struct file_operations snd_info_entry_operations = .release = snd_info_entry_release, }; -/** - * snd_create_proc_entry - create a procfs entry - * @name: the name of the proc file - * @mode: the file permission bits, S_Ixxx - * @parent: the parent proc-directory entry - * - * Creates a new proc file entry with the given name and permission - * on the given directory. - * - * Returns the pointer of new instance or NULL on failure. - */ -static struct proc_dir_entry *snd_create_proc_entry(const char *name, mode_t mode, - struct proc_dir_entry *parent) -{ - struct proc_dir_entry *p; - p = create_proc_entry(name, mode, parent); - if (p) - snd_info_entry_prepare(p); - return p; -} - int __init snd_info_init(void) { struct proc_dir_entry *p; - p = snd_create_proc_entry("asound", S_IFDIR | S_IRUGO | S_IXUGO, NULL); + p = create_proc_entry("asound", S_IFDIR | S_IRUGO | S_IXUGO, NULL); if (p == NULL) return -ENOMEM; snd_proc_root = p; @@ -974,12 +948,11 @@ int snd_info_register(struct snd_info_entry * entry) return -ENXIO; root = entry->parent == NULL ? snd_proc_root : entry->parent->p; mutex_lock(&info_mutex); - p = snd_create_proc_entry(entry->name, entry->mode, root); + p = create_proc_entry(entry->name, entry->mode, root); if (!p) { mutex_unlock(&info_mutex); return -ENOMEM; } - p->owner = entry->module; if (!S_ISDIR(entry->mode)) p->proc_fops = &snd_info_entry_operations; p->size = entry->size; -- cgit v1.2.3 From 5886cea45d1b230f86fd24de708b0d9f14ff88db Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Tue, 31 Mar 2009 19:13:13 +0200 Subject: [PATCH] sysrq: include interrupt.h instead of irq.h With "cpumask: update irq_desc to use cpumask_var_t" we get this build failure on s390: CC drivers/char/sysrq.o In file included from drivers/char/sysrq.c:38: include/linux/irq.h: In function 'init_alloc_desc_masks': include/linux/irq.h:442: error: dereferencing pointer to incomplete type drivers/char/sysrq.c should include interrupt.h instead of irq.h. Cc: Mike Travis Cc: Ingo Molnar Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- drivers/char/sysrq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c index 33a9351c896d..3df694e54545 100644 --- a/drivers/char/sysrq.c +++ b/drivers/char/sysrq.c @@ -35,7 +35,7 @@ #include #include #include -#include +#include #include #include -- cgit v1.2.3 From 156013ffd1225ef862853a4340b46f76845f8db1 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Tue, 31 Mar 2009 19:16:03 +0200 Subject: [S390] cio: wake up on failed recognition Wake up even on failed device recognition, since this may be triggered from a user trying to force a device online. With this patch a write to the online sysfs attribute will not block for ever but return with -EAGAIN in this case. Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device.c | 10 ++++++++-- drivers/s390/cio/device_fsm.c | 4 ++-- 2 files changed, 10 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index c4d2f667a2f6..9f016fef6101 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -482,17 +482,21 @@ static int online_store_recog_and_online(struct ccw_device *cdev) } wait_event(cdev->private->wait_q, cdev->private->flags.recog_done); + if (cdev->private->state != DEV_STATE_OFFLINE) + /* recognition failed */ + return -EAGAIN; } if (cdev->drv && cdev->drv->set_online) ccw_device_set_online(cdev); return 0; } + static int online_store_handle_online(struct ccw_device *cdev, int force) { int ret; ret = online_store_recog_and_online(cdev); - if (ret) + if (ret && !force) return ret; if (force && cdev->private->state == DEV_STATE_BOXED) { ret = ccw_device_stlck(cdev); @@ -500,7 +504,9 @@ static int online_store_handle_online(struct ccw_device *cdev, int force) return ret; if (cdev->id.cu_type == 0) cdev->private->state = DEV_STATE_NOT_OPER; - online_store_recog_and_online(cdev); + ret = online_store_recog_and_online(cdev); + if (ret) + return ret; } return 0; } diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 87b4bfca080f..ccd72f95765f 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -260,6 +260,7 @@ ccw_device_recog_done(struct ccw_device *cdev, int state) if (state == DEV_STATE_NOT_OPER) { cdev->private->flags.recog_done = 1; cdev->private->state = DEV_STATE_DISCONNECTED; + wake_up(&cdev->private->wait_q); return; } /* Boxed devices don't need extra treatment. */ @@ -311,8 +312,7 @@ ccw_device_recog_done(struct ccw_device *cdev, int state) } cdev->private->state = state; io_subchannel_recog_done(cdev); - if (state != DEV_STATE_NOT_OPER) - wake_up(&cdev->private->wait_q); + wake_up(&cdev->private->wait_q); } /* -- cgit v1.2.3 From c4621a62649a56f155a96dfc5de479be226f0768 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Tue, 31 Mar 2009 19:16:04 +0200 Subject: [S390] cio: introduce ccw_device_schedule_sch_unregister Introduce ccw_device_schedule_sch_unregister as a wrapper for queuing ccw_device_call_sch_unregister on the slow_path_wq. This wrapper will be used in the next patch. Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device.c | 21 +++++++++++---------- drivers/s390/cio/device.h | 1 + 2 files changed, 12 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 9f016fef6101..cdbf664ed446 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -310,8 +310,6 @@ static void ccw_device_remove_orphan_cb(struct work_struct *work) put_device(&cdev->dev); } -static void ccw_device_call_sch_unregister(struct work_struct *work); - static void ccw_device_remove_disconnected(struct ccw_device *cdev) { @@ -335,11 +333,10 @@ ccw_device_remove_disconnected(struct ccw_device *cdev) spin_unlock_irqrestore(cdev->ccwlock, flags); PREPARE_WORK(&cdev->private->kick_work, ccw_device_remove_orphan_cb); + queue_work(slow_path_wq, &cdev->private->kick_work); } else /* Deregister subchannel, which will kill the ccw device. */ - PREPARE_WORK(&cdev->private->kick_work, - ccw_device_call_sch_unregister); - queue_work(slow_path_wq, &cdev->private->kick_work); + ccw_device_schedule_sch_unregister(cdev); } /** @@ -1020,6 +1017,13 @@ static void ccw_device_call_sch_unregister(struct work_struct *work) put_device(&sch->dev); } +void ccw_device_schedule_sch_unregister(struct ccw_device *cdev) +{ + PREPARE_WORK(&cdev->private->kick_work, + ccw_device_call_sch_unregister); + queue_work(slow_path_wq, &cdev->private->kick_work); +} + /* * subchannel recognition done. Called from the state machine. */ @@ -1036,9 +1040,7 @@ io_subchannel_recog_done(struct ccw_device *cdev) /* Remove device found not operational. */ if (!get_device(&cdev->dev)) break; - PREPARE_WORK(&cdev->private->kick_work, - ccw_device_call_sch_unregister); - queue_work(slow_path_wq, &cdev->private->kick_work); + ccw_device_schedule_sch_unregister(cdev); if (atomic_dec_and_test(&ccw_device_init_count)) wake_up(&ccw_device_init_wq); break; @@ -1557,8 +1559,7 @@ static int purge_fn(struct device *dev, void *data) goto out; CIO_MSG_EVENT(3, "ccw: purging 0.%x.%04x\n", priv->dev_id.ssid, priv->dev_id.devno); - PREPARE_WORK(&cdev->private->kick_work, ccw_device_call_sch_unregister); - queue_work(slow_path_wq, &cdev->private->kick_work); + ccw_device_schedule_sch_unregister(cdev); out: /* Abort loop in case of pending signal. */ diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h index 85e01846ca65..f1cbbd94ad4e 100644 --- a/drivers/s390/cio/device.h +++ b/drivers/s390/cio/device.h @@ -87,6 +87,7 @@ int ccw_device_is_orphan(struct ccw_device *); int ccw_device_recognition(struct ccw_device *); int ccw_device_online(struct ccw_device *); int ccw_device_offline(struct ccw_device *); +void ccw_device_schedule_sch_unregister(struct ccw_device *); int ccw_purge_blacklisted(void); /* Function prototypes for device status and basic sense stuff. */ -- cgit v1.2.3 From 47593bfa1056d306fde067b28dd8617009be4121 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Tue, 31 Mar 2009 19:16:05 +0200 Subject: [S390] cio: introduce notifier for boxed state If a ccw device did not respond in time during internal io, we set it into boxed state. With this patch we have the following behaviour: * the ccw driver will get a notification if the device was online and goes into the boxed state * if the device was disconnected and got boxed nothing special is to be done (it will be handled in reprobing later) * if the device got boxed while initial sensing it will be unregistered Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/cio.h | 2 ++ drivers/s390/block/dasd.c | 1 + drivers/s390/cio/device.c | 4 ++-- drivers/s390/cio/device_fsm.c | 29 ++++++++++++++++++----------- drivers/s390/scsi/zfcp_ccw.c | 5 +++++ 5 files changed, 28 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/arch/s390/include/asm/cio.h b/arch/s390/include/asm/cio.h index 6dccb071aec3..619bf94b11f1 100644 --- a/arch/s390/include/asm/cio.h +++ b/arch/s390/include/asm/cio.h @@ -456,6 +456,8 @@ struct ciw { #define CIO_OPER 0x0004 /* Sick revalidation of device. */ #define CIO_REVALIDATE 0x0008 +/* Device did not respond in time. */ +#define CIO_BOXED 0x0010 /** * struct ccw_dev_id - unique identifier for ccw devices diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 2fd64e5a9ab2..0570794ccf1c 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -2363,6 +2363,7 @@ int dasd_generic_notify(struct ccw_device *cdev, int event) ret = 0; switch (event) { case CIO_GONE: + case CIO_BOXED: case CIO_NO_PATH: /* First of all call extended error reporting. */ dasd_eer_write(device, NULL, DASD_EER_NOPATH); diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index cdbf664ed446..868f8c6b053a 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -1035,6 +1035,8 @@ io_subchannel_recog_done(struct ccw_device *cdev) return; } switch (cdev->private->state) { + case DEV_STATE_BOXED: + /* Device did not respond in time. */ case DEV_STATE_NOT_OPER: cdev->private->flags.recog_done = 1; /* Remove device found not operational. */ @@ -1044,8 +1046,6 @@ io_subchannel_recog_done(struct ccw_device *cdev) if (atomic_dec_and_test(&ccw_device_init_count)) wake_up(&ccw_device_init_wq); break; - case DEV_STATE_BOXED: - /* Device did not respond in time. */ case DEV_STATE_OFFLINE: /* * We can't register the device in interrupt context so diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index ccd72f95765f..e46049261561 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -256,14 +256,12 @@ ccw_device_recog_done(struct ccw_device *cdev, int state) old_lpm = 0; if (sch->lpm != old_lpm) __recover_lost_chpids(sch, old_lpm); - if (cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID) { - if (state == DEV_STATE_NOT_OPER) { - cdev->private->flags.recog_done = 1; - cdev->private->state = DEV_STATE_DISCONNECTED; - wake_up(&cdev->private->wait_q); - return; - } - /* Boxed devices don't need extra treatment. */ + if (cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID && + (state == DEV_STATE_NOT_OPER || state == DEV_STATE_BOXED)) { + cdev->private->flags.recog_done = 1; + cdev->private->state = DEV_STATE_DISCONNECTED; + wake_up(&cdev->private->wait_q); + return; } notify = 0; same_dev = 0; /* Keep the compiler quiet... */ @@ -275,7 +273,7 @@ ccw_device_recog_done(struct ccw_device *cdev, int state) sch->schid.ssid, sch->schid.sch_no); break; case DEV_STATE_OFFLINE: - if (cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID) { + if (cdev->online) { same_dev = ccw_device_handle_oper(cdev); notify = 1; } @@ -308,6 +306,12 @@ ccw_device_recog_done(struct ccw_device *cdev, int state) " subchannel 0.%x.%04x\n", cdev->private->dev_id.devno, sch->schid.ssid, sch->schid.sch_no); + if (cdev->id.cu_type != 0) { /* device was recognized before */ + cdev->private->flags.recog_done = 1; + cdev->private->state = DEV_STATE_BOXED; + wake_up(&cdev->private->wait_q); + return; + } break; } cdev->private->state = state; @@ -390,10 +394,13 @@ ccw_device_done(struct ccw_device *cdev, int state) cdev->private->state = state; - - if (state == DEV_STATE_BOXED) + if (state == DEV_STATE_BOXED) { CIO_MSG_EVENT(0, "Boxed device %04x on subchannel %04x\n", cdev->private->dev_id.devno, sch->schid.sch_no); + if (cdev->online && !ccw_device_notify(cdev, CIO_BOXED)) + ccw_device_schedule_sch_unregister(cdev); + cdev->private->flags.donotify = 0; + } if (cdev->private->flags.donotify) { cdev->private->flags.donotify = 0; diff --git a/drivers/s390/scsi/zfcp_ccw.c b/drivers/s390/scsi/zfcp_ccw.c index 1fe1e2eda512..cfb0dcb6e3ff 100644 --- a/drivers/s390/scsi/zfcp_ccw.c +++ b/drivers/s390/scsi/zfcp_ccw.c @@ -176,6 +176,11 @@ static int zfcp_ccw_notify(struct ccw_device *ccw_device, int event) zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED, "ccnoti4", NULL); break; + case CIO_BOXED: + dev_warn(&adapter->ccw_device->dev, + "The ccw device did not respond in time.\n"); + zfcp_erp_adapter_shutdown(adapter, 0, "ccnoti5", NULL); + break; } return 1; } -- cgit v1.2.3 From b5cd99e6b002776c0e946f38292adbb0258b7983 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Tue, 31 Mar 2009 19:16:06 +0200 Subject: [S390] cio: disallow online setting of device in transient state Return -EAGAIN on writes to sysfs online attribute if the corresponding ccw device is in transient state. Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 868f8c6b053a..e47aa3f04769 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -515,7 +515,11 @@ static ssize_t online_store (struct device *dev, struct device_attribute *attr, int force, ret; unsigned long i; - if (atomic_cmpxchg(&cdev->private->onoff, 0, 1) != 0) + if ((cdev->private->state != DEV_STATE_OFFLINE && + cdev->private->state != DEV_STATE_ONLINE && + cdev->private->state != DEV_STATE_BOXED && + cdev->private->state != DEV_STATE_DISCONNECTED) || + atomic_cmpxchg(&cdev->private->onoff, 0, 1) != 0) return -EAGAIN; if (cdev->drv && !try_module_get(cdev->drv->owner)) { -- cgit v1.2.3 From 99f6a570eedc885675b6aa36b7acdbdcc3a7f55b Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Tue, 31 Mar 2009 19:16:07 +0200 Subject: [S390] cio: online_store - trigger recognition for boxed devices Start a new device recognition if someone writes to sysfs online attribute of a boxed ccw device. The current test will fail, since cu_type != 0 for devices which were recognized before. Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index e47aa3f04769..35441fa16be1 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -468,7 +468,7 @@ static int online_store_recog_and_online(struct ccw_device *cdev) int ret; /* Do device recognition, if needed. */ - if (cdev->id.cu_type == 0) { + if (cdev->private->state == DEV_STATE_BOXED) { ret = ccw_device_recognition(cdev); if (ret) { CIO_MSG_EVENT(0, "Couldn't start recognition " -- cgit v1.2.3 From 9010941c5483a7a5bb1f7d97ee62491fb078bb51 Mon Sep 17 00:00:00 2001 From: Elias Oltmanns Date: Tue, 31 Mar 2009 20:14:56 +0200 Subject: ide: Fix code dealing with sleeping devices in do_ide_request() Unfortunately, I missed a catch when reviewing the patch committed as 201bffa4. Here is the fix to the currently broken handling of sleeping devices. In particular, this is required to get the disk shock protection code working again. Reported-by: Christian Thaeter Cc: stable@kernel.org Signed-off-by: Elias Oltmanns Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-io.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index 1adc5e2e7fb3..3c52317d8524 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -481,11 +481,10 @@ repeat: prev_port = hwif->host->cur_port; hwif->rq = NULL; - if (drive->dev_flags & IDE_DFLAG_SLEEPING) { - if (time_before(drive->sleep, jiffies)) { - ide_unlock_port(hwif); - goto plug_device; - } + if (drive->dev_flags & IDE_DFLAG_SLEEPING && + time_after(drive->sleep, jiffies)) { + ide_unlock_port(hwif); + goto plug_device; } if ((hwif->host->host_flags & IDE_HFLAG_SERIALIZE) && -- cgit v1.2.3 From 479edf065576aeed7ac99d10838bb3b4f870b5f9 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 31 Mar 2009 20:14:57 +0200 Subject: ide: drivers/ide/ide-atapi.c needs On m68k: | drivers/ide/ide-atapi.c: In function 'ide_io_buffers': | drivers/ide/ide-atapi.c:87: error: implicit declaration of function 'sg_page' | drivers/ide/ide-atapi.c:87: warning: passing argument 1 of 'PageHighMem' makes pointer from integer without a cast | drivers/ide/ide-atapi.c:91: warning: passing argument 1 of 'kmap_atomic' makes pointer from integer without a cast | drivers/ide/ide-atapi.c:96: error: implicit declaration of function 'sg_virt' | drivers/ide/ide-atapi.c:96: warning: assignment makes pointer from integer without a cast | drivers/ide/ide-atapi.c:107: error: implicit declaration of function 'sg_next' | drivers/ide/ide-atapi.c:107: warning: assignment makes pointer from integer without a cast [bart: Dmitri Vorobiev submitted similar patch fixing MIPS] Signed-off-by: Geert Uytterhoeven Cc: Dmitri Vorobiev Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-atapi.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index 2fb5d28a9be5..d937e45a6777 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -6,6 +6,8 @@ #include #include #include +#include + #include #ifdef DEBUG -- cgit v1.2.3 From da19620d99377a52b953245089f831a9c3f049c2 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 31 Mar 2009 20:14:57 +0200 Subject: at91_ide: fix ->ftf_flags handling Fix some incorrect IDE_FTFLAG_* changes which slipped in commit "ide: add "flagged" taskfile flags to struct ide_taskfile (v2)" (commit 19710d25d50ae0be05eebe4231ed8918b1092d82) few days ago. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/at91_ide.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/at91_ide.c b/drivers/ide/at91_ide.c index 27547121daff..6bb45a2b24c2 100644 --- a/drivers/ide/at91_ide.c +++ b/drivers/ide/at91_ide.c @@ -192,10 +192,10 @@ static void at91_ide_tf_load(ide_drive_t *drive, struct ide_cmd *cmd) struct ide_taskfile *tf = &cmd->tf; u8 HIHI = (cmd->tf_flags & IDE_TFLAG_LBA48) ? 0xE0 : 0xEF; - if (cmd->tf_flags & IDE_FTFLAG_FLAGGED) + if (cmd->ftf_flags & IDE_FTFLAG_FLAGGED) HIHI = 0xFF; - if (cmd->tf_flags & IDE_FTFLAG_OUT_DATA) { + if (cmd->ftf_flags & IDE_FTFLAG_OUT_DATA) { u16 data = (tf->hob_data << 8) | tf->data; at91_ide_output_data(drive, NULL, &data, 2); @@ -233,7 +233,7 @@ static void at91_ide_tf_read(ide_drive_t *drive, struct ide_cmd *cmd) struct ide_io_ports *io_ports = &hwif->io_ports; struct ide_taskfile *tf = &cmd->tf; - if (cmd->tf_flags & IDE_FTFLAG_IN_DATA) { + if (cmd->ftf_flags & IDE_FTFLAG_IN_DATA) { u16 data; at91_ide_input_data(drive, NULL, &data, 2); -- cgit v1.2.3 From 2eba08270990b99fb5429b76ee97184ddd272f7f Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Tue, 31 Mar 2009 20:14:58 +0200 Subject: ide-atapi: start DMA after issuing a packet command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apparently¹, some ATAPI devices want to see the packet command first before enabling DMA otherwise they simply hang indefinitely. Reorder the two steps and start DMA only after having issued the command first. [1] http://marc.info/?l=linux-kernel&m=123835520317235&w=2 Signed-off-by: Borislav Petkov Reported-by: Michael Roth Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-atapi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index d937e45a6777..f591166d2c93 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -613,6 +613,10 @@ static ide_startstop_t ide_transfer_pc(ide_drive_t *drive) : ide_pc_intr), timeout); + /* Send the actual packet */ + if ((drive->atapi_flags & IDE_AFLAG_ZIP_DRIVE) == 0) + hwif->tp_ops->output_data(drive, NULL, rq->cmd, cmd_len); + /* Begin DMA, if necessary */ if (dev_is_idecd(drive)) { if (drive->dma) @@ -624,10 +628,6 @@ static ide_startstop_t ide_transfer_pc(ide_drive_t *drive) } } - /* Send the actual packet */ - if ((drive->atapi_flags & IDE_AFLAG_ZIP_DRIVE) == 0) - hwif->tp_ops->output_data(drive, NULL, rq->cmd, cmd_len); - return ide_started; } -- cgit v1.2.3 From 7a00798b1a7502ff31736152b23189138db0b978 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 31 Mar 2009 20:14:59 +0200 Subject: ide: add support for arbitrary transfer lengths to ide_pio_bytes() Add support for arbitrary transfer lengths to ide_pio_bytes() and then inline ide_pio_multi() into ide_pio_datablock(). There should be no functional changes caused by this patch. Cc: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-taskfile.c | 73 +++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 36 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c index 84532be97c00..8d7e87d04b3b 100644 --- a/drivers/ide/ide-taskfile.c +++ b/drivers/ide/ide-taskfile.c @@ -189,7 +189,7 @@ static u8 wait_drive_not_busy(ide_drive_t *drive) } static void ide_pio_bytes(ide_drive_t *drive, struct ide_cmd *cmd, - unsigned int write, unsigned int nr_bytes) + unsigned int write, unsigned int len) { ide_hwif_t *hwif = drive->hwif; struct scatterlist *sg = hwif->sg_table; @@ -202,56 +202,55 @@ static void ide_pio_bytes(ide_drive_t *drive, struct ide_cmd *cmd, u8 *buf; cursg = cmd->cursg; - if (!cursg) { - cursg = sg; - cmd->cursg = sg; - } + if (cursg == NULL) + cursg = cmd->cursg = sg; + + while (len) { + unsigned nr_bytes = min(len, cursg->length - cmd->cursg_ofs); - page = sg_page(cursg); - offset = cursg->offset + cmd->cursg_ofs; + if (nr_bytes > PAGE_SIZE) + nr_bytes = PAGE_SIZE; - /* get the current page and offset */ - page = nth_page(page, (offset >> PAGE_SHIFT)); - offset %= PAGE_SIZE; + page = sg_page(cursg); + offset = cursg->offset + cmd->cursg_ofs; + + /* get the current page and offset */ + page = nth_page(page, (offset >> PAGE_SHIFT)); + offset %= PAGE_SIZE; #ifdef CONFIG_HIGHMEM - local_irq_save(flags); + local_irq_save(flags); #endif - buf = kmap_atomic(page, KM_BIO_SRC_IRQ) + offset; + buf = kmap_atomic(page, KM_BIO_SRC_IRQ) + offset; - cmd->nleft -= nr_bytes; - cmd->cursg_ofs += nr_bytes; + cmd->nleft -= nr_bytes; + cmd->cursg_ofs += nr_bytes; - if (cmd->cursg_ofs == cursg->length) { - cmd->cursg = sg_next(cmd->cursg); - cmd->cursg_ofs = 0; - } + if (cmd->cursg_ofs == cursg->length) { + cursg = cmd->cursg = sg_next(cmd->cursg); + cmd->cursg_ofs = 0; + } - /* do the actual data transfer */ - if (write) - hwif->tp_ops->output_data(drive, cmd, buf, nr_bytes); - else - hwif->tp_ops->input_data(drive, cmd, buf, nr_bytes); + /* do the actual data transfer */ + if (write) + hwif->tp_ops->output_data(drive, cmd, buf, nr_bytes); + else + hwif->tp_ops->input_data(drive, cmd, buf, nr_bytes); - kunmap_atomic(buf, KM_BIO_SRC_IRQ); + kunmap_atomic(buf, KM_BIO_SRC_IRQ); #ifdef CONFIG_HIGHMEM - local_irq_restore(flags); + local_irq_restore(flags); #endif -} - -static void ide_pio_multi(ide_drive_t *drive, struct ide_cmd *cmd, - unsigned int write) -{ - unsigned int nsect; - nsect = min_t(unsigned int, cmd->nleft >> 9, drive->mult_count); - while (nsect--) - ide_pio_bytes(drive, cmd, write, SECTOR_SIZE); + len -= nr_bytes; + } } static void ide_pio_datablock(ide_drive_t *drive, struct ide_cmd *cmd, unsigned int write) { + unsigned int nr_bytes; + u8 saved_io_32bit = drive->io_32bit; if (cmd->tf_flags & IDE_TFLAG_FS) @@ -263,9 +262,11 @@ static void ide_pio_datablock(ide_drive_t *drive, struct ide_cmd *cmd, touch_softlockup_watchdog(); if (cmd->tf_flags & IDE_TFLAG_MULTI_PIO) - ide_pio_multi(drive, cmd, write); + nr_bytes = min_t(unsigned, cmd->nleft, drive->mult_count << 9); else - ide_pio_bytes(drive, cmd, write, SECTOR_SIZE); + nr_bytes = SECTOR_SIZE; + + ide_pio_bytes(drive, cmd, write, nr_bytes); drive->io_32bit = saved_io_32bit; } -- cgit v1.2.3 From f2bc316736e69e5623443a010f9581a01429c075 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 31 Mar 2009 20:14:59 +0200 Subject: ide: use PageHighMem() instead of ifdefs in ide_pio_bytes() Use PageHighMem() instead of ifdefs in ide_pio_bytes() (=> local IRQs won't be disabled when not necessary). Cc: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-taskfile.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c index 8d7e87d04b3b..0e333ecf2ad6 100644 --- a/drivers/ide/ide-taskfile.c +++ b/drivers/ide/ide-taskfile.c @@ -195,9 +195,7 @@ static void ide_pio_bytes(ide_drive_t *drive, struct ide_cmd *cmd, struct scatterlist *sg = hwif->sg_table; struct scatterlist *cursg = cmd->cursg; struct page *page; -#ifdef CONFIG_HIGHMEM unsigned long flags; -#endif unsigned int offset; u8 *buf; @@ -218,9 +216,9 @@ static void ide_pio_bytes(ide_drive_t *drive, struct ide_cmd *cmd, page = nth_page(page, (offset >> PAGE_SHIFT)); offset %= PAGE_SIZE; -#ifdef CONFIG_HIGHMEM - local_irq_save(flags); -#endif + if (PageHighMem(page)) + local_irq_save(flags); + buf = kmap_atomic(page, KM_BIO_SRC_IRQ) + offset; cmd->nleft -= nr_bytes; @@ -238,9 +236,9 @@ static void ide_pio_bytes(ide_drive_t *drive, struct ide_cmd *cmd, hwif->tp_ops->input_data(drive, cmd, buf, nr_bytes); kunmap_atomic(buf, KM_BIO_SRC_IRQ); -#ifdef CONFIG_HIGHMEM - local_irq_restore(flags); -#endif + + if (PageHighMem(page)) + local_irq_restore(flags); len -= nr_bytes; } -- cgit v1.2.3 From 116e690f4e69ce0458a9be7010c80b59eb7a99d8 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 31 Mar 2009 20:14:59 +0200 Subject: ide-cd: remove dead URLs Cc: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-cd.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 3f630e4080d4..a71ca2a9ab4b 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -12,12 +12,9 @@ * See Documentation/cdrom/ide-cd for usage information. * * Suggestions are welcome. Patches that work are more welcome though. ;-) - * For those wishing to work on this driver, please be sure you download - * and comply with the latest Mt. Fuji (SFF8090 version 4) and ATAPI - * (SFF-8020i rev 2.6) standards. These documents can be obtained by - * anonymous ftp from: - * ftp://fission.dt.wdc.com/pub/standards/SFF_atapi/spec/SFF8020-r2.6/PS/8020r26.ps - * ftp://ftp.avc-pioneer.com/Mtfuji4/Spec/Fuji4r10.pdf + * + * Documentation: + * Mt. Fuji (SFF8090 version 4) and ATAPI (SFF-8020i rev 2.6) standards. * * For historical changelog please see: * Documentation/ide/ChangeLog.ide-cd.1994-2004 -- cgit v1.2.3 From bf12a9c1c95e1b0204fc2fc9fe625a056e284f5a Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 31 Mar 2009 20:15:00 +0200 Subject: ide-cd: use ide_end_rq() also for failed non-fs requests Use ide_end_rq() also for failed non-fs requests on completion of REQUEST SENSE requests + use blk_rq_bytes() while at it. Cc: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-cd.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index a71ca2a9ab4b..6f64fb2f63d0 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -265,18 +265,10 @@ static void cdrom_end_request(ide_drive_t *drive, int uptodate) failed->sense_len = rq->sense_len; } cdrom_analyze_sense_data(drive, failed, sense); - /* - * now end the failed request - */ - if (blk_fs_request(failed)) { - if (ide_end_rq(drive, failed, -EIO, - failed->hard_nr_sectors << 9)) - BUG(); - } else { - if (blk_end_request(failed, -EIO, - failed->data_len)) - BUG(); - } + + if (ide_end_rq(drive, failed, -EIO, + blk_rq_bytes(failed))) + BUG(); } else cdrom_analyze_sense_data(drive, NULL, sense); } -- cgit v1.2.3 From 13eae6a48fc57495eb9e733430b8fc20df7bf415 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 31 Mar 2009 20:15:00 +0200 Subject: ide-cd: remove dead code from cdrom_decode_status() There should be no functional changes caused by this patch. Cc: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-cd.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 6f64fb2f63d0..4a289711c551 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -321,12 +321,6 @@ static int cdrom_decode_status(ide_drive_t *drive, int good_stat, int *stat_ret) err = ide_read_error(drive); sense_key = err >> 4; - if (rq == NULL) { - printk(KERN_ERR PFX "%s: missing rq in %s\n", - drive->name, __func__); - return 1; - } - ide_debug_log(IDE_DBG_RQ, "stat: 0x%x, good_stat: 0x%x, cmd[0]: 0x%x, " "rq->cmd_type: 0x%x, err: 0x%x", stat, good_stat, rq->cmd[0], rq->cmd_type, -- cgit v1.2.3 From 1ab6d7451684078bfc4fbabc432f0ef8a809e975 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 31 Mar 2009 20:15:00 +0200 Subject: ide-cd: remove needless ide_dump_status_no_sense() wrapper It makes no sense to check for BSY bit being set as earlier OK_STAT() check in cdrom_end_request() makes sure that BSY bit is cleared. There should be no functional changes caused by this patch. Cc: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-cd.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 4a289711c551..19ccadead5e8 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -290,13 +290,6 @@ static void cdrom_end_request(ide_drive_t *drive, int uptodate) ide_complete_rq(drive, uptodate ? 0 : -EIO, nsectors << 9); } -static void ide_dump_status_no_sense(ide_drive_t *drive, const char *msg, u8 st) -{ - if (st & 0x80) - return; - ide_dump_status(drive, msg, st); -} - /* * Returns: * 0: if the request should be continued. @@ -439,21 +432,19 @@ static int cdrom_decode_status(ide_drive_t *drive, int good_stat, int *stat_ret) * No point in retrying after an illegal request or data * protect error. */ - ide_dump_status_no_sense(drive, "command error", stat); + ide_dump_status(drive, "command error", stat); do_end_request = 1; } else if (sense_key == MEDIUM_ERROR) { /* * No point in re-trying a zillion times on a bad * sector. If we got here the error is not correctable. */ - ide_dump_status_no_sense(drive, - "media error (bad sector)", - stat); + ide_dump_status(drive, "media error (bad sector)", + stat); do_end_request = 1; } else if (sense_key == BLANK_CHECK) { /* disk appears blank ?? */ - ide_dump_status_no_sense(drive, "media error (blank)", - stat); + ide_dump_status(drive, "media error (blank)", stat); do_end_request = 1; } else if ((err & ~ATA_ABORTED) != 0) { /* go to the default handler for other errors */ -- cgit v1.2.3 From 6041e8fba8b9a9a64bd7402be700b0f1247a9c55 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 31 Mar 2009 20:15:01 +0200 Subject: ide-cd: remove no longer needed 'ignore' module parameter ide-scsi is gone... Cc: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-cd.c | 12 ------------ 1 file changed, 12 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 19ccadead5e8..9d3150f549fe 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -1942,9 +1942,6 @@ static struct block_device_operations idecd_ops = { }; /* module options */ -static char *ignore; -module_param(ignore, charp, 0400); - static unsigned long debug_mask; module_param(debug_mask, ulong, 0644); @@ -1965,15 +1962,6 @@ static int ide_cd_probe(ide_drive_t *drive) if (drive->media != ide_cdrom && drive->media != ide_optical) goto failed; - /* skip drives that we were told to ignore */ - if (ignore != NULL) { - if (strstr(ignore, drive->name)) { - printk(KERN_INFO PFX "ignoring drive %s\n", - drive->name); - goto failed; - } - } - drive->debug_mask = debug_mask; drive->irq_handler = cdrom_newpc_intr; -- cgit v1.2.3 From 299c4852fc6995e0665d246927d25cefd4dad754 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 31 Mar 2009 20:15:01 +0200 Subject: ide-cd: factor out failed request completion from cdrom_end_request() There should be no functional changes caused by this patch. Cc: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-cd.c | 47 +++++++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 9d3150f549fe..eb0c2fe2b874 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -242,6 +242,29 @@ static void cdrom_queue_request_sense(ide_drive_t *drive, void *sense, elv_add_request(drive->queue, rq, ELEVATOR_INSERT_FRONT, 0); } +static void ide_cd_complete_failed_rq(ide_drive_t *drive, struct request *rq) +{ + /* + * For REQ_TYPE_SENSE, "rq->buffer" points to the original + * failed request + */ + struct request *failed = (struct request *)rq->buffer; + struct cdrom_info *info = drive->driver_data; + void *sense = &info->sense_data; + + if (failed) { + if (failed->sense) { + sense = failed->sense; + failed->sense_len = rq->sense_len; + } + cdrom_analyze_sense_data(drive, failed, sense); + + if (ide_end_rq(drive, failed, -EIO, blk_rq_bytes(failed))) + BUG(); + } else + cdrom_analyze_sense_data(drive, NULL, sense); +} + static void cdrom_end_request(ide_drive_t *drive, int uptodate) { struct request *rq = drive->hwif->rq; @@ -250,28 +273,8 @@ static void cdrom_end_request(ide_drive_t *drive, int uptodate) ide_debug_log(IDE_DBG_FUNC, "cmd: 0x%x, uptodate: 0x%x, nsectors: %d", rq->cmd[0], uptodate, nsectors); - if (blk_sense_request(rq) && uptodate) { - /* - * For REQ_TYPE_SENSE, "rq->buffer" points to the original - * failed request - */ - struct request *failed = (struct request *) rq->buffer; - struct cdrom_info *info = drive->driver_data; - void *sense = &info->sense_data; - - if (failed) { - if (failed->sense) { - sense = failed->sense; - failed->sense_len = rq->sense_len; - } - cdrom_analyze_sense_data(drive, failed, sense); - - if (ide_end_rq(drive, failed, -EIO, - blk_rq_bytes(failed))) - BUG(); - } else - cdrom_analyze_sense_data(drive, NULL, sense); - } + if (blk_sense_request(rq) && uptodate) + ide_cd_complete_failed_rq(drive, rq); if (!rq->current_nr_sectors && blk_fs_request(rq)) uptodate = 1; -- cgit v1.2.3 From e0458ccb0668edbecbc1ae1c17ed58a6b1a4ff3e Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 31 Mar 2009 20:15:02 +0200 Subject: ide-cd: unify ide_cd_do_request() exit paths * Move cdrom_end_request() calls from cdrom_start_rw() and ide_cd_prepare_rw_request() to ide_cd_do_request(). * Unify ide_cd_do_request() exit paths. There should be no functional changes caused by this patch. Cc: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-cd.c | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index eb0c2fe2b874..3e3058cff843 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -595,7 +595,6 @@ static ide_startstop_t ide_cd_prepare_rw_request(ide_drive_t *drive, printk(KERN_ERR PFX "%s: %s: buffer botch (%u)\n", drive->name, __func__, rq->current_nr_sectors); - cdrom_end_request(drive, 0); return ide_stopped; } rq->current_nr_sectors += nskip; @@ -972,10 +971,8 @@ static ide_startstop_t cdrom_start_rw(ide_drive_t *drive, struct request *rq) if (write) { /* disk has become write protected */ - if (get_disk_ro(cd->disk)) { - cdrom_end_request(drive, 0); + if (get_disk_ro(cd->disk)) return ide_stopped; - } } else { /* * We may be retrying this request after an error. Fix up any @@ -987,10 +984,8 @@ static ide_startstop_t cdrom_start_rw(ide_drive_t *drive, struct request *rq) /* use DMA, if possible / writes *must* be hardware frame aligned */ if ((rq->nr_sectors & (sectors_per_frame - 1)) || (rq->sector & (sectors_per_frame - 1))) { - if (write) { - cdrom_end_request(drive, 0); + if (write) return ide_stopped; - } drive->dma = 0; } else drive->dma = !!(drive->dev_flags & IDE_DFLAG_USING_DMA); @@ -1045,6 +1040,7 @@ static ide_startstop_t ide_cd_do_request(ide_drive_t *drive, struct request *rq, sector_t block) { struct ide_cmd cmd; + int uptodate = 0; ide_debug_log(IDE_DBG_RQ, "cmd: 0x%x, block: %llu", rq->cmd[0], (unsigned long long)block); @@ -1053,11 +1049,9 @@ static ide_startstop_t ide_cd_do_request(ide_drive_t *drive, struct request *rq, blk_dump_rq_flags(rq, "ide_cd_do_request"); if (blk_fs_request(rq)) { - if (cdrom_start_rw(drive, rq) == ide_stopped) - return ide_stopped; - - if (ide_cd_prepare_rw_request(drive, rq) == ide_stopped) - return ide_stopped; + if (cdrom_start_rw(drive, rq) == ide_stopped || + ide_cd_prepare_rw_request(drive, rq) == ide_stopped) + goto out_end; } else if (blk_sense_request(rq) || blk_pc_request(rq) || rq->cmd_type == REQ_TYPE_ATA_PC) { if (!rq->timeout) @@ -1066,12 +1060,11 @@ static ide_startstop_t ide_cd_do_request(ide_drive_t *drive, struct request *rq, cdrom_do_block_pc(drive, rq); } else if (blk_special_request(rq)) { /* right now this can only be a reset... */ - cdrom_end_request(drive, 1); - return ide_stopped; + uptodate = 1; + goto out_end; } else { blk_dump_rq_flags(rq, DRV_NAME " bad flags"); - cdrom_end_request(drive, 0); - return ide_stopped; + goto out_end; } memset(&cmd, 0, sizeof(cmd)); @@ -1082,6 +1075,9 @@ static ide_startstop_t ide_cd_do_request(ide_drive_t *drive, struct request *rq, cmd.rq = rq; return ide_issue_pc(drive, &cmd); +out_end: + cdrom_end_request(drive, uptodate); + return ide_stopped; } /* -- cgit v1.2.3 From 984c5e5974227d2d4dba58cdf19af641f89be83f Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 31 Mar 2009 20:15:03 +0200 Subject: ide-cd: move setting REQ_FAILED flag out from 'end_request' exit path Move setting REQ_FAILED flag out from 'end_request' exit path in cdrom_newpc_intr() and also rename 'end_request' to 'out_end'. There should be no functional changes caused by this patch. Cc: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-cd.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 3e3058cff843..b66da3f1678e 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -776,7 +776,9 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive) ide_complete_rq(drive, 0, 512); return ide_stopped; } - goto end_request; + if (blk_pc_request(rq) == 0 && uptodate == 0) + rq->cmd_flags |= REQ_FAILED; + goto out_end; } ide_read_bcount_and_ireason(drive, &len, &ireason); @@ -811,8 +813,10 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive) ide_cd_request_sense_fixup(drive, rq); /* complain if we still have data left to transfer */ uptodate = rq->data_len ? 0 : 1; + if (uptodate == 0) + rq->cmd_flags |= REQ_FAILED; } - goto end_request; + goto out_end; } /* check which way to transfer data */ @@ -939,7 +943,7 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive) ide_set_handler(drive, cdrom_newpc_intr, timeout); return ide_started; -end_request: +out_end: if (blk_pc_request(rq)) { unsigned int dlen = rq->data_len; @@ -951,8 +955,6 @@ end_request: hwif->rq = NULL; } else { - if (!uptodate) - rq->cmd_flags |= REQ_FAILED; cdrom_end_request(drive, uptodate); } return ide_stopped; -- cgit v1.2.3 From 8a116974852a727bdfe6b1b897102903a17228a5 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 31 Mar 2009 20:15:03 +0200 Subject: ide-cd: unify cdrom_newpc_intr() exit paths * Move cdrom_end_request() calls from cdrom_decode_status() and ide_cd_check_ireason() to cdrom_newpc_intr(). * Unify cdrom_newpc_intr() exit paths. There should be no functional changes caused by this patch. Cc: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-cd.c | 48 +++++++++++++++++++++++------------------------- 1 file changed, 23 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index b66da3f1678e..4c32e8db55c3 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -296,7 +296,8 @@ static void cdrom_end_request(ide_drive_t *drive, int uptodate) /* * Returns: * 0: if the request should be continued. - * 1: if the request was ended. + * 1: if the request will be going through error recovery. + * 2: if the request should be ended. */ static int cdrom_decode_status(ide_drive_t *drive, int good_stat, int *stat_ret) { @@ -329,10 +330,7 @@ static int cdrom_decode_status(ide_drive_t *drive, int good_stat, int *stat_ret) * Just give up. */ rq->cmd_flags |= REQ_FAILED; - cdrom_end_request(drive, 0); - ide_error(drive, "request sense failure", stat); - return 1; - + return 2; } else if (blk_pc_request(rq) || rq->cmd_type == REQ_TYPE_ATA_PC) { /* All other functions, except for READ. */ @@ -472,14 +470,12 @@ static int cdrom_decode_status(ide_drive_t *drive, int good_stat, int *stat_ret) */ if (stat & ATA_ERR) cdrom_queue_request_sense(drive, NULL, NULL); + return 1; } else { blk_dump_rq_flags(rq, PFX "bad rq"); - cdrom_end_request(drive, 0); + return 2; } - /* retry, or handle the next request */ - return 1; - end_request: if (stat & ATA_ERR) { struct request_queue *q = drive->queue; @@ -492,10 +488,9 @@ end_request: hwif->rq = NULL; cdrom_queue_request_sense(drive, rq->sense, rq); + return 1; } else - cdrom_end_request(drive, 0); - - return 1; + return 2; } /* @@ -539,7 +534,6 @@ static int ide_cd_check_ireason(ide_drive_t *drive, struct request *rq, if (rq->cmd_type == REQ_TYPE_ATA_PC) rq->cmd_flags |= REQ_FAILED; - cdrom_end_request(drive, 0); return -1; } @@ -741,7 +735,8 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive) xfer_func_t *xferfunc; ide_expiry_t *expiry = NULL; int dma_error = 0, dma, stat, thislen, uptodate = 0; - int write = (rq_data_dir(rq) == WRITE) ? 1 : 0; + int write = (rq_data_dir(rq) == WRITE) ? 1 : 0, rc; + int sense = blk_sense_request(rq); unsigned int timeout; u16 len; u8 ireason; @@ -761,8 +756,12 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive) } } - if (cdrom_decode_status(drive, 0, &stat)) + rc = cdrom_decode_status(drive, 0, &stat); + if (rc) { + if (rc == 2) + goto out_end; return ide_stopped; + } /* using dma, transfer is complete now */ if (dma) { @@ -807,8 +806,6 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive) rq->cmd_flags |= REQ_FAILED; uptodate = 0; } - cdrom_end_request(drive, uptodate); - return ide_stopped; } else if (!blk_pc_request(rq)) { ide_cd_request_sense_fixup(drive, rq); /* complain if we still have data left to transfer */ @@ -820,17 +817,16 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive) } /* check which way to transfer data */ - if (ide_cd_check_ireason(drive, rq, len, ireason, write)) - return ide_stopped; + rc = ide_cd_check_ireason(drive, rq, len, ireason, write); + if (rc) + goto out_end; if (blk_fs_request(rq)) { if (write == 0) { int nskip; - if (ide_cd_check_transfer_size(drive, len)) { - cdrom_end_request(drive, 0); - return ide_stopped; - } + if (ide_cd_check_transfer_size(drive, len)) + goto out_end; /* * First, figure out if we need to bit-bucket @@ -923,7 +919,7 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive) else rq->data += blen; } - if (!write && blk_sense_request(rq)) + if (sense && write == 0) rq->sense_len += blen; } @@ -944,7 +940,7 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive) return ide_started; out_end: - if (blk_pc_request(rq)) { + if (blk_pc_request(rq) && rc == 0) { unsigned int dlen = rq->data_len; if (dma) @@ -956,6 +952,8 @@ out_end: hwif->rq = NULL; } else { cdrom_end_request(drive, uptodate); + if (sense && rc == 2) + ide_error(drive, "request sense failure", stat); } return ide_stopped; } -- cgit v1.2.3 From f63174e7a7ba3afa7f53e61c59b3f1ca5d88f3fb Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 31 Mar 2009 20:15:04 +0200 Subject: ide-cd: remove cdrom_end_request() Inline cdrom_end_request() into cdrom_newpc_intr() and ide_cd_do_request(). There should be no functional changes caused by this patch. Cc: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-cd.c | 81 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 47 insertions(+), 34 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 4c32e8db55c3..c859eafe759b 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -265,34 +265,6 @@ static void ide_cd_complete_failed_rq(ide_drive_t *drive, struct request *rq) cdrom_analyze_sense_data(drive, NULL, sense); } -static void cdrom_end_request(ide_drive_t *drive, int uptodate) -{ - struct request *rq = drive->hwif->rq; - int nsectors = rq->hard_cur_sectors; - - ide_debug_log(IDE_DBG_FUNC, "cmd: 0x%x, uptodate: 0x%x, nsectors: %d", - rq->cmd[0], uptodate, nsectors); - - if (blk_sense_request(rq) && uptodate) - ide_cd_complete_failed_rq(drive, rq); - - if (!rq->current_nr_sectors && blk_fs_request(rq)) - uptodate = 1; - /* make sure it's fully ended */ - if (blk_pc_request(rq)) - nsectors = (rq->data_len + 511) >> 9; - if (!nsectors) - nsectors = 1; - - ide_debug_log(IDE_DBG_FUNC, "uptodate: 0x%x, nsectors: %d", - uptodate, nsectors); - - if (blk_fs_request(rq) == 0 && uptodate <= 0 && rq->errors == 0) - rq->errors = -EIO; - - ide_complete_rq(drive, uptodate ? 0 : -EIO, nsectors << 9); -} - /* * Returns: * 0: if the request should be continued. @@ -735,7 +707,7 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive) xfer_func_t *xferfunc; ide_expiry_t *expiry = NULL; int dma_error = 0, dma, stat, thislen, uptodate = 0; - int write = (rq_data_dir(rq) == WRITE) ? 1 : 0, rc; + int write = (rq_data_dir(rq) == WRITE) ? 1 : 0, rc, nsectors; int sense = blk_sense_request(rq); unsigned int timeout; u16 len; @@ -902,8 +874,14 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive) rq->current_nr_sectors -= (blen >> 9); rq->sector += (blen >> 9); - if (rq->current_nr_sectors == 0 && rq->nr_sectors) - cdrom_end_request(drive, 1); + if (rq->current_nr_sectors == 0 && rq->nr_sectors) { + nsectors = rq->hard_cur_sectors; + + if (nsectors == 0) + nsectors = 1; + + ide_complete_rq(drive, 0, nsectors << 9); + } } else { rq->data_len -= blen; @@ -951,7 +929,28 @@ out_end: hwif->rq = NULL; } else { - cdrom_end_request(drive, uptodate); + if (sense && uptodate) + ide_cd_complete_failed_rq(drive, rq); + + if (blk_fs_request(rq)) { + if (rq->current_nr_sectors == 0) + uptodate = 1; + } else { + if (uptodate <= 0 && rq->errors == 0) + rq->errors = -EIO; + } + + /* make sure it's fully ended */ + if (blk_pc_request(rq)) + nsectors = (rq->data_len + 511) >> 9; + else + nsectors = rq->hard_cur_sectors; + + if (nsectors == 0) + nsectors = 1; + + ide_complete_rq(drive, uptodate ? 0 : -EIO, nsectors << 9); + if (sense && rc == 2) ide_error(drive, "request sense failure", stat); } @@ -1040,7 +1039,7 @@ static ide_startstop_t ide_cd_do_request(ide_drive_t *drive, struct request *rq, sector_t block) { struct ide_cmd cmd; - int uptodate = 0; + int uptodate = 0, nsectors; ide_debug_log(IDE_DBG_RQ, "cmd: 0x%x, block: %llu", rq->cmd[0], (unsigned long long)block); @@ -1076,7 +1075,21 @@ static ide_startstop_t ide_cd_do_request(ide_drive_t *drive, struct request *rq, return ide_issue_pc(drive, &cmd); out_end: - cdrom_end_request(drive, uptodate); + if (blk_fs_request(rq)) { + if (rq->current_nr_sectors == 0) + uptodate = 1; + } else { + if (uptodate <= 0 && rq->errors == 0) + rq->errors = -EIO; + } + + nsectors = rq->hard_cur_sectors; + + if (nsectors == 0) + nsectors = 1; + + ide_complete_rq(drive, uptodate ? 0 : -EIO, nsectors << 9); + return ide_stopped; } -- cgit v1.2.3 From c4c69e21b51005e24e2fc4efc8a73460a5ab7799 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 31 Mar 2009 20:15:04 +0200 Subject: ide-cd: kill whole failed request in ide_cd_do_request() Untangling cdrom_end_request() uncovered an error in completing failed requests in ide_cd_do_request(). Fix it. Cc: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-cd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index c859eafe759b..978e1c0c1722 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -1083,7 +1083,7 @@ out_end: rq->errors = -EIO; } - nsectors = rq->hard_cur_sectors; + nsectors = rq->hard_nr_sectors; if (nsectors == 0) nsectors = 1; -- cgit v1.2.3 From 5ed57ad705d6b58386ac43d2ca1c8fc66aee1101 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 31 Mar 2009 20:15:05 +0200 Subject: ide-cd: cleanup ide_cd_do_request() There should be no functional changes caused by this patch. Cc: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-cd.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 978e1c0c1722..30113e69c8bb 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -1049,8 +1049,11 @@ static ide_startstop_t ide_cd_do_request(ide_drive_t *drive, struct request *rq, if (blk_fs_request(rq)) { if (cdrom_start_rw(drive, rq) == ide_stopped || - ide_cd_prepare_rw_request(drive, rq) == ide_stopped) + ide_cd_prepare_rw_request(drive, rq) == ide_stopped) { + if (rq->current_nr_sectors == 0) + uptodate = 1; goto out_end; + } } else if (blk_sense_request(rq) || blk_pc_request(rq) || rq->cmd_type == REQ_TYPE_ATA_PC) { if (!rq->timeout) @@ -1063,6 +1066,8 @@ static ide_startstop_t ide_cd_do_request(ide_drive_t *drive, struct request *rq, goto out_end; } else { blk_dump_rq_flags(rq, DRV_NAME " bad flags"); + if (rq->errors == 0) + rq->errors = -EIO; goto out_end; } @@ -1075,14 +1080,6 @@ static ide_startstop_t ide_cd_do_request(ide_drive_t *drive, struct request *rq, return ide_issue_pc(drive, &cmd); out_end: - if (blk_fs_request(rq)) { - if (rq->current_nr_sectors == 0) - uptodate = 1; - } else { - if (uptodate <= 0 && rq->errors == 0) - rq->errors = -EIO; - } - nsectors = rq->hard_nr_sectors; if (nsectors == 0) -- cgit v1.2.3 From a08915ba594da66145f33a972db578a58b9135f1 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 31 Mar 2009 20:15:13 +0200 Subject: ide-cd: use scatterlists for PIO transfers (fs requests) * Export ide_pio_bytes(). * Add ->last_xfer_len field to struct ide_cmd. * Add ide_cd_error_cmd() helper to ide-cd. * Convert ide-cd to use scatterlists also for PIO transfers (fs requests only for now) and get rid of partial completions (except when the error happens -- which is still subject to change later because looking at ATAPI spec it seems that the device is free to error the whole transfer with setting the Error bit only on the last transfer chunk). * Update ide_cd_{prepare_rw,restore_request,do_request}() accordingly. * Inline ide_cd_restore_request() into cdrom_start_rw(). Cc: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-cd.c | 146 +++++++++++++-------------------------------- drivers/ide/ide-taskfile.c | 5 +- include/linux/ide.h | 4 ++ 3 files changed, 50 insertions(+), 105 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 30113e69c8bb..5f15859c2c73 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -539,64 +539,12 @@ static ide_startstop_t ide_cd_prepare_rw_request(ide_drive_t *drive, { ide_debug_log(IDE_DBG_RQ, "rq->cmd_flags: 0x%x", rq->cmd_flags); - if (rq_data_dir(rq) == READ) { - unsigned short sectors_per_frame = - queue_hardsect_size(drive->queue) >> SECTOR_BITS; - int nskip = rq->sector & (sectors_per_frame - 1); - - /* - * If the requested sector doesn't start on a frame boundary, - * we must adjust the start of the transfer so that it does, - * and remember to skip the first few sectors. - * - * If the rq->current_nr_sectors field is larger than the size - * of the buffer, it will mean that we're to skip a number of - * sectors equal to the amount by which rq->current_nr_sectors - * is larger than the buffer size. - */ - if (nskip > 0) { - /* sanity check... */ - if (rq->current_nr_sectors != - bio_cur_sectors(rq->bio)) { - printk(KERN_ERR PFX "%s: %s: buffer botch (%u)\n", - drive->name, __func__, - rq->current_nr_sectors); - return ide_stopped; - } - rq->current_nr_sectors += nskip; - } - } - /* set up the command */ rq->timeout = ATAPI_WAIT_PC; return ide_started; } -/* - * Fix up a possibly partially-processed request so that we can start it over - * entirely, or even put it back on the request queue. - */ -static void ide_cd_restore_request(ide_drive_t *drive, struct request *rq) -{ - - ide_debug_log(IDE_DBG_FUNC, "enter"); - - if (rq->buffer != bio_data(rq->bio)) { - sector_t n = - (rq->buffer - (char *)bio_data(rq->bio)) / SECTOR_SIZE; - - rq->buffer = bio_data(rq->bio); - rq->nr_sectors += n; - rq->sector -= n; - } - rq->current_nr_sectors = bio_cur_sectors(rq->bio); - rq->hard_cur_sectors = rq->current_nr_sectors; - rq->hard_nr_sectors = rq->nr_sectors; - rq->hard_sector = rq->sector; - rq->q->prep_rq_fn(rq->q, rq); -} - static void ide_cd_request_sense_fixup(ide_drive_t *drive, struct request *rq) { ide_debug_log(IDE_DBG_FUNC, "rq->cmd[0]: 0x%x", rq->cmd[0]); @@ -690,6 +638,17 @@ int ide_cd_queue_pc(ide_drive_t *drive, const unsigned char *cmd, return (flags & REQ_FAILED) ? -EIO : 0; } +static void ide_cd_error_cmd(ide_drive_t *drive, struct ide_cmd *cmd) +{ + unsigned int nr_bytes = cmd->nbytes - cmd->nleft; + + if (cmd->tf_flags & IDE_TFLAG_WRITE) + nr_bytes -= cmd->last_xfer_len; + + if (nr_bytes > 0) + ide_complete_rq(drive, 0, nr_bytes); +} + /* * Called from blk_end_request_callback() after the data of the request is * completed and before the request itself is completed. By returning value '1', @@ -703,6 +662,7 @@ static int cdrom_newpc_intr_dummy_cb(struct request *rq) static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; + struct ide_cmd *cmd = &hwif->cmd; struct request *rq = hwif->rq; xfer_func_t *xferfunc; ide_expiry_t *expiry = NULL; @@ -769,11 +729,10 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive) * Otherwise, complete the command normally. */ uptodate = 1; - if (rq->current_nr_sectors > 0) { + if (cmd->nleft > 0) { printk(KERN_ERR PFX "%s: %s: data underrun " - "(%d blocks)\n", - drive->name, __func__, - rq->current_nr_sectors); + "(%u bytes)\n", drive->name, __func__, + cmd->nleft); if (!write) rq->cmd_flags |= REQ_FAILED; uptodate = 0; @@ -795,24 +754,10 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive) if (blk_fs_request(rq)) { if (write == 0) { - int nskip; - if (ide_cd_check_transfer_size(drive, len)) goto out_end; - - /* - * First, figure out if we need to bit-bucket - * any of the leading sectors. - */ - nskip = min_t(int, rq->current_nr_sectors - - bio_cur_sectors(rq->bio), - thislen >> 9); - if (nskip > 0) { - ide_pad_transfer(drive, write, nskip << 9); - rq->current_nr_sectors -= nskip; - thislen -= (nskip << 9); - } } + cmd->last_xfer_len = 0; } if (ireason == 0) { @@ -835,15 +780,15 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive) /* bio backed? */ if (rq->bio) { if (blk_fs_request(rq)) { - ptr = rq->buffer; - blen = rq->current_nr_sectors << 9; + blen = min_t(int, thislen, cmd->nleft); } else { ptr = bio_data(rq->bio); blen = bio_iovec(rq->bio)->bv_len; } } - if (!ptr) { + if ((blk_fs_request(rq) && cmd->nleft == 0) || + (blk_fs_request(rq) == 0 && ptr == NULL)) { if (blk_fs_request(rq) && !write) /* * If the buffers are full, pipe the rest into @@ -863,26 +808,16 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive) if (blen > thislen) blen = thislen; - xferfunc(drive, NULL, ptr, blen); + if (blk_fs_request(rq)) { + ide_pio_bytes(drive, cmd, write, blen); + cmd->last_xfer_len += blen; + } else + xferfunc(drive, NULL, ptr, blen); thislen -= blen; len -= blen; - if (blk_fs_request(rq)) { - rq->buffer += blen; - rq->nr_sectors -= (blen >> 9); - rq->current_nr_sectors -= (blen >> 9); - rq->sector += (blen >> 9); - - if (rq->current_nr_sectors == 0 && rq->nr_sectors) { - nsectors = rq->hard_cur_sectors; - - if (nsectors == 0) - nsectors = 1; - - ide_complete_rq(drive, 0, nsectors << 9); - } - } else { + if (blk_fs_request(rq) == 0) { rq->data_len -= blen; /* @@ -933,8 +868,10 @@ out_end: ide_cd_complete_failed_rq(drive, rq); if (blk_fs_request(rq)) { - if (rq->current_nr_sectors == 0) + if (cmd->nleft == 0) uptodate = 1; + if (uptodate == 0) + ide_cd_error_cmd(drive, cmd); } else { if (uptodate <= 0 && rq->errors == 0) rq->errors = -EIO; @@ -944,7 +881,7 @@ out_end: if (blk_pc_request(rq)) nsectors = (rq->data_len + 511) >> 9; else - nsectors = rq->hard_cur_sectors; + nsectors = rq->hard_nr_sectors; if (nsectors == 0) nsectors = 1; @@ -960,9 +897,10 @@ out_end: static ide_startstop_t cdrom_start_rw(ide_drive_t *drive, struct request *rq) { struct cdrom_info *cd = drive->driver_data; + struct request_queue *q = drive->queue; int write = rq_data_dir(rq) == WRITE; unsigned short sectors_per_frame = - queue_hardsect_size(drive->queue) >> SECTOR_BITS; + queue_hardsect_size(q) >> SECTOR_BITS; ide_debug_log(IDE_DBG_RQ, "rq->cmd[0]: 0x%x, write: 0x%x, " "secs_per_frame: %u", @@ -977,17 +915,16 @@ static ide_startstop_t cdrom_start_rw(ide_drive_t *drive, struct request *rq) * We may be retrying this request after an error. Fix up any * weirdness which might be present in the request packet. */ - ide_cd_restore_request(drive, rq); + q->prep_rq_fn(q, rq); } - /* use DMA, if possible / writes *must* be hardware frame aligned */ + /* fs requests *must* be hardware frame aligned */ if ((rq->nr_sectors & (sectors_per_frame - 1)) || - (rq->sector & (sectors_per_frame - 1))) { - if (write) - return ide_stopped; - drive->dma = 0; - } else - drive->dma = !!(drive->dev_flags & IDE_DFLAG_USING_DMA); + (rq->sector & (sectors_per_frame - 1))) + return ide_stopped; + + /* use DMA, if possible */ + drive->dma = !!(drive->dev_flags & IDE_DFLAG_USING_DMA); if (write) cd->devinfo.media_written = 1; @@ -1050,8 +987,6 @@ static ide_startstop_t ide_cd_do_request(ide_drive_t *drive, struct request *rq, if (blk_fs_request(rq)) { if (cdrom_start_rw(drive, rq) == ide_stopped || ide_cd_prepare_rw_request(drive, rq) == ide_stopped) { - if (rq->current_nr_sectors == 0) - uptodate = 1; goto out_end; } } else if (blk_sense_request(rq) || blk_pc_request(rq) || @@ -1078,6 +1013,11 @@ static ide_startstop_t ide_cd_do_request(ide_drive_t *drive, struct request *rq, cmd.rq = rq; + if (blk_fs_request(rq)) { + ide_init_sg_cmd(&cmd, rq->nr_sectors << 9); + ide_map_sg(drive, &cmd); + } + return ide_issue_pc(drive, &cmd); out_end: nsectors = rq->hard_nr_sectors; diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c index 0e333ecf2ad6..a3b7a50562b2 100644 --- a/drivers/ide/ide-taskfile.c +++ b/drivers/ide/ide-taskfile.c @@ -188,8 +188,8 @@ static u8 wait_drive_not_busy(ide_drive_t *drive) return stat; } -static void ide_pio_bytes(ide_drive_t *drive, struct ide_cmd *cmd, - unsigned int write, unsigned int len) +void ide_pio_bytes(ide_drive_t *drive, struct ide_cmd *cmd, + unsigned int write, unsigned int len) { ide_hwif_t *hwif = drive->hwif; struct scatterlist *sg = hwif->sg_table; @@ -243,6 +243,7 @@ static void ide_pio_bytes(ide_drive_t *drive, struct ide_cmd *cmd, len -= nr_bytes; } } +EXPORT_SYMBOL_GPL(ide_pio_bytes); static void ide_pio_datablock(ide_drive_t *drive, struct ide_cmd *cmd, unsigned int write) diff --git a/include/linux/ide.h b/include/linux/ide.h index d5d832271f44..c2841c0c36c8 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -352,6 +352,8 @@ struct ide_cmd { unsigned int nbytes; unsigned int nleft; + unsigned int last_xfer_len; + struct scatterlist *cursg; unsigned int cursg_ofs; @@ -1226,6 +1228,8 @@ ide_startstop_t ide_issue_pc(ide_drive_t *, struct ide_cmd *); ide_startstop_t do_rw_taskfile(ide_drive_t *, struct ide_cmd *); +void ide_pio_bytes(ide_drive_t *, struct ide_cmd *, unsigned int, unsigned int); + void ide_finish_cmd(ide_drive_t *, struct ide_cmd *, u8); int ide_raw_taskfile(ide_drive_t *, struct ide_cmd *, u8 *, u16); -- cgit v1.2.3 From 06a449e30135aabb6686c95bf0c42b46d169a3b3 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 31 Mar 2009 20:15:13 +0200 Subject: ide-cd: fix non-SECTOR_SIZE-multiples PIO transfers for fs requests We now support arbitrary number of bytes per-IRQ also for fs requests so remove ide_cd_check_transfer_size() and IDE_AFLAG_LIMIT_NFRAMES. Cc: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-cd.c | 36 +----------------------------------- include/linux/ide.h | 5 ----- 2 files changed, 1 insertion(+), 40 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 5f15859c2c73..c0cefe5becf3 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -509,31 +509,6 @@ static int ide_cd_check_ireason(ide_drive_t *drive, struct request *rq, return -1; } -/* - * Assume that the drive will always provide data in multiples of at least - * SECTOR_SIZE, as it gets hairy to keep track of the transfers otherwise. - */ -static int ide_cd_check_transfer_size(ide_drive_t *drive, int len) -{ - ide_debug_log(IDE_DBG_FUNC, "len: %d", len); - - if ((len % SECTOR_SIZE) == 0) - return 0; - - printk(KERN_ERR PFX "%s: %s: Bad transfer size %d\n", drive->name, - __func__, len); - - if (drive->atapi_flags & IDE_AFLAG_LIMIT_NFRAMES) - printk(KERN_ERR PFX "This drive is not supported by this " - "version of the driver\n"); - else { - printk(KERN_ERR PFX "Trying to limit transfer sizes\n"); - drive->atapi_flags |= IDE_AFLAG_LIMIT_NFRAMES; - } - - return 1; -} - static ide_startstop_t ide_cd_prepare_rw_request(ide_drive_t *drive, struct request *rq) { @@ -752,13 +727,7 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive) if (rc) goto out_end; - if (blk_fs_request(rq)) { - if (write == 0) { - if (ide_cd_check_transfer_size(drive, len)) - goto out_end; - } - cmd->last_xfer_len = 0; - } + cmd->last_xfer_len = 0; if (ireason == 0) { write = 1; @@ -1619,9 +1588,6 @@ static const struct ide_proc_devset *ide_cd_proc_devsets(ide_drive_t *drive) #endif static const struct cd_list_entry ide_cd_quirks_list[] = { - /* Limit transfer size per interrupt. */ - { "SAMSUNG CD-ROM SCR-2430", NULL, IDE_AFLAG_LIMIT_NFRAMES }, - { "SAMSUNG CD-ROM SCR-2432", NULL, IDE_AFLAG_LIMIT_NFRAMES }, /* SCR-3231 doesn't support the SET_CD_SPEED command. */ { "SAMSUNG CD-ROM SCR-3231", NULL, IDE_AFLAG_NO_SPEED_SELECT }, /* Old NEC260 (not R) was released before ATAPI 1.2 spec. */ diff --git a/include/linux/ide.h b/include/linux/ide.h index c2841c0c36c8..cb501bf78f7d 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -458,11 +458,6 @@ enum { IDE_AFLAG_TOCADDR_AS_BCD = (1 << 3), /* TOC track numbers are in BCD. */ IDE_AFLAG_TOCTRACKS_AS_BCD = (1 << 4), - /* - * Drive does not provide data in multiples of SECTOR_SIZE - * when more than one interrupt is needed. - */ - IDE_AFLAG_LIMIT_NFRAMES = (1 << 5), /* Saved TOC information is current. */ IDE_AFLAG_TOC_VALID = (1 << 6), /* We think that the drive door is locked. */ -- cgit v1.2.3 From 8652b31ab211b6fe2a4994cc47b61d7038c3489c Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 31 Mar 2009 20:15:14 +0200 Subject: ide-cd: merge ide_cd_prepare_rw_request() into cdrom_start_rw() There should be no functional changes caused by this patch. Cc: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-cd.c | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index c0cefe5becf3..cf0707fe87e8 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -509,17 +509,6 @@ static int ide_cd_check_ireason(ide_drive_t *drive, struct request *rq, return -1; } -static ide_startstop_t ide_cd_prepare_rw_request(ide_drive_t *drive, - struct request *rq) -{ - ide_debug_log(IDE_DBG_RQ, "rq->cmd_flags: 0x%x", rq->cmd_flags); - - /* set up the command */ - rq->timeout = ATAPI_WAIT_PC; - - return ide_started; -} - static void ide_cd_request_sense_fixup(ide_drive_t *drive, struct request *rq) { ide_debug_log(IDE_DBG_FUNC, "rq->cmd[0]: 0x%x", rq->cmd[0]); @@ -871,9 +860,9 @@ static ide_startstop_t cdrom_start_rw(ide_drive_t *drive, struct request *rq) unsigned short sectors_per_frame = queue_hardsect_size(q) >> SECTOR_BITS; - ide_debug_log(IDE_DBG_RQ, "rq->cmd[0]: 0x%x, write: 0x%x, " + ide_debug_log(IDE_DBG_RQ, "rq->cmd[0]: 0x%x, rq->cmd_flags: 0x%x, " "secs_per_frame: %u", - rq->cmd[0], write, sectors_per_frame); + rq->cmd[0], rq->cmd_flags, sectors_per_frame); if (write) { /* disk has become write protected */ @@ -898,6 +887,8 @@ static ide_startstop_t cdrom_start_rw(ide_drive_t *drive, struct request *rq) if (write) cd->devinfo.media_written = 1; + rq->timeout = ATAPI_WAIT_PC; + return ide_started; } @@ -954,10 +945,8 @@ static ide_startstop_t ide_cd_do_request(ide_drive_t *drive, struct request *rq, blk_dump_rq_flags(rq, "ide_cd_do_request"); if (blk_fs_request(rq)) { - if (cdrom_start_rw(drive, rq) == ide_stopped || - ide_cd_prepare_rw_request(drive, rq) == ide_stopped) { + if (cdrom_start_rw(drive, rq) == ide_stopped) goto out_end; - } } else if (blk_sense_request(rq) || blk_pc_request(rq) || rq->cmd_type == REQ_TYPE_ATA_PC) { if (!rq->timeout) -- cgit v1.2.3 From c7ec89994fec4353d5b4251213bdfa7b1a68c26b Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 31 Mar 2009 20:15:15 +0200 Subject: ide-cd: use scatterlists for PIO transfers (non-fs requests) (v2) Convert ide-cd to use scatterlists for PIO transfers and get rid of partial completions (except on error) also for non-fs requests. v2: Do not map dataless commands to an sg since it oopses on the virt_to_page() translation check when DEBUG_VIRTUAL is enabled. (from Borislav Petkov, reported/bisected-by Tetsuo Handa). Cc: Tetsuo Handa Acked-by: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-cd.c | 100 ++++++++++++++++----------------------------------- 1 file changed, 30 insertions(+), 70 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index cf0707fe87e8..7fdfb55011a6 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -509,8 +509,10 @@ static int ide_cd_check_ireason(ide_drive_t *drive, struct request *rq, return -1; } -static void ide_cd_request_sense_fixup(ide_drive_t *drive, struct request *rq) +static void ide_cd_request_sense_fixup(ide_drive_t *drive, struct ide_cmd *cmd) { + struct request *rq = cmd->rq; + ide_debug_log(IDE_DBG_FUNC, "rq->cmd[0]: 0x%x", rq->cmd[0]); /* @@ -518,11 +520,14 @@ static void ide_cd_request_sense_fixup(ide_drive_t *drive, struct request *rq) * and some drives don't send them. Sigh. */ if (rq->cmd[0] == GPCMD_REQUEST_SENSE && - rq->data_len > 0 && rq->data_len <= 5) - while (rq->data_len > 0) { - *(u8 *)rq->data++ = 0; - --rq->data_len; + cmd->nleft > 0 && cmd->nleft <= 5) { + unsigned int ofs = cmd->nbytes - cmd->nleft; + + while (cmd->nleft > 0) { + *((u8 *)rq->data + ofs++) = 0; + cmd->nleft--; } + } } int ide_cd_queue_pc(ide_drive_t *drive, const unsigned char *cmd, @@ -613,22 +618,11 @@ static void ide_cd_error_cmd(ide_drive_t *drive, struct ide_cmd *cmd) ide_complete_rq(drive, 0, nr_bytes); } -/* - * Called from blk_end_request_callback() after the data of the request is - * completed and before the request itself is completed. By returning value '1', - * blk_end_request_callback() returns immediately without completing it. - */ -static int cdrom_newpc_intr_dummy_cb(struct request *rq) -{ - return 1; -} - static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; struct ide_cmd *cmd = &hwif->cmd; struct request *rq = hwif->rq; - xfer_func_t *xferfunc; ide_expiry_t *expiry = NULL; int dma_error = 0, dma, stat, thislen, uptodate = 0; int write = (rq_data_dir(rq) == WRITE) ? 1 : 0, rc, nsectors; @@ -678,7 +672,7 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive) ide_read_bcount_and_ireason(drive, &len, &ireason); - thislen = blk_fs_request(rq) ? len : rq->data_len; + thislen = blk_fs_request(rq) ? len : cmd->nleft; if (thislen > len) thislen = len; @@ -702,9 +696,9 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive) uptodate = 0; } } else if (!blk_pc_request(rq)) { - ide_cd_request_sense_fixup(drive, rq); + ide_cd_request_sense_fixup(drive, cmd); /* complain if we still have data left to transfer */ - uptodate = rq->data_len ? 0 : 1; + uptodate = cmd->nleft ? 0 : 1; if (uptodate == 0) rq->cmd_flags |= REQ_FAILED; } @@ -718,35 +712,15 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive) cmd->last_xfer_len = 0; - if (ireason == 0) { - write = 1; - xferfunc = hwif->tp_ops->output_data; - } else { - write = 0; - xferfunc = hwif->tp_ops->input_data; - } - ide_debug_log(IDE_DBG_PC, "data transfer, rq->cmd_type: 0x%x, " "ireason: 0x%x", rq->cmd_type, ireason); /* transfer data */ while (thislen > 0) { - u8 *ptr = blk_fs_request(rq) ? NULL : rq->data; - int blen = rq->data_len; - - /* bio backed? */ - if (rq->bio) { - if (blk_fs_request(rq)) { - blen = min_t(int, thislen, cmd->nleft); - } else { - ptr = bio_data(rq->bio); - blen = bio_iovec(rq->bio)->bv_len; - } - } + int blen = min_t(int, thislen, cmd->nleft); - if ((blk_fs_request(rq) && cmd->nleft == 0) || - (blk_fs_request(rq) == 0 && ptr == NULL)) { + if (cmd->nleft == 0) { if (blk_fs_request(rq) && !write) /* * If the buffers are full, pipe the rest into @@ -763,33 +737,12 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive) break; } - if (blen > thislen) - blen = thislen; - - if (blk_fs_request(rq)) { - ide_pio_bytes(drive, cmd, write, blen); - cmd->last_xfer_len += blen; - } else - xferfunc(drive, NULL, ptr, blen); + ide_pio_bytes(drive, cmd, write, blen); + cmd->last_xfer_len += blen; thislen -= blen; len -= blen; - if (blk_fs_request(rq) == 0) { - rq->data_len -= blen; - - /* - * The request can't be completed until DRQ is cleared. - * So complete the data, but don't complete the request - * using the dummy function for the callback feature - * of blk_end_request_callback(). - */ - if (rq->bio) - blk_end_request_callback(rq, 0, blen, - cdrom_newpc_intr_dummy_cb); - else - rq->data += blen; - } if (sense && write == 0) rq->sense_len += blen; } @@ -814,8 +767,7 @@ out_end: if (blk_pc_request(rq) && rc == 0) { unsigned int dlen = rq->data_len; - if (dma) - rq->data_len = 0; + rq->data_len = 0; if (blk_end_request(rq, 0, dlen)) BUG(); @@ -828,13 +780,14 @@ out_end: if (blk_fs_request(rq)) { if (cmd->nleft == 0) uptodate = 1; - if (uptodate == 0) - ide_cd_error_cmd(drive, cmd); } else { if (uptodate <= 0 && rq->errors == 0) rq->errors = -EIO; } + if (uptodate == 0) + ide_cd_error_cmd(drive, cmd); + /* make sure it's fully ended */ if (blk_pc_request(rq)) nsectors = (rq->data_len + 511) >> 9; @@ -844,6 +797,12 @@ out_end: if (nsectors == 0) nsectors = 1; + if (blk_fs_request(rq) == 0) { + rq->data_len -= (cmd->nbytes - cmd->nleft); + if (uptodate == 0 && (cmd->tf_flags & IDE_TFLAG_WRITE)) + rq->data_len += cmd->last_xfer_len; + } + ide_complete_rq(drive, uptodate ? 0 : -EIO, nsectors << 9); if (sense && rc == 2) @@ -971,8 +930,9 @@ static ide_startstop_t ide_cd_do_request(ide_drive_t *drive, struct request *rq, cmd.rq = rq; - if (blk_fs_request(rq)) { - ide_init_sg_cmd(&cmd, rq->nr_sectors << 9); + if (blk_fs_request(rq) || rq->data_len) { + ide_init_sg_cmd(&cmd, blk_fs_request(rq) ? (rq->nr_sectors << 9) + : rq->data_len); ide_map_sg(drive, &cmd); } -- cgit v1.2.3 From 4a3d8cf48c7baf3439aed06c847cd4562adfc468 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 31 Mar 2009 20:15:15 +0200 Subject: ide-cd: use common completion path for DMA requests in cdrom_newpc_intr() Use the following facts: - rq->nr_sectors should now be always equal to (non-zero) rq->hard_nr_sectors for fs requests - REQ_TYPE_ATA_PC requests have never bio attached to them - rq->hard_nr_sectors == 0 for REQ_TYPE_ATA_PC requests - DMA is used only for fs, pc and REQ_TYPE_ATA_PC requests - 'uptodate' is ignored for pc requests ('rc == 0' case) and use the common completion path also for DMA requests. There should be no functional changes caused by this patch Cc: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-cd.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 7fdfb55011a6..11d8d5b870b7 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -657,16 +657,7 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive) if (dma) { if (dma_error) return ide_error(drive, "dma error", stat); - if (blk_fs_request(rq)) { - ide_complete_rq(drive, 0, rq->nr_sectors - ? (rq->nr_sectors << 9) : ide_rq_bytes(rq)); - return ide_stopped; - } else if (rq->cmd_type == REQ_TYPE_ATA_PC && !rq->bio) { - ide_complete_rq(drive, 0, 512); - return ide_stopped; - } - if (blk_pc_request(rq) == 0 && uptodate == 0) - rq->cmd_flags |= REQ_FAILED; + uptodate = 1; goto out_end; } -- cgit v1.2.3 From 14fa91ccbafa02a71cfb53f9c830b8c0c65119d0 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 31 Mar 2009 20:15:16 +0200 Subject: ide-cd: unify transfer padding in cdrom_newpc_intr() * 'thislen' is always <= cmd->nleft for non-fs requests so the transfer padding inside the 'while (thislen > 0)' loop can happen only for fs requests -- then move it out of the loop and unify with the transfer padding for non-fs requests ('thislen' == 'len' for fs requests). * blk_dump_rq_flags() dumps all request flags so it is enough to pass only the function name to it. * Update my Copyrights while at it. Cc: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-cd.c | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 11d8d5b870b7..0201201c6e48 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -4,7 +4,7 @@ * Copyright (C) 1994-1996 Scott Snyder * Copyright (C) 1996-1998 Erik Andersen * Copyright (C) 1998-2000 Jens Axboe - * Copyright (C) 2005, 2007 Bartlomiej Zolnierkiewicz + * Copyright (C) 2005, 2007-2009 Bartlomiej Zolnierkiewicz * * May be copied or modified under the terms of the GNU General Public * License. See linux/COPYING for more information. @@ -711,22 +711,8 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive) while (thislen > 0) { int blen = min_t(int, thislen, cmd->nleft); - if (cmd->nleft == 0) { - if (blk_fs_request(rq) && !write) - /* - * If the buffers are full, pipe the rest into - * oblivion. - */ - ide_pad_transfer(drive, 0, thislen); - else { - printk(KERN_ERR PFX "%s: confused, missing data\n", - drive->name); - blk_dump_rq_flags(rq, rq_data_dir(rq) - ? "cdrom_newpc_intr, write" - : "cdrom_newpc_intr, read"); - } + if (cmd->nleft == 0) break; - } ide_pio_bytes(drive, cmd, write, blen); cmd->last_xfer_len += blen; @@ -739,8 +725,15 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive) } /* pad, if necessary */ - if (!blk_fs_request(rq) && len > 0) - ide_pad_transfer(drive, write, len); + if (len > 0) { + if (blk_fs_request(rq) == 0 || write == 0) + ide_pad_transfer(drive, write, len); + else { + printk(KERN_ERR PFX "%s: confused, missing data\n", + drive->name); + blk_dump_rq_flags(rq, "cdrom_newpc_intr"); + } + } if (blk_pc_request(rq)) { timeout = rq->timeout; -- cgit v1.2.3 From e698ea83a8531a6740dc657329dcf0728392d6ac Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 31 Mar 2009 20:15:16 +0200 Subject: ide-cd: minor ide_cdrom_setup() cleanup Cache drive->queue in local variable and use max(). There should be no functional changes caused by this patch. Acked-by: Sergei Shtylyov Acked-by: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-cd.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 0201201c6e48..5319e7a73708 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -1581,18 +1581,18 @@ static int ide_cdrom_setup(ide_drive_t *drive) { struct cdrom_info *cd = drive->driver_data; struct cdrom_device_info *cdi = &cd->devinfo; + struct request_queue *q = drive->queue; u16 *id = drive->id; char *fw_rev = (char *)&id[ATA_ID_FW_REV]; int nslots; ide_debug_log(IDE_DBG_PROBE, "enter"); - blk_queue_prep_rq(drive->queue, ide_cdrom_prep_fn); - blk_queue_dma_alignment(drive->queue, 31); - blk_queue_update_dma_pad(drive->queue, 15); - drive->queue->unplug_delay = (1 * HZ) / 1000; - if (!drive->queue->unplug_delay) - drive->queue->unplug_delay = 1; + blk_queue_prep_rq(q, ide_cdrom_prep_fn); + blk_queue_dma_alignment(q, 31); + blk_queue_update_dma_pad(q, 15); + + q->unplug_delay = max((1 * HZ) / 1000, 1); drive->dev_flags |= IDE_DFLAG_MEDIA_CHANGED; drive->atapi_flags = IDE_AFLAG_NO_EJECT | ide_cd_flags(id); @@ -1610,8 +1610,7 @@ static int ide_cdrom_setup(ide_drive_t *drive) nslots = ide_cdrom_probe_capabilities(drive); - /* set correct block size */ - blk_queue_hardsect_size(drive->queue, CD_FRAMESIZE); + blk_queue_hardsect_size(q, CD_FRAMESIZE); if (ide_cdrom_register(drive, nslots)) { printk(KERN_ERR PFX "%s: %s failed to register device with the" -- cgit v1.2.3 From 35c9b4daf4c94b30e5cede597d98016ebf31b5ad Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 31 Mar 2009 20:15:19 +0200 Subject: ide: add ->dma_clear method and remove ->dma_timeout one All custom ->dma_timeout implementations call the generic one thus it is possible to have only an optional method for resetting DMA engine instead: * Add ->dma_clear method and convert hpt366, pdc202xx_old and sl82c105 host drivers to use it. * Always use ide_dma_timeout() in ide_dma_timeout_retry() and remove ->dma_timeout method. * Make ide_dma_timeout() static. There should be no functional changes caused by this patch. Acked-by: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/alim15x3.c | 1 - drivers/ide/au1xxx-ide.c | 1 - drivers/ide/cmd64x.c | 3 --- drivers/ide/cs5536.c | 1 - drivers/ide/hpt366.c | 10 +--------- drivers/ide/icside.c | 1 - drivers/ide/ide-dma-sff.c | 3 +-- drivers/ide/ide-dma.c | 10 ++++++---- drivers/ide/it821x.c | 3 +-- drivers/ide/ns87415.c | 1 - drivers/ide/pdc202xx_old.c | 10 ++-------- drivers/ide/pmac.c | 1 - drivers/ide/sc1200.c | 1 - drivers/ide/scc_pata.c | 1 - drivers/ide/sgiioc4.c | 1 - drivers/ide/siimage.c | 1 - drivers/ide/sl82c105.c | 7 +++---- drivers/ide/tc86c001.c | 1 - drivers/ide/trm290.c | 1 - drivers/ide/tx4939ide.c | 1 - include/linux/ide.h | 4 ++-- 21 files changed, 16 insertions(+), 47 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/alim15x3.c b/drivers/ide/alim15x3.c index d516168464fc..d3faf0b97f42 100644 --- a/drivers/ide/alim15x3.c +++ b/drivers/ide/alim15x3.c @@ -509,7 +509,6 @@ static const struct ide_dma_ops ali_dma_ops = { .dma_test_irq = ide_dma_test_irq, .dma_lost_irq = ide_dma_lost_irq, .dma_timer_expiry = ide_dma_sff_timer_expiry, - .dma_timeout = ide_dma_timeout, .dma_sff_read_status = ide_dma_sff_read_status, }; diff --git a/drivers/ide/au1xxx-ide.c b/drivers/ide/au1xxx-ide.c index d3a9d6c15328..0c08c5e01f2a 100644 --- a/drivers/ide/au1xxx-ide.c +++ b/drivers/ide/au1xxx-ide.c @@ -353,7 +353,6 @@ static const struct ide_dma_ops au1xxx_dma_ops = { .dma_end = auide_dma_end, .dma_test_irq = auide_dma_test_irq, .dma_lost_irq = ide_dma_lost_irq, - .dma_timeout = ide_dma_timeout, }; static int auide_ddma_init(ide_hwif_t *hwif, const struct ide_port_info *d) diff --git a/drivers/ide/cmd64x.c b/drivers/ide/cmd64x.c index bf0e3f470824..f0a49d2ff711 100644 --- a/drivers/ide/cmd64x.c +++ b/drivers/ide/cmd64x.c @@ -384,7 +384,6 @@ static const struct ide_dma_ops cmd64x_dma_ops = { .dma_test_irq = cmd64x_dma_test_irq, .dma_lost_irq = ide_dma_lost_irq, .dma_timer_expiry = ide_dma_sff_timer_expiry, - .dma_timeout = ide_dma_timeout, .dma_sff_read_status = ide_dma_sff_read_status, }; @@ -396,7 +395,6 @@ static const struct ide_dma_ops cmd646_rev1_dma_ops = { .dma_test_irq = ide_dma_test_irq, .dma_lost_irq = ide_dma_lost_irq, .dma_timer_expiry = ide_dma_sff_timer_expiry, - .dma_timeout = ide_dma_timeout, .dma_sff_read_status = ide_dma_sff_read_status, }; @@ -408,7 +406,6 @@ static const struct ide_dma_ops cmd648_dma_ops = { .dma_test_irq = cmd648_dma_test_irq, .dma_lost_irq = ide_dma_lost_irq, .dma_timer_expiry = ide_dma_sff_timer_expiry, - .dma_timeout = ide_dma_timeout, .dma_sff_read_status = ide_dma_sff_read_status, }; diff --git a/drivers/ide/cs5536.c b/drivers/ide/cs5536.c index d5dcf4899607..353a35bbba63 100644 --- a/drivers/ide/cs5536.c +++ b/drivers/ide/cs5536.c @@ -236,7 +236,6 @@ static const struct ide_dma_ops cs5536_dma_ops = { .dma_test_irq = ide_dma_test_irq, .dma_lost_irq = ide_dma_lost_irq, .dma_timer_expiry = ide_dma_sff_timer_expiry, - .dma_timeout = ide_dma_timeout, }; static const struct ide_port_info cs5536_info = { diff --git a/drivers/ide/hpt366.c b/drivers/ide/hpt366.c index dbaf184ed9c5..a0eb87f59134 100644 --- a/drivers/ide/hpt366.c +++ b/drivers/ide/hpt366.c @@ -835,12 +835,6 @@ static int hpt370_dma_end(ide_drive_t *drive) return ide_dma_end(drive); } -static void hpt370_dma_timeout(ide_drive_t *drive) -{ - hpt370_irq_timeout(drive); - ide_dma_timeout(drive); -} - /* returns 1 if DMA IRQ issued, 0 otherwise */ static int hpt374_dma_test_irq(ide_drive_t *drive) { @@ -1423,7 +1417,6 @@ static const struct ide_dma_ops hpt37x_dma_ops = { .dma_test_irq = hpt374_dma_test_irq, .dma_lost_irq = ide_dma_lost_irq, .dma_timer_expiry = ide_dma_sff_timer_expiry, - .dma_timeout = ide_dma_timeout, .dma_sff_read_status = ide_dma_sff_read_status, }; @@ -1435,7 +1428,7 @@ static const struct ide_dma_ops hpt370_dma_ops = { .dma_test_irq = ide_dma_test_irq, .dma_lost_irq = ide_dma_lost_irq, .dma_timer_expiry = ide_dma_sff_timer_expiry, - .dma_timeout = hpt370_dma_timeout, + .dma_clear = hpt370_irq_timeout, .dma_sff_read_status = ide_dma_sff_read_status, }; @@ -1447,7 +1440,6 @@ static const struct ide_dma_ops hpt36x_dma_ops = { .dma_test_irq = ide_dma_test_irq, .dma_lost_irq = hpt366_dma_lost_irq, .dma_timer_expiry = ide_dma_sff_timer_expiry, - .dma_timeout = ide_dma_timeout, .dma_sff_read_status = ide_dma_sff_read_status, }; diff --git a/drivers/ide/icside.c b/drivers/ide/icside.c index 51ce404fe532..f069f122ee6e 100644 --- a/drivers/ide/icside.c +++ b/drivers/ide/icside.c @@ -377,7 +377,6 @@ static const struct ide_dma_ops icside_v6_dma_ops = { .dma_start = icside_dma_start, .dma_end = icside_dma_end, .dma_test_irq = icside_dma_test_irq, - .dma_timeout = ide_dma_timeout, .dma_lost_irq = ide_dma_lost_irq, }; #else diff --git a/drivers/ide/ide-dma-sff.c b/drivers/ide/ide-dma-sff.c index 75a9ea2e4c82..7836d7e03fff 100644 --- a/drivers/ide/ide-dma-sff.c +++ b/drivers/ide/ide-dma-sff.c @@ -338,9 +338,8 @@ const struct ide_dma_ops sff_dma_ops = { .dma_start = ide_dma_start, .dma_end = ide_dma_end, .dma_test_irq = ide_dma_test_irq, - .dma_timer_expiry = ide_dma_sff_timer_expiry, - .dma_timeout = ide_dma_timeout, .dma_lost_irq = ide_dma_lost_irq, + .dma_timer_expiry = ide_dma_sff_timer_expiry, .dma_sff_read_status = ide_dma_sff_read_status, }; EXPORT_SYMBOL_GPL(sff_dma_ops); diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index 3dbf80c15491..dc5d9bc4ced0 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -460,7 +460,7 @@ void ide_dma_lost_irq(ide_drive_t *drive) } EXPORT_SYMBOL_GPL(ide_dma_lost_irq); -void ide_dma_timeout(ide_drive_t *drive) +static void ide_dma_timeout(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; @@ -473,7 +473,6 @@ void ide_dma_timeout(ide_drive_t *drive) hwif->dma_ops->dma_end(drive); } -EXPORT_SYMBOL_GPL(ide_dma_timeout); /* * un-busy the port etc, and clear any pending DMA status. we want to @@ -483,6 +482,7 @@ EXPORT_SYMBOL_GPL(ide_dma_timeout); ide_startstop_t ide_dma_timeout_retry(ide_drive_t *drive, int error) { ide_hwif_t *hwif = drive->hwif; + const struct ide_dma_ops *dma_ops = hwif->dma_ops; struct request *rq; ide_startstop_t ret = ide_stopped; @@ -492,12 +492,14 @@ ide_startstop_t ide_dma_timeout_retry(ide_drive_t *drive, int error) if (error < 0) { printk(KERN_WARNING "%s: DMA timeout error\n", drive->name); - (void)hwif->dma_ops->dma_end(drive); + (void)dma_ops->dma_end(drive); ret = ide_error(drive, "dma timeout error", hwif->tp_ops->read_status(hwif)); } else { printk(KERN_WARNING "%s: DMA timeout retry\n", drive->name); - hwif->dma_ops->dma_timeout(drive); + if (dma_ops->dma_clear) + dma_ops->dma_clear(drive); + ide_dma_timeout(drive); } /* diff --git a/drivers/ide/it821x.c b/drivers/ide/it821x.c index 0d4ac65cf949..51aa745246dc 100644 --- a/drivers/ide/it821x.c +++ b/drivers/ide/it821x.c @@ -511,9 +511,8 @@ static struct ide_dma_ops it821x_pass_through_dma_ops = { .dma_start = it821x_dma_start, .dma_end = it821x_dma_end, .dma_test_irq = ide_dma_test_irq, - .dma_timer_expiry = ide_dma_sff_timer_expiry, - .dma_timeout = ide_dma_timeout, .dma_lost_irq = ide_dma_lost_irq, + .dma_timer_expiry = ide_dma_sff_timer_expiry, .dma_sff_read_status = ide_dma_sff_read_status, }; diff --git a/drivers/ide/ns87415.c b/drivers/ide/ns87415.c index 7b65fe5bf449..9039a373020f 100644 --- a/drivers/ide/ns87415.c +++ b/drivers/ide/ns87415.c @@ -306,7 +306,6 @@ static const struct ide_dma_ops ns87415_dma_ops = { .dma_test_irq = ide_dma_test_irq, .dma_lost_irq = ide_dma_lost_irq, .dma_timer_expiry = ide_dma_sff_timer_expiry, - .dma_timeout = ide_dma_timeout, .dma_sff_read_status = superio_dma_sff_read_status, }; diff --git a/drivers/ide/pdc202xx_old.c b/drivers/ide/pdc202xx_old.c index f7536d1943f7..248a54bd2386 100644 --- a/drivers/ide/pdc202xx_old.c +++ b/drivers/ide/pdc202xx_old.c @@ -258,12 +258,6 @@ static void pdc202xx_dma_lost_irq(ide_drive_t *drive) ide_dma_lost_irq(drive); } -static void pdc202xx_dma_timeout(ide_drive_t *drive) -{ - pdc202xx_reset(drive); - ide_dma_timeout(drive); -} - static int init_chipset_pdc202xx(struct pci_dev *dev) { unsigned long dmabase = pci_resource_start(dev, 4); @@ -336,7 +330,7 @@ static const struct ide_dma_ops pdc20246_dma_ops = { .dma_test_irq = pdc202xx_dma_test_irq, .dma_lost_irq = pdc202xx_dma_lost_irq, .dma_timer_expiry = ide_dma_sff_timer_expiry, - .dma_timeout = pdc202xx_dma_timeout, + .dma_clear = pdc202xx_reset, .dma_sff_read_status = ide_dma_sff_read_status, }; @@ -348,7 +342,7 @@ static const struct ide_dma_ops pdc2026x_dma_ops = { .dma_test_irq = pdc202xx_dma_test_irq, .dma_lost_irq = pdc202xx_dma_lost_irq, .dma_timer_expiry = ide_dma_sff_timer_expiry, - .dma_timeout = pdc202xx_dma_timeout, + .dma_clear = pdc202xx_reset, .dma_sff_read_status = ide_dma_sff_read_status, }; diff --git a/drivers/ide/pmac.c b/drivers/ide/pmac.c index 2bfcfedaa076..d15cc46a66e3 100644 --- a/drivers/ide/pmac.c +++ b/drivers/ide/pmac.c @@ -1650,7 +1650,6 @@ static const struct ide_dma_ops pmac_dma_ops = { .dma_start = pmac_ide_dma_start, .dma_end = pmac_ide_dma_end, .dma_test_irq = pmac_ide_dma_test_irq, - .dma_timeout = ide_dma_timeout, .dma_lost_irq = pmac_ide_dma_lost_irq, }; diff --git a/drivers/ide/sc1200.c b/drivers/ide/sc1200.c index 1c3a82914999..371549d18a01 100644 --- a/drivers/ide/sc1200.c +++ b/drivers/ide/sc1200.c @@ -291,7 +291,6 @@ static const struct ide_dma_ops sc1200_dma_ops = { .dma_test_irq = ide_dma_test_irq, .dma_lost_irq = ide_dma_lost_irq, .dma_timer_expiry = ide_dma_sff_timer_expiry, - .dma_timeout = ide_dma_timeout, .dma_sff_read_status = ide_dma_sff_read_status, }; diff --git a/drivers/ide/scc_pata.c b/drivers/ide/scc_pata.c index 0cc137cfe76d..64534d150b0c 100644 --- a/drivers/ide/scc_pata.c +++ b/drivers/ide/scc_pata.c @@ -872,7 +872,6 @@ static const struct ide_dma_ops scc_dma_ops = { .dma_end = scc_dma_end, .dma_test_irq = scc_dma_test_irq, .dma_lost_irq = ide_dma_lost_irq, - .dma_timeout = ide_dma_timeout, .dma_timer_expiry = ide_dma_sff_timer_expiry, .dma_sff_read_status = scc_dma_sff_read_status, }; diff --git a/drivers/ide/sgiioc4.c b/drivers/ide/sgiioc4.c index b12de8346c73..44df0c750bab 100644 --- a/drivers/ide/sgiioc4.c +++ b/drivers/ide/sgiioc4.c @@ -533,7 +533,6 @@ static const struct ide_dma_ops sgiioc4_dma_ops = { .dma_end = sgiioc4_dma_end, .dma_test_irq = sgiioc4_dma_test_irq, .dma_lost_irq = sgiioc4_dma_lost_irq, - .dma_timeout = ide_dma_timeout, }; static const struct ide_port_info sgiioc4_port_info __devinitconst = { diff --git a/drivers/ide/siimage.c b/drivers/ide/siimage.c index 075cb1243b2a..e4973cd1fba9 100644 --- a/drivers/ide/siimage.c +++ b/drivers/ide/siimage.c @@ -715,7 +715,6 @@ static const struct ide_dma_ops sil_dma_ops = { .dma_end = ide_dma_end, .dma_test_irq = siimage_dma_test_irq, .dma_timer_expiry = ide_dma_sff_timer_expiry, - .dma_timeout = ide_dma_timeout, .dma_lost_irq = ide_dma_lost_irq, .dma_sff_read_status = ide_dma_sff_read_status, }; diff --git a/drivers/ide/sl82c105.c b/drivers/ide/sl82c105.c index d25137b04e7a..d6f8977191c8 100644 --- a/drivers/ide/sl82c105.c +++ b/drivers/ide/sl82c105.c @@ -189,14 +189,13 @@ static void sl82c105_dma_start(ide_drive_t *drive) ide_dma_start(drive); } -static void sl82c105_dma_timeout(ide_drive_t *drive) +static void sl82c105_dma_clear(ide_drive_t *drive) { struct pci_dev *dev = to_pci_dev(drive->hwif->dev); - DBG(("sl82c105_dma_timeout(drive:%s)\n", drive->name)); + DBG(("sl82c105_dma_clear(drive:%s)\n", drive->name)); sl82c105_reset_host(dev); - ide_dma_timeout(drive); } static int sl82c105_dma_end(ide_drive_t *drive) @@ -298,7 +297,7 @@ static const struct ide_dma_ops sl82c105_dma_ops = { .dma_test_irq = ide_dma_test_irq, .dma_lost_irq = sl82c105_dma_lost_irq, .dma_timer_expiry = ide_dma_sff_timer_expiry, - .dma_timeout = sl82c105_dma_timeout, + .dma_clear = sl82c105_dma_clear, .dma_sff_read_status = ide_dma_sff_read_status, }; diff --git a/drivers/ide/tc86c001.c b/drivers/ide/tc86c001.c index 427d4b3c2c63..b4cf42dc8a6f 100644 --- a/drivers/ide/tc86c001.c +++ b/drivers/ide/tc86c001.c @@ -187,7 +187,6 @@ static const struct ide_dma_ops tc86c001_dma_ops = { .dma_test_irq = ide_dma_test_irq, .dma_lost_irq = ide_dma_lost_irq, .dma_timer_expiry = ide_dma_sff_timer_expiry, - .dma_timeout = ide_dma_timeout, .dma_sff_read_status = ide_dma_sff_read_status, }; diff --git a/drivers/ide/trm290.c b/drivers/ide/trm290.c index ed1496845a93..d6a950828e9f 100644 --- a/drivers/ide/trm290.c +++ b/drivers/ide/trm290.c @@ -314,7 +314,6 @@ static struct ide_dma_ops trm290_dma_ops = { .dma_end = trm290_dma_end, .dma_test_irq = trm290_dma_test_irq, .dma_lost_irq = ide_dma_lost_irq, - .dma_timeout = ide_dma_timeout, }; static const struct ide_port_info trm290_chipset __devinitdata = { diff --git a/drivers/ide/tx4939ide.c b/drivers/ide/tx4939ide.c index e0e0a803dde3..53f99853b065 100644 --- a/drivers/ide/tx4939ide.c +++ b/drivers/ide/tx4939ide.c @@ -632,7 +632,6 @@ static const struct ide_dma_ops tx4939ide_dma_ops = { .dma_test_irq = tx4939ide_dma_test_irq, .dma_lost_irq = ide_dma_lost_irq, .dma_timer_expiry = ide_dma_sff_timer_expiry, - .dma_timeout = ide_dma_timeout, .dma_sff_read_status = tx4939ide_dma_sff_read_status, }; diff --git a/include/linux/ide.h b/include/linux/ide.h index cb501bf78f7d..d3035f2f1250 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -716,8 +716,9 @@ struct ide_dma_ops { int (*dma_end)(struct ide_drive_s *); int (*dma_test_irq)(struct ide_drive_s *); void (*dma_lost_irq)(struct ide_drive_s *); + /* below ones are optional */ int (*dma_timer_expiry)(struct ide_drive_s *); - void (*dma_timeout)(struct ide_drive_s *); + void (*dma_clear)(struct ide_drive_s *); /* * The following method is optional and only required to be * implemented for the SFF-8038i compatible controllers. @@ -1461,7 +1462,6 @@ static inline int config_drive_for_dma(ide_drive_t *drive) { return 0; } #endif /* CONFIG_BLK_DEV_IDEDMA_SFF */ void ide_dma_lost_irq(ide_drive_t *); -void ide_dma_timeout(ide_drive_t *); ide_startstop_t ide_dma_timeout_retry(ide_drive_t *, int); #else -- cgit v1.2.3 From 1cee52de28aa687760ad262ad0834d1bf6c6d2ac Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 31 Mar 2009 20:15:19 +0200 Subject: ide: inline ide_dma_timeout() into ide_dma_timeout_retry() Since ide_dma_timeout() is only used by ide_dma_timeout_retry() inline it there. There should be no functional changes caused by this patch. Acked-by: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-dma.c | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index dc5d9bc4ced0..4e2005071113 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -460,20 +460,6 @@ void ide_dma_lost_irq(ide_drive_t *drive) } EXPORT_SYMBOL_GPL(ide_dma_lost_irq); -static void ide_dma_timeout(ide_drive_t *drive) -{ - ide_hwif_t *hwif = drive->hwif; - - printk(KERN_ERR "%s: timeout waiting for DMA\n", drive->name); - - if (hwif->dma_ops->dma_test_irq(drive)) - return; - - ide_dump_status(drive, "DMA timeout", hwif->tp_ops->read_status(hwif)); - - hwif->dma_ops->dma_end(drive); -} - /* * un-busy the port etc, and clear any pending DMA status. we want to * retry the current request in pio mode instead of risking tossing it @@ -499,7 +485,12 @@ ide_startstop_t ide_dma_timeout_retry(ide_drive_t *drive, int error) printk(KERN_WARNING "%s: DMA timeout retry\n", drive->name); if (dma_ops->dma_clear) dma_ops->dma_clear(drive); - ide_dma_timeout(drive); + printk(KERN_ERR "%s: timeout waiting for DMA\n", drive->name); + if (dma_ops->dma_test_irq(drive) == 0) { + ide_dump_status(drive, "DMA timeout", + hwif->tp_ops->read_status(hwif)); + (void)dma_ops->dma_end(drive); + } } /* -- cgit v1.2.3 From 4453011f959a5f5c6c7a33aea54fe17f5e43a867 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 31 Mar 2009 20:15:20 +0200 Subject: ide: destroy DMA mappings after ending DMA (v2) Move ide_destroy_dmatable() call out from ->dma_end method to {ide_pc,cdrom_newpc,ide_dma}_intr(), ide_dma_timeout_retry() and sgiioc4_resetproc(). This causes minor/safe behavior changes w.r.t.: * cmd64x.c::cmd64{8,x}_dma_end() * cs5536.c::cs5536_dma_end() * icside.c::icside_dma_end() * it821x.c::it821x_dma_end() * scc_pata.c::__scc_dma_end() * sl82c105.c::sl82c105_dma_end() * tx4939ide.c::tx4939ide_dma_end() v2: * Fix build for CONFIG_BLK_DEV_IDEDMA=n (reported by Randy Dunlap). Cc: Randy Dunlap Acked-by: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/au1xxx-ide.c | 2 -- drivers/ide/cmd64x.c | 2 -- drivers/ide/icside.c | 3 --- drivers/ide/ide-atapi.c | 7 +++++-- drivers/ide/ide-cd.c | 1 + drivers/ide/ide-dma-sff.c | 2 -- drivers/ide/ide-dma.c | 3 +++ drivers/ide/ns87415.c | 2 -- drivers/ide/pmac.c | 2 -- drivers/ide/sc1200.c | 1 - drivers/ide/scc_pata.c | 2 -- drivers/ide/sgiioc4.c | 2 +- drivers/ide/trm290.c | 3 +-- drivers/ide/tx4939ide.c | 4 +--- include/linux/ide.h | 1 + 15 files changed, 13 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/au1xxx-ide.c b/drivers/ide/au1xxx-ide.c index 0c08c5e01f2a..ba2a211758a9 100644 --- a/drivers/ide/au1xxx-ide.c +++ b/drivers/ide/au1xxx-ide.c @@ -280,8 +280,6 @@ static int auide_build_dmatable(ide_drive_t *drive, struct ide_cmd *cmd) static int auide_dma_end(ide_drive_t *drive) { - ide_destroy_dmatable(drive); - return 0; } diff --git a/drivers/ide/cmd64x.c b/drivers/ide/cmd64x.c index f0a49d2ff711..f2edf280ef8b 100644 --- a/drivers/ide/cmd64x.c +++ b/drivers/ide/cmd64x.c @@ -327,8 +327,6 @@ static int cmd646_1_dma_end(ide_drive_t *drive) outb(dma_cmd & ~1, hwif->dma_base + ATA_DMA_CMD); /* clear the INTR & ERROR bits */ outb(dma_stat | 6, hwif->dma_base + ATA_DMA_STATUS); - /* and free any DMA resources */ - ide_destroy_dmatable(drive); /* verify good DMA status */ return (dma_stat & 7) != 4; } diff --git a/drivers/ide/icside.c b/drivers/ide/icside.c index f069f122ee6e..9bf57d7c8e57 100644 --- a/drivers/ide/icside.c +++ b/drivers/ide/icside.c @@ -291,9 +291,6 @@ static int icside_dma_end(ide_drive_t *drive) disable_dma(ec->dma); - /* Teardown mappings after DMA has completed. */ - ide_destroy_dmatable(drive); - return get_dma_residue(ec->dma) != 0; } diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index f591166d2c93..1481f71f8173 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -342,8 +342,11 @@ static ide_startstop_t ide_pc_intr(ide_drive_t *drive) stat = tp_ops->read_status(hwif); if (pc->flags & PC_FLAG_DMA_IN_PROGRESS) { - if (hwif->dma_ops->dma_end(drive) || - (drive->media == ide_tape && (stat & ATA_ERR))) { + int rc = hwif->dma_ops->dma_end(drive); + + ide_destroy_dmatable(drive); + + if (rc || (drive->media == ide_tape && (stat & ATA_ERR))) { if (drive->media == ide_floppy) printk(KERN_ERR "%s: DMA %s error\n", drive->name, rq_data_dir(pc->rq) diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 5319e7a73708..4a0d66ee9547 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -639,6 +639,7 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive) if (dma) { drive->dma = 0; dma_error = hwif->dma_ops->dma_end(drive); + ide_destroy_dmatable(drive); if (dma_error) { printk(KERN_ERR PFX "%s: DMA %s error\n", drive->name, write ? "write" : "read"); diff --git a/drivers/ide/ide-dma-sff.c b/drivers/ide/ide-dma-sff.c index 7836d7e03fff..f8adbb5eb339 100644 --- a/drivers/ide/ide-dma-sff.c +++ b/drivers/ide/ide-dma-sff.c @@ -310,8 +310,6 @@ int ide_dma_end(ide_drive_t *drive) /* clear INTR & ERROR bits */ ide_dma_sff_write_status(hwif, dma_stat | ATA_DMA_ERR | ATA_DMA_INTR); - /* purge DMA mappings */ - ide_destroy_dmatable(drive); wmb(); /* verify good DMA status */ diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index 4e2005071113..b430898bbcd6 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -92,6 +92,7 @@ ide_startstop_t ide_dma_intr(ide_drive_t *drive) u8 stat = 0, dma_stat = 0; dma_stat = hwif->dma_ops->dma_end(drive); + ide_destroy_dmatable(drive); stat = hwif->tp_ops->read_status(hwif); if (OK_STAT(stat, DRIVE_READY, drive->bad_wstat | ATA_DRQ)) { @@ -479,6 +480,7 @@ ide_startstop_t ide_dma_timeout_retry(ide_drive_t *drive, int error) if (error < 0) { printk(KERN_WARNING "%s: DMA timeout error\n", drive->name); (void)dma_ops->dma_end(drive); + ide_destroy_dmatable(drive); ret = ide_error(drive, "dma timeout error", hwif->tp_ops->read_status(hwif)); } else { @@ -490,6 +492,7 @@ ide_startstop_t ide_dma_timeout_retry(ide_drive_t *drive, int error) ide_dump_status(drive, "DMA timeout", hwif->tp_ops->read_status(hwif)); (void)dma_ops->dma_end(drive); + ide_destroy_dmatable(drive); } } diff --git a/drivers/ide/ns87415.c b/drivers/ide/ns87415.c index 9039a373020f..9ad71a74f93f 100644 --- a/drivers/ide/ns87415.c +++ b/drivers/ide/ns87415.c @@ -210,8 +210,6 @@ static int ns87415_dma_end(ide_drive_t *drive) /* from ERRATA: clear the INTR & ERROR bits */ dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD); outb(dma_cmd | 6, hwif->dma_base + ATA_DMA_CMD); - /* and free any DMA resources */ - ide_destroy_dmatable(drive); /* verify good DMA status */ return (dma_stat & 7) != 4; } diff --git a/drivers/ide/pmac.c b/drivers/ide/pmac.c index d15cc46a66e3..5643a8b957bf 100644 --- a/drivers/ide/pmac.c +++ b/drivers/ide/pmac.c @@ -1562,8 +1562,6 @@ pmac_ide_dma_end (ide_drive_t *drive) dstat = readl(&dma->status); writel(((RUN|WAKE|DEAD) << 16), &dma->control); - ide_destroy_dmatable(drive); - /* verify good dma status. we don't check for ACTIVE beeing 0. We should... * in theory, but with ATAPI decices doing buffer underruns, that would * cause us to disable DMA, which isn't what we want diff --git a/drivers/ide/sc1200.c b/drivers/ide/sc1200.c index 371549d18a01..d9c47034bedd 100644 --- a/drivers/ide/sc1200.c +++ b/drivers/ide/sc1200.c @@ -184,7 +184,6 @@ static int sc1200_dma_end(ide_drive_t *drive) outb(inb(dma_base)&~1, dma_base); /* !! DO THIS HERE !! stop DMA */ drive->waiting_for_dma = 0; - ide_destroy_dmatable(drive); /* purge DMA mappings */ return (dma_stat & 7) != 4; /* verify good DMA status */ } diff --git a/drivers/ide/scc_pata.c b/drivers/ide/scc_pata.c index 64534d150b0c..693536ebe331 100644 --- a/drivers/ide/scc_pata.c +++ b/drivers/ide/scc_pata.c @@ -365,8 +365,6 @@ static int __scc_dma_end(ide_drive_t *drive) dma_stat = scc_dma_sff_read_status(hwif); /* clear the INTR & ERROR bits */ scc_ide_outb(dma_stat | 6, hwif->dma_base + 4); - /* purge DMA mappings */ - ide_destroy_dmatable(drive); /* verify good DMA status */ wmb(); return (dma_stat & 7) != 4 ? (0x10 | dma_stat) : 0; diff --git a/drivers/ide/sgiioc4.c b/drivers/ide/sgiioc4.c index 44df0c750bab..457a762a1f29 100644 --- a/drivers/ide/sgiioc4.c +++ b/drivers/ide/sgiioc4.c @@ -259,7 +259,6 @@ static int sgiioc4_dma_end(ide_drive_t *drive) } drive->waiting_for_dma = 0; - ide_destroy_dmatable(drive); return dma_stat; } @@ -284,6 +283,7 @@ static void sgiioc4_resetproc(ide_drive_t * drive) { sgiioc4_dma_end(drive); + ide_destroy_dmatable(drive); sgiioc4_clearirq(drive); } diff --git a/drivers/ide/trm290.c b/drivers/ide/trm290.c index d6a950828e9f..8dd3d8226870 100644 --- a/drivers/ide/trm290.c +++ b/drivers/ide/trm290.c @@ -216,8 +216,7 @@ static int trm290_dma_end(ide_drive_t *drive) u16 status; drive->waiting_for_dma = 0; - /* purge DMA mappings */ - ide_destroy_dmatable(drive); + status = inw(drive->hwif->dma_base + 2); return status != 0x00ff; diff --git a/drivers/ide/tx4939ide.c b/drivers/ide/tx4939ide.c index 53f99853b065..f62ced855cf3 100644 --- a/drivers/ide/tx4939ide.c +++ b/drivers/ide/tx4939ide.c @@ -335,11 +335,9 @@ static int tx4939ide_dma_end(ide_drive_t *drive) /* read and clear the INTR & ERROR bits */ dma_stat = tx4939ide_clear_dma_status(base); - /* purge DMA mappings */ - ide_destroy_dmatable(drive); - /* verify good DMA status */ wmb(); + /* verify good DMA status */ if ((dma_stat & (ATA_DMA_INTR | ATA_DMA_ERR | ATA_DMA_ACTIVE)) == 0 && (ctl & (TX4939IDE_INT_XFEREND | TX4939IDE_INT_HOST)) == (TX4939IDE_INT_XFEREND | TX4939IDE_INT_HOST)) diff --git a/include/linux/ide.h b/include/linux/ide.h index d3035f2f1250..b6c4942fde11 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1479,6 +1479,7 @@ static inline ide_startstop_t ide_dma_timeout_retry(ide_drive_t *drive, int erro static inline void ide_release_dma_engine(ide_hwif_t *hwif) { ; } static inline int ide_build_sglist(ide_drive_t *drive, struct ide_cmd *cmd) { return 0; } +static inline void ide_destroy_dmatable(ide_drive_t *drive) { ; } #endif /* CONFIG_BLK_DEV_IDEDMA */ #ifdef CONFIG_BLK_DEV_IDEACPI -- cgit v1.2.3 From 5ae5412d9a23b05ab08461b202bad21ad8f6b66d Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 31 Mar 2009 20:15:20 +0200 Subject: ide: add ide_dma_prepare() helper * Add ide_dma_prepare() helper. * Convert ide_issue_pc() and do_rw_taskfile() to use it. * Make ide_build_sglist() static. There should be no functional changes caused by this patch. Acked-by: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-atapi.c | 18 ++++-------------- drivers/ide/ide-dma.c | 11 ++++++++++- drivers/ide/ide-taskfile.c | 4 +--- include/linux/ide.h | 7 ++++--- 4 files changed, 19 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index 1481f71f8173..89d2339bdef3 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -638,7 +638,6 @@ ide_startstop_t ide_issue_pc(ide_drive_t *drive, struct ide_cmd *cmd) { struct ide_atapi_pc *pc; ide_hwif_t *hwif = drive->hwif; - const struct ide_dma_ops *dma_ops = hwif->dma_ops; ide_expiry_t *expiry = NULL; struct request *rq = hwif->rq; unsigned int timeout; @@ -652,12 +651,8 @@ ide_startstop_t ide_issue_pc(ide_drive_t *drive, struct ide_cmd *cmd) expiry = ide_cd_expiry; timeout = ATAPI_WAIT_PC; - if (drive->dma) { - if (ide_build_sglist(drive, cmd)) - drive->dma = !dma_ops->dma_setup(drive, cmd); - else - drive->dma = 0; - } + if (drive->dma) + drive->dma = !ide_dma_prepare(drive, cmd); } else { pc = drive->pc; @@ -675,13 +670,8 @@ ide_startstop_t ide_issue_pc(ide_drive_t *drive, struct ide_cmd *cmd) ide_dma_off(drive); } - if ((pc->flags & PC_FLAG_DMA_OK) && - (drive->dev_flags & IDE_DFLAG_USING_DMA)) { - if (ide_build_sglist(drive, cmd)) - drive->dma = !dma_ops->dma_setup(drive, cmd); - else - drive->dma = 0; - } + if (pc->flags & PC_FLAG_DMA_OK) + drive->dma = !ide_dma_prepare(drive, cmd); if (!drive->dma) pc->flags &= ~PC_FLAG_DMA_OK; diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index b430898bbcd6..cf5897f5533f 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -128,7 +128,7 @@ int ide_dma_good_drive(ide_drive_t *drive) * operate in a portable fashion. */ -int ide_build_sglist(ide_drive_t *drive, struct ide_cmd *cmd) +static int ide_build_sglist(ide_drive_t *drive, struct ide_cmd *cmd) { ide_hwif_t *hwif = drive->hwif; struct scatterlist *sg = hwif->sg_table; @@ -563,3 +563,12 @@ int ide_allocate_dma_engine(ide_hwif_t *hwif) return 0; } EXPORT_SYMBOL_GPL(ide_allocate_dma_engine); + +int ide_dma_prepare(ide_drive_t *drive, struct ide_cmd *cmd) +{ + if ((drive->dev_flags & IDE_DFLAG_USING_DMA) == 0 || + ide_build_sglist(drive, cmd) == 0 || + drive->hwif->dma_ops->dma_setup(drive, cmd)) + return 1; + return 0; +} diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c index a3b7a50562b2..dba68db629bf 100644 --- a/drivers/ide/ide-taskfile.c +++ b/drivers/ide/ide-taskfile.c @@ -100,9 +100,7 @@ ide_startstop_t do_rw_taskfile(ide_drive_t *drive, struct ide_cmd *orig_cmd) ide_execute_command(drive, cmd, handler, WAIT_WORSTCASE); return ide_started; case ATA_PROT_DMA: - if ((drive->dev_flags & IDE_DFLAG_USING_DMA) == 0 || - ide_build_sglist(drive, cmd) == 0 || - dma_ops->dma_setup(drive, cmd)) + if (ide_dma_prepare(drive, cmd)) return ide_stopped; hwif->expiry = dma_ops->dma_timer_expiry; ide_execute_command(drive, cmd, ide_dma_intr, 2 * WAIT_CMD); diff --git a/include/linux/ide.h b/include/linux/ide.h index b6c4942fde11..78892e2a432c 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1443,7 +1443,8 @@ ide_startstop_t ide_dma_intr(ide_drive_t *); int ide_allocate_dma_engine(ide_hwif_t *); void ide_release_dma_engine(ide_hwif_t *); -int ide_build_sglist(ide_drive_t *, struct ide_cmd *); +int ide_dma_prepare(ide_drive_t *, struct ide_cmd *); + void ide_destroy_dmatable(ide_drive_t *); #ifdef CONFIG_BLK_DEV_IDEDMA_SFF @@ -1477,8 +1478,8 @@ static inline void ide_check_dma_crc(ide_drive_t *drive) { ; } static inline ide_startstop_t ide_dma_intr(ide_drive_t *drive) { return ide_stopped; } static inline ide_startstop_t ide_dma_timeout_retry(ide_drive_t *drive, int error) { return ide_stopped; } static inline void ide_release_dma_engine(ide_hwif_t *hwif) { ; } -static inline int ide_build_sglist(ide_drive_t *drive, - struct ide_cmd *cmd) { return 0; } +static inline int ide_dma_prepare(ide_drive_t *drive, + struct ide_cmd *cmd) { return 1; } static inline void ide_destroy_dmatable(ide_drive_t *drive) { ; } #endif /* CONFIG_BLK_DEV_IDEDMA */ -- cgit v1.2.3 From a6d67ffa7dfe9515d8f2051a76b14c82b748475a Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 31 Mar 2009 20:15:20 +0200 Subject: ns87415: use custom ->dma_{start,end} to handle ns87415_prepare_drive() Use custom ->dma_{start,end} methods to handle ns87415_prepare_drive() there instead of in ->dma_setup method. Acked-by: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ns87415.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/ns87415.c b/drivers/ide/ns87415.c index 9ad71a74f93f..6e0f372d2f09 100644 --- a/drivers/ide/ns87415.c +++ b/drivers/ide/ns87415.c @@ -196,6 +196,12 @@ static void ns87415_selectproc (ide_drive_t *drive) !!(drive->dev_flags & IDE_DFLAG_USING_DMA)); } +static void ns87415_dma_start(ide_drive_t *drive) +{ + ns87415_prepare_drive(drive, 1); + ide_dma_start(drive); +} + static int ns87415_dma_end(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; @@ -210,19 +216,11 @@ static int ns87415_dma_end(ide_drive_t *drive) /* from ERRATA: clear the INTR & ERROR bits */ dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD); outb(dma_cmd | 6, hwif->dma_base + ATA_DMA_CMD); - /* verify good DMA status */ - return (dma_stat & 7) != 4; -} -static int ns87415_dma_setup(ide_drive_t *drive, struct ide_cmd *cmd) -{ - /* select DMA xfer */ - ns87415_prepare_drive(drive, 1); - if (ide_dma_setup(drive, cmd) == 0) - return 0; - /* DMA failed: select PIO xfer */ ns87415_prepare_drive(drive, 0); - return 1; + + /* verify good DMA status */ + return (dma_stat & 7) != 4; } static void __devinit init_hwif_ns87415 (ide_hwif_t *hwif) @@ -298,8 +296,8 @@ static const struct ide_port_ops ns87415_port_ops = { static const struct ide_dma_ops ns87415_dma_ops = { .dma_host_set = ide_dma_host_set, - .dma_setup = ns87415_dma_setup, - .dma_start = ide_dma_start, + .dma_setup = ide_dma_setup, + .dma_start = ns87415_dma_start, .dma_end = ns87415_dma_end, .dma_test_irq = ide_dma_test_irq, .dma_lost_irq = ide_dma_lost_irq, -- cgit v1.2.3 From 7526efaafdc835b8d6b22aa1a302e14651373908 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 31 Mar 2009 20:15:21 +0200 Subject: trm290: use custom ->dma_{start,end} to handle trm290_prepare_drive() Use custom ->dma_{start,end} methods to handle trm290_prepare_drive() there instead of in ->dma_setup method. There should be no functional changes caused by this patch (DMA support is disabled currently in trm290.c). Acked-by: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/trm290.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/trm290.c b/drivers/ide/trm290.c index 8dd3d8226870..b91bb709af40 100644 --- a/drivers/ide/trm290.c +++ b/drivers/ide/trm290.c @@ -184,7 +184,6 @@ static int trm290_dma_setup(ide_drive_t *drive, struct ide_cmd *cmd) if (cmd->tf_flags & IDE_TFLAG_WRITE) { #ifdef TRM290_NO_DMA_WRITES /* always use PIO for writes */ - trm290_prepare_drive(drive, 0); /* select PIO xfer */ return 1; #endif rw = 1; @@ -195,11 +194,8 @@ static int trm290_dma_setup(ide_drive_t *drive, struct ide_cmd *cmd) if (count == 0) { ide_map_sg(drive, cmd); /* try PIO instead of DMA */ - trm290_prepare_drive(drive, 0); /* select PIO xfer */ return 1; } - /* select DMA xfer */ - trm290_prepare_drive(drive, 1); outl(hwif->dmatable_dma | rw, hwif->dma_base); drive->waiting_for_dma = 1; /* start DMA */ @@ -209,6 +205,7 @@ static int trm290_dma_setup(ide_drive_t *drive, struct ide_cmd *cmd) static void trm290_dma_start(ide_drive_t *drive) { + trm290_prepare_drive(drive, 1); } static int trm290_dma_end(ide_drive_t *drive) @@ -219,6 +216,8 @@ static int trm290_dma_end(ide_drive_t *drive) status = inw(drive->hwif->dma_base + 2); + trm290_prepare_drive(drive, 0); + return status != 0x00ff; } -- cgit v1.2.3 From 8a4a5738ba499083cf4c5668895efe220b1946d3 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 31 Mar 2009 20:15:21 +0200 Subject: ide: add ->dma_check method * Add (an optional) ->dma_check method for checking if DMA can be used for a given command and fail DMA setup in ide_dma_prepare() if necessary. * Convert alim15x3 and trm290 host drivers to use ->dma_check. * Rename ali15x3_dma_setup() to ali_dma_check() while at it. There should be no functional changes caused by this patch. Acked-by: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/alim15x3.c | 9 +++++---- drivers/ide/ide-dma.c | 5 ++++- drivers/ide/trm290.c | 17 ++++++++++------- include/linux/ide.h | 1 + 4 files changed, 20 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/alim15x3.c b/drivers/ide/alim15x3.c index d3faf0b97f42..537da1cde16d 100644 --- a/drivers/ide/alim15x3.c +++ b/drivers/ide/alim15x3.c @@ -189,20 +189,20 @@ static void ali_set_dma_mode(ide_drive_t *drive, const u8 speed) } /** - * ali15x3_dma_setup - begin a DMA phase + * ali_dma_check - DMA check * @drive: target device * @cmd: command * * Returns 1 if the DMA cannot be performed, zero on success. */ -static int ali15x3_dma_setup(ide_drive_t *drive, struct ide_cmd *cmd) +static int ali_dma_check(ide_drive_t *drive, struct ide_cmd *cmd) { if (m5229_revision < 0xC2 && drive->media != ide_disk) { if (cmd->tf_flags & IDE_TFLAG_WRITE) return 1; /* try PIO instead of DMA */ } - return ide_dma_setup(drive, cmd); + return 0; } /** @@ -503,11 +503,12 @@ static const struct ide_port_ops ali_port_ops = { static const struct ide_dma_ops ali_dma_ops = { .dma_host_set = ide_dma_host_set, - .dma_setup = ali15x3_dma_setup, + .dma_setup = ide_dma_setup, .dma_start = ide_dma_start, .dma_end = ide_dma_end, .dma_test_irq = ide_dma_test_irq, .dma_lost_irq = ide_dma_lost_irq, + .dma_check = ali_dma_check, .dma_timer_expiry = ide_dma_sff_timer_expiry, .dma_sff_read_status = ide_dma_sff_read_status, }; diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index cf5897f5533f..c0505e2dfc2e 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -566,9 +566,12 @@ EXPORT_SYMBOL_GPL(ide_allocate_dma_engine); int ide_dma_prepare(ide_drive_t *drive, struct ide_cmd *cmd) { + const struct ide_dma_ops *dma_ops = drive->hwif->dma_ops; + if ((drive->dev_flags & IDE_DFLAG_USING_DMA) == 0 || + (dma_ops->dma_check && dma_ops->dma_check(drive, cmd)) || ide_build_sglist(drive, cmd) == 0 || - drive->hwif->dma_ops->dma_setup(drive, cmd)) + dma_ops->dma_setup(drive, cmd)) return 1; return 0; } diff --git a/drivers/ide/trm290.c b/drivers/ide/trm290.c index b91bb709af40..1076efd050dc 100644 --- a/drivers/ide/trm290.c +++ b/drivers/ide/trm290.c @@ -176,19 +176,21 @@ static void trm290_selectproc (ide_drive_t *drive) trm290_prepare_drive(drive, !!(drive->dev_flags & IDE_DFLAG_USING_DMA)); } -static int trm290_dma_setup(ide_drive_t *drive, struct ide_cmd *cmd) +static int trm290_dma_check(ide_drive_t *drive, struct ide_cmd *cmd) { - ide_hwif_t *hwif = drive->hwif; - unsigned int count, rw; - if (cmd->tf_flags & IDE_TFLAG_WRITE) { #ifdef TRM290_NO_DMA_WRITES /* always use PIO for writes */ return 1; #endif - rw = 1; - } else - rw = 2; + } + return 0; +} + +static int trm290_dma_setup(ide_drive_t *drive, struct ide_cmd *cmd) +{ + ide_hwif_t *hwif = drive->hwif; + unsigned int count, rw = (cmd->tf_flags & IDE_TFLAG_WRITE) ? 1 : 2; count = ide_build_dmatable(drive, cmd); if (count == 0) { @@ -312,6 +314,7 @@ static struct ide_dma_ops trm290_dma_ops = { .dma_end = trm290_dma_end, .dma_test_irq = trm290_dma_test_irq, .dma_lost_irq = ide_dma_lost_irq, + .dma_check = trm290_dma_check, }; static const struct ide_port_info trm290_chipset __devinitdata = { diff --git a/include/linux/ide.h b/include/linux/ide.h index 78892e2a432c..b350667b83ad 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -717,6 +717,7 @@ struct ide_dma_ops { int (*dma_test_irq)(struct ide_drive_s *); void (*dma_lost_irq)(struct ide_drive_s *); /* below ones are optional */ + int (*dma_check)(struct ide_drive_s *, struct ide_cmd *); int (*dma_timer_expiry)(struct ide_drive_s *); void (*dma_clear)(struct ide_drive_s *); /* -- cgit v1.2.3 From 11998b316173f814698f9037ce9179d634d1f423 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 31 Mar 2009 20:15:21 +0200 Subject: ide: move ide_map_sg() call out of ->dma_setup method (take 2) Move ide_map_sg() call from ->dma_setup implementations and ide_destroy_dmatable() one from *_build_dmatable() to ide_dma_prepare(). There should be no functional changes caused by this patch. Sergei: Removed 'use_pio_instead' labels and replaced 'goto' with 'return 0' -- that required no changes to the follow-up patches... Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/au1xxx-ide.c | 9 ++------- drivers/ide/ide-dma-sff.c | 2 -- drivers/ide/ide-dma.c | 8 ++++++-- drivers/ide/pmac.c | 11 +++-------- drivers/ide/scc_pata.c | 4 +--- drivers/ide/sgiioc4.c | 9 ++------- drivers/ide/trm290.c | 5 ++--- drivers/ide/tx4939ide.c | 6 +----- 8 files changed, 17 insertions(+), 37 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/au1xxx-ide.c b/drivers/ide/au1xxx-ide.c index ba2a211758a9..239643806b97 100644 --- a/drivers/ide/au1xxx-ide.c +++ b/drivers/ide/au1xxx-ide.c @@ -236,7 +236,7 @@ static int auide_build_dmatable(ide_drive_t *drive, struct ide_cmd *cmd) if (++count >= PRD_ENTRIES) { printk(KERN_WARNING "%s: DMA table too small\n", drive->name); - goto use_pio_instead; + return 0; } /* Lets enable intr for the last descriptor only */ @@ -272,9 +272,6 @@ static int auide_build_dmatable(ide_drive_t *drive, struct ide_cmd *cmd) if (count) return 1; - use_pio_instead: - ide_destroy_dmatable(drive); - return 0; /* revert to PIO for this request */ } @@ -290,10 +287,8 @@ static void auide_dma_start(ide_drive_t *drive ) static int auide_dma_setup(ide_drive_t *drive, struct ide_cmd *cmd) { - if (auide_build_dmatable(drive, cmd) == 0) { - ide_map_sg(drive, cmd); + if (auide_build_dmatable(drive, cmd) == 0) return 1; - } drive->waiting_for_dma = 1; return 0; diff --git a/drivers/ide/ide-dma-sff.c b/drivers/ide/ide-dma-sff.c index f8adbb5eb339..e10054b827a0 100644 --- a/drivers/ide/ide-dma-sff.c +++ b/drivers/ide/ide-dma-sff.c @@ -166,8 +166,6 @@ use_pio_instead: printk(KERN_ERR "%s: %s\n", drive->name, count ? "DMA table too small" : "empty DMA table?"); - ide_destroy_dmatable(drive); - return 0; /* revert to PIO for this request */ } EXPORT_SYMBOL_GPL(ide_build_dmatable); diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index c0505e2dfc2e..d61f9a8cc18a 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -570,8 +570,12 @@ int ide_dma_prepare(ide_drive_t *drive, struct ide_cmd *cmd) if ((drive->dev_flags & IDE_DFLAG_USING_DMA) == 0 || (dma_ops->dma_check && dma_ops->dma_check(drive, cmd)) || - ide_build_sglist(drive, cmd) == 0 || - dma_ops->dma_setup(drive, cmd)) + ide_build_sglist(drive, cmd) == 0) return 1; + if (dma_ops->dma_setup(drive, cmd)) { + ide_destroy_dmatable(drive); + ide_map_sg(drive, cmd); + return 1; + } return 0; } diff --git a/drivers/ide/pmac.c b/drivers/ide/pmac.c index 5643a8b957bf..1df7b7636453 100644 --- a/drivers/ide/pmac.c +++ b/drivers/ide/pmac.c @@ -1455,7 +1455,7 @@ static int pmac_ide_build_dmatable(ide_drive_t *drive, struct ide_cmd *cmd) "switching to PIO on Ohare chipset\n", drive->name); pmif->broken_dma_warn = 1; } - goto use_pio_instead; + return 0; } while (cur_len) { unsigned int tc = (cur_len < 0xfe00)? cur_len: 0xfe00; @@ -1463,7 +1463,7 @@ static int pmac_ide_build_dmatable(ide_drive_t *drive, struct ide_cmd *cmd) if (count++ >= MAX_DCMDS) { printk(KERN_WARNING "%s: DMA table too small\n", drive->name); - goto use_pio_instead; + return 0; } st_le16(&table->command, wr? OUTPUT_MORE: INPUT_MORE); st_le16(&table->req_count, tc); @@ -1492,9 +1492,6 @@ static int pmac_ide_build_dmatable(ide_drive_t *drive, struct ide_cmd *cmd) printk(KERN_DEBUG "%s: empty DMA table?\n", drive->name); -use_pio_instead: - ide_destroy_dmatable(drive); - return 0; /* revert to PIO for this request */ } @@ -1510,10 +1507,8 @@ static int pmac_ide_dma_setup(ide_drive_t *drive, struct ide_cmd *cmd) u8 unit = drive->dn & 1, ata4 = (pmif->kind == controller_kl_ata4); u8 write = !!(cmd->tf_flags & IDE_TFLAG_WRITE); - if (pmac_ide_build_dmatable(drive, cmd) == 0) { - ide_map_sg(drive, cmd); + if (pmac_ide_build_dmatable(drive, cmd) == 0) return 1; - } /* Apple adds 60ns to wrDataSetup on reads */ if (ata4 && (pmif->timings[unit] & TR_66_UDMA_EN)) { diff --git a/drivers/ide/scc_pata.c b/drivers/ide/scc_pata.c index 693536ebe331..2cdf51d8917a 100644 --- a/drivers/ide/scc_pata.c +++ b/drivers/ide/scc_pata.c @@ -321,10 +321,8 @@ static int scc_dma_setup(ide_drive_t *drive, struct ide_cmd *cmd) u8 dma_stat; /* fall back to pio! */ - if (ide_build_dmatable(drive, cmd) == 0) { - ide_map_sg(drive, cmd); + if (ide_build_dmatable(drive, cmd) == 0) return 1; - } /* PRD table */ out_be32((void __iomem *)(hwif->dma_base + 8), hwif->dmatable_dma); diff --git a/drivers/ide/sgiioc4.c b/drivers/ide/sgiioc4.c index 457a762a1f29..d90e8c5af0a2 100644 --- a/drivers/ide/sgiioc4.c +++ b/drivers/ide/sgiioc4.c @@ -442,7 +442,7 @@ static int sgiioc4_build_dmatable(ide_drive_t *drive, struct ide_cmd *cmd) printk(KERN_WARNING "%s: DMA table too small\n", drive->name); - goto use_pio_instead; + return 0; } else { u32 bcount = 0x10000 - (cur_addr & 0xffff); @@ -477,9 +477,6 @@ static int sgiioc4_build_dmatable(ide_drive_t *drive, struct ide_cmd *cmd) return count; } -use_pio_instead: - ide_destroy_dmatable(drive); - return 0; /* revert to PIO for this request */ } @@ -488,11 +485,9 @@ static int sgiioc4_dma_setup(ide_drive_t *drive, struct ide_cmd *cmd) int ddir; u8 write = !!(cmd->tf_flags & IDE_TFLAG_WRITE); - if (sgiioc4_build_dmatable(drive, cmd) == 0) { + if (sgiioc4_build_dmatable(drive, cmd) == 0) /* try PIO instead of DMA */ - ide_map_sg(drive, cmd); return 1; - } if (write) /* Writes TO the IOC4 FROM Main Memory */ diff --git a/drivers/ide/trm290.c b/drivers/ide/trm290.c index 1076efd050dc..0be27cae27d5 100644 --- a/drivers/ide/trm290.c +++ b/drivers/ide/trm290.c @@ -193,11 +193,10 @@ static int trm290_dma_setup(ide_drive_t *drive, struct ide_cmd *cmd) unsigned int count, rw = (cmd->tf_flags & IDE_TFLAG_WRITE) ? 1 : 2; count = ide_build_dmatable(drive, cmd); - if (count == 0) { - ide_map_sg(drive, cmd); + if (count == 0) /* try PIO instead of DMA */ return 1; - } + outl(hwif->dmatable_dma | rw, hwif->dma_base); drive->waiting_for_dma = 1; /* start DMA */ diff --git a/drivers/ide/tx4939ide.c b/drivers/ide/tx4939ide.c index f62ced855cf3..7267c46c07cd 100644 --- a/drivers/ide/tx4939ide.c +++ b/drivers/ide/tx4939ide.c @@ -279,8 +279,6 @@ use_pio_instead: printk(KERN_ERR "%s: %s\n", drive->name, count ? "DMA table too small" : "empty DMA table?"); - ide_destroy_dmatable(drive); - return 0; /* revert to PIO for this request */ } #else @@ -294,10 +292,8 @@ static int tx4939ide_dma_setup(ide_drive_t *drive, struct ide_cmd *cmd) u8 rw = (cmd->tf_flags & IDE_TFLAG_WRITE) ? 0 : ATA_DMA_WR; /* fall back to PIO! */ - if (tx4939ide_build_dmatable(drive, cmd) == 0) { - ide_map_sg(drive, cmd); + if (tx4939ide_build_dmatable(drive, cmd) == 0) return 1; - } /* PRD table */ tx4939ide_writel(hwif->dmatable_dma, base, TX4939IDE_PRD_Ptr); -- cgit v1.2.3 From 88b4132e101e60e8fa67996ae3072ab6b71e8500 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 31 Mar 2009 20:15:22 +0200 Subject: ide: set/clear drive->waiting_for_dma flag in the core code Set/clear drive->waiting_for_dma flag in the core code instead of in ->dma_setup and ->dma_end methods. There should be no functional changes caused by this patch. Acked-by: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/au1xxx-ide.c | 6 ------ drivers/ide/cmd64x.c | 1 - drivers/ide/icside.c | 4 ---- drivers/ide/ide-atapi.c | 4 +++- drivers/ide/ide-cd.c | 1 + drivers/ide/ide-dma-sff.c | 3 --- drivers/ide/ide-dma.c | 4 ++++ drivers/ide/ns87415.c | 1 - drivers/ide/pmac.c | 3 --- drivers/ide/sc1200.c | 2 -- drivers/ide/scc_pata.c | 3 +-- drivers/ide/sgiioc4.c | 3 --- drivers/ide/trm290.c | 8 ++------ drivers/ide/tx4939ide.c | 4 ---- 14 files changed, 11 insertions(+), 36 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/au1xxx-ide.c b/drivers/ide/au1xxx-ide.c index 239643806b97..549bbb2755a8 100644 --- a/drivers/ide/au1xxx-ide.c +++ b/drivers/ide/au1xxx-ide.c @@ -290,7 +290,6 @@ static int auide_dma_setup(ide_drive_t *drive, struct ide_cmd *cmd) if (auide_build_dmatable(drive, cmd) == 0) return 1; - drive->waiting_for_dma = 1; return 0; } @@ -315,16 +314,11 @@ static void auide_dma_host_set(ide_drive_t *drive, int on) static void auide_ddma_tx_callback(int irq, void *param) { - _auide_hwif *ahwif = (_auide_hwif*)param; - ahwif->drive->waiting_for_dma = 0; } static void auide_ddma_rx_callback(int irq, void *param) { - _auide_hwif *ahwif = (_auide_hwif*)param; - ahwif->drive->waiting_for_dma = 0; } - #endif /* end CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA */ static void auide_init_dbdma_dev(dbdev_tab_t *dev, u32 dev_id, u32 tsize, u32 devwidth, u32 flags) diff --git a/drivers/ide/cmd64x.c b/drivers/ide/cmd64x.c index f2edf280ef8b..80b777e4247b 100644 --- a/drivers/ide/cmd64x.c +++ b/drivers/ide/cmd64x.c @@ -318,7 +318,6 @@ static int cmd646_1_dma_end(ide_drive_t *drive) ide_hwif_t *hwif = drive->hwif; u8 dma_stat = 0, dma_cmd = 0; - drive->waiting_for_dma = 0; /* get DMA status */ dma_stat = inb(hwif->dma_base + ATA_DMA_STATUS); /* read DMA command state */ diff --git a/drivers/ide/icside.c b/drivers/ide/icside.c index 9bf57d7c8e57..4e16ce68b063 100644 --- a/drivers/ide/icside.c +++ b/drivers/ide/icside.c @@ -287,8 +287,6 @@ static int icside_dma_end(ide_drive_t *drive) ide_hwif_t *hwif = drive->hwif; struct expansion_card *ec = ECARD_DEV(hwif->dev); - drive->waiting_for_dma = 0; - disable_dma(ec->dma); return get_dma_residue(ec->dma) != 0; @@ -343,8 +341,6 @@ static int icside_dma_setup(ide_drive_t *drive, struct ide_cmd *cmd) set_dma_sg(ec->dma, hwif->sg_table, cmd->sg_nents); set_dma_mode(ec->dma, dma_mode); - drive->waiting_for_dma = 1; - return 0; } diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index 89d2339bdef3..db6e617790bd 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -342,8 +342,10 @@ static ide_startstop_t ide_pc_intr(ide_drive_t *drive) stat = tp_ops->read_status(hwif); if (pc->flags & PC_FLAG_DMA_IN_PROGRESS) { - int rc = hwif->dma_ops->dma_end(drive); + int rc; + drive->waiting_for_dma = 0; + rc = hwif->dma_ops->dma_end(drive); ide_destroy_dmatable(drive); if (rc || (drive->media == ide_tape && (stat & ATA_ERR))) { diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 4a0d66ee9547..f5c7bb739f45 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -638,6 +638,7 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive) dma = drive->dma; if (dma) { drive->dma = 0; + drive->waiting_for_dma = 0; dma_error = hwif->dma_ops->dma_end(drive); ide_destroy_dmatable(drive); if (dma_error) { diff --git a/drivers/ide/ide-dma-sff.c b/drivers/ide/ide-dma-sff.c index e10054b827a0..f8c7d0cd2ff8 100644 --- a/drivers/ide/ide-dma-sff.c +++ b/drivers/ide/ide-dma-sff.c @@ -216,7 +216,6 @@ int ide_dma_setup(ide_drive_t *drive, struct ide_cmd *cmd) /* clear INTR & ERROR flags */ ide_dma_sff_write_status(hwif, dma_stat | ATA_DMA_ERR | ATA_DMA_INTR); - drive->waiting_for_dma = 1; return 0; } EXPORT_SYMBOL_GPL(ide_dma_setup); @@ -290,8 +289,6 @@ int ide_dma_end(ide_drive_t *drive) ide_hwif_t *hwif = drive->hwif; u8 dma_stat = 0, dma_cmd = 0, mask; - drive->waiting_for_dma = 0; - /* stop DMA */ if (hwif->host_flags & IDE_HFLAG_MMIO) { dma_cmd = readb((void __iomem *)(hwif->dma_base + ATA_DMA_CMD)); diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index d61f9a8cc18a..4d3102887d9c 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -91,6 +91,7 @@ ide_startstop_t ide_dma_intr(ide_drive_t *drive) ide_hwif_t *hwif = drive->hwif; u8 stat = 0, dma_stat = 0; + drive->waiting_for_dma = 0; dma_stat = hwif->dma_ops->dma_end(drive); ide_destroy_dmatable(drive); stat = hwif->tp_ops->read_status(hwif); @@ -479,6 +480,7 @@ ide_startstop_t ide_dma_timeout_retry(ide_drive_t *drive, int error) if (error < 0) { printk(KERN_WARNING "%s: DMA timeout error\n", drive->name); + drive->waiting_for_dma = 0; (void)dma_ops->dma_end(drive); ide_destroy_dmatable(drive); ret = ide_error(drive, "dma timeout error", @@ -491,6 +493,7 @@ ide_startstop_t ide_dma_timeout_retry(ide_drive_t *drive, int error) if (dma_ops->dma_test_irq(drive) == 0) { ide_dump_status(drive, "DMA timeout", hwif->tp_ops->read_status(hwif)); + drive->waiting_for_dma = 0; (void)dma_ops->dma_end(drive); ide_destroy_dmatable(drive); } @@ -577,5 +580,6 @@ int ide_dma_prepare(ide_drive_t *drive, struct ide_cmd *cmd) ide_map_sg(drive, cmd); return 1; } + drive->waiting_for_dma = 1; return 0; } diff --git a/drivers/ide/ns87415.c b/drivers/ide/ns87415.c index 6e0f372d2f09..cbc1d1110385 100644 --- a/drivers/ide/ns87415.c +++ b/drivers/ide/ns87415.c @@ -207,7 +207,6 @@ static int ns87415_dma_end(ide_drive_t *drive) ide_hwif_t *hwif = drive->hwif; u8 dma_stat = 0, dma_cmd = 0; - drive->waiting_for_dma = 0; dma_stat = hwif->dma_ops->dma_sff_read_status(hwif); /* get DMA command mode */ dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD); diff --git a/drivers/ide/pmac.c b/drivers/ide/pmac.c index 1df7b7636453..879c3d8d9f36 100644 --- a/drivers/ide/pmac.c +++ b/drivers/ide/pmac.c @@ -1517,8 +1517,6 @@ static int pmac_ide_dma_setup(ide_drive_t *drive, struct ide_cmd *cmd) (void)readl(PMAC_IDE_REG(IDE_TIMING_CONFIG)); } - drive->waiting_for_dma = 1; - return 0; } @@ -1553,7 +1551,6 @@ pmac_ide_dma_end (ide_drive_t *drive) volatile struct dbdma_regs __iomem *dma = pmif->dma_regs; u32 dstat; - drive->waiting_for_dma = 0; dstat = readl(&dma->status); writel(((RUN|WAKE|DEAD) << 16), &dma->control); diff --git a/drivers/ide/sc1200.c b/drivers/ide/sc1200.c index d9c47034bedd..13e3988f00f0 100644 --- a/drivers/ide/sc1200.c +++ b/drivers/ide/sc1200.c @@ -183,8 +183,6 @@ static int sc1200_dma_end(ide_drive_t *drive) outb(dma_stat|0x1b, dma_base+2); /* clear the INTR & ERROR bits */ outb(inb(dma_base)&~1, dma_base); /* !! DO THIS HERE !! stop DMA */ - drive->waiting_for_dma = 0; - return (dma_stat & 7) != 4; /* verify good DMA status */ } diff --git a/drivers/ide/scc_pata.c b/drivers/ide/scc_pata.c index 2cdf51d8917a..542419ffa9b7 100644 --- a/drivers/ide/scc_pata.c +++ b/drivers/ide/scc_pata.c @@ -335,7 +335,7 @@ static int scc_dma_setup(ide_drive_t *drive, struct ide_cmd *cmd) /* clear INTR & ERROR flags */ out_be32((void __iomem *)(hwif->dma_base + 4), dma_stat | 6); - drive->waiting_for_dma = 1; + return 0; } @@ -354,7 +354,6 @@ static int __scc_dma_end(ide_drive_t *drive) ide_hwif_t *hwif = drive->hwif; u8 dma_stat, dma_cmd; - drive->waiting_for_dma = 0; /* get DMA command mode */ dma_cmd = scc_ide_inb(hwif->dma_base); /* stop DMA */ diff --git a/drivers/ide/sgiioc4.c b/drivers/ide/sgiioc4.c index d90e8c5af0a2..cb2657c4c976 100644 --- a/drivers/ide/sgiioc4.c +++ b/drivers/ide/sgiioc4.c @@ -258,8 +258,6 @@ static int sgiioc4_dma_end(ide_drive_t *drive) } } - drive->waiting_for_dma = 0; - return dma_stat; } @@ -412,7 +410,6 @@ sgiioc4_configure_for_dma(int dma_direction, ide_drive_t * drive) writel(ending_dma_addr, (void __iomem *)(dma_base + IOC4_DMA_END_ADDR * 4)); writel(dma_direction, (void __iomem *)ioc4_dma_addr); - drive->waiting_for_dma = 1; } /* IOC4 Scatter Gather list Format */ diff --git a/drivers/ide/trm290.c b/drivers/ide/trm290.c index 0be27cae27d5..c0528f27fcae 100644 --- a/drivers/ide/trm290.c +++ b/drivers/ide/trm290.c @@ -198,9 +198,9 @@ static int trm290_dma_setup(ide_drive_t *drive, struct ide_cmd *cmd) return 1; outl(hwif->dmatable_dma | rw, hwif->dma_base); - drive->waiting_for_dma = 1; /* start DMA */ outw(count * 2 - 1, hwif->dma_base + 2); + return 0; } @@ -211,11 +211,7 @@ static void trm290_dma_start(ide_drive_t *drive) static int trm290_dma_end(ide_drive_t *drive) { - u16 status; - - drive->waiting_for_dma = 0; - - status = inw(drive->hwif->dma_base + 2); + u16 status = inw(drive->hwif->dma_base + 2); trm290_prepare_drive(drive, 0); diff --git a/drivers/ide/tx4939ide.c b/drivers/ide/tx4939ide.c index 7267c46c07cd..faf0d97b9ce8 100644 --- a/drivers/ide/tx4939ide.c +++ b/drivers/ide/tx4939ide.c @@ -304,8 +304,6 @@ static int tx4939ide_dma_setup(ide_drive_t *drive, struct ide_cmd *cmd) /* clear INTR & ERROR flags */ tx4939ide_clear_dma_status(base); - drive->waiting_for_dma = 1; - tx4939ide_writew(SECTOR_SIZE / 2, base, drive->dn ? TX4939IDE_Xfer_Cnt_2 : TX4939IDE_Xfer_Cnt_1); @@ -321,8 +319,6 @@ static int tx4939ide_dma_end(ide_drive_t *drive) void __iomem *base = TX4939IDE_BASE(hwif); u16 ctl = tx4939ide_readw(base, TX4939IDE_Int_Ctl); - drive->waiting_for_dma = 0; - /* get DMA command mode */ dma_cmd = tx4939ide_readb(base, TX4939IDE_DMA_Cmd); /* stop DMA */ -- cgit v1.2.3 From f094d4d83bccee9277ddb6aadccf35747889426b Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 31 Mar 2009 20:15:24 +0200 Subject: ide: sanitize ide_build_sglist() and ide_destroy_dmatable() * Move ide_map_sg() calls out from ide_build_sglist() to ide_dma_prepare(). * Pass command to ide_destroy_dmatable(). * Rename ide_build_sglist() to ide_dma_map_sg() and ide_destroy_dmatable() to ide_dma_unmap_sg(). There should be no functional changes caused by this patch. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-atapi.c | 3 ++- drivers/ide/ide-cd.c | 2 +- drivers/ide/ide-dma.c | 50 ++++++++++++++++++++++++------------------------- drivers/ide/sgiioc4.c | 7 ++++--- include/linux/ide.h | 6 +++--- 5 files changed, 35 insertions(+), 33 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index db6e617790bd..3f3fc7c7b2fe 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -326,6 +326,7 @@ static ide_startstop_t ide_pc_intr(ide_drive_t *drive) { struct ide_atapi_pc *pc = drive->pc; ide_hwif_t *hwif = drive->hwif; + struct ide_cmd *cmd = &hwif->cmd; struct request *rq = hwif->rq; const struct ide_tp_ops *tp_ops = hwif->tp_ops; xfer_func_t *xferfunc; @@ -346,7 +347,7 @@ static ide_startstop_t ide_pc_intr(ide_drive_t *drive) drive->waiting_for_dma = 0; rc = hwif->dma_ops->dma_end(drive); - ide_destroy_dmatable(drive); + ide_dma_unmap_sg(drive, cmd); if (rc || (drive->media == ide_tape && (stat & ATA_ERR))) { if (drive->media == ide_floppy) diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index f5c7bb739f45..35729a47f797 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -640,7 +640,7 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive) drive->dma = 0; drive->waiting_for_dma = 0; dma_error = hwif->dma_ops->dma_end(drive); - ide_destroy_dmatable(drive); + ide_dma_unmap_sg(drive, cmd); if (dma_error) { printk(KERN_ERR PFX "%s: DMA %s error\n", drive->name, write ? "write" : "read"); diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index 4d3102887d9c..a5612eadc306 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -89,17 +89,16 @@ static const struct drive_list_entry drive_blacklist[] = { ide_startstop_t ide_dma_intr(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; + struct ide_cmd *cmd = &hwif->cmd; u8 stat = 0, dma_stat = 0; drive->waiting_for_dma = 0; dma_stat = hwif->dma_ops->dma_end(drive); - ide_destroy_dmatable(drive); + ide_dma_unmap_sg(drive, cmd); stat = hwif->tp_ops->read_status(hwif); if (OK_STAT(stat, DRIVE_READY, drive->bad_wstat | ATA_DRQ)) { if (!dma_stat) { - struct ide_cmd *cmd = &hwif->cmd; - if ((cmd->tf_flags & IDE_TFLAG_FS) == 0) ide_finish_cmd(drive, cmd, stat); else @@ -119,8 +118,8 @@ int ide_dma_good_drive(ide_drive_t *drive) } /** - * ide_build_sglist - map IDE scatter gather for DMA I/O - * @drive: the drive to build the DMA table for + * ide_dma_map_sg - map IDE scatter gather for DMA I/O + * @drive: the drive to map the DMA table for * @cmd: command * * Perform the DMA mapping magic necessary to access the source or @@ -129,23 +128,19 @@ int ide_dma_good_drive(ide_drive_t *drive) * operate in a portable fashion. */ -static int ide_build_sglist(ide_drive_t *drive, struct ide_cmd *cmd) +static int ide_dma_map_sg(ide_drive_t *drive, struct ide_cmd *cmd) { ide_hwif_t *hwif = drive->hwif; struct scatterlist *sg = hwif->sg_table; int i; - ide_map_sg(drive, cmd); - if (cmd->tf_flags & IDE_TFLAG_WRITE) cmd->sg_dma_direction = DMA_TO_DEVICE; else cmd->sg_dma_direction = DMA_FROM_DEVICE; i = dma_map_sg(hwif->dev, sg, cmd->sg_nents, cmd->sg_dma_direction); - if (i == 0) - ide_map_sg(drive, cmd); - else { + if (i) { cmd->orig_sg_nents = cmd->sg_nents; cmd->sg_nents = i; } @@ -154,7 +149,7 @@ static int ide_build_sglist(ide_drive_t *drive, struct ide_cmd *cmd) } /** - * ide_destroy_dmatable - clean up DMA mapping + * ide_dma_unmap_sg - clean up DMA mapping * @drive: The drive to unmap * * Teardown mappings after DMA has completed. This must be called @@ -164,15 +159,14 @@ static int ide_build_sglist(ide_drive_t *drive, struct ide_cmd *cmd) * time. */ -void ide_destroy_dmatable(ide_drive_t *drive) +void ide_dma_unmap_sg(ide_drive_t *drive, struct ide_cmd *cmd) { ide_hwif_t *hwif = drive->hwif; - struct ide_cmd *cmd = &hwif->cmd; dma_unmap_sg(hwif->dev, hwif->sg_table, cmd->orig_sg_nents, cmd->sg_dma_direction); } -EXPORT_SYMBOL_GPL(ide_destroy_dmatable); +EXPORT_SYMBOL_GPL(ide_dma_unmap_sg); /** * ide_dma_off_quietly - Generic DMA kill @@ -471,6 +465,7 @@ ide_startstop_t ide_dma_timeout_retry(ide_drive_t *drive, int error) { ide_hwif_t *hwif = drive->hwif; const struct ide_dma_ops *dma_ops = hwif->dma_ops; + struct ide_cmd *cmd = &hwif->cmd; struct request *rq; ide_startstop_t ret = ide_stopped; @@ -482,7 +477,7 @@ ide_startstop_t ide_dma_timeout_retry(ide_drive_t *drive, int error) printk(KERN_WARNING "%s: DMA timeout error\n", drive->name); drive->waiting_for_dma = 0; (void)dma_ops->dma_end(drive); - ide_destroy_dmatable(drive); + ide_dma_unmap_sg(drive, cmd); ret = ide_error(drive, "dma timeout error", hwif->tp_ops->read_status(hwif)); } else { @@ -495,7 +490,7 @@ ide_startstop_t ide_dma_timeout_retry(ide_drive_t *drive, int error) hwif->tp_ops->read_status(hwif)); drive->waiting_for_dma = 0; (void)dma_ops->dma_end(drive); - ide_destroy_dmatable(drive); + ide_dma_unmap_sg(drive, cmd); } } @@ -572,14 +567,19 @@ int ide_dma_prepare(ide_drive_t *drive, struct ide_cmd *cmd) const struct ide_dma_ops *dma_ops = drive->hwif->dma_ops; if ((drive->dev_flags & IDE_DFLAG_USING_DMA) == 0 || - (dma_ops->dma_check && dma_ops->dma_check(drive, cmd)) || - ide_build_sglist(drive, cmd) == 0) - return 1; - if (dma_ops->dma_setup(drive, cmd)) { - ide_destroy_dmatable(drive); - ide_map_sg(drive, cmd); - return 1; - } + (dma_ops->dma_check && dma_ops->dma_check(drive, cmd))) + goto out; + ide_map_sg(drive, cmd); + if (ide_dma_map_sg(drive, cmd) == 0) + goto out_map; + if (dma_ops->dma_setup(drive, cmd)) + goto out_dma_unmap; drive->waiting_for_dma = 1; return 0; +out_dma_unmap: + ide_dma_unmap_sg(drive, cmd); +out_map: + ide_map_sg(drive, cmd); +out: + return 1; } diff --git a/drivers/ide/sgiioc4.c b/drivers/ide/sgiioc4.c index cb2657c4c976..6ef5a567d377 100644 --- a/drivers/ide/sgiioc4.c +++ b/drivers/ide/sgiioc4.c @@ -277,11 +277,12 @@ static void sgiioc4_dma_host_set(ide_drive_t *drive, int on) sgiioc4_clearirq(drive); } -static void -sgiioc4_resetproc(ide_drive_t * drive) +static void sgiioc4_resetproc(ide_drive_t *drive) { + struct ide_cmd *cmd = &drive->hwif->cmd; + sgiioc4_dma_end(drive); - ide_destroy_dmatable(drive); + ide_dma_unmap_sg(drive, cmd); sgiioc4_clearirq(drive); } diff --git a/include/linux/ide.h b/include/linux/ide.h index b350667b83ad..03c520917b7a 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1445,8 +1445,7 @@ int ide_allocate_dma_engine(ide_hwif_t *); void ide_release_dma_engine(ide_hwif_t *); int ide_dma_prepare(ide_drive_t *, struct ide_cmd *); - -void ide_destroy_dmatable(ide_drive_t *); +void ide_dma_unmap_sg(ide_drive_t *, struct ide_cmd *); #ifdef CONFIG_BLK_DEV_IDEDMA_SFF int config_drive_for_dma(ide_drive_t *); @@ -1481,7 +1480,8 @@ static inline ide_startstop_t ide_dma_timeout_retry(ide_drive_t *drive, int erro static inline void ide_release_dma_engine(ide_hwif_t *hwif) { ; } static inline int ide_dma_prepare(ide_drive_t *drive, struct ide_cmd *cmd) { return 1; } -static inline void ide_destroy_dmatable(ide_drive_t *drive) { ; } +static inline void ide_dma_unmap_sg(ide_drive_t *drive, + struct ide_cmd *cmd) { ; } #endif /* CONFIG_BLK_DEV_IDEDMA */ #ifdef CONFIG_BLK_DEV_IDEACPI -- cgit v1.2.3 From 52913ab2c6f760c2af9f9396765ce8fa1a2baf17 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 31 Mar 2009 20:15:24 +0200 Subject: ide-generic: remove no longer needed sysfs interface Nowadays we have "ide_generic.probe_mask=" module parameter and ide_platform host driver so sysfs interface for adding IDE interfaces is no longer needed. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-generic.c | 68 +---------------------------------------------- 1 file changed, 1 insertion(+), 67 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/ide-generic.c b/drivers/ide/ide-generic.c index 9d03e8211536..3a93e4c41bf7 100644 --- a/drivers/ide/ide-generic.c +++ b/drivers/ide/ide-generic.c @@ -1,20 +1,12 @@ /* * generic/default IDE host driver * - * Copyright (C) 2004, 2008 Bartlomiej Zolnierkiewicz + * Copyright (C) 2004, 2008-2009 Bartlomiej Zolnierkiewicz * This code was split off from ide.c. See it for original copyrights. * * May be copied or modified under the terms of the GNU General Public License. */ -/* - * For special cases new interfaces may be added using sysfs, i.e. - * - * echo -n "0x168:0x36e:10" > /sys/class/ide_generic/add - * - * will add an interface using I/O ports 0x168-0x16f/0x36e and IRQ 10. - */ - #include #include #include @@ -36,60 +28,6 @@ static const struct ide_port_info ide_generic_port_info = { .host_flags = IDE_HFLAG_NO_DMA, }; -static ssize_t store_add(struct class *cls, const char *buf, size_t n) -{ - unsigned int base, ctl; - int irq, rc; - hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; - - if (sscanf(buf, "%x:%x:%d", &base, &ctl, &irq) != 3) - return -EINVAL; - - memset(&hw, 0, sizeof(hw)); - ide_std_init_ports(&hw, base, ctl); - hw.irq = irq; - hw.chipset = ide_generic; - - rc = ide_host_add(&ide_generic_port_info, hws, NULL); - if (rc) - return rc; - - return n; -}; - -static struct class_attribute ide_generic_class_attrs[] = { - __ATTR(add, S_IWUSR, NULL, store_add), - __ATTR_NULL -}; - -static void ide_generic_class_release(struct class *cls) -{ - kfree(cls); -} - -static int __init ide_generic_sysfs_init(void) -{ - struct class *cls; - int rc; - - cls = kzalloc(sizeof(*cls), GFP_KERNEL); - if (!cls) - return -ENOMEM; - - cls->name = DRV_NAME; - cls->owner = THIS_MODULE; - cls->class_release = ide_generic_class_release; - cls->class_attrs = ide_generic_class_attrs; - - rc = class_register(cls); - if (rc) { - kfree(cls); - return rc; - } - - return 0; -} - #if defined(CONFIG_PLAT_M32700UT) || defined(CONFIG_PLAT_MAPPI2) \ || defined(CONFIG_PLAT_OPSPUT) static const u16 legacy_bases[] = { 0x1f0 }; @@ -196,10 +134,6 @@ static int __init ide_generic_init(void) } } - if (ide_generic_sysfs_init()) - printk(KERN_ERR DRV_NAME ": failed to create ide_generic " - "class\n"); - return rc; } -- cgit v1.2.3 From 4465461ece2b9249d6c0cf57bc0002100823e361 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 31 Mar 2009 20:15:24 +0200 Subject: ide: merge ide_arm and ide_generic host drivers There is no need for a separate ide_arm host driver nowadays so merge it into ide_generic one. While at it: - return -EBUSY from ide_generic_init() if I/O resources are busy - scale down ide_generic_check_pci_legacy_iobases() for CONFIG_PCI=n Cc: Russell King Cc: Alexander Schulz Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/Kconfig | 8 ++----- drivers/ide/Makefile | 2 -- drivers/ide/ide-generic.c | 18 +++++++++++----- drivers/ide/ide_arm.c | 53 ----------------------------------------------- 4 files changed, 15 insertions(+), 66 deletions(-) delete mode 100644 drivers/ide/ide_arm.c (limited to 'drivers') diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig index 640c99207242..896424445f65 100644 --- a/drivers/ide/Kconfig +++ b/drivers/ide/Kconfig @@ -222,7 +222,8 @@ comment "IDE chipset support/bugfixes" config IDE_GENERIC tristate "generic/default IDE chipset support" - depends on ALPHA || X86 || IA64 || M32R || MIPS + depends on ALPHA || X86 || IA64 || M32R || MIPS || ARCH_RPC || ARCH_SHARK + default ARM && (ARCH_RPC || ARCH_SHARK) help This is the generic IDE driver. This driver attaches to the fixed legacy ports (e.g. on PCs 0x1f0/0x170, 0x1e8/0x168 and @@ -731,11 +732,6 @@ config BLK_DEV_IDE_AT91 depends on ARM && ARCH_AT91 && !ARCH_AT91RM9200 && !ARCH_AT91X40 select IDE_TIMINGS -config IDE_ARM - tristate "ARM IDE support" - depends on ARM && (ARCH_RPC || ARCH_SHARK) - default y - config BLK_DEV_IDE_ICSIDE tristate "ICS IDE interface support" depends on ARM && ARCH_ACORN diff --git a/drivers/ide/Makefile b/drivers/ide/Makefile index 9b4bbe1cdc1a..81df925f0e8b 100644 --- a/drivers/ide/Makefile +++ b/drivers/ide/Makefile @@ -21,8 +21,6 @@ ide-core-$(CONFIG_IDE_LEGACY) += ide-legacy.o obj-$(CONFIG_IDE) += ide-core.o -obj-$(CONFIG_IDE_ARM) += ide_arm.o - obj-$(CONFIG_BLK_DEV_ALI14XX) += ali14xx.o obj-$(CONFIG_BLK_DEV_UMC8672) += umc8672.o obj-$(CONFIG_BLK_DEV_DTC2278) += dtc2278.o diff --git a/drivers/ide/ide-generic.c b/drivers/ide/ide-generic.c index 3a93e4c41bf7..7812ca0be13b 100644 --- a/drivers/ide/ide-generic.c +++ b/drivers/ide/ide-generic.c @@ -13,7 +13,10 @@ #include #include -/* FIXME: convert m32r to use ide_platform host driver */ +/* FIXME: convert arm and m32r to use ide_platform host driver */ +#ifdef CONFIG_ARM +#include +#endif #ifdef CONFIG_M32R #include #endif @@ -28,8 +31,11 @@ static const struct ide_port_info ide_generic_port_info = { .host_flags = IDE_HFLAG_NO_DMA, }; -#if defined(CONFIG_PLAT_M32700UT) || defined(CONFIG_PLAT_MAPPI2) \ - || defined(CONFIG_PLAT_OPSPUT) +#ifdef CONFIG_ARM +static const u16 legacy_bases[] = { 0x1f0 }; +static const int legacy_irqs[] = { IRQ_HARDDISK }; +#elif defined(CONFIG_PLAT_M32700UT) || defined(CONFIG_PLAT_MAPPI2) || \ + defined(CONFIG_PLAT_OPSPUT) static const u16 legacy_bases[] = { 0x1f0 }; static const int legacy_irqs[] = { PLD_IRQ_CFIREQ }; #elif defined(CONFIG_PLAT_MAPPI3) @@ -45,11 +51,11 @@ static const int legacy_irqs[] = { 14, 15, 11, 10, 8, 12 }; static void ide_generic_check_pci_legacy_iobases(int *primary, int *secondary) { +#ifdef CONFIG_PCI struct pci_dev *p = NULL; u16 val; for_each_pci_dev(p) { - if (pci_resource_start(p, 0) == 0x1f0) *primary = 1; if (pci_resource_start(p, 2) == 0x170) @@ -64,7 +70,6 @@ static void ide_generic_check_pci_legacy_iobases(int *primary, int *secondary) /* Intel MPIIX - PIO ATA on non PCI side of bridge */ if (p->vendor == PCI_VENDOR_ID_INTEL && p->device == PCI_DEVICE_ID_INTEL_82371MX) { - pci_read_config_word(p, 0x6C, &val); if (val & 0x8000) { /* ATA port enabled */ @@ -75,6 +80,7 @@ static void ide_generic_check_pci_legacy_iobases(int *primary, int *secondary) } } } +#endif } static int __init ide_generic_init(void) @@ -106,6 +112,7 @@ static int __init ide_generic_init(void) printk(KERN_ERR "%s: I/O resource 0x%lX-0x%lX " "not free.\n", DRV_NAME, io_addr, io_addr + 7); + rc = -EBUSY; continue; } @@ -114,6 +121,7 @@ static int __init ide_generic_init(void) "not free.\n", DRV_NAME, io_addr + 0x206); release_region(io_addr, 8); + rc = -EBUSY; continue; } diff --git a/drivers/ide/ide_arm.c b/drivers/ide/ide_arm.c deleted file mode 100644 index cf6385446ece..000000000000 --- a/drivers/ide/ide_arm.c +++ /dev/null @@ -1,53 +0,0 @@ -/* - * ARM default IDE host driver - * - * Copyright (C) 2004 Bartlomiej Zolnierkiewicz - * Based on code by: Russell King, Ian Molton and Alexander Schulz. - * - * May be copied or modified under the terms of the GNU General Public License. - */ - -#include -#include -#include - -#include - -#define DRV_NAME "ide_arm" - -#define IDE_ARM_IO 0x1f0 -#define IDE_ARM_IRQ IRQ_HARDDISK - -static const struct ide_port_info ide_arm_port_info = { - .host_flags = IDE_HFLAG_NO_DMA, -}; - -static int __init ide_arm_init(void) -{ - unsigned long base = IDE_ARM_IO, ctl = IDE_ARM_IO + 0x206; - hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; - - if (!request_region(base, 8, DRV_NAME)) { - printk(KERN_ERR "%s: I/O resource 0x%lX-0x%lX not free.\n", - DRV_NAME, base, base + 7); - return -EBUSY; - } - - if (!request_region(ctl, 1, DRV_NAME)) { - printk(KERN_ERR "%s: I/O resource 0x%lX not free.\n", - DRV_NAME, ctl); - release_region(base, 8); - return -EBUSY; - } - - memset(&hw, 0, sizeof(hw)); - ide_std_init_ports(&hw, base, ctl); - hw.irq = IDE_ARM_IRQ; - hw.chipset = ide_generic; - - return ide_host_add(&ide_arm_port_info, hws, NULL); -} - -module_init(ide_arm_init); - -MODULE_LICENSE("GPL"); -- cgit v1.2.3 From b5479167f4206e0d821a51ae149d921cd7a58e54 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 31 Mar 2009 20:15:25 +0200 Subject: ide: fix locking in drive_release_dev() * Request queue cleanup should happen before freeing drive->id and marking device as non-present. Fix it. * Remove superfluous hwif->lock acquiring/releasing. Cc: Stanislaw Gruszka Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-probe.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 548864510ba9..7c1f1bf81836 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -942,20 +942,16 @@ EXPORT_SYMBOL_GPL(ide_init_disk); static void drive_release_dev (struct device *dev) { ide_drive_t *drive = container_of(dev, ide_drive_t, gendev); - ide_hwif_t *hwif = drive->hwif; ide_proc_unregister_device(drive); - spin_lock_irq(&hwif->lock); + blk_cleanup_queue(drive->queue); + drive->queue = NULL; + kfree(drive->id); drive->id = NULL; + drive->dev_flags &= ~IDE_DFLAG_PRESENT; - /* Messed up locking ... */ - spin_unlock_irq(&hwif->lock); - blk_cleanup_queue(drive->queue); - spin_lock_irq(&hwif->lock); - drive->queue = NULL; - spin_unlock_irq(&hwif->lock); complete(&drive->gendev_rel_comp); } -- cgit v1.2.3 From 41fa9f863baacd32dd049daf8050d55a0c9e6f1a Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 31 Mar 2009 20:15:25 +0200 Subject: ide: decrease size of ->pc_buf field in struct ide_atapi_pc struct ide_atapi_pc is often allocated on the stack and size of ->pc_buf size is 256 bytes. However since only ide_floppy_create_read_capacity_cmd() and idetape_create_inquiry_cmd() require such size allocate buffers for these pc-s explicitely and decrease ->pc_buf size to 64 bytes. Cc: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-floppy.c | 5 ++++- drivers/ide/ide-floppy_ioctl.c | 5 ++++- drivers/ide/ide-tape.c | 4 ++++ include/linux/ide.h | 2 +- 4 files changed, 13 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index 7ae662334835..0faae3098295 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -385,7 +385,7 @@ static int ide_floppy_get_capacity(ide_drive_t *drive) struct gendisk *disk = floppy->disk; struct ide_atapi_pc pc; u8 *cap_desc; - u8 header_len, desc_cnt; + u8 pc_buf[256], header_len, desc_cnt; int i, rc = 1, blocks, length; drive->bios_cyl = 0; @@ -395,6 +395,9 @@ static int ide_floppy_get_capacity(ide_drive_t *drive) drive->capacity64 = 0; ide_floppy_create_read_capacity_cmd(&pc); + pc.buf = &pc_buf[0]; + pc.buf_size = sizeof(pc_buf); + if (ide_queue_pc_tail(drive, disk, &pc)) { printk(KERN_ERR PFX "Can't get floppy parameters\n"); return 1; diff --git a/drivers/ide/ide-floppy_ioctl.c b/drivers/ide/ide-floppy_ioctl.c index 8f8be8546038..cd8a42027ede 100644 --- a/drivers/ide/ide-floppy_ioctl.c +++ b/drivers/ide/ide-floppy_ioctl.c @@ -36,9 +36,9 @@ static int ide_floppy_get_format_capacities(ide_drive_t *drive, int __user *arg) { struct ide_disk_obj *floppy = drive->driver_data; - u8 header_len, desc_cnt; int i, blocks, length, u_array_size, u_index; int __user *argp; + u8 pc_buf[256], header_len, desc_cnt; if (get_user(u_array_size, arg)) return -EFAULT; @@ -47,6 +47,9 @@ static int ide_floppy_get_format_capacities(ide_drive_t *drive, return -EINVAL; ide_floppy_create_read_capacity_cmd(pc); + pc->buf = &pc_buf[0]; + pc->buf_size = sizeof(pc_buf); + if (ide_queue_pc_tail(drive, floppy->disk, pc)) { printk(KERN_ERR "ide-floppy: Can't get floppy parameters\n"); return -EIO; diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index 64dfa7458f8d..cafc67d9e2e8 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -2014,9 +2014,13 @@ static void idetape_get_inquiry_results(ide_drive_t *drive) { idetape_tape_t *tape = drive->driver_data; struct ide_atapi_pc pc; + u8 pc_buf[256]; char fw_rev[4], vendor_id[8], product_id[16]; idetape_create_inquiry_cmd(&pc); + pc.buf = &pc_buf[0]; + pc.buf_size = sizeof(pc_buf); + if (ide_queue_pc_tail(drive, tape->disk, &pc)) { printk(KERN_ERR "ide-tape: %s: can't get INQUIRY results\n", tape->name); diff --git a/include/linux/ide.h b/include/linux/ide.h index 03c520917b7a..0f48fbd46028 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -377,7 +377,7 @@ enum { * With each packet command, we allocate a buffer of IDE_PC_BUFFER_SIZE bytes. * This is used for several packet commands (not for READ/WRITE commands). */ -#define IDE_PC_BUFFER_SIZE 256 +#define IDE_PC_BUFFER_SIZE 64 #define ATAPI_WAIT_PC (60 * HZ) struct ide_atapi_pc { -- cgit v1.2.3 From 9f5af4d667a6d4ebd66019b4b26b445ddbae6d6c Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 31 Mar 2009 20:15:26 +0200 Subject: ide: remove CONFIG_BLK_DEV_IDEDOUBLER config option Nowadays it is not worth having a separate config option for Amiga IDE Doubler support so always include it (it still needs to be explicitly enabled by module parameter). Cc: Geert Uytterhoeven Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/Kconfig | 23 ++++++++--------------- drivers/ide/gayle.c | 12 +----------- 2 files changed, 9 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig index 896424445f65..4d2f2a6b5bdb 100644 --- a/drivers/ide/Kconfig +++ b/drivers/ide/Kconfig @@ -770,27 +770,20 @@ config BLK_DEV_GAYLE This includes on-board IDE interfaces on some Amiga models (A600, A1200, A4000, and A4000T), and IDE interfaces on the Zorro expansion bus (M-Tech E-Matrix 530 expansion card). - Say Y if you have an Amiga with a Gayle IDE interface and want to use - IDE devices (hard disks, CD-ROM drives, etc.) that are connected to - it. - Note that you also have to enable Zorro bus support if you want to - use Gayle IDE interfaces on the Zorro expansion bus. -config BLK_DEV_IDEDOUBLER - bool "Amiga IDE Doubler support (EXPERIMENTAL)" - depends on BLK_DEV_GAYLE && EXPERIMENTAL - ---help--- - This feature provides support for the so-called `IDE doublers' (made + It also provides support for the so-called `IDE doublers' (made by various manufacturers, e.g. Eyetech) that can be connected to the on-board IDE interface of some Amiga models. Using such an IDE doubler, you can connect up to four instead of two IDE devices to - the Amiga's on-board IDE interface. + the Amiga's on-board IDE interface. The feature is enabled at kernel + runtime using the "gayle.doubler" kernel boot parameter. - Note that the normal Amiga Gayle IDE driver may not work correctly - if you have an IDE doubler and don't enable this feature! + Say Y if you have an Amiga with a Gayle IDE interface and want to use + IDE devices (hard disks, CD-ROM drives, etc.) that are connected to + it. - Say Y if you have an IDE doubler. The feature is enabled at kernel - runtime using the "gayle.doubler" kernel boot parameter. + Note that you also have to enable Zorro bus support if you want to + use Gayle IDE interfaces on the Zorro expansion bus. config BLK_DEV_BUDDHA tristate "Buddha/Catweasel/X-Surf IDE interface support (EXPERIMENTAL)" diff --git a/drivers/ide/gayle.c b/drivers/ide/gayle.c index dc778251cb05..c7119516c5a7 100644 --- a/drivers/ide/gayle.c +++ b/drivers/ide/gayle.c @@ -53,11 +53,6 @@ #define GAYLE_NEXT_PORT 0x1000 -#ifndef CONFIG_BLK_DEV_IDEDOUBLER -#define GAYLE_NUM_HWIFS 1 -#define GAYLE_NUM_PROBE_HWIFS GAYLE_NUM_HWIFS -#define GAYLE_HAS_CONTROL_REG 1 -#else /* CONFIG_BLK_DEV_IDEDOUBLER */ #define GAYLE_NUM_HWIFS 2 #define GAYLE_NUM_PROBE_HWIFS (ide_doubler ? GAYLE_NUM_HWIFS : \ GAYLE_NUM_HWIFS-1) @@ -66,8 +61,6 @@ static int ide_doubler; module_param_named(doubler, ide_doubler, bool, 0); MODULE_PARM_DESC(doubler, "enable support for IDE doublers"); -#endif /* CONFIG_BLK_DEV_IDEDOUBLER */ - /* * Check and acknowledge the interrupt status @@ -151,10 +144,7 @@ static int __init gayle_init(void) found: printk(KERN_INFO "ide: Gayle IDE controller (A%d style%s)\n", a4000 ? 4000 : 1200, -#ifdef CONFIG_BLK_DEV_IDEDOUBLER - ide_doubler ? ", IDE doubler" : -#endif - ""); + ide_doubler ? ", IDE doubler" : ""); if (a4000) { phys_base = GAYLE_BASE_4000; -- cgit v1.2.3 From d93bc4521c80e9d87767779814e88f6d725453d7 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 31 Mar 2009 20:15:26 +0200 Subject: ide-{floppy,tape}: fix padding for PIO transfers * Return number of bytes left to transfer from idetape_{in,out}put_buffers() and number of bytes done from ide_tape_io_buffers(). * Fix padding for PIO transfers in ide_pc_intr() so read/write buffers are always completely processed and then the transfer is padded if necessary. * Remove invalid error messages. * Remove now superfluous padding from ide{_io_buffers,tape_input_buffers}(). While at it: * Set pc->bh to NULL in idetape_input_buffers() after all bh-s are done. * Cache !!(pc->flags & PC_FLAG_WRITING) in local variable in ide_pc_intr(). Cc: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-atapi.c | 57 +++++++++++++++++-------------------------------- drivers/ide/ide-tape.c | 32 +++++++++++++-------------- 2 files changed, 35 insertions(+), 54 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index 3f3fc7c7b2fe..40f413b20bd8 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -110,13 +110,6 @@ int ide_io_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc, } } - if (bcount) { - printk(KERN_ERR "%s: %d leftover bytes, %s\n", drive->name, - bcount, write ? "padding with zeros" - : "discarding data"); - ide_pad_transfer(drive, write, bcount); - } - return done; } EXPORT_SYMBOL_GPL(ide_io_buffers); @@ -330,9 +323,10 @@ static ide_startstop_t ide_pc_intr(ide_drive_t *drive) struct request *rq = hwif->rq; const struct ide_tp_ops *tp_ops = hwif->tp_ops; xfer_func_t *xferfunc; - unsigned int timeout, temp; + unsigned int timeout, done; u16 bcount; u8 stat, ireason, dsc = 0; + u8 write = !!(pc->flags & PC_FLAG_WRITING); debug_log("Enter %s - interrupt handler\n", __func__); @@ -441,8 +435,7 @@ static ide_startstop_t ide_pc_intr(ide_drive_t *drive) return ide_do_reset(drive); } - if (((ireason & ATAPI_IO) == ATAPI_IO) == - !!(pc->flags & PC_FLAG_WRITING)) { + if (((ireason & ATAPI_IO) == ATAPI_IO) == write) { /* Hopefully, we will never get here */ printk(KERN_ERR "%s: We wanted to %s, but the device wants us " "to %s!\n", drive->name, @@ -451,45 +444,33 @@ static ide_startstop_t ide_pc_intr(ide_drive_t *drive) return ide_do_reset(drive); } - if (!(pc->flags & PC_FLAG_WRITING)) { - /* Reading - Check that we have enough space */ - temp = pc->xferred + bcount; - if (temp > pc->req_xfer) { - if (temp > pc->buf_size) { - printk(KERN_ERR "%s: The device wants to send " - "us more data than expected - " - "discarding data\n", - drive->name); - - ide_pad_transfer(drive, 0, bcount); - goto next_irq; - } - debug_log("The device wants to send us more data than " - "expected - allowing transfer\n"); - } - xferfunc = tp_ops->input_data; - } else - xferfunc = tp_ops->output_data; + xferfunc = write ? tp_ops->output_data : tp_ops->input_data; if ((drive->media == ide_floppy && !pc->buf) || (drive->media == ide_tape && pc->bh)) { - int done = drive->pc_io_buffers(drive, pc, bcount, - !!(pc->flags & PC_FLAG_WRITING)); + done = drive->pc_io_buffers(drive, pc, bcount, write); /* FIXME: don't do partial completions */ if (drive->media == ide_floppy) ide_complete_rq(drive, 0, done ? done : ide_rq_bytes(rq)); - } else - xferfunc(drive, NULL, pc->cur_pos, bcount); + } else { + done = min_t(unsigned int, bcount, pc->req_xfer - pc->xferred); + xferfunc(drive, NULL, pc->cur_pos, done); + } /* Update the current position */ - pc->xferred += bcount; - pc->cur_pos += bcount; + pc->xferred += done; + pc->cur_pos += done; + + bcount -= done; + + if (bcount) + ide_pad_transfer(drive, write, bcount); + + debug_log("[cmd %x] transferred %d bytes, padded %d bytes\n", + rq->cmd[0], done, bcount); - debug_log("[cmd %x] transferred %d bytes on that intr.\n", - rq->cmd[0], bcount); -next_irq: /* And set the interrupt handler again */ ide_set_handler(drive, ide_pc_intr, timeout); return ide_started; diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index cafc67d9e2e8..cb942a9b580f 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -297,19 +297,15 @@ static struct ide_tape_obj *ide_tape_chrdev_get(unsigned int i) return tape; } -static void idetape_input_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc, +static int idetape_input_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc, unsigned int bcount) { struct idetape_bh *bh = pc->bh; int count; while (bcount) { - if (bh == NULL) { - printk(KERN_ERR "ide-tape: bh == NULL in " - "idetape_input_buffers\n"); - ide_pad_transfer(drive, 0, bcount); - return; - } + if (bh == NULL) + break; count = min( (unsigned int)(bh->b_size - atomic_read(&bh->b_count)), bcount); @@ -323,21 +319,21 @@ static void idetape_input_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc, atomic_set(&bh->b_count, 0); } } + pc->bh = bh; + + return bcount; } -static void idetape_output_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc, +static int idetape_output_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc, unsigned int bcount) { struct idetape_bh *bh = pc->bh; int count; while (bcount) { - if (bh == NULL) { - printk(KERN_ERR "ide-tape: bh == NULL in %s\n", - __func__); - return; - } + if (bh == NULL) + break; count = min((unsigned int)pc->b_count, (unsigned int)bcount); drive->hwif->tp_ops->output_data(drive, NULL, pc->b_data, count); bcount -= count; @@ -352,6 +348,8 @@ static void idetape_output_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc, } } } + + return bcount; } static void idetape_update_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc) @@ -563,12 +561,14 @@ static void ide_tape_handle_dsc(ide_drive_t *drive) static int ide_tape_io_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc, unsigned int bcount, int write) { + unsigned int bleft; + if (write) - idetape_output_buffers(drive, pc, bcount); + bleft = idetape_output_buffers(drive, pc, bcount); else - idetape_input_buffers(drive, pc, bcount); + bleft = idetape_input_buffers(drive, pc, bcount); - return bcount; + return bcount - bleft; } /* -- cgit v1.2.3 From 349d12a1fe57d49287a539909cf14f362634342d Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 31 Mar 2009 20:15:26 +0200 Subject: ide-floppy: use ide_pio_bytes() * Fix ide_init_sg_cmd() setup for non-fs requests. * Convert ide_pc_intr() to use ide_pio_bytes() for floppy media. * Remove no longer needed ide_io_buffers() and sg/sg_cnt fields from struct ide_atapi_pc. * Remove partial completions; kill idefloppy_update_buffers(), as a result. * Add some more debugging statements. Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-atapi.c | 68 ++++++++++++------------------------------------ drivers/ide/ide-floppy.c | 24 ++++------------- include/linux/ide.h | 5 ---- 3 files changed, 21 insertions(+), 76 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index 40f413b20bd8..100e6f94b4f0 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -71,49 +71,6 @@ int ide_check_atapi_device(ide_drive_t *drive, const char *s) } EXPORT_SYMBOL_GPL(ide_check_atapi_device); -/* PIO data transfer routine using the scatter gather table. */ -int ide_io_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc, - unsigned int bcount, int write) -{ - ide_hwif_t *hwif = drive->hwif; - const struct ide_tp_ops *tp_ops = hwif->tp_ops; - xfer_func_t *xf = write ? tp_ops->output_data : tp_ops->input_data; - struct scatterlist *sg = pc->sg; - char *buf; - int count, done = 0; - - while (bcount) { - count = min(sg->length - pc->b_count, bcount); - - if (PageHighMem(sg_page(sg))) { - unsigned long flags; - - local_irq_save(flags); - buf = kmap_atomic(sg_page(sg), KM_IRQ0) + sg->offset; - xf(drive, NULL, buf + pc->b_count, count); - kunmap_atomic(buf - sg->offset, KM_IRQ0); - local_irq_restore(flags); - } else { - buf = sg_virt(sg); - xf(drive, NULL, buf + pc->b_count, count); - } - - bcount -= count; - pc->b_count += count; - done += count; - - if (pc->b_count == sg->length) { - if (!--pc->sg_cnt) - break; - pc->sg = sg = sg_next(sg); - pc->b_count = 0; - } - } - - return done; -} -EXPORT_SYMBOL_GPL(ide_io_buffers); - void ide_init_pc(struct ide_atapi_pc *pc) { memset(pc, 0, sizeof(*pc)); @@ -353,6 +310,9 @@ static ide_startstop_t ide_pc_intr(ide_drive_t *drive) pc->xferred = pc->req_xfer; if (drive->pc_update_buffers) drive->pc_update_buffers(drive, pc); + + if (drive->media == ide_floppy) + ide_complete_rq(drive, 0, blk_rq_bytes(rq)); } debug_log("%s: DMA finished\n", drive->name); } @@ -408,12 +368,19 @@ static ide_startstop_t ide_pc_intr(ide_drive_t *drive) rq->errors = 0; ide_complete_rq(drive, 0, blk_rq_bytes(rq)); } else { + unsigned int done; + if (blk_fs_request(rq) == 0 && uptodate <= 0) { if (rq->errors == 0) rq->errors = -EIO; } - ide_complete_rq(drive, uptodate ? 0 : -EIO, - ide_rq_bytes(rq)); + + if (drive->media == ide_tape) + done = ide_rq_bytes(rq); /* FIXME */ + else + done = blk_rq_bytes(rq); + + ide_complete_rq(drive, uptodate ? 0 : -EIO, done); } return ide_stopped; @@ -446,14 +413,11 @@ static ide_startstop_t ide_pc_intr(ide_drive_t *drive) xferfunc = write ? tp_ops->output_data : tp_ops->input_data; - if ((drive->media == ide_floppy && !pc->buf) || - (drive->media == ide_tape && pc->bh)) { + if (drive->media == ide_floppy && pc->buf == NULL) { + done = min_t(unsigned int, bcount, cmd->nleft); + ide_pio_bytes(drive, cmd, write, done); + } else if (drive->media == ide_tape && pc->bh) { done = drive->pc_io_buffers(drive, pc, bcount, write); - - /* FIXME: don't do partial completions */ - if (drive->media == ide_floppy) - ide_complete_rq(drive, 0, - done ? done : ide_rq_bytes(rq)); } else { done = min_t(unsigned int, bcount, pc->req_xfer - pc->xferred); xferfunc(drive, NULL, pc->cur_pos, done); diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index 0faae3098295..2b4868d95f8b 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -61,16 +61,6 @@ */ #define IDEFLOPPY_PC_DELAY (HZ/20) /* default delay for ZIP 100 (50ms) */ -static void idefloppy_update_buffers(ide_drive_t *drive, - struct ide_atapi_pc *pc) -{ - struct request *rq = pc->rq; - struct bio *bio = rq->bio; - - while ((bio = rq->bio) != NULL) - ide_complete_rq(drive, 0, ide_rq_bytes(rq)); -} - static int ide_floppy_callback(ide_drive_t *drive, int dsc) { struct ide_disk_obj *floppy = drive->driver_data; @@ -213,7 +203,6 @@ static void idefloppy_create_rw_cmd(ide_drive_t *drive, memcpy(rq->cmd, pc->c, 12); pc->rq = rq; - pc->b_count = 0; if (rq->cmd_flags & REQ_RW) pc->flags |= PC_FLAG_WRITING; pc->buf = NULL; @@ -227,7 +216,6 @@ static void idefloppy_blockpc_cmd(struct ide_disk_obj *floppy, ide_init_pc(pc); memcpy(pc->c, rq->cmd, sizeof(pc->c)); pc->rq = rq; - pc->b_count = 0; if (rq->data_len && rq_data_dir(rq) == WRITE) pc->flags |= PC_FLAG_WRITING; pc->buf = rq->data; @@ -244,10 +232,11 @@ static ide_startstop_t ide_floppy_do_request(ide_drive_t *drive, struct request *rq, sector_t block) { struct ide_disk_obj *floppy = drive->driver_data; - ide_hwif_t *hwif = drive->hwif; struct ide_cmd cmd; struct ide_atapi_pc *pc; + ide_debug_log(IDE_DBG_FUNC, "enter, cmd: 0x%x\n", rq->cmd[0]); + if (drive->debug_mask & IDE_DBG_RQ) blk_dump_rq_flags(rq, (rq->rq_disk ? rq->rq_disk->disk_name @@ -294,13 +283,10 @@ static ide_startstop_t ide_floppy_do_request(ide_drive_t *drive, cmd.rq = rq; if (blk_fs_request(rq) || pc->req_xfer) { - ide_init_sg_cmd(&cmd, rq->nr_sectors << 9); + ide_init_sg_cmd(&cmd, pc->req_xfer); ide_map_sg(drive, &cmd); } - pc->sg = hwif->sg_table; - pc->sg_cnt = cmd.sg_nents; - pc->rq = rq; return ide_floppy_issue_pc(drive, &cmd, pc); @@ -388,6 +374,8 @@ static int ide_floppy_get_capacity(ide_drive_t *drive) u8 pc_buf[256], header_len, desc_cnt; int i, rc = 1, blocks, length; + ide_debug_log(IDE_DBG_FUNC, "enter"); + drive->bios_cyl = 0; drive->bios_head = drive->bios_sect = 0; floppy->blocks = 0; @@ -488,8 +476,6 @@ static void ide_floppy_setup(ide_drive_t *drive) u16 *id = drive->id; drive->pc_callback = ide_floppy_callback; - drive->pc_update_buffers = idefloppy_update_buffers; - drive->pc_io_buffers = ide_io_buffers; /* * We used to check revisions here. At this point however I'm giving up. diff --git a/include/linux/ide.h b/include/linux/ide.h index 0f48fbd46028..836c4c6cb7e3 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -415,9 +415,6 @@ struct ide_atapi_pc { struct idetape_bh *bh; char *b_data; - struct scatterlist *sg; - unsigned int sg_cnt; - unsigned long timeout; }; @@ -1177,8 +1174,6 @@ void ide_tf_read(ide_drive_t *, struct ide_cmd *); void ide_input_data(ide_drive_t *, struct ide_cmd *, void *, unsigned int); void ide_output_data(ide_drive_t *, struct ide_cmd *, void *, unsigned int); -int ide_io_buffers(ide_drive_t *, struct ide_atapi_pc *, unsigned int, int); - extern void SELECT_DRIVE(ide_drive_t *); void SELECT_MASK(ide_drive_t *, int); -- cgit v1.2.3 From 985232e388714d4a9e94b4d96ee69b6ff8c9dc31 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Tue, 31 Mar 2009 20:15:27 +0200 Subject: au1xxx-ide: auide_{in|out}sw() should be static Make auide_{insw|outsw}() 'static' and mark them 'inline' as there's only one call site for each: in the driver's {in|out}put_data() methods respectively... Signed-off-by: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/au1xxx-ide.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/au1xxx-ide.c b/drivers/ide/au1xxx-ide.c index 549bbb2755a8..1bfb43d0d3a8 100644 --- a/drivers/ide/au1xxx-ide.c +++ b/drivers/ide/au1xxx-ide.c @@ -50,7 +50,7 @@ static _auide_hwif auide_hwif; #if defined(CONFIG_BLK_DEV_IDE_AU1XXX_PIO_DBDMA) -void auide_insw(unsigned long port, void *addr, u32 count) +static inline void auide_insw(unsigned long port, void *addr, u32 count) { _auide_hwif *ahwif = &auide_hwif; chan_tab_t *ctp; @@ -68,7 +68,7 @@ void auide_insw(unsigned long port, void *addr, u32 count) ctp->cur_ptr = au1xxx_ddma_get_nextptr_virt(dp); } -void auide_outsw(unsigned long port, void *addr, u32 count) +static inline void auide_outsw(unsigned long port, void *addr, u32 count) { _auide_hwif *ahwif = &auide_hwif; chan_tab_t *ctp; -- cgit v1.2.3 From 8d64fcd9357798ad0d61f8877de13d5e1b1ab510 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Tue, 31 Mar 2009 20:15:27 +0200 Subject: ide: identify data word 53 bit 1 doesn't cover words 62 and 63 (take 3) The IDE code assumed for years that the bit 1 of the identify data word 53 also covers the validity of the SW/MW DMA information in words 62 and 63, but it has always covered only words 64 thru 70, with words 62 and 63 being defined in the original ATA spec, not in ATA-2... This fix however should only concern *very* old hard disks and rather old CF cards... Signed-off-by: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/cs5530.c | 3 +-- drivers/ide/ide-dma-sff.c | 7 +++---- drivers/ide/ide-dma.c | 32 ++++++++++++++------------------ drivers/ide/sc1200.c | 3 +-- 4 files changed, 19 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/cs5530.c b/drivers/ide/cs5530.c index 8e8b35a89901..40bf05eddf6e 100644 --- a/drivers/ide/cs5530.c +++ b/drivers/ide/cs5530.c @@ -92,8 +92,7 @@ static u8 cs5530_udma_filter(ide_drive_t *drive) if ((mateid[ATA_ID_FIELD_VALID] & 4) && (mateid[ATA_ID_UDMA_MODES] & 7)) goto out; - if ((mateid[ATA_ID_FIELD_VALID] & 2) && - (mateid[ATA_ID_MWDMA_MODES] & 7)) + if (mateid[ATA_ID_MWDMA_MODES] & 7) mask = 0; } out: diff --git a/drivers/ide/ide-dma-sff.c b/drivers/ide/ide-dma-sff.c index f8c7d0cd2ff8..16fc46edc32d 100644 --- a/drivers/ide/ide-dma-sff.c +++ b/drivers/ide/ide-dma-sff.c @@ -38,10 +38,9 @@ int config_drive_for_dma(ide_drive_t *drive) * Enable DMA on any drive that has mode2 DMA * (multi or single) enabled */ - if (id[ATA_ID_FIELD_VALID] & 2) /* regular DMA */ - if ((id[ATA_ID_MWDMA_MODES] & 0x404) == 0x404 || - (id[ATA_ID_SWDMA_MODES] & 0x404) == 0x404) - return 1; + if ((id[ATA_ID_MWDMA_MODES] & 0x404) == 0x404 || + (id[ATA_ID_SWDMA_MODES] & 0x404) == 0x404) + return 1; /* Consult the list of known "good" drives */ if (ide_dma_good_drive(drive)) diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index a5612eadc306..f9c91752f275 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -245,12 +245,11 @@ static unsigned int ide_get_mode_mask(ide_drive_t *drive, u8 base, u8 req_mode) case XFER_UDMA_0: if ((id[ATA_ID_FIELD_VALID] & 4) == 0) break; - + mask = id[ATA_ID_UDMA_MODES]; if (port_ops && port_ops->udma_filter) - mask = port_ops->udma_filter(drive); + mask &= port_ops->udma_filter(drive); else - mask = hwif->ultra_mask; - mask &= id[ATA_ID_UDMA_MODES]; + mask &= hwif->ultra_mask; /* * avoid false cable warning from eighty_ninty_three() @@ -261,18 +260,15 @@ static unsigned int ide_get_mode_mask(ide_drive_t *drive, u8 base, u8 req_mode) } break; case XFER_MW_DMA_0: - if ((id[ATA_ID_FIELD_VALID] & 2) == 0) - break; + mask = id[ATA_ID_MWDMA_MODES]; if (port_ops && port_ops->mdma_filter) - mask = port_ops->mdma_filter(drive); + mask &= port_ops->mdma_filter(drive); else - mask = hwif->mwdma_mask; - mask &= id[ATA_ID_MWDMA_MODES]; + mask &= hwif->mwdma_mask; break; case XFER_SW_DMA_0: - if (id[ATA_ID_FIELD_VALID] & 2) { - mask = id[ATA_ID_SWDMA_MODES] & hwif->swdma_mask; - } else if (id[ATA_ID_OLD_DMA_MODES] >> 8) { + mask = id[ATA_ID_SWDMA_MODES]; + if (!(mask & ATA_SWDMA2) && (id[ATA_ID_OLD_DMA_MODES] >> 8)) { u8 mode = id[ATA_ID_OLD_DMA_MODES] >> 8; /* @@ -280,8 +276,9 @@ static unsigned int ide_get_mode_mask(ide_drive_t *drive, u8 base, u8 req_mode) * (the maximum allowed mode is XFER_SW_DMA_2) */ if (mode <= 2) - mask = ((2 << mode) - 1) & hwif->swdma_mask; + mask = (2 << mode) - 1; } + mask &= hwif->swdma_mask; break; default: BUG(); @@ -398,11 +395,10 @@ int ide_id_dma_bug(ide_drive_t *drive) if ((id[ATA_ID_UDMA_MODES] >> 8) && (id[ATA_ID_MWDMA_MODES] >> 8)) goto err_out; - } else if (id[ATA_ID_FIELD_VALID] & 2) { - if ((id[ATA_ID_MWDMA_MODES] >> 8) && - (id[ATA_ID_SWDMA_MODES] >> 8)) - goto err_out; - } + } else if ((id[ATA_ID_MWDMA_MODES] >> 8) && + (id[ATA_ID_SWDMA_MODES] >> 8)) + goto err_out; + return 0; err_out: printk(KERN_ERR "%s: bad DMA info in identify block\n", drive->name); diff --git a/drivers/ide/sc1200.c b/drivers/ide/sc1200.c index 13e3988f00f0..d467478d68da 100644 --- a/drivers/ide/sc1200.c +++ b/drivers/ide/sc1200.c @@ -115,8 +115,7 @@ static u8 sc1200_udma_filter(ide_drive_t *drive) if ((mateid[ATA_ID_FIELD_VALID] & 4) && (mateid[ATA_ID_UDMA_MODES] & 7)) goto out; - if ((mateid[ATA_ID_FIELD_VALID] & 2) && - (mateid[ATA_ID_MWDMA_MODES] & 7)) + if (mateid[ATA_ID_MWDMA_MODES] & 7) mask = 0; } out: -- cgit v1.2.3 From c4199930b119eb9c1ffb102ed57eaac4d4424d08 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Tue, 31 Mar 2009 20:15:27 +0200 Subject: ide-iops: only clear DMA words on setting DMA mode The bytes indicating current DMA mode in the identify data words 62, 63, and 88 should only change on setting a DMA mode, so stop clearing them on setting PIO mode in ide_config_drive_speed(). While at it, correct SW/MW DMA mode masks... Signed-off-by: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-iops.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index 5403e4a44be4..0e62698146b6 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -386,9 +386,11 @@ int ide_config_drive_speed(ide_drive_t *drive, u8 speed) return error; } - id[ATA_ID_UDMA_MODES] &= ~0xFF00; - id[ATA_ID_MWDMA_MODES] &= ~0x0F00; - id[ATA_ID_SWDMA_MODES] &= ~0x0F00; + if (speed >= XFER_SW_DMA_0) { + id[ATA_ID_UDMA_MODES] &= ~0xFF00; + id[ATA_ID_MWDMA_MODES] &= ~0x0700; + id[ATA_ID_SWDMA_MODES] &= ~0x0700; + } skip: #ifdef CONFIG_BLK_DEV_IDEDMA -- cgit v1.2.3 From 74638c84821c066d02c158bc843c84499ddc9764 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Tue, 31 Mar 2009 20:15:28 +0200 Subject: ide: add support for CFA specified transfer modes (take 3) Add support for the CompactFlash specific PIO modes 5/6 and MWDMA modes 3/4. Since there were no PIO5 capable hard drives produced and one would also need 66 MHz IDE clock to actually get the difference WRT the address setup timings programmed, I decided to simply replace the old non-standard PIO mode 5 timings with the CFA specified ones. Signed-off-by: Sergei Shtylyov Cc: stf_xl@wp.pl Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-dma.c | 8 ++++++++ drivers/ide/ide-iops.c | 12 +++++++++++- drivers/ide/ide-timings.c | 12 ++++++++++-- drivers/ide/ide-xfer-mode.c | 15 +++++++++------ drivers/ide/sl82c105.c | 3 ++- 5 files changed, 40 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index f9c91752f275..a0b8cab1d9a6 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -261,6 +261,14 @@ static unsigned int ide_get_mode_mask(ide_drive_t *drive, u8 base, u8 req_mode) break; case XFER_MW_DMA_0: mask = id[ATA_ID_MWDMA_MODES]; + + /* Also look for the CF specific MWDMA modes... */ + if (ata_id_is_cfa(id) && (id[ATA_ID_CFA_MODES] & 0x38)) { + u8 mode = ((id[ATA_ID_CFA_MODES] & 0x38) >> 3) - 1; + + mask |= ((2 << mode) - 1) << 3; + } + if (port_ops && port_ops->mdma_filter) mask &= port_ops->mdma_filter(drive); else diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index 0e62698146b6..0caca342802d 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -306,6 +306,7 @@ int ide_driveid_update(ide_drive_t *drive) drive->id[ATA_ID_UDMA_MODES] = id[ATA_ID_UDMA_MODES]; drive->id[ATA_ID_MWDMA_MODES] = id[ATA_ID_MWDMA_MODES]; drive->id[ATA_ID_SWDMA_MODES] = id[ATA_ID_SWDMA_MODES]; + drive->id[ATA_ID_CFA_MODES] = id[ATA_ID_CFA_MODES]; /* anything more ? */ kfree(id); @@ -390,7 +391,10 @@ int ide_config_drive_speed(ide_drive_t *drive, u8 speed) id[ATA_ID_UDMA_MODES] &= ~0xFF00; id[ATA_ID_MWDMA_MODES] &= ~0x0700; id[ATA_ID_SWDMA_MODES] &= ~0x0700; - } + if (ata_id_is_cfa(id)) + id[ATA_ID_CFA_MODES] &= ~0x0E00; + } else if (ata_id_is_cfa(id)) + id[ATA_ID_CFA_MODES] &= ~0x01C0; skip: #ifdef CONFIG_BLK_DEV_IDEDMA @@ -403,12 +407,18 @@ int ide_config_drive_speed(ide_drive_t *drive, u8 speed) if (speed >= XFER_UDMA_0) { i = 1 << (speed - XFER_UDMA_0); id[ATA_ID_UDMA_MODES] |= (i << 8 | i); + } else if (ata_id_is_cfa(id) && speed >= XFER_MW_DMA_3) { + i = speed - XFER_MW_DMA_2; + id[ATA_ID_CFA_MODES] |= i << 9; } else if (speed >= XFER_MW_DMA_0) { i = 1 << (speed - XFER_MW_DMA_0); id[ATA_ID_MWDMA_MODES] |= (i << 8 | i); } else if (speed >= XFER_SW_DMA_0) { i = 1 << (speed - XFER_SW_DMA_0); id[ATA_ID_SWDMA_MODES] |= (i << 8 | i); + } else if (ata_id_is_cfa(id) && speed >= XFER_PIO_5) { + i = speed - XFER_PIO_4; + id[ATA_ID_CFA_MODES] |= i << 6; } if (!drive->init_speed) diff --git a/drivers/ide/ide-timings.c b/drivers/ide/ide-timings.c index 81f527af8fae..001a56365be5 100644 --- a/drivers/ide/ide-timings.c +++ b/drivers/ide/ide-timings.c @@ -43,6 +43,8 @@ static struct ide_timing ide_timing[] = { { XFER_UDMA_1, 0, 0, 0, 0, 0, 0, 0, 80 }, { XFER_UDMA_0, 0, 0, 0, 0, 0, 0, 0, 120 }, + { XFER_MW_DMA_4, 25, 0, 0, 0, 55, 20, 80, 0 }, + { XFER_MW_DMA_3, 25, 0, 0, 0, 65, 25, 100, 0 }, { XFER_MW_DMA_2, 25, 0, 0, 0, 70, 25, 120, 0 }, { XFER_MW_DMA_1, 45, 0, 0, 0, 80, 50, 150, 0 }, { XFER_MW_DMA_0, 60, 0, 0, 0, 215, 215, 480, 0 }, @@ -51,7 +53,8 @@ static struct ide_timing ide_timing[] = { { XFER_SW_DMA_1, 90, 0, 0, 0, 240, 240, 480, 0 }, { XFER_SW_DMA_0, 120, 0, 0, 0, 480, 480, 960, 0 }, - { XFER_PIO_5, 20, 50, 30, 100, 50, 30, 100, 0 }, + { XFER_PIO_6, 10, 55, 20, 80, 55, 20, 80, 0 }, + { XFER_PIO_5, 15, 65, 25, 100, 65, 25, 100, 0 }, { XFER_PIO_4, 25, 70, 25, 120, 70, 25, 120, 0 }, { XFER_PIO_3, 30, 80, 70, 180, 80, 70, 180, 0 }, @@ -90,6 +93,10 @@ u16 ide_pio_cycle_time(ide_drive_t *drive, u8 pio) /* conservative "downgrade" for all pre-ATA2 drives */ if (pio < 3 && cycle < t->cycle) cycle = 0; /* use standard timing */ + + /* Use the standard timing for the CF specific modes too */ + if (pio > 4 && ata_id_is_cfa(id)) + cycle = 0; } return cycle ? cycle : t->cycle; @@ -161,7 +168,8 @@ int ide_timing_compute(ide_drive_t *drive, u8 speed, if (speed <= XFER_PIO_2) p.cycle = p.cyc8b = id[ATA_ID_EIDE_PIO]; - else if (speed <= XFER_PIO_5) + else if ((speed <= XFER_PIO_4) || + (speed == XFER_PIO_5 && !ata_id_is_cfa(id))) p.cycle = p.cyc8b = id[ATA_ID_EIDE_PIO_IORDY]; else if (speed >= XFER_MW_DMA_0 && speed <= XFER_MW_DMA_2) p.cycle = id[ATA_ID_EIDE_DMA_MIN]; diff --git a/drivers/ide/ide-xfer-mode.c b/drivers/ide/ide-xfer-mode.c index 6910f6a257e8..af44be9d546c 100644 --- a/drivers/ide/ide-xfer-mode.c +++ b/drivers/ide/ide-xfer-mode.c @@ -9,11 +9,11 @@ static const char *udma_str[] = { "UDMA/16", "UDMA/25", "UDMA/33", "UDMA/44", "UDMA/66", "UDMA/100", "UDMA/133", "UDMA7" }; static const char *mwdma_str[] = - { "MWDMA0", "MWDMA1", "MWDMA2" }; + { "MWDMA0", "MWDMA1", "MWDMA2", "MWDMA3", "MWDMA4" }; static const char *swdma_str[] = { "SWDMA0", "SWDMA1", "SWDMA2" }; static const char *pio_str[] = - { "PIO0", "PIO1", "PIO2", "PIO3", "PIO4", "PIO5" }; + { "PIO0", "PIO1", "PIO2", "PIO3", "PIO4", "PIO5", "PIO6" }; /** * ide_xfer_verbose - return IDE mode names @@ -30,11 +30,11 @@ const char *ide_xfer_verbose(u8 mode) if (mode >= XFER_UDMA_0 && mode <= XFER_UDMA_7) s = udma_str[i]; - else if (mode >= XFER_MW_DMA_0 && mode <= XFER_MW_DMA_2) + else if (mode >= XFER_MW_DMA_0 && mode <= XFER_MW_DMA_4) s = mwdma_str[i]; else if (mode >= XFER_SW_DMA_0 && mode <= XFER_SW_DMA_2) s = swdma_str[i]; - else if (mode >= XFER_PIO_0 && mode <= XFER_PIO_5) + else if (mode >= XFER_PIO_0 && mode <= XFER_PIO_6) s = pio_str[i & 0x7]; else if (mode == XFER_PIO_SLOW) s = "PIO SLOW"; @@ -79,7 +79,10 @@ u8 ide_get_best_pio_mode(ide_drive_t *drive, u8 mode_wanted, u8 max_mode) } if (id[ATA_ID_FIELD_VALID] & 2) { /* ATA2? */ - if (ata_id_has_iordy(id)) { + if (ata_id_is_cfa(id) && (id[ATA_ID_CFA_MODES] & 7)) + pio_mode = 4 + min_t(int, 2, + id[ATA_ID_CFA_MODES] & 7); + else if (ata_id_has_iordy(id)) { if (id[ATA_ID_PIO_MODES] & 7) { overridden = 0; if (id[ATA_ID_PIO_MODES] & 4) @@ -239,7 +242,7 @@ int ide_set_xfer_rate(ide_drive_t *drive, u8 rate) BUG_ON(rate < XFER_PIO_0); - if (rate >= XFER_PIO_0 && rate <= XFER_PIO_5) + if (rate >= XFER_PIO_0 && rate <= XFER_PIO_6) return ide_set_pio_mode(drive, rate); return ide_set_dma_mode(drive, rate); diff --git a/drivers/ide/sl82c105.c b/drivers/ide/sl82c105.c index d6f8977191c8..b0a460625335 100644 --- a/drivers/ide/sl82c105.c +++ b/drivers/ide/sl82c105.c @@ -61,7 +61,8 @@ static unsigned int get_pio_timings(ide_drive_t *drive, u8 pio) if (cmd_off == 0) cmd_off = 1; - if (pio > 2 || ata_id_has_iordy(drive->id)) + if ((pio > 2 || ata_id_has_iordy(drive->id)) && + !(pio > 4 && ata_id_is_cfa(drive->id))) iordy = 0x40; return (cmd_on - 1) << 8 | (cmd_off - 1) | iordy; -- cgit v1.2.3 From 47ab834854d4639fedf2ed2f21b41297f2abe1a7 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Tue, 31 Mar 2009 20:15:29 +0200 Subject: ide-disk: use ATA_ERR Make use of ATA_ERR instead of hard-coded value in idedisk_set_max_address() and idedisk_read_native_max_address(). Signed-off-by: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-disk.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index ca934c8a1289..c998cf8e971a 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -227,7 +227,7 @@ static u64 idedisk_read_native_max_address(ide_drive_t *drive, int lba48) ide_no_data_taskfile(drive, &cmd); /* if OK, compute maximum address value */ - if ((tf->status & 0x01) == 0) + if (!(tf->status & ATA_ERR)) addr = ide_get_lba_addr(tf, lba48) + 1; return addr; @@ -267,7 +267,7 @@ static u64 idedisk_set_max_address(ide_drive_t *drive, u64 addr_req, int lba48) ide_no_data_taskfile(drive, &cmd); /* if OK, compute maximum address value */ - if ((tf->status & 0x01) == 0) + if (!(tf->status & ATA_ERR)) addr_set = ide_get_lba_addr(tf, lba48) + 1; return addr_set; -- cgit v1.2.3 From 4d74c3fcf2b90487eacec511bc8c07177711c81c Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Tue, 31 Mar 2009 20:15:29 +0200 Subject: ide: use ATA_HOB Make use of ATA_HOB instead of hard-coded value in the tf_read() method. Signed-off-by: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/at91_ide.c | 4 ++-- drivers/ide/ide-h8300.c | 4 ++-- drivers/ide/ide-io-std.c | 4 ++-- drivers/ide/ns87415.c | 4 ++-- drivers/ide/scc_pata.c | 4 ++-- drivers/ide/tx4938ide.c | 4 ++-- drivers/ide/tx4939ide.c | 4 ++-- 7 files changed, 14 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/at91_ide.c b/drivers/ide/at91_ide.c index 6bb45a2b24c2..8fc6ae958b0b 100644 --- a/drivers/ide/at91_ide.c +++ b/drivers/ide/at91_ide.c @@ -242,7 +242,7 @@ static void at91_ide_tf_read(ide_drive_t *drive, struct ide_cmd *cmd) } /* be sure we're looking at the low order bits */ - ide_mm_outb(ATA_DEVCTL_OBS & ~0x80, io_ports->ctl_addr); + ide_mm_outb(ATA_DEVCTL_OBS, io_ports->ctl_addr); if (cmd->tf_flags & IDE_TFLAG_IN_FEATURE) tf->feature = ide_mm_inb(io_ports->feature_addr); @@ -258,7 +258,7 @@ static void at91_ide_tf_read(ide_drive_t *drive, struct ide_cmd *cmd) tf->device = ide_mm_inb(io_ports->device_addr); if (cmd->tf_flags & IDE_TFLAG_LBA48) { - ide_mm_outb(ATA_DEVCTL_OBS | 0x80, io_ports->ctl_addr); + ide_mm_outb(ATA_HOB | ATA_DEVCTL_OBS, io_ports->ctl_addr); if (cmd->tf_flags & IDE_TFLAG_IN_HOB_FEATURE) tf->hob_feature = ide_mm_inb(io_ports->feature_addr); diff --git a/drivers/ide/ide-h8300.c b/drivers/ide/ide-h8300.c index ff8339ed59ab..7492f28d1290 100644 --- a/drivers/ide/ide-h8300.c +++ b/drivers/ide/ide-h8300.c @@ -98,7 +98,7 @@ static void h8300_tf_read(ide_drive_t *drive, struct ide_cmd *cmd) } /* be sure we're looking at the low order bits */ - outb(ATA_DEVCTL_OBS & ~0x80, io_ports->ctl_addr); + outb(ATA_DEVCTL_OBS, io_ports->ctl_addr); if (cmd->tf_flags & IDE_TFLAG_IN_FEATURE) tf->feature = inb(io_ports->feature_addr); @@ -114,7 +114,7 @@ static void h8300_tf_read(ide_drive_t *drive, struct ide_cmd *cmd) tf->device = inb(io_ports->device_addr); if (cmd->tf_flags & IDE_TFLAG_LBA48) { - outb(ATA_DEVCTL_OBS | 0x80, io_ports->ctl_addr); + outb(ATA_HOB | ATA_DEVCTL_OBS, io_ports->ctl_addr); if (cmd->tf_flags & IDE_TFLAG_IN_HOB_FEATURE) tf->hob_feature = inb(io_ports->feature_addr); diff --git a/drivers/ide/ide-io-std.c b/drivers/ide/ide-io-std.c index 2d9c6dc3f956..3a867e49a0af 100644 --- a/drivers/ide/ide-io-std.c +++ b/drivers/ide/ide-io-std.c @@ -166,7 +166,7 @@ void ide_tf_read(ide_drive_t *drive, struct ide_cmd *cmd) } /* be sure we're looking at the low order bits */ - tf_outb(ATA_DEVCTL_OBS & ~0x80, io_ports->ctl_addr); + tf_outb(ATA_DEVCTL_OBS, io_ports->ctl_addr); if (cmd->tf_flags & IDE_TFLAG_IN_FEATURE) tf->feature = tf_inb(io_ports->feature_addr); @@ -182,7 +182,7 @@ void ide_tf_read(ide_drive_t *drive, struct ide_cmd *cmd) tf->device = tf_inb(io_ports->device_addr); if (cmd->tf_flags & IDE_TFLAG_LBA48) { - tf_outb(ATA_DEVCTL_OBS | 0x80, io_ports->ctl_addr); + tf_outb(ATA_HOB | ATA_DEVCTL_OBS, io_ports->ctl_addr); if (cmd->tf_flags & IDE_TFLAG_IN_HOB_FEATURE) tf->hob_feature = tf_inb(io_ports->feature_addr); diff --git a/drivers/ide/ns87415.c b/drivers/ide/ns87415.c index cbc1d1110385..13a9e00efa13 100644 --- a/drivers/ide/ns87415.c +++ b/drivers/ide/ns87415.c @@ -74,7 +74,7 @@ static void superio_tf_read(ide_drive_t *drive, struct ide_cmd *cmd) } /* be sure we're looking at the low order bits */ - outb(ATA_DEVCTL_OBS & ~0x80, io_ports->ctl_addr); + outb(ATA_DEVCTL_OBS, io_ports->ctl_addr); if (cmd->tf_flags & IDE_TFLAG_IN_FEATURE) tf->feature = inb(io_ports->feature_addr); @@ -90,7 +90,7 @@ static void superio_tf_read(ide_drive_t *drive, struct ide_cmd *cmd) tf->device = superio_ide_inb(io_ports->device_addr); if (cmd->tf_flags & IDE_TFLAG_LBA48) { - outb(ATA_DEVCTL_OBS | 0x80, io_ports->ctl_addr); + outb(ATA_HOB | ATA_DEVCTL_OBS, io_ports->ctl_addr); if (cmd->tf_flags & IDE_TFLAG_IN_HOB_FEATURE) tf->hob_feature = inb(io_ports->feature_addr); diff --git a/drivers/ide/scc_pata.c b/drivers/ide/scc_pata.c index 542419ffa9b7..6e47eac1cd7f 100644 --- a/drivers/ide/scc_pata.c +++ b/drivers/ide/scc_pata.c @@ -709,7 +709,7 @@ static void scc_tf_read(ide_drive_t *drive, struct ide_cmd *cmd) } /* be sure we're looking at the low order bits */ - scc_ide_outb(ATA_DEVCTL_OBS & ~0x80, io_ports->ctl_addr); + scc_ide_outb(ATA_DEVCTL_OBS, io_ports->ctl_addr); if (cmd->tf_flags & IDE_TFLAG_IN_FEATURE) tf->feature = scc_ide_inb(io_ports->feature_addr); @@ -725,7 +725,7 @@ static void scc_tf_read(ide_drive_t *drive, struct ide_cmd *cmd) tf->device = scc_ide_inb(io_ports->device_addr); if (cmd->tf_flags & IDE_TFLAG_LBA48) { - scc_ide_outb(ATA_DEVCTL_OBS | 0x80, io_ports->ctl_addr); + scc_ide_outb(ATA_HOB | ATA_DEVCTL_OBS, io_ports->ctl_addr); if (cmd->tf_flags & IDE_TFLAG_IN_HOB_FEATURE) tf->hob_feature = scc_ide_inb(io_ports->feature_addr); diff --git a/drivers/ide/tx4938ide.c b/drivers/ide/tx4938ide.c index 657a61890b1c..1c4a78ac1a20 100644 --- a/drivers/ide/tx4938ide.c +++ b/drivers/ide/tx4938ide.c @@ -142,7 +142,7 @@ static void tx4938ide_tf_read(ide_drive_t *drive, struct ide_cmd *cmd) } /* be sure we're looking at the low order bits */ - tx4938ide_outb(ATA_DEVCTL_OBS & ~0x80, io_ports->ctl_addr); + tx4938ide_outb(ATA_DEVCTL_OBS, io_ports->ctl_addr); if (cmd->tf_flags & IDE_TFLAG_IN_FEATURE) tf->feature = tx4938ide_inb(io_ports->feature_addr); @@ -158,7 +158,7 @@ static void tx4938ide_tf_read(ide_drive_t *drive, struct ide_cmd *cmd) tf->device = tx4938ide_inb(io_ports->device_addr); if (cmd->tf_flags & IDE_TFLAG_LBA48) { - tx4938ide_outb(ATA_DEVCTL_OBS | 0x80, io_ports->ctl_addr); + tx4938ide_outb(ATA_HOB | ATA_DEVCTL_OBS, io_ports->ctl_addr); if (cmd->tf_flags & IDE_TFLAG_IN_HOB_FEATURE) tf->hob_feature = diff --git a/drivers/ide/tx4939ide.c b/drivers/ide/tx4939ide.c index faf0d97b9ce8..77aee5b2ce95 100644 --- a/drivers/ide/tx4939ide.c +++ b/drivers/ide/tx4939ide.c @@ -509,7 +509,7 @@ static void tx4939ide_tf_read(ide_drive_t *drive, struct ide_cmd *cmd) } /* be sure we're looking at the low order bits */ - tx4939ide_outb(ATA_DEVCTL_OBS & ~0x80, io_ports->ctl_addr); + tx4939ide_outb(ATA_DEVCTL_OBS, io_ports->ctl_addr); if (cmd->tf_flags & IDE_TFLAG_IN_FEATURE) tf->feature = tx4939ide_inb(io_ports->feature_addr); @@ -525,7 +525,7 @@ static void tx4939ide_tf_read(ide_drive_t *drive, struct ide_cmd *cmd) tf->device = tx4939ide_inb(io_ports->device_addr); if (cmd->tf_flags & IDE_TFLAG_LBA48) { - tx4939ide_outb(ATA_DEVCTL_OBS | 0x80, io_ports->ctl_addr); + tx4939ide_outb(ATA_HOB | ATA_DEVCTL_OBS, io_ports->ctl_addr); if (cmd->tf_flags & IDE_TFLAG_IN_HOB_FEATURE) tf->hob_feature = -- cgit v1.2.3 From ecf3a31d2a08a419bdf919456f1724f5b72bde2c Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Tue, 31 Mar 2009 20:15:30 +0200 Subject: ide: turn set_irq() method into write_devctl() method Turn set_irq() method with its software reset hack into write_devctl() method (for just writing a value into the device control register) at last... Signed-off-by: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/at91_ide.c | 2 +- drivers/ide/au1xxx-ide.c | 3 +-- drivers/ide/falconide.c | 3 +-- drivers/ide/ide-eh.c | 7 +++---- drivers/ide/ide-h8300.c | 3 +-- drivers/ide/ide-io-std.c | 16 +++------------- drivers/ide/ide-io.c | 4 +++- drivers/ide/ide-iops.c | 4 ++-- drivers/ide/ide-pm.c | 2 +- drivers/ide/ide-probe.c | 6 +++--- drivers/ide/ide-taskfile.c | 2 +- drivers/ide/ns87415.c | 3 +-- drivers/ide/pmac.c | 14 ++------------ drivers/ide/q40ide.c | 3 +-- drivers/ide/scc_pata.c | 14 ++------------ drivers/ide/sgiioc4.c | 3 +-- drivers/ide/tx4938ide.c | 3 +-- drivers/ide/tx4939ide.c | 6 ++---- include/linux/ide.h | 6 ++---- 19 files changed, 32 insertions(+), 72 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/at91_ide.c b/drivers/ide/at91_ide.c index 8fc6ae958b0b..e6e96743aa7b 100644 --- a/drivers/ide/at91_ide.c +++ b/drivers/ide/at91_ide.c @@ -295,7 +295,7 @@ static const struct ide_tp_ops at91_ide_tp_ops = { .exec_command = ide_exec_command, .read_status = ide_read_status, .read_altstatus = ide_read_altstatus, - .set_irq = ide_set_irq, + .write_devctl = ide_write_devctl, .tf_load = at91_ide_tf_load, .tf_read = at91_ide_tf_read, diff --git a/drivers/ide/au1xxx-ide.c b/drivers/ide/au1xxx-ide.c index 1bfb43d0d3a8..2ca10d533dad 100644 --- a/drivers/ide/au1xxx-ide.c +++ b/drivers/ide/au1xxx-ide.c @@ -467,8 +467,7 @@ static const struct ide_tp_ops au1xxx_tp_ops = { .exec_command = ide_exec_command, .read_status = ide_read_status, .read_altstatus = ide_read_altstatus, - - .set_irq = ide_set_irq, + .write_devctl = ide_write_devctl, .tf_load = ide_tf_load, .tf_read = ide_tf_read, diff --git a/drivers/ide/falconide.c b/drivers/ide/falconide.c index b368a5effc3a..5063be85dc33 100644 --- a/drivers/ide/falconide.c +++ b/drivers/ide/falconide.c @@ -89,8 +89,7 @@ static const struct ide_tp_ops falconide_tp_ops = { .exec_command = ide_exec_command, .read_status = ide_read_status, .read_altstatus = ide_read_altstatus, - - .set_irq = ide_set_irq, + .write_devctl = ide_write_devctl, .tf_load = ide_tf_load, .tf_read = ide_tf_read, diff --git a/drivers/ide/ide-eh.c b/drivers/ide/ide-eh.c index 11664976eea3..de4b7f1c9c9f 100644 --- a/drivers/ide/ide-eh.c +++ b/drivers/ide/ide-eh.c @@ -401,15 +401,14 @@ static ide_startstop_t do_reset1(ide_drive_t *drive, int do_not_try_atapi) * immediate interrupt due to the edge transition it produces. * This single interrupt gives us a "fast poll" for drives that * recover from reset very quickly, saving us the first 50ms wait time. - * - * TODO: add ->softreset method and stop abusing ->set_irq */ /* set SRST and nIEN */ - tp_ops->set_irq(hwif, 4); + tp_ops->write_devctl(hwif, ATA_SRST | ATA_NIEN | ATA_DEVCTL_OBS); /* more than enough time */ udelay(10); /* clear SRST, leave nIEN (unless device is on the quirk list) */ - tp_ops->set_irq(hwif, drive->quirk_list == 2); + tp_ops->write_devctl(hwif, (drive->quirk_list == 2 ? 0 : ATA_NIEN) | + ATA_DEVCTL_OBS); /* more than enough time */ udelay(10); hwif->poll_timeout = jiffies + WAIT_WORSTCASE; diff --git a/drivers/ide/ide-h8300.c b/drivers/ide/ide-h8300.c index 7492f28d1290..a57ccad61acf 100644 --- a/drivers/ide/ide-h8300.c +++ b/drivers/ide/ide-h8300.c @@ -159,8 +159,7 @@ static const struct ide_tp_ops h8300_tp_ops = { .exec_command = ide_exec_command, .read_status = ide_read_status, .read_altstatus = ide_read_altstatus, - - .set_irq = ide_set_irq, + .write_devctl = ide_write_devctl, .tf_load = h8300_tf_load, .tf_read = h8300_tf_read, diff --git a/drivers/ide/ide-io-std.c b/drivers/ide/ide-io-std.c index 3a867e49a0af..bbeedce6b17d 100644 --- a/drivers/ide/ide-io-std.c +++ b/drivers/ide/ide-io-std.c @@ -64,23 +64,14 @@ u8 ide_read_altstatus(ide_hwif_t *hwif) } EXPORT_SYMBOL_GPL(ide_read_altstatus); -void ide_set_irq(ide_hwif_t *hwif, int on) +void ide_write_devctl(ide_hwif_t *hwif, u8 ctl) { - u8 ctl = ATA_DEVCTL_OBS; - - if (on == 4) { /* hack for SRST */ - ctl |= 4; - on &= ~4; - } - - ctl |= on ? 0 : 2; - if (hwif->host_flags & IDE_HFLAG_MMIO) writeb(ctl, (void __iomem *)hwif->io_ports.ctl_addr); else outb(ctl, hwif->io_ports.ctl_addr); } -EXPORT_SYMBOL_GPL(ide_set_irq); +EXPORT_SYMBOL_GPL(ide_write_devctl); void ide_tf_load(ide_drive_t *drive, struct ide_cmd *cmd) { @@ -312,8 +303,7 @@ const struct ide_tp_ops default_tp_ops = { .exec_command = ide_exec_command, .read_status = ide_read_status, .read_altstatus = ide_read_altstatus, - - .set_irq = ide_set_irq, + .write_devctl = ide_write_devctl, .tf_load = ide_tf_load, .tf_read = ide_tf_read, diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index 3c52317d8524..5b57905a7d71 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -494,7 +494,9 @@ repeat: * quirk_list may not like intr setups/cleanups */ if (prev_port && prev_port->cur_dev->quirk_list == 0) - prev_port->tp_ops->set_irq(prev_port, 0); + prev_port->tp_ops->write_devctl(prev_port, + ATA_NIEN | + ATA_DEVCTL_OBS); hwif->host->cur_port = hwif; } diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index 0caca342802d..ae227dd8466f 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -360,7 +360,7 @@ int ide_config_drive_speed(ide_drive_t *drive, u8 speed) SELECT_DRIVE(drive); SELECT_MASK(drive, 1); udelay(1); - tp_ops->set_irq(hwif, 0); + tp_ops->write_devctl(hwif, ATA_NIEN | ATA_DEVCTL_OBS); memset(&cmd, 0, sizeof(cmd)); cmd.tf_flags = IDE_TFLAG_OUT_FEATURE | IDE_TFLAG_OUT_NSECT; @@ -372,7 +372,7 @@ int ide_config_drive_speed(ide_drive_t *drive, u8 speed) tp_ops->exec_command(hwif, ATA_CMD_SET_FEATURES); if (drive->quirk_list == 2) - tp_ops->set_irq(hwif, 1); + tp_ops->write_devctl(hwif, ATA_DEVCTL_OBS); error = __ide_wait_stat(drive, drive->ready_stat, ATA_BUSY | ATA_DRQ | ATA_ERR, diff --git a/drivers/ide/ide-pm.c b/drivers/ide/ide-pm.c index ebf2d21ebdcb..20553d4c42a2 100644 --- a/drivers/ide/ide-pm.c +++ b/drivers/ide/ide-pm.c @@ -233,7 +233,7 @@ void ide_check_pm_state(ide_drive_t *drive, struct request *rq) if (rc) printk(KERN_WARNING "%s: bus not ready on wakeup\n", drive->name); SELECT_DRIVE(drive); - hwif->tp_ops->set_irq(hwif, 1); + hwif->tp_ops->write_devctl(hwif, ATA_DEVCTL_OBS); rc = ide_wait_not_busy(hwif, 100000); if (rc) printk(KERN_WARNING "%s: drive not ready on wakeup\n", drive->name); diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 7c1f1bf81836..d240f76b0da6 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -260,7 +260,7 @@ int ide_dev_read_id(ide_drive_t *drive, u8 cmd, u16 *id) * during the identify phase that the IRQ handler isn't expecting. */ if (io_ports->ctl_addr) - tp_ops->set_irq(hwif, 0); + tp_ops->write_devctl(hwif, ATA_NIEN | ATA_DEVCTL_OBS); /* take a deep breath */ msleep(50); @@ -628,7 +628,7 @@ static int ide_port_wait_ready(ide_hwif_t *hwif) if ((drive->dev_flags & IDE_DFLAG_NOPROBE) == 0 || (drive->dev_flags & IDE_DFLAG_PRESENT)) { SELECT_DRIVE(drive); - hwif->tp_ops->set_irq(hwif, 1); + hwif->tp_ops->write_devctl(hwif, ATA_DEVCTL_OBS); mdelay(2); rc = ide_wait_not_busy(hwif, 35000); if (rc) @@ -845,7 +845,7 @@ static int init_irq (ide_hwif_t *hwif) irq_handler = ide_intr; if (io_ports->ctl_addr) - hwif->tp_ops->set_irq(hwif, 1); + hwif->tp_ops->write_devctl(hwif, ATA_DEVCTL_OBS); if (request_irq(hwif->irq, irq_handler, sa, hwif->name, hwif)) goto out_up; diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c index dba68db629bf..47f13cd11031 100644 --- a/drivers/ide/ide-taskfile.c +++ b/drivers/ide/ide-taskfile.c @@ -80,7 +80,7 @@ ide_startstop_t do_rw_taskfile(ide_drive_t *drive, struct ide_cmd *orig_cmd) if ((cmd->tf_flags & IDE_TFLAG_DMA_PIO_FALLBACK) == 0) { ide_tf_dump(drive->name, tf); - tp_ops->set_irq(hwif, 1); + tp_ops->write_devctl(hwif, ATA_DEVCTL_OBS); SELECT_MASK(drive, 0); tp_ops->tf_load(drive, cmd); } diff --git a/drivers/ide/ns87415.c b/drivers/ide/ns87415.c index 13a9e00efa13..00ab0be7335a 100644 --- a/drivers/ide/ns87415.c +++ b/drivers/ide/ns87415.c @@ -109,8 +109,7 @@ static const struct ide_tp_ops superio_tp_ops = { .exec_command = ide_exec_command, .read_status = superio_read_status, .read_altstatus = ide_read_altstatus, - - .set_irq = ide_set_irq, + .write_devctl = ide_write_devctl, .tf_load = ide_tf_load, .tf_read = superio_tf_read, diff --git a/drivers/ide/pmac.c b/drivers/ide/pmac.c index 879c3d8d9f36..7aa45ea37eeb 100644 --- a/drivers/ide/pmac.c +++ b/drivers/ide/pmac.c @@ -476,17 +476,8 @@ static void pmac_exec_command(ide_hwif_t *hwif, u8 cmd) + IDE_TIMING_CONFIG)); } -static void pmac_set_irq(ide_hwif_t *hwif, int on) +static void pmac_write_devctl(ide_hwif_t *hwif, u8 ctl) { - u8 ctl = ATA_DEVCTL_OBS; - - if (on == 4) { /* hack for SRST */ - ctl |= 4; - on &= ~4; - } - - ctl |= on ? 0 : 2; - writeb(ctl, (void __iomem *)hwif->io_ports.ctl_addr); (void)readl((void __iomem *)(hwif->io_ports.data_addr + IDE_TIMING_CONFIG)); @@ -954,8 +945,7 @@ static const struct ide_tp_ops pmac_tp_ops = { .exec_command = pmac_exec_command, .read_status = ide_read_status, .read_altstatus = ide_read_altstatus, - - .set_irq = pmac_set_irq, + .write_devctl = pmac_write_devctl, .tf_load = ide_tf_load, .tf_read = ide_tf_read, diff --git a/drivers/ide/q40ide.c b/drivers/ide/q40ide.c index 2a43a2f49633..7fddfd34fcce 100644 --- a/drivers/ide/q40ide.c +++ b/drivers/ide/q40ide.c @@ -99,8 +99,7 @@ static const struct ide_tp_ops q40ide_tp_ops = { .exec_command = ide_exec_command, .read_status = ide_read_status, .read_altstatus = ide_read_altstatus, - - .set_irq = ide_set_irq, + .write_devctl = ide_write_devctl, .tf_load = ide_tf_load, .tf_read = ide_tf_read, diff --git a/drivers/ide/scc_pata.c b/drivers/ide/scc_pata.c index 6e47eac1cd7f..6ba4983d831c 100644 --- a/drivers/ide/scc_pata.c +++ b/drivers/ide/scc_pata.c @@ -148,17 +148,8 @@ static u8 scc_dma_sff_read_status(ide_hwif_t *hwif) return (u8)in_be32((void *)(hwif->dma_base + 4)); } -static void scc_set_irq(ide_hwif_t *hwif, int on) +static void scc_write_devctl(ide_hwif_t *hwif, u8 ctl) { - u8 ctl = ATA_DEVCTL_OBS; - - if (on == 4) { /* hack for SRST */ - ctl |= 4; - on &= ~4; - } - - ctl |= on ? 0 : 2; - out_be32((void *)hwif->io_ports.ctl_addr, ctl); eieio(); in_be32((void *)(hwif->dma_base + 0x01c)); @@ -843,8 +834,7 @@ static const struct ide_tp_ops scc_tp_ops = { .exec_command = scc_exec_command, .read_status = scc_read_status, .read_altstatus = scc_read_altstatus, - - .set_irq = scc_set_irq, + .write_devctl = scc_write_devctl, .tf_load = scc_tf_load, .tf_read = scc_tf_read, diff --git a/drivers/ide/sgiioc4.c b/drivers/ide/sgiioc4.c index 6ef5a567d377..58980fcafc3b 100644 --- a/drivers/ide/sgiioc4.c +++ b/drivers/ide/sgiioc4.c @@ -503,8 +503,7 @@ static const struct ide_tp_ops sgiioc4_tp_ops = { .exec_command = ide_exec_command, .read_status = sgiioc4_read_status, .read_altstatus = ide_read_altstatus, - - .set_irq = ide_set_irq, + .write_devctl = ide_write_devctl, .tf_load = ide_tf_load, .tf_read = ide_tf_read, diff --git a/drivers/ide/tx4938ide.c b/drivers/ide/tx4938ide.c index 1c4a78ac1a20..ec3aa32fbbe0 100644 --- a/drivers/ide/tx4938ide.c +++ b/drivers/ide/tx4938ide.c @@ -204,8 +204,7 @@ static const struct ide_tp_ops tx4938ide_tp_ops = { .exec_command = ide_exec_command, .read_status = ide_read_status, .read_altstatus = ide_read_altstatus, - - .set_irq = ide_set_irq, + .write_devctl = ide_write_devctl, .tf_load = tx4938ide_tf_load, .tf_read = tx4938ide_tf_read, diff --git a/drivers/ide/tx4939ide.c b/drivers/ide/tx4939ide.c index 77aee5b2ce95..43bc0372413a 100644 --- a/drivers/ide/tx4939ide.c +++ b/drivers/ide/tx4939ide.c @@ -571,8 +571,7 @@ static const struct ide_tp_ops tx4939ide_tp_ops = { .exec_command = ide_exec_command, .read_status = ide_read_status, .read_altstatus = ide_read_altstatus, - - .set_irq = ide_set_irq, + .write_devctl = ide_write_devctl, .tf_load = tx4939ide_tf_load, .tf_read = tx4939ide_tf_read, @@ -595,8 +594,7 @@ static const struct ide_tp_ops tx4939ide_tp_ops = { .exec_command = ide_exec_command, .read_status = ide_read_status, .read_altstatus = ide_read_altstatus, - - .set_irq = ide_set_irq, + .write_devctl = ide_write_devctl, .tf_load = tx4939ide_tf_load, .tf_read = ide_tf_read, diff --git a/include/linux/ide.h b/include/linux/ide.h index 836c4c6cb7e3..ccb70abe991b 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -655,8 +655,7 @@ struct ide_tp_ops { void (*exec_command)(struct hwif_s *, u8); u8 (*read_status)(struct hwif_s *); u8 (*read_altstatus)(struct hwif_s *); - - void (*set_irq)(struct hwif_s *, int); + void (*write_devctl)(struct hwif_s *, u8); void (*tf_load)(ide_drive_t *, struct ide_cmd *); void (*tf_read)(ide_drive_t *, struct ide_cmd *); @@ -1165,8 +1164,7 @@ void ide_tf_dump(const char *, struct ide_taskfile *); void ide_exec_command(ide_hwif_t *, u8); u8 ide_read_status(ide_hwif_t *); u8 ide_read_altstatus(ide_hwif_t *); - -void ide_set_irq(ide_hwif_t *, int); +void ide_write_devctl(ide_hwif_t *, u8); void ide_tf_load(ide_drive_t *, struct ide_cmd *); void ide_tf_read(ide_drive_t *, struct ide_cmd *); -- cgit v1.2.3 From 6762511934e6e7287ce3c8baac0d52ef64e3787b Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Tue, 31 Mar 2009 20:15:30 +0200 Subject: ide: rename IDE_TFLAG_IN_[HOB_]FEATURE The feature register has never been readable -- when its location is read, one gets the error register value; hence rename IDE_TFLAG_IN_[HOB_]FEATURE into IDE_TFLAG_IN_[HOB_]ERROR and introduce the 'hob_error' field into the 'struct ide_taskfile' (despite the error register not really depending on the HOB bit). Signed-off-by: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/at91_ide.c | 16 ++++++++-------- drivers/ide/ide-h8300.c | 16 ++++++++-------- drivers/ide/ide-io-std.c | 16 ++++++++-------- drivers/ide/ide-iops.c | 2 +- drivers/ide/ns87415.c | 16 ++++++++-------- drivers/ide/scc_pata.c | 16 ++++++++-------- drivers/ide/tx4938ide.c | 17 ++++++++--------- drivers/ide/tx4939ide.c | 17 ++++++++--------- include/linux/ide.h | 12 ++++++++---- 9 files changed, 65 insertions(+), 63 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/at91_ide.c b/drivers/ide/at91_ide.c index e6e96743aa7b..9dce793d93b4 100644 --- a/drivers/ide/at91_ide.c +++ b/drivers/ide/at91_ide.c @@ -244,8 +244,8 @@ static void at91_ide_tf_read(ide_drive_t *drive, struct ide_cmd *cmd) /* be sure we're looking at the low order bits */ ide_mm_outb(ATA_DEVCTL_OBS, io_ports->ctl_addr); - if (cmd->tf_flags & IDE_TFLAG_IN_FEATURE) - tf->feature = ide_mm_inb(io_ports->feature_addr); + if (cmd->tf_flags & IDE_TFLAG_IN_ERROR) + tf->error = ide_mm_inb(io_ports->feature_addr); if (cmd->tf_flags & IDE_TFLAG_IN_NSECT) tf->nsect = ide_mm_inb(io_ports->nsect_addr); if (cmd->tf_flags & IDE_TFLAG_IN_LBAL) @@ -260,16 +260,16 @@ static void at91_ide_tf_read(ide_drive_t *drive, struct ide_cmd *cmd) if (cmd->tf_flags & IDE_TFLAG_LBA48) { ide_mm_outb(ATA_HOB | ATA_DEVCTL_OBS, io_ports->ctl_addr); - if (cmd->tf_flags & IDE_TFLAG_IN_HOB_FEATURE) - tf->hob_feature = ide_mm_inb(io_ports->feature_addr); + if (cmd->tf_flags & IDE_TFLAG_IN_HOB_ERROR) + tf->hob_error = ide_mm_inb(io_ports->feature_addr); if (cmd->tf_flags & IDE_TFLAG_IN_HOB_NSECT) - tf->hob_nsect = ide_mm_inb(io_ports->nsect_addr); + tf->hob_nsect = ide_mm_inb(io_ports->nsect_addr); if (cmd->tf_flags & IDE_TFLAG_IN_HOB_LBAL) - tf->hob_lbal = ide_mm_inb(io_ports->lbal_addr); + tf->hob_lbal = ide_mm_inb(io_ports->lbal_addr); if (cmd->tf_flags & IDE_TFLAG_IN_HOB_LBAM) - tf->hob_lbam = ide_mm_inb(io_ports->lbam_addr); + tf->hob_lbam = ide_mm_inb(io_ports->lbam_addr); if (cmd->tf_flags & IDE_TFLAG_IN_HOB_LBAH) - tf->hob_lbah = ide_mm_inb(io_ports->lbah_addr); + tf->hob_lbah = ide_mm_inb(io_ports->lbah_addr); } } diff --git a/drivers/ide/ide-h8300.c b/drivers/ide/ide-h8300.c index a57ccad61acf..1d45cd5b6a1c 100644 --- a/drivers/ide/ide-h8300.c +++ b/drivers/ide/ide-h8300.c @@ -100,8 +100,8 @@ static void h8300_tf_read(ide_drive_t *drive, struct ide_cmd *cmd) /* be sure we're looking at the low order bits */ outb(ATA_DEVCTL_OBS, io_ports->ctl_addr); - if (cmd->tf_flags & IDE_TFLAG_IN_FEATURE) - tf->feature = inb(io_ports->feature_addr); + if (cmd->tf_flags & IDE_TFLAG_IN_ERROR) + tf->error = inb(io_ports->feature_addr); if (cmd->tf_flags & IDE_TFLAG_IN_NSECT) tf->nsect = inb(io_ports->nsect_addr); if (cmd->tf_flags & IDE_TFLAG_IN_LBAL) @@ -116,16 +116,16 @@ static void h8300_tf_read(ide_drive_t *drive, struct ide_cmd *cmd) if (cmd->tf_flags & IDE_TFLAG_LBA48) { outb(ATA_HOB | ATA_DEVCTL_OBS, io_ports->ctl_addr); - if (cmd->tf_flags & IDE_TFLAG_IN_HOB_FEATURE) - tf->hob_feature = inb(io_ports->feature_addr); + if (cmd->tf_flags & IDE_TFLAG_IN_HOB_ERROR) + tf->hob_error = inb(io_ports->feature_addr); if (cmd->tf_flags & IDE_TFLAG_IN_HOB_NSECT) - tf->hob_nsect = inb(io_ports->nsect_addr); + tf->hob_nsect = inb(io_ports->nsect_addr); if (cmd->tf_flags & IDE_TFLAG_IN_HOB_LBAL) - tf->hob_lbal = inb(io_ports->lbal_addr); + tf->hob_lbal = inb(io_ports->lbal_addr); if (cmd->tf_flags & IDE_TFLAG_IN_HOB_LBAM) - tf->hob_lbam = inb(io_ports->lbam_addr); + tf->hob_lbam = inb(io_ports->lbam_addr); if (cmd->tf_flags & IDE_TFLAG_IN_HOB_LBAH) - tf->hob_lbah = inb(io_ports->lbah_addr); + tf->hob_lbah = inb(io_ports->lbah_addr); } } diff --git a/drivers/ide/ide-io-std.c b/drivers/ide/ide-io-std.c index bbeedce6b17d..31f5c5f4c093 100644 --- a/drivers/ide/ide-io-std.c +++ b/drivers/ide/ide-io-std.c @@ -159,8 +159,8 @@ void ide_tf_read(ide_drive_t *drive, struct ide_cmd *cmd) /* be sure we're looking at the low order bits */ tf_outb(ATA_DEVCTL_OBS, io_ports->ctl_addr); - if (cmd->tf_flags & IDE_TFLAG_IN_FEATURE) - tf->feature = tf_inb(io_ports->feature_addr); + if (cmd->tf_flags & IDE_TFLAG_IN_ERROR) + tf->error = tf_inb(io_ports->feature_addr); if (cmd->tf_flags & IDE_TFLAG_IN_NSECT) tf->nsect = tf_inb(io_ports->nsect_addr); if (cmd->tf_flags & IDE_TFLAG_IN_LBAL) @@ -175,16 +175,16 @@ void ide_tf_read(ide_drive_t *drive, struct ide_cmd *cmd) if (cmd->tf_flags & IDE_TFLAG_LBA48) { tf_outb(ATA_HOB | ATA_DEVCTL_OBS, io_ports->ctl_addr); - if (cmd->tf_flags & IDE_TFLAG_IN_HOB_FEATURE) - tf->hob_feature = tf_inb(io_ports->feature_addr); + if (cmd->tf_flags & IDE_TFLAG_IN_HOB_ERROR) + tf->hob_error = tf_inb(io_ports->feature_addr); if (cmd->tf_flags & IDE_TFLAG_IN_HOB_NSECT) - tf->hob_nsect = tf_inb(io_ports->nsect_addr); + tf->hob_nsect = tf_inb(io_ports->nsect_addr); if (cmd->tf_flags & IDE_TFLAG_IN_HOB_LBAL) - tf->hob_lbal = tf_inb(io_ports->lbal_addr); + tf->hob_lbal = tf_inb(io_ports->lbal_addr); if (cmd->tf_flags & IDE_TFLAG_IN_HOB_LBAM) - tf->hob_lbam = tf_inb(io_ports->lbam_addr); + tf->hob_lbam = tf_inb(io_ports->lbam_addr); if (cmd->tf_flags & IDE_TFLAG_IN_HOB_LBAH) - tf->hob_lbah = tf_inb(io_ports->lbah_addr); + tf->hob_lbah = tf_inb(io_ports->lbah_addr); } } EXPORT_SYMBOL_GPL(ide_tf_read); diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index ae227dd8466f..6f363a26700d 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -55,7 +55,7 @@ u8 ide_read_error(ide_drive_t *drive) struct ide_cmd cmd; memset(&cmd, 0, sizeof(cmd)); - cmd.tf_flags = IDE_TFLAG_IN_FEATURE; + cmd.tf_flags = IDE_TFLAG_IN_ERROR; drive->hwif->tp_ops->tf_read(drive, &cmd); diff --git a/drivers/ide/ns87415.c b/drivers/ide/ns87415.c index 00ab0be7335a..0a6cf74c3265 100644 --- a/drivers/ide/ns87415.c +++ b/drivers/ide/ns87415.c @@ -76,8 +76,8 @@ static void superio_tf_read(ide_drive_t *drive, struct ide_cmd *cmd) /* be sure we're looking at the low order bits */ outb(ATA_DEVCTL_OBS, io_ports->ctl_addr); - if (cmd->tf_flags & IDE_TFLAG_IN_FEATURE) - tf->feature = inb(io_ports->feature_addr); + if (cmd->tf_flags & IDE_TFLAG_IN_ERROR) + tf->error = inb(io_ports->feature_addr); if (cmd->tf_flags & IDE_TFLAG_IN_NSECT) tf->nsect = inb(io_ports->nsect_addr); if (cmd->tf_flags & IDE_TFLAG_IN_LBAL) @@ -92,16 +92,16 @@ static void superio_tf_read(ide_drive_t *drive, struct ide_cmd *cmd) if (cmd->tf_flags & IDE_TFLAG_LBA48) { outb(ATA_HOB | ATA_DEVCTL_OBS, io_ports->ctl_addr); - if (cmd->tf_flags & IDE_TFLAG_IN_HOB_FEATURE) - tf->hob_feature = inb(io_ports->feature_addr); + if (cmd->tf_flags & IDE_TFLAG_IN_HOB_ERROR) + tf->hob_error = inb(io_ports->feature_addr); if (cmd->tf_flags & IDE_TFLAG_IN_HOB_NSECT) - tf->hob_nsect = inb(io_ports->nsect_addr); + tf->hob_nsect = inb(io_ports->nsect_addr); if (cmd->tf_flags & IDE_TFLAG_IN_HOB_LBAL) - tf->hob_lbal = inb(io_ports->lbal_addr); + tf->hob_lbal = inb(io_ports->lbal_addr); if (cmd->tf_flags & IDE_TFLAG_IN_HOB_LBAM) - tf->hob_lbam = inb(io_ports->lbam_addr); + tf->hob_lbam = inb(io_ports->lbam_addr); if (cmd->tf_flags & IDE_TFLAG_IN_HOB_LBAH) - tf->hob_lbah = inb(io_ports->lbah_addr); + tf->hob_lbah = inb(io_ports->lbah_addr); } } diff --git a/drivers/ide/scc_pata.c b/drivers/ide/scc_pata.c index 6ba4983d831c..ea0a9752c6f9 100644 --- a/drivers/ide/scc_pata.c +++ b/drivers/ide/scc_pata.c @@ -702,8 +702,8 @@ static void scc_tf_read(ide_drive_t *drive, struct ide_cmd *cmd) /* be sure we're looking at the low order bits */ scc_ide_outb(ATA_DEVCTL_OBS, io_ports->ctl_addr); - if (cmd->tf_flags & IDE_TFLAG_IN_FEATURE) - tf->feature = scc_ide_inb(io_ports->feature_addr); + if (cmd->tf_flags & IDE_TFLAG_IN_ERROR) + tf->error = scc_ide_inb(io_ports->feature_addr); if (cmd->tf_flags & IDE_TFLAG_IN_NSECT) tf->nsect = scc_ide_inb(io_ports->nsect_addr); if (cmd->tf_flags & IDE_TFLAG_IN_LBAL) @@ -718,16 +718,16 @@ static void scc_tf_read(ide_drive_t *drive, struct ide_cmd *cmd) if (cmd->tf_flags & IDE_TFLAG_LBA48) { scc_ide_outb(ATA_HOB | ATA_DEVCTL_OBS, io_ports->ctl_addr); - if (cmd->tf_flags & IDE_TFLAG_IN_HOB_FEATURE) - tf->hob_feature = scc_ide_inb(io_ports->feature_addr); + if (cmd->tf_flags & IDE_TFLAG_IN_HOB_ERROR) + tf->hob_error = scc_ide_inb(io_ports->feature_addr); if (cmd->tf_flags & IDE_TFLAG_IN_HOB_NSECT) - tf->hob_nsect = scc_ide_inb(io_ports->nsect_addr); + tf->hob_nsect = scc_ide_inb(io_ports->nsect_addr); if (cmd->tf_flags & IDE_TFLAG_IN_HOB_LBAL) - tf->hob_lbal = scc_ide_inb(io_ports->lbal_addr); + tf->hob_lbal = scc_ide_inb(io_ports->lbal_addr); if (cmd->tf_flags & IDE_TFLAG_IN_HOB_LBAM) - tf->hob_lbam = scc_ide_inb(io_ports->lbam_addr); + tf->hob_lbam = scc_ide_inb(io_ports->lbam_addr); if (cmd->tf_flags & IDE_TFLAG_IN_HOB_LBAH) - tf->hob_lbah = scc_ide_inb(io_ports->lbah_addr); + tf->hob_lbah = scc_ide_inb(io_ports->lbah_addr); } } diff --git a/drivers/ide/tx4938ide.c b/drivers/ide/tx4938ide.c index ec3aa32fbbe0..606c37f5267d 100644 --- a/drivers/ide/tx4938ide.c +++ b/drivers/ide/tx4938ide.c @@ -144,8 +144,8 @@ static void tx4938ide_tf_read(ide_drive_t *drive, struct ide_cmd *cmd) /* be sure we're looking at the low order bits */ tx4938ide_outb(ATA_DEVCTL_OBS, io_ports->ctl_addr); - if (cmd->tf_flags & IDE_TFLAG_IN_FEATURE) - tf->feature = tx4938ide_inb(io_ports->feature_addr); + if (cmd->tf_flags & IDE_TFLAG_IN_ERROR) + tf->error = tx4938ide_inb(io_ports->feature_addr); if (cmd->tf_flags & IDE_TFLAG_IN_NSECT) tf->nsect = tx4938ide_inb(io_ports->nsect_addr); if (cmd->tf_flags & IDE_TFLAG_IN_LBAL) @@ -160,17 +160,16 @@ static void tx4938ide_tf_read(ide_drive_t *drive, struct ide_cmd *cmd) if (cmd->tf_flags & IDE_TFLAG_LBA48) { tx4938ide_outb(ATA_HOB | ATA_DEVCTL_OBS, io_ports->ctl_addr); - if (cmd->tf_flags & IDE_TFLAG_IN_HOB_FEATURE) - tf->hob_feature = - tx4938ide_inb(io_ports->feature_addr); + if (cmd->tf_flags & IDE_TFLAG_IN_HOB_ERROR) + tf->hob_error = tx4938ide_inb(io_ports->feature_addr); if (cmd->tf_flags & IDE_TFLAG_IN_HOB_NSECT) - tf->hob_nsect = tx4938ide_inb(io_ports->nsect_addr); + tf->hob_nsect = tx4938ide_inb(io_ports->nsect_addr); if (cmd->tf_flags & IDE_TFLAG_IN_HOB_LBAL) - tf->hob_lbal = tx4938ide_inb(io_ports->lbal_addr); + tf->hob_lbal = tx4938ide_inb(io_ports->lbal_addr); if (cmd->tf_flags & IDE_TFLAG_IN_HOB_LBAM) - tf->hob_lbam = tx4938ide_inb(io_ports->lbam_addr); + tf->hob_lbam = tx4938ide_inb(io_ports->lbam_addr); if (cmd->tf_flags & IDE_TFLAG_IN_HOB_LBAH) - tf->hob_lbah = tx4938ide_inb(io_ports->lbah_addr); + tf->hob_lbah = tx4938ide_inb(io_ports->lbah_addr); } } diff --git a/drivers/ide/tx4939ide.c b/drivers/ide/tx4939ide.c index 43bc0372413a..f1e9da71110c 100644 --- a/drivers/ide/tx4939ide.c +++ b/drivers/ide/tx4939ide.c @@ -511,8 +511,8 @@ static void tx4939ide_tf_read(ide_drive_t *drive, struct ide_cmd *cmd) /* be sure we're looking at the low order bits */ tx4939ide_outb(ATA_DEVCTL_OBS, io_ports->ctl_addr); - if (cmd->tf_flags & IDE_TFLAG_IN_FEATURE) - tf->feature = tx4939ide_inb(io_ports->feature_addr); + if (cmd->tf_flags & IDE_TFLAG_IN_ERROR) + tf->error = tx4939ide_inb(io_ports->feature_addr); if (cmd->tf_flags & IDE_TFLAG_IN_NSECT) tf->nsect = tx4939ide_inb(io_ports->nsect_addr); if (cmd->tf_flags & IDE_TFLAG_IN_LBAL) @@ -527,17 +527,16 @@ static void tx4939ide_tf_read(ide_drive_t *drive, struct ide_cmd *cmd) if (cmd->tf_flags & IDE_TFLAG_LBA48) { tx4939ide_outb(ATA_HOB | ATA_DEVCTL_OBS, io_ports->ctl_addr); - if (cmd->tf_flags & IDE_TFLAG_IN_HOB_FEATURE) - tf->hob_feature = - tx4939ide_inb(io_ports->feature_addr); + if (cmd->tf_flags & IDE_TFLAG_IN_HOB_ERROR) + tf->hob_error = tx4939ide_inb(io_ports->feature_addr); if (cmd->tf_flags & IDE_TFLAG_IN_HOB_NSECT) - tf->hob_nsect = tx4939ide_inb(io_ports->nsect_addr); + tf->hob_nsect = tx4939ide_inb(io_ports->nsect_addr); if (cmd->tf_flags & IDE_TFLAG_IN_HOB_LBAL) - tf->hob_lbal = tx4939ide_inb(io_ports->lbal_addr); + tf->hob_lbal = tx4939ide_inb(io_ports->lbal_addr); if (cmd->tf_flags & IDE_TFLAG_IN_HOB_LBAM) - tf->hob_lbam = tx4939ide_inb(io_ports->lbam_addr); + tf->hob_lbam = tx4939ide_inb(io_ports->lbam_addr); if (cmd->tf_flags & IDE_TFLAG_IN_HOB_LBAH) - tf->hob_lbah = tx4939ide_inb(io_ports->lbah_addr); + tf->hob_lbah = tx4939ide_inb(io_ports->lbah_addr); } } diff --git a/include/linux/ide.h b/include/linux/ide.h index ccb70abe991b..e919c865f0c7 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -265,7 +265,7 @@ enum { IDE_TFLAG_WRITE = (1 << 12), IDE_TFLAG_CUSTOM_HANDLER = (1 << 13), IDE_TFLAG_DMA_PIO_FALLBACK = (1 << 14), - IDE_TFLAG_IN_HOB_FEATURE = (1 << 15), + IDE_TFLAG_IN_HOB_ERROR = (1 << 15), IDE_TFLAG_IN_HOB_NSECT = (1 << 16), IDE_TFLAG_IN_HOB_LBAL = (1 << 17), IDE_TFLAG_IN_HOB_LBAM = (1 << 18), @@ -273,10 +273,10 @@ enum { IDE_TFLAG_IN_HOB_LBA = IDE_TFLAG_IN_HOB_LBAL | IDE_TFLAG_IN_HOB_LBAM | IDE_TFLAG_IN_HOB_LBAH, - IDE_TFLAG_IN_HOB = IDE_TFLAG_IN_HOB_FEATURE | + IDE_TFLAG_IN_HOB = IDE_TFLAG_IN_HOB_ERROR | IDE_TFLAG_IN_HOB_NSECT | IDE_TFLAG_IN_HOB_LBA, - IDE_TFLAG_IN_FEATURE = (1 << 20), + IDE_TFLAG_IN_ERROR = (1 << 20), IDE_TFLAG_IN_NSECT = (1 << 21), IDE_TFLAG_IN_LBAL = (1 << 22), IDE_TFLAG_IN_LBAM = (1 << 23), @@ -310,8 +310,12 @@ enum { struct ide_taskfile { u8 hob_data; /* 0: high data byte (for TASKFILE IOCTL) */ + /* 1-5: additional data to support LBA48 */ + union { + u8 hob_error; /* read: error */ + u8 hob_feature; /* write: feature */ + }; - u8 hob_feature; /* 1-5: additional data to support LBA48 */ u8 hob_nsect; u8 hob_lbal; u8 hob_lbam; -- cgit v1.2.3 From deae17fd5d147ae65e277905343b7ea578574d12 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Tue, 31 Mar 2009 20:15:31 +0200 Subject: ide-io-std: shorten ide_{in|out}put_data() ide_{in|out|put_data() can be somewhat shortened by merging the paths doing 16-bit I/O... Signed-off-by: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-io-std.c | 60 ++++++++++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 32 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/ide-io-std.c b/drivers/ide/ide-io-std.c index 31f5c5f4c093..96a537da8925 100644 --- a/drivers/ide/ide-io-std.c +++ b/drivers/ide/ide-io-std.c @@ -216,11 +216,10 @@ void ide_input_data(ide_drive_t *drive, struct ide_cmd *cmd, void *buf, ide_hwif_t *hwif = drive->hwif; struct ide_io_ports *io_ports = &hwif->io_ports; unsigned long data_addr = io_ports->data_addr; + unsigned int words = (len + 1) >> 1; u8 io_32bit = drive->io_32bit; u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0; - len++; - if (io_32bit) { unsigned long uninitialized_var(flags); @@ -229,27 +228,26 @@ void ide_input_data(ide_drive_t *drive, struct ide_cmd *cmd, void *buf, ata_vlb_sync(io_ports->nsect_addr); } + words >>= 1; if (mmio) - __ide_mm_insl((void __iomem *)data_addr, buf, len / 4); + __ide_mm_insl((void __iomem *)data_addr, buf, words); else - insl(data_addr, buf, len / 4); + insl(data_addr, buf, words); if ((io_32bit & 2) && !mmio) local_irq_restore(flags); - if ((len & 3) >= 2) { - if (mmio) - __ide_mm_insw((void __iomem *)data_addr, - (u8 *)buf + (len & ~3), 1); - else - insw(data_addr, (u8 *)buf + (len & ~3), 1); - } - } else { - if (mmio) - __ide_mm_insw((void __iomem *)data_addr, buf, len / 2); - else - insw(data_addr, buf, len / 2); + if (((len + 1) & 3) < 2) + return; + + buf += len & ~3; + words = 1; } + + if (mmio) + __ide_mm_insw((void __iomem *)data_addr, buf, words); + else + insw(data_addr, buf, words); } EXPORT_SYMBOL_GPL(ide_input_data); @@ -262,11 +260,10 @@ void ide_output_data(ide_drive_t *drive, struct ide_cmd *cmd, void *buf, ide_hwif_t *hwif = drive->hwif; struct ide_io_ports *io_ports = &hwif->io_ports; unsigned long data_addr = io_ports->data_addr; + unsigned int words = (len + 1) >> 1; u8 io_32bit = drive->io_32bit; u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0; - len++; - if (io_32bit) { unsigned long uninitialized_var(flags); @@ -275,27 +272,26 @@ void ide_output_data(ide_drive_t *drive, struct ide_cmd *cmd, void *buf, ata_vlb_sync(io_ports->nsect_addr); } + words >>= 1; if (mmio) - __ide_mm_outsl((void __iomem *)data_addr, buf, len / 4); + __ide_mm_outsl((void __iomem *)data_addr, buf, words); else - outsl(data_addr, buf, len / 4); + outsl(data_addr, buf, words); if ((io_32bit & 2) && !mmio) local_irq_restore(flags); - if ((len & 3) >= 2) { - if (mmio) - __ide_mm_outsw((void __iomem *)data_addr, - (u8 *)buf + (len & ~3), 1); - else - outsw(data_addr, (u8 *)buf + (len & ~3), 1); - } - } else { - if (mmio) - __ide_mm_outsw((void __iomem *)data_addr, buf, len / 2); - else - outsw(data_addr, buf, len / 2); + if (((len + 1) & 3) < 2) + return; + + buf += len & ~3; + words = 1; } + + if (mmio) + __ide_mm_outsw((void __iomem *)data_addr, buf, words); + else + outsw(data_addr, buf, words); } EXPORT_SYMBOL_GPL(ide_output_data); -- cgit v1.2.3 From bac08cee93f9cb37b40ecfa8eaf1f6d8daf3909b Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Tue, 31 Mar 2009 20:15:31 +0200 Subject: ide: call {in|out}put_data() methods from tf_{read|load}() methods (take 2) Handle IDE_FTFLAG_{IN|OUT}_DATA flags in tf_{read|load}() methods by calling {in|out}put_data() methods to transfer 2 bytes -- this will allow us to move that handling out of those methods altogether... Signed-off-by: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/at91_ide.c | 13 +++++++------ drivers/ide/ide-h8300.c | 15 ++++++++++----- drivers/ide/ide-io-std.c | 18 ++++++------------ drivers/ide/ns87415.c | 8 +++++--- drivers/ide/scc_pata.c | 16 ++++++++++------ drivers/ide/tx4938ide.c | 15 +++++++-------- drivers/ide/tx4939ide.c | 15 +++++++-------- 7 files changed, 52 insertions(+), 48 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/at91_ide.c b/drivers/ide/at91_ide.c index 9dce793d93b4..b7be66d600f8 100644 --- a/drivers/ide/at91_ide.c +++ b/drivers/ide/at91_ide.c @@ -196,9 +196,9 @@ static void at91_ide_tf_load(ide_drive_t *drive, struct ide_cmd *cmd) HIHI = 0xFF; if (cmd->ftf_flags & IDE_FTFLAG_OUT_DATA) { - u16 data = (tf->hob_data << 8) | tf->data; + u8 data[2] = { tf->data, tf->hob_data }; - at91_ide_output_data(drive, NULL, &data, 2); + at91_ide_output_data(drive, cmd, data, 2); } if (cmd->tf_flags & IDE_TFLAG_OUT_HOB_FEATURE) @@ -234,11 +234,12 @@ static void at91_ide_tf_read(ide_drive_t *drive, struct ide_cmd *cmd) struct ide_taskfile *tf = &cmd->tf; if (cmd->ftf_flags & IDE_FTFLAG_IN_DATA) { - u16 data; + u8 data[2]; - at91_ide_input_data(drive, NULL, &data, 2); - tf->data = data & 0xff; - tf->hob_data = (data >> 8) & 0xff; + at91_ide_input_data(drive, cmd, data, 2); + + tf->data = data[0]; + tf->hob_data = data[1]; } /* be sure we're looking at the low order bits */ diff --git a/drivers/ide/ide-h8300.c b/drivers/ide/ide-h8300.c index 1d45cd5b6a1c..6cb1785d32e6 100644 --- a/drivers/ide/ide-h8300.c +++ b/drivers/ide/ide-h8300.c @@ -54,8 +54,11 @@ static void h8300_tf_load(ide_drive_t *drive, struct ide_cmd *cmd) if (cmd->ftf_flags & IDE_FTFLAG_FLAGGED) HIHI = 0xFF; - if (cmd->ftf_flags & IDE_FTFLAG_OUT_DATA) - mm_outw((tf->hob_data << 8) | tf->data, io_ports->data_addr); + if (cmd->ftf_flags & IDE_FTFLAG_OUT_DATA) { + u8 data[2] = { tf->data, tf->hob_data }; + + h8300_output_data(drive, cmd, data, 2); + } if (cmd->tf_flags & IDE_TFLAG_OUT_HOB_FEATURE) outb(tf->hob_feature, io_ports->feature_addr); @@ -91,10 +94,12 @@ static void h8300_tf_read(ide_drive_t *drive, struct ide_cmd *cmd) struct ide_taskfile *tf = &cmd->tf; if (cmd->ftf_flags & IDE_FTFLAG_IN_DATA) { - u16 data = mm_inw(io_ports->data_addr); + u8 data[2]; + + h8300_input_data(drive, cmd, data, 2); - tf->data = data & 0xff; - tf->hob_data = (data >> 8) & 0xff; + tf->data = data[0]; + tf->hob_data = data[1]; } /* be sure we're looking at the low order bits */ diff --git a/drivers/ide/ide-io-std.c b/drivers/ide/ide-io-std.c index 96a537da8925..f06940df255e 100644 --- a/drivers/ide/ide-io-std.c +++ b/drivers/ide/ide-io-std.c @@ -91,12 +91,9 @@ void ide_tf_load(ide_drive_t *drive, struct ide_cmd *cmd) HIHI = 0xFF; if (cmd->ftf_flags & IDE_FTFLAG_OUT_DATA) { - u16 data = (tf->hob_data << 8) | tf->data; + u8 data[2] = { tf->data, tf->hob_data }; - if (mmio) - writew(data, (void __iomem *)io_ports->data_addr); - else - outw(data, io_ports->data_addr); + ide_output_data(drive, cmd, data, 2); } if (cmd->tf_flags & IDE_TFLAG_OUT_HOB_FEATURE) @@ -145,15 +142,12 @@ void ide_tf_read(ide_drive_t *drive, struct ide_cmd *cmd) } if (cmd->ftf_flags & IDE_FTFLAG_IN_DATA) { - u16 data; + u8 data[2]; - if (mmio) - data = readw((void __iomem *)io_ports->data_addr); - else - data = inw(io_ports->data_addr); + ide_input_data(drive, cmd, data, 2); - tf->data = data & 0xff; - tf->hob_data = (data >> 8) & 0xff; + tf->data = data[0]; + tf->hob_data = data[1]; } /* be sure we're looking at the low order bits */ diff --git a/drivers/ide/ns87415.c b/drivers/ide/ns87415.c index 0a6cf74c3265..7d64bc079fa8 100644 --- a/drivers/ide/ns87415.c +++ b/drivers/ide/ns87415.c @@ -67,10 +67,12 @@ static void superio_tf_read(ide_drive_t *drive, struct ide_cmd *cmd) struct ide_taskfile *tf = &cmd->tf; if (cmd->ftf_flags & IDE_FTFLAG_IN_DATA) { - u16 data = inw(io_ports->data_addr); + u8 data[2]; - tf->data = data & 0xff; - tf->hob_data = (data >> 8) & 0xff; + ide_input_data(drive, cmd, data, 2); + + tf->data = data[0]; + tf->hob_data = data[1]; } /* be sure we're looking at the low order bits */ diff --git a/drivers/ide/scc_pata.c b/drivers/ide/scc_pata.c index ea0a9752c6f9..9c47cbf8c506 100644 --- a/drivers/ide/scc_pata.c +++ b/drivers/ide/scc_pata.c @@ -656,9 +656,11 @@ static void scc_tf_load(ide_drive_t *drive, struct ide_cmd *cmd) if (cmd->ftf_flags & IDE_FTFLAG_FLAGGED) HIHI = 0xFF; - if (cmd->ftf_flags & IDE_FTFLAG_OUT_DATA) - out_be32((void *)io_ports->data_addr, - (tf->hob_data << 8) | tf->data); + if (cmd->ftf_flags & IDE_FTFLAG_OUT_DATA) { + u8 data[2] = { tf->data, tf->hob_data }; + + scc_output_data(drive, NULL, data, 2); + } if (cmd->tf_flags & IDE_TFLAG_OUT_HOB_FEATURE) scc_ide_outb(tf->hob_feature, io_ports->feature_addr); @@ -693,10 +695,12 @@ static void scc_tf_read(ide_drive_t *drive, struct ide_cmd *cmd) struct ide_taskfile *tf = &cmd->tf; if (cmd->ftf_flags & IDE_FTFLAG_IN_DATA) { - u16 data = (u16)in_be32((void *)io_ports->data_addr); + u8 data[2]; + + scc_input_data(drive, cmd, data, 2); - tf->data = data & 0xff; - tf->hob_data = (data >> 8) & 0xff; + tf->data = data[0]; + tf->hob_data = data[1]; } /* be sure we're looking at the low order bits */ diff --git a/drivers/ide/tx4938ide.c b/drivers/ide/tx4938ide.c index 606c37f5267d..c075464308a0 100644 --- a/drivers/ide/tx4938ide.c +++ b/drivers/ide/tx4938ide.c @@ -93,10 +93,9 @@ static void tx4938ide_tf_load(ide_drive_t *drive, struct ide_cmd *cmd) HIHI = 0xFF; if (cmd->ftf_flags & IDE_FTFLAG_OUT_DATA) { - u16 data = (tf->hob_data << 8) | tf->data; + u8 data[2] = { tf->data, tf->hob_data }; - /* no endian swap */ - __raw_writew(data, (void __iomem *)io_ports->data_addr); + hwif->tp_ops->output_data(drive, cmd, data, 2); } if (cmd->tf_flags & IDE_TFLAG_OUT_HOB_FEATURE) @@ -133,12 +132,12 @@ static void tx4938ide_tf_read(ide_drive_t *drive, struct ide_cmd *cmd) struct ide_taskfile *tf = &cmd->tf; if (cmd->ftf_flags & IDE_FTFLAG_IN_DATA) { - u16 data; + u8 data[2]; - /* no endian swap */ - data = __raw_readw((void __iomem *)io_ports->data_addr); - tf->data = data & 0xff; - tf->hob_data = (data >> 8) & 0xff; + hwif->tp_ops->input_data(drive, cmd, data, 2); + + tf->data = data[0]; + tf->hob_data = data[1]; } /* be sure we're looking at the low order bits */ diff --git a/drivers/ide/tx4939ide.c b/drivers/ide/tx4939ide.c index f1e9da71110c..c350d0c7ba4a 100644 --- a/drivers/ide/tx4939ide.c +++ b/drivers/ide/tx4939ide.c @@ -458,10 +458,9 @@ static void tx4939ide_tf_load(ide_drive_t *drive, struct ide_cmd *cmd) HIHI = 0xFF; if (cmd->ftf_flags & IDE_FTFLAG_OUT_DATA) { - u16 data = (tf->hob_data << 8) | tf->data; + u8 data[2] = { tf->data, tf->hob_data }; - /* no endian swap */ - __raw_writew(data, (void __iomem *)io_ports->data_addr); + hwif->tp_ops->output_data(drive, cmd, data, 2); } if (cmd->tf_flags & IDE_TFLAG_OUT_HOB_FEATURE) @@ -500,12 +499,12 @@ static void tx4939ide_tf_read(ide_drive_t *drive, struct ide_cmd *cmd) struct ide_taskfile *tf = &cmd->tf; if (cmd->ftf_flags & IDE_FTFLAG_IN_DATA) { - u16 data; + u8 data[2]; - /* no endian swap */ - data = __raw_readw((void __iomem *)io_ports->data_addr); - tf->data = data & 0xff; - tf->hob_data = (data >> 8) & 0xff; + hwif->tp_ops->input_data(drive, cmd, data, 2); + + tf->data = data[0]; + tf->hob_data = data[1]; } /* be sure we're looking at the low order bits */ -- cgit v1.2.3 From 35218d1ca808ed19b8c6f079ce91872b3deb2219 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Tue, 31 Mar 2009 20:15:31 +0200 Subject: ide: move data register access out of tf_{read|load}() methods (take 2) Move IDE_FTFLAG_{IN|OUT}_DATA flag handling out of tf_{read|load}() methods into the only two functions where these flags actually need to be handled: do_rw_taskfile() and ide_complete_cmd()... Signed-off-by: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/at91_ide.c | 15 --------------- drivers/ide/ide-h8300.c | 15 --------------- drivers/ide/ide-io-std.c | 15 --------------- drivers/ide/ide-io.c | 12 +++++++++++- drivers/ide/ide-taskfile.c | 6 ++++++ drivers/ide/ns87415.c | 9 --------- drivers/ide/scc_pata.c | 15 --------------- drivers/ide/tx4938ide.c | 15 --------------- drivers/ide/tx4939ide.c | 15 --------------- 9 files changed, 17 insertions(+), 100 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/at91_ide.c b/drivers/ide/at91_ide.c index b7be66d600f8..04b39ff02d76 100644 --- a/drivers/ide/at91_ide.c +++ b/drivers/ide/at91_ide.c @@ -195,12 +195,6 @@ static void at91_ide_tf_load(ide_drive_t *drive, struct ide_cmd *cmd) if (cmd->ftf_flags & IDE_FTFLAG_FLAGGED) HIHI = 0xFF; - if (cmd->ftf_flags & IDE_FTFLAG_OUT_DATA) { - u8 data[2] = { tf->data, tf->hob_data }; - - at91_ide_output_data(drive, cmd, data, 2); - } - if (cmd->tf_flags & IDE_TFLAG_OUT_HOB_FEATURE) ide_mm_outb(tf->hob_feature, io_ports->feature_addr); if (cmd->tf_flags & IDE_TFLAG_OUT_HOB_NSECT) @@ -233,15 +227,6 @@ static void at91_ide_tf_read(ide_drive_t *drive, struct ide_cmd *cmd) struct ide_io_ports *io_ports = &hwif->io_ports; struct ide_taskfile *tf = &cmd->tf; - if (cmd->ftf_flags & IDE_FTFLAG_IN_DATA) { - u8 data[2]; - - at91_ide_input_data(drive, cmd, data, 2); - - tf->data = data[0]; - tf->hob_data = data[1]; - } - /* be sure we're looking at the low order bits */ ide_mm_outb(ATA_DEVCTL_OBS, io_ports->ctl_addr); diff --git a/drivers/ide/ide-h8300.c b/drivers/ide/ide-h8300.c index 6cb1785d32e6..8541a9abd7ac 100644 --- a/drivers/ide/ide-h8300.c +++ b/drivers/ide/ide-h8300.c @@ -54,12 +54,6 @@ static void h8300_tf_load(ide_drive_t *drive, struct ide_cmd *cmd) if (cmd->ftf_flags & IDE_FTFLAG_FLAGGED) HIHI = 0xFF; - if (cmd->ftf_flags & IDE_FTFLAG_OUT_DATA) { - u8 data[2] = { tf->data, tf->hob_data }; - - h8300_output_data(drive, cmd, data, 2); - } - if (cmd->tf_flags & IDE_TFLAG_OUT_HOB_FEATURE) outb(tf->hob_feature, io_ports->feature_addr); if (cmd->tf_flags & IDE_TFLAG_OUT_HOB_NSECT) @@ -93,15 +87,6 @@ static void h8300_tf_read(ide_drive_t *drive, struct ide_cmd *cmd) struct ide_io_ports *io_ports = &hwif->io_ports; struct ide_taskfile *tf = &cmd->tf; - if (cmd->ftf_flags & IDE_FTFLAG_IN_DATA) { - u8 data[2]; - - h8300_input_data(drive, cmd, data, 2); - - tf->data = data[0]; - tf->hob_data = data[1]; - } - /* be sure we're looking at the low order bits */ outb(ATA_DEVCTL_OBS, io_ports->ctl_addr); diff --git a/drivers/ide/ide-io-std.c b/drivers/ide/ide-io-std.c index f06940df255e..7f77bb7db488 100644 --- a/drivers/ide/ide-io-std.c +++ b/drivers/ide/ide-io-std.c @@ -90,12 +90,6 @@ void ide_tf_load(ide_drive_t *drive, struct ide_cmd *cmd) if (cmd->ftf_flags & IDE_FTFLAG_FLAGGED) HIHI = 0xFF; - if (cmd->ftf_flags & IDE_FTFLAG_OUT_DATA) { - u8 data[2] = { tf->data, tf->hob_data }; - - ide_output_data(drive, cmd, data, 2); - } - if (cmd->tf_flags & IDE_TFLAG_OUT_HOB_FEATURE) tf_outb(tf->hob_feature, io_ports->feature_addr); if (cmd->tf_flags & IDE_TFLAG_OUT_HOB_NSECT) @@ -141,15 +135,6 @@ void ide_tf_read(ide_drive_t *drive, struct ide_cmd *cmd) tf_inb = ide_inb; } - if (cmd->ftf_flags & IDE_FTFLAG_IN_DATA) { - u8 data[2]; - - ide_input_data(drive, cmd, data, 2); - - tf->data = data[0]; - tf->hob_data = data[1]; - } - /* be sure we're looking at the low order bits */ tf_outb(ATA_DEVCTL_OBS, io_ports->ctl_addr); diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index 5b57905a7d71..5589dce88674 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -73,6 +73,7 @@ EXPORT_SYMBOL_GPL(ide_end_rq); void ide_complete_cmd(ide_drive_t *drive, struct ide_cmd *cmd, u8 stat, u8 err) { + const struct ide_tp_ops *tp_ops = drive->hwif->tp_ops; struct ide_taskfile *tf = &cmd->tf; struct request *rq = cmd->rq; u8 tf_cmd = tf->command; @@ -80,7 +81,16 @@ void ide_complete_cmd(ide_drive_t *drive, struct ide_cmd *cmd, u8 stat, u8 err) tf->error = err; tf->status = stat; - drive->hwif->tp_ops->tf_read(drive, cmd); + if (cmd->ftf_flags & IDE_FTFLAG_IN_DATA) { + u8 data[2]; + + tp_ops->input_data(drive, cmd, data, 2); + + tf->data = data[0]; + tf->hob_data = data[1]; + } + + tp_ops->tf_read(drive, cmd); if ((cmd->tf_flags & IDE_TFLAG_CUSTOM_HANDLER) && tf_cmd == ATA_CMD_IDLEIMMEDIATE) { diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c index 47f13cd11031..243421ce40d0 100644 --- a/drivers/ide/ide-taskfile.c +++ b/drivers/ide/ide-taskfile.c @@ -82,6 +82,12 @@ ide_startstop_t do_rw_taskfile(ide_drive_t *drive, struct ide_cmd *orig_cmd) ide_tf_dump(drive->name, tf); tp_ops->write_devctl(hwif, ATA_DEVCTL_OBS); SELECT_MASK(drive, 0); + + if (cmd->ftf_flags & IDE_FTFLAG_OUT_DATA) { + u8 data[2] = { tf->data, tf->hob_data }; + + tp_ops->output_data(drive, cmd, data, 2); + } tp_ops->tf_load(drive, cmd); } diff --git a/drivers/ide/ns87415.c b/drivers/ide/ns87415.c index 7d64bc079fa8..9f6dff83b141 100644 --- a/drivers/ide/ns87415.c +++ b/drivers/ide/ns87415.c @@ -66,15 +66,6 @@ static void superio_tf_read(ide_drive_t *drive, struct ide_cmd *cmd) struct ide_io_ports *io_ports = &drive->hwif->io_ports; struct ide_taskfile *tf = &cmd->tf; - if (cmd->ftf_flags & IDE_FTFLAG_IN_DATA) { - u8 data[2]; - - ide_input_data(drive, cmd, data, 2); - - tf->data = data[0]; - tf->hob_data = data[1]; - } - /* be sure we're looking at the low order bits */ outb(ATA_DEVCTL_OBS, io_ports->ctl_addr); diff --git a/drivers/ide/scc_pata.c b/drivers/ide/scc_pata.c index 9c47cbf8c506..97f8e0ef21b1 100644 --- a/drivers/ide/scc_pata.c +++ b/drivers/ide/scc_pata.c @@ -656,12 +656,6 @@ static void scc_tf_load(ide_drive_t *drive, struct ide_cmd *cmd) if (cmd->ftf_flags & IDE_FTFLAG_FLAGGED) HIHI = 0xFF; - if (cmd->ftf_flags & IDE_FTFLAG_OUT_DATA) { - u8 data[2] = { tf->data, tf->hob_data }; - - scc_output_data(drive, NULL, data, 2); - } - if (cmd->tf_flags & IDE_TFLAG_OUT_HOB_FEATURE) scc_ide_outb(tf->hob_feature, io_ports->feature_addr); if (cmd->tf_flags & IDE_TFLAG_OUT_HOB_NSECT) @@ -694,15 +688,6 @@ static void scc_tf_read(ide_drive_t *drive, struct ide_cmd *cmd) struct ide_io_ports *io_ports = &drive->hwif->io_ports; struct ide_taskfile *tf = &cmd->tf; - if (cmd->ftf_flags & IDE_FTFLAG_IN_DATA) { - u8 data[2]; - - scc_input_data(drive, cmd, data, 2); - - tf->data = data[0]; - tf->hob_data = data[1]; - } - /* be sure we're looking at the low order bits */ scc_ide_outb(ATA_DEVCTL_OBS, io_ports->ctl_addr); diff --git a/drivers/ide/tx4938ide.c b/drivers/ide/tx4938ide.c index c075464308a0..be391b615963 100644 --- a/drivers/ide/tx4938ide.c +++ b/drivers/ide/tx4938ide.c @@ -92,12 +92,6 @@ static void tx4938ide_tf_load(ide_drive_t *drive, struct ide_cmd *cmd) if (cmd->ftf_flags & IDE_FTFLAG_FLAGGED) HIHI = 0xFF; - if (cmd->ftf_flags & IDE_FTFLAG_OUT_DATA) { - u8 data[2] = { tf->data, tf->hob_data }; - - hwif->tp_ops->output_data(drive, cmd, data, 2); - } - if (cmd->tf_flags & IDE_TFLAG_OUT_HOB_FEATURE) tx4938ide_outb(tf->hob_feature, io_ports->feature_addr); if (cmd->tf_flags & IDE_TFLAG_OUT_HOB_NSECT) @@ -131,15 +125,6 @@ static void tx4938ide_tf_read(ide_drive_t *drive, struct ide_cmd *cmd) struct ide_io_ports *io_ports = &hwif->io_ports; struct ide_taskfile *tf = &cmd->tf; - if (cmd->ftf_flags & IDE_FTFLAG_IN_DATA) { - u8 data[2]; - - hwif->tp_ops->input_data(drive, cmd, data, 2); - - tf->data = data[0]; - tf->hob_data = data[1]; - } - /* be sure we're looking at the low order bits */ tx4938ide_outb(ATA_DEVCTL_OBS, io_ports->ctl_addr); diff --git a/drivers/ide/tx4939ide.c b/drivers/ide/tx4939ide.c index c350d0c7ba4a..5a614d1c94f1 100644 --- a/drivers/ide/tx4939ide.c +++ b/drivers/ide/tx4939ide.c @@ -457,12 +457,6 @@ static void tx4939ide_tf_load(ide_drive_t *drive, struct ide_cmd *cmd) if (cmd->ftf_flags & IDE_FTFLAG_FLAGGED) HIHI = 0xFF; - if (cmd->ftf_flags & IDE_FTFLAG_OUT_DATA) { - u8 data[2] = { tf->data, tf->hob_data }; - - hwif->tp_ops->output_data(drive, cmd, data, 2); - } - if (cmd->tf_flags & IDE_TFLAG_OUT_HOB_FEATURE) tx4939ide_outb(tf->hob_feature, io_ports->feature_addr); if (cmd->tf_flags & IDE_TFLAG_OUT_HOB_NSECT) @@ -498,15 +492,6 @@ static void tx4939ide_tf_read(ide_drive_t *drive, struct ide_cmd *cmd) struct ide_io_ports *io_ports = &hwif->io_ports; struct ide_taskfile *tf = &cmd->tf; - if (cmd->ftf_flags & IDE_FTFLAG_IN_DATA) { - u8 data[2]; - - hwif->tp_ops->input_data(drive, cmd, data, 2); - - tf->data = data[0]; - tf->hob_data = data[1]; - } - /* be sure we're looking at the low order bits */ tx4939ide_outb(ATA_DEVCTL_OBS, io_ports->ctl_addr); -- cgit v1.2.3 From abb596b25edac1ec1acc4ef53df190771661c3d2 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Tue, 31 Mar 2009 20:15:32 +0200 Subject: ide: turn selectproc() method into dev_select() method (take 5) Turn selectproc() method into dev_select() method by teaching it to write to the device register and moving it from 'struct ide_port_ops' to 'struct ide_tp_ops'. Signed-off-by: Sergei Shtylyov Cc: benh@kernel.crashing.org Cc: petkovbb@gmail.com [bart: add ->dev_select to at91_ide.c and tx4939.c (__BIG_ENDIAN case)] Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/at91_ide.c | 1 + drivers/ide/au1xxx-ide.c | 1 + drivers/ide/falconide.c | 1 + drivers/ide/ht6560b.c | 20 +++++++++++++++-- drivers/ide/ide-h8300.c | 1 + drivers/ide/ide-io-std.c | 13 +++++++++++ drivers/ide/ide-iops.c | 12 +--------- drivers/ide/ns87415.c | 25 ++++++++++++++++----- drivers/ide/pmac.c | 58 ++++++++++++++++++++++++++++++++---------------- drivers/ide/q40ide.c | 1 + drivers/ide/qd65xx.c | 21 +++++++++++++++--- drivers/ide/scc_pata.c | 1 + drivers/ide/sgiioc4.c | 1 + drivers/ide/trm290.c | 20 +++++++++++++---- drivers/ide/tx4938ide.c | 1 + drivers/ide/tx4939ide.c | 4 +++- include/linux/ide.h | 6 ++--- 17 files changed, 139 insertions(+), 48 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/at91_ide.c b/drivers/ide/at91_ide.c index 04b39ff02d76..8eda552326e9 100644 --- a/drivers/ide/at91_ide.c +++ b/drivers/ide/at91_ide.c @@ -283,6 +283,7 @@ static const struct ide_tp_ops at91_ide_tp_ops = { .read_altstatus = ide_read_altstatus, .write_devctl = ide_write_devctl, + .dev_select = ide_dev_select, .tf_load = at91_ide_tf_load, .tf_read = at91_ide_tf_read, diff --git a/drivers/ide/au1xxx-ide.c b/drivers/ide/au1xxx-ide.c index 2ca10d533dad..46013644c965 100644 --- a/drivers/ide/au1xxx-ide.c +++ b/drivers/ide/au1xxx-ide.c @@ -469,6 +469,7 @@ static const struct ide_tp_ops au1xxx_tp_ops = { .read_altstatus = ide_read_altstatus, .write_devctl = ide_write_devctl, + .dev_select = ide_dev_select, .tf_load = ide_tf_load, .tf_read = ide_tf_read, diff --git a/drivers/ide/falconide.c b/drivers/ide/falconide.c index 5063be85dc33..afa2af9a362b 100644 --- a/drivers/ide/falconide.c +++ b/drivers/ide/falconide.c @@ -91,6 +91,7 @@ static const struct ide_tp_ops falconide_tp_ops = { .read_altstatus = ide_read_altstatus, .write_devctl = ide_write_devctl, + .dev_select = ide_dev_select, .tf_load = ide_tf_load, .tf_read = ide_tf_read, diff --git a/drivers/ide/ht6560b.c b/drivers/ide/ht6560b.c index c7e5c2246b79..2fb0f2965009 100644 --- a/drivers/ide/ht6560b.c +++ b/drivers/ide/ht6560b.c @@ -103,7 +103,7 @@ /* * This routine is invoked from ide.c to prepare for access to a given drive. */ -static void ht6560b_selectproc (ide_drive_t *drive) +static void ht6560b_dev_select(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; unsigned long flags; @@ -143,6 +143,8 @@ static void ht6560b_selectproc (ide_drive_t *drive) #endif } local_irq_restore(flags); + + outb(drive->select | ATA_DEVICE_OBS, hwif->io_ports.device_addr); } /* @@ -305,15 +307,29 @@ static int probe_ht6560b; module_param_named(probe, probe_ht6560b, bool, 0); MODULE_PARM_DESC(probe, "probe for HT6560B chipset"); +static const struct ide_tp_ops ht6560b_tp_ops = { + .exec_command = ide_exec_command, + .read_status = ide_read_status, + .read_altstatus = ide_read_altstatus, + .write_devctl = ide_write_devctl, + + .dev_select = ht6560b_dev_select, + .tf_load = ide_tf_load, + .tf_read = ide_tf_read, + + .input_data = ide_input_data, + .output_data = ide_output_data, +}; + static const struct ide_port_ops ht6560b_port_ops = { .init_dev = ht6560b_init_dev, .set_pio_mode = ht6560b_set_pio_mode, - .selectproc = ht6560b_selectproc, }; static const struct ide_port_info ht6560b_port_info __initdata = { .name = DRV_NAME, .chipset = ide_ht6560b, + .tp_ops = &ht6560b_tp_ops, .port_ops = &ht6560b_port_ops, .host_flags = IDE_HFLAG_SERIALIZE | /* is this needed? */ IDE_HFLAG_NO_DMA | diff --git a/drivers/ide/ide-h8300.c b/drivers/ide/ide-h8300.c index 8541a9abd7ac..dac9a6d44963 100644 --- a/drivers/ide/ide-h8300.c +++ b/drivers/ide/ide-h8300.c @@ -151,6 +151,7 @@ static const struct ide_tp_ops h8300_tp_ops = { .read_altstatus = ide_read_altstatus, .write_devctl = ide_write_devctl, + .dev_select = ide_dev_select, .tf_load = h8300_tf_load, .tf_read = h8300_tf_read, diff --git a/drivers/ide/ide-io-std.c b/drivers/ide/ide-io-std.c index 7f77bb7db488..9cac281d82c4 100644 --- a/drivers/ide/ide-io-std.c +++ b/drivers/ide/ide-io-std.c @@ -73,6 +73,18 @@ void ide_write_devctl(ide_hwif_t *hwif, u8 ctl) } EXPORT_SYMBOL_GPL(ide_write_devctl); +void ide_dev_select(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + u8 select = drive->select | ATA_DEVICE_OBS; + + if (hwif->host_flags & IDE_HFLAG_MMIO) + writeb(select, (void __iomem *)hwif->io_ports.device_addr); + else + outb(select, hwif->io_ports.device_addr); +} +EXPORT_SYMBOL_GPL(ide_dev_select); + void ide_tf_load(ide_drive_t *drive, struct ide_cmd *cmd) { ide_hwif_t *hwif = drive->hwif; @@ -280,6 +292,7 @@ const struct ide_tp_ops default_tp_ops = { .read_altstatus = ide_read_altstatus, .write_devctl = ide_write_devctl, + .dev_select = ide_dev_select, .tf_load = ide_tf_load, .tf_read = ide_tf_read, diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index 6f363a26700d..dfb0ec317fa3 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -29,17 +29,7 @@ void SELECT_DRIVE(ide_drive_t *drive) { - ide_hwif_t *hwif = drive->hwif; - const struct ide_port_ops *port_ops = hwif->port_ops; - struct ide_cmd cmd; - - if (port_ops && port_ops->selectproc) - port_ops->selectproc(drive); - - memset(&cmd, 0, sizeof(cmd)); - cmd.tf_flags = IDE_TFLAG_OUT_DEVICE; - - drive->hwif->tp_ops->tf_load(drive, &cmd); + drive->hwif->tp_ops->dev_select(drive); } void SELECT_MASK(ide_drive_t *drive, int mask) diff --git a/drivers/ide/ns87415.c b/drivers/ide/ns87415.c index 9f6dff83b141..af1b421eb450 100644 --- a/drivers/ide/ns87415.c +++ b/drivers/ide/ns87415.c @@ -98,12 +98,15 @@ static void superio_tf_read(ide_drive_t *drive, struct ide_cmd *cmd) } } +static void ns87415_dev_select(ide_drive_t *drive); + static const struct ide_tp_ops superio_tp_ops = { .exec_command = ide_exec_command, .read_status = superio_read_status, .read_altstatus = ide_read_altstatus, .write_devctl = ide_write_devctl, + .dev_select = ns87415_dev_select, .tf_load = ide_tf_load, .tf_read = superio_tf_read, @@ -182,10 +185,12 @@ static void ns87415_prepare_drive (ide_drive_t *drive, unsigned int use_dma) local_irq_restore(flags); } -static void ns87415_selectproc (ide_drive_t *drive) +static void ns87415_dev_select(ide_drive_t *drive) { ns87415_prepare_drive(drive, !!(drive->dev_flags & IDE_DFLAG_USING_DMA)); + + outb(drive->select | ATA_DEVICE_OBS, drive->hwif->io_ports.device_addr); } static void ns87415_dma_start(ide_drive_t *drive) @@ -229,7 +234,7 @@ static void __devinit init_hwif_ns87415 (ide_hwif_t *hwif) * Also, leave IRQ masked during drive probing, to prevent infinite * interrupts from a potentially floating INTA.. * - * IRQs get unmasked in selectproc when drive is first used. + * IRQs get unmasked in dev_select() when drive is first used. */ (void) pci_read_config_dword(dev, 0x40, &ctrl); (void) pci_read_config_byte(dev, 0x09, &progif); @@ -281,8 +286,18 @@ static void __devinit init_hwif_ns87415 (ide_hwif_t *hwif) outb(0x60, hwif->dma_base + ATA_DMA_STATUS); } -static const struct ide_port_ops ns87415_port_ops = { - .selectproc = ns87415_selectproc, +static const struct ide_tp_ops ns87415_tp_ops = { + .exec_command = ide_exec_command, + .read_status = ide_read_status, + .read_altstatus = ide_read_altstatus, + .write_devctl = ide_write_devctl, + + .dev_select = ns87415_dev_select, + .tf_load = ide_tf_load, + .tf_read = ide_tf_read, + + .input_data = ide_input_data, + .output_data = ide_output_data, }; static const struct ide_dma_ops ns87415_dma_ops = { @@ -299,7 +314,7 @@ static const struct ide_dma_ops ns87415_dma_ops = { static const struct ide_port_info ns87415_chipset __devinitdata = { .name = DRV_NAME, .init_hwif = init_hwif_ns87415, - .port_ops = &ns87415_port_ops, + .tp_ops = &ns87415_tp_ops, .dma_ops = &ns87415_dma_ops, .host_flags = IDE_HFLAG_TRUST_BIOS_FOR_DMA | IDE_HFLAG_NO_ATAPI_DMA, diff --git a/drivers/ide/pmac.c b/drivers/ide/pmac.c index 7aa45ea37eeb..24ce1f805cd7 100644 --- a/drivers/ide/pmac.c +++ b/drivers/ide/pmac.c @@ -404,8 +404,6 @@ kauai_lookup_timing(struct kauai_timing* table, int cycle_time) #define IDE_WAKEUP_DELAY (1*HZ) static int pmac_ide_init_dma(ide_hwif_t *, const struct ide_port_info *); -static void pmac_ide_selectproc(ide_drive_t *drive); -static void pmac_ide_kauai_selectproc(ide_drive_t *drive); #define PMAC_IDE_REG(x) \ ((void __iomem *)((drive)->hwif->io_ports.data_addr + (x))) @@ -415,8 +413,7 @@ static void pmac_ide_kauai_selectproc(ide_drive_t *drive); * timing register when selecting that unit. This version is for * ASICs with a single timing register */ -static void -pmac_ide_selectproc(ide_drive_t *drive) +static void pmac_ide_apply_timings(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; pmac_ide_hwif_t *pmif = @@ -434,8 +431,7 @@ pmac_ide_selectproc(ide_drive_t *drive) * timing register when selecting that unit. This version is for * ASICs with a dual timing register (Kauai) */ -static void -pmac_ide_kauai_selectproc(ide_drive_t *drive) +static void pmac_ide_kauai_apply_timings(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; pmac_ide_hwif_t *pmif = @@ -464,9 +460,25 @@ pmac_ide_do_update_timings(ide_drive_t *drive) if (pmif->kind == controller_sh_ata6 || pmif->kind == controller_un_ata6 || pmif->kind == controller_k2_ata6) - pmac_ide_kauai_selectproc(drive); + pmac_ide_kauai_apply_timings(drive); else - pmac_ide_selectproc(drive); + pmac_ide_apply_timings(drive); +} + +static void pmac_dev_select(ide_drive_t *drive) +{ + pmac_ide_apply_timings(drive); + + writeb(drive->select | ATA_DEVICE_OBS, + (void __iomem *)drive->hwif->io_ports.device_addr); +} + +static void pmac_kauai_dev_select(ide_drive_t *drive) +{ + pmac_ide_kauai_apply_timings(drive); + + writeb(drive->select | ATA_DEVICE_OBS, + (void __iomem *)drive->hwif->io_ports.device_addr); } static void pmac_exec_command(ide_hwif_t *hwif, u8 cmd) @@ -947,6 +959,7 @@ static const struct ide_tp_ops pmac_tp_ops = { .read_altstatus = ide_read_altstatus, .write_devctl = pmac_write_devctl, + .dev_select = pmac_dev_select, .tf_load = ide_tf_load, .tf_read = ide_tf_read, @@ -954,19 +967,24 @@ static const struct ide_tp_ops pmac_tp_ops = { .output_data = ide_output_data, }; -static const struct ide_port_ops pmac_ide_ata6_port_ops = { - .init_dev = pmac_ide_init_dev, - .set_pio_mode = pmac_ide_set_pio_mode, - .set_dma_mode = pmac_ide_set_dma_mode, - .selectproc = pmac_ide_kauai_selectproc, - .cable_detect = pmac_ide_cable_detect, +static const struct ide_tp_ops pmac_ata6_tp_ops = { + .exec_command = pmac_exec_command, + .read_status = ide_read_status, + .read_altstatus = ide_read_altstatus, + .write_devctl = pmac_write_devctl, + + .dev_select = pmac_kauai_dev_select, + .tf_load = ide_tf_load, + .tf_read = ide_tf_read, + + .input_data = ide_input_data, + .output_data = ide_output_data, }; static const struct ide_port_ops pmac_ide_ata4_port_ops = { .init_dev = pmac_ide_init_dev, .set_pio_mode = pmac_ide_set_pio_mode, .set_dma_mode = pmac_ide_set_dma_mode, - .selectproc = pmac_ide_selectproc, .cable_detect = pmac_ide_cable_detect, }; @@ -974,7 +992,6 @@ static const struct ide_port_ops pmac_ide_port_ops = { .init_dev = pmac_ide_init_dev, .set_pio_mode = pmac_ide_set_pio_mode, .set_dma_mode = pmac_ide_set_dma_mode, - .selectproc = pmac_ide_selectproc, }; static const struct ide_dma_ops pmac_dma_ops; @@ -1011,15 +1028,18 @@ static int __devinit pmac_ide_setup_device(pmac_ide_hwif_t *pmif, hw_regs_t *hw) pmif->broken_dma = pmif->broken_dma_warn = 0; if (of_device_is_compatible(np, "shasta-ata")) { pmif->kind = controller_sh_ata6; - d.port_ops = &pmac_ide_ata6_port_ops; + d.tp_ops = &pmac_ata6_tp_ops; + d.port_ops = &pmac_ide_ata4_port_ops; d.udma_mask = ATA_UDMA6; } else if (of_device_is_compatible(np, "kauai-ata")) { pmif->kind = controller_un_ata6; - d.port_ops = &pmac_ide_ata6_port_ops; + d.tp_ops = &pmac_ata6_tp_ops; + d.port_ops = &pmac_ide_ata4_port_ops; d.udma_mask = ATA_UDMA5; } else if (of_device_is_compatible(np, "K2-UATA")) { pmif->kind = controller_k2_ata6; - d.port_ops = &pmac_ide_ata6_port_ops; + d.tp_ops = &pmac_ata6_tp_ops; + d.port_ops = &pmac_ide_ata4_port_ops; d.udma_mask = ATA_UDMA5; } else if (of_device_is_compatible(np, "keylargo-ata")) { if (strcmp(np->name, "ata-4") == 0) { diff --git a/drivers/ide/q40ide.c b/drivers/ide/q40ide.c index 7fddfd34fcce..d007e7f66598 100644 --- a/drivers/ide/q40ide.c +++ b/drivers/ide/q40ide.c @@ -101,6 +101,7 @@ static const struct ide_tp_ops q40ide_tp_ops = { .read_altstatus = ide_read_altstatus, .write_devctl = ide_write_devctl, + .dev_select = ide_dev_select, .tf_load = ide_tf_load, .tf_read = ide_tf_read, diff --git a/drivers/ide/qd65xx.c b/drivers/ide/qd65xx.c index 08c4fa35e9b1..c9a134986891 100644 --- a/drivers/ide/qd65xx.c +++ b/drivers/ide/qd65xx.c @@ -90,13 +90,15 @@ static int timings[4]={-1,-1,-1,-1}; /* stores current timing for each timer */ * This routine is invoked to prepare for access to a given drive. */ -static void qd65xx_select(ide_drive_t *drive) +static void qd65xx_dev_select(ide_drive_t *drive) { u8 index = (( (QD_TIMREG(drive)) & 0x80 ) >> 7) | (QD_TIMREG(drive) & 0x02); if (timings[index] != QD_TIMING(drive)) outb(timings[index] = QD_TIMING(drive), QD_TIMREG(drive)); + + outb(drive->select | ATA_DEVICE_OBS, drive->hwif->io_ports.device_addr); } /* @@ -309,20 +311,33 @@ static void __init qd6580_init_dev(ide_drive_t *drive) drive->drive_data = (drive->dn & 1) ? t2 : t1; } +static const struct ide_tp_ops qd65xx_tp_ops = { + .exec_command = ide_exec_command, + .read_status = ide_read_status, + .read_altstatus = ide_read_altstatus, + .write_devctl = ide_write_devctl, + + .dev_select = qd65xx_dev_select, + .tf_load = ide_tf_load, + .tf_read = ide_tf_read, + + .input_data = ide_input_data, + .output_data = ide_output_data, +}; + static const struct ide_port_ops qd6500_port_ops = { .init_dev = qd6500_init_dev, .set_pio_mode = qd6500_set_pio_mode, - .selectproc = qd65xx_select, }; static const struct ide_port_ops qd6580_port_ops = { .init_dev = qd6580_init_dev, .set_pio_mode = qd6580_set_pio_mode, - .selectproc = qd65xx_select, }; static const struct ide_port_info qd65xx_port_info __initdata = { .name = DRV_NAME, + .tp_ops = &qd65xx_tp_ops, .chipset = ide_qd65xx, .host_flags = IDE_HFLAG_IO_32BIT | IDE_HFLAG_NO_DMA, diff --git a/drivers/ide/scc_pata.c b/drivers/ide/scc_pata.c index 97f8e0ef21b1..6d8dbd9c10bc 100644 --- a/drivers/ide/scc_pata.c +++ b/drivers/ide/scc_pata.c @@ -825,6 +825,7 @@ static const struct ide_tp_ops scc_tp_ops = { .read_altstatus = scc_read_altstatus, .write_devctl = scc_write_devctl, + .dev_select = ide_dev_select, .tf_load = scc_tf_load, .tf_read = scc_tf_read, diff --git a/drivers/ide/sgiioc4.c b/drivers/ide/sgiioc4.c index 58980fcafc3b..e5d2a48a84de 100644 --- a/drivers/ide/sgiioc4.c +++ b/drivers/ide/sgiioc4.c @@ -505,6 +505,7 @@ static const struct ide_tp_ops sgiioc4_tp_ops = { .read_altstatus = ide_read_altstatus, .write_devctl = ide_write_devctl, + .dev_select = ide_dev_select, .tf_load = ide_tf_load, .tf_read = ide_tf_read, diff --git a/drivers/ide/trm290.c b/drivers/ide/trm290.c index c0528f27fcae..4b42ca091534 100644 --- a/drivers/ide/trm290.c +++ b/drivers/ide/trm290.c @@ -171,9 +171,11 @@ static void trm290_prepare_drive (ide_drive_t *drive, unsigned int use_dma) local_irq_restore(flags); } -static void trm290_selectproc (ide_drive_t *drive) +static void trm290_dev_select(ide_drive_t *drive) { trm290_prepare_drive(drive, !!(drive->dev_flags & IDE_DFLAG_USING_DMA)); + + outb(drive->select | ATA_DEVICE_OBS, drive->hwif->io_ports.device_addr); } static int trm290_dma_check(ide_drive_t *drive, struct ide_cmd *cmd) @@ -298,8 +300,18 @@ static void __devinit init_hwif_trm290(ide_hwif_t *hwif) #endif } -static const struct ide_port_ops trm290_port_ops = { - .selectproc = trm290_selectproc, +static const struct ide_tp_ops trm290_tp_ops = { + .exec_command = ide_exec_command, + .read_status = ide_read_status, + .read_altstatus = ide_read_altstatus, + .write_devctl = ide_write_devctl, + + .dev_select = trm290_dev_select, + .tf_load = ide_tf_load, + .tf_read = ide_tf_read, + + .input_data = ide_input_data, + .output_data = ide_output_data, }; static struct ide_dma_ops trm290_dma_ops = { @@ -315,7 +327,7 @@ static struct ide_dma_ops trm290_dma_ops = { static const struct ide_port_info trm290_chipset __devinitdata = { .name = DRV_NAME, .init_hwif = init_hwif_trm290, - .port_ops = &trm290_port_ops, + .tp_ops = &trm290_tp_ops, .dma_ops = &trm290_dma_ops, .host_flags = IDE_HFLAG_TRM290 | IDE_HFLAG_NO_ATAPI_DMA | diff --git a/drivers/ide/tx4938ide.c b/drivers/ide/tx4938ide.c index be391b615963..4cb79c4c2604 100644 --- a/drivers/ide/tx4938ide.c +++ b/drivers/ide/tx4938ide.c @@ -189,6 +189,7 @@ static const struct ide_tp_ops tx4938ide_tp_ops = { .read_altstatus = ide_read_altstatus, .write_devctl = ide_write_devctl, + .dev_select = ide_dev_select, .tf_load = tx4938ide_tf_load, .tf_read = tx4938ide_tf_read, diff --git a/drivers/ide/tx4939ide.c b/drivers/ide/tx4939ide.c index 5a614d1c94f1..0040a9a3e26e 100644 --- a/drivers/ide/tx4939ide.c +++ b/drivers/ide/tx4939ide.c @@ -429,7 +429,7 @@ static void tx4939ide_tf_load_fixup(ide_drive_t *drive) * Fix ATA100 CORE System Control Register. (The write to the * Device/Head register may write wrong data to the System * Control Register) - * While Sys_Ctl is written here, selectproc is not needed. + * While Sys_Ctl is written here, dev_select() is not needed. */ tx4939ide_writew(sysctl, base, TX4939IDE_Sys_Ctl); } @@ -556,6 +556,7 @@ static const struct ide_tp_ops tx4939ide_tp_ops = { .read_altstatus = ide_read_altstatus, .write_devctl = ide_write_devctl, + .dev_select = ide_dev_select, .tf_load = tx4939ide_tf_load, .tf_read = tx4939ide_tf_read, @@ -579,6 +580,7 @@ static const struct ide_tp_ops tx4939ide_tp_ops = { .read_altstatus = ide_read_altstatus, .write_devctl = ide_write_devctl, + .dev_select = ide_dev_select, .tf_load = tx4939ide_tf_load, .tf_read = ide_tf_read, diff --git a/include/linux/ide.h b/include/linux/ide.h index e919c865f0c7..c69181c61fd8 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -603,7 +603,7 @@ struct ide_drive_s { unsigned int bios_cyl; /* BIOS/fdisk/LILO number of cyls */ unsigned int cyl; /* "real" number of cyls */ - unsigned int drive_data; /* used by set_pio_mode/selectproc */ + unsigned int drive_data; /* used by set_pio_mode/dev_select() */ unsigned int failures; /* current failure count */ unsigned int max_failures; /* maximum allowed failure count */ u64 probed_capacity;/* initial reported media capacity (ide-cd only currently) */ @@ -661,6 +661,7 @@ struct ide_tp_ops { u8 (*read_altstatus)(struct hwif_s *); void (*write_devctl)(struct hwif_s *, u8); + void (*dev_select)(ide_drive_t *); void (*tf_load)(ide_drive_t *, struct ide_cmd *); void (*tf_read)(ide_drive_t *, struct ide_cmd *); @@ -678,7 +679,6 @@ extern const struct ide_tp_ops default_tp_ops; * @init_dev: host specific initialization of a device * @set_pio_mode: routine to program host for PIO mode * @set_dma_mode: routine to program host for DMA mode - * @selectproc: tweaks hardware to select drive * @reset_poll: chipset polling based on hba specifics * @pre_reset: chipset specific changes to default for device-hba resets * @resetproc: routine to reset controller after a disk reset @@ -695,7 +695,6 @@ struct ide_port_ops { void (*init_dev)(ide_drive_t *); void (*set_pio_mode)(ide_drive_t *, const u8); void (*set_dma_mode)(ide_drive_t *, const u8); - void (*selectproc)(ide_drive_t *); int (*reset_poll)(ide_drive_t *); void (*pre_reset)(ide_drive_t *); void (*resetproc)(ide_drive_t *); @@ -1170,6 +1169,7 @@ u8 ide_read_status(ide_hwif_t *); u8 ide_read_altstatus(ide_hwif_t *); void ide_write_devctl(ide_hwif_t *, u8); +void ide_dev_select(ide_drive_t *); void ide_tf_load(ide_drive_t *, struct ide_cmd *); void ide_tf_read(ide_drive_t *, struct ide_cmd *); -- cgit v1.2.3 From fdd88f0af616db59a6a36bdf0185181d2b779f53 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Tue, 31 Mar 2009 20:15:33 +0200 Subject: ide: inline SELECT_DRIVE() Since SELECT_DRIVE() has boiled down to a mere dev_select() method call, it now makes sense to just inline it... Signed-off-by: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-eh.c | 7 ++++--- drivers/ide/ide-io.c | 2 +- drivers/ide/ide-iops.c | 7 +------ drivers/ide/ide-pm.c | 5 +++-- drivers/ide/ide-probe.c | 15 ++++++++------- drivers/ide/ns87415.c | 2 +- include/linux/ide.h | 1 - 7 files changed, 18 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/ide-eh.c b/drivers/ide/ide-eh.c index de4b7f1c9c9f..5d5fb961b5ce 100644 --- a/drivers/ide/ide-eh.c +++ b/drivers/ide/ide-eh.c @@ -165,11 +165,12 @@ static ide_startstop_t do_reset1(ide_drive_t *, int); static ide_startstop_t atapi_reset_pollfunc(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; + const struct ide_tp_ops *tp_ops = hwif->tp_ops; u8 stat; - SELECT_DRIVE(drive); + tp_ops->dev_select(drive); udelay(10); - stat = hwif->tp_ops->read_status(hwif); + stat = tp_ops->read_status(hwif); if (OK_STAT(stat, 0, ATA_BUSY)) printk(KERN_INFO "%s: ATAPI reset complete\n", drive->name); @@ -348,7 +349,7 @@ static ide_startstop_t do_reset1(ide_drive_t *drive, int do_not_try_atapi) /* For an ATAPI device, first try an ATAPI SRST. */ if (drive->media != ide_disk && !do_not_try_atapi) { pre_reset(drive); - SELECT_DRIVE(drive); + tp_ops->dev_select(drive); udelay(20); tp_ops->exec_command(hwif, ATA_CMD_DEV_RESET); ndelay(400); diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index 5589dce88674..1deb6d29b186 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -348,7 +348,7 @@ static ide_startstop_t start_request (ide_drive_t *drive, struct request *rq) if (blk_pm_request(rq)) ide_check_pm_state(drive, rq); - SELECT_DRIVE(drive); + drive->hwif->tp_ops->dev_select(drive); if (ide_wait_stat(&startstop, drive, drive->ready_stat, ATA_BUSY | ATA_DRQ, WAIT_READY)) { printk(KERN_ERR "%s: drive not ready for command\n", drive->name); diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index dfb0ec317fa3..27bb70ddd459 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -27,11 +27,6 @@ #include #include -void SELECT_DRIVE(ide_drive_t *drive) -{ - drive->hwif->tp_ops->dev_select(drive); -} - void SELECT_MASK(ide_drive_t *drive, int mask) { const struct ide_port_ops *port_ops = drive->hwif->port_ops; @@ -347,7 +342,7 @@ int ide_config_drive_speed(ide_drive_t *drive, u8 speed) disable_irq_nosync(hwif->irq); udelay(1); - SELECT_DRIVE(drive); + tp_ops->dev_select(drive); SELECT_MASK(drive, 1); udelay(1); tp_ops->write_devctl(hwif, ATA_NIEN | ATA_DEVCTL_OBS); diff --git a/drivers/ide/ide-pm.c b/drivers/ide/ide-pm.c index 20553d4c42a2..bb7858ebb7d1 100644 --- a/drivers/ide/ide-pm.c +++ b/drivers/ide/ide-pm.c @@ -223,6 +223,7 @@ void ide_check_pm_state(ide_drive_t *drive, struct request *rq) * point. */ ide_hwif_t *hwif = drive->hwif; + const struct ide_tp_ops *tp_ops = hwif->tp_ops; struct request_queue *q = drive->queue; unsigned long flags; int rc; @@ -232,8 +233,8 @@ void ide_check_pm_state(ide_drive_t *drive, struct request *rq) rc = ide_wait_not_busy(hwif, 35000); if (rc) printk(KERN_WARNING "%s: bus not ready on wakeup\n", drive->name); - SELECT_DRIVE(drive); - hwif->tp_ops->write_devctl(hwif, ATA_DEVCTL_OBS); + tp_ops->dev_select(drive); + tp_ops->write_devctl(hwif, ATA_DEVCTL_OBS); rc = ide_wait_not_busy(hwif, 100000); if (rc) printk(KERN_WARNING "%s: drive not ready on wakeup\n", drive->name); diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index d240f76b0da6..d8c1c3e735bb 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -390,13 +390,13 @@ static int do_probe (ide_drive_t *drive, u8 cmd) * (e.g. crw9624 as drive0 with disk as slave) */ msleep(50); - SELECT_DRIVE(drive); + tp_ops->dev_select(drive); msleep(50); if (ide_read_device(drive) != drive->select && present == 0) { if (drive->dn & 1) { /* exit with drive0 selected */ - SELECT_DRIVE(hwif->devices[0]); + tp_ops->dev_select(hwif->devices[0]); /* allow ATA_BUSY to assert & clear */ msleep(50); } @@ -422,7 +422,7 @@ static int do_probe (ide_drive_t *drive, u8 cmd) printk(KERN_ERR "%s: no response (status = 0x%02x), " "resetting drive\n", drive->name, stat); msleep(50); - SELECT_DRIVE(drive); + tp_ops->dev_select(drive); msleep(50); tp_ops->exec_command(hwif, ATA_CMD_DEV_RESET); (void)ide_busy_sleep(hwif, WAIT_WORSTCASE, 0); @@ -441,7 +441,7 @@ static int do_probe (ide_drive_t *drive, u8 cmd) } if (drive->dn & 1) { /* exit with drive0 selected */ - SELECT_DRIVE(hwif->devices[0]); + tp_ops->dev_select(hwif->devices[0]); msleep(50); /* ensure drive irq is clear */ (void)tp_ops->read_status(hwif); @@ -605,6 +605,7 @@ out: static int ide_port_wait_ready(ide_hwif_t *hwif) { + const struct ide_tp_ops *tp_ops = hwif->tp_ops; ide_drive_t *drive; int i, rc; @@ -627,8 +628,8 @@ static int ide_port_wait_ready(ide_hwif_t *hwif) /* Ignore disks that we will not probe for later. */ if ((drive->dev_flags & IDE_DFLAG_NOPROBE) == 0 || (drive->dev_flags & IDE_DFLAG_PRESENT)) { - SELECT_DRIVE(drive); - hwif->tp_ops->write_devctl(hwif, ATA_DEVCTL_OBS); + tp_ops->dev_select(drive); + tp_ops->write_devctl(hwif, ATA_DEVCTL_OBS); mdelay(2); rc = ide_wait_not_busy(hwif, 35000); if (rc) @@ -640,7 +641,7 @@ static int ide_port_wait_ready(ide_hwif_t *hwif) out: /* Exit function with master reselected (let's be sane) */ if (i) - SELECT_DRIVE(hwif->devices[0]); + tp_ops->dev_select(hwif->devices[0]); return rc; } diff --git a/drivers/ide/ns87415.c b/drivers/ide/ns87415.c index af1b421eb450..71a39fb3856f 100644 --- a/drivers/ide/ns87415.c +++ b/drivers/ide/ns87415.c @@ -262,7 +262,7 @@ static void __devinit init_hwif_ns87415 (ide_hwif_t *hwif) #ifdef __sparc_v9__ /* * XXX: Reset the device, if we don't it will not respond to - * SELECT_DRIVE() properly during first ide_probe_port(). + * dev_select() properly during first ide_probe_port(). */ timeout = 10000; outb(12, hwif->io_ports.ctl_addr); diff --git a/include/linux/ide.h b/include/linux/ide.h index c69181c61fd8..a5d26f66ef78 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1176,7 +1176,6 @@ void ide_tf_read(ide_drive_t *, struct ide_cmd *); void ide_input_data(ide_drive_t *, struct ide_cmd *, void *, unsigned int); void ide_output_data(ide_drive_t *, struct ide_cmd *, void *, unsigned int); -extern void SELECT_DRIVE(ide_drive_t *); void SELECT_MASK(ide_drive_t *, int); u8 ide_read_error(ide_drive_t *); -- cgit v1.2.3 From a9d5a97fa3828e7cbc577805eba3d0a0d35dd5a0 Mon Sep 17 00:00:00 2001 From: TOMARI Hisanobu Date: Tue, 31 Mar 2009 20:15:34 +0200 Subject: ide-pmac: IDE cable detection on Apple PowerBook As IDE cable used on Apple PowerBook/iBook laptops are always of "Short 40" type when the firmware says it's 80 conductor one, the cable detection should return ATA_CBL_PATA40_SHORT on those machines. This enables to automatically use UDMA5 even with drives that doesn't correctly detect those cables on Apple laptops. Signed-off-by: TOMARI Hisanobu Cc: Sergei Shtylyov Cc: benh@kernel.crashing.org [bart: beautify patch description] Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/pmac.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/pmac.c b/drivers/ide/pmac.c index 24ce1f805cd7..052b9bf1f8fb 100644 --- a/drivers/ide/pmac.c +++ b/drivers/ide/pmac.c @@ -919,10 +919,18 @@ static u8 pmac_ide_cable_detect(ide_hwif_t *hwif) (pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent); struct device_node *np = pmif->node; const char *cable = of_get_property(np, "cable-type", NULL); + struct device_node *root = of_find_node_by_path("/"); + const char *model = of_get_property(root, "model", NULL); /* Get cable type from device-tree. */ - if (cable && !strncmp(cable, "80-", 3)) - return ATA_CBL_PATA80; + if (cable && !strncmp(cable, "80-", 3)) { + /* Some drives fail to detect 80c cable in PowerBook */ + /* These machine use proprietary short IDE cable anyway */ + if (!strncmp(model, "PowerBook", 9)) + return ATA_CBL_PATA40_SHORT; + else + return ATA_CBL_PATA80; + } /* * G5's seem to have incorrect cable type in device-tree. -- cgit v1.2.3 From d80c592c38378c88c568b96963f7a98d927d05fa Mon Sep 17 00:00:00 2001 From: Gilles Espinasse Date: Tue, 31 Mar 2009 20:15:34 +0200 Subject: ide: be able to build pmac driver without IDE built-in No reason to need IDE built-in to be able to compile pmac driver. Tested to work on 2.6.29-rc8 and 2.6.28.8 with ide and pmac as modules inside an initramfs. Signed-off-by: Gilles Espinasse Cc: sam@ravnborg.org Cc: benh@kernel.crashing.org [bart: remove now superfluous IDE check] Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig index 4d2f2a6b5bdb..cf06494bb744 100644 --- a/drivers/ide/Kconfig +++ b/drivers/ide/Kconfig @@ -681,7 +681,7 @@ endif # TODO: BLK_DEV_IDEDMA_PCI -> BLK_DEV_IDEDMA_SFF config BLK_DEV_IDE_PMAC tristate "PowerMac on-board IDE support" - depends on PPC_PMAC && IDE=y + depends on PPC_PMAC select IDE_TIMINGS select BLK_DEV_IDEDMA_PCI help -- cgit v1.2.3 From 5b6c942dd1f13835eff8105ec2aa859544a1498d Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Tue, 31 Mar 2009 20:15:34 +0200 Subject: ide-floppy: do not complete rq's prematurely ... and access them afterwards. Simplify rq completing code while at it. Spotted-by: Tejun Heo Signed-off-by: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-atapi.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index 100e6f94b4f0..3e43b889dd64 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -310,16 +310,14 @@ static ide_startstop_t ide_pc_intr(ide_drive_t *drive) pc->xferred = pc->req_xfer; if (drive->pc_update_buffers) drive->pc_update_buffers(drive, pc); - - if (drive->media == ide_floppy) - ide_complete_rq(drive, 0, blk_rq_bytes(rq)); } debug_log("%s: DMA finished\n", drive->name); } /* No more interrupts */ if ((stat & ATA_DRQ) == 0) { - int uptodate; + int uptodate, error; + unsigned int done; debug_log("Packet command completed, %d bytes transferred\n", pc->xferred); @@ -366,9 +364,9 @@ static ide_startstop_t ide_pc_intr(ide_drive_t *drive) if (blk_special_request(rq)) { rq->errors = 0; - ide_complete_rq(drive, 0, blk_rq_bytes(rq)); + done = blk_rq_bytes(rq); + error = 0; } else { - unsigned int done; if (blk_fs_request(rq) == 0 && uptodate <= 0) { if (rq->errors == 0) @@ -380,9 +378,10 @@ static ide_startstop_t ide_pc_intr(ide_drive_t *drive) else done = blk_rq_bytes(rq); - ide_complete_rq(drive, uptodate ? 0 : -EIO, done); + error = uptodate ? 0 : -EIO; } + ide_complete_rq(drive, error, done); return ide_stopped; } -- cgit v1.2.3 From 444697d61b6d5ae43b317d259db7c362c9d3756a Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Tue, 31 Mar 2009 15:19:15 -0700 Subject: proc tty: switch cyclades to ->proc_fops Signed-off-by: Alexey Dobriyan Acked-by: Alan Cox Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/cyclades.c | 54 ++++++++++++++++++------------------------------- 1 file changed, 20 insertions(+), 34 deletions(-) (limited to 'drivers') diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index 6a59f72a9c21..272db0e2b491 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -657,6 +657,7 @@ #include #include +#include static void cy_throttle(struct tty_struct *tty); static void cy_send_xchar(struct tty_struct *tty, char ch); @@ -868,8 +869,6 @@ static int cyz_issue_cmd(struct cyclades_card *, __u32, __u8, __u32); static unsigned detect_isa_irq(void __iomem *); #endif /* CONFIG_ISA */ -static int cyclades_get_proc_info(char *, char **, off_t, int, int *, void *); - #ifndef CONFIG_CYZ_INTR static void cyz_poll(unsigned long); @@ -5216,31 +5215,22 @@ static struct pci_driver cy_pci_driver = { }; #endif -static int -cyclades_get_proc_info(char *buf, char **start, off_t offset, int length, - int *eof, void *data) +static int cyclades_proc_show(struct seq_file *m, void *v) { struct cyclades_port *info; unsigned int i, j; - int len = 0; - off_t begin = 0; - off_t pos = 0; - int size; __u32 cur_jifs = jiffies; - size = sprintf(buf, "Dev TimeOpen BytesOut IdleOut BytesIn " + seq_puts(m, "Dev TimeOpen BytesOut IdleOut BytesIn " "IdleIn Overruns Ldisc\n"); - pos += size; - len += size; - /* Output one line for each known port */ for (i = 0; i < NR_CARDS; i++) for (j = 0; j < cy_card[i].nports; j++) { info = &cy_card[i].ports[j]; if (info->port.count) - size = sprintf(buf + len, "%3d %8lu %10lu %8lu " + seq_printf(m, "%3d %8lu %10lu %8lu " "%10lu %8lu %9lu %6ld\n", info->line, (cur_jifs - info->idle_stats.in_use) / HZ, info->idle_stats.xmit_bytes, @@ -5251,30 +5241,26 @@ cyclades_get_proc_info(char *buf, char **start, off_t offset, int length, /* FIXME: double check locking */ (long)info->port.tty->ldisc.ops->num); else - size = sprintf(buf + len, "%3d %8lu %10lu %8lu " + seq_printf(m, "%3d %8lu %10lu %8lu " "%10lu %8lu %9lu %6ld\n", info->line, 0L, 0L, 0L, 0L, 0L, 0L, 0L); - len += size; - pos = begin + len; - - if (pos < offset) { - len = 0; - begin = pos; - } - if (pos > offset + length) - goto done; } - *eof = 1; -done: - *start = buf + (offset - begin); /* Start of wanted data */ - len -= (offset - begin); /* Start slop */ - if (len > length) - len = length; /* Ending slop */ - if (len < 0) - len = 0; - return len; + return 0; +} + +static int cyclades_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, cyclades_proc_show, NULL); } +static const struct file_operations cyclades_proc_fops = { + .owner = THIS_MODULE, + .open = cyclades_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + /* The serial driver boot-time initialization code! Hardware I/O ports are mapped to character special devices on a first found, first allocated manner. That is, this code searches @@ -5311,9 +5297,9 @@ static const struct tty_operations cy_ops = { .hangup = cy_hangup, .break_ctl = cy_break, .wait_until_sent = cy_wait_until_sent, - .read_proc = cyclades_get_proc_info, .tiocmget = cy_tiocmget, .tiocmset = cy_tiocmset, + .proc_fops = &cyclades_proc_fops, }; static int __init cy_init(void) -- cgit v1.2.3 From cdda7cd92b9c0b8b25c906a1f39c61954432357a Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Tue, 31 Mar 2009 15:19:16 -0700 Subject: proc tty: switch ip2 to ->proc_fops Signed-off-by: Alexey Dobriyan Cc: Alan Cox Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/ip2/ip2main.c | 74 +++++++++++++++++++++------------------------- 1 file changed, 34 insertions(+), 40 deletions(-) (limited to 'drivers') diff --git a/drivers/char/ip2/ip2main.c b/drivers/char/ip2/ip2main.c index 70e0ebc30bd0..afd9247cf082 100644 --- a/drivers/char/ip2/ip2main.c +++ b/drivers/char/ip2/ip2main.c @@ -139,7 +139,7 @@ #include static const struct file_operations ip2mem_proc_fops; -static int ip2_read_proc(char *, char **, off_t, int, int *, void * ); +static const struct file_operations ip2_proc_fops; /********************/ /* Type Definitions */ @@ -446,9 +446,9 @@ static const struct tty_operations ip2_ops = { .stop = ip2_stop, .start = ip2_start, .hangup = ip2_hangup, - .read_proc = ip2_read_proc, .tiocmget = ip2_tiocmget, .tiocmset = ip2_tiocmset, + .proc_fops = &ip2_proc_fops, }; /******************************************************************************/ @@ -3029,19 +3029,17 @@ static const struct file_operations ip2mem_proc_fops = { * different sources including ip2mkdev.c and a couple of other drivers. * The bugs are all mine. :-) =mhw= */ -static int ip2_read_proc(char *page, char **start, off_t off, - int count, int *eof, void *data) +static int ip2_proc_show(struct seq_file *m, void *v) { int i, j, box; - int len = 0; int boxes = 0; int ports = 0; int tports = 0; - off_t begin = 0; i2eBordStrPtr pB; + char *sep; - len += sprintf(page, "ip2info: 1.0 driver: %s\n", pcVersion ); - len += sprintf(page+len, "Driver: SMajor=%d CMajor=%d IMajor=%d MaxBoards=%d MaxBoxes=%d MaxPorts=%d\n", + seq_printf(m, "ip2info: 1.0 driver: %s\n", pcVersion); + seq_printf(m, "Driver: SMajor=%d CMajor=%d IMajor=%d MaxBoards=%d MaxBoxes=%d MaxPorts=%d\n", IP2_TTY_MAJOR, IP2_CALLOUT_MAJOR, IP2_IPL_MAJOR, IP2_MAX_BOARDS, ABS_MAX_BOXES, ABS_BIGGEST_BOX); @@ -3053,7 +3051,8 @@ static int ip2_read_proc(char *page, char **start, off_t off, switch( pB->i2ePom.e.porID & ~POR_ID_RESERVED ) { case POR_ID_FIIEX: - len += sprintf( page+len, "Board %d: EX ports=", i ); + seq_printf(m, "Board %d: EX ports=", i); + sep = ""; for( box = 0; box < ABS_MAX_BOXES; ++box ) { ports = 0; @@ -3065,79 +3064,74 @@ static int ip2_read_proc(char *page, char **start, off_t off, ++ports; } } - len += sprintf( page+len, "%d,", ports ); + seq_printf(m, "%s%d", sep, ports); + sep = ","; tports += ports; } - - --len; /* Backup over that last comma */ - - len += sprintf( page+len, " boxes=%d width=%d", boxes, pB->i2eDataWidth16 ? 16 : 8 ); + seq_printf(m, " boxes=%d width=%d", boxes, pB->i2eDataWidth16 ? 16 : 8); break; case POR_ID_II_4: - len += sprintf(page+len, "Board %d: ISA-4 ports=4 boxes=1", i ); + seq_printf(m, "Board %d: ISA-4 ports=4 boxes=1", i); tports = ports = 4; break; case POR_ID_II_8: - len += sprintf(page+len, "Board %d: ISA-8-std ports=8 boxes=1", i ); + seq_printf(m, "Board %d: ISA-8-std ports=8 boxes=1", i); tports = ports = 8; break; case POR_ID_II_8R: - len += sprintf(page+len, "Board %d: ISA-8-RJ11 ports=8 boxes=1", i ); + seq_printf(m, "Board %d: ISA-8-RJ11 ports=8 boxes=1", i); tports = ports = 8; break; default: - len += sprintf(page+len, "Board %d: unknown", i ); + seq_printf(m, "Board %d: unknown", i); /* Don't try and probe for minor numbers */ tports = ports = 0; } } else { /* Don't try and probe for minor numbers */ - len += sprintf(page+len, "Board %d: vacant", i ); + seq_printf(m, "Board %d: vacant", i); tports = ports = 0; } if( tports ) { - len += sprintf(page+len, " minors=" ); - + seq_puts(m, " minors="); + sep = ""; for ( box = 0; box < ABS_MAX_BOXES; ++box ) { for ( j = 0; j < ABS_BIGGEST_BOX; ++j ) { if ( pB->i2eChannelMap[box] & (1 << j) ) { - len += sprintf (page+len,"%d,", + seq_printf(m, "%s%d", sep, j + ABS_BIGGEST_BOX * (box+i*ABS_MAX_BOXES)); + sep = ","; } } } - - page[ len - 1 ] = '\n'; /* Overwrite that last comma */ - } else { - len += sprintf (page+len,"\n" ); - } - - if (len+begin > off+count) - break; - if (len+begin < off) { - begin += len; - len = 0; } + seq_putc(m, '\n'); } + return 0; + } - if (i >= IP2_MAX_BOARDS) - *eof = 1; - if (off >= len+begin) - return 0; +static int ip2_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, ip2_proc_show, NULL); +} - *start = page + (off-begin); - return ((count < begin+len-off) ? count : begin+len-off); - } +static const struct file_operations ip2_proc_fops = { + .owner = THIS_MODULE, + .open = ip2_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; /******************************************************************************/ /* Function: ip2trace() */ -- cgit v1.2.3 From 5bd6de7dadb8054a558ae4ac29121d8e93493065 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Tue, 31 Mar 2009 15:19:16 -0700 Subject: proc tty: switch istallion to ->proc_fops Signed-off-by: Alexey Dobriyan Cc: Alan Cox Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/istallion.c | 121 ++++++++++++++++++++--------------------------- 1 file changed, 52 insertions(+), 69 deletions(-) (limited to 'drivers') diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c index 5c3dc6b8411c..fff19f7e29d2 100644 --- a/drivers/char/istallion.c +++ b/drivers/char/istallion.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -613,7 +614,6 @@ static int stli_breakctl(struct tty_struct *tty, int state); static void stli_waituntilsent(struct tty_struct *tty, int timeout); static void stli_sendxchar(struct tty_struct *tty, char ch); static void stli_hangup(struct tty_struct *tty); -static int stli_portinfo(struct stlibrd *brdp, struct stliport *portp, int portnr, char *pos); static int stli_brdinit(struct stlibrd *brdp); static int stli_startbrd(struct stlibrd *brdp); @@ -1893,20 +1893,10 @@ static void stli_sendxchar(struct tty_struct *tty, char ch) stli_cmdwait(brdp, portp, A_PORTCTRL, &actrl, sizeof(asyctrl_t), 0); } -/*****************************************************************************/ - -#define MAXLINE 80 - -/* - * Format info for a specified port. The line is deliberately limited - * to 80 characters. (If it is too long it will be truncated, if too - * short then padded with spaces). - */ - -static int stli_portinfo(struct stlibrd *brdp, struct stliport *portp, int portnr, char *pos) +static void stli_portinfo(struct seq_file *m, struct stlibrd *brdp, struct stliport *portp, int portnr) { - char *sp, *uart; - int rc, cnt; + char *uart; + int rc; rc = stli_portcmdstats(NULL, portp); @@ -1918,44 +1908,50 @@ static int stli_portinfo(struct stlibrd *brdp, struct stliport *portp, int portn default:uart = "CD1400"; break; } } - - sp = pos; - sp += sprintf(sp, "%d: uart:%s ", portnr, uart); + seq_printf(m, "%d: uart:%s ", portnr, uart); if ((brdp->state & BST_STARTED) && (rc >= 0)) { - sp += sprintf(sp, "tx:%d rx:%d", (int) stli_comstats.txtotal, + char sep; + + seq_printf(m, "tx:%d rx:%d", (int) stli_comstats.txtotal, (int) stli_comstats.rxtotal); if (stli_comstats.rxframing) - sp += sprintf(sp, " fe:%d", + seq_printf(m, " fe:%d", (int) stli_comstats.rxframing); if (stli_comstats.rxparity) - sp += sprintf(sp, " pe:%d", + seq_printf(m, " pe:%d", (int) stli_comstats.rxparity); if (stli_comstats.rxbreaks) - sp += sprintf(sp, " brk:%d", + seq_printf(m, " brk:%d", (int) stli_comstats.rxbreaks); if (stli_comstats.rxoverrun) - sp += sprintf(sp, " oe:%d", + seq_printf(m, " oe:%d", (int) stli_comstats.rxoverrun); - cnt = sprintf(sp, "%s%s%s%s%s ", - (stli_comstats.signals & TIOCM_RTS) ? "|RTS" : "", - (stli_comstats.signals & TIOCM_CTS) ? "|CTS" : "", - (stli_comstats.signals & TIOCM_DTR) ? "|DTR" : "", - (stli_comstats.signals & TIOCM_CD) ? "|DCD" : "", - (stli_comstats.signals & TIOCM_DSR) ? "|DSR" : ""); - *sp = ' '; - sp += cnt; + sep = ' '; + if (stli_comstats.signals & TIOCM_RTS) { + seq_printf(m, "%c%s", sep, "RTS"); + sep = '|'; + } + if (stli_comstats.signals & TIOCM_CTS) { + seq_printf(m, "%c%s", sep, "CTS"); + sep = '|'; + } + if (stli_comstats.signals & TIOCM_DTR) { + seq_printf(m, "%c%s", sep, "DTR"); + sep = '|'; + } + if (stli_comstats.signals & TIOCM_CD) { + seq_printf(m, "%c%s", sep, "DCD"); + sep = '|'; + } + if (stli_comstats.signals & TIOCM_DSR) { + seq_printf(m, "%c%s", sep, "DSR"); + sep = '|'; + } } - - for (cnt = (sp - pos); (cnt < (MAXLINE - 1)); cnt++) - *sp++ = ' '; - if (cnt >= MAXLINE) - pos[(MAXLINE - 2)] = '+'; - pos[(MAXLINE - 1)] = '\n'; - - return(MAXLINE); + seq_putc(m, '\n'); } /*****************************************************************************/ @@ -1964,26 +1960,15 @@ static int stli_portinfo(struct stlibrd *brdp, struct stliport *portp, int portn * Port info, read from the /proc file system. */ -static int stli_readproc(char *page, char **start, off_t off, int count, int *eof, void *data) +static int stli_proc_show(struct seq_file *m, void *v) { struct stlibrd *brdp; struct stliport *portp; unsigned int brdnr, portnr, totalport; - int curoff, maxoff; - char *pos; - pos = page; totalport = 0; - curoff = 0; - - if (off == 0) { - pos += sprintf(pos, "%s: version %s", stli_drvtitle, - stli_drvversion); - while (pos < (page + MAXLINE - 1)) - *pos++ = ' '; - *pos++ = '\n'; - } - curoff = MAXLINE; + + seq_printf(m, "%s: version %s\n", stli_drvtitle, stli_drvversion); /* * We scan through for each board, panel and port. The offset is @@ -1996,33 +1981,31 @@ static int stli_readproc(char *page, char **start, off_t off, int count, int *eo if (brdp->state == 0) continue; - maxoff = curoff + (brdp->nrports * MAXLINE); - if (off >= maxoff) { - curoff = maxoff; - continue; - } - totalport = brdnr * STL_MAXPORTS; for (portnr = 0; (portnr < brdp->nrports); portnr++, totalport++) { portp = brdp->ports[portnr]; if (portp == NULL) continue; - if (off >= (curoff += MAXLINE)) - continue; - if ((pos - page + MAXLINE) > count) - goto stli_readdone; - pos += stli_portinfo(brdp, portp, totalport, pos); + stli_portinfo(m, brdp, portp, totalport); } } + return 0; +} - *eof = 1; - -stli_readdone: - *start = page; - return(pos - page); +static int stli_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, stli_proc_show, NULL); } +static const struct file_operations stli_proc_fops = { + .owner = THIS_MODULE, + .open = stli_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + /*****************************************************************************/ /* @@ -4427,9 +4410,9 @@ static const struct tty_operations stli_ops = { .break_ctl = stli_breakctl, .wait_until_sent = stli_waituntilsent, .send_xchar = stli_sendxchar, - .read_proc = stli_readproc, .tiocmget = stli_tiocmget, .tiocmset = stli_tiocmset, + .proc_fops = &stli_proc_fops, }; static const struct tty_port_operations stli_port_ops = { -- cgit v1.2.3 From 87687144b4fce2ad083e689eec8b219054c292ae Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Tue, 31 Mar 2009 15:19:17 -0700 Subject: proc tty: switch synclink_cs to ->proc_fops Signed-off-by: Alexey Dobriyan Cc: Alan Cox Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/pcmcia/synclink_cs.c | 73 ++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 39 deletions(-) (limited to 'drivers') diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index 5608a1e5a3b3..19d79fc54461 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -51,6 +51,7 @@ #include #include #include +#include #include #include #include @@ -2619,13 +2620,12 @@ cleanup: * /proc fs routines.... */ -static inline int line_info(char *buf, MGSLPC_INFO *info) +static inline void line_info(struct seq_file *m, MGSLPC_INFO *info) { char stat_buf[30]; - int ret; unsigned long flags; - ret = sprintf(buf, "%s:io:%04X irq:%d", + seq_printf(m, "%s:io:%04X irq:%d", info->device_name, info->io_base, info->irq_level); /* output current serial signal states */ @@ -2649,75 +2649,70 @@ static inline int line_info(char *buf, MGSLPC_INFO *info) strcat(stat_buf, "|RI"); if (info->params.mode == MGSL_MODE_HDLC) { - ret += sprintf(buf+ret, " HDLC txok:%d rxok:%d", + seq_printf(m, " HDLC txok:%d rxok:%d", info->icount.txok, info->icount.rxok); if (info->icount.txunder) - ret += sprintf(buf+ret, " txunder:%d", info->icount.txunder); + seq_printf(m, " txunder:%d", info->icount.txunder); if (info->icount.txabort) - ret += sprintf(buf+ret, " txabort:%d", info->icount.txabort); + seq_printf(m, " txabort:%d", info->icount.txabort); if (info->icount.rxshort) - ret += sprintf(buf+ret, " rxshort:%d", info->icount.rxshort); + seq_printf(m, " rxshort:%d", info->icount.rxshort); if (info->icount.rxlong) - ret += sprintf(buf+ret, " rxlong:%d", info->icount.rxlong); + seq_printf(m, " rxlong:%d", info->icount.rxlong); if (info->icount.rxover) - ret += sprintf(buf+ret, " rxover:%d", info->icount.rxover); + seq_printf(m, " rxover:%d", info->icount.rxover); if (info->icount.rxcrc) - ret += sprintf(buf+ret, " rxcrc:%d", info->icount.rxcrc); + seq_printf(m, " rxcrc:%d", info->icount.rxcrc); } else { - ret += sprintf(buf+ret, " ASYNC tx:%d rx:%d", + seq_printf(m, " ASYNC tx:%d rx:%d", info->icount.tx, info->icount.rx); if (info->icount.frame) - ret += sprintf(buf+ret, " fe:%d", info->icount.frame); + seq_printf(m, " fe:%d", info->icount.frame); if (info->icount.parity) - ret += sprintf(buf+ret, " pe:%d", info->icount.parity); + seq_printf(m, " pe:%d", info->icount.parity); if (info->icount.brk) - ret += sprintf(buf+ret, " brk:%d", info->icount.brk); + seq_printf(m, " brk:%d", info->icount.brk); if (info->icount.overrun) - ret += sprintf(buf+ret, " oe:%d", info->icount.overrun); + seq_printf(m, " oe:%d", info->icount.overrun); } /* Append serial signal status to end */ - ret += sprintf(buf+ret, " %s\n", stat_buf+1); + seq_printf(m, " %s\n", stat_buf+1); - ret += sprintf(buf+ret, "txactive=%d bh_req=%d bh_run=%d pending_bh=%x\n", + seq_printf(m, "txactive=%d bh_req=%d bh_run=%d pending_bh=%x\n", info->tx_active,info->bh_requested,info->bh_running, info->pending_bh); - - return ret; } /* Called to print information about devices */ -static int mgslpc_read_proc(char *page, char **start, off_t off, int count, - int *eof, void *data) +static int mgslpc_proc_show(struct seq_file *m, void *v) { - int len = 0, l; - off_t begin = 0; MGSLPC_INFO *info; - len += sprintf(page, "synclink driver:%s\n", driver_version); + seq_printf(m, "synclink driver:%s\n", driver_version); info = mgslpc_device_list; while( info ) { - l = line_info(page + len, info); - len += l; - if (len+begin > off+count) - goto done; - if (len+begin < off) { - begin += len; - len = 0; - } + line_info(m, info); info = info->next_device; } + return 0; +} - *eof = 1; -done: - if (off >= len+begin) - return 0; - *start = page + (off-begin); - return ((count < begin+len-off) ? count : begin+len-off); +static int mgslpc_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, mgslpc_proc_show, NULL); } +static const struct file_operations mgslpc_proc_fops = { + .owner = THIS_MODULE, + .open = mgslpc_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + static int rx_alloc_buffers(MGSLPC_INFO *info) { /* each buffer has header and data */ @@ -2861,13 +2856,13 @@ static const struct tty_operations mgslpc_ops = { .send_xchar = mgslpc_send_xchar, .break_ctl = mgslpc_break, .wait_until_sent = mgslpc_wait_until_sent, - .read_proc = mgslpc_read_proc, .set_termios = mgslpc_set_termios, .stop = tx_pause, .start = tx_release, .hangup = mgslpc_hangup, .tiocmget = tiocmget, .tiocmset = tiocmset, + .proc_fops = &mgslpc_proc_fops, }; static void synclink_cs_cleanup(void) -- cgit v1.2.3 From 8561c44c9e8baf02a9e3018f76c53aa99038a499 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Tue, 31 Mar 2009 15:19:18 -0700 Subject: proc tty: switch stallion to ->proc_fops Signed-off-by: Alexey Dobriyan Cc: Alan Cox Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/stallion.c | 126 +++++++++++++++++++----------------------------- 1 file changed, 50 insertions(+), 76 deletions(-) (limited to 'drivers') diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c index e1e0dd89ac9a..2ad813a801dc 100644 --- a/drivers/char/stallion.c +++ b/drivers/char/stallion.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -1379,52 +1380,47 @@ static void stl_sendxchar(struct tty_struct *tty, char ch) stl_putchar(tty, ch); } -/*****************************************************************************/ - -#define MAXLINE 80 - -/* - * Format info for a specified port. The line is deliberately limited - * to 80 characters. (If it is too long it will be truncated, if too - * short then padded with spaces). - */ - -static int stl_portinfo(struct stlport *portp, int portnr, char *pos) +static void stl_portinfo(struct seq_file *m, struct stlport *portp, int portnr) { - char *sp; - int sigs, cnt; + int sigs; + char sep; - sp = pos; - sp += sprintf(sp, "%d: uart:%s tx:%d rx:%d", + seq_printf(m, "%d: uart:%s tx:%d rx:%d", portnr, (portp->hwid == 1) ? "SC26198" : "CD1400", (int) portp->stats.txtotal, (int) portp->stats.rxtotal); if (portp->stats.rxframing) - sp += sprintf(sp, " fe:%d", (int) portp->stats.rxframing); + seq_printf(m, " fe:%d", (int) portp->stats.rxframing); if (portp->stats.rxparity) - sp += sprintf(sp, " pe:%d", (int) portp->stats.rxparity); + seq_printf(m, " pe:%d", (int) portp->stats.rxparity); if (portp->stats.rxbreaks) - sp += sprintf(sp, " brk:%d", (int) portp->stats.rxbreaks); + seq_printf(m, " brk:%d", (int) portp->stats.rxbreaks); if (portp->stats.rxoverrun) - sp += sprintf(sp, " oe:%d", (int) portp->stats.rxoverrun); + seq_printf(m, " oe:%d", (int) portp->stats.rxoverrun); sigs = stl_getsignals(portp); - cnt = sprintf(sp, "%s%s%s%s%s ", - (sigs & TIOCM_RTS) ? "|RTS" : "", - (sigs & TIOCM_CTS) ? "|CTS" : "", - (sigs & TIOCM_DTR) ? "|DTR" : "", - (sigs & TIOCM_CD) ? "|DCD" : "", - (sigs & TIOCM_DSR) ? "|DSR" : ""); - *sp = ' '; - sp += cnt; - - for (cnt = sp - pos; cnt < (MAXLINE - 1); cnt++) - *sp++ = ' '; - if (cnt >= MAXLINE) - pos[(MAXLINE - 2)] = '+'; - pos[(MAXLINE - 1)] = '\n'; - - return MAXLINE; + sep = ' '; + if (sigs & TIOCM_RTS) { + seq_printf(m, "%c%s", sep, "RTS"); + sep = '|'; + } + if (sigs & TIOCM_CTS) { + seq_printf(m, "%c%s", sep, "CTS"); + sep = '|'; + } + if (sigs & TIOCM_DTR) { + seq_printf(m, "%c%s", sep, "DTR"); + sep = '|'; + } + if (sigs & TIOCM_CD) { + seq_printf(m, "%c%s", sep, "DCD"); + sep = '|'; + } + if (sigs & TIOCM_DSR) { + seq_printf(m, "%c%s", sep, "DSR"); + sep = '|'; + } + seq_putc(m, '\n'); } /*****************************************************************************/ @@ -1433,30 +1429,17 @@ static int stl_portinfo(struct stlport *portp, int portnr, char *pos) * Port info, read from the /proc file system. */ -static int stl_readproc(char *page, char **start, off_t off, int count, int *eof, void *data) +static int stl_proc_show(struct seq_file *m, void *v) { struct stlbrd *brdp; struct stlpanel *panelp; struct stlport *portp; unsigned int brdnr, panelnr, portnr; - int totalport, curoff, maxoff; - char *pos; + int totalport; - pr_debug("stl_readproc(page=%p,start=%p,off=%lx,count=%d,eof=%p," - "data=%p\n", page, start, off, count, eof, data); - - pos = page; totalport = 0; - curoff = 0; - - if (off == 0) { - pos += sprintf(pos, "%s: version %s", stl_drvtitle, - stl_drvversion); - while (pos < (page + MAXLINE - 1)) - *pos++ = ' '; - *pos++ = '\n'; - } - curoff = MAXLINE; + + seq_printf(m, "%s: version %s\n", stl_drvtitle, stl_drvversion); /* * We scan through for each board, panel and port. The offset is @@ -1469,46 +1452,37 @@ static int stl_readproc(char *page, char **start, off_t off, int count, int *eof if (brdp->state == 0) continue; - maxoff = curoff + (brdp->nrports * MAXLINE); - if (off >= maxoff) { - curoff = maxoff; - continue; - } - totalport = brdnr * STL_MAXPORTS; for (panelnr = 0; panelnr < brdp->nrpanels; panelnr++) { panelp = brdp->panels[panelnr]; if (panelp == NULL) continue; - maxoff = curoff + (panelp->nrports * MAXLINE); - if (off >= maxoff) { - curoff = maxoff; - totalport += panelp->nrports; - continue; - } - for (portnr = 0; portnr < panelp->nrports; portnr++, totalport++) { portp = panelp->ports[portnr]; if (portp == NULL) continue; - if (off >= (curoff += MAXLINE)) - continue; - if ((pos - page + MAXLINE) > count) - goto stl_readdone; - pos += stl_portinfo(portp, totalport, pos); + stl_portinfo(m, portp, totalport); } } } + return 0; +} - *eof = 1; - -stl_readdone: - *start = page; - return pos - page; +static int stl_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, stl_proc_show, NULL); } +static const struct file_operations stl_proc_fops = { + .owner = THIS_MODULE, + .open = stl_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + /*****************************************************************************/ /* @@ -2566,9 +2540,9 @@ static const struct tty_operations stl_ops = { .break_ctl = stl_breakctl, .wait_until_sent = stl_waituntilsent, .send_xchar = stl_sendxchar, - .read_proc = stl_readproc, .tiocmget = stl_tiocmget, .tiocmset = stl_tiocmset, + .proc_fops = &stl_proc_fops, }; static const struct tty_port_operations stl_port_ops = { -- cgit v1.2.3 From d337829bd841974045846ec8b428f4199453159e Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Tue, 31 Mar 2009 15:19:18 -0700 Subject: proc tty: switch synclink to ->proc_fops Signed-off-by: Alexey Dobriyan Cc: Alan Cox Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/synclink.c | 98 ++++++++++++++++++++----------------------------- 1 file changed, 39 insertions(+), 59 deletions(-) (limited to 'drivers') diff --git a/drivers/char/synclink.c b/drivers/char/synclink.c index 0057a8f58cb1..afd0b26ca056 100644 --- a/drivers/char/synclink.c +++ b/drivers/char/synclink.c @@ -79,6 +79,7 @@ #include #include #include +#include #include #include #include @@ -3459,18 +3460,17 @@ cleanup: * /proc fs routines.... */ -static inline int line_info(char *buf, struct mgsl_struct *info) +static inline void line_info(struct seq_file *m, struct mgsl_struct *info) { char stat_buf[30]; - int ret; unsigned long flags; if (info->bus_type == MGSL_BUS_TYPE_PCI) { - ret = sprintf(buf, "%s:PCI io:%04X irq:%d mem:%08X lcr:%08X", + seq_printf(m, "%s:PCI io:%04X irq:%d mem:%08X lcr:%08X", info->device_name, info->io_base, info->irq_level, info->phys_memory_base, info->phys_lcr_base); } else { - ret = sprintf(buf, "%s:(E)ISA io:%04X irq:%d dma:%d", + seq_printf(m, "%s:(E)ISA io:%04X irq:%d dma:%d", info->device_name, info->io_base, info->irq_level, info->dma_level); } @@ -3497,37 +3497,37 @@ static inline int line_info(char *buf, struct mgsl_struct *info) if (info->params.mode == MGSL_MODE_HDLC || info->params.mode == MGSL_MODE_RAW ) { - ret += sprintf(buf+ret, " HDLC txok:%d rxok:%d", + seq_printf(m, " HDLC txok:%d rxok:%d", info->icount.txok, info->icount.rxok); if (info->icount.txunder) - ret += sprintf(buf+ret, " txunder:%d", info->icount.txunder); + seq_printf(m, " txunder:%d", info->icount.txunder); if (info->icount.txabort) - ret += sprintf(buf+ret, " txabort:%d", info->icount.txabort); + seq_printf(m, " txabort:%d", info->icount.txabort); if (info->icount.rxshort) - ret += sprintf(buf+ret, " rxshort:%d", info->icount.rxshort); + seq_printf(m, " rxshort:%d", info->icount.rxshort); if (info->icount.rxlong) - ret += sprintf(buf+ret, " rxlong:%d", info->icount.rxlong); + seq_printf(m, " rxlong:%d", info->icount.rxlong); if (info->icount.rxover) - ret += sprintf(buf+ret, " rxover:%d", info->icount.rxover); + seq_printf(m, " rxover:%d", info->icount.rxover); if (info->icount.rxcrc) - ret += sprintf(buf+ret, " rxcrc:%d", info->icount.rxcrc); + seq_printf(m, " rxcrc:%d", info->icount.rxcrc); } else { - ret += sprintf(buf+ret, " ASYNC tx:%d rx:%d", + seq_printf(m, " ASYNC tx:%d rx:%d", info->icount.tx, info->icount.rx); if (info->icount.frame) - ret += sprintf(buf+ret, " fe:%d", info->icount.frame); + seq_printf(m, " fe:%d", info->icount.frame); if (info->icount.parity) - ret += sprintf(buf+ret, " pe:%d", info->icount.parity); + seq_printf(m, " pe:%d", info->icount.parity); if (info->icount.brk) - ret += sprintf(buf+ret, " brk:%d", info->icount.brk); + seq_printf(m, " brk:%d", info->icount.brk); if (info->icount.overrun) - ret += sprintf(buf+ret, " oe:%d", info->icount.overrun); + seq_printf(m, " oe:%d", info->icount.overrun); } /* Append serial signal status to end */ - ret += sprintf(buf+ret, " %s\n", stat_buf+1); + seq_printf(m, " %s\n", stat_buf+1); - ret += sprintf(buf+ret, "txactive=%d bh_req=%d bh_run=%d pending_bh=%x\n", + seq_printf(m, "txactive=%d bh_req=%d bh_run=%d pending_bh=%x\n", info->tx_active,info->bh_requested,info->bh_running, info->pending_bh); @@ -3544,60 +3544,40 @@ static inline int line_info(char *buf, struct mgsl_struct *info) u16 Tmr = usc_InReg( info, TMR ); u16 Tccr = usc_InReg( info, TCCR ); u16 Ccar = inw( info->io_base + CCAR ); - ret += sprintf(buf+ret, "tcsr=%04X tdmr=%04X ticr=%04X rcsr=%04X rdmr=%04X\n" + seq_printf(m, "tcsr=%04X tdmr=%04X ticr=%04X rcsr=%04X rdmr=%04X\n" "ricr=%04X icr =%04X dccr=%04X tmr=%04X tccr=%04X ccar=%04X\n", Tcsr,Tdmr,Ticr,Rscr,Rdmr,Ricr,Icr,Dccr,Tmr,Tccr,Ccar ); } spin_unlock_irqrestore(&info->irq_spinlock,flags); - - return ret; - -} /* end of line_info() */ +} -/* mgsl_read_proc() - * - * Called to print information about devices - * - * Arguments: - * page page of memory to hold returned info - * start - * off - * count - * eof - * data - * - * Return Value: - */ -static int mgsl_read_proc(char *page, char **start, off_t off, int count, - int *eof, void *data) +/* Called to print information about devices */ +static int mgsl_proc_show(struct seq_file *m, void *v) { - int len = 0, l; - off_t begin = 0; struct mgsl_struct *info; - len += sprintf(page, "synclink driver:%s\n", driver_version); + seq_printf(m, "synclink driver:%s\n", driver_version); info = mgsl_device_list; while( info ) { - l = line_info(page + len, info); - len += l; - if (len+begin > off+count) - goto done; - if (len+begin < off) { - begin += len; - len = 0; - } + line_info(m, info); info = info->next_device; } + return 0; +} - *eof = 1; -done: - if (off >= len+begin) - return 0; - *start = page + (off-begin); - return ((count < begin+len-off) ? count : begin+len-off); - -} /* end of mgsl_read_proc() */ +static int mgsl_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, mgsl_proc_show, NULL); +} + +static const struct file_operations mgsl_proc_fops = { + .owner = THIS_MODULE, + .open = mgsl_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; /* mgsl_allocate_dma_buffers() * @@ -4335,13 +4315,13 @@ static const struct tty_operations mgsl_ops = { .send_xchar = mgsl_send_xchar, .break_ctl = mgsl_break, .wait_until_sent = mgsl_wait_until_sent, - .read_proc = mgsl_read_proc, .set_termios = mgsl_set_termios, .stop = mgsl_stop, .start = mgsl_start, .hangup = mgsl_hangup, .tiocmget = tiocmget, .tiocmset = tiocmset, + .proc_fops = &mgsl_proc_fops, }; /* -- cgit v1.2.3 From a18c56e5af41a6391a6bee2c26e806e7997f6698 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Tue, 31 Mar 2009 15:19:19 -0700 Subject: proc tty: switch synclink_gt to ->proc_fops Signed-off-by: Alexey Dobriyan Cc: Alan Cox Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/synclink_gt.c | 74 +++++++++++++++++++++------------------------- 1 file changed, 34 insertions(+), 40 deletions(-) (limited to 'drivers') diff --git a/drivers/char/synclink_gt.c b/drivers/char/synclink_gt.c index efb3dc928a43..6ec6e13d47d7 100644 --- a/drivers/char/synclink_gt.c +++ b/drivers/char/synclink_gt.c @@ -60,6 +60,7 @@ #include #include #include +#include #include #include #include @@ -154,7 +155,6 @@ static void tx_hold(struct tty_struct *tty); static void tx_release(struct tty_struct *tty); static int ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); -static int read_proc(char *page, char **start, off_t off, int count,int *eof, void *data); static int chars_in_buffer(struct tty_struct *tty); static void throttle(struct tty_struct * tty); static void unthrottle(struct tty_struct * tty); @@ -1229,13 +1229,12 @@ static long slgt_compat_ioctl(struct tty_struct *tty, struct file *file, /* * proc fs support */ -static inline int line_info(char *buf, struct slgt_info *info) +static inline void line_info(struct seq_file *m, struct slgt_info *info) { char stat_buf[30]; - int ret; unsigned long flags; - ret = sprintf(buf, "%s: IO=%08X IRQ=%d MaxFrameSize=%u\n", + seq_printf(m, "%s: IO=%08X IRQ=%d MaxFrameSize=%u\n", info->device_name, info->phys_reg_addr, info->irq_level, info->max_frame_size); @@ -1260,75 +1259,70 @@ static inline int line_info(char *buf, struct slgt_info *info) strcat(stat_buf, "|RI"); if (info->params.mode != MGSL_MODE_ASYNC) { - ret += sprintf(buf+ret, "\tHDLC txok:%d rxok:%d", + seq_printf(m, "\tHDLC txok:%d rxok:%d", info->icount.txok, info->icount.rxok); if (info->icount.txunder) - ret += sprintf(buf+ret, " txunder:%d", info->icount.txunder); + seq_printf(m, " txunder:%d", info->icount.txunder); if (info->icount.txabort) - ret += sprintf(buf+ret, " txabort:%d", info->icount.txabort); + seq_printf(m, " txabort:%d", info->icount.txabort); if (info->icount.rxshort) - ret += sprintf(buf+ret, " rxshort:%d", info->icount.rxshort); + seq_printf(m, " rxshort:%d", info->icount.rxshort); if (info->icount.rxlong) - ret += sprintf(buf+ret, " rxlong:%d", info->icount.rxlong); + seq_printf(m, " rxlong:%d", info->icount.rxlong); if (info->icount.rxover) - ret += sprintf(buf+ret, " rxover:%d", info->icount.rxover); + seq_printf(m, " rxover:%d", info->icount.rxover); if (info->icount.rxcrc) - ret += sprintf(buf+ret, " rxcrc:%d", info->icount.rxcrc); + seq_printf(m, " rxcrc:%d", info->icount.rxcrc); } else { - ret += sprintf(buf+ret, "\tASYNC tx:%d rx:%d", + seq_printf(m, "\tASYNC tx:%d rx:%d", info->icount.tx, info->icount.rx); if (info->icount.frame) - ret += sprintf(buf+ret, " fe:%d", info->icount.frame); + seq_printf(m, " fe:%d", info->icount.frame); if (info->icount.parity) - ret += sprintf(buf+ret, " pe:%d", info->icount.parity); + seq_printf(m, " pe:%d", info->icount.parity); if (info->icount.brk) - ret += sprintf(buf+ret, " brk:%d", info->icount.brk); + seq_printf(m, " brk:%d", info->icount.brk); if (info->icount.overrun) - ret += sprintf(buf+ret, " oe:%d", info->icount.overrun); + seq_printf(m, " oe:%d", info->icount.overrun); } /* Append serial signal status to end */ - ret += sprintf(buf+ret, " %s\n", stat_buf+1); + seq_printf(m, " %s\n", stat_buf+1); - ret += sprintf(buf+ret, "\ttxactive=%d bh_req=%d bh_run=%d pending_bh=%x\n", + seq_printf(m, "\ttxactive=%d bh_req=%d bh_run=%d pending_bh=%x\n", info->tx_active,info->bh_requested,info->bh_running, info->pending_bh); - - return ret; } /* Called to print information about devices */ -static int read_proc(char *page, char **start, off_t off, int count, - int *eof, void *data) +static int synclink_gt_proc_show(struct seq_file *m, void *v) { - int len = 0, l; - off_t begin = 0; struct slgt_info *info; - len += sprintf(page, "synclink_gt driver\n"); + seq_puts(m, "synclink_gt driver\n"); info = slgt_device_list; while( info ) { - l = line_info(page + len, info); - len += l; - if (len+begin > off+count) - goto done; - if (len+begin < off) { - begin += len; - len = 0; - } + line_info(m, info); info = info->next_device; } + return 0; +} - *eof = 1; -done: - if (off >= len+begin) - return 0; - *start = page + (off-begin); - return ((count < begin+len-off) ? count : begin+len-off); +static int synclink_gt_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, synclink_gt_proc_show, NULL); } +static const struct file_operations synclink_gt_proc_fops = { + .owner = THIS_MODULE, + .open = synclink_gt_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + /* * return count of bytes in transmit buffer */ @@ -3562,13 +3556,13 @@ static const struct tty_operations ops = { .send_xchar = send_xchar, .break_ctl = set_break, .wait_until_sent = wait_until_sent, - .read_proc = read_proc, .set_termios = set_termios, .stop = tx_hold, .start = tx_release, .hangup = hangup, .tiocmget = tiocmget, .tiocmset = tiocmset, + .proc_fops = &synclink_gt_proc_fops, }; static void slgt_cleanup(void) -- cgit v1.2.3 From e6c8dd8a5c887caaf6ee29f04c7260617cb28295 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Tue, 31 Mar 2009 15:19:20 -0700 Subject: proc tty: switch synclinkmp to ->proc_fops Signed-off-by: Alexey Dobriyan Cc: Alan Cox Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/synclinkmp.c | 74 ++++++++++++++++++++++------------------------- 1 file changed, 34 insertions(+), 40 deletions(-) (limited to 'drivers') diff --git a/drivers/char/synclinkmp.c b/drivers/char/synclinkmp.c index 8eb6c89a980e..26de60efe4b2 100644 --- a/drivers/char/synclinkmp.c +++ b/drivers/char/synclinkmp.c @@ -50,6 +50,7 @@ #include #include #include +#include #include #include #include @@ -520,7 +521,6 @@ static void tx_hold(struct tty_struct *tty); static void tx_release(struct tty_struct *tty); static int ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); -static int read_proc(char *page, char **start, off_t off, int count,int *eof, void *data); static int chars_in_buffer(struct tty_struct *tty); static void throttle(struct tty_struct * tty); static void unthrottle(struct tty_struct * tty); @@ -1354,13 +1354,12 @@ static int ioctl(struct tty_struct *tty, struct file *file, * /proc fs routines.... */ -static inline int line_info(char *buf, SLMP_INFO *info) +static inline void line_info(struct seq_file *m, SLMP_INFO *info) { char stat_buf[30]; - int ret; unsigned long flags; - ret = sprintf(buf, "%s: SCABase=%08x Mem=%08X StatusControl=%08x LCR=%08X\n" + seq_printf(m, "%s: SCABase=%08x Mem=%08X StatusControl=%08x LCR=%08X\n" "\tIRQ=%d MaxFrameSize=%u\n", info->device_name, info->phys_sca_base, @@ -1391,75 +1390,70 @@ static inline int line_info(char *buf, SLMP_INFO *info) strcat(stat_buf, "|RI"); if (info->params.mode == MGSL_MODE_HDLC) { - ret += sprintf(buf+ret, "\tHDLC txok:%d rxok:%d", + seq_printf(m, "\tHDLC txok:%d rxok:%d", info->icount.txok, info->icount.rxok); if (info->icount.txunder) - ret += sprintf(buf+ret, " txunder:%d", info->icount.txunder); + seq_printf(m, " txunder:%d", info->icount.txunder); if (info->icount.txabort) - ret += sprintf(buf+ret, " txabort:%d", info->icount.txabort); + seq_printf(m, " txabort:%d", info->icount.txabort); if (info->icount.rxshort) - ret += sprintf(buf+ret, " rxshort:%d", info->icount.rxshort); + seq_printf(m, " rxshort:%d", info->icount.rxshort); if (info->icount.rxlong) - ret += sprintf(buf+ret, " rxlong:%d", info->icount.rxlong); + seq_printf(m, " rxlong:%d", info->icount.rxlong); if (info->icount.rxover) - ret += sprintf(buf+ret, " rxover:%d", info->icount.rxover); + seq_printf(m, " rxover:%d", info->icount.rxover); if (info->icount.rxcrc) - ret += sprintf(buf+ret, " rxlong:%d", info->icount.rxcrc); + seq_printf(m, " rxlong:%d", info->icount.rxcrc); } else { - ret += sprintf(buf+ret, "\tASYNC tx:%d rx:%d", + seq_printf(m, "\tASYNC tx:%d rx:%d", info->icount.tx, info->icount.rx); if (info->icount.frame) - ret += sprintf(buf+ret, " fe:%d", info->icount.frame); + seq_printf(m, " fe:%d", info->icount.frame); if (info->icount.parity) - ret += sprintf(buf+ret, " pe:%d", info->icount.parity); + seq_printf(m, " pe:%d", info->icount.parity); if (info->icount.brk) - ret += sprintf(buf+ret, " brk:%d", info->icount.brk); + seq_printf(m, " brk:%d", info->icount.brk); if (info->icount.overrun) - ret += sprintf(buf+ret, " oe:%d", info->icount.overrun); + seq_printf(m, " oe:%d", info->icount.overrun); } /* Append serial signal status to end */ - ret += sprintf(buf+ret, " %s\n", stat_buf+1); + seq_printf(m, " %s\n", stat_buf+1); - ret += sprintf(buf+ret, "\ttxactive=%d bh_req=%d bh_run=%d pending_bh=%x\n", + seq_printf(m, "\ttxactive=%d bh_req=%d bh_run=%d pending_bh=%x\n", info->tx_active,info->bh_requested,info->bh_running, info->pending_bh); - - return ret; } /* Called to print information about devices */ -static int read_proc(char *page, char **start, off_t off, int count, - int *eof, void *data) +static int synclinkmp_proc_show(struct seq_file *m, void *v) { - int len = 0, l; - off_t begin = 0; SLMP_INFO *info; - len += sprintf(page, "synclinkmp driver:%s\n", driver_version); + seq_printf(m, "synclinkmp driver:%s\n", driver_version); info = synclinkmp_device_list; while( info ) { - l = line_info(page + len, info); - len += l; - if (len+begin > off+count) - goto done; - if (len+begin < off) { - begin += len; - len = 0; - } + line_info(m, info); info = info->next_device; } + return 0; +} - *eof = 1; -done: - if (off >= len+begin) - return 0; - *start = page + (off-begin); - return ((count < begin+len-off) ? count : begin+len-off); +static int synclinkmp_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, synclinkmp_proc_show, NULL); } +static const struct file_operations synclinkmp_proc_fops = { + .owner = THIS_MODULE, + .open = synclinkmp_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + /* Return the count of bytes in transmit buffer */ static int chars_in_buffer(struct tty_struct *tty) @@ -3905,13 +3899,13 @@ static const struct tty_operations ops = { .send_xchar = send_xchar, .break_ctl = set_break, .wait_until_sent = wait_until_sent, - .read_proc = read_proc, .set_termios = set_termios, .stop = tx_hold, .start = tx_release, .hangup = hangup, .tiocmget = tiocmget, .tiocmset = tiocmset, + .proc_fops = &synclinkmp_proc_fops, }; -- cgit v1.2.3 From 201a50ba6627dd00aa7b7673a5c454ca387095fb Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Tue, 31 Mar 2009 15:19:20 -0700 Subject: proc tty: switch sdio_uart to ->proc_fops Signed-off-by: Alexey Dobriyan Cc: Alan Cox Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/mmc/card/sdio_uart.c | 62 +++++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 32 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c index 78ad48718ab0..36a8d53ad2a2 100644 --- a/drivers/mmc/card/sdio_uart.c +++ b/drivers/mmc/card/sdio_uart.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -933,67 +934,64 @@ static int sdio_uart_tiocmset(struct tty_struct *tty, struct file *file, return result; } -static int sdio_uart_read_proc(char *page, char **start, off_t off, - int count, int *eof, void *data) +static int sdio_uart_proc_show(struct seq_file *m, void *v) { - int i, len = 0; - off_t begin = 0; + int i; - len += sprintf(page, "serinfo:1.0 driver%s%s revision:%s\n", + seq_printf(m, "serinfo:1.0 driver%s%s revision:%s\n", "", "", ""); - for (i = 0; i < UART_NR && len < PAGE_SIZE - 96; i++) { + for (i = 0; i < UART_NR; i++) { struct sdio_uart_port *port = sdio_uart_port_get(i); if (port) { - len += sprintf(page+len, "%d: uart:SDIO", i); + seq_printf(m, "%d: uart:SDIO", i); if(capable(CAP_SYS_ADMIN)) { - len += sprintf(page + len, " tx:%d rx:%d", + seq_printf(m, " tx:%d rx:%d", port->icount.tx, port->icount.rx); if (port->icount.frame) - len += sprintf(page + len, " fe:%d", + seq_printf(m, " fe:%d", port->icount.frame); if (port->icount.parity) - len += sprintf(page + len, " pe:%d", + seq_printf(m, " pe:%d", port->icount.parity); if (port->icount.brk) - len += sprintf(page + len, " brk:%d", + seq_printf(m, " brk:%d", port->icount.brk); if (port->icount.overrun) - len += sprintf(page + len, " oe:%d", + seq_printf(m, " oe:%d", port->icount.overrun); if (port->icount.cts) - len += sprintf(page + len, " cts:%d", + seq_printf(m, " cts:%d", port->icount.cts); if (port->icount.dsr) - len += sprintf(page + len, " dsr:%d", + seq_printf(m, " dsr:%d", port->icount.dsr); if (port->icount.rng) - len += sprintf(page + len, " rng:%d", + seq_printf(m, " rng:%d", port->icount.rng); if (port->icount.dcd) - len += sprintf(page + len, " dcd:%d", + seq_printf(m, " dcd:%d", port->icount.dcd); } - strcat(page, "\n"); - len++; sdio_uart_port_put(port); - } - - if (len + begin > off + count) - goto done; - if (len + begin < off) { - begin += len; - len = 0; + seq_putc(m, '\n'); } } - *eof = 1; + return 0; +} -done: - if (off >= len + begin) - return 0; - *start = page + (off - begin); - return (count < begin + len - off) ? count : (begin + len - off); +static int sdio_uart_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, sdio_uart_proc_show, NULL); } +static const struct file_operations sdio_uart_proc_fops = { + .owner = THIS_MODULE, + .open = sdio_uart_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + static const struct tty_operations sdio_uart_ops = { .open = sdio_uart_open, .close = sdio_uart_close, @@ -1007,7 +1005,7 @@ static const struct tty_operations sdio_uart_ops = { .break_ctl = sdio_uart_break_ctl, .tiocmget = sdio_uart_tiocmget, .tiocmset = sdio_uart_tiocmset, - .read_proc = sdio_uart_read_proc, + .proc_fops = &sdio_uart_proc_fops, }; static struct tty_driver *sdio_uart_tty_driver; -- cgit v1.2.3 From d196a949ba0fb85121c0dc0720b13380d802dbd6 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Tue, 31 Mar 2009 15:19:21 -0700 Subject: proc tty: switch serial_core to ->proc_fops Signed-off-by: Alexey Dobriyan Cc: Alan Cox Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/serial/serial_core.c | 76 +++++++++++++++++++++----------------------- 1 file changed, 36 insertions(+), 40 deletions(-) (limited to 'drivers') diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c index 42f4e66fccaf..bf3c0e32a334 100644 --- a/drivers/serial/serial_core.c +++ b/drivers/serial/serial_core.c @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include #include #include @@ -1682,20 +1684,20 @@ static const char *uart_type(struct uart_port *port) #ifdef CONFIG_PROC_FS -static int uart_line_info(char *buf, struct uart_driver *drv, int i) +static void uart_line_info(struct seq_file *m, struct uart_driver *drv, int i) { struct uart_state *state = drv->state + i; int pm_state; struct uart_port *port = state->port; char stat_buf[32]; unsigned int status; - int mmio, ret; + int mmio; if (!port) - return 0; + return; mmio = port->iotype >= UPIO_MEM; - ret = sprintf(buf, "%d: uart:%s %s%08llX irq:%d", + seq_printf(m, "%d: uart:%s %s%08llX irq:%d", port->line, uart_type(port), mmio ? "mmio:0x" : "port:", mmio ? (unsigned long long)port->mapbase @@ -1703,8 +1705,8 @@ static int uart_line_info(char *buf, struct uart_driver *drv, int i) port->irq); if (port->type == PORT_UNKNOWN) { - strcat(buf, "\n"); - return ret + 1; + seq_putc(m, '\n'); + return; } if (capable(CAP_SYS_ADMIN)) { @@ -1719,19 +1721,19 @@ static int uart_line_info(char *buf, struct uart_driver *drv, int i) uart_change_pm(state, pm_state); mutex_unlock(&state->mutex); - ret += sprintf(buf + ret, " tx:%d rx:%d", + seq_printf(m, " tx:%d rx:%d", port->icount.tx, port->icount.rx); if (port->icount.frame) - ret += sprintf(buf + ret, " fe:%d", + seq_printf(m, " fe:%d", port->icount.frame); if (port->icount.parity) - ret += sprintf(buf + ret, " pe:%d", + seq_printf(m, " pe:%d", port->icount.parity); if (port->icount.brk) - ret += sprintf(buf + ret, " brk:%d", + seq_printf(m, " brk:%d", port->icount.brk); if (port->icount.overrun) - ret += sprintf(buf + ret, " oe:%d", + seq_printf(m, " oe:%d", port->icount.overrun); #define INFOBIT(bit, str) \ @@ -1753,45 +1755,39 @@ static int uart_line_info(char *buf, struct uart_driver *drv, int i) STATBIT(TIOCM_RNG, "|RI"); if (stat_buf[0]) stat_buf[0] = ' '; - strcat(stat_buf, "\n"); - ret += sprintf(buf + ret, stat_buf); - } else { - strcat(buf, "\n"); - ret++; + seq_puts(m, stat_buf); } + seq_putc(m, '\n'); #undef STATBIT #undef INFOBIT - return ret; } -static int uart_read_proc(char *page, char **start, off_t off, - int count, int *eof, void *data) +static int uart_proc_show(struct seq_file *m, void *v) { - struct tty_driver *ttydrv = data; + struct tty_driver *ttydrv = v; struct uart_driver *drv = ttydrv->driver_state; - int i, len = 0, l; - off_t begin = 0; + int i; - len += sprintf(page, "serinfo:1.0 driver%s%s revision:%s\n", + seq_printf(m, "serinfo:1.0 driver%s%s revision:%s\n", "", "", ""); - for (i = 0; i < drv->nr && len < PAGE_SIZE - 96; i++) { - l = uart_line_info(page + len, drv, i); - len += l; - if (len + begin > off + count) - goto done; - if (len + begin < off) { - begin += len; - len = 0; - } - } - *eof = 1; - done: - if (off >= len + begin) - return 0; - *start = page + (off - begin); - return (count < begin + len - off) ? count : (begin + len - off); + for (i = 0; i < drv->nr; i++) + uart_line_info(m, drv, i); + return 0; } + +static int uart_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, uart_proc_show, PDE(inode)->data); +} + +static const struct file_operations uart_proc_fops = { + .owner = THIS_MODULE, + .open = uart_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; #endif #if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(CONFIG_CONSOLE_POLL) @@ -2299,7 +2295,7 @@ static const struct tty_operations uart_ops = { .break_ctl = uart_break_ctl, .wait_until_sent= uart_wait_until_sent, #ifdef CONFIG_PROC_FS - .read_proc = uart_read_proc, + .proc_fops = &uart_proc_fops, #endif .tiocmget = uart_tiocmget, .tiocmset = uart_tiocmset, -- cgit v1.2.3 From 6fd69d3cf1496c8e6751ecb3eae254e1a839bd5d Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Tue, 31 Mar 2009 15:19:21 -0700 Subject: proc tty: switch usb-serial to ->proc_fops Signed-off-by: Alexey Dobriyan Cc: Alan Cox Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/usb/serial/usb-serial.c | 58 +++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 31 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 742a5bc44be8..2a70563bbee1 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -421,57 +422,52 @@ static int serial_break(struct tty_struct *tty, int break_state) return 0; } -static int serial_read_proc(char *page, char **start, off_t off, int count, - int *eof, void *data) +static int serial_proc_show(struct seq_file *m, void *v) { struct usb_serial *serial; - int length = 0; int i; - off_t begin = 0; char tmp[40]; dbg("%s", __func__); - length += sprintf(page, "usbserinfo:1.0 driver:2.0\n"); - for (i = 0; i < SERIAL_TTY_MINORS && length < PAGE_SIZE; ++i) { + seq_puts(m, "usbserinfo:1.0 driver:2.0\n"); + for (i = 0; i < SERIAL_TTY_MINORS; ++i) { serial = usb_serial_get_by_index(i); if (serial == NULL) continue; - length += sprintf(page+length, "%d:", i); + seq_printf(m, "%d:", i); if (serial->type->driver.owner) - length += sprintf(page+length, " module:%s", + seq_printf(m, " module:%s", module_name(serial->type->driver.owner)); - length += sprintf(page+length, " name:\"%s\"", + seq_printf(m, " name:\"%s\"", serial->type->description); - length += sprintf(page+length, " vendor:%04x product:%04x", + seq_printf(m, " vendor:%04x product:%04x", le16_to_cpu(serial->dev->descriptor.idVendor), le16_to_cpu(serial->dev->descriptor.idProduct)); - length += sprintf(page+length, " num_ports:%d", - serial->num_ports); - length += sprintf(page+length, " port:%d", - i - serial->minor + 1); + seq_printf(m, " num_ports:%d", serial->num_ports); + seq_printf(m, " port:%d", i - serial->minor + 1); usb_make_path(serial->dev, tmp, sizeof(tmp)); - length += sprintf(page+length, " path:%s", tmp); + seq_printf(m, " path:%s", tmp); - length += sprintf(page+length, "\n"); - if ((length + begin) > (off + count)) { - usb_serial_put(serial); - goto done; - } - if ((length + begin) < off) { - begin += length; - length = 0; - } + seq_putc(m, '\n'); usb_serial_put(serial); } - *eof = 1; -done: - if (off >= (length + begin)) - return 0; - *start = page + (off-begin); - return (count < begin+length-off) ? count : begin+length-off; + return 0; } +static int serial_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, serial_proc_show, NULL); +} + +static const struct file_operations serial_proc_fops = { + .owner = THIS_MODULE, + .open = serial_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + static int serial_tiocmget(struct tty_struct *tty, struct file *file) { struct usb_serial_port *port = tty->driver_data; @@ -1113,9 +1109,9 @@ static const struct tty_operations serial_ops = { .unthrottle = serial_unthrottle, .break_ctl = serial_break, .chars_in_buffer = serial_chars_in_buffer, - .read_proc = serial_read_proc, .tiocmget = serial_tiocmget, .tiocmset = serial_tiocmset, + .proc_fops = &serial_proc_fops, }; struct tty_driver *usb_serial_tty_driver; -- cgit v1.2.3 From d594027d62808f6649b273544ab8c19ed5b98fd1 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Tue, 31 Mar 2009 15:19:23 -0700 Subject: proc tty: switch amiserial to ->proc_fops Signed-off-by: Alexey Dobriyan Cc: Alan Cox Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/amiserial.c | 62 ++++++++++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 34 deletions(-) (limited to 'drivers') diff --git a/drivers/char/amiserial.c b/drivers/char/amiserial.c index a58869ea8513..fd3ebd1be570 100644 --- a/drivers/char/amiserial.c +++ b/drivers/char/amiserial.c @@ -79,6 +79,7 @@ static char *serial_version = "4.30"; #include #include #include +#include #include #include #include @@ -1825,14 +1826,13 @@ static int rs_open(struct tty_struct *tty, struct file * filp) * /proc fs routines.... */ -static inline int line_info(char *buf, struct serial_state *state) +static inline void line_info(struct seq_file *m, struct serial_state *state) { struct async_struct *info = state->info, scr_info; char stat_buf[30], control, status; - int ret; unsigned long flags; - ret = sprintf(buf, "%d: uart:amiga_builtin",state->line); + seq_printf(m, "%d: uart:amiga_builtin",state->line); /* * Figure out the current RS-232 lines @@ -1864,55 +1864,49 @@ static inline int line_info(char *buf, struct serial_state *state) strcat(stat_buf, "|CD"); if (info->quot) { - ret += sprintf(buf+ret, " baud:%d", - state->baud_base / info->quot); + seq_printf(m, " baud:%d", state->baud_base / info->quot); } - ret += sprintf(buf+ret, " tx:%d rx:%d", - state->icount.tx, state->icount.rx); + seq_printf(m, " tx:%d rx:%d", state->icount.tx, state->icount.rx); if (state->icount.frame) - ret += sprintf(buf+ret, " fe:%d", state->icount.frame); + seq_printf(m, " fe:%d", state->icount.frame); if (state->icount.parity) - ret += sprintf(buf+ret, " pe:%d", state->icount.parity); + seq_printf(m, " pe:%d", state->icount.parity); if (state->icount.brk) - ret += sprintf(buf+ret, " brk:%d", state->icount.brk); + seq_printf(m, " brk:%d", state->icount.brk); if (state->icount.overrun) - ret += sprintf(buf+ret, " oe:%d", state->icount.overrun); + seq_printf(m, " oe:%d", state->icount.overrun); /* * Last thing is the RS-232 status lines */ - ret += sprintf(buf+ret, " %s\n", stat_buf+1); - return ret; + seq_printf(m, " %s\n", stat_buf+1); } -static int rs_read_proc(char *page, char **start, off_t off, int count, - int *eof, void *data) +static int rs_proc_show(struct seq_file *m, void *v) { - int len = 0, l; - off_t begin = 0; - - len += sprintf(page, "serinfo:1.0 driver:%s\n", serial_version); - l = line_info(page + len, &rs_table[0]); - len += l; - if (len+begin > off+count) - goto done; - if (len+begin < off) { - begin += len; - len = 0; - } - *eof = 1; -done: - if (off >= len+begin) - return 0; - *start = page + (off-begin); - return ((count < begin+len-off) ? count : begin+len-off); + seq_printf(m, "serinfo:1.0 driver:%s\n", serial_version); + line_info(m, &rs_table[0]); + return 0; } +static int rs_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, rs_proc_show, NULL); +} + +static const struct file_operations rs_proc_fops = { + .owner = THIS_MODULE, + .open = rs_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + /* * --------------------------------------------------------------------- * rs_init() and friends @@ -1951,9 +1945,9 @@ static const struct tty_operations serial_ops = { .break_ctl = rs_break, .send_xchar = rs_send_xchar, .wait_until_sent = rs_wait_until_sent, - .read_proc = rs_read_proc, .tiocmget = rs_tiocmget, .tiocmset = rs_tiocmset, + .proc_fops = &rs_proc_fops, }; /* -- cgit v1.2.3 From 0f043a81ebe84be3576667f04fdda481609e3816 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Tue, 31 Mar 2009 15:19:25 -0700 Subject: proc tty: remove struct tty_operations::read_proc struct tty_operations::proc_fops took it's place and there is one less create_proc_read_entry() user now! Signed-off-by: Alexey Dobriyan Cc: Alan Cox Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/isdn/capi/capi.c | 7 ------- fs/proc/proc_tty.c | 18 ++++-------------- include/linux/tty_driver.h | 2 -- net/bluetooth/rfcomm/tty.c | 6 ------ 4 files changed, 4 insertions(+), 29 deletions(-) (limited to 'drivers') diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c index 3e468d2cf730..2d8352419c0d 100644 --- a/drivers/isdn/capi/capi.c +++ b/drivers/isdn/capi/capi.c @@ -1331,12 +1331,6 @@ static void capinc_tty_send_xchar(struct tty_struct *tty, char ch) #endif } -static int capinc_tty_read_proc(char *page, char **start, off_t off, - int count, int *eof, void *data) -{ - return 0; -} - static struct tty_driver *capinc_tty_driver; static const struct tty_operations capinc_ops = { @@ -1358,7 +1352,6 @@ static const struct tty_operations capinc_ops = { .flush_buffer = capinc_tty_flush_buffer, .set_ldisc = capinc_tty_set_ldisc, .send_xchar = capinc_tty_send_xchar, - .read_proc = capinc_tty_read_proc, }; static int capinc_tty_init(void) diff --git a/fs/proc/proc_tty.c b/fs/proc/proc_tty.c index 854827b1d463..83adcc869437 100644 --- a/fs/proc/proc_tty.c +++ b/fs/proc/proc_tty.c @@ -144,22 +144,12 @@ void proc_tty_register_driver(struct tty_driver *driver) { struct proc_dir_entry *ent; - if (!driver->driver_name || driver->proc_entry) + if (!driver->driver_name || driver->proc_entry || + !driver->ops->proc_fops) return; - if (driver->ops->proc_fops) { - ent = proc_create_data(driver->driver_name, 0, proc_tty_driver, - driver->ops->proc_fops, driver); - if (!ent) - return; - } else if (driver->ops->read_proc) { - ent = create_proc_entry(driver->driver_name, 0, proc_tty_driver); - if (!ent) - return; - ent->read_proc = driver->ops->read_proc; - ent->data = driver; - } else - return; + ent = proc_create_data(driver->driver_name, 0, proc_tty_driver, + driver->ops->proc_fops, driver); driver->proc_entry = ent; } diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h index c9a69575ded6..8615d661ab60 100644 --- a/include/linux/tty_driver.h +++ b/include/linux/tty_driver.h @@ -252,8 +252,6 @@ struct tty_operations { void (*set_ldisc)(struct tty_struct *tty); void (*wait_until_sent)(struct tty_struct *tty, int timeout); void (*send_xchar)(struct tty_struct *tty, char ch); - int (*read_proc)(char *page, char **start, off_t off, - int count, int *eof, void *data); int (*tiocmget)(struct tty_struct *tty, struct file *file); int (*tiocmset)(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index abdc703a11d2..cab71ea2796d 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -1093,11 +1093,6 @@ static void rfcomm_tty_hangup(struct tty_struct *tty) } } -static int rfcomm_tty_read_proc(char *buf, char **start, off_t offset, int len, int *eof, void *unused) -{ - return 0; -} - static int rfcomm_tty_tiocmget(struct tty_struct *tty, struct file *filp) { struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; @@ -1156,7 +1151,6 @@ static const struct tty_operations rfcomm_ops = { .send_xchar = rfcomm_tty_send_xchar, .hangup = rfcomm_tty_hangup, .wait_until_sent = rfcomm_tty_wait_until_sent, - .read_proc = rfcomm_tty_read_proc, .tiocmget = rfcomm_tty_tiocmget, .tiocmset = rfcomm_tty_tiocmset, }; -- cgit v1.2.3 From c2ec175c39f62949438354f603f4aa170846aabb Mon Sep 17 00:00:00 2001 From: Nick Piggin Date: Tue, 31 Mar 2009 15:23:21 -0700 Subject: mm: page_mkwrite change prototype to match fault Change the page_mkwrite prototype to take a struct vm_fault, and return VM_FAULT_xxx flags. There should be no functional change. This makes it possible to return much more detailed error information to the VM (and also can provide more information eg. virtual_address to the driver, which might be important in some special cases). This is required for a subsequent fix. And will also make it easier to merge page_mkwrite() with fault() in future. Signed-off-by: Nick Piggin Cc: Chris Mason Cc: Trond Myklebust Cc: Miklos Szeredi Cc: Steven Whitehouse Cc: Mark Fasheh Cc: Joel Becker Cc: Artem Bityutskiy Cc: Felix Blyakher Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/filesystems/Locking | 2 +- drivers/video/fb_defio.c | 3 ++- fs/btrfs/ctree.h | 2 +- fs/btrfs/inode.c | 5 ++++- fs/buffer.c | 6 +++++- fs/ext4/ext4.h | 2 +- fs/ext4/inode.c | 5 ++++- fs/fuse/file.c | 3 ++- fs/gfs2/ops_file.c | 5 ++++- fs/nfs/file.c | 5 ++++- fs/ocfs2/mmap.c | 6 ++++-- fs/ubifs/file.c | 9 ++++++--- fs/xfs/linux-2.6/xfs_file.c | 4 ++-- include/linux/buffer_head.h | 2 +- include/linux/mm.h | 3 ++- mm/memory.c | 26 ++++++++++++++++++++++---- 16 files changed, 65 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index 4e78ce677843..76efe5b71d7d 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking @@ -505,7 +505,7 @@ prototypes: void (*open)(struct vm_area_struct*); void (*close)(struct vm_area_struct*); int (*fault)(struct vm_area_struct*, struct vm_fault *); - int (*page_mkwrite)(struct vm_area_struct *, struct page *); + int (*page_mkwrite)(struct vm_area_struct *, struct vm_fault *); int (*access)(struct vm_area_struct *, unsigned long, void*, int, int); locking rules: diff --git a/drivers/video/fb_defio.c b/drivers/video/fb_defio.c index 082026546aee..0a7a6679ee6e 100644 --- a/drivers/video/fb_defio.c +++ b/drivers/video/fb_defio.c @@ -85,8 +85,9 @@ EXPORT_SYMBOL_GPL(fb_deferred_io_fsync); /* vm_ops->page_mkwrite handler */ static int fb_deferred_io_mkwrite(struct vm_area_struct *vma, - struct page *page) + struct vm_fault *vmf) { + struct page *page = vmf->page; struct fb_info *info = vma->vm_private_data; struct fb_deferred_io *fbdefio = info->fbdefio; struct page *cur; diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 5e1d4e30e9d8..7dd1b6d0bf32 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -2060,7 +2060,7 @@ int btrfs_merge_bio_hook(struct page *page, unsigned long offset, unsigned long btrfs_force_ra(struct address_space *mapping, struct file_ra_state *ra, struct file *file, pgoff_t offset, pgoff_t last_index); -int btrfs_page_mkwrite(struct vm_area_struct *vma, struct page *page); +int btrfs_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf); int btrfs_readpage(struct file *file, struct page *page); void btrfs_delete_inode(struct inode *inode); void btrfs_put_inode(struct inode *inode); diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 7d4f948bc22a..ec5423790bbb 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -4292,8 +4292,9 @@ static void btrfs_invalidatepage(struct page *page, unsigned long offset) * beyond EOF, then the page is guaranteed safe against truncation until we * unlock the page. */ -int btrfs_page_mkwrite(struct vm_area_struct *vma, struct page *page) +int btrfs_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) { + struct page *page = vmf->page; struct inode *inode = fdentry(vma->vm_file)->d_inode; struct btrfs_root *root = BTRFS_I(inode)->root; struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree; @@ -4362,6 +4363,8 @@ again: out_unlock: unlock_page(page); out: + if (ret) + ret = VM_FAULT_SIGBUS; return ret; } diff --git a/fs/buffer.c b/fs/buffer.c index 73abe6d8218c..6d51a3da362c 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -2313,9 +2313,10 @@ int block_commit_write(struct page *page, unsigned from, unsigned to) * unlock the page. */ int -block_page_mkwrite(struct vm_area_struct *vma, struct page *page, +block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf, get_block_t get_block) { + struct page *page = vmf->page; struct inode *inode = vma->vm_file->f_path.dentry->d_inode; unsigned long end; loff_t size; @@ -2340,6 +2341,9 @@ block_page_mkwrite(struct vm_area_struct *vma, struct page *page, ret = block_commit_write(page, 0, end); out_unlock: + if (ret) + ret = VM_FAULT_SIGBUS; + unlock_page(page); return ret; } diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 6083bb38057b..990c94000924 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -1098,7 +1098,7 @@ extern int ext4_meta_trans_blocks(struct inode *, int nrblocks, int idxblocks); extern int ext4_chunk_trans_blocks(struct inode *, int nrblocks); extern int ext4_block_truncate_page(handle_t *handle, struct address_space *mapping, loff_t from); -extern int ext4_page_mkwrite(struct vm_area_struct *vma, struct page *page); +extern int ext4_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf); extern qsize_t ext4_get_reserved_space(struct inode *inode); /* ioctl.c */ diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 71d3ecd5db79..dd82ff390067 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -5146,8 +5146,9 @@ static int ext4_bh_unmapped(handle_t *handle, struct buffer_head *bh) return !buffer_mapped(bh); } -int ext4_page_mkwrite(struct vm_area_struct *vma, struct page *page) +int ext4_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) { + struct page *page = vmf->page; loff_t size; unsigned long len; int ret = -EINVAL; @@ -5199,6 +5200,8 @@ int ext4_page_mkwrite(struct vm_area_struct *vma, struct page *page) goto out_unlock; ret = 0; out_unlock: + if (ret) + ret = VM_FAULT_SIGBUS; up_read(&inode->i_alloc_sem); return ret; } diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 821d10f719bd..4e340fedf768 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1234,8 +1234,9 @@ static void fuse_vma_close(struct vm_area_struct *vma) * - sync(2) * - try_to_free_pages() with order > PAGE_ALLOC_COSTLY_ORDER */ -static int fuse_page_mkwrite(struct vm_area_struct *vma, struct page *page) +static int fuse_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) { + struct page *page = vmf->page; /* * Don't use page->mapping as it may become NULL from a * concurrent truncate. diff --git a/fs/gfs2/ops_file.c b/fs/gfs2/ops_file.c index 3b9e8de3500b..70b9b8548945 100644 --- a/fs/gfs2/ops_file.c +++ b/fs/gfs2/ops_file.c @@ -337,8 +337,9 @@ static int gfs2_allocate_page_backing(struct page *page) * blocks allocated on disk to back that page. */ -static int gfs2_page_mkwrite(struct vm_area_struct *vma, struct page *page) +static int gfs2_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) { + struct page *page = vmf->page; struct inode *inode = vma->vm_file->f_path.dentry->d_inode; struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_sbd *sdp = GFS2_SB(inode); @@ -412,6 +413,8 @@ out_unlock: gfs2_glock_dq(&gh); out: gfs2_holder_uninit(&gh); + if (ret) + ret = VM_FAULT_SIGBUS; return ret; } diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 90f292b520d2..cec79392e4ba 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -451,8 +451,9 @@ const struct address_space_operations nfs_file_aops = { .launder_page = nfs_launder_page, }; -static int nfs_vm_page_mkwrite(struct vm_area_struct *vma, struct page *page) +static int nfs_vm_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) { + struct page *page = vmf->page; struct file *filp = vma->vm_file; struct dentry *dentry = filp->f_path.dentry; unsigned pagelen; @@ -483,6 +484,8 @@ static int nfs_vm_page_mkwrite(struct vm_area_struct *vma, struct page *page) ret = pagelen; out_unlock: unlock_page(page); + if (ret) + ret = VM_FAULT_SIGBUS; return ret; } diff --git a/fs/ocfs2/mmap.c b/fs/ocfs2/mmap.c index eea1d24713ea..b606496b72ec 100644 --- a/fs/ocfs2/mmap.c +++ b/fs/ocfs2/mmap.c @@ -154,8 +154,9 @@ out: return ret; } -static int ocfs2_page_mkwrite(struct vm_area_struct *vma, struct page *page) +static int ocfs2_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) { + struct page *page = vmf->page; struct inode *inode = vma->vm_file->f_path.dentry->d_inode; struct buffer_head *di_bh = NULL; sigset_t blocked, oldset; @@ -196,7 +197,8 @@ out: ret2 = ocfs2_vm_op_unblock_sigs(&oldset); if (ret2 < 0) mlog_errno(ret2); - + if (ret) + ret = VM_FAULT_SIGBUS; return ret; } diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c index 93b6de51f261..0ff89fe71e51 100644 --- a/fs/ubifs/file.c +++ b/fs/ubifs/file.c @@ -1434,8 +1434,9 @@ static int ubifs_releasepage(struct page *page, gfp_t unused_gfp_flags) * mmap()d file has taken write protection fault and is being made * writable. UBIFS must ensure page is budgeted for. */ -static int ubifs_vm_page_mkwrite(struct vm_area_struct *vma, struct page *page) +static int ubifs_vm_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) { + struct page *page = vmf->page; struct inode *inode = vma->vm_file->f_path.dentry->d_inode; struct ubifs_info *c = inode->i_sb->s_fs_info; struct timespec now = ubifs_current_time(inode); @@ -1447,7 +1448,7 @@ static int ubifs_vm_page_mkwrite(struct vm_area_struct *vma, struct page *page) ubifs_assert(!(inode->i_sb->s_flags & MS_RDONLY)); if (unlikely(c->ro_media)) - return -EROFS; + return VM_FAULT_SIGBUS; /* -EROFS */ /* * We have not locked @page so far so we may budget for changing the @@ -1480,7 +1481,7 @@ static int ubifs_vm_page_mkwrite(struct vm_area_struct *vma, struct page *page) if (err == -ENOSPC) ubifs_warn("out of space for mmapped file " "(inode number %lu)", inode->i_ino); - return err; + return VM_FAULT_SIGBUS; } lock_page(page); @@ -1520,6 +1521,8 @@ static int ubifs_vm_page_mkwrite(struct vm_area_struct *vma, struct page *page) out_unlock: unlock_page(page); ubifs_release_budget(c, &req); + if (err) + err = VM_FAULT_SIGBUS; return err; } diff --git a/fs/xfs/linux-2.6/xfs_file.c b/fs/xfs/linux-2.6/xfs_file.c index e14c4e3aea0c..f4e255441574 100644 --- a/fs/xfs/linux-2.6/xfs_file.c +++ b/fs/xfs/linux-2.6/xfs_file.c @@ -234,9 +234,9 @@ xfs_file_mmap( STATIC int xfs_vm_page_mkwrite( struct vm_area_struct *vma, - struct page *page) + struct vm_fault *vmf) { - return block_page_mkwrite(vma, page, xfs_get_blocks); + return block_page_mkwrite(vma, vmf, xfs_get_blocks); } const struct file_operations xfs_file_operations = { diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h index f19fd9045ea0..3d7bcde2e332 100644 --- a/include/linux/buffer_head.h +++ b/include/linux/buffer_head.h @@ -216,7 +216,7 @@ int cont_write_begin(struct file *, struct address_space *, loff_t, get_block_t *, loff_t *); int generic_cont_expand_simple(struct inode *inode, loff_t size); int block_commit_write(struct page *page, unsigned from, unsigned to); -int block_page_mkwrite(struct vm_area_struct *vma, struct page *page, +int block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf, get_block_t get_block); void block_sync_page(struct page *); sector_t generic_block_bmap(struct address_space *, sector_t, get_block_t *); diff --git a/include/linux/mm.h b/include/linux/mm.h index 2223f8dfa568..aeabe953ba4f 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -135,6 +135,7 @@ extern pgprot_t protection_map[16]; #define FAULT_FLAG_WRITE 0x01 /* Fault was a write access */ #define FAULT_FLAG_NONLINEAR 0x02 /* Fault was via a nonlinear mapping */ +#define FAULT_FLAG_MKWRITE 0x04 /* Fault was mkwrite of existing pte */ /* * This interface is used by x86 PAT code to identify a pfn mapping that is @@ -187,7 +188,7 @@ struct vm_operations_struct { /* notification that a previously read-only page is about to become * writable, if an error is returned it will cause a SIGBUS */ - int (*page_mkwrite)(struct vm_area_struct *vma, struct page *page); + int (*page_mkwrite)(struct vm_area_struct *vma, struct vm_fault *vmf); /* called by access_process_vm when get_user_pages() fails, typically * for use by special VMAs that can switch between memory and hardware diff --git a/mm/memory.c b/mm/memory.c index 5b4ad5e4f98d..cf6873e91c6a 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -1945,6 +1945,15 @@ static int do_wp_page(struct mm_struct *mm, struct vm_area_struct *vma, * get_user_pages(.write=1, .force=1). */ if (vma->vm_ops && vma->vm_ops->page_mkwrite) { + struct vm_fault vmf; + int tmp; + + vmf.virtual_address = (void __user *)(address & + PAGE_MASK); + vmf.pgoff = old_page->index; + vmf.flags = FAULT_FLAG_WRITE|FAULT_FLAG_MKWRITE; + vmf.page = old_page; + /* * Notify the address space that the page is about to * become writable so that it can prohibit this or wait @@ -1956,8 +1965,12 @@ static int do_wp_page(struct mm_struct *mm, struct vm_area_struct *vma, page_cache_get(old_page); pte_unmap_unlock(page_table, ptl); - if (vma->vm_ops->page_mkwrite(vma, old_page) < 0) + tmp = vma->vm_ops->page_mkwrite(vma, &vmf); + if (unlikely(tmp & + (VM_FAULT_ERROR | VM_FAULT_NOPAGE))) { + ret = tmp; goto unwritable_page; + } /* * Since we dropped the lock we need to revalidate @@ -2106,7 +2119,7 @@ oom: unwritable_page: page_cache_release(old_page); - return VM_FAULT_SIGBUS; + return ret; } /* @@ -2648,9 +2661,14 @@ static int __do_fault(struct mm_struct *mm, struct vm_area_struct *vma, * to become writable */ if (vma->vm_ops->page_mkwrite) { + int tmp; + unlock_page(page); - if (vma->vm_ops->page_mkwrite(vma, page) < 0) { - ret = VM_FAULT_SIGBUS; + vmf.flags |= FAULT_FLAG_MKWRITE; + tmp = vma->vm_ops->page_mkwrite(vma, &vmf); + if (unlikely(tmp & + (VM_FAULT_ERROR | VM_FAULT_NOPAGE))) { + ret = tmp; anon = 1; /* no anon but release vmf.page */ goto out_unlocked; } -- cgit v1.2.3 From 53d6660836f233df66490707365ab177e5fb2bb4 Mon Sep 17 00:00:00 2001 From: "J. R. Okajima" Date: Tue, 31 Mar 2009 15:23:43 -0700 Subject: loop: add ioctl to resize a loop device Add the ability to 'resize' the loop device on the fly. One practical application is a loop file with XFS filesystem, already mounted: You can easily enlarge the file (append some bytes) and then call ioctl(fd, LOOP_SET_CAPACITY, new); The loop driver will learn about the new size and you can use xfs_growfs later on, which will allow you to use full capacity of the loop file without the need to unmount. Test app: #include #include #include #include #include #include #include #include #include #include #include #define _GNU_SOURCE #include char *me; void usage(FILE *f) { fprintf(f, "%s [options] loop_dev [backend_file]\n" "-s, --set new_size_in_bytes\n" "\twhen backend_file is given, " "it will be expanded too while keeping the original contents\n", me); } struct option opts[] = { { .name = "set", .has_arg = 1, .flag = NULL, .val = 's' }, { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' } }; void err_size(char *name, __u64 old) { fprintf(stderr, "size must be larger than current %s (%llu)\n", name, old); } int main(int argc, char *argv[]) { int fd, err, c, i, bfd; ssize_t ssz; size_t sz; __u64 old, new, append; char a[BUFSIZ]; struct stat st; FILE *out; char *backend, *dev; err = EINVAL; out = stderr; me = argv[0]; new = 0; while ((c = getopt_long(argc, argv, "s:h", opts, &i)) != -1) { switch (c) { case 's': errno = 0; new = strtoull(optarg, NULL, 0); if (errno) { err = errno; perror(argv[i]); goto out; } break; case 'h': err = 0; out = stdout; goto err; default: perror(argv[i]); goto err; } } if (optind < argc) dev = argv[optind++]; else goto err; fd = open(dev, O_RDONLY); if (fd < 0) { err = errno; perror(dev); goto out; } err = ioctl(fd, BLKGETSIZE64, &old); if (err) { err = errno; perror("ioctl BLKGETSIZE64"); goto out; } if (!new) { printf("%llu\n", old); goto out; } if (new < old) { err = EINVAL; err_size(dev, old); goto out; } if (optind < argc) { backend = argv[optind++]; bfd = open(backend, O_WRONLY|O_APPEND); if (bfd < 0) { err = errno; perror(backend); goto out; } err = fstat(bfd, &st); if (err) { err = errno; perror(backend); goto out; } if (new < st.st_size) { err = EINVAL; err_size(backend, st.st_size); goto out; } append = new - st.st_size; sz = sizeof(a); while (append > 0) { if (append < sz) sz = append; ssz = write(bfd, a, sz); if (ssz != sz) { err = errno; perror(backend); goto out; } append -= sz; } err = fsync(bfd); if (err) { err = errno; perror(backend); goto out; } } err = ioctl(fd, LOOP_SET_CAPACITY, new); if (err) { err = errno; perror("ioctl LOOP_SET_CAPACITY"); } goto out; err: usage(out); out: return err; } Signed-off-by: J. R. Okajima Signed-off-by: Tomas Matejicek Cc: Cc: Karel Zak Cc: Jens Axboe Cc: Al Viro Cc: Christoph Hellwig Cc: Akinobu Mita Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/loop.c | 30 ++++++++++++++++++++++++++++++ include/linux/loop.h | 1 + 2 files changed, 31 insertions(+) (limited to 'drivers') diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 2621ed2ce6d2..40b17d3b55a1 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -1192,6 +1192,30 @@ loop_get_status64(struct loop_device *lo, struct loop_info64 __user *arg) { return err; } +static int loop_set_capacity(struct loop_device *lo, struct block_device *bdev) +{ + int err; + sector_t sec; + loff_t sz; + + err = -ENXIO; + if (unlikely(lo->lo_state != Lo_bound)) + goto out; + err = figure_loop_size(lo); + if (unlikely(err)) + goto out; + sec = get_capacity(lo->lo_disk); + /* the width of sector_t may be narrow for bit-shift */ + sz = sec; + sz <<= 9; + mutex_lock(&bdev->bd_mutex); + bd_set_size(bdev, sz); + mutex_unlock(&bdev->bd_mutex); + + out: + return err; +} + static int lo_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg) { @@ -1224,6 +1248,11 @@ static int lo_ioctl(struct block_device *bdev, fmode_t mode, case LOOP_GET_STATUS64: err = loop_get_status64(lo, (struct loop_info64 __user *) arg); break; + case LOOP_SET_CAPACITY: + err = -EPERM; + if ((mode & FMODE_WRITE) || capable(CAP_SYS_ADMIN)) + err = loop_set_capacity(lo, bdev); + break; default: err = lo->ioctl ? lo->ioctl(lo, cmd, arg) : -EINVAL; } @@ -1371,6 +1400,7 @@ static int lo_compat_ioctl(struct block_device *bdev, fmode_t mode, lo, (struct compat_loop_info __user *) arg); mutex_unlock(&lo->lo_ctl_mutex); break; + case LOOP_SET_CAPACITY: case LOOP_CLR_FD: case LOOP_GET_STATUS64: case LOOP_SET_STATUS64: diff --git a/include/linux/loop.h b/include/linux/loop.h index 6ffd6db5bb0d..40725447f5e0 100644 --- a/include/linux/loop.h +++ b/include/linux/loop.h @@ -160,5 +160,6 @@ int loop_unregister_transfer(int number); #define LOOP_SET_STATUS64 0x4C04 #define LOOP_GET_STATUS64 0x4C05 #define LOOP_CHANGE_FD 0x4C06 +#define LOOP_SET_CAPACITY 0x4C07 #endif -- cgit v1.2.3 From c2d7543851849a6923680cdd7e1047ed1a84a1c5 Mon Sep 17 00:00:00 2001 From: Eric Sandeen Date: Tue, 31 Mar 2009 15:23:46 -0700 Subject: filesystem freeze: allow SysRq emergency thaw to thaw frozen filesystems Now that the filesystem freeze operation has been elevated to the VFS, and is just an ioctl away, some sort of safety net for unintentionally frozen root filesystems may be in order. The timeout thaw originally proposed did not get merged, but perhaps something like this would be useful in emergencies. For example, freeze /path/to/mountpoint may freeze your root filesystem if you forgot that you had that unmounted. I chose 'j' as the last remaining character other than 'h' which is sort of reserved for help (because help is generated on any unknown character). I've tested this on a non-root fs with multiple (nested) freezers, as well as on a system rendered unresponsive due to a frozen root fs. [randy.dunlap@oracle.com: emergency thaw only if CONFIG_BLOCK enabled] Signed-off-by: Eric Sandeen Cc: Takashi Sato Signed-off-by: Randy Dunlap Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/sysrq.txt | 5 +++++ drivers/char/sysrq.c | 19 ++++++++++++++++++- fs/buffer.c | 33 +++++++++++++++++++++++++++++++++ include/linux/fs.h | 1 + 4 files changed, 57 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/Documentation/sysrq.txt b/Documentation/sysrq.txt index 9e592c718afb..afa2946892da 100644 --- a/Documentation/sysrq.txt +++ b/Documentation/sysrq.txt @@ -81,6 +81,8 @@ On all - write a character to /proc/sysrq-trigger. e.g.: 'i' - Send a SIGKILL to all processes, except for init. +'j' - Forcibly "Just thaw it" - filesystems frozen by the FIFREEZE ioctl. + 'k' - Secure Access Key (SAK) Kills all programs on the current virtual console. NOTE: See important comments below in SAK section. @@ -160,6 +162,9 @@ t'E'rm and k'I'll are useful if you have some sort of runaway process you are unable to kill any other way, especially if it's spawning other processes. +"'J'ust thaw it" is useful if your system becomes unresponsive due to a frozen +(probably root) filesystem via the FIFREEZE ioctl. + * Sometimes SysRq seems to get 'stuck' after using it, what can I do? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ That happens to me, also. I've found that tapping shift, alt, and control diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c index 33a9351c896d..5afe7316c72e 100644 --- a/drivers/char/sysrq.c +++ b/drivers/char/sysrq.c @@ -346,6 +346,19 @@ static struct sysrq_key_op sysrq_moom_op = { .enable_mask = SYSRQ_ENABLE_SIGNAL, }; +#ifdef CONFIG_BLOCK +static void sysrq_handle_thaw(int key, struct tty_struct *tty) +{ + emergency_thaw_all(); +} +static struct sysrq_key_op sysrq_thaw_op = { + .handler = sysrq_handle_thaw, + .help_msg = "thaw-filesystems(J)", + .action_msg = "Emergency Thaw of all frozen filesystems", + .enable_mask = SYSRQ_ENABLE_SIGNAL, +}; +#endif + static void sysrq_handle_kill(int key, struct tty_struct *tty) { send_sig_all(SIGKILL); @@ -396,9 +409,13 @@ static struct sysrq_key_op *sysrq_key_table[36] = { &sysrq_moom_op, /* f */ /* g: May be registered by ppc for kgdb */ NULL, /* g */ - NULL, /* h */ + NULL, /* h - reserved for help */ &sysrq_kill_op, /* i */ +#ifdef CONFIG_BLOCK + &sysrq_thaw_op, /* j */ +#else NULL, /* j */ +#endif &sysrq_SAK_op, /* k */ #ifdef CONFIG_SMP &sysrq_showallcpus_op, /* l */ diff --git a/fs/buffer.c b/fs/buffer.c index c77b848c3d43..f5f8b15a6e40 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -547,6 +547,39 @@ repeat: return err; } +void do_thaw_all(unsigned long unused) +{ + struct super_block *sb; + char b[BDEVNAME_SIZE]; + + spin_lock(&sb_lock); +restart: + list_for_each_entry(sb, &super_blocks, s_list) { + sb->s_count++; + spin_unlock(&sb_lock); + down_read(&sb->s_umount); + while (sb->s_bdev && !thaw_bdev(sb->s_bdev, sb)) + printk(KERN_WARNING "Emergency Thaw on %s\n", + bdevname(sb->s_bdev, b)); + up_read(&sb->s_umount); + spin_lock(&sb_lock); + if (__put_super_and_need_restart(sb)) + goto restart; + } + spin_unlock(&sb_lock); + printk(KERN_WARNING "Emergency Thaw complete\n"); +} + +/** + * emergency_thaw_all -- forcibly thaw every frozen filesystem + * + * Used for emergency unfreeze of all filesystems via SysRq + */ +void emergency_thaw_all(void) +{ + pdflush_operation(do_thaw_all, 0); +} + /** * sync_mapping_buffers - write out & wait upon a mapping's "associated" buffers * @mapping: the mapping which wants those buffers written diff --git a/include/linux/fs.h b/include/linux/fs.h index 87e7bfc5ebd7..61211ad823fe 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1878,6 +1878,7 @@ extern struct block_device *open_by_devnum(dev_t, fmode_t); extern void invalidate_bdev(struct block_device *); extern int sync_blockdev(struct block_device *bdev); extern struct super_block *freeze_bdev(struct block_device *); +extern void emergency_thaw_all(void); extern int thaw_bdev(struct block_device *bdev, struct super_block *sb); extern int fsync_bdev(struct block_device *); extern int fsync_super(struct super_block *); -- cgit v1.2.3 From c0aa24ba8962bd1db98938fb2f3773f870896036 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Tue, 31 Mar 2009 15:23:48 -0700 Subject: auxdisplay: remove PARPORT dependency Remove PARPORT dependency for Auxiliary Display support. This is not needed since the dependency for the KS0108 driver is PARPORT_PC. Signed-off-by: H Hartley Sweeten Cc: Miguel Ojeda Sandonis Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/auxdisplay/Kconfig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/auxdisplay/Kconfig b/drivers/auxdisplay/Kconfig index 14b9d5f4c203..c07e725ea93d 100644 --- a/drivers/auxdisplay/Kconfig +++ b/drivers/auxdisplay/Kconfig @@ -6,7 +6,6 @@ # menuconfig AUXDISPLAY - depends on PARPORT bool "Auxiliary Display support" ---help--- Say Y here to get to see options for auxiliary display drivers. @@ -14,7 +13,7 @@ menuconfig AUXDISPLAY If you say N, all options in this submenu will be skipped and disabled. -if AUXDISPLAY && PARPORT +if AUXDISPLAY config KS0108 tristate "KS0108 LCD Controller" -- cgit v1.2.3 From 891f7d73ea30f925596b90bcf21020bfc5d90f3f Mon Sep 17 00:00:00 2001 From: David Altobelli Date: Tue, 31 Mar 2009 15:23:53 -0700 Subject: hpilo: reduce frequency of IO operations Change hpilo open and close logic to spin for 10usec between checking device, rather than every usec. Because the loop is coded to take up to 10ms, it seemed prudent to increase the interval between polling the device, to reduce the load on the system and allow more other work to happen. Signed-off-by: David Altobelli Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/misc/hpilo.c | 6 +++--- drivers/misc/hpilo.h | 6 +++++- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/hpilo.c b/drivers/misc/hpilo.c index cf991850f01b..880ccf39e23b 100644 --- a/drivers/misc/hpilo.c +++ b/drivers/misc/hpilo.c @@ -209,7 +209,7 @@ static void ilo_ccb_close(struct pci_dev *pdev, struct ccb_data *data) /* give iLO some time to process stop request */ for (retries = MAX_WAIT; retries > 0; retries--) { doorbell_set(driver_ccb); - udelay(1); + udelay(WAIT_TIME); if (!(ioread32(&device_ccb->send_ctrl) & (1 << CTRL_BITPOS_A)) && !(ioread32(&device_ccb->recv_ctrl) & (1 << CTRL_BITPOS_A))) @@ -312,7 +312,7 @@ static int ilo_ccb_open(struct ilo_hwinfo *hw, struct ccb_data *data, int slot) for (i = MAX_WAIT; i > 0; i--) { if (ilo_pkt_dequeue(hw, driver_ccb, SENDQ, &pkt_id, NULL, NULL)) break; - udelay(1); + udelay(WAIT_TIME); } if (i) { @@ -759,7 +759,7 @@ static void __exit ilo_exit(void) class_destroy(ilo_class); } -MODULE_VERSION("1.0"); +MODULE_VERSION("1.1"); MODULE_ALIAS(ILO_NAME); MODULE_DESCRIPTION(ILO_NAME); MODULE_AUTHOR("David Altobelli "); diff --git a/drivers/misc/hpilo.h b/drivers/misc/hpilo.h index b64a20ef07e3..03a14c82aad9 100644 --- a/drivers/misc/hpilo.h +++ b/drivers/misc/hpilo.h @@ -19,8 +19,12 @@ #define MAX_ILO_DEV 1 /* max number of files */ #define MAX_OPEN (MAX_CCB * MAX_ILO_DEV) +/* total wait time in usec */ +#define MAX_WAIT_TIME 10000 +/* per spin wait time in usec */ +#define WAIT_TIME 10 /* spin counter for open/close delay */ -#define MAX_WAIT 10000 +#define MAX_WAIT (MAX_WAIT_TIME / WAIT_TIME) /* * Per device, used to track global memory allocations. -- cgit v1.2.3 From 3cdbbeebb77348176bd6a03fd86e11bc281c529e Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Tue, 31 Mar 2009 15:23:53 -0700 Subject: drivers/misc/isl29003.c: driver for the ISL29003 ambient light sensor Add a driver for Intersil's ISL29003 ambient light sensor device plus some documentation. Inspired by tsl2550.c, a driver for a similar device. It is put in drivers/misc for now until the industrial I/O framework gets merged. Signed-off-by: Daniel Mack Acked-by: Jonathan Cameron Cc: Jean Delvare Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/misc-devices/isl29003 | 62 +++++ drivers/misc/Kconfig | 10 + drivers/misc/Makefile | 1 + drivers/misc/isl29003.c | 470 ++++++++++++++++++++++++++++++++++++ 4 files changed, 543 insertions(+) create mode 100644 Documentation/misc-devices/isl29003 create mode 100644 drivers/misc/isl29003.c (limited to 'drivers') diff --git a/Documentation/misc-devices/isl29003 b/Documentation/misc-devices/isl29003 new file mode 100644 index 000000000000..c4ff5f38e010 --- /dev/null +++ b/Documentation/misc-devices/isl29003 @@ -0,0 +1,62 @@ +Kernel driver isl29003 +===================== + +Supported chips: +* Intersil ISL29003 +Prefix: 'isl29003' +Addresses scanned: none +Datasheet: +http://www.intersil.com/data/fn/fn7464.pdf + +Author: Daniel Mack + + +Description +----------- +The ISL29003 is an integrated light sensor with a 16-bit integrating type +ADC, I2C user programmable lux range select for optimized counts/lux, and +I2C multi-function control and monitoring capabilities. The internal ADC +provides 16-bit resolution while rejecting 50Hz and 60Hz flicker caused by +artificial light sources. + +The driver allows to set the lux range, the bit resolution, the operational +mode (see below) and the power state of device and can read the current lux +value, of course. + + +Detection +--------- + +The ISL29003 does not have an ID register which could be used to identify +it, so the detection routine will just try to read from the configured I2C +addess and consider the device to be present as soon as it ACKs the +transfer. + + +Sysfs entries +------------- + +range: + 0: 0 lux to 1000 lux (default) + 1: 0 lux to 4000 lux + 2: 0 lux to 16,000 lux + 3: 0 lux to 64,000 lux + +resolution: + 0: 2^16 cycles (default) + 1: 2^12 cycles + 2: 2^8 cycles + 3: 2^4 cycles + +mode: + 0: diode1's current (unsigned 16bit) (default) + 1: diode1's current (unsigned 16bit) + 2: difference between diodes (l1 - l2, signed 15bit) + +power_state: + 0: device is disabled (default) + 1: device is enabled + +lux (read only): + returns the value from the last sensor reading + diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 1c484084ed4f..5f3bff434621 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -223,6 +223,16 @@ config DELL_LAPTOP This driver adds support for rfkill and backlight control to Dell laptops. +config ISL29003 + tristate "Intersil ISL29003 ambient light sensor" + depends on I2C && SYSFS + help + If you say yes here you get support for the Intersil ISL29003 + ambient light sensor. + + This driver can also be built as a module. If so, the module + will be called isl29003. + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index bc1199830554..7871f05dcb9b 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -18,5 +18,6 @@ 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_ISL29003) += isl29003.o obj-$(CONFIG_C2PORT) += c2port/ obj-y += eeprom/ diff --git a/drivers/misc/isl29003.c b/drivers/misc/isl29003.c new file mode 100644 index 000000000000..2e2a5923d4c2 --- /dev/null +++ b/drivers/misc/isl29003.c @@ -0,0 +1,470 @@ +/* + * isl29003.c - Linux kernel module for + * Intersil ISL29003 ambient light sensor + * + * See file:Documentation/misc-devices/isl29003 + * + * Copyright (c) 2009 Daniel Mack + * + * Based on code written by + * Rodolfo Giometti + * Eurotech S.p.A. + * + * 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 +#include +#include +#include +#include +#include + +#define ISL29003_DRV_NAME "isl29003" +#define DRIVER_VERSION "1.0" + +#define ISL29003_REG_COMMAND 0x00 +#define ISL29003_ADC_ENABLED (1 << 7) +#define ISL29003_ADC_PD (1 << 6) +#define ISL29003_TIMING_INT (1 << 5) +#define ISL29003_MODE_SHIFT (2) +#define ISL29003_MODE_MASK (0x3 << ISL29003_MODE_SHIFT) +#define ISL29003_RES_SHIFT (0) +#define ISL29003_RES_MASK (0x3 << ISL29003_RES_SHIFT) + +#define ISL29003_REG_CONTROL 0x01 +#define ISL29003_INT_FLG (1 << 5) +#define ISL29003_RANGE_SHIFT (2) +#define ISL29003_RANGE_MASK (0x3 << ISL29003_RANGE_SHIFT) +#define ISL29003_INT_PERSISTS_SHIFT (0) +#define ISL29003_INT_PERSISTS_MASK (0xf << ISL29003_INT_PERSISTS_SHIFT) + +#define ISL29003_REG_IRQ_THRESH_HI 0x02 +#define ISL29003_REG_IRQ_THRESH_LO 0x03 +#define ISL29003_REG_LSB_SENSOR 0x04 +#define ISL29003_REG_MSB_SENSOR 0x05 +#define ISL29003_REG_LSB_TIMER 0x06 +#define ISL29003_REG_MSB_TIMER 0x07 + +#define ISL29003_NUM_CACHABLE_REGS 4 + +struct isl29003_data { + struct i2c_client *client; + struct mutex lock; + u8 reg_cache[ISL29003_NUM_CACHABLE_REGS]; +}; + +static int gain_range[] = { + 1000, 4000, 16000, 64000 +}; + +/* + * register access helpers + */ + +static int __isl29003_read_reg(struct i2c_client *client, + u32 reg, u8 mask, u8 shift) +{ + struct isl29003_data *data = i2c_get_clientdata(client); + return (data->reg_cache[reg] & mask) >> shift; +} + +static int __isl29003_write_reg(struct i2c_client *client, + u32 reg, u8 mask, u8 shift, u8 val) +{ + struct isl29003_data *data = i2c_get_clientdata(client); + int ret = 0; + u8 tmp; + + if (reg >= ISL29003_NUM_CACHABLE_REGS) + return -EINVAL; + + mutex_lock(&data->lock); + + tmp = data->reg_cache[reg]; + tmp &= ~mask; + tmp |= val << shift; + + ret = i2c_smbus_write_byte_data(client, reg, tmp); + if (!ret) + data->reg_cache[reg] = tmp; + + mutex_unlock(&data->lock); + return ret; +} + +/* + * internally used functions + */ + +/* range */ +static int isl29003_get_range(struct i2c_client *client) +{ + return __isl29003_read_reg(client, ISL29003_REG_CONTROL, + ISL29003_RANGE_MASK, ISL29003_RANGE_SHIFT); +} + +static int isl29003_set_range(struct i2c_client *client, int range) +{ + return __isl29003_write_reg(client, ISL29003_REG_CONTROL, + ISL29003_RANGE_MASK, ISL29003_RANGE_SHIFT, range); +} + +/* resolution */ +static int isl29003_get_resolution(struct i2c_client *client) +{ + return __isl29003_read_reg(client, ISL29003_REG_COMMAND, + ISL29003_RES_MASK, ISL29003_RES_SHIFT); +} + +static int isl29003_set_resolution(struct i2c_client *client, int res) +{ + return __isl29003_write_reg(client, ISL29003_REG_COMMAND, + ISL29003_RES_MASK, ISL29003_RES_SHIFT, res); +} + +/* mode */ +static int isl29003_get_mode(struct i2c_client *client) +{ + return __isl29003_read_reg(client, ISL29003_REG_COMMAND, + ISL29003_RES_MASK, ISL29003_RES_SHIFT); +} + +static int isl29003_set_mode(struct i2c_client *client, int mode) +{ + return __isl29003_write_reg(client, ISL29003_REG_COMMAND, + ISL29003_RES_MASK, ISL29003_RES_SHIFT, mode); +} + +/* power_state */ +static int isl29003_set_power_state(struct i2c_client *client, int state) +{ + return __isl29003_write_reg(client, ISL29003_REG_COMMAND, + ISL29003_ADC_ENABLED | ISL29003_ADC_PD, 0, + state ? ISL29003_ADC_ENABLED : ISL29003_ADC_PD); +} + +static int isl29003_get_power_state(struct i2c_client *client) +{ + struct isl29003_data *data = i2c_get_clientdata(client); + u8 cmdreg = data->reg_cache[ISL29003_REG_COMMAND]; + return ~cmdreg & ISL29003_ADC_PD; +} + +static int isl29003_get_adc_value(struct i2c_client *client) +{ + struct isl29003_data *data = i2c_get_clientdata(client); + int lsb, msb, range, bitdepth; + + mutex_lock(&data->lock); + lsb = i2c_smbus_read_byte_data(client, ISL29003_REG_LSB_SENSOR); + + if (lsb < 0) { + mutex_unlock(&data->lock); + return lsb; + } + + msb = i2c_smbus_read_byte_data(client, ISL29003_REG_MSB_SENSOR); + mutex_unlock(&data->lock); + + if (msb < 0) + return msb; + + range = isl29003_get_range(client); + bitdepth = (4 - isl29003_get_resolution(client)) * 4; + return (((msb << 8) | lsb) * gain_range[range]) >> bitdepth; +} + +/* + * sysfs layer + */ + +/* range */ +static ssize_t isl29003_show_range(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + return sprintf(buf, "%i\n", isl29003_get_range(client)); +} + +static ssize_t isl29003_store_range(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned long val; + int ret; + + if ((strict_strtoul(buf, 10, &val) < 0) || (val > 3)) + return -EINVAL; + + ret = isl29003_set_range(client, val); + if (ret < 0) + return ret; + + return count; +} + +static DEVICE_ATTR(range, S_IWUSR | S_IRUGO, + isl29003_show_range, isl29003_store_range); + + +/* resolution */ +static ssize_t isl29003_show_resolution(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + return sprintf(buf, "%d\n", isl29003_get_resolution(client)); +} + +static ssize_t isl29003_store_resolution(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned long val; + int ret; + + if ((strict_strtoul(buf, 10, &val) < 0) || (val > 3)) + return -EINVAL; + + ret = isl29003_set_resolution(client, val); + if (ret < 0) + return ret; + + return count; +} + +static DEVICE_ATTR(resolution, S_IWUSR | S_IRUGO, + isl29003_show_resolution, isl29003_store_resolution); + +/* mode */ +static ssize_t isl29003_show_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + return sprintf(buf, "%d\n", isl29003_get_mode(client)); +} + +static ssize_t isl29003_store_mode(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned long val; + int ret; + + if ((strict_strtoul(buf, 10, &val) < 0) || (val > 2)) + return -EINVAL; + + ret = isl29003_set_mode(client, val); + if (ret < 0) + return ret; + + return count; +} + +static DEVICE_ATTR(mode, S_IWUSR | S_IRUGO, + isl29003_show_mode, isl29003_store_mode); + + +/* power state */ +static ssize_t isl29003_show_power_state(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + return sprintf(buf, "%d\n", isl29003_get_power_state(client)); +} + +static ssize_t isl29003_store_power_state(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned long val; + int ret; + + if ((strict_strtoul(buf, 10, &val) < 0) || (val > 1)) + return -EINVAL; + + ret = isl29003_set_power_state(client, val); + return ret ? ret : count; +} + +static DEVICE_ATTR(power_state, S_IWUSR | S_IRUGO, + isl29003_show_power_state, isl29003_store_power_state); + + +/* lux */ +static ssize_t isl29003_show_lux(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + + /* No LUX data if not operational */ + if (!isl29003_get_power_state(client)) + return -EBUSY; + + return sprintf(buf, "%d\n", isl29003_get_adc_value(client)); +} + +static DEVICE_ATTR(lux, S_IRUGO, isl29003_show_lux, NULL); + +static struct attribute *isl29003_attributes[] = { + &dev_attr_range.attr, + &dev_attr_resolution.attr, + &dev_attr_mode.attr, + &dev_attr_power_state.attr, + &dev_attr_lux.attr, + NULL +}; + +static const struct attribute_group isl29003_attr_group = { + .attrs = isl29003_attributes, +}; + +static int isl29003_init_client(struct i2c_client *client) +{ + struct isl29003_data *data = i2c_get_clientdata(client); + int i; + + /* read all the registers once to fill the cache. + * if one of the reads fails, we consider the init failed */ + for (i = 0; i < ARRAY_SIZE(data->reg_cache); i++) { + int v = i2c_smbus_read_byte_data(client, i); + if (v < 0) + return -ENODEV; + + data->reg_cache[i] = v; + } + + /* set defaults */ + isl29003_set_range(client, 0); + isl29003_set_resolution(client, 0); + isl29003_set_mode(client, 0); + isl29003_set_power_state(client, 0); + + return 0; +} + +/* + * I2C layer + */ + +static int __devinit isl29003_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct isl29003_data *data; + int err = 0; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) + return -EIO; + + data = kzalloc(sizeof(struct isl29003_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->client = client; + i2c_set_clientdata(client, data); + mutex_init(&data->lock); + + /* initialize the ISL29003 chip */ + err = isl29003_init_client(client); + if (err) + goto exit_kfree; + + /* register sysfs hooks */ + err = sysfs_create_group(&client->dev.kobj, &isl29003_attr_group); + if (err) + goto exit_kfree; + + dev_info(&client->dev, "driver version %s enabled\n", DRIVER_VERSION); + return 0; + +exit_kfree: + kfree(data); + return err; +} + +static int __devexit isl29003_remove(struct i2c_client *client) +{ + sysfs_remove_group(&client->dev.kobj, &isl29003_attr_group); + isl29003_set_power_state(client, 0); + kfree(i2c_get_clientdata(client)); + return 0; +} + +#ifdef CONFIG_PM +static int isl29003_suspend(struct i2c_client *client, pm_message_t mesg) +{ + return isl29003_set_power_state(client, 0); +} + +static int isl29003_resume(struct i2c_client *client) +{ + int i; + struct isl29003_data *data = i2c_get_clientdata(client); + + /* restore registers from cache */ + for (i = 0; i < ARRAY_SIZE(data->reg_cache); i++) + if (!i2c_smbus_write_byte_data(client, i, data->reg_cache[i])) + return -EIO; + + return 0; +} + +#else +#define isl29003_suspend NULL +#define isl29003_resume NULL +#endif /* CONFIG_PM */ + +static const struct i2c_device_id isl29003_id[] = { + { "isl29003", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, isl29003_id); + +static struct i2c_driver isl29003_driver = { + .driver = { + .name = ISL29003_DRV_NAME, + .owner = THIS_MODULE, + }, + .suspend = isl29003_suspend, + .resume = isl29003_resume, + .probe = isl29003_probe, + .remove = __devexit_p(isl29003_remove), + .id_table = isl29003_id, +}; + +static int __init isl29003_init(void) +{ + return i2c_add_driver(&isl29003_driver); +} + +static void __exit isl29003_exit(void) +{ + i2c_del_driver(&isl29003_driver); +} + +MODULE_AUTHOR("Daniel Mack "); +MODULE_DESCRIPTION("ISL29003 ambient light sensor driver"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(DRIVER_VERSION); + +module_init(isl29003_init); +module_exit(isl29003_exit); + -- cgit v1.2.3 From 4b19449db074eec86ae31a96d3cdca4aa7f138ab Mon Sep 17 00:00:00 2001 From: Davide Libenzi Date: Tue, 31 Mar 2009 15:24:24 -0700 Subject: epoll keyed wakeups: make tty use keyed wakeups Introduce keyed event wakeups inside the TTY code. Signed-off-by: Davide Libenzi Cc: Alan Cox Cc: Ingo Molnar Cc: David Miller Cc: William Lee Irwin III Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/tty_io.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 224f271d8cbe..33dac94922a7 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -464,7 +464,7 @@ void tty_wakeup(struct tty_struct *tty) tty_ldisc_deref(ld); } } - wake_up_interruptible(&tty->write_wait); + wake_up_interruptible_poll(&tty->write_wait, POLLOUT); } EXPORT_SYMBOL_GPL(tty_wakeup); @@ -587,8 +587,8 @@ static void do_tty_hangup(struct work_struct *work) * FIXME: Once we trust the LDISC code better we can wait here for * ldisc completion and fix the driver call race */ - wake_up_interruptible(&tty->write_wait); - wake_up_interruptible(&tty->read_wait); + wake_up_interruptible_poll(&tty->write_wait, POLLOUT); + wake_up_interruptible_poll(&tty->read_wait, POLLIN); /* * Shutdown the current line discipline, and reset it to * N_TTY. @@ -879,7 +879,7 @@ void stop_tty(struct tty_struct *tty) if (tty->link && tty->link->packet) { tty->ctrl_status &= ~TIOCPKT_START; tty->ctrl_status |= TIOCPKT_STOP; - wake_up_interruptible(&tty->link->read_wait); + wake_up_interruptible_poll(&tty->link->read_wait, POLLIN); } spin_unlock_irqrestore(&tty->ctrl_lock, flags); if (tty->ops->stop) @@ -913,7 +913,7 @@ void start_tty(struct tty_struct *tty) if (tty->link && tty->link->packet) { tty->ctrl_status &= ~TIOCPKT_STOP; tty->ctrl_status |= TIOCPKT_START; - wake_up_interruptible(&tty->link->read_wait); + wake_up_interruptible_poll(&tty->link->read_wait, POLLIN); } spin_unlock_irqrestore(&tty->ctrl_lock, flags); if (tty->ops->start) @@ -970,7 +970,7 @@ static ssize_t tty_read(struct file *file, char __user *buf, size_t count, void tty_write_unlock(struct tty_struct *tty) { mutex_unlock(&tty->atomic_write_lock); - wake_up_interruptible(&tty->write_wait); + wake_up_interruptible_poll(&tty->write_wait, POLLOUT); } int tty_write_lock(struct tty_struct *tty, int ndelay) @@ -1623,21 +1623,21 @@ void tty_release_dev(struct file *filp) if (tty_closing) { if (waitqueue_active(&tty->read_wait)) { - wake_up(&tty->read_wait); + wake_up_poll(&tty->read_wait, POLLIN); do_sleep++; } if (waitqueue_active(&tty->write_wait)) { - wake_up(&tty->write_wait); + wake_up_poll(&tty->write_wait, POLLOUT); do_sleep++; } } if (o_tty_closing) { if (waitqueue_active(&o_tty->read_wait)) { - wake_up(&o_tty->read_wait); + wake_up_poll(&o_tty->read_wait, POLLIN); do_sleep++; } if (waitqueue_active(&o_tty->write_wait)) { - wake_up(&o_tty->write_wait); + wake_up_poll(&o_tty->write_wait, POLLOUT); do_sleep++; } } -- cgit v1.2.3 From be84cfc588b19f14764d78556dc7b630ee8c914c Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Tue, 31 Mar 2009 15:24:26 -0700 Subject: hp_accel: adev is poor name of exported symbol As Andrew noted, adev is pretty poor name for symbol being exported. Rename it to lis3. Signed-off-by: Pavel Machek Cc: Eric Piel Cc: Vladimir Botka Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/hwmon/hp_accel.c | 48 ++++++------- drivers/hwmon/lis3lv02d.c | 172 ++++++++++++++++++++++++---------------------- drivers/hwmon/lis3lv02d.h | 2 +- 3 files changed, 116 insertions(+), 106 deletions(-) (limited to 'drivers') diff --git a/drivers/hwmon/hp_accel.c b/drivers/hwmon/hp_accel.c index 29c83b5b9697..ee1ed4270df7 100644 --- a/drivers/hwmon/hp_accel.c +++ b/drivers/hwmon/hp_accel.c @@ -140,7 +140,7 @@ acpi_status lis3lv02d_acpi_write(acpi_handle handle, int reg, u8 val) static int lis3lv02d_dmi_matched(const struct dmi_system_id *dmi) { - adev.ac = *((struct axis_conversion *)dmi->driver_data); + lis3_dev.ac = *((struct axis_conversion *)dmi->driver_data); printk(KERN_INFO DRIVER_NAME ": hardware type %s found.\n", dmi->ident); return 1; @@ -214,7 +214,7 @@ static struct dmi_system_id lis3lv02d_dmi_ids[] = { static void hpled_set(struct delayed_led_classdev *led_cdev, enum led_brightness value) { - acpi_handle handle = adev.device->handle; + acpi_handle handle = lis3_dev.device->handle; unsigned long long ret; /* Not used when writing */ union acpi_object in_obj[1]; struct acpi_object_list args = { 1, in_obj }; @@ -254,7 +254,7 @@ static void lis3lv02d_enum_resources(struct acpi_device *device) acpi_status status; status = acpi_walk_resources(device->handle, METHOD_NAME__CRS, - lis3lv02d_get_resource, &adev.irq); + lis3lv02d_get_resource, &lis3_dev.irq); if (ACPI_FAILURE(status)) printk(KERN_DEBUG DRIVER_NAME ": Error getting resources\n"); } @@ -263,8 +263,8 @@ static s16 lis3lv02d_read_16(acpi_handle handle, int reg) { u8 lo, hi; - adev.read(handle, reg - 1, &lo); - adev.read(handle, reg, &hi); + lis3_dev.read(handle, reg - 1, &lo); + lis3_dev.read(handle, reg, &hi); /* In "12 bit right justified" mode, bit 6, bit 7, bit 8 = bit 5 */ return (s16)((hi << 8) | lo); } @@ -272,7 +272,7 @@ static s16 lis3lv02d_read_16(acpi_handle handle, int reg) static s16 lis3lv02d_read_8(acpi_handle handle, int reg) { s8 lo; - adev.read(handle, reg, &lo); + lis3_dev.read(handle, reg, &lo); return lo; } @@ -283,29 +283,29 @@ static int lis3lv02d_add(struct acpi_device *device) if (!device) return -EINVAL; - adev.device = device; - adev.init = lis3lv02d_acpi_init; - adev.read = lis3lv02d_acpi_read; - adev.write = lis3lv02d_acpi_write; + lis3_dev.device = device; + lis3_dev.init = lis3lv02d_acpi_init; + lis3_dev.read = lis3lv02d_acpi_read; + lis3_dev.write = lis3lv02d_acpi_write; strcpy(acpi_device_name(device), DRIVER_NAME); strcpy(acpi_device_class(device), ACPI_MDPS_CLASS); - device->driver_data = &adev; + device->driver_data = &lis3_dev; - lis3lv02d_acpi_read(device->handle, WHO_AM_I, &adev.whoami); - switch (adev.whoami) { + lis3lv02d_acpi_read(device->handle, WHO_AM_I, &lis3_dev.whoami); + switch (lis3_dev.whoami) { case LIS_DOUBLE_ID: printk(KERN_INFO DRIVER_NAME ": 2-byte sensor found\n"); - adev.read_data = lis3lv02d_read_16; - adev.mdps_max_val = 2048; + lis3_dev.read_data = lis3lv02d_read_16; + lis3_dev.mdps_max_val = 2048; break; case LIS_SINGLE_ID: printk(KERN_INFO DRIVER_NAME ": 1-byte sensor found\n"); - adev.read_data = lis3lv02d_read_8; - adev.mdps_max_val = 128; + lis3_dev.read_data = lis3lv02d_read_8; + lis3_dev.mdps_max_val = 128; break; default: printk(KERN_ERR DRIVER_NAME - ": unknown sensor type 0x%X\n", adev.whoami); + ": unknown sensor type 0x%X\n", lis3_dev.whoami); return -EINVAL; } @@ -313,7 +313,7 @@ static int lis3lv02d_add(struct acpi_device *device) 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; + lis3_dev.ac = lis3lv02d_axis_normal; } INIT_WORK(&hpled_led.work, delayed_set_status_worker); @@ -322,9 +322,9 @@ static int lis3lv02d_add(struct acpi_device *device) return ret; /* obtain IRQ number of our device from ACPI */ - lis3lv02d_enum_resources(adev.device); + lis3lv02d_enum_resources(lis3_dev.device); - ret = lis3lv02d_init_device(&adev); + ret = lis3lv02d_init_device(&lis3_dev); if (ret) { flush_work(&hpled_led.work); led_classdev_unregister(&hpled_led.led_classdev); @@ -360,12 +360,12 @@ static int lis3lv02d_suspend(struct acpi_device *device, pm_message_t state) 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) + mutex_lock(&lis3_dev.lock); + if (lis3_dev.usage > 0) lis3lv02d_poweron(device->handle); else lis3lv02d_poweroff(device->handle); - mutex_unlock(&adev.lock); + mutex_unlock(&lis3_dev.lock); return 0; } #else diff --git a/drivers/hwmon/lis3lv02d.c b/drivers/hwmon/lis3lv02d.c index 8bb2158f0453..4888ac5ba8cc 100644 --- a/drivers/hwmon/lis3lv02d.c +++ b/drivers/hwmon/lis3lv02d.c @@ -53,14 +53,24 @@ * joystick. */ -struct acpi_lis3lv02d adev = { - .misc_wait = __WAIT_QUEUE_HEAD_INITIALIZER(adev.misc_wait), +struct acpi_lis3lv02d lis3_dev = { + .misc_wait = __WAIT_QUEUE_HEAD_INITIALIZER(lis3_dev.misc_wait), }; -EXPORT_SYMBOL_GPL(adev); +EXPORT_SYMBOL_GPL(lis3_dev); static int lis3lv02d_add_fs(struct acpi_device *device); +static s16 lis3lv02d_read_16(acpi_handle handle, int reg) +{ + u8 lo, hi; + + lis3_dev.read(handle, reg, &lo); + lis3_dev.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 @@ -89,25 +99,25 @@ static void lis3lv02d_get_xyz(acpi_handle handle, int *x, int *y, int *z) { int position[3]; - position[0] = adev.read_data(handle, OUTX); - position[1] = adev.read_data(handle, OUTY); - position[2] = adev.read_data(handle, OUTZ); + position[0] = lis3_dev.read_data(handle, OUTX); + position[1] = lis3_dev.read_data(handle, OUTY); + position[2] = lis3_dev.read_data(handle, OUTZ); - *x = lis3lv02d_get_axis(adev.ac.x, position); - *y = lis3lv02d_get_axis(adev.ac.y, position); - *z = lis3lv02d_get_axis(adev.ac.z, position); + *x = lis3lv02d_get_axis(lis3_dev.ac.x, position); + *y = lis3lv02d_get_axis(lis3_dev.ac.y, position); + *z = lis3lv02d_get_axis(lis3_dev.ac.z, position); } void lis3lv02d_poweroff(acpi_handle handle) { - adev.is_on = 0; + lis3_dev.is_on = 0; } EXPORT_SYMBOL_GPL(lis3lv02d_poweroff); void lis3lv02d_poweron(acpi_handle handle) { - adev.is_on = 1; - adev.init(handle); + lis3_dev.is_on = 1; + lis3_dev.init(handle); } EXPORT_SYMBOL_GPL(lis3lv02d_poweron); @@ -147,10 +157,10 @@ static irqreturn_t lis302dl_interrupt(int irq, void *dummy) * the lid is closed. This leads to interrupts as soon as a little move * is done. */ - atomic_inc(&adev.count); + atomic_inc(&lis3_dev.count); - wake_up_interruptible(&adev.misc_wait); - kill_fasync(&adev.async_queue, SIGIO, POLL_IN); + wake_up_interruptible(&lis3_dev.misc_wait); + kill_fasync(&lis3_dev.async_queue, SIGIO, POLL_IN); return IRQ_HANDLED; } @@ -158,10 +168,10 @@ static int lis3lv02d_misc_open(struct inode *inode, struct file *file) { int ret; - if (test_and_set_bit(0, &adev.misc_opened)) + if (test_and_set_bit(0, &lis3_dev.misc_opened)) return -EBUSY; /* already open */ - atomic_set(&adev.count, 0); + atomic_set(&lis3_dev.count, 0); /* * The sensor can generate interrupts for free-fall and direction @@ -174,25 +184,25 @@ static int lis3lv02d_misc_open(struct inode *inode, struct file *file) * io-apic is not configurable (and generates a warning) but I keep it * in case of support for other hardware. */ - ret = request_irq(adev.irq, lis302dl_interrupt, IRQF_TRIGGER_RISING, - DRIVER_NAME, &adev); + ret = request_irq(lis3_dev.irq, lis302dl_interrupt, IRQF_TRIGGER_RISING, + DRIVER_NAME, &lis3_dev); if (ret) { - clear_bit(0, &adev.misc_opened); - printk(KERN_ERR DRIVER_NAME ": IRQ%d allocation failed\n", adev.irq); + clear_bit(0, &lis3_dev.misc_opened); + printk(KERN_ERR DRIVER_NAME ": IRQ%d allocation failed\n", lis3_dev.irq); return -EBUSY; } - lis3lv02d_increase_use(&adev); - printk("lis3: registered interrupt %d\n", adev.irq); + lis3lv02d_increase_use(&lis3_dev); + printk("lis3: registered interrupt %d\n", lis3_dev.irq); return 0; } static int lis3lv02d_misc_release(struct inode *inode, struct file *file) { - fasync_helper(-1, file, 0, &adev.async_queue); - lis3lv02d_decrease_use(&adev); - free_irq(adev.irq, &adev); - clear_bit(0, &adev.misc_opened); /* release the device */ + fasync_helper(-1, file, 0, &lis3_dev.async_queue); + lis3lv02d_decrease_use(&lis3_dev); + free_irq(lis3_dev.irq, &lis3_dev); + clear_bit(0, &lis3_dev.misc_opened); /* release the device */ return 0; } @@ -207,10 +217,10 @@ static ssize_t lis3lv02d_misc_read(struct file *file, char __user *buf, if (count < 1) return -EINVAL; - add_wait_queue(&adev.misc_wait, &wait); + add_wait_queue(&lis3_dev.misc_wait, &wait); while (true) { set_current_state(TASK_INTERRUPTIBLE); - data = atomic_xchg(&adev.count, 0); + data = atomic_xchg(&lis3_dev.count, 0); if (data) break; @@ -240,22 +250,22 @@ static ssize_t lis3lv02d_misc_read(struct file *file, char __user *buf, out: __set_current_state(TASK_RUNNING); - remove_wait_queue(&adev.misc_wait, &wait); + remove_wait_queue(&lis3_dev.misc_wait, &wait); return retval; } static unsigned int lis3lv02d_misc_poll(struct file *file, poll_table *wait) { - poll_wait(file, &adev.misc_wait, wait); - if (atomic_read(&adev.count)) + poll_wait(file, &lis3_dev.misc_wait, wait); + if (atomic_read(&lis3_dev.count)) return POLLIN | POLLRDNORM; return 0; } static int lis3lv02d_misc_fasync(int fd, struct file *file, int on) { - return fasync_helper(fd, file, on, &adev.async_queue); + return fasync_helper(fd, file, on, &lis3_dev.async_queue); } static const struct file_operations lis3lv02d_misc_fops = { @@ -283,12 +293,12 @@ 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); + lis3lv02d_get_xyz(lis3_dev.device->handle, &x, &y, &z); + input_report_abs(lis3_dev.idev, ABS_X, x - lis3_dev.xcalib); + input_report_abs(lis3_dev.idev, ABS_Y, y - lis3_dev.ycalib); + input_report_abs(lis3_dev.idev, ABS_Z, z - lis3_dev.zcalib); - input_sync(adev.idev); + input_sync(lis3_dev.idev); try_to_freeze(); msleep_interruptible(MDPS_POLL_INTERVAL); @@ -299,11 +309,11 @@ static int lis3lv02d_joystick_kthread(void *data) 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); + lis3lv02d_increase_use(&lis3_dev); + lis3_dev.kthread = kthread_run(lis3lv02d_joystick_kthread, NULL, "klis3lv02d"); + if (IS_ERR(lis3_dev.kthread)) { + lis3lv02d_decrease_use(&lis3_dev); + return PTR_ERR(lis3_dev.kthread); } return 0; @@ -311,45 +321,45 @@ static int lis3lv02d_joystick_open(struct input_dev *input) static void lis3lv02d_joystick_close(struct input_dev *input) { - kthread_stop(adev.kthread); - lis3lv02d_decrease_use(&adev); + kthread_stop(lis3_dev.kthread); + lis3lv02d_decrease_use(&lis3_dev); } static inline void lis3lv02d_calibrate_joystick(void) { - lis3lv02d_get_xyz(adev.device->handle, &adev.xcalib, &adev.ycalib, &adev.zcalib); + lis3lv02d_get_xyz(lis3_dev.device->handle, &lis3_dev.xcalib, &lis3_dev.ycalib, &lis3_dev.zcalib); } int lis3lv02d_joystick_enable(void) { int err; - if (adev.idev) + if (lis3_dev.idev) return -EINVAL; - adev.idev = input_allocate_device(); - if (!adev.idev) + lis3_dev.idev = input_allocate_device(); + if (!lis3_dev.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; + lis3_dev.idev->name = "ST LIS3LV02DL Accelerometer"; + lis3_dev.idev->phys = DRIVER_NAME "/input0"; + lis3_dev.idev->id.bustype = BUS_HOST; + lis3_dev.idev->id.vendor = 0; + lis3_dev.idev->dev.parent = &lis3_dev.pdev->dev; + lis3_dev.idev->open = lis3lv02d_joystick_open; + lis3_dev.idev->close = lis3lv02d_joystick_close; - set_bit(EV_ABS, adev.idev->evbit); - input_set_abs_params(adev.idev, ABS_X, -adev.mdps_max_val, adev.mdps_max_val, 3, 3); - input_set_abs_params(adev.idev, ABS_Y, -adev.mdps_max_val, adev.mdps_max_val, 3, 3); - input_set_abs_params(adev.idev, ABS_Z, -adev.mdps_max_val, adev.mdps_max_val, 3, 3); + set_bit(EV_ABS, lis3_dev.idev->evbit); + input_set_abs_params(lis3_dev.idev, ABS_X, -lis3_dev.mdps_max_val, lis3_dev.mdps_max_val, 3, 3); + input_set_abs_params(lis3_dev.idev, ABS_Y, -lis3_dev.mdps_max_val, lis3_dev.mdps_max_val, 3, 3); + input_set_abs_params(lis3_dev.idev, ABS_Z, -lis3_dev.mdps_max_val, lis3_dev.mdps_max_val, 3, 3); - err = input_register_device(adev.idev); + err = input_register_device(lis3_dev.idev); if (err) { - input_free_device(adev.idev); - adev.idev = NULL; + input_free_device(lis3_dev.idev); + lis3_dev.idev = NULL; } return err; @@ -358,12 +368,12 @@ EXPORT_SYMBOL_GPL(lis3lv02d_joystick_enable); void lis3lv02d_joystick_disable(void) { - if (!adev.idev) + if (!lis3_dev.idev) return; misc_deregister(&lis3lv02d_misc_device); - input_unregister_device(adev.idev); - adev.idev = NULL; + input_unregister_device(lis3_dev.idev); + lis3_dev.idev = NULL; } EXPORT_SYMBOL_GPL(lis3lv02d_joystick_disable); @@ -404,25 +414,25 @@ static ssize_t lis3lv02d_position_show(struct device *dev, { int x, y, z; - lis3lv02d_increase_use(&adev); - lis3lv02d_get_xyz(adev.device->handle, &x, &y, &z); - lis3lv02d_decrease_use(&adev); + lis3lv02d_increase_use(&lis3_dev); + lis3lv02d_get_xyz(lis3_dev.device->handle, &x, &y, &z); + lis3lv02d_decrease_use(&lis3_dev); 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); + return sprintf(buf, "(%d,%d,%d)\n", lis3_dev.xcalib, lis3_dev.ycalib, lis3_dev.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_increase_use(&lis3_dev); lis3lv02d_calibrate_joystick(); - lis3lv02d_decrease_use(&adev); + lis3lv02d_decrease_use(&lis3_dev); return count; } @@ -434,9 +444,9 @@ static ssize_t lis3lv02d_rate_show(struct device *dev, u8 ctrl; int val; - lis3lv02d_increase_use(&adev); - adev.read(adev.device->handle, CTRL_REG1, &ctrl); - lis3lv02d_decrease_use(&adev); + lis3lv02d_increase_use(&lis3_dev); + lis3_dev.read(lis3_dev.device->handle, CTRL_REG1, &ctrl); + lis3lv02d_decrease_use(&lis3_dev); val = (ctrl & (CTRL1_DF0 | CTRL1_DF1)) >> 4; return sprintf(buf, "%d\n", lis3lv02dl_df_val[val]); } @@ -460,17 +470,17 @@ static struct attribute_group lis3lv02d_attribute_group = { 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); + lis3_dev.pdev = platform_device_register_simple(DRIVER_NAME, -1, NULL, 0); + if (IS_ERR(lis3_dev.pdev)) + return PTR_ERR(lis3_dev.pdev); - return sysfs_create_group(&adev.pdev->dev.kobj, &lis3lv02d_attribute_group); + return sysfs_create_group(&lis3_dev.pdev->dev.kobj, &lis3lv02d_attribute_group); } int lis3lv02d_remove_fs(void) { - sysfs_remove_group(&adev.pdev->dev.kobj, &lis3lv02d_attribute_group); - platform_device_unregister(adev.pdev); + sysfs_remove_group(&lis3_dev.pdev->dev.kobj, &lis3lv02d_attribute_group); + platform_device_unregister(lis3_dev.pdev); return 0; } EXPORT_SYMBOL_GPL(lis3lv02d_remove_fs); diff --git a/drivers/hwmon/lis3lv02d.h b/drivers/hwmon/lis3lv02d.h index 75972bf372ff..017fb2b754d5 100644 --- a/drivers/hwmon/lis3lv02d.h +++ b/drivers/hwmon/lis3lv02d.h @@ -194,4 +194,4 @@ void lis3lv02d_poweroff(acpi_handle handle); void lis3lv02d_poweron(acpi_handle handle); int lis3lv02d_remove_fs(void); -extern struct acpi_lis3lv02d adev; +extern struct acpi_lis3lv02d lis3_dev; -- cgit v1.2.3 From 061603275814544842e7df77d1157eff18565997 Mon Sep 17 00:00:00 2001 From: Davide Rizzo Date: Tue, 31 Mar 2009 15:24:27 -0700 Subject: hwmon: LM95241 driver An hwmon driver for the National Semiconductor LM95241 triple temperature sensors chip Signed-off-by: Davide Rizzo Cc: Jean Delvare Cc: "Mark M. Hoffman" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/hwmon/Kconfig | 9 + drivers/hwmon/Makefile | 1 + drivers/hwmon/lm95241.c | 527 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 537 insertions(+) create mode 100644 drivers/hwmon/lm95241.c (limited to 'drivers') diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 51ff9b3d7ea2..8a91c9971558 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -582,6 +582,15 @@ config SENSORS_LTC4245 This driver can also be built as a module. If so, the module will be called ltc4245. +config SENSORS_LM95241 + tristate "National Semiconductor LM95241 sensor chip" + depends on I2C + help + If you say yes here you get support for LM95241 sensor chip. + + This driver can also be built as a module. If so, the module + will be called lm95241. + config SENSORS_MAX1111 tristate "Maxim MAX1111 Multichannel, Serial 8-bit ADC chip" depends on SPI_MASTER diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index e332d6267920..81c88822a3eb 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -64,6 +64,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_LM95241) += lm95241.o obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o obj-$(CONFIG_SENSORS_MAX1111) += max1111.o obj-$(CONFIG_SENSORS_MAX1619) += max1619.o diff --git a/drivers/hwmon/lm95241.c b/drivers/hwmon/lm95241.c new file mode 100644 index 000000000000..091d95f38aaa --- /dev/null +++ b/drivers/hwmon/lm95241.c @@ -0,0 +1,527 @@ +/* + * lm95241.c - Part of lm_sensors, Linux kernel modules for hardware + * monitoring + * Copyright (C) 2008 Davide Rizzo + * + * Based on the max1619 driver. The LM95241 is a sensor chip made by National + * Semiconductors. + * It reports up to three temperatures (its own plus up to + * two external ones). Complete datasheet can be + * obtained from National's website at: + * http://www.national.com/ds.cgi/LM/LM95241.pdf + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const unsigned short normal_i2c[] = { + 0x19, 0x2a, 0x2b, I2C_CLIENT_END}; + +/* Insmod parameters */ +I2C_CLIENT_INSMOD_1(lm95241); + +/* LM95241 registers */ +#define LM95241_REG_R_MAN_ID 0xFE +#define LM95241_REG_R_CHIP_ID 0xFF +#define LM95241_REG_R_STATUS 0x02 +#define LM95241_REG_RW_CONFIG 0x03 +#define LM95241_REG_RW_REM_FILTER 0x06 +#define LM95241_REG_RW_TRUTHERM 0x07 +#define LM95241_REG_W_ONE_SHOT 0x0F +#define LM95241_REG_R_LOCAL_TEMPH 0x10 +#define LM95241_REG_R_REMOTE1_TEMPH 0x11 +#define LM95241_REG_R_REMOTE2_TEMPH 0x12 +#define LM95241_REG_R_LOCAL_TEMPL 0x20 +#define LM95241_REG_R_REMOTE1_TEMPL 0x21 +#define LM95241_REG_R_REMOTE2_TEMPL 0x22 +#define LM95241_REG_RW_REMOTE_MODEL 0x30 + +/* LM95241 specific bitfields */ +#define CFG_STOP 0x40 +#define CFG_CR0076 0x00 +#define CFG_CR0182 0x10 +#define CFG_CR1000 0x20 +#define CFG_CR2700 0x30 +#define R1MS_SHIFT 0 +#define R2MS_SHIFT 2 +#define R1MS_MASK (0x01 << (R1MS_SHIFT)) +#define R2MS_MASK (0x01 << (R2MS_SHIFT)) +#define R1DF_SHIFT 1 +#define R2DF_SHIFT 2 +#define R1DF_MASK (0x01 << (R1DF_SHIFT)) +#define R2DF_MASK (0x01 << (R2DF_SHIFT)) +#define R1FE_MASK 0x01 +#define R2FE_MASK 0x05 +#define TT1_SHIFT 0 +#define TT2_SHIFT 4 +#define TT_OFF 0 +#define TT_ON 1 +#define TT_MASK 7 +#define MANUFACTURER_ID 0x01 +#define DEFAULT_REVISION 0xA4 + +/* Conversions and various macros */ +#define TEMP_FROM_REG(val_h, val_l) (((val_h) & 0x80 ? (val_h) - 0x100 : \ + (val_h)) * 1000 + (val_l) * 1000 / 256) + +/* Functions declaration */ +static int lm95241_attach_adapter(struct i2c_adapter *adapter); +static int lm95241_detect(struct i2c_adapter *adapter, int address, + int kind); +static void lm95241_init_client(struct i2c_client *client); +static int lm95241_detach_client(struct i2c_client *client); +static struct lm95241_data *lm95241_update_device(struct device *dev); + +/* Driver data (common to all clients) */ +static struct i2c_driver lm95241_driver = { + .driver = { + .name = "lm95241", + }, + .attach_adapter = lm95241_attach_adapter, + .detach_client = lm95241_detach_client, +}; + +/* Client data (each client gets its own) */ +struct lm95241_data { + struct i2c_client client; + struct device *hwmon_dev; + struct mutex update_lock; + unsigned long last_updated, rate; /* in jiffies */ + char valid; /* zero until following fields are valid */ + /* registers values */ + u8 local_h, local_l; /* local */ + u8 remote1_h, remote1_l; /* remote1 */ + u8 remote2_h, remote2_l; /* remote2 */ + u8 config, model, trutherm; +}; + +/* Sysfs stuff */ +#define show_temp(value) \ +static ssize_t show_##value(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct lm95241_data *data = lm95241_update_device(dev); \ + snprintf(buf, PAGE_SIZE - 1, "%d\n", \ + TEMP_FROM_REG(data->value##_h, data->value##_l)); \ + return strlen(buf); \ +} +show_temp(local); +show_temp(remote1); +show_temp(remote2); + +static ssize_t show_rate(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct lm95241_data *data = lm95241_update_device(dev); + + snprintf(buf, PAGE_SIZE - 1, "%lu\n", 1000 * data->rate / HZ); + return strlen(buf); +} + +static ssize_t set_rate(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm95241_data *data = i2c_get_clientdata(client); + + strict_strtol(buf, 10, &data->rate); + data->rate = data->rate * HZ / 1000; + + return count; +} + +#define show_type(flag) \ +static ssize_t show_type##flag(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct lm95241_data *data = i2c_get_clientdata(client); \ +\ + snprintf(buf, PAGE_SIZE - 1, \ + data->model & R##flag##MS_MASK ? "1\n" : "2\n"); \ + return strlen(buf); \ +} +show_type(1); +show_type(2); + +#define show_min(flag) \ +static ssize_t show_min##flag(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct lm95241_data *data = i2c_get_clientdata(client); \ +\ + snprintf(buf, PAGE_SIZE - 1, \ + data->config & R##flag##DF_MASK ? \ + "-127000\n" : "0\n"); \ + return strlen(buf); \ +} +show_min(1); +show_min(2); + +#define show_max(flag) \ +static ssize_t show_max##flag(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct lm95241_data *data = i2c_get_clientdata(client); \ +\ + snprintf(buf, PAGE_SIZE - 1, \ + data->config & R##flag##DF_MASK ? \ + "127000\n" : "255000\n"); \ + return strlen(buf); \ +} +show_max(1); +show_max(2); + +#define set_type(flag) \ +static ssize_t set_type##flag(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct lm95241_data *data = i2c_get_clientdata(client); \ +\ + long val; \ + strict_strtol(buf, 10, &val); \ +\ + if ((val == 1) || (val == 2)) { \ +\ + mutex_lock(&data->update_lock); \ +\ + data->trutherm &= ~(TT_MASK << TT##flag##_SHIFT); \ + if (val == 1) { \ + data->model |= R##flag##MS_MASK; \ + data->trutherm |= (TT_ON << TT##flag##_SHIFT); \ + } \ + else { \ + data->model &= ~R##flag##MS_MASK; \ + data->trutherm |= (TT_OFF << TT##flag##_SHIFT); \ + } \ +\ + data->valid = 0; \ +\ + i2c_smbus_write_byte_data(client, LM95241_REG_RW_REMOTE_MODEL, \ + data->model); \ + i2c_smbus_write_byte_data(client, LM95241_REG_RW_TRUTHERM, \ + data->trutherm); \ +\ + mutex_unlock(&data->update_lock); \ +\ + } \ + return count; \ +} +set_type(1); +set_type(2); + +#define set_min(flag) \ +static ssize_t set_min##flag(struct device *dev, \ + struct device_attribute *devattr, const char *buf, size_t count) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct lm95241_data *data = i2c_get_clientdata(client); \ +\ + long val; \ + strict_strtol(buf, 10, &val); \ +\ + mutex_lock(&data->update_lock); \ +\ + if (val < 0) \ + data->config |= R##flag##DF_MASK; \ + else \ + data->config &= ~R##flag##DF_MASK; \ +\ + data->valid = 0; \ +\ + i2c_smbus_write_byte_data(client, LM95241_REG_RW_CONFIG, \ + data->config); \ +\ + mutex_unlock(&data->update_lock); \ +\ + return count; \ +} +set_min(1); +set_min(2); + +#define set_max(flag) \ +static ssize_t set_max##flag(struct device *dev, \ + struct device_attribute *devattr, const char *buf, size_t count) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct lm95241_data *data = i2c_get_clientdata(client); \ +\ + long val; \ + strict_strtol(buf, 10, &val); \ +\ + mutex_lock(&data->update_lock); \ +\ + if (val <= 127000) \ + data->config |= R##flag##DF_MASK; \ + else \ + data->config &= ~R##flag##DF_MASK; \ +\ + data->valid = 0; \ +\ + i2c_smbus_write_byte_data(client, LM95241_REG_RW_CONFIG, \ + data->config); \ +\ + mutex_unlock(&data->update_lock); \ +\ + return count; \ +} +set_max(1); +set_max(2); + +static DEVICE_ATTR(temp1_input, S_IRUGO, show_local, NULL); +static DEVICE_ATTR(temp2_input, S_IRUGO, show_remote1, NULL); +static DEVICE_ATTR(temp3_input, S_IRUGO, show_remote2, NULL); +static DEVICE_ATTR(temp2_type, S_IWUSR | S_IRUGO, show_type1, set_type1); +static DEVICE_ATTR(temp3_type, S_IWUSR | S_IRUGO, show_type2, set_type2); +static DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_min1, set_min1); +static DEVICE_ATTR(temp3_min, S_IWUSR | S_IRUGO, show_min2, set_min2); +static DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_max1, set_max1); +static DEVICE_ATTR(temp3_max, S_IWUSR | S_IRUGO, show_max2, set_max2); +static DEVICE_ATTR(rate, S_IWUSR | S_IRUGO, show_rate, set_rate); + +static struct attribute *lm95241_attributes[] = { + &dev_attr_temp1_input.attr, + &dev_attr_temp2_input.attr, + &dev_attr_temp3_input.attr, + &dev_attr_temp2_type.attr, + &dev_attr_temp3_type.attr, + &dev_attr_temp2_min.attr, + &dev_attr_temp3_min.attr, + &dev_attr_temp2_max.attr, + &dev_attr_temp3_max.attr, + &dev_attr_rate.attr, + NULL +}; + +static const struct attribute_group lm95241_group = { + .attrs = lm95241_attributes, +}; + +/* Init/exit code */ +static int lm95241_attach_adapter(struct i2c_adapter *adapter) +{ + if (!(adapter->class & I2C_CLASS_HWMON)) + return 0; + return i2c_probe(adapter, &addr_data, lm95241_detect); +} + +/* + * The following function does more than just detection. If detection + * succeeds, it also registers the new chip. + */ +static int lm95241_detect(struct i2c_adapter *adapter, int address, int kind) +{ + struct i2c_client *new_client; + struct lm95241_data *data; + int err = 0; + const char *name = ""; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto exit; + + data = kzalloc(sizeof(struct lm95241_data), GFP_KERNEL); + if (!data) { + err = -ENOMEM; + goto exit; + } + + /* The common I2C client data is placed right before the + LM95241-specific data. */ + new_client = &data->client; + i2c_set_clientdata(new_client, data); + new_client->addr = address; + new_client->adapter = adapter; + new_client->driver = &lm95241_driver; + new_client->flags = 0; + + /* + * Now we do the remaining detection. A negative kind means that + * the driver was loaded with no force parameter (default), so we + * must both detect and identify the chip. A zero kind means that + * the driver was loaded with the force parameter, the detection + * step shall be skipped. A positive kind means that the driver + * was loaded with the force parameter and a given kind of chip is + * requested, so both the detection and the identification steps + * are skipped. + */ + if (kind < 0) { /* detection */ + if ((i2c_smbus_read_byte_data(new_client, LM95241_REG_R_MAN_ID) + != MANUFACTURER_ID) + || (i2c_smbus_read_byte_data(new_client, LM95241_REG_R_CHIP_ID) + < DEFAULT_REVISION)) { + dev_dbg(&adapter->dev, + "LM95241 detection failed at 0x%02x.\n", + address); + goto exit_free; + } + } + + if (kind <= 0) { /* identification */ + if ((i2c_smbus_read_byte_data(new_client, LM95241_REG_R_MAN_ID) + == MANUFACTURER_ID) + && (i2c_smbus_read_byte_data(new_client, LM95241_REG_R_CHIP_ID) + >= DEFAULT_REVISION)) { + + kind = lm95241; + + if (kind <= 0) { /* identification failed */ + dev_info(&adapter->dev, "Unsupported chip\n"); + goto exit_free; + } + } + } + + if (kind == lm95241) + name = "lm95241"; + + /* We can fill in the remaining client fields */ + strlcpy(new_client->name, name, I2C_NAME_SIZE); + data->valid = 0; + mutex_init(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + err = i2c_attach_client(new_client); + if (err) + goto exit_free; + + /* Initialize the LM95241 chip */ + lm95241_init_client(new_client); + + /* Register sysfs hooks */ + err = sysfs_create_group(&new_client->dev.kobj, &lm95241_group); + if (err) + goto exit_detach; + + data->hwmon_dev = hwmon_device_register(&new_client->dev); + if (IS_ERR(data->hwmon_dev)) { + err = PTR_ERR(data->hwmon_dev); + goto exit_remove_files; + } + + return 0; + +exit_remove_files: + sysfs_remove_group(&new_client->dev.kobj, &lm95241_group); +exit_detach: + i2c_detach_client(new_client); +exit_free: + kfree(data); +exit: + return err; +} + +static void lm95241_init_client(struct i2c_client *client) +{ + struct lm95241_data *data = i2c_get_clientdata(client); + + data->rate = HZ; /* 1 sec default */ + data->valid = 0; + data->config = CFG_CR0076; + data->model = 0; + data->trutherm = (TT_OFF << TT1_SHIFT) | (TT_OFF << TT2_SHIFT); + + i2c_smbus_write_byte_data(client, LM95241_REG_RW_CONFIG, + data->config); + i2c_smbus_write_byte_data(client, LM95241_REG_RW_REM_FILTER, + R1FE_MASK | R2FE_MASK); + i2c_smbus_write_byte_data(client, LM95241_REG_RW_TRUTHERM, + data->trutherm); + i2c_smbus_write_byte_data(client, LM95241_REG_RW_REMOTE_MODEL, + data->model); +} + +static int lm95241_detach_client(struct i2c_client *client) +{ + struct lm95241_data *data = i2c_get_clientdata(client); + int err; + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &lm95241_group); + + err = i2c_detach_client(client); + if (err) + return err; + + kfree(data); + return 0; +} + +static struct lm95241_data *lm95241_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm95241_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + data->rate) || + !data->valid) { + dev_dbg(&client->dev, "Updating lm95241 data.\n"); + data->local_h = + i2c_smbus_read_byte_data(client, + LM95241_REG_R_LOCAL_TEMPH); + data->local_l = + i2c_smbus_read_byte_data(client, + LM95241_REG_R_LOCAL_TEMPL); + data->remote1_h = + i2c_smbus_read_byte_data(client, + LM95241_REG_R_REMOTE1_TEMPH); + data->remote1_l = + i2c_smbus_read_byte_data(client, + LM95241_REG_R_REMOTE1_TEMPL); + data->remote2_h = + i2c_smbus_read_byte_data(client, + LM95241_REG_R_REMOTE2_TEMPH); + data->remote2_l = + i2c_smbus_read_byte_data(client, + LM95241_REG_R_REMOTE2_TEMPL); + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + + return data; +} + +static int __init sensors_lm95241_init(void) +{ + return i2c_add_driver(&lm95241_driver); +} + +static void __exit sensors_lm95241_exit(void) +{ + i2c_del_driver(&lm95241_driver); +} + +MODULE_AUTHOR("Davide Rizzo "); +MODULE_DESCRIPTION("LM95241 sensor driver"); +MODULE_LICENSE("GPL"); + +module_init(sensors_lm95241_init); +module_exit(sensors_lm95241_exit); -- cgit v1.2.3 From 72f5de92e199f96cfcea125aefc76c138d8c553c Mon Sep 17 00:00:00 2001 From: Ira Snyder Date: Tue, 31 Mar 2009 15:24:29 -0700 Subject: hwmon: Add LTC4215 driver Add Linux support for the Linear Technology LTC4215 Hot Swap controller I2C monitoring interface. I have tested the driver with my board, and it appears to work fine. With the power supplies disabled, it reads 11.93V input, 1.93V output, no current and no power. With the supplies enabled, it reads 11.93V input, 11.98V output, no current, no power. I'm not drawing any current at the moment, so this is reasonable. The value in the sense register never reads anything except 0, so I expect to get zero from the current and power calculations. I didn't attempt to support changing any of the chip's settings or enabling the FET. I'm not sure even how to do that and still fit within the hwmon framework. :) Signed-off-by: Ira W. Snyder Cc: Jean Delvare Cc: "Mark M. Hoffman" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/hwmon/ltc4215 | 50 ++++++ drivers/hwmon/Kconfig | 11 ++ drivers/hwmon/Makefile | 1 + drivers/hwmon/ltc4215.c | 364 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 426 insertions(+) create mode 100644 Documentation/hwmon/ltc4215 create mode 100644 drivers/hwmon/ltc4215.c (limited to 'drivers') diff --git a/Documentation/hwmon/ltc4215 b/Documentation/hwmon/ltc4215 new file mode 100644 index 000000000000..2e6a21eb656c --- /dev/null +++ b/Documentation/hwmon/ltc4215 @@ -0,0 +1,50 @@ +Kernel driver ltc4215 +===================== + +Supported chips: + * Linear Technology LTC4215 + Prefix: 'ltc4215' + Addresses scanned: 0x44 + Datasheet: + http://www.linear.com/pc/downloadDocument.do?navId=H0,C1,C1003,C1006,C1163,P17572,D12697 + +Author: Ira W. Snyder + + +Description +----------- + +The LTC4215 controller allows a board to be safely inserted and removed +from a live backplane. + + +Usage Notes +----------- + +This driver does not probe for LTC4215 devices, due to the fact that some +of the possible addresses are unfriendly to probing. You will need to use +the "force" parameter to tell the driver where to find the device. + +Example: the following will load the driver for an LTC4215 at address 0x44 +on I2C bus #0: +$ modprobe ltc4215 force=0,0x44 + + +Sysfs entries +------------- + +The LTC4215 has built-in limits for overvoltage, undervoltage, and +undercurrent warnings. This makes it very likely that the reference +circuit will be used. + +in1_input input voltage +in2_input output voltage + +in1_min_alarm input undervoltage alarm +in1_max_alarm input overvoltage alarm + +curr1_input current +curr1_max_alarm overcurrent alarm + +power1_input power usage +power1_alarm power bad alarm diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 8a91c9971558..bc6810c22dd7 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -571,6 +571,17 @@ config SENSORS_LM93 This driver can also be built as a module. If so, the module will be called lm93. +config SENSORS_LTC4215 + tristate "Linear Technology LTC4215" + depends on I2C && EXPERIMENTAL + default n + help + If you say yes here you get support for Linear Technology LTC4215 + Hot Swap Controller I2C interface. + + This driver can also be built as a module. If so, the module will + be called ltc4215. + config SENSORS_LTC4245 tristate "Linear Technology LTC4245" depends on I2C && EXPERIMENTAL diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 81c88822a3eb..4da261d16abf 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -65,6 +65,7 @@ obj-$(CONFIG_SENSORS_LM90) += lm90.o obj-$(CONFIG_SENSORS_LM92) += lm92.o obj-$(CONFIG_SENSORS_LM93) += lm93.o obj-$(CONFIG_SENSORS_LM95241) += lm95241.o +obj-$(CONFIG_SENSORS_LTC4215) += ltc4215.o obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o obj-$(CONFIG_SENSORS_MAX1111) += max1111.o obj-$(CONFIG_SENSORS_MAX1619) += max1619.o diff --git a/drivers/hwmon/ltc4215.c b/drivers/hwmon/ltc4215.c new file mode 100644 index 000000000000..9386e2a39211 --- /dev/null +++ b/drivers/hwmon/ltc4215.c @@ -0,0 +1,364 @@ +/* + * Driver for Linear Technology LTC4215 I2C Hot Swap Controller + * + * Copyright (C) 2009 Ira W. Snyder + * + * 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. + * + * Datasheet: + * http://www.linear.com/pc/downloadDocument.do?navId=H0,C1,C1003,C1006,C1163,P17572,D12697 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; + +/* Insmod parameters */ +I2C_CLIENT_INSMOD_1(ltc4215); + +/* Here are names of the chip's registers (a.k.a. commands) */ +enum ltc4215_cmd { + LTC4215_CONTROL = 0x00, /* rw */ + LTC4215_ALERT = 0x01, /* rw */ + LTC4215_STATUS = 0x02, /* ro */ + LTC4215_FAULT = 0x03, /* rw */ + LTC4215_SENSE = 0x04, /* rw */ + LTC4215_SOURCE = 0x05, /* rw */ + LTC4215_ADIN = 0x06, /* rw */ +}; + +struct ltc4215_data { + struct device *hwmon_dev; + + struct mutex update_lock; + bool valid; + unsigned long last_updated; /* in jiffies */ + + /* Registers */ + u8 regs[7]; +}; + +static struct ltc4215_data *ltc4215_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ltc4215_data *data = i2c_get_clientdata(client); + s32 val; + int i; + + mutex_lock(&data->update_lock); + + /* The chip's A/D updates 10 times per second */ + if (time_after(jiffies, data->last_updated + HZ / 10) || !data->valid) { + + dev_dbg(&client->dev, "Starting ltc4215 update\n"); + + /* Read all registers */ + for (i = 0; i < ARRAY_SIZE(data->regs); i++) { + val = i2c_smbus_read_byte_data(client, i); + if (unlikely(val < 0)) + data->regs[i] = 0; + else + data->regs[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 ltc4215_get_voltage(struct device *dev, u8 reg) +{ + struct ltc4215_data *data = ltc4215_update_device(dev); + const u8 regval = data->regs[reg]; + u32 voltage = 0; + + switch (reg) { + case LTC4215_SENSE: + /* 151 uV per increment */ + voltage = regval * 151 / 1000; + break; + case LTC4215_SOURCE: + /* 60.5 mV per increment */ + voltage = regval * 605 / 10; + break; + case LTC4215_ADIN: + /* The ADIN input is divided by 12.5, and has 4.82 mV + * per increment, so we have the additional multiply */ + voltage = regval * 482 * 125 / 1000; + break; + default: + /* If we get here, the developer messed up */ + WARN_ON_ONCE(1); + break; + } + + return voltage; +} + +/* Return the current from the sense resistor in mA */ +static unsigned int ltc4215_get_current(struct device *dev) +{ + struct ltc4215_data *data = ltc4215_update_device(dev); + + /* 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. + */ + + /* Calculate voltage in microVolts (151 uV per increment) */ + const unsigned int voltage = data->regs[LTC4215_SENSE] * 151; + + /* Calculate current in milliAmperes (4 milliOhm sense resistor) */ + const unsigned int curr = voltage / 4; + + return curr; +} + +static ssize_t ltc4215_show_voltage(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + const int voltage = ltc4215_get_voltage(dev, attr->index); + + return snprintf(buf, PAGE_SIZE, "%d\n", voltage); +} + +static ssize_t ltc4215_show_current(struct device *dev, + struct device_attribute *da, + char *buf) +{ + const unsigned int curr = ltc4215_get_current(dev); + + return snprintf(buf, PAGE_SIZE, "%u\n", curr); +} + +static ssize_t ltc4215_show_power(struct device *dev, + struct device_attribute *da, + char *buf) +{ + const unsigned int curr = ltc4215_get_current(dev); + const int output_voltage = ltc4215_get_voltage(dev, LTC4215_ADIN); + + /* 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 ltc4215_show_alarm(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(da); + struct ltc4215_data *data = ltc4215_update_device(dev); + const u8 reg = data->regs[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 LTC4215_VOLTAGE(name, ltc4215_cmd_idx) \ + static SENSOR_DEVICE_ATTR(name, S_IRUGO, \ + ltc4215_show_voltage, NULL, ltc4215_cmd_idx) + +#define LTC4215_CURRENT(name) \ + static SENSOR_DEVICE_ATTR(name, S_IRUGO, \ + ltc4215_show_current, NULL, 0); + +#define LTC4215_POWER(name) \ + static SENSOR_DEVICE_ATTR(name, S_IRUGO, \ + ltc4215_show_power, NULL, 0); + +#define LTC4215_ALARM(name, mask, reg) \ + static SENSOR_DEVICE_ATTR_2(name, S_IRUGO, \ + ltc4215_show_alarm, NULL, (mask), reg) + +/* Construct a sensor_device_attribute structure for each register */ + +/* Current */ +LTC4215_CURRENT(curr1_input); +LTC4215_ALARM(curr1_max_alarm, (1 << 2), LTC4215_STATUS); + +/* Power (virtual) */ +LTC4215_POWER(power1_input); +LTC4215_ALARM(power1_alarm, (1 << 3), LTC4215_STATUS); + +/* Input Voltage */ +LTC4215_VOLTAGE(in1_input, LTC4215_ADIN); +LTC4215_ALARM(in1_max_alarm, (1 << 0), LTC4215_STATUS); +LTC4215_ALARM(in1_min_alarm, (1 << 1), LTC4215_STATUS); + +/* Output Voltage */ +LTC4215_VOLTAGE(in2_input, LTC4215_SOURCE); + +/* Finally, construct an array of pointers to members of the above objects, + * as required for sysfs_create_group() + */ +static struct attribute *ltc4215_attributes[] = { + &sensor_dev_attr_curr1_input.dev_attr.attr, + &sensor_dev_attr_curr1_max_alarm.dev_attr.attr, + + &sensor_dev_attr_power1_input.dev_attr.attr, + &sensor_dev_attr_power1_alarm.dev_attr.attr, + + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in1_max_alarm.dev_attr.attr, + &sensor_dev_attr_in1_min_alarm.dev_attr.attr, + + &sensor_dev_attr_in2_input.dev_attr.attr, + + NULL, +}; + +static const struct attribute_group ltc4215_group = { + .attrs = ltc4215_attributes, +}; + +static int ltc4215_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ltc4215_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 LTC4215 chip */ + /* TODO */ + + /* Register sysfs hooks */ + ret = sysfs_create_group(&client->dev.kobj, <c4215_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, <c4215_group); +out_sysfs_create_group: + kfree(data); +out_kzalloc: + return ret; +} + +static int ltc4215_remove(struct i2c_client *client) +{ + struct ltc4215_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, <c4215_group); + + kfree(data); + + return 0; +} + +static int ltc4215_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 */ + + /* + * Register 0x01 bit b7 is reserved, expect 0 + * Register 0x03 bit b6 and b7 are reserved, expect 0 + */ + v = i2c_smbus_read_byte_data(client, LTC4215_ALERT); + if (v < 0 || (v & (1 << 7)) != 0) + return -ENODEV; + + v = i2c_smbus_read_byte_data(client, LTC4215_FAULT); + if (v < 0 || (v & ((1 << 6) | (1 << 7))) != 0) + return -ENODEV; + } + + strlcpy(info->type, "ltc4215", I2C_NAME_SIZE); + dev_info(&adapter->dev, "ltc4215 %s at address 0x%02x\n", + kind < 0 ? "probed" : "forced", + client->addr); + + return 0; +} + +static const struct i2c_device_id ltc4215_id[] = { + { "ltc4215", ltc4215 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ltc4215_id); + +/* This is the driver that will be inserted */ +static struct i2c_driver ltc4215_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "ltc4215", + }, + .probe = ltc4215_probe, + .remove = ltc4215_remove, + .id_table = ltc4215_id, + .detect = ltc4215_detect, + .address_data = &addr_data, +}; + +static int __init ltc4215_init(void) +{ + return i2c_add_driver(<c4215_driver); +} + +static void __exit ltc4215_exit(void) +{ + i2c_del_driver(<c4215_driver); +} + +MODULE_AUTHOR("Ira W. Snyder "); +MODULE_DESCRIPTION("LTC4215 driver"); +MODULE_LICENSE("GPL"); + +module_init(ltc4215_init); +module_exit(ltc4215_exit); -- cgit v1.2.3 From 9d7639d33a348d5637daa50bba27497e9f5dac64 Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Tue, 31 Mar 2009 15:24:29 -0700 Subject: hp_accel: add two more axis information Add two more laptops to whitelist. Signed-off-by: Michal Marek Signed-off-by: Pavel Machek Cc: Daniel Mack Cc: Eric Piel Cc: Vladimir Botka Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/hwmon/hp_accel.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/hwmon/hp_accel.c b/drivers/hwmon/hp_accel.c index ee1ed4270df7..448a462ad789 100644 --- a/drivers/hwmon/hp_accel.c +++ b/drivers/hwmon/hp_accel.c @@ -187,6 +187,7 @@ static struct dmi_system_id lis3lv02d_dmi_ids[] = { 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), + AXIS_DMI_MATCH("HP2140", "HP 2140", xy_swap_inverted), AXIS_DMI_MATCH("NC653x", "HP Compaq 653", xy_rotated_left_usd), AXIS_DMI_MATCH("NC673x", "HP Compaq 673", xy_rotated_left_usd), AXIS_DMI_MATCH("NC651xx", "HP Compaq 651", xy_rotated_right), @@ -201,6 +202,7 @@ static struct dmi_system_id lis3lv02d_dmi_ids[] = { PRODUCT_NAME, "HP Pavilion dv5", BOARD_NAME, "3600", y_inverted), + AXIS_DMI_MATCH("DV7", "HP Pavilion dv7", x_inverted), { NULL, } /* Laptop models without axis info (yet): * "NC6910" "HP Compaq 6910" -- cgit v1.2.3 From 12a324b6a3758f04a952b99d75a625732d767585 Mon Sep 17 00:00:00 2001 From: Luca Cappa Date: Tue, 31 Mar 2009 15:24:31 -0700 Subject: hp_accel: axis conversion for hp compaq 8710w I have a laptop HP Compaq 8710W, I compiled into my kernel the LIS3LV02DL and HP_ACCEL module drivers. While loading it cannot recognize the laptop model, so i am sending the necessary information to update the database of axis orientations. >When the laptop is horizontal the position reported is about 0 for X and Y >and a positive value for Z Yes, it is about 0,0,1000, the actual reading says: (-17,-26,1018); > If the left side is elevated, X increases (becomes positive) Yes, X goes toward to positive 1000. >If the front side (where the touchpad is) is elevated, Y decreases (becomes negative) No, Y goes toward to positive 1000. >If the laptop is put upside-down, Z becomes negative Yes, the laptop on a table Z gives 1000, and if upsidedown the Z reads -1000. So in few words the Y axis is inverted. Cc: Eric Piel Signed-off-by: Pavel Machek Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/hwmon/hp_accel.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/hwmon/hp_accel.c b/drivers/hwmon/hp_accel.c index 448a462ad789..de26b1c3a96c 100644 --- a/drivers/hwmon/hp_accel.c +++ b/drivers/hwmon/hp_accel.c @@ -203,6 +203,7 @@ static struct dmi_system_id lis3lv02d_dmi_ids[] = { BOARD_NAME, "3600", y_inverted), AXIS_DMI_MATCH("DV7", "HP Pavilion dv7", x_inverted), + AXIS_DMI_MATCH("HP8710", "HP Compaq 8710", y_inverted), { NULL, } /* Laptop models without axis info (yet): * "NC6910" "HP Compaq 6910" -- cgit v1.2.3 From ab337a632783c251a3c3852aec0ead8a0281cbdd Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Tue, 31 Mar 2009 15:24:31 -0700 Subject: lis3: reorder functions to make forward decl obsolete Move lis3lv02d_init_device() down so that the forward declaration of lis3lv02d_add_fs() becomes unnecessary. Signed-off-by: Daniel Mack Acked-by: Pavel Machek Acked-by: Eric Piel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/hwmon/lis3lv02d.c | 64 +++++++++++++++++++++++------------------------ 1 file changed, 31 insertions(+), 33 deletions(-) (limited to 'drivers') diff --git a/drivers/hwmon/lis3lv02d.c b/drivers/hwmon/lis3lv02d.c index 4888ac5ba8cc..eeae7c9600e4 100644 --- a/drivers/hwmon/lis3lv02d.c +++ b/drivers/hwmon/lis3lv02d.c @@ -59,8 +59,6 @@ struct acpi_lis3lv02d lis3_dev = { EXPORT_SYMBOL_GPL(lis3_dev); -static int lis3lv02d_add_fs(struct acpi_device *device); - static s16 lis3lv02d_read_16(acpi_handle handle, int reg) { u8 lo, hi; @@ -377,37 +375,6 @@ void lis3lv02d_joystick_disable(void) } EXPORT_SYMBOL_GPL(lis3lv02d_joystick_disable); -/* - * Initialise the accelerometer and the various subsystems. - * Should be rather independant of the bus system. - */ -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"); - - printk("lis3_init_device: irq %d\n", dev->irq); - - /* if we did not get an IRQ from ACPI - we have nothing more to do */ - if (!dev->irq) { - printk(KERN_ERR DRIVER_NAME - ": No IRQ in ACPI. Disabling /dev/freefall\n"); - goto out; - } - - printk("lis3: registering device\n"); - if (misc_register(&lis3lv02d_misc_device)) - printk(KERN_ERR DRIVER_NAME ": misc_register failed\n"); -out: - lis3lv02d_decrease_use(dev); - return 0; -} -EXPORT_SYMBOL_GPL(lis3lv02d_init_device); - /* Sysfs stuff */ static ssize_t lis3lv02d_position_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -485,6 +452,37 @@ int lis3lv02d_remove_fs(void) } EXPORT_SYMBOL_GPL(lis3lv02d_remove_fs); +/* + * Initialise the accelerometer and the various subsystems. + * Should be rather independant of the bus system. + */ +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"); + + printk("lis3_init_device: irq %d\n", dev->irq); + + /* if we did not get an IRQ from ACPI - we have nothing more to do */ + if (!dev->irq) { + printk(KERN_ERR DRIVER_NAME + ": No IRQ in ACPI. Disabling /dev/freefall\n"); + goto out; + } + + printk("lis3: registering device\n"); + if (misc_register(&lis3lv02d_misc_device)) + printk(KERN_ERR DRIVER_NAME ": misc_register failed\n"); +out: + lis3lv02d_decrease_use(dev); + return 0; +} +EXPORT_SYMBOL_GPL(lis3lv02d_init_device); + MODULE_DESCRIPTION("ST LIS3LV02Dx three-axis digital accelerometer driver"); MODULE_AUTHOR("Yan Burman, Eric Piel, Pavel Machek"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From a38da2ed74f628c1f3e907c772be21a66eccab9c Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Tue, 31 Mar 2009 15:24:32 -0700 Subject: lis3: solve dependency between core and ACPI This solves the dependency between lis3lv02d.[ch] and ACPI specific methods. It introduces a ->bus_priv pointer to the device struct which is casted to 'struct acpi_device' in the ACIP layer. Changed hp_accel.c accordingly. Signed-off-by: Daniel Mack Acked-by: Pavel Machek Acked-by: Eric Piel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/hwmon/hp_accel.c | 99 ++++++++++++++++++----------------------------- drivers/hwmon/lis3lv02d.c | 86 ++++++++++++++++++++++++++-------------- drivers/hwmon/lis3lv02d.h | 20 +++++----- 3 files changed, 105 insertions(+), 100 deletions(-) (limited to 'drivers') diff --git a/drivers/hwmon/hp_accel.c b/drivers/hwmon/hp_accel.c index de26b1c3a96c..55d3dc565be6 100644 --- a/drivers/hwmon/hp_accel.c +++ b/drivers/hwmon/hp_accel.c @@ -85,25 +85,31 @@ MODULE_DEVICE_TABLE(acpi, lis3lv02d_device_ids); /** * lis3lv02d_acpi_init - ACPI _INI method: initialize the device. - * @handle: the handle of the device + * @lis3: pointer to the device struct * - * Returns AE_OK on success. + * Returns 0 on success. */ -acpi_status lis3lv02d_acpi_init(acpi_handle handle) +int lis3lv02d_acpi_init(struct lis3lv02d *lis3) { - return acpi_evaluate_object(handle, METHOD_NAME__INI, NULL, NULL); + struct acpi_device *dev = lis3->bus_priv; + if (acpi_evaluate_object(dev->handle, METHOD_NAME__INI, + NULL, NULL) != AE_OK) + return -EINVAL; + + return 0; } /** * lis3lv02d_acpi_read - ACPI ALRD method: read a register - * @handle: the handle of the device + * @lis3: pointer to the device struct * @reg: the register to read * @ret: result of the operation * - * Returns AE_OK on success. + * Returns 0 on success. */ -acpi_status lis3lv02d_acpi_read(acpi_handle handle, int reg, u8 *ret) +int lis3lv02d_acpi_read(struct lis3lv02d *lis3, int reg, u8 *ret) { + struct acpi_device *dev = lis3->bus_priv; union acpi_object arg0 = { ACPI_TYPE_INTEGER }; struct acpi_object_list args = { 1, &arg0 }; unsigned long long lret; @@ -111,21 +117,22 @@ acpi_status lis3lv02d_acpi_read(acpi_handle handle, int reg, u8 *ret) arg0.integer.value = reg; - status = acpi_evaluate_integer(handle, "ALRD", &args, &lret); + status = acpi_evaluate_integer(dev->handle, "ALRD", &args, &lret); *ret = lret; - return status; + return (status != AE_OK) ? -EINVAL : 0; } /** * lis3lv02d_acpi_write - ACPI ALWR method: write to a register - * @handle: the handle of the device + * @lis3: pointer to the device struct * @reg: the register to write to * @val: the value to write * - * Returns AE_OK on success. + * Returns 0 on success. */ -acpi_status lis3lv02d_acpi_write(acpi_handle handle, int reg, u8 val) +int lis3lv02d_acpi_write(struct lis3lv02d *lis3, int reg, u8 val) { + struct acpi_device *dev = lis3->bus_priv; unsigned long long ret; /* Not used when writting */ union acpi_object in_obj[2]; struct acpi_object_list args = { 2, in_obj }; @@ -135,7 +142,10 @@ acpi_status lis3lv02d_acpi_write(acpi_handle handle, int reg, u8 val) in_obj[1].type = ACPI_TYPE_INTEGER; in_obj[1].integer.value = val; - return acpi_evaluate_integer(handle, "ALWR", &args, &ret); + if (acpi_evaluate_integer(dev->handle, "ALWR", &args, &ret) != AE_OK) + return -EINVAL; + + return 0; } static int lis3lv02d_dmi_matched(const struct dmi_system_id *dmi) @@ -217,7 +227,7 @@ static struct dmi_system_id lis3lv02d_dmi_ids[] = { static void hpled_set(struct delayed_led_classdev *led_cdev, enum led_brightness value) { - acpi_handle handle = lis3_dev.device->handle; + struct acpi_device *dev = lis3_dev.bus_priv; unsigned long long ret; /* Not used when writing */ union acpi_object in_obj[1]; struct acpi_object_list args = { 1, in_obj }; @@ -225,7 +235,7 @@ static void hpled_set(struct delayed_led_classdev *led_cdev, enum led_brightness in_obj[0].type = ACPI_TYPE_INTEGER; in_obj[0].integer.value = !!value; - acpi_evaluate_integer(handle, "ALED", &args, &ret); + acpi_evaluate_integer(dev->handle, "ALED", &args, &ret); } static struct delayed_led_classdev hpled_led = { @@ -262,23 +272,6 @@ static void lis3lv02d_enum_resources(struct acpi_device *device) printk(KERN_DEBUG DRIVER_NAME ": Error getting resources\n"); } -static s16 lis3lv02d_read_16(acpi_handle handle, int reg) -{ - u8 lo, hi; - - lis3_dev.read(handle, reg - 1, &lo); - lis3_dev.read(handle, reg, &hi); - /* In "12 bit right justified" mode, bit 6, bit 7, bit 8 = bit 5 */ - return (s16)((hi << 8) | lo); -} - -static s16 lis3lv02d_read_8(acpi_handle handle, int reg) -{ - s8 lo; - lis3_dev.read(handle, reg, &lo); - return lo; -} - static int lis3lv02d_add(struct acpi_device *device) { int ret; @@ -286,7 +279,7 @@ static int lis3lv02d_add(struct acpi_device *device) if (!device) return -EINVAL; - lis3_dev.device = device; + lis3_dev.bus_priv = device; lis3_dev.init = lis3lv02d_acpi_init; lis3_dev.read = lis3lv02d_acpi_read; lis3_dev.write = lis3lv02d_acpi_write; @@ -294,23 +287,8 @@ static int lis3lv02d_add(struct acpi_device *device) strcpy(acpi_device_class(device), ACPI_MDPS_CLASS); device->driver_data = &lis3_dev; - lis3lv02d_acpi_read(device->handle, WHO_AM_I, &lis3_dev.whoami); - switch (lis3_dev.whoami) { - case LIS_DOUBLE_ID: - printk(KERN_INFO DRIVER_NAME ": 2-byte sensor found\n"); - lis3_dev.read_data = lis3lv02d_read_16; - lis3_dev.mdps_max_val = 2048; - break; - case LIS_SINGLE_ID: - printk(KERN_INFO DRIVER_NAME ": 1-byte sensor found\n"); - lis3_dev.read_data = lis3lv02d_read_8; - lis3_dev.mdps_max_val = 128; - break; - default: - printk(KERN_ERR DRIVER_NAME - ": unknown sensor type 0x%X\n", lis3_dev.whoami); - return -EINVAL; - } + /* obtain IRQ number of our device from ACPI */ + lis3lv02d_enum_resources(device); /* If possible use a "standard" axes order */ if (dmi_check_system(lis3lv02d_dmi_ids) == 0) { @@ -319,18 +297,17 @@ static int lis3lv02d_add(struct acpi_device *device) lis3_dev.ac = lis3lv02d_axis_normal; } - INIT_WORK(&hpled_led.work, delayed_set_status_worker); - ret = led_classdev_register(NULL, &hpled_led.led_classdev); + /* call the core layer do its init */ + ret = lis3lv02d_init_device(&lis3_dev); if (ret) return ret; - /* obtain IRQ number of our device from ACPI */ - lis3lv02d_enum_resources(lis3_dev.device); - - ret = lis3lv02d_init_device(&lis3_dev); + INIT_WORK(&hpled_led.work, delayed_set_status_worker); + ret = led_classdev_register(NULL, &hpled_led.led_classdev); if (ret) { + lis3lv02d_joystick_disable(); + lis3lv02d_poweroff(&lis3_dev); flush_work(&hpled_led.work); - led_classdev_unregister(&hpled_led.led_classdev); return ret; } @@ -343,7 +320,7 @@ static int lis3lv02d_remove(struct acpi_device *device, int type) return -EINVAL; lis3lv02d_joystick_disable(); - lis3lv02d_poweroff(device->handle); + lis3lv02d_poweroff(&lis3_dev); flush_work(&hpled_led.work); led_classdev_unregister(&hpled_led.led_classdev); @@ -356,7 +333,7 @@ static int lis3lv02d_remove(struct acpi_device *device, int type) 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); + lis3lv02d_poweroff(&lis3_dev); return 0; } @@ -365,9 +342,9 @@ static int lis3lv02d_resume(struct acpi_device *device) /* put back the device in the right state (ACPI might turn it on) */ mutex_lock(&lis3_dev.lock); if (lis3_dev.usage > 0) - lis3lv02d_poweron(device->handle); + lis3lv02d_poweron(&lis3_dev); else - lis3lv02d_poweroff(device->handle); + lis3lv02d_poweroff(&lis3_dev); mutex_unlock(&lis3_dev.lock); return 0; } diff --git a/drivers/hwmon/lis3lv02d.c b/drivers/hwmon/lis3lv02d.c index eeae7c9600e4..778eb7795983 100644 --- a/drivers/hwmon/lis3lv02d.c +++ b/drivers/hwmon/lis3lv02d.c @@ -36,7 +36,6 @@ #include #include #include -#include #include #include "lis3lv02d.h" @@ -53,18 +52,27 @@ * joystick. */ -struct acpi_lis3lv02d lis3_dev = { +struct lis3lv02d lis3_dev = { .misc_wait = __WAIT_QUEUE_HEAD_INITIALIZER(lis3_dev.misc_wait), }; EXPORT_SYMBOL_GPL(lis3_dev); -static s16 lis3lv02d_read_16(acpi_handle handle, int reg) +static s16 lis3lv02d_read_8(struct lis3lv02d *lis3, int reg) +{ + s8 lo; + if (lis3->read(lis3, reg, &lo) < 0) + return 0; + + return lo; +} + +static s16 lis3lv02d_read_16(struct lis3lv02d *lis3, int reg) { u8 lo, hi; - lis3_dev.read(handle, reg, &lo); - lis3_dev.read(handle, reg + 1, &hi); + lis3->read(lis3, reg - 1, &lo); + lis3->read(lis3, reg, &hi); /* In "12 bit right justified" mode, bit 6, bit 7, bit 8 = bit 5 */ return (s16)((hi << 8) | lo); } @@ -86,36 +94,36 @@ static inline int lis3lv02d_get_axis(s8 axis, int hw_values[3]) /** * 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 + * @lis3: pointer to the device struct + * @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) +static void lis3lv02d_get_xyz(struct lis3lv02d *lis3, int *x, int *y, int *z) { int position[3]; - position[0] = lis3_dev.read_data(handle, OUTX); - position[1] = lis3_dev.read_data(handle, OUTY); - position[2] = lis3_dev.read_data(handle, OUTZ); + position[0] = lis3_dev.read_data(lis3, OUTX); + position[1] = lis3_dev.read_data(lis3, OUTY); + position[2] = lis3_dev.read_data(lis3, OUTZ); *x = lis3lv02d_get_axis(lis3_dev.ac.x, position); *y = lis3lv02d_get_axis(lis3_dev.ac.y, position); *z = lis3lv02d_get_axis(lis3_dev.ac.z, position); } -void lis3lv02d_poweroff(acpi_handle handle) +void lis3lv02d_poweroff(struct lis3lv02d *lis3) { lis3_dev.is_on = 0; } EXPORT_SYMBOL_GPL(lis3lv02d_poweroff); -void lis3lv02d_poweron(acpi_handle handle) +void lis3lv02d_poweron(struct lis3lv02d *lis3) { lis3_dev.is_on = 1; - lis3_dev.init(handle); + lis3_dev.init(lis3); } EXPORT_SYMBOL_GPL(lis3lv02d_poweron); @@ -124,13 +132,13 @@ EXPORT_SYMBOL_GPL(lis3lv02d_poweron); * 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) +static void lis3lv02d_increase_use(struct lis3lv02d *dev) { mutex_lock(&dev->lock); dev->usage++; if (dev->usage == 1) { if (!dev->is_on) - lis3lv02d_poweron(dev->device->handle); + lis3lv02d_poweron(dev); } mutex_unlock(&dev->lock); } @@ -139,12 +147,12 @@ static void lis3lv02d_increase_use(struct acpi_lis3lv02d *dev) * 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) +static void lis3lv02d_decrease_use(struct lis3lv02d *dev) { mutex_lock(&dev->lock); dev->usage--; if (dev->usage == 0) - lis3lv02d_poweroff(dev->device->handle); + lis3lv02d_poweroff(dev); mutex_unlock(&dev->lock); } @@ -291,7 +299,7 @@ static int lis3lv02d_joystick_kthread(void *data) int x, y, z; while (!kthread_should_stop()) { - lis3lv02d_get_xyz(lis3_dev.device->handle, &x, &y, &z); + lis3lv02d_get_xyz(&lis3_dev, &x, &y, &z); input_report_abs(lis3_dev.idev, ABS_X, x - lis3_dev.xcalib); input_report_abs(lis3_dev.idev, ABS_Y, y - lis3_dev.ycalib); input_report_abs(lis3_dev.idev, ABS_Z, z - lis3_dev.zcalib); @@ -325,7 +333,8 @@ static void lis3lv02d_joystick_close(struct input_dev *input) static inline void lis3lv02d_calibrate_joystick(void) { - lis3lv02d_get_xyz(lis3_dev.device->handle, &lis3_dev.xcalib, &lis3_dev.ycalib, &lis3_dev.zcalib); + lis3lv02d_get_xyz(&lis3_dev, + &lis3_dev.xcalib, &lis3_dev.ycalib, &lis3_dev.zcalib); } int lis3lv02d_joystick_enable(void) @@ -382,7 +391,7 @@ static ssize_t lis3lv02d_position_show(struct device *dev, int x, y, z; lis3lv02d_increase_use(&lis3_dev); - lis3lv02d_get_xyz(lis3_dev.device->handle, &x, &y, &z); + lis3lv02d_get_xyz(&lis3_dev, &x, &y, &z); lis3lv02d_decrease_use(&lis3_dev); return sprintf(buf, "(%d,%d,%d)\n", x, y, z); } @@ -412,7 +421,7 @@ static ssize_t lis3lv02d_rate_show(struct device *dev, int val; lis3lv02d_increase_use(&lis3_dev); - lis3_dev.read(lis3_dev.device->handle, CTRL_REG1, &ctrl); + lis3_dev.read(&lis3_dev, CTRL_REG1, &ctrl); lis3lv02d_decrease_use(&lis3_dev); val = (ctrl & (CTRL1_DF0 | CTRL1_DF1)) >> 4; return sprintf(buf, "%d\n", lis3lv02dl_df_val[val]); @@ -435,7 +444,7 @@ static struct attribute_group lis3lv02d_attribute_group = { }; -static int lis3lv02d_add_fs(struct acpi_device *device) +static int lis3lv02d_add_fs(struct lis3lv02d *lis3) { lis3_dev.pdev = platform_device_register_simple(DRIVER_NAME, -1, NULL, 0); if (IS_ERR(lis3_dev.pdev)) @@ -456,10 +465,29 @@ EXPORT_SYMBOL_GPL(lis3lv02d_remove_fs); * Initialise the accelerometer and the various subsystems. * Should be rather independant of the bus system. */ -int lis3lv02d_init_device(struct acpi_lis3lv02d *dev) +int lis3lv02d_init_device(struct lis3lv02d *dev) { + dev->whoami = lis3lv02d_read_8(dev, WHO_AM_I); + + switch (dev->whoami) { + case LIS_DOUBLE_ID: + printk(KERN_INFO DRIVER_NAME ": 2-byte sensor found\n"); + dev->read_data = lis3lv02d_read_16; + dev->mdps_max_val = 2048; + break; + case LIS_SINGLE_ID: + printk(KERN_INFO DRIVER_NAME ": 1-byte sensor found\n"); + dev->read_data = lis3lv02d_read_8; + dev->mdps_max_val = 128; + break; + default: + printk(KERN_ERR DRIVER_NAME + ": unknown sensor type 0x%X\n", lis3_dev.whoami); + return -EINVAL; + } + mutex_init(&dev->lock); - lis3lv02d_add_fs(dev->device); + lis3lv02d_add_fs(dev); lis3lv02d_increase_use(dev); if (lis3lv02d_joystick_enable()) @@ -467,10 +495,10 @@ int lis3lv02d_init_device(struct acpi_lis3lv02d *dev) printk("lis3_init_device: irq %d\n", dev->irq); - /* if we did not get an IRQ from ACPI - we have nothing more to do */ + /* bail if we did not get an IRQ from the bus layer */ if (!dev->irq) { printk(KERN_ERR DRIVER_NAME - ": No IRQ in ACPI. Disabling /dev/freefall\n"); + ": No IRQ. Disabling /dev/freefall\n"); goto out; } diff --git a/drivers/hwmon/lis3lv02d.h b/drivers/hwmon/lis3lv02d.h index 017fb2b754d5..745ec96806d4 100644 --- a/drivers/hwmon/lis3lv02d.h +++ b/drivers/hwmon/lis3lv02d.h @@ -159,14 +159,14 @@ struct axis_conversion { s8 z; }; -struct acpi_lis3lv02d { - struct acpi_device *device; /* The ACPI device */ - acpi_status (*init) (acpi_handle handle); - acpi_status (*write) (acpi_handle handle, int reg, u8 val); - acpi_status (*read) (acpi_handle handle, int reg, u8 *ret); +struct lis3lv02d { + void *bus_priv; /* used by the bus layer only */ + int (*init) (struct lis3lv02d *lis3); + int (*write) (struct lis3lv02d *lis3, int reg, u8 val); + int (*read) (struct lis3lv02d *lis3, int reg, u8 *ret); u8 whoami; /* 3Ah: 2-byte registries, 3Bh: 1-byte registries */ - s16 (*read_data) (acpi_handle handle, int reg); + s16 (*read_data) (struct lis3lv02d *lis3, int reg); int mdps_max_val; struct input_dev *idev; /* input device */ @@ -187,11 +187,11 @@ struct acpi_lis3lv02d { unsigned long misc_opened; /* bit0: whether the device is open */ }; -int lis3lv02d_init_device(struct acpi_lis3lv02d *dev); +int lis3lv02d_init_device(struct lis3lv02d *lis3); int lis3lv02d_joystick_enable(void); void lis3lv02d_joystick_disable(void); -void lis3lv02d_poweroff(acpi_handle handle); -void lis3lv02d_poweron(acpi_handle handle); +void lis3lv02d_poweroff(struct lis3lv02d *lis3); +void lis3lv02d_poweron(struct lis3lv02d *lis3); int lis3lv02d_remove_fs(void); -extern struct acpi_lis3lv02d lis3_dev; +extern struct lis3lv02d lis3_dev; -- cgit v1.2.3 From bb233fdfc7b7cefe45bfa2e8d1b24e79c60a48e5 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Tue, 31 Mar 2009 15:24:33 -0700 Subject: lis3: SPI transport layer Make use of the new abstraction layer and add a new transport layer for spi. Works fine on a PXA based board. Signed-off-by: Daniel Mack Acked-by: Pavel Machek Acked-by: Eric Piel Cc: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/hwmon/Kconfig | 16 ++++++ drivers/hwmon/Makefile | 1 + drivers/hwmon/lis3lv02d_spi.c | 114 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 131 insertions(+) create mode 100644 drivers/hwmon/lis3lv02d_spi.c (limited to 'drivers') diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index bc6810c22dd7..ce52bf2f235e 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -932,6 +932,22 @@ config SENSORS_LIS3LV02D Say Y here if you have an applicable laptop and want to experience the awesome power of lis3lv02d. +config SENSORS_LIS3_SPI + tristate "STMicroeletronics LIS3LV02Dx three-axis digital accelerometer (SPI)" + depends on !ACPI && SPI_MASTER && INPUT + default n + help + This driver provides support for the LIS3LV02Dx accelerometer connected + via SPI. 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 modules. If so, the core module + will be called lis3lv02d and a specific module for the SPI transport + is called lis3lv02d_spi. + 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 4da261d16abf..3a6b1f06f8f4 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -52,6 +52,7 @@ obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o obj-$(CONFIG_SENSORS_IT87) += it87.o obj-$(CONFIG_SENSORS_K8TEMP) += k8temp.o obj-$(CONFIG_SENSORS_LIS3LV02D) += lis3lv02d.o hp_accel.o +obj-$(CONFIG_SENSORS_LIS3_SPI) += lis3lv02d.o lis3lv02d_spi.o obj-$(CONFIG_SENSORS_LM63) += lm63.o obj-$(CONFIG_SENSORS_LM70) += lm70.o obj-$(CONFIG_SENSORS_LM75) += lm75.o diff --git a/drivers/hwmon/lis3lv02d_spi.c b/drivers/hwmon/lis3lv02d_spi.c new file mode 100644 index 000000000000..07ae74b0e191 --- /dev/null +++ b/drivers/hwmon/lis3lv02d_spi.c @@ -0,0 +1,114 @@ +/* + * lis3lv02d_spi - SPI glue layer for lis3lv02d + * + * Copyright (c) 2009 Daniel Mack + * + * 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 + * publishhed by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lis3lv02d.h" + +#define DRV_NAME "lis3lv02d_spi" +#define LIS3_SPI_READ 0x80 + +static int lis3_spi_read(struct lis3lv02d *lis3, int reg, u8 *v) +{ + struct spi_device *spi = lis3->bus_priv; + int ret = spi_w8r8(spi, reg | LIS3_SPI_READ); + if (ret < 0) + return -EINVAL; + + *v = (u8) ret; + return 0; +} + +static int lis3_spi_write(struct lis3lv02d *lis3, int reg, u8 val) +{ + u8 tmp[2] = { reg, val }; + struct spi_device *spi = lis3->bus_priv; + return spi_write(spi, tmp, sizeof(tmp)); +} + +static int lis3_spi_init(struct lis3lv02d *lis3) +{ + u8 reg; + int ret; + + /* power up the device */ + ret = lis3->read(lis3, CTRL_REG1, ®); + if (ret < 0) + return ret; + + reg |= CTRL1_PD0; + return lis3->write(lis3, CTRL_REG1, reg); +} + +static struct axis_conversion lis3lv02d_axis_normal = { 1, 2, 3 }; + +static int __devinit lis302dl_spi_probe(struct spi_device *spi) +{ + int ret; + + spi->bits_per_word = 8; + spi->mode = SPI_MODE_0; + ret = spi_setup(spi); + if (ret < 0) + return ret; + + lis3_dev.bus_priv = spi; + lis3_dev.init = lis3_spi_init; + lis3_dev.read = lis3_spi_read; + lis3_dev.write = lis3_spi_write; + lis3_dev.irq = spi->irq; + lis3_dev.ac = lis3lv02d_axis_normal; + spi_set_drvdata(spi, &lis3_dev); + + ret = lis3lv02d_init_device(&lis3_dev); + return ret; +} + +static int __devexit lis302dl_spi_remove(struct spi_device *spi) +{ + struct lis3lv02d *lis3 = spi_get_drvdata(spi); + lis3lv02d_joystick_disable(); + lis3lv02d_poweroff(lis3); + return 0; +} + +static struct spi_driver lis302dl_spi_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, + .probe = lis302dl_spi_probe, + .remove = __devexit_p(lis302dl_spi_remove), +}; + +static int __init lis302dl_init(void) +{ + return spi_register_driver(&lis302dl_spi_driver); +} + +static void __exit lis302dl_exit(void) +{ + spi_unregister_driver(&lis302dl_spi_driver); +} + +module_init(lis302dl_init); +module_exit(lis302dl_exit); + +MODULE_AUTHOR("Daniel Mack "); +MODULE_DESCRIPTION("lis3lv02d SPI glue layer"); +MODULE_LICENSE("GPL"); + -- cgit v1.2.3 From 34c8a20c6ee6af25ee35da9ca15ba81faacfc73d Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Tue, 31 Mar 2009 15:24:35 -0700 Subject: spi_mpc83xx: fix sparse warnings The patch fixes following sparse warnings: CHECK spi_mpc83xx.c spi_mpc83xx.c:145:1: warning: symbol 'mpc83xx_spi_rx_buf_u8' was not declared. Should it be static? spi_mpc83xx.c:146:1: warning: symbol 'mpc83xx_spi_rx_buf_u16' was not declared. Should it be static? spi_mpc83xx.c:147:1: warning: symbol 'mpc83xx_spi_rx_buf_u32' was not declared. Should it be static? spi_mpc83xx.c:148:1: warning: symbol 'mpc83xx_spi_tx_buf_u8' was not declared. Should it be static? spi_mpc83xx.c:149:1: warning: symbol 'mpc83xx_spi_tx_buf_u16' was not declared. Should it be static? spi_mpc83xx.c:150:1: warning: symbol 'mpc83xx_spi_tx_buf_u32' was not declared. Should it be static? spi_mpc83xx.c:175:32: warning: incorrect type in initializer (different address spaces) spi_mpc83xx.c:175:32: expected void *tmp_ptr spi_mpc83xx.c:175:32: got unsigned int [noderef] * spi_mpc83xx.c:183:26: warning: incorrect type in argument 1 (different address spaces) spi_mpc83xx.c:183:26: expected unsigned int [noderef] [usertype] *reg spi_mpc83xx.c:183:26: got void *tmp_ptr spi_mpc83xx.c:184:26: warning: incorrect type in argument 1 (different address spaces) spi_mpc83xx.c:184:26: expected unsigned int [noderef] [usertype] *reg spi_mpc83xx.c:184:26: got void *tmp_ptr spi_mpc83xx.c:287:31: warning: incorrect type in initializer (different address spaces) spi_mpc83xx.c:287:31: expected void *tmp_ptr spi_mpc83xx.c:287:31: got unsigned int [noderef] * spi_mpc83xx.c:295:25: warning: incorrect type in argument 1 (different address spaces) spi_mpc83xx.c:295:25: expected unsigned int [noderef] [usertype] *reg spi_mpc83xx.c:295:25: got void *tmp_ptr spi_mpc83xx.c:296:25: warning: incorrect type in argument 1 (different address spaces) spi_mpc83xx.c:296:25: expected unsigned int [noderef] [usertype] *reg spi_mpc83xx.c:296:25: got void *tmp_ptr spi_mpc83xx.c:486:13: warning: symbol 'mpc83xx_spi_irq' was not declared. Should it be static? Signed-off-by: Anton Vorontsov Cc: David Brownell Cc: Benjamin Herrenschmidt Cc: Kumar Gala Cc: Grant Likely Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/spi/spi_mpc83xx.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi_mpc83xx.c b/drivers/spi/spi_mpc83xx.c index 44a2b46ccb79..df6420029004 100644 --- a/drivers/spi/spi_mpc83xx.c +++ b/drivers/spi/spi_mpc83xx.c @@ -123,6 +123,7 @@ static inline u32 mpc83xx_spi_read_reg(__be32 __iomem * reg) } #define MPC83XX_SPI_RX_BUF(type) \ +static \ void mpc83xx_spi_rx_buf_##type(u32 data, struct mpc83xx_spi *mpc83xx_spi) \ { \ type * rx = mpc83xx_spi->rx; \ @@ -131,6 +132,7 @@ void mpc83xx_spi_rx_buf_##type(u32 data, struct mpc83xx_spi *mpc83xx_spi) \ } #define MPC83XX_SPI_TX_BUF(type) \ +static \ u32 mpc83xx_spi_tx_buf_##type(struct mpc83xx_spi *mpc83xx_spi) \ { \ u32 data; \ @@ -172,7 +174,7 @@ static void mpc83xx_spi_chipselect(struct spi_device *spi, int value) if (cs->hw_mode != regval) { unsigned long flags; - void *tmp_ptr = &mpc83xx_spi->base->mode; + __be32 __iomem *mode = &mpc83xx_spi->base->mode; regval = cs->hw_mode; /* Turn off IRQs locally to minimize time that @@ -180,8 +182,8 @@ static void mpc83xx_spi_chipselect(struct spi_device *spi, int value) */ local_irq_save(flags); /* Turn off SPI unit prior changing mode */ - mpc83xx_spi_write_reg(tmp_ptr, regval & ~SPMODE_ENABLE); - mpc83xx_spi_write_reg(tmp_ptr, regval); + mpc83xx_spi_write_reg(mode, regval & ~SPMODE_ENABLE); + mpc83xx_spi_write_reg(mode, regval); local_irq_restore(flags); } if (mpc83xx_spi->activate_cs) @@ -284,7 +286,7 @@ int mpc83xx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) regval = mpc83xx_spi_read_reg(&mpc83xx_spi->base->mode); if (cs->hw_mode != regval) { unsigned long flags; - void *tmp_ptr = &mpc83xx_spi->base->mode; + __be32 __iomem *mode = &mpc83xx_spi->base->mode; regval = cs->hw_mode; /* Turn off IRQs locally to minimize time @@ -292,8 +294,8 @@ int mpc83xx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) */ local_irq_save(flags); /* Turn off SPI unit prior changing mode */ - mpc83xx_spi_write_reg(tmp_ptr, regval & ~SPMODE_ENABLE); - mpc83xx_spi_write_reg(tmp_ptr, regval); + mpc83xx_spi_write_reg(mode, regval & ~SPMODE_ENABLE); + mpc83xx_spi_write_reg(mode, regval); local_irq_restore(flags); } return 0; @@ -483,7 +485,7 @@ static int mpc83xx_spi_setup(struct spi_device *spi) return 0; } -irqreturn_t mpc83xx_spi_irq(s32 irq, void *context_data) +static irqreturn_t mpc83xx_spi_irq(s32 irq, void *context_data) { struct mpc83xx_spi *mpc83xx_spi = context_data; u32 event; -- cgit v1.2.3 From 364fdbc00fbdd409ade63500710123fe323aa164 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Tue, 31 Mar 2009 15:24:36 -0700 Subject: spi_mpc83xx: rework chip selects handling The main purpose of this patch is to pass 'struct spi_device' to the chip select handling routines. This is needed so that we could implement full-fledged OpenFirmware support for this driver. While at it, also: - Replace two {de,activate}_cs routines by single cs_contol(). - Don't duplicate platform data callbacks in mpc83xx_spi struct. Signed-off-by: Anton Vorontsov Cc: David Brownell Cc: Benjamin Herrenschmidt Cc: Kumar Gala Cc: Grant Likely Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/powerpc/platforms/83xx/mpc832x_rdb.c | 16 ++++------------ arch/powerpc/sysdev/fsl_soc.c | 14 ++++++-------- arch/powerpc/sysdev/fsl_soc.h | 5 +++-- drivers/spi/spi_mpc83xx.c | 20 +++++++------------- include/linux/fsl_devices.h | 5 +++-- 5 files changed, 23 insertions(+), 37 deletions(-) (limited to 'drivers') diff --git a/arch/powerpc/platforms/83xx/mpc832x_rdb.c b/arch/powerpc/platforms/83xx/mpc832x_rdb.c index 2a1295f19832..28e23cde64a1 100644 --- a/arch/powerpc/platforms/83xx/mpc832x_rdb.c +++ b/arch/powerpc/platforms/83xx/mpc832x_rdb.c @@ -39,16 +39,10 @@ #endif #ifdef CONFIG_QUICC_ENGINE -static void mpc83xx_spi_activate_cs(u8 cs, u8 polarity) +static void mpc83xx_spi_cs_control(struct spi_device *spi, bool on) { - pr_debug("%s %d %d\n", __func__, cs, polarity); - par_io_data_set(3, 13, polarity); -} - -static void mpc83xx_spi_deactivate_cs(u8 cs, u8 polarity) -{ - pr_debug("%s %d %d\n", __func__, cs, polarity); - par_io_data_set(3, 13, !polarity); + pr_debug("%s %d %d\n", __func__, spi->chip_select, on); + par_io_data_set(3, 13, on); } static struct mmc_spi_platform_data mpc832x_mmc_pdata = { @@ -74,9 +68,7 @@ static int __init mpc832x_spi_init(void) par_io_config_pin(3, 14, 2, 0, 0, 0); /* SD_INSERT, I */ par_io_config_pin(3, 15, 2, 0, 0, 0); /* SD_PROTECT,I */ - return fsl_spi_init(&mpc832x_spi_boardinfo, 1, - mpc83xx_spi_activate_cs, - mpc83xx_spi_deactivate_cs); + return fsl_spi_init(&mpc832x_spi_boardinfo, 1, mpc83xx_spi_cs_control); } machine_device_initcall(mpc832x_rdb, mpc832x_spi_init); #endif /* CONFIG_QUICC_ENGINE */ diff --git a/arch/powerpc/sysdev/fsl_soc.c b/arch/powerpc/sysdev/fsl_soc.c index a01c89d3f9bd..a46c1c867930 100644 --- a/arch/powerpc/sysdev/fsl_soc.c +++ b/arch/powerpc/sysdev/fsl_soc.c @@ -420,8 +420,8 @@ arch_initcall(fsl_usb_of_init); static int __init of_fsl_spi_probe(char *type, char *compatible, u32 sysclk, struct spi_board_info *board_infos, unsigned int num_board_infos, - void (*activate_cs)(u8 cs, u8 polarity), - void (*deactivate_cs)(u8 cs, u8 polarity)) + void (*cs_control)(struct spi_device *dev, + bool on)) { struct device_node *np; unsigned int i = 0; @@ -433,8 +433,7 @@ static int __init of_fsl_spi_probe(char *type, char *compatible, u32 sysclk, struct resource res[2]; struct platform_device *pdev; struct fsl_spi_platform_data pdata = { - .activate_cs = activate_cs, - .deactivate_cs = deactivate_cs, + .cs_control = cs_control, }; memset(res, 0, sizeof(res)); @@ -501,8 +500,7 @@ next: int __init fsl_spi_init(struct spi_board_info *board_infos, unsigned int num_board_infos, - void (*activate_cs)(u8 cs, u8 polarity), - void (*deactivate_cs)(u8 cs, u8 polarity)) + void (*cs_control)(struct spi_device *spi, bool on)) { u32 sysclk = -1; int ret; @@ -518,10 +516,10 @@ int __init fsl_spi_init(struct spi_board_info *board_infos, } ret = of_fsl_spi_probe(NULL, "fsl,spi", sysclk, board_infos, - num_board_infos, activate_cs, deactivate_cs); + num_board_infos, cs_control); if (!ret) of_fsl_spi_probe("spi", "fsl_spi", sysclk, board_infos, - num_board_infos, activate_cs, deactivate_cs); + num_board_infos, cs_control); return spi_register_board_info(board_infos, num_board_infos); } diff --git a/arch/powerpc/sysdev/fsl_soc.h b/arch/powerpc/sysdev/fsl_soc.h index 9c744e4285a0..b5f3456780b8 100644 --- a/arch/powerpc/sysdev/fsl_soc.h +++ b/arch/powerpc/sysdev/fsl_soc.h @@ -4,6 +4,8 @@ #include +struct spi_device; + extern phys_addr_t get_immrbase(void); #if defined(CONFIG_CPM2) || defined(CONFIG_QUICC_ENGINE) || defined(CONFIG_8xx) extern u32 get_brgfreq(void); @@ -19,8 +21,7 @@ struct device_node; extern int fsl_spi_init(struct spi_board_info *board_infos, unsigned int num_board_infos, - void (*activate_cs)(u8 cs, u8 polarity), - void (*deactivate_cs)(u8 cs, u8 polarity)); + void (*cs_control)(struct spi_device *spi, bool on)); extern void fsl_rstcr_restart(char *cmd); diff --git a/drivers/spi/spi_mpc83xx.c b/drivers/spi/spi_mpc83xx.c index df6420029004..b95085a46f90 100644 --- a/drivers/spi/spi_mpc83xx.c +++ b/drivers/spi/spi_mpc83xx.c @@ -89,9 +89,6 @@ struct mpc83xx_spi { bool qe_mode; - void (*activate_cs) (u8 cs, u8 polarity); - void (*deactivate_cs) (u8 cs, u8 polarity); - u8 busy; struct workqueue_struct *workqueue; @@ -153,15 +150,14 @@ MPC83XX_SPI_TX_BUF(u32) static void mpc83xx_spi_chipselect(struct spi_device *spi, int value) { - struct mpc83xx_spi *mpc83xx_spi; - u8 pol = spi->mode & SPI_CS_HIGH ? 1 : 0; + struct mpc83xx_spi *mpc83xx_spi = spi_master_get_devdata(spi->master); + struct fsl_spi_platform_data *pdata = spi->dev.parent->platform_data; + bool pol = spi->mode & SPI_CS_HIGH; struct spi_mpc83xx_cs *cs = spi->controller_state; - mpc83xx_spi = spi_master_get_devdata(spi->master); - if (value == BITBANG_CS_INACTIVE) { - if (mpc83xx_spi->deactivate_cs) - mpc83xx_spi->deactivate_cs(spi->chip_select, pol); + if (pdata->cs_control) + pdata->cs_control(spi, !pol); } if (value == BITBANG_CS_ACTIVE) { @@ -186,8 +182,8 @@ static void mpc83xx_spi_chipselect(struct spi_device *spi, int value) mpc83xx_spi_write_reg(mode, regval); local_irq_restore(flags); } - if (mpc83xx_spi->activate_cs) - mpc83xx_spi->activate_cs(spi->chip_select, pol); + if (pdata->cs_control) + pdata->cs_control(spi, pol); } } @@ -582,8 +578,6 @@ static int __init mpc83xx_spi_probe(struct platform_device *dev) master->cleanup = mpc83xx_spi_cleanup; mpc83xx_spi = spi_master_get_devdata(master); - mpc83xx_spi->activate_cs = pdata->activate_cs; - mpc83xx_spi->deactivate_cs = pdata->deactivate_cs; mpc83xx_spi->qe_mode = pdata->qe_mode; mpc83xx_spi->get_rx = mpc83xx_spi_rx_buf_u8; mpc83xx_spi->get_tx = mpc83xx_spi_tx_buf_u8; diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h index d9051d717d27..7bc1b643d370 100644 --- a/include/linux/fsl_devices.h +++ b/include/linux/fsl_devices.h @@ -95,14 +95,15 @@ struct fsl_usb2_platform_data { #define FSL_USB2_PORT0_ENABLED 0x00000001 #define FSL_USB2_PORT1_ENABLED 0x00000002 +struct spi_device; + struct fsl_spi_platform_data { u32 initial_spmode; /* initial SPMODE value */ u16 bus_num; bool qe_mode; /* board specific information */ u16 max_chipselect; - void (*activate_cs)(u8 cs, u8 polarity); - void (*deactivate_cs)(u8 cs, u8 polarity); + void (*cs_control)(struct spi_device *spi, bool on); u32 sysclk; }; -- cgit v1.2.3 From 35b4b3c0c1265f1a7342574be393c157601401f0 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Tue, 31 Mar 2009 15:24:37 -0700 Subject: spi_mpc83xx: add OF platform driver bindings Implement full support for OF SPI bindings. Now the driver can manage its own chip selects without any help from the board files and/or fsl_soc constructors. The "legacy" code is well isolated and could be removed as time goes by. Signed-off-by: Anton Vorontsov Cc: David Brownell Cc: Benjamin Herrenschmidt Cc: Kumar Gala Cc: Grant Likely Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/spi/spi_mpc83xx.c | 330 +++++++++++++++++++++++++++++++++++++++----- include/linux/fsl_devices.h | 2 +- 2 files changed, 295 insertions(+), 37 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi_mpc83xx.c b/drivers/spi/spi_mpc83xx.c index b95085a46f90..f4573a96af24 100644 --- a/drivers/spi/spi_mpc83xx.c +++ b/drivers/spi/spi_mpc83xx.c @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include #include @@ -23,7 +25,13 @@ #include #include #include +#include +#include +#include +#include +#include +#include #include #include @@ -79,7 +87,7 @@ struct mpc83xx_spi { u32(*get_tx) (struct mpc83xx_spi *); unsigned int count; - int irq; + unsigned int irq; unsigned nsecs; /* (clock cycle time)/2 */ @@ -543,36 +551,23 @@ static void mpc83xx_spi_cleanup(struct spi_device *spi) kfree(spi->controller_state); } -static int __init mpc83xx_spi_probe(struct platform_device *dev) +static struct spi_master * __devinit +mpc83xx_spi_probe(struct device *dev, struct resource *mem, unsigned int irq) { + struct fsl_spi_platform_data *pdata = dev->platform_data; struct spi_master *master; struct mpc83xx_spi *mpc83xx_spi; - struct fsl_spi_platform_data *pdata; - struct resource *r; u32 regval; int ret = 0; - /* Get resources(memory, IRQ) associated with the device */ - master = spi_alloc_master(&dev->dev, sizeof(struct mpc83xx_spi)); - + master = spi_alloc_master(dev, sizeof(struct mpc83xx_spi)); if (master == NULL) { ret = -ENOMEM; goto err; } - platform_set_drvdata(dev, master); - pdata = dev->dev.platform_data; - - if (pdata == NULL) { - ret = -ENODEV; - goto free_master; - } + dev_set_drvdata(dev, master); - r = platform_get_resource(dev, IORESOURCE_MEM, 0); - if (r == NULL) { - ret = -ENODEV; - goto free_master; - } master->setup = mpc83xx_spi_setup; master->transfer = mpc83xx_spi_transfer; master->cleanup = mpc83xx_spi_cleanup; @@ -592,18 +587,13 @@ static int __init mpc83xx_spi_probe(struct platform_device *dev) init_completion(&mpc83xx_spi->done); - mpc83xx_spi->base = ioremap(r->start, r->end - r->start + 1); + mpc83xx_spi->base = ioremap(mem->start, mem->end - mem->start + 1); if (mpc83xx_spi->base == NULL) { ret = -ENOMEM; goto put_master; } - mpc83xx_spi->irq = platform_get_irq(dev, 0); - - if (mpc83xx_spi->irq < 0) { - ret = -ENXIO; - goto unmap_io; - } + mpc83xx_spi->irq = irq; /* Register for SPI Interrupt */ ret = request_irq(mpc83xx_spi->irq, mpc83xx_spi_irq, @@ -645,9 +635,9 @@ static int __init mpc83xx_spi_probe(struct platform_device *dev) printk(KERN_INFO "%s: MPC83xx SPI Controller driver at 0x%p (irq = %d)\n", - dev_name(&dev->dev), mpc83xx_spi->base, mpc83xx_spi->irq); + dev_name(dev), mpc83xx_spi->base, mpc83xx_spi->irq); - return ret; + return master; unreg_master: destroy_workqueue(mpc83xx_spi->workqueue); @@ -657,18 +647,16 @@ unmap_io: iounmap(mpc83xx_spi->base); put_master: spi_master_put(master); -free_master: - kfree(master); err: - return ret; + return ERR_PTR(ret); } -static int __exit mpc83xx_spi_remove(struct platform_device *dev) +static int __devexit mpc83xx_spi_remove(struct device *dev) { struct mpc83xx_spi *mpc83xx_spi; struct spi_master *master; - master = platform_get_drvdata(dev); + master = dev_get_drvdata(dev); mpc83xx_spi = spi_master_get_devdata(master); flush_workqueue(mpc83xx_spi->workqueue); @@ -681,23 +669,293 @@ static int __exit mpc83xx_spi_remove(struct platform_device *dev) return 0; } +struct mpc83xx_spi_probe_info { + struct fsl_spi_platform_data pdata; + int *gpios; + bool *alow_flags; +}; + +static struct mpc83xx_spi_probe_info * +to_of_pinfo(struct fsl_spi_platform_data *pdata) +{ + return container_of(pdata, struct mpc83xx_spi_probe_info, pdata); +} + +static void mpc83xx_spi_cs_control(struct spi_device *spi, bool on) +{ + struct device *dev = spi->dev.parent; + struct mpc83xx_spi_probe_info *pinfo = to_of_pinfo(dev->platform_data); + u16 cs = spi->chip_select; + int gpio = pinfo->gpios[cs]; + bool alow = pinfo->alow_flags[cs]; + + gpio_set_value(gpio, on ^ alow); +} + +static int of_mpc83xx_spi_get_chipselects(struct device *dev) +{ + struct device_node *np = dev_archdata_get_node(&dev->archdata); + struct fsl_spi_platform_data *pdata = dev->platform_data; + struct mpc83xx_spi_probe_info *pinfo = to_of_pinfo(pdata); + unsigned int ngpios; + int i = 0; + int ret; + + ngpios = of_gpio_count(np); + if (!ngpios) { + /* + * SPI w/o chip-select line. One SPI device is still permitted + * though. + */ + pdata->max_chipselect = 1; + return 0; + } + + pinfo->gpios = kmalloc(ngpios * sizeof(pinfo->gpios), GFP_KERNEL); + if (!pinfo->gpios) + return -ENOMEM; + memset(pinfo->gpios, -1, ngpios * sizeof(pinfo->gpios)); + + pinfo->alow_flags = kzalloc(ngpios * sizeof(pinfo->alow_flags), + GFP_KERNEL); + if (!pinfo->alow_flags) { + ret = -ENOMEM; + goto err_alloc_flags; + } + + for (; i < ngpios; i++) { + int gpio; + enum of_gpio_flags flags; + + gpio = of_get_gpio_flags(np, i, &flags); + if (!gpio_is_valid(gpio)) { + dev_err(dev, "invalid gpio #%d: %d\n", i, gpio); + goto err_loop; + } + + ret = gpio_request(gpio, dev_name(dev)); + if (ret) { + dev_err(dev, "can't request gpio #%d: %d\n", i, ret); + goto err_loop; + } + + pinfo->gpios[i] = gpio; + pinfo->alow_flags[i] = flags & OF_GPIO_ACTIVE_LOW; + + ret = gpio_direction_output(pinfo->gpios[i], + pinfo->alow_flags[i]); + if (ret) { + dev_err(dev, "can't set output direction for gpio " + "#%d: %d\n", i, ret); + goto err_loop; + } + } + + pdata->max_chipselect = ngpios; + pdata->cs_control = mpc83xx_spi_cs_control; + + return 0; + +err_loop: + while (i >= 0) { + if (gpio_is_valid(pinfo->gpios[i])) + gpio_free(pinfo->gpios[i]); + i--; + } + + kfree(pinfo->alow_flags); + pinfo->alow_flags = NULL; +err_alloc_flags: + kfree(pinfo->gpios); + pinfo->gpios = NULL; + return ret; +} + +static int of_mpc83xx_spi_free_chipselects(struct device *dev) +{ + struct fsl_spi_platform_data *pdata = dev->platform_data; + struct mpc83xx_spi_probe_info *pinfo = to_of_pinfo(pdata); + int i; + + if (!pinfo->gpios) + return 0; + + for (i = 0; i < pdata->max_chipselect; i++) { + if (gpio_is_valid(pinfo->gpios[i])) + gpio_free(pinfo->gpios[i]); + } + + kfree(pinfo->gpios); + kfree(pinfo->alow_flags); + return 0; +} + +static int __devinit of_mpc83xx_spi_probe(struct of_device *ofdev, + const struct of_device_id *ofid) +{ + struct device *dev = &ofdev->dev; + struct device_node *np = ofdev->node; + struct mpc83xx_spi_probe_info *pinfo; + struct fsl_spi_platform_data *pdata; + struct spi_master *master; + struct resource mem; + struct resource irq; + const void *prop; + int ret = -ENOMEM; + + pinfo = kzalloc(sizeof(*pinfo), GFP_KERNEL); + if (!pinfo) + return -ENOMEM; + + pdata = &pinfo->pdata; + dev->platform_data = pdata; + + /* Allocate bus num dynamically. */ + pdata->bus_num = -1; + + /* SPI controller is either clocked from QE or SoC clock. */ + pdata->sysclk = get_brgfreq(); + if (pdata->sysclk == -1) { + pdata->sysclk = fsl_get_sys_freq(); + if (pdata->sysclk == -1) { + ret = -ENODEV; + goto err_clk; + } + } + + prop = of_get_property(np, "mode", NULL); + if (prop && !strcmp(prop, "cpu-qe")) + pdata->qe_mode = 1; + + ret = of_mpc83xx_spi_get_chipselects(dev); + if (ret) + goto err; + + ret = of_address_to_resource(np, 0, &mem); + if (ret) + goto err; + + ret = of_irq_to_resource(np, 0, &irq); + if (!ret) { + ret = -EINVAL; + goto err; + } + + master = mpc83xx_spi_probe(dev, &mem, irq.start); + if (IS_ERR(master)) { + ret = PTR_ERR(master); + goto err; + } + + of_register_spi_devices(master, np); + + return 0; + +err: + of_mpc83xx_spi_free_chipselects(dev); +err_clk: + kfree(pinfo); + return ret; +} + +static int __devexit of_mpc83xx_spi_remove(struct of_device *ofdev) +{ + int ret; + + ret = mpc83xx_spi_remove(&ofdev->dev); + if (ret) + return ret; + of_mpc83xx_spi_free_chipselects(&ofdev->dev); + return 0; +} + +static const struct of_device_id of_mpc83xx_spi_match[] = { + { .compatible = "fsl,spi" }, + {}, +}; +MODULE_DEVICE_TABLE(of, of_mpc83xx_spi_match); + +static struct of_platform_driver of_mpc83xx_spi_driver = { + .name = "mpc83xx_spi", + .match_table = of_mpc83xx_spi_match, + .probe = of_mpc83xx_spi_probe, + .remove = __devexit_p(of_mpc83xx_spi_remove), +}; + +#ifdef CONFIG_MPC832x_RDB +/* + * XXX XXX XXX + * This is "legacy" platform driver, was used by the MPC8323E-RDB boards + * only. The driver should go away soon, since newer MPC8323E-RDB's device + * tree can work with OpenFirmware driver. But for now we support old trees + * as well. + */ +static int __devinit plat_mpc83xx_spi_probe(struct platform_device *pdev) +{ + struct resource *mem; + unsigned int irq; + struct spi_master *master; + + if (!pdev->dev.platform_data) + return -EINVAL; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) + return -EINVAL; + + irq = platform_get_irq(pdev, 0); + if (!irq) + return -EINVAL; + + master = mpc83xx_spi_probe(&pdev->dev, mem, irq); + if (IS_ERR(master)) + return PTR_ERR(master); + return 0; +} + +static int __devexit plat_mpc83xx_spi_remove(struct platform_device *pdev) +{ + return mpc83xx_spi_remove(&pdev->dev); +} + MODULE_ALIAS("platform:mpc83xx_spi"); static struct platform_driver mpc83xx_spi_driver = { - .remove = __exit_p(mpc83xx_spi_remove), + .probe = plat_mpc83xx_spi_probe, + .remove = __exit_p(plat_mpc83xx_spi_remove), .driver = { .name = "mpc83xx_spi", .owner = THIS_MODULE, }, }; +static bool legacy_driver_failed; + +static void __init legacy_driver_register(void) +{ + legacy_driver_failed = platform_driver_register(&mpc83xx_spi_driver); +} + +static void __exit legacy_driver_unregister(void) +{ + if (legacy_driver_failed) + return; + platform_driver_unregister(&mpc83xx_spi_driver); +} +#else +static void __init legacy_driver_register(void) {} +static void __exit legacy_driver_unregister(void) {} +#endif /* CONFIG_MPC832x_RDB */ + static int __init mpc83xx_spi_init(void) { - return platform_driver_probe(&mpc83xx_spi_driver, mpc83xx_spi_probe); + legacy_driver_register(); + return of_register_platform_driver(&of_mpc83xx_spi_driver); } static void __exit mpc83xx_spi_exit(void) { - platform_driver_unregister(&mpc83xx_spi_driver); + of_unregister_platform_driver(&of_mpc83xx_spi_driver); + legacy_driver_unregister(); } module_init(mpc83xx_spi_init); diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h index 7bc1b643d370..7ef1caf50269 100644 --- a/include/linux/fsl_devices.h +++ b/include/linux/fsl_devices.h @@ -99,7 +99,7 @@ struct spi_device; struct fsl_spi_platform_data { u32 initial_spmode; /* initial SPMODE value */ - u16 bus_num; + s16 bus_num; bool qe_mode; /* board specific information */ u16 max_chipselect; -- cgit v1.2.3 From 3f1c6ebf57b815ad709e89291e446935fee78f75 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Tue, 31 Mar 2009 15:24:38 -0700 Subject: powerpc: add mmc-spi-slot bindings The bindings describes a case where MMC/SD/SDIO slot directly connected to a SPI bus. Such setups are widely used on embedded PowerPC boards. The patch also adds the mmc-spi-slot entry to the OpenFirmware modalias table. Signed-off-by: Anton Vorontsov Cc: David Brownell Cc: Benjamin Herrenschmidt Cc: Kumar Gala Cc: Grant Likely Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- .../powerpc/dts-bindings/mmc-spi-slot.txt | 23 ++++++++++++++++++++++ drivers/of/base.c | 1 + 2 files changed, 24 insertions(+) create mode 100644 Documentation/powerpc/dts-bindings/mmc-spi-slot.txt (limited to 'drivers') diff --git a/Documentation/powerpc/dts-bindings/mmc-spi-slot.txt b/Documentation/powerpc/dts-bindings/mmc-spi-slot.txt new file mode 100644 index 000000000000..c39ac2891951 --- /dev/null +++ b/Documentation/powerpc/dts-bindings/mmc-spi-slot.txt @@ -0,0 +1,23 @@ +MMC/SD/SDIO slot directly connected to a SPI bus + +Required properties: +- compatible : should be "mmc-spi-slot". +- reg : should specify SPI address (chip-select number). +- spi-max-frequency : maximum frequency for this device (Hz). +- voltage-ranges : two cells are required, first cell specifies minimum + slot voltage (mV), second cell specifies maximum slot voltage (mV). + Several ranges could be specified. +- gpios : (optional) may specify GPIOs in this order: Card-Detect GPIO, + Write-Protect GPIO. + +Example: + + mmc-slot@0 { + compatible = "fsl,mpc8323rdb-mmc-slot", + "mmc-spi-slot"; + reg = <0>; + gpios = <&qe_pio_d 14 1 + &qe_pio_d 15 0>; + voltage-ranges = <3300 3300>; + spi-max-frequency = <50000000>; + }; diff --git a/drivers/of/base.c b/drivers/of/base.c index cd17092b82bd..41c5dfd85358 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -446,6 +446,7 @@ struct of_modalias_table { }; static struct of_modalias_table of_modalias_table[] = { { "fsl,mcu-mpc8349emitx", "mcu-mpc8349emitx" }, + { "mmc-spi-slot", "mmc_spi" }, }; /** -- cgit v1.2.3 From 47367a3ba425d70467af0009782098235ddbf204 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 31 Mar 2009 15:24:46 -0700 Subject: rtc: convert wm8350 use new alarm and update operations These are the only two ioctls so the ioctl() function is also removed. Signed-off-by: Mark Brown Cc: Acked-by: Alessandro Zummo Cc: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-wm8350.c | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/rtc/rtc-wm8350.c b/drivers/rtc/rtc-wm8350.c index 5c5e3aa91385..616630d1e317 100644 --- a/drivers/rtc/rtc-wm8350.c +++ b/drivers/rtc/rtc-wm8350.c @@ -236,6 +236,17 @@ static int wm8350_rtc_start_alarm(struct wm8350 *wm8350) return 0; } +static int wm8350_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct wm8350 *wm8350 = dev_get_drvdata(dev); + + if (enabled) + return wm8350_rtc_start_alarm(wm8350); + else + return wm8350_rtc_stop_alarm(wm8350); +} + static int wm8350_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) { struct wm8350 *wm8350 = dev_get_drvdata(dev); @@ -291,30 +302,15 @@ static int wm8350_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) return ret; } -/* - * Handle commands from user-space - */ -static int wm8350_rtc_ioctl(struct device *dev, unsigned int cmd, - unsigned long arg) +static int wm8350_rtc_update_irq_enable(struct device *dev, + unsigned int enabled) { 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: + if (enabled) wm8350_unmask_irq(wm8350, WM8350_IRQ_RTC_SEC); - break; - - default: - return -ENOIOCTLCMD; - } + else + wm8350_mask_irq(wm8350, WM8350_IRQ_RTC_SEC); return 0; } @@ -345,11 +341,12 @@ static void wm8350_rtc_update_handler(struct wm8350 *wm8350, int irq, } 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, + .alarm_irq_enable = wm8350_rtc_alarm_irq_enable, + .update_irq_enable = wm8350_rtc_update_irq_enable, }; #ifdef CONFIG_PM -- cgit v1.2.3 From 78d89ef40c2ff7265df077e20c4d76be7d415204 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Tue, 31 Mar 2009 15:24:48 -0700 Subject: rtc: convert LEAP_YEAR into an inline - the LEAP_YEAR macro is buggy - it references its arg multiple times. Fix this by turning it into a C function. - give it a more approriate name - Move it to rtc.h so that other .c files can use it, instead of copying it. Cc: dann frazier Acked-by: Alessandro Zummo Cc: stephane eranian Cc: "Luck, Tony" Cc: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-lib.c | 7 +++---- include/linux/rtc.h | 6 ++++++ 2 files changed, 9 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/rtc/rtc-lib.c b/drivers/rtc/rtc-lib.c index dd70bf73ce9d..773851f338b8 100644 --- a/drivers/rtc/rtc-lib.c +++ b/drivers/rtc/rtc-lib.c @@ -26,14 +26,13 @@ static const unsigned short rtc_ydays[2][13] = { }; #define LEAPS_THRU_END_OF(y) ((y)/4 - (y)/100 + (y)/400) -#define LEAP_YEAR(year) ((!(year % 4) && (year % 100)) || !(year % 400)) /* * The number of days in the month. */ int rtc_month_days(unsigned int month, unsigned int year) { - return rtc_days_in_month[month] + (LEAP_YEAR(year) && month == 1); + return rtc_days_in_month[month] + (is_leap_year(year) && month == 1); } EXPORT_SYMBOL(rtc_month_days); @@ -42,7 +41,7 @@ EXPORT_SYMBOL(rtc_month_days); */ int rtc_year_days(unsigned int day, unsigned int month, unsigned int year) { - return rtc_ydays[LEAP_YEAR(year)][month] + day-1; + return rtc_ydays[is_leap_year(year)][month] + day-1; } EXPORT_SYMBOL(rtc_year_days); @@ -66,7 +65,7 @@ void rtc_time_to_tm(unsigned long time, struct rtc_time *tm) - LEAPS_THRU_END_OF(1970 - 1); if (days < 0) { year -= 1; - days += 365 + LEAP_YEAR(year); + days += 365 + is_leap_year(year); } tm->tm_year = year - 1900; tm->tm_yday = days + 1; diff --git a/include/linux/rtc.h b/include/linux/rtc.h index 4046b75563c1..60f88a7fb13d 100644 --- a/include/linux/rtc.h +++ b/include/linux/rtc.h @@ -99,6 +99,7 @@ struct rtc_pll_info { #ifdef __KERNEL__ +#include #include extern int rtc_month_days(unsigned int month, unsigned int year); @@ -232,6 +233,11 @@ int rtc_register(rtc_task_t *task); int rtc_unregister(rtc_task_t *task); int rtc_control(rtc_task_t *t, unsigned int cmd, unsigned long arg); +static inline bool is_leap_year(unsigned int year) +{ + return (!(year % 4) && (year % 100)) || !(year % 400); +} + #endif /* __KERNEL__ */ #endif /* _LINUX_RTC_H_ */ -- cgit v1.2.3 From 5e3fd9e5810f141c9c70c36992d4ed72b3aa1fed Mon Sep 17 00:00:00 2001 From: dann frazier Date: Tue, 31 Mar 2009 15:24:48 -0700 Subject: rtc: add platform driver for EFI Munge Stephane Eranian's efirtc.c code into an rtc platform driver [akpm@linux-foundation.org: use is_leap_year()] Signed-off-by: dann frazier Cc: Alessandro Zummo Cc: stephane eranian Cc: "Luck, Tony" Cc: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/ia64/kernel/time.c | 16 ++++ drivers/rtc/Kconfig | 10 +++ drivers/rtc/Makefile | 1 + drivers/rtc/rtc-efi.c | 235 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 262 insertions(+) create mode 100644 drivers/rtc/rtc-efi.c (limited to 'drivers') diff --git a/arch/ia64/kernel/time.c b/arch/ia64/kernel/time.c index f0ebb342409d..d6747bae52d8 100644 --- a/arch/ia64/kernel/time.c +++ b/arch/ia64/kernel/time.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -405,6 +406,21 @@ static struct irqaction timer_irqaction = { .name = "timer" }; +static struct platform_device rtc_efi_dev = { + .name = "rtc-efi", + .id = -1, +}; + +static int __init rtc_init(void) +{ + if (platform_device_register(&rtc_efi_dev) < 0) + printk(KERN_ERR "unable to register rtc device...\n"); + + /* not necessarily an error */ + return 0; +} +module_init(rtc_init); + void __init time_init (void) { diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 81450fbd8b12..d669b9169278 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -440,6 +440,16 @@ config RTC_DRV_DS1742 This driver can also be built as a module. If so, the module will be called rtc-ds1742. +config RTC_DRV_EFI + tristate "EFI RTC" + depends on IA64 + help + If you say yes here you will get support for the EFI + Real Time Clock. + + This driver can also be built as a module. If so, the module + will be called rtc-efi. + config RTC_DRV_STK17TA8 tristate "Simtek STK17TA8" depends on RTC_CLASS diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 0e697aa51caa..e7b09986d26e 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -36,6 +36,7 @@ obj-$(CONFIG_RTC_DRV_DS1553) += rtc-ds1553.o obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o obj-$(CONFIG_RTC_DRV_DS1742) += rtc-ds1742.o obj-$(CONFIG_RTC_DRV_DS3234) += rtc-ds3234.o +obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c new file mode 100644 index 000000000000..550292304b0f --- /dev/null +++ b/drivers/rtc/rtc-efi.c @@ -0,0 +1,235 @@ +/* + * rtc-efi: RTC Class Driver for EFI-based systems + * + * Copyright (C) 2009 Hewlett-Packard Development Company, L.P. + * + * Author: dann frazier + * Based on efirtc.c by Stephane Eranian + * + * 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 +#include +#include +#include +#include +#include + +#define EFI_ISDST (EFI_TIME_ADJUST_DAYLIGHT|EFI_TIME_IN_DAYLIGHT) +/* + * EFI Epoch is 1/1/1998 + */ +#define EFI_RTC_EPOCH 1998 + +/* + * returns day of the year [0-365] + */ +static inline int +compute_yday(efi_time_t *eft) +{ + /* efi_time_t.month is in the [1-12] so, we need -1 */ + return rtc_year_days(eft->day - 1, eft->month - 1, eft->year); +} +/* + * returns day of the week [0-6] 0=Sunday + * + * Don't try to provide a year that's before 1998, please ! + */ +static int +compute_wday(efi_time_t *eft) +{ + int y; + int ndays = 0; + + if (eft->year < 1998) { + printk(KERN_ERR "efirtc: EFI year < 1998, invalid date\n"); + return -1; + } + + for (y = EFI_RTC_EPOCH; y < eft->year; y++) + ndays += 365 + (is_leap_year(y) ? 1 : 0); + + ndays += compute_yday(eft); + + /* + * 4=1/1/1998 was a Thursday + */ + return (ndays + 4) % 7; +} + +static void +convert_to_efi_time(struct rtc_time *wtime, efi_time_t *eft) +{ + eft->year = wtime->tm_year + 1900; + eft->month = wtime->tm_mon + 1; + eft->day = wtime->tm_mday; + eft->hour = wtime->tm_hour; + eft->minute = wtime->tm_min; + eft->second = wtime->tm_sec; + eft->nanosecond = 0; + eft->daylight = wtime->tm_isdst ? EFI_ISDST : 0; + eft->timezone = EFI_UNSPECIFIED_TIMEZONE; +} + +static void +convert_from_efi_time(efi_time_t *eft, struct rtc_time *wtime) +{ + memset(wtime, 0, sizeof(*wtime)); + wtime->tm_sec = eft->second; + wtime->tm_min = eft->minute; + wtime->tm_hour = eft->hour; + wtime->tm_mday = eft->day; + wtime->tm_mon = eft->month - 1; + wtime->tm_year = eft->year - 1900; + + /* day of the week [0-6], Sunday=0 */ + wtime->tm_wday = compute_wday(eft); + + /* day in the year [1-365]*/ + wtime->tm_yday = compute_yday(eft); + + + switch (eft->daylight & EFI_ISDST) { + case EFI_ISDST: + wtime->tm_isdst = 1; + break; + case EFI_TIME_ADJUST_DAYLIGHT: + wtime->tm_isdst = 0; + break; + default: + wtime->tm_isdst = -1; + } +} + +static int efi_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) +{ + efi_time_t eft; + efi_status_t status; + + /* + * As of EFI v1.10, this call always returns an unsupported status + */ + status = efi.get_wakeup_time((efi_bool_t *)&wkalrm->enabled, + (efi_bool_t *)&wkalrm->pending, &eft); + + if (status != EFI_SUCCESS) + return -EINVAL; + + convert_from_efi_time(&eft, &wkalrm->time); + + return rtc_valid_tm(&wkalrm->time); +} + +static int efi_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) +{ + efi_time_t eft; + efi_status_t status; + + convert_to_efi_time(&wkalrm->time, &eft); + + /* + * XXX Fixme: + * As of EFI 0.92 with the firmware I have on my + * machine this call does not seem to work quite + * right + * + * As of v1.10, this call always returns an unsupported status + */ + status = efi.set_wakeup_time((efi_bool_t)wkalrm->enabled, &eft); + + printk(KERN_WARNING "write status is %d\n", (int)status); + + return status == EFI_SUCCESS ? 0 : -EINVAL; +} + +static int efi_read_time(struct device *dev, struct rtc_time *tm) +{ + efi_status_t status; + efi_time_t eft; + efi_time_cap_t cap; + + status = efi.get_time(&eft, &cap); + + if (status != EFI_SUCCESS) { + /* should never happen */ + printk(KERN_ERR "efitime: can't read time\n"); + return -EINVAL; + } + + convert_from_efi_time(&eft, tm); + + return rtc_valid_tm(tm); +} + +static int efi_set_time(struct device *dev, struct rtc_time *tm) +{ + efi_status_t status; + efi_time_t eft; + + convert_to_efi_time(tm, &eft); + + status = efi.set_time(&eft); + + return status == EFI_SUCCESS ? 0 : -EINVAL; +} + +static const struct rtc_class_ops efi_rtc_ops = { + .read_time = efi_read_time, + .set_time = efi_set_time, + .read_alarm = efi_read_alarm, + .set_alarm = efi_set_alarm, +}; + +static int __init efi_rtc_probe(struct platform_device *dev) +{ + struct rtc_device *rtc; + + rtc = rtc_device_register("rtc-efi", &dev->dev, &efi_rtc_ops, + THIS_MODULE); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + platform_set_drvdata(dev, rtc); + + return 0; +} + +static int __exit efi_rtc_remove(struct platform_device *dev) +{ + struct rtc_device *rtc = platform_get_drvdata(dev); + + rtc_device_unregister(rtc); + + return 0; +} + +static struct platform_driver efi_rtc_driver = { + .driver = { + .name = "rtc-efi", + .owner = THIS_MODULE, + }, + .probe = efi_rtc_probe, + .remove = __exit_p(efi_rtc_remove), +}; + +static int __init efi_rtc_init(void) +{ + return platform_driver_probe(&efi_rtc_driver, efi_rtc_probe); +} + +static void __exit efi_rtc_exit(void) +{ + platform_driver_unregister(&efi_rtc_driver); +} + +module_init(efi_rtc_init); +module_exit(efi_rtc_exit); + +MODULE_AUTHOR("dann frazier "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("EFI RTC driver"); -- cgit v1.2.3 From 93d456d9802a40859ecc3d67be8c759b03aa487d Mon Sep 17 00:00:00 2001 From: dann frazier Date: Tue, 31 Mar 2009 15:24:49 -0700 Subject: rtc-parisc: add a missing include for linux/rtc.h Signed-off-by: dann frazier Cc: Alessandro Zummo Cc: Kyle McMartin Cc: Grant Grundler Cc: Matthew Wilcox Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-parisc.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/rtc/rtc-parisc.c b/drivers/rtc/rtc-parisc.c index c6bfa6fe1a2a..319bb5d445ea 100644 --- a/drivers/rtc/rtc-parisc.c +++ b/drivers/rtc/rtc-parisc.c @@ -7,6 +7,7 @@ #include #include #include +#include #include -- cgit v1.2.3 From 05439f1f89aebbdb791c49e980f0f31652e4055b Mon Sep 17 00:00:00 2001 From: dann frazier Date: Tue, 31 Mar 2009 15:24:50 -0700 Subject: rtc-parisc: remove redundant locking The RTC subsystem proides ops locking, no need to implement our own Signed-off-by: dann frazier Cc: Alessandro Zummo Cc: Kyle McMartin Cc: Grant Grundler Cc: Matthew Wilcox Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-parisc.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/rtc/rtc-parisc.c b/drivers/rtc/rtc-parisc.c index 319bb5d445ea..cb087ad407f6 100644 --- a/drivers/rtc/rtc-parisc.c +++ b/drivers/rtc/rtc-parisc.c @@ -14,17 +14,13 @@ /* as simple as can be, and no simpler. */ struct parisc_rtc { struct rtc_device *rtc; - spinlock_t lock; }; static int parisc_get_time(struct device *dev, struct rtc_time *tm) { - struct parisc_rtc *p = dev_get_drvdata(dev); - unsigned long flags, ret; + unsigned long ret; - spin_lock_irqsave(&p->lock, flags); ret = get_rtc_time(tm); - spin_unlock_irqrestore(&p->lock, flags); if (ret & RTC_BATT_BAD) return -EOPNOTSUPP; @@ -34,13 +30,9 @@ static int parisc_get_time(struct device *dev, struct rtc_time *tm) static int parisc_set_time(struct device *dev, struct rtc_time *tm) { - struct parisc_rtc *p = dev_get_drvdata(dev); - unsigned long flags; int ret; - spin_lock_irqsave(&p->lock, flags); ret = set_rtc_time(tm); - spin_unlock_irqrestore(&p->lock, flags); if (ret < 0) return -EOPNOTSUPP; @@ -61,8 +53,6 @@ static int __devinit parisc_rtc_probe(struct platform_device *dev) if (!p) return -ENOMEM; - spin_lock_init(&p->lock); - p->rtc = rtc_device_register("rtc-parisc", &dev->dev, &parisc_rtc_ops, THIS_MODULE); if (IS_ERR(p->rtc)) { -- cgit v1.2.3 From 6b318f66dca829a72c974083cc10fd5556eec6f1 Mon Sep 17 00:00:00 2001 From: dann frazier Date: Tue, 31 Mar 2009 15:24:51 -0700 Subject: rtc-parisc: remove struct parisc_rtc parisc_rtc now only includes an rtc_device pointer, so let's just use the rtc_device type directly. Signed-off-by: dann frazier Cc: Alessandro Zummo Cc: Kyle McMartin Cc: Grant Grundler Cc: Matthew Wilcox Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-parisc.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/rtc/rtc-parisc.c b/drivers/rtc/rtc-parisc.c index cb087ad407f6..ee4e9a3fb583 100644 --- a/drivers/rtc/rtc-parisc.c +++ b/drivers/rtc/rtc-parisc.c @@ -11,11 +11,6 @@ #include -/* as simple as can be, and no simpler. */ -struct parisc_rtc { - struct rtc_device *rtc; -}; - static int parisc_get_time(struct device *dev, struct rtc_time *tm) { unsigned long ret; @@ -47,16 +42,16 @@ static const struct rtc_class_ops parisc_rtc_ops = { static int __devinit parisc_rtc_probe(struct platform_device *dev) { - struct parisc_rtc *p; + struct rtc_device *p; p = kzalloc(sizeof (*p), GFP_KERNEL); if (!p) return -ENOMEM; - p->rtc = rtc_device_register("rtc-parisc", &dev->dev, &parisc_rtc_ops, - THIS_MODULE); - if (IS_ERR(p->rtc)) { - int err = PTR_ERR(p->rtc); + p = rtc_device_register("rtc-parisc", &dev->dev, &parisc_rtc_ops, + THIS_MODULE); + if (IS_ERR(p)) { + int err = PTR_ERR(p); kfree(p); return err; } @@ -68,9 +63,9 @@ static int __devinit parisc_rtc_probe(struct platform_device *dev) static int __devexit parisc_rtc_remove(struct platform_device *dev) { - struct parisc_rtc *p = platform_get_drvdata(dev); + struct rtc_device *p = platform_get_drvdata(dev); - rtc_device_unregister(p->rtc); + rtc_device_unregister(p); kfree(p); return 0; -- cgit v1.2.3 From f62bacd4d48a1a6b8931a0140fb2324a06dd89fe Mon Sep 17 00:00:00 2001 From: dann frazier Date: Tue, 31 Mar 2009 15:24:52 -0700 Subject: rtc-parisc: use rtc_valid_tm() in parisc_get_time Use the return value of rtc_valid_tm() instead of just returning 0. Signed-off-by: dann frazier Cc: Alessandro Zummo Cc: Kyle McMartin Cc: Grant Grundler Cc: Matthew Wilcox Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-parisc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/rtc/rtc-parisc.c b/drivers/rtc/rtc-parisc.c index ee4e9a3fb583..0477cc129c6d 100644 --- a/drivers/rtc/rtc-parisc.c +++ b/drivers/rtc/rtc-parisc.c @@ -20,7 +20,7 @@ static int parisc_get_time(struct device *dev, struct rtc_time *tm) if (ret & RTC_BATT_BAD) return -EOPNOTSUPP; - return 0; + return rtc_valid_tm(tm); } static int parisc_set_time(struct device *dev, struct rtc_time *tm) -- cgit v1.2.3 From 2b93cff4dc184bf7b4858dc7a9bd2e8d33c1a3eb Mon Sep 17 00:00:00 2001 From: dann frazier Date: Tue, 31 Mar 2009 15:24:52 -0700 Subject: rtc-parisc: use platform_driver_probe This isn't a hotpluggable device, so call platform_driver_probe directly in parisc_rtc_init Signed-off-by: dann frazier Cc: Alessandro Zummo Cc: Kyle McMartin Cc: Grant Grundler Cc: Matthew Wilcox Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-parisc.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/rtc/rtc-parisc.c b/drivers/rtc/rtc-parisc.c index 0477cc129c6d..a2ca07ad42cb 100644 --- a/drivers/rtc/rtc-parisc.c +++ b/drivers/rtc/rtc-parisc.c @@ -40,19 +40,14 @@ static const struct rtc_class_ops parisc_rtc_ops = { .set_time = parisc_set_time, }; -static int __devinit parisc_rtc_probe(struct platform_device *dev) +static int __init parisc_rtc_probe(struct platform_device *dev) { struct rtc_device *p; - p = kzalloc(sizeof (*p), GFP_KERNEL); - if (!p) - return -ENOMEM; - p = rtc_device_register("rtc-parisc", &dev->dev, &parisc_rtc_ops, THIS_MODULE); if (IS_ERR(p)) { int err = PTR_ERR(p); - kfree(p); return err; } @@ -61,12 +56,11 @@ static int __devinit parisc_rtc_probe(struct platform_device *dev) return 0; } -static int __devexit parisc_rtc_remove(struct platform_device *dev) +static int __exit parisc_rtc_remove(struct platform_device *dev) { struct rtc_device *p = platform_get_drvdata(dev); rtc_device_unregister(p); - kfree(p); return 0; } @@ -82,7 +76,7 @@ static struct platform_driver parisc_rtc_driver = { static int __init parisc_rtc_init(void) { - return platform_driver_register(&parisc_rtc_driver); + return platform_driver_probe(&parisc_rtc_driver, parisc_rtc_probe); } static void __exit parisc_rtc_fini(void) -- cgit v1.2.3 From a8c20cd3f7e2e223898c53adfb74420db5d9ac47 Mon Sep 17 00:00:00 2001 From: dann frazier Date: Tue, 31 Mar 2009 15:24:55 -0700 Subject: rtc-parisc: remove a couple unnecessary variables Signed-off-by: dann frazier Reviewed-by: Grant Grundler Cc: Alessandro Zummo Cc: Kyle McMartin Cc: Matthew Wilcox Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-parisc.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/rtc/rtc-parisc.c b/drivers/rtc/rtc-parisc.c index a2ca07ad42cb..c29e91814c9a 100644 --- a/drivers/rtc/rtc-parisc.c +++ b/drivers/rtc/rtc-parisc.c @@ -25,11 +25,7 @@ static int parisc_get_time(struct device *dev, struct rtc_time *tm) static int parisc_set_time(struct device *dev, struct rtc_time *tm) { - int ret; - - ret = set_rtc_time(tm); - - if (ret < 0) + if (set_rtc_time(tm) < 0) return -EOPNOTSUPP; return 0; @@ -46,10 +42,8 @@ static int __init parisc_rtc_probe(struct platform_device *dev) p = rtc_device_register("rtc-parisc", &dev->dev, &parisc_rtc_ops, THIS_MODULE); - if (IS_ERR(p)) { - int err = PTR_ERR(p); - return err; - } + if (IS_ERR(p)) + return PTR_ERR(p); platform_set_drvdata(dev, p); -- cgit v1.2.3 From b250c96ea9d7bc0b9ac3ff6e878b254b0b0b6abc Mon Sep 17 00:00:00 2001 From: dann frazier Date: Tue, 31 Mar 2009 15:24:55 -0700 Subject: rtc-parisc: rename p pointer to rtc Signed-off-by: dann frazier Cc: Alessandro Zummo Cc: Kyle McMartin Cc: Grant Grundler Cc: Matthew Wilcox Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-parisc.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/rtc/rtc-parisc.c b/drivers/rtc/rtc-parisc.c index c29e91814c9a..b966f56da976 100644 --- a/drivers/rtc/rtc-parisc.c +++ b/drivers/rtc/rtc-parisc.c @@ -38,23 +38,23 @@ static const struct rtc_class_ops parisc_rtc_ops = { static int __init parisc_rtc_probe(struct platform_device *dev) { - struct rtc_device *p; + struct rtc_device *rtc; - p = rtc_device_register("rtc-parisc", &dev->dev, &parisc_rtc_ops, - THIS_MODULE); - if (IS_ERR(p)) - return PTR_ERR(p); + rtc = rtc_device_register("rtc-parisc", &dev->dev, &parisc_rtc_ops, + THIS_MODULE); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); - platform_set_drvdata(dev, p); + platform_set_drvdata(dev, rtc); return 0; } static int __exit parisc_rtc_remove(struct platform_device *dev) { - struct rtc_device *p = platform_get_drvdata(dev); + struct rtc_device *rtc = platform_get_drvdata(dev); - rtc_device_unregister(p); + rtc_device_unregister(rtc); return 0; } -- cgit v1.2.3 From 30e7b039b1f9a6d5a4e50df5469a4f347ea1aa77 Mon Sep 17 00:00:00 2001 From: Ed Swierk Date: Tue, 31 Mar 2009 15:24:56 -0700 Subject: rtc-ds1307: true SMBus compatibility Allow the rtc-ds1307 driver to work with SMBus controllers like nforce2 that do not support i2c block transfers. Signed-off-by: Ed Swierk Acked-by: Jean Delvare Acked-by: David Brownell Cc: Alessandro Zummo Cc: BARRE Sebastien Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-ds1307.c | 109 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 97 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index 7e5155e88ac7..1c975e6439b3 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -94,6 +94,10 @@ struct ds1307 { struct i2c_client *client; struct rtc_device *rtc; struct work_struct work; + s32 (*read_block_data)(struct i2c_client *client, u8 command, + u8 length, u8 *values); + s32 (*write_block_data)(struct i2c_client *client, u8 command, + u8 length, const u8 *values); }; struct chip_desc { @@ -132,6 +136,79 @@ MODULE_DEVICE_TABLE(i2c, ds1307_id); /*----------------------------------------------------------------------*/ +#define BLOCK_DATA_MAX_TRIES 10 + +static s32 ds1307_read_block_data_once(struct i2c_client *client, u8 command, + u8 length, u8 *values) +{ + s32 i, data; + + for (i = 0; i < length; i++) { + data = i2c_smbus_read_byte_data(client, command + i); + if (data < 0) + return data; + values[i] = data; + } + return i; +} + +static s32 ds1307_read_block_data(struct i2c_client *client, u8 command, + u8 length, u8 *values) +{ + u8 oldvalues[I2C_SMBUS_BLOCK_MAX]; + s32 ret; + int tries = 0; + + dev_dbg(&client->dev, "ds1307_read_block_data (length=%d)\n", length); + ret = ds1307_read_block_data_once(client, command, length, values); + if (ret < 0) + return ret; + do { + if (++tries > BLOCK_DATA_MAX_TRIES) { + dev_err(&client->dev, + "ds1307_read_block_data failed\n"); + return -EIO; + } + memcpy(oldvalues, values, length); + ret = ds1307_read_block_data_once(client, command, length, + values); + if (ret < 0) + return ret; + } while (memcmp(oldvalues, values, length)); + return length; +} + +static s32 ds1307_write_block_data(struct i2c_client *client, u8 command, + u8 length, const u8 *values) +{ + u8 currvalues[I2C_SMBUS_BLOCK_MAX]; + int tries = 0; + + dev_dbg(&client->dev, "ds1307_write_block_data (length=%d)\n", length); + do { + s32 i, ret; + + if (++tries > BLOCK_DATA_MAX_TRIES) { + dev_err(&client->dev, + "ds1307_write_block_data failed\n"); + return -EIO; + } + for (i = 0; i < length; i++) { + ret = i2c_smbus_write_byte_data(client, command + i, + values[i]); + if (ret < 0) + return ret; + } + ret = ds1307_read_block_data_once(client, command, length, + currvalues); + if (ret < 0) + return ret; + } while (memcmp(currvalues, values, length)); + return length; +} + +/*----------------------------------------------------------------------*/ + /* * The IRQ logic includes a "real" handler running in IRQ context just * long enough to schedule this workqueue entry. We need a task context @@ -202,7 +279,7 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t) int tmp; /* read the RTC date and time registers all at once */ - tmp = i2c_smbus_read_i2c_block_data(ds1307->client, + tmp = ds1307->read_block_data(ds1307->client, DS1307_REG_SECS, 7, ds1307->regs); if (tmp != 7) { dev_err(dev, "%s error %d\n", "read", tmp); @@ -279,7 +356,7 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t) "write", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); - result = i2c_smbus_write_i2c_block_data(ds1307->client, 0, 7, buf); + result = ds1307->write_block_data(ds1307->client, 0, 7, buf); if (result < 0) { dev_err(dev, "%s error %d\n", "write", result); return result; @@ -297,7 +374,7 @@ static int ds1337_read_alarm(struct device *dev, struct rtc_wkalrm *t) return -EINVAL; /* read all ALARM1, ALARM2, and status registers at once */ - ret = i2c_smbus_read_i2c_block_data(client, + ret = ds1307->read_block_data(client, DS1339_REG_ALARM1_SECS, 9, ds1307->regs); if (ret != 9) { dev_err(dev, "%s error %d\n", "alarm read", ret); @@ -356,7 +433,7 @@ static int ds1337_set_alarm(struct device *dev, struct rtc_wkalrm *t) t->enabled, t->pending); /* read current status of both alarms and the chip */ - ret = i2c_smbus_read_i2c_block_data(client, + ret = ds1307->read_block_data(client, DS1339_REG_ALARM1_SECS, 9, buf); if (ret != 9) { dev_err(dev, "%s error %d\n", "alarm write", ret); @@ -391,7 +468,7 @@ static int ds1337_set_alarm(struct device *dev, struct rtc_wkalrm *t) } buf[8] = status & ~(DS1337_BIT_A1I | DS1337_BIT_A2I); - ret = i2c_smbus_write_i2c_block_data(client, + ret = ds1307->write_block_data(client, DS1339_REG_ALARM1_SECS, 9, buf); if (ret < 0) { dev_err(dev, "can't set alarm time\n"); @@ -479,7 +556,7 @@ ds1307_nvram_read(struct kobject *kobj, struct bin_attribute *attr, if (unlikely(!count)) return count; - result = i2c_smbus_read_i2c_block_data(client, 8 + off, count, buf); + result = ds1307->read_block_data(client, 8 + off, count, buf); if (result < 0) dev_err(&client->dev, "%s error %d\n", "nvram read", result); return result; @@ -490,9 +567,11 @@ ds1307_nvram_write(struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t off, size_t count) { struct i2c_client *client; + struct ds1307 *ds1307; int result; client = kobj_to_i2c_client(kobj); + ds1307 = i2c_get_clientdata(client); if (unlikely(off >= NVRAM_SIZE)) return -EFBIG; @@ -501,7 +580,7 @@ ds1307_nvram_write(struct kobject *kobj, struct bin_attribute *attr, if (unlikely(!count)) return count; - result = i2c_smbus_write_i2c_block_data(client, 8 + off, count, buf); + result = ds1307->write_block_data(client, 8 + off, count, buf); if (result < 0) { dev_err(&client->dev, "%s error %d\n", "nvram write", result); return result; @@ -535,9 +614,8 @@ static int __devinit ds1307_probe(struct i2c_client *client, int want_irq = false; unsigned char *buf; - if (!i2c_check_functionality(adapter, - I2C_FUNC_SMBUS_WRITE_BYTE_DATA | - I2C_FUNC_SMBUS_I2C_BLOCK)) + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA) + && !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) return -EIO; if (!(ds1307 = kzalloc(sizeof(struct ds1307), GFP_KERNEL))) @@ -547,6 +625,13 @@ static int __devinit ds1307_probe(struct i2c_client *client, i2c_set_clientdata(client, ds1307); ds1307->type = id->driver_data; buf = ds1307->regs; + if (i2c_check_functionality(adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) { + ds1307->read_block_data = i2c_smbus_read_i2c_block_data; + ds1307->write_block_data = i2c_smbus_write_i2c_block_data; + } else { + ds1307->read_block_data = ds1307_read_block_data; + ds1307->write_block_data = ds1307_write_block_data; + } switch (ds1307->type) { case ds_1337: @@ -557,7 +642,7 @@ static int __devinit ds1307_probe(struct i2c_client *client, want_irq = true; } /* get registers that the "rtc" read below won't read... */ - tmp = i2c_smbus_read_i2c_block_data(ds1307->client, + tmp = ds1307->read_block_data(ds1307->client, DS1337_REG_CONTROL, 2, buf); if (tmp != 2) { pr_debug("read error %d\n", tmp); @@ -595,7 +680,7 @@ static int __devinit ds1307_probe(struct i2c_client *client, read_rtc: /* read RTC registers */ - tmp = i2c_smbus_read_i2c_block_data(ds1307->client, 0, 8, buf); + tmp = ds1307->read_block_data(ds1307->client, 0, 8, buf); if (tmp != 8) { pr_debug("read error %d\n", tmp); err = -EIO; -- cgit v1.2.3 From a216685818a54b4f15235068b53908f954850251 Mon Sep 17 00:00:00 2001 From: Matthias Fuchs Date: Tue, 31 Mar 2009 15:24:58 -0700 Subject: rtc: add EPSON RX8025 support to DS1307 RTC driver Add support for the EPSON RX8025 RTC. The date/time registers of this chip are compatible with the DS1307. Signed-off-by: Matthias Fuchs Signed-off-by: Alessandro Zummo Cc: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/Kconfig | 7 +++-- drivers/rtc/rtc-ds1307.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index d669b9169278..09d5cd33a3f6 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -129,13 +129,14 @@ comment "I2C RTC drivers" if I2C config RTC_DRV_DS1307 - tristate "Dallas/Maxim DS1307/37/38/39/40, ST M41T00" + tristate "Dallas/Maxim DS1307/37/38/39/40, ST M41T00, EPSON RX-8025" help If you say yes here you get support for various compatible RTC chips (often with battery backup) connected with I2C. This driver should handle DS1307, DS1337, DS1338, DS1339, DS1340, ST M41T00, - and probably other chips. In some cases the RTC must already - have been initialized (by manufacturing or a bootloader). + EPSON RX-8025 and probably other chips. In some cases the RTC + must already have been initialized (by manufacturing or a + bootloader). The first seven registers on these chips hold an RTC, and other registers may add features such as NVRAM, a trickle charger for diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index 1c975e6439b3..2c4a65302a9d 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -3,6 +3,7 @@ * * Copyright (C) 2005 James Chapman (ds1337 core) * Copyright (C) 2006 David Brownell + * Copyright (C) 2009 Matthias Fuchs (rx8025 support) * * 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 @@ -31,6 +32,7 @@ enum ds_type { ds_1339, ds_1340, m41t00, + rx_8025, // rs5c372 too? different address... }; @@ -83,6 +85,12 @@ enum ds_type { #define DS1339_REG_ALARM1_SECS 0x07 #define DS1339_REG_TRICKLE 0x10 +#define RX8025_REG_CTRL1 0x0e +# define RX8025_BIT_2412 0x20 +#define RX8025_REG_CTRL2 0x0f +# define RX8025_BIT_PON 0x10 +# define RX8025_BIT_VDET 0x40 +# define RX8025_BIT_XST 0x20 struct ds1307 { @@ -121,6 +129,8 @@ static const struct chip_desc chips[] = { [ds_1340] = { }, [m41t00] = { +}, +[rx_8025] = { }, }; static const struct i2c_device_id ds1307_id[] = { @@ -130,6 +140,7 @@ static const struct i2c_device_id ds1307_id[] = { { "ds1339", ds_1339 }, { "ds1340", ds_1340 }, { "m41t00", m41t00 }, + { "rx8025", rx_8025 }, { } }; MODULE_DEVICE_TABLE(i2c, ds1307_id); @@ -674,6 +685,72 @@ static int __devinit ds1307_probe(struct i2c_client *client, dev_warn(&client->dev, "SET TIME!\n"); } break; + + case rx_8025: + tmp = i2c_smbus_read_i2c_block_data(ds1307->client, + RX8025_REG_CTRL1 << 4 | 0x08, 2, buf); + if (tmp != 2) { + pr_debug("read error %d\n", tmp); + err = -EIO; + goto exit_free; + } + + /* oscillator off? turn it on, so clock can tick. */ + if (!(ds1307->regs[1] & RX8025_BIT_XST)) { + ds1307->regs[1] |= RX8025_BIT_XST; + i2c_smbus_write_byte_data(client, + RX8025_REG_CTRL2 << 4 | 0x08, + ds1307->regs[1]); + dev_warn(&client->dev, + "oscillator stop detected - SET TIME!\n"); + } + + if (ds1307->regs[1] & RX8025_BIT_PON) { + ds1307->regs[1] &= ~RX8025_BIT_PON; + i2c_smbus_write_byte_data(client, + RX8025_REG_CTRL2 << 4 | 0x08, + ds1307->regs[1]); + dev_warn(&client->dev, "power-on detected\n"); + } + + if (ds1307->regs[1] & RX8025_BIT_VDET) { + ds1307->regs[1] &= ~RX8025_BIT_VDET; + i2c_smbus_write_byte_data(client, + RX8025_REG_CTRL2 << 4 | 0x08, + ds1307->regs[1]); + dev_warn(&client->dev, "voltage drop detected\n"); + } + + /* make sure we are running in 24hour mode */ + if (!(ds1307->regs[0] & RX8025_BIT_2412)) { + u8 hour; + + /* switch to 24 hour mode */ + i2c_smbus_write_byte_data(client, + RX8025_REG_CTRL1 << 4 | 0x08, + ds1307->regs[0] | + RX8025_BIT_2412); + + tmp = i2c_smbus_read_i2c_block_data(ds1307->client, + RX8025_REG_CTRL1 << 4 | 0x08, 2, buf); + if (tmp != 2) { + pr_debug("read error %d\n", tmp); + err = -EIO; + goto exit_free; + } + + /* correct hour */ + hour = bcd2bin(ds1307->regs[DS1307_REG_HOUR]); + if (hour == 12) + hour = 0; + if (ds1307->regs[DS1307_REG_HOUR] & DS1307_BIT_PM) + hour += 12; + + i2c_smbus_write_byte_data(client, + DS1307_REG_HOUR << 4 | 0x08, + hour); + } + break; default: break; } @@ -734,6 +811,7 @@ read_rtc: dev_warn(&client->dev, "SET TIME!\n"); } break; + case rx_8025: case ds_1337: case ds_1339: break; @@ -747,6 +825,8 @@ read_rtc: * systems that will run through year 2100. */ break; + case rx_8025: + break; default: if (!(tmp & DS1307_BIT_12HR)) break; -- cgit v1.2.3 From 62da659a7057f7227a99a42eea6aa606b09c1e8c Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Tue, 31 Mar 2009 15:24:59 -0700 Subject: rtc-wm8350: retries will reach -1 With a postfix decrement retries will reach -1 rather than 0, so the warning and error-out will not occur. Signed-off-by: Roel Kluin Acked-by: Mark Brown Cc: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-wm8350.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/rtc/rtc-wm8350.c b/drivers/rtc/rtc-wm8350.c index 616630d1e317..c91edc572eb6 100644 --- a/drivers/rtc/rtc-wm8350.c +++ b/drivers/rtc/rtc-wm8350.c @@ -122,7 +122,7 @@ static int wm8350_rtc_settime(struct device *dev, struct rtc_time *tm) 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)); + } while (--retries && !(rtc_ctrl & WM8350_RTC_STS)); if (!retries) { dev_err(dev, "timed out on set confirmation\n"); @@ -437,7 +437,7 @@ static int wm8350_rtc_probe(struct platform_device *pdev) do { timectl = wm8350_reg_read(wm8350, WM8350_RTC_TIME_CONTROL); - } while (timectl & WM8350_RTC_STS && retries--); + } while (timectl & WM8350_RTC_STS && --retries); if (retries == 0) { dev_err(&pdev->dev, "failed to start: timeout\n"); -- cgit v1.2.3 From c08cf9daf66844c60ebe9f89885d3a3e1893e61f Mon Sep 17 00:00:00 2001 From: Mike Rapoport Date: Tue, 31 Mar 2009 15:24:59 -0700 Subject: rtc-v3020: coding style cleanup Signed-off-by: Mike Rapoport Acked-by: Alessandro Zummo Cc: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-v3020.c | 40 ++++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/rtc/rtc-v3020.c b/drivers/rtc/rtc-v3020.c index 14d4f036a768..66955cc9c746 100644 --- a/drivers/rtc/rtc-v3020.c +++ b/drivers/rtc/rtc-v3020.c @@ -28,7 +28,7 @@ #include #include -#include +#include #undef DEBUG @@ -63,7 +63,7 @@ static void v3020_set_reg(struct v3020 *chip, unsigned char address, static unsigned char v3020_get_reg(struct v3020 *chip, unsigned char address) { - unsigned int data=0; + unsigned int data = 0; int i; for (i = 0; i < 4; i++) { @@ -106,16 +106,14 @@ static int v3020_read_time(struct device *dev, struct rtc_time *dt) tmp = v3020_get_reg(chip, V3020_YEAR); dt->tm_year = bcd2bin(tmp)+100; -#ifdef DEBUG - printk("\n%s : Read RTC values\n",__func__); - printk("tm_hour: %i\n",dt->tm_hour); - printk("tm_min : %i\n",dt->tm_min); - printk("tm_sec : %i\n",dt->tm_sec); - printk("tm_year: %i\n",dt->tm_year); - printk("tm_mon : %i\n",dt->tm_mon); - printk("tm_mday: %i\n",dt->tm_mday); - printk("tm_wday: %i\n",dt->tm_wday); -#endif + dev_dbg(dev, "\n%s : Read RTC values\n", __func__); + dev_dbg(dev, "tm_hour: %i\n", dt->tm_hour); + dev_dbg(dev, "tm_min : %i\n", dt->tm_min); + dev_dbg(dev, "tm_sec : %i\n", dt->tm_sec); + dev_dbg(dev, "tm_year: %i\n", dt->tm_year); + dev_dbg(dev, "tm_mon : %i\n", dt->tm_mon); + dev_dbg(dev, "tm_mday: %i\n", dt->tm_mday); + dev_dbg(dev, "tm_wday: %i\n", dt->tm_wday); return 0; } @@ -125,15 +123,13 @@ static int v3020_set_time(struct device *dev, struct rtc_time *dt) { struct v3020 *chip = dev_get_drvdata(dev); -#ifdef DEBUG - printk("\n%s : Setting RTC values\n",__func__); - printk("tm_sec : %i\n",dt->tm_sec); - printk("tm_min : %i\n",dt->tm_min); - printk("tm_hour: %i\n",dt->tm_hour); - printk("tm_mday: %i\n",dt->tm_mday); - printk("tm_wday: %i\n",dt->tm_wday); - printk("tm_year: %i\n",dt->tm_year); -#endif + dev_dbg(dev, "\n%s : Setting RTC values\n", __func__); + dev_dbg(dev, "tm_sec : %i\n", dt->tm_sec); + dev_dbg(dev, "tm_min : %i\n", dt->tm_min); + dev_dbg(dev, "tm_hour: %i\n", dt->tm_hour); + dev_dbg(dev, "tm_mday: %i\n", dt->tm_mday); + dev_dbg(dev, "tm_wday: %i\n", dt->tm_wday); + dev_dbg(dev, "tm_year: %i\n", dt->tm_year); /* Write all the values to ram... */ v3020_set_reg(chip, V3020_SECONDS, bin2bcd(dt->tm_sec)); @@ -191,7 +187,7 @@ static int rtc_probe(struct platform_device *pdev) /* Test chip by doing a write/read sequence * to the chip ram */ v3020_set_reg(chip, V3020_SECONDS, 0x33); - if(v3020_get_reg(chip, V3020_SECONDS) != 0x33) { + if (v3020_get_reg(chip, V3020_SECONDS) != 0x33) { retval = -ENODEV; goto err_io; } -- cgit v1.2.3 From fa7af8b1bb6dfca7a0c8541683a9bfffbc8dd345 Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Tue, 31 Mar 2009 15:25:00 -0700 Subject: rtc: test before subtraction on unsigned new_alarm is unsigned so test before the subtraction. [akpm@linux-foundation.org: time-wrapping fix] Signed-off-by: Roel Kluin Cc: David Brownell Cc: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-ds1374.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/rtc/rtc-ds1374.c b/drivers/rtc/rtc-ds1374.c index a5b0fc09f0c6..4d32e328f6cd 100644 --- a/drivers/rtc/rtc-ds1374.c +++ b/drivers/rtc/rtc-ds1374.c @@ -222,16 +222,16 @@ static int ds1374_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) rtc_tm_to_time(&alarm->time, &new_alarm); rtc_tm_to_time(&now, &itime); - new_alarm -= itime; - /* This can happen due to races, in addition to dates that are * truly in the past. To avoid requiring the caller to check for * races, dates in the past are assumed to be in the recent past * (i.e. not something that we'd rather the caller know about via * an error), and the alarm is set to go off as soon as possible. */ - if (new_alarm <= 0) + if (time_before_eq(new_alarm, itime)) new_alarm = 1; + else + new_alarm -= itime; mutex_lock(&ds1374->mutex); -- cgit v1.2.3 From 6e6fe42227e23a379d3c70f6ff257131399e4075 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Tue, 31 Mar 2009 15:25:01 -0700 Subject: drivers/video/uvesafb.c: don't use gfp_any() GFP_KERNEL is legal here - we don't need to use gfp_any(). Cc: Evgeniy Polyakov Cc: "David S. Miller" Cc: Andi Kleen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/uvesafb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/video/uvesafb.c b/drivers/video/uvesafb.c index 74ae75899009..398fd25e0810 100644 --- a/drivers/video/uvesafb.c +++ b/drivers/video/uvesafb.c @@ -189,7 +189,7 @@ static int uvesafb_exec(struct uvesafb_ktask *task) uvfb_tasks[seq] = task; mutex_unlock(&uvfb_lock); - err = cn_netlink_send(m, 0, gfp_any()); + err = cn_netlink_send(m, 0, GFP_KERNEL); if (err == -ESRCH) { /* * Try to start the userspace helper if sending -- cgit v1.2.3 From 75ed3a17a5bc0ecff5c256cfb81ed06f8a6fbb54 Mon Sep 17 00:00:00 2001 From: Krzysztof Helt Date: Tue, 31 Mar 2009 15:25:03 -0700 Subject: cirrusfb: convert printks to dev_foo Convert all printks to dev_dbg, dev_info or dev_err. Kill some excessive debug information and code in the process. [akpm@linux-foundation.org: printk fixes] [akpm@linux-foundation.org: cleanups] Signed-off-by: Krzysztof Helt Cc: Geert Uytterhoeven Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/cirrusfb.c | 455 ++++++++++++++++------------------------------- 1 file changed, 158 insertions(+), 297 deletions(-) (limited to 'drivers') diff --git a/drivers/video/cirrusfb.c b/drivers/video/cirrusfb.c index a2aa6ddffbe2..3b4b0f1e0615 100644 --- a/drivers/video/cirrusfb.c +++ b/drivers/video/cirrusfb.c @@ -34,8 +34,6 @@ * */ -#define CIRRUSFB_VERSION "2.0-pre2" - #include #include #include @@ -72,20 +70,9 @@ * */ -/* enable debug output? */ -/* #define CIRRUSFB_DEBUG 1 */ - /* disable runtime assertions? */ /* #define CIRRUSFB_NDEBUG */ -/* debug output */ -#ifdef CIRRUSFB_DEBUG -#define DPRINTK(fmt, args...) \ - printk(KERN_DEBUG "%s: " fmt, __func__ , ## args) -#else -#define DPRINTK(fmt, args...) -#endif - /* debugging assertions */ #ifndef CIRRUSFB_NDEBUG #define assert(expr) \ @@ -150,7 +137,7 @@ static const struct cirrusfb_board_info_rec { .maxclock = { /* guess */ /* the SD64/P4 have a higher max. videoclock */ - 140000, 140000, 140000, 140000, 140000, + 135100, 135100, 85500, 85500, 0 }, .init_sr07 = true, .init_sr1f = true, @@ -426,11 +413,10 @@ static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel, static void bestclock(long freq, int *nom, int *den, int *div); #ifdef CIRRUSFB_DEBUG -static void cirrusfb_dump(void); -static void cirrusfb_dbg_reg_dump(caddr_t regbase); -static void cirrusfb_dbg_print_regs(caddr_t regbase, +static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase); +static void cirrusfb_dbg_print_regs(struct fb_info *info, + caddr_t regbase, enum cirrusfb_dbg_reg_class reg_class, ...); -static void cirrusfb_dbg_print_byte(const char *name, unsigned char val); #endif /* CIRRUSFB_DEBUG */ /*** END PROTOTYPES ********************************************************/ @@ -460,23 +446,24 @@ static int cirrusfb_release(struct fb_info *info, int user) /**** BEGIN Hardware specific Routines **************************************/ /* Check if the MCLK is not a better clock source */ -static int cirrusfb_check_mclk(struct cirrusfb_info *cinfo, long freq) +static int cirrusfb_check_mclk(struct fb_info *info, long freq) { + struct cirrusfb_info *cinfo = info->par; long mclk = vga_rseq(cinfo->regbase, CL_SEQR1F) & 0x3f; /* Read MCLK value */ mclk = (14318 * mclk) >> 3; - DPRINTK("Read MCLK of %ld kHz\n", mclk); + dev_dbg(info->device, "Read MCLK of %ld kHz\n", mclk); /* Determine if we should use MCLK instead of VCLK, and if so, what we * should divide it by to get VCLK */ if (abs(freq - mclk) < 250) { - DPRINTK("Using VCLK = MCLK\n"); + dev_dbg(info->device, "Using VCLK = MCLK\n"); return 1; } else if (abs(freq - (mclk / 2)) < 250) { - DPRINTK("Using VCLK = MCLK/2\n"); + dev_dbg(info->device, "Using VCLK = MCLK/2\n"); return 2; } @@ -490,56 +477,6 @@ static int cirrusfb_check_var(struct fb_var_screeninfo *var, /* memory size in pixels */ unsigned pixels = info->screen_size * 8 / var->bits_per_pixel; - switch (var->bits_per_pixel) { - case 1: - pixels /= 4; - break; /* 8 pixel per byte, only 1/4th of mem usable */ - case 8: - case 16: - case 32: - break; /* 1 pixel == 1 byte */ - default: - printk(KERN_ERR "cirrusfb: mode %dx%dx%d rejected..." - "color depth not supported.\n", - var->xres, var->yres, var->bits_per_pixel); - DPRINTK("EXIT - EINVAL error\n"); - return -EINVAL; - } - - if (var->xres_virtual < var->xres) - var->xres_virtual = var->xres; - /* use highest possible virtual resolution */ - if (var->yres_virtual == -1) { - var->yres_virtual = pixels / var->xres_virtual; - - printk(KERN_INFO "cirrusfb: virtual resolution set to " - "maximum of %dx%d\n", var->xres_virtual, - var->yres_virtual); - } - if (var->yres_virtual < var->yres) - var->yres_virtual = var->yres; - - if (var->xres_virtual * var->yres_virtual > pixels) { - printk(KERN_ERR "cirrusfb: mode %dx%dx%d rejected... " - "virtual resolution too high to fit into video memory!\n", - var->xres_virtual, var->yres_virtual, - var->bits_per_pixel); - DPRINTK("EXIT - EINVAL error\n"); - return -EINVAL; - } - - - if (var->xoffset < 0) - var->xoffset = 0; - if (var->yoffset < 0) - var->yoffset = 0; - - /* truncate xoffset and yoffset to maximum if too high */ - if (var->xoffset > var->xres_virtual - var->xres) - var->xoffset = var->xres_virtual - var->xres - 1; - if (var->yoffset > var->yres_virtual - var->yres) - var->yoffset = var->yres_virtual - var->yres - 1; - switch (var->bits_per_pixel) { case 1: var->red.offset = 0; @@ -586,12 +523,46 @@ static int cirrusfb_check_var(struct fb_var_screeninfo *var, break; default: - DPRINTK("Unsupported bpp size: %d\n", var->bits_per_pixel); + dev_dbg(info->device, + "Unsupported bpp size: %d\n", var->bits_per_pixel); assert(false); /* should never occur */ break; } + if (var->xres_virtual < var->xres) + var->xres_virtual = var->xres; + /* use highest possible virtual resolution */ + if (var->yres_virtual == -1) { + var->yres_virtual = pixels / var->xres_virtual; + + dev_info(info->device, + "virtual resolution set to maximum of %dx%d\n", + var->xres_virtual, var->yres_virtual); + } + if (var->yres_virtual < var->yres) + var->yres_virtual = var->yres; + + if (var->xres_virtual * var->yres_virtual > pixels) { + dev_err(info->device, "mode %dx%dx%d rejected... " + "virtual resolution too high to fit into video memory!\n", + var->xres_virtual, var->yres_virtual, + var->bits_per_pixel); + return -EINVAL; + } + + + if (var->xoffset < 0) + var->xoffset = 0; + if (var->yoffset < 0) + var->yoffset = 0; + + /* truncate xoffset and yoffset to maximum if too high */ + if (var->xoffset > var->xres_virtual - var->xres) + var->xoffset = var->xres_virtual - var->xres - 1; + if (var->yoffset > var->yres_virtual - var->yres) + var->yoffset = var->yres_virtual - var->yres - 1; + var->red.msb_right = var->green.msb_right = var->blue.msb_right = @@ -606,9 +577,8 @@ static int cirrusfb_check_var(struct fb_var_screeninfo *var, yres = (yres + 1) / 2; if (yres >= 1280) { - printk(KERN_ERR "cirrusfb: ERROR: VerticalTotal >= 1280; " + dev_err(info->device, "ERROR: VerticalTotal >= 1280; " "special treatment required! (TODO)\n"); - DPRINTK("EXIT - EINVAL error\n"); return -EINVAL; } @@ -642,7 +612,8 @@ static int cirrusfb_decode_var(const struct fb_var_screeninfo *var, break; default: - DPRINTK("Unsupported bpp size: %d\n", var->bits_per_pixel); + dev_dbg(info->device, + "Unsupported bpp size: %d\n", var->bits_per_pixel); assert(false); /* should never occur */ break; @@ -653,7 +624,7 @@ static int cirrusfb_decode_var(const struct fb_var_screeninfo *var, /* convert from ps to kHz */ freq = PICOS2KHZ(var->pixclock); - DPRINTK("desired pixclock: %ld kHz\n", freq); + dev_dbg(info->device, "desired pixclock: %ld kHz\n", freq); maxclock = cirrusfb_board_info[cinfo->btype].maxclock[maxclockidx]; regs->multiplexing = 0; @@ -668,9 +639,9 @@ static int cirrusfb_decode_var(const struct fb_var_screeninfo *var, break; default: - printk(KERN_ERR "cirrusfb: Frequency greater " - "than maxclock (%ld kHz)\n", maxclock); - DPRINTK("EXIT - return -EINVAL\n"); + dev_err(info->device, + "Frequency greater than maxclock (%ld kHz)\n", + maxclock); return -EINVAL; } } @@ -689,16 +660,17 @@ static int cirrusfb_decode_var(const struct fb_var_screeninfo *var, return 0; } -static void cirrusfb_set_mclk_as_source(const struct cirrusfb_info *cinfo, - int div) +static void cirrusfb_set_mclk_as_source(const struct fb_info *info, int div) { + struct cirrusfb_info *cinfo = info->par; unsigned char old1f, old1e; + assert(cinfo != NULL); old1f = vga_rseq(cinfo->regbase, CL_SEQR1F) & ~0x40; if (div) { - DPRINTK("Set %s as pixclock source.\n", - (div == 2) ? "MCLK/2" : "MCLK"); + dev_dbg(info->device, "Set %s as pixclock source.\n", + (div == 2) ? "MCLK/2" : "MCLK"); old1f |= 0x40; old1e = vga_rseq(cinfo->regbase, CL_SEQR1E) & ~0x1; if (div == 2) @@ -728,17 +700,16 @@ static int cirrusfb_set_par_foo(struct fb_info *info) long freq; int nom, den, div; - DPRINTK("ENTER\n"); - DPRINTK("Requested mode: %dx%dx%d\n", + dev_dbg(info->device, "Requested mode: %dx%dx%d\n", var->xres, var->yres, var->bits_per_pixel); - DPRINTK("pixclock: %d\n", var->pixclock); + dev_dbg(info->device, "pixclock: %d\n", var->pixclock); init_vgachip(info); err = cirrusfb_decode_var(var, ®s, info); if (err) { /* should never happen */ - DPRINTK("mode change aborted. invalid var.\n"); + dev_dbg(info->device, "mode change aborted. invalid var.\n"); return -EINVAL; } @@ -789,30 +760,30 @@ static int cirrusfb_set_par_foo(struct fb_info *info) vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20); /* previously: 0x00) */ /* if debugging is enabled, all parameters get output before writing */ - DPRINTK("CRT0: %d\n", htotal); + dev_dbg(info->device, "CRT0: %d\n", htotal); vga_wcrt(regbase, VGA_CRTC_H_TOTAL, htotal); - DPRINTK("CRT1: %d\n", hdispend); + dev_dbg(info->device, "CRT1: %d\n", hdispend); vga_wcrt(regbase, VGA_CRTC_H_DISP, hdispend); - DPRINTK("CRT2: %d\n", var->xres / 8); + dev_dbg(info->device, "CRT2: %d\n", var->xres / 8); vga_wcrt(regbase, VGA_CRTC_H_BLANK_START, var->xres / 8); /* + 128: Compatible read */ - DPRINTK("CRT3: 128+%d\n", (htotal + 5) % 32); + dev_dbg(info->device, "CRT3: 128+%d\n", (htotal + 5) % 32); vga_wcrt(regbase, VGA_CRTC_H_BLANK_END, 128 + ((htotal + 5) % 32)); - DPRINTK("CRT4: %d\n", hsyncstart); + dev_dbg(info->device, "CRT4: %d\n", hsyncstart); vga_wcrt(regbase, VGA_CRTC_H_SYNC_START, hsyncstart); tmp = hsyncend % 32; if ((htotal + 5) & 32) tmp += 128; - DPRINTK("CRT5: %d\n", tmp); + dev_dbg(info->device, "CRT5: %d\n", tmp); vga_wcrt(regbase, VGA_CRTC_H_SYNC_END, tmp); - DPRINTK("CRT6: %d\n", vtotal & 0xff); + dev_dbg(info->device, "CRT6: %d\n", vtotal & 0xff); vga_wcrt(regbase, VGA_CRTC_V_TOTAL, vtotal & 0xff); tmp = 16; /* LineCompare bit #9 */ @@ -830,7 +801,7 @@ static int cirrusfb_set_par_foo(struct fb_info *info) tmp |= 64; if (vsyncstart & 512) tmp |= 128; - DPRINTK("CRT7: %d\n", tmp); + dev_dbg(info->device, "CRT7: %d\n", tmp); vga_wcrt(regbase, VGA_CRTC_OVERFLOW, tmp); tmp = 0x40; /* LineCompare bit #8 */ @@ -838,25 +809,25 @@ static int cirrusfb_set_par_foo(struct fb_info *info) tmp |= 0x20; if (var->vmode & FB_VMODE_DOUBLE) tmp |= 0x80; - DPRINTK("CRT9: %d\n", tmp); + dev_dbg(info->device, "CRT9: %d\n", tmp); vga_wcrt(regbase, VGA_CRTC_MAX_SCAN, tmp); - DPRINTK("CRT10: %d\n", vsyncstart & 0xff); + dev_dbg(info->device, "CRT10: %d\n", vsyncstart & 0xff); vga_wcrt(regbase, VGA_CRTC_V_SYNC_START, vsyncstart & 0xff); - DPRINTK("CRT11: 64+32+%d\n", vsyncend % 16); + dev_dbg(info->device, "CRT11: 64+32+%d\n", vsyncend % 16); vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, vsyncend % 16 + 64 + 32); - DPRINTK("CRT12: %d\n", vdispend & 0xff); + dev_dbg(info->device, "CRT12: %d\n", vdispend & 0xff); vga_wcrt(regbase, VGA_CRTC_V_DISP_END, vdispend & 0xff); - DPRINTK("CRT15: %d\n", (vdispend + 1) & 0xff); + dev_dbg(info->device, "CRT15: %d\n", (vdispend + 1) & 0xff); vga_wcrt(regbase, VGA_CRTC_V_BLANK_START, (vdispend + 1) & 0xff); - DPRINTK("CRT16: %d\n", vtotal & 0xff); + dev_dbg(info->device, "CRT16: %d\n", vtotal & 0xff); vga_wcrt(regbase, VGA_CRTC_V_BLANK_END, vtotal & 0xff); - DPRINTK("CRT18: 0xff\n"); + dev_dbg(info->device, "CRT18: 0xff\n"); vga_wcrt(regbase, VGA_CRTC_LINE_COMPARE, 0xff); tmp = 0; @@ -871,12 +842,15 @@ static int cirrusfb_set_par_foo(struct fb_info *info) if (vtotal & 512) tmp |= 128; - DPRINTK("CRT1a: %d\n", tmp); + dev_dbg(info->device, "CRT1a: %d\n", tmp); vga_wcrt(regbase, CL_CRT1A, tmp); freq = PICOS2KHZ(var->pixclock); bestclock(freq, &nom, &den, &div); + dev_dbg(info->device, "VCLK freq: %ld kHz nom: %d den: %d div: %d\n", + freq, nom, den, div); + /* set VCLK0 */ /* hardware RefClock: 14.31818 MHz */ /* formula: VClk = (OSC * N) / (D * (1+P)) */ @@ -886,10 +860,10 @@ static int cirrusfb_set_par_foo(struct fb_info *info) /* if freq is close to mclk or mclk/2 select mclk * as clock source */ - int divMCLK = cirrusfb_check_mclk(cinfo, freq); + int divMCLK = cirrusfb_check_mclk(info, freq); if (divMCLK) { nom = 0; - cirrusfb_set_mclk_as_source(cinfo, divMCLK); + cirrusfb_set_mclk_as_source(info, divMCLK); } } if (nom) { @@ -904,7 +878,7 @@ static int cirrusfb_set_par_foo(struct fb_info *info) (cinfo->btype == BT_GD5480)) tmp |= 0x80; - DPRINTK("CL_SEQR1B: %ld\n", (long) tmp); + dev_dbg(info->device, "CL_SEQR1B: %ld\n", (long) tmp); vga_wseq(regbase, CL_SEQR1B, tmp); } @@ -952,7 +926,7 @@ static int cirrusfb_set_par_foo(struct fb_info *info) /* programming for different color depths */ if (var->bits_per_pixel == 1) { - DPRINTK("cirrusfb: preparing for 1 bit deep display\n"); + dev_dbg(info->device, "preparing for 1 bit deep display\n"); vga_wgfx(regbase, VGA_GFX_MODE, 0); /* mode register */ /* SR07 */ @@ -964,20 +938,18 @@ static int cirrusfb_set_par_foo(struct fb_info *info) case BT_PICASSO4: case BT_ALPINE: case BT_GD5480: - DPRINTK(" (for GD54xx)\n"); vga_wseq(regbase, CL_SEQR7, - regs.multiplexing ? + regs.multiplexing ? bi->sr07_1bpp_mux : bi->sr07_1bpp); break; case BT_LAGUNA: - DPRINTK(" (for GD546x)\n"); vga_wseq(regbase, CL_SEQR7, vga_rseq(regbase, CL_SEQR7) & ~0x01); break; default: - printk(KERN_WARNING "cirrusfb: unknown Board\n"); + dev_warn(info->device, "unknown Board\n"); break; } @@ -987,14 +959,12 @@ static int cirrusfb_set_par_foo(struct fb_info *info) /* setting the SEQRF on SD64 is not necessary * (only during init) */ - DPRINTK("(for SD64)\n"); /* MCLK select */ vga_wseq(regbase, CL_SEQR1F, 0x1a); break; case BT_PICCOLO: case BT_SPECTRUM: - DPRINTK("(for Piccolo/Spectrum)\n"); /* ### ueberall 0x22? */ /* ##vorher 1c MCLK select */ vga_wseq(regbase, CL_SEQR1F, 0x22); @@ -1003,7 +973,6 @@ static int cirrusfb_set_par_foo(struct fb_info *info) break; case BT_PICASSO: - DPRINTK("(for Picasso)\n"); /* ##vorher 22 MCLK select */ vga_wseq(regbase, CL_SEQR1F, 0x22); /* ## vorher d0 avoid FIFO underruns..? */ @@ -1014,12 +983,11 @@ static int cirrusfb_set_par_foo(struct fb_info *info) case BT_ALPINE: case BT_GD5480: case BT_LAGUNA: - DPRINTK(" (for GD54xx)\n"); /* do nothing */ break; default: - printk(KERN_WARNING "cirrusfb: unknown Board\n"); + dev_warn(info->device, "unknown Board\n"); break; } @@ -1045,7 +1013,7 @@ static int cirrusfb_set_par_foo(struct fb_info *info) */ else if (var->bits_per_pixel == 8) { - DPRINTK("cirrusfb: preparing for 8 bit deep display\n"); + dev_dbg(info->device, "preparing for 8 bit deep display\n"); switch (cinfo->btype) { case BT_SD64: case BT_PICCOLO: @@ -1054,20 +1022,18 @@ static int cirrusfb_set_par_foo(struct fb_info *info) case BT_PICASSO4: case BT_ALPINE: case BT_GD5480: - DPRINTK(" (for GD54xx)\n"); vga_wseq(regbase, CL_SEQR7, regs.multiplexing ? bi->sr07_8bpp_mux : bi->sr07_8bpp); break; case BT_LAGUNA: - DPRINTK(" (for GD546x)\n"); vga_wseq(regbase, CL_SEQR7, vga_rseq(regbase, CL_SEQR7) | 0x01); break; default: - printk(KERN_WARNING "cirrusfb: unknown Board\n"); + dev_warn(info->device, "unknown Board\n"); break; } @@ -1095,18 +1061,16 @@ static int cirrusfb_set_par_foo(struct fb_info *info) break; case BT_ALPINE: - DPRINTK(" (for GD543x)\n"); /* We already set SRF and SR1F */ break; case BT_GD5480: case BT_LAGUNA: - DPRINTK(" (for GD54xx)\n"); /* do nothing */ break; default: - printk(KERN_WARNING "cirrusfb: unknown Board\n"); + dev_warn(info->device, "unknown board\n"); break; } @@ -1134,7 +1098,7 @@ static int cirrusfb_set_par_foo(struct fb_info *info) */ else if (var->bits_per_pixel == 16) { - DPRINTK("cirrusfb: preparing for 16 bit deep display\n"); + dev_dbg(info->device, "preparing for 16 bit deep display\n"); switch (cinfo->btype) { case BT_SD64: /* Extended Sequencer Mode: 256c col. mode */ @@ -1166,24 +1130,21 @@ static int cirrusfb_set_par_foo(struct fb_info *info) break; case BT_ALPINE: - DPRINTK(" (for GD543x)\n"); vga_wseq(regbase, CL_SEQR7, 0xa7); break; case BT_GD5480: - DPRINTK(" (for GD5480)\n"); vga_wseq(regbase, CL_SEQR7, 0x17); /* We already set SRF and SR1F */ break; case BT_LAGUNA: - DPRINTK(" (for GD546x)\n"); vga_wseq(regbase, CL_SEQR7, vga_rseq(regbase, CL_SEQR7) & ~0x01); break; default: - printk(KERN_WARNING "CIRRUSFB: unknown Board\n"); + dev_warn(info->device, "unknown Board\n"); break; } @@ -1211,7 +1172,7 @@ static int cirrusfb_set_par_foo(struct fb_info *info) */ else if (var->bits_per_pixel == 32) { - DPRINTK("cirrusfb: preparing for 32 bit deep display\n"); + dev_dbg(info->device, "preparing for 32 bit deep display\n"); switch (cinfo->btype) { case BT_SD64: /* Extended Sequencer Mode: 256c col. mode */ @@ -1243,24 +1204,21 @@ static int cirrusfb_set_par_foo(struct fb_info *info) break; case BT_ALPINE: - DPRINTK(" (for GD543x)\n"); vga_wseq(regbase, CL_SEQR7, 0xa9); break; case BT_GD5480: - DPRINTK(" (for GD5480)\n"); vga_wseq(regbase, CL_SEQR7, 0x19); /* We already set SRF and SR1F */ break; case BT_LAGUNA: - DPRINTK(" (for GD546x)\n"); vga_wseq(regbase, CL_SEQR7, vga_rseq(regbase, CL_SEQR7) & ~0x01); break; default: - printk(KERN_WARNING "cirrusfb: unknown Board\n"); + dev_warn(info->device, "unknown Board\n"); break; } @@ -1284,8 +1242,8 @@ static int cirrusfb_set_par_foo(struct fb_info *info) */ else - printk(KERN_ERR "cirrusfb: What's this?? " - " requested color depth == %d.\n", + dev_err(info->device, + "What's this? requested color depth == %d.\n", var->bits_per_pixel); vga_wcrt(regbase, VGA_CRTC_OFFSET, offset & 0xff); @@ -1355,7 +1313,7 @@ static int cirrusfb_set_par_foo(struct fb_info *info) */ vga_wseq(regbase, VGA_SEQ_CLOCK_MODE, tmp); - DPRINTK("CL_SEQR1: %d\n", tmp); + dev_dbg(info->device, "CL_SEQR1: %d\n", tmp); cinfo->currentmode = regs; @@ -1363,10 +1321,9 @@ static int cirrusfb_set_par_foo(struct fb_info *info) cirrusfb_pan_display(var, info); #ifdef CIRRUSFB_DEBUG - cirrusfb_dump(); + cirrusfb_dbg_reg_dump(info, NULL); #endif - DPRINTK("EXIT\n"); return 0; } @@ -1424,8 +1381,8 @@ static int cirrusfb_pan_display(struct fb_var_screeninfo *var, unsigned char tmp = 0, tmp2 = 0, xpix; struct cirrusfb_info *cinfo = info->par; - DPRINTK("ENTER\n"); - DPRINTK("virtual offset: (%d,%d)\n", var->xoffset, var->yoffset); + dev_dbg(info->device, + "virtual offset: (%d,%d)\n", var->xoffset, var->yoffset); /* no range checks for xoffset and yoffset, */ /* as fb_pan_display has already done this */ @@ -1481,7 +1438,6 @@ static int cirrusfb_pan_display(struct fb_var_screeninfo *var, cirrusfb_WaitBLT(cinfo->regbase); - DPRINTK("EXIT\n"); return 0; } @@ -1502,11 +1458,11 @@ static int cirrusfb_blank(int blank_mode, struct fb_info *info) struct cirrusfb_info *cinfo = info->par; int current_mode = cinfo->blank_mode; - DPRINTK("ENTER, blank mode = %d\n", blank_mode); + dev_dbg(info->device, "ENTER, blank mode = %d\n", blank_mode); if (info->state != FBINFO_STATE_RUNNING || current_mode == blank_mode) { - DPRINTK("EXIT, returning 0\n"); + dev_dbg(info->device, "EXIT, returning 0\n"); return 0; } @@ -1543,12 +1499,12 @@ static int cirrusfb_blank(int blank_mode, struct fb_info *info) vga_wgfx(cinfo->regbase, CL_GRE, 0x06); break; default: - DPRINTK("EXIT, returning 1\n"); + dev_dbg(info->device, "EXIT, returning 1\n"); return 1; } cinfo->blank_mode = blank_mode; - DPRINTK("EXIT, returning 0\n"); + dev_dbg(info->device, "EXIT, returning 0\n"); /* Let fbcon do a soft blank for us */ return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0; @@ -1562,8 +1518,6 @@ static void init_vgachip(struct fb_info *info) struct cirrusfb_info *cinfo = info->par; const struct cirrusfb_board_info_rec *bi; - DPRINTK("ENTER\n"); - assert(cinfo != NULL); bi = &cirrusfb_board_info[cinfo->btype]; @@ -1609,7 +1563,7 @@ static void init_vgachip(struct fb_info *info) break; default: - printk(KERN_ERR "cirrusfb: Warning: Unknown board type\n"); + dev_err(info->device, "Warning: Unknown board type\n"); break; } @@ -1798,8 +1752,6 @@ static void init_vgachip(struct fb_info *info) /* misc... */ WHDR(cinfo, 0); /* Hidden DAC register: - */ - - DPRINTK("EXIT\n"); return; } @@ -1808,8 +1760,6 @@ static void switch_monitor(struct cirrusfb_info *cinfo, int on) #ifdef CONFIG_ZORRO /* only works on Zorro boards */ static int IsOn = 0; /* XXX not ok for multiple boards */ - DPRINTK("ENTER\n"); - if (cinfo->btype == BT_PICASSO4) return; /* nothing to switch */ if (cinfo->btype == BT_ALPINE) @@ -1819,8 +1769,6 @@ static void switch_monitor(struct cirrusfb_info *cinfo, int on) if (cinfo->btype == BT_PICASSO) { if ((on && !IsOn) || (!on && IsOn)) WSFR(cinfo, 0xff); - - DPRINTK("EXIT\n"); return; } if (on) { @@ -1847,11 +1795,10 @@ static void switch_monitor(struct cirrusfb_info *cinfo, int on) case BT_SPECTRUM: WSFR(cinfo, 0x4f); break; - default: /* do nothing */ break; + default: /* do nothing */ + break; } } - - DPRINTK("EXIT\n"); #endif /* CONFIG_ZORRO */ } @@ -1953,12 +1900,8 @@ static void cirrusfb_imageblit(struct fb_info *info, #define PREP_IO_BASE ((volatile unsigned char *) 0x80000000) static void get_prep_addrs(unsigned long *display, unsigned long *registers) { - DPRINTK("ENTER\n"); - *display = PREP_VIDEO_BASE; *registers = (unsigned long) PREP_IO_BASE; - - DPRINTK("EXIT\n"); } #endif /* CONFIG_PPC_PREP */ @@ -1970,13 +1913,12 @@ static int release_io_ports; * based on the DRAM bandwidth bit and DRAM bank switching bit. This * works with 1MB, 2MB and 4MB configurations (which the Motorola boards * seem to have. */ -static unsigned int __devinit cirrusfb_get_memsize(u8 __iomem *regbase) +static unsigned int __devinit cirrusfb_get_memsize(struct fb_info *info, + u8 __iomem *regbase) { unsigned long mem; unsigned char SRF; - DPRINTK("ENTER\n"); - SRF = vga_rseq(regbase, CL_SEQRF); switch ((SRF & 0x18)) { case 0x08: @@ -1992,7 +1934,7 @@ static unsigned int __devinit cirrusfb_get_memsize(u8 __iomem *regbase) mem = 2048 * 1024; break; default: - printk(KERN_WARNING "CLgenfb: Unknown memory size!\n"); + dev_warn(info->device, "CLgenfb: Unknown memory size!\n"); mem = 1024 * 1024; } if (SRF & 0x80) @@ -2002,8 +1944,6 @@ static unsigned int __devinit cirrusfb_get_memsize(u8 __iomem *regbase) mem *= 2; /* TODO: Handling of GD5446/5480 (see XF86 sources ...) */ - - DPRINTK("EXIT\n"); return mem; } @@ -2014,8 +1954,6 @@ static void get_pci_addrs(const struct pci_dev *pdev, assert(display != NULL); assert(registers != NULL); - DPRINTK("ENTER\n"); - *display = 0; *registers = 0; @@ -2030,8 +1968,6 @@ static void get_pci_addrs(const struct pci_dev *pdev, } assert(*display != 0); - - DPRINTK("EXIT\n"); } static void cirrusfb_pci_unmap(struct fb_info *info) @@ -2117,11 +2053,6 @@ static int __devinit cirrusfb_register(struct fb_info *info) int err; enum cirrus_board btype; - DPRINTK("ENTER\n"); - - printk(KERN_INFO "cirrusfb: Driver for Cirrus Logic based " - "graphic boards, v" CIRRUSFB_VERSION "\n"); - btype = cinfo->btype; /* sanity checks */ @@ -2130,11 +2061,11 @@ static int __devinit cirrusfb_register(struct fb_info *info) /* set all the vital stuff */ cirrusfb_set_fbinfo(info); - DPRINTK("cirrusfb: (RAM start set to: 0x%p)\n", info->screen_base); + dev_dbg(info->device, "(RAM start set to: 0x%p)\n", info->screen_base); err = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8); if (!err) { - DPRINTK("wrong initial video mode\n"); + dev_dbg(info->device, "wrong initial video mode\n"); err = -EINVAL; goto err_dealloc_cmap; } @@ -2144,18 +2075,18 @@ static int __devinit cirrusfb_register(struct fb_info *info) err = cirrusfb_decode_var(&info->var, &cinfo->currentmode, info); if (err < 0) { /* should never happen */ - DPRINTK("choking on default var... umm, no good.\n"); + dev_dbg(info->device, + "choking on default var... umm, no good.\n"); goto err_dealloc_cmap; } err = register_framebuffer(info); if (err < 0) { - printk(KERN_ERR "cirrusfb: could not register " - "fb device; err = %d!\n", err); + dev_err(info->device, + "could not register fb device; err = %d!\n", err); goto err_dealloc_cmap; } - DPRINTK("EXIT, returning 0\n"); return 0; err_dealloc_cmap: @@ -2168,17 +2099,13 @@ err_dealloc_cmap: static void __devexit cirrusfb_cleanup(struct fb_info *info) { struct cirrusfb_info *cinfo = info->par; - DPRINTK("ENTER\n"); switch_monitor(cinfo, 0); - unregister_framebuffer(info); fb_dealloc_cmap(&info->cmap); - printk("Framebuffer unregistered\n"); + dev_dbg(info->device, "Framebuffer unregistered\n"); cinfo->unmap(info); framebuffer_release(info); - - DPRINTK("EXIT\n"); } #ifdef CONFIG_PCI @@ -2207,9 +2134,11 @@ static int __devinit cirrusfb_pci_register(struct pci_dev *pdev, cinfo = info->par; cinfo->btype = btype = (enum cirrus_board) ent->driver_data; - DPRINTK(" Found PCI device, base address 0 is 0x%x, btype set to %d\n", - pdev->resource[0].start, btype); - DPRINTK(" base address 1 is 0x%x\n", pdev->resource[1].start); + dev_dbg(info->device, + " Found PCI device, base address 0 is 0x%Lx, btype set to %d\n", + (unsigned long long)pdev->resource[0].start, btype); + dev_dbg(info->device, " base address 1 is 0x%Lx\n", + (unsigned long long)pdev->resource[1].start); if (isPReP) { pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, 0x00000000); @@ -2219,30 +2148,29 @@ static int __devinit cirrusfb_pci_register(struct pci_dev *pdev, /* PReP dies if we ioremap the IO registers, but it works w/out... */ cinfo->regbase = (char __iomem *) info->fix.mmio_start; } else { - DPRINTK("Attempt to get PCI info for Cirrus Graphics Card\n"); + dev_dbg(info->device, + "Attempt to get PCI info for Cirrus Graphics Card\n"); get_pci_addrs(pdev, &board_addr, &info->fix.mmio_start); /* FIXME: this forces VGA. alternatives? */ cinfo->regbase = NULL; } - DPRINTK("Board address: 0x%lx, register address: 0x%lx\n", + dev_dbg(info->device, "Board address: 0x%lx, register address: 0x%lx\n", board_addr, info->fix.mmio_start); board_size = (btype == BT_GD5480) ? - 32 * MB_ : cirrusfb_get_memsize(cinfo->regbase); + 32 * MB_ : cirrusfb_get_memsize(info, cinfo->regbase); ret = pci_request_regions(pdev, "cirrusfb"); if (ret < 0) { - printk(KERN_ERR "cirrusfb: cannot reserve region 0x%lx, " - "abort\n", - board_addr); + dev_err(info->device, "cannot reserve region 0x%lx, abort\n", + board_addr); goto err_release_fb; } #if 0 /* if the system didn't claim this region, we would... */ if (!request_mem_region(0xA0000, 65535, "cirrusfb")) { - printk(KERN_ERR "cirrusfb: cannot reserve region 0x%lx, abort\n" -, - 0xA0000L); + dev_err(info->device, "cannot reserve region 0x%lx, abort\n", + 0xA0000L); ret = -EBUSY; goto err_release_regions; } @@ -2260,9 +2188,9 @@ static int __devinit cirrusfb_pci_register(struct pci_dev *pdev, info->screen_size = board_size; cinfo->unmap = cirrusfb_pci_unmap; - printk(KERN_INFO "RAM (%lu kB) at 0x%lx, Cirrus " - "Logic chipset on PCI bus\n", - info->screen_size >> 10, board_addr); + dev_info(info->device, + "Cirrus Logic chipset on PCI bus, RAM (%lu kB) at 0x%lx\n", + info->screen_size >> 10, board_addr); pci_set_drvdata(pdev, info); ret = cirrusfb_register(info); @@ -2288,11 +2216,8 @@ err_out: static void __devexit cirrusfb_pci_unregister(struct pci_dev *pdev) { struct fb_info *info = pci_get_drvdata(pdev); - DPRINTK("ENTER\n"); cirrusfb_cleanup(info); - - DPRINTK("EXIT\n"); } static struct pci_driver cirrusfb_pci_driver = { @@ -2324,8 +2249,6 @@ static int __devinit cirrusfb_zorro_register(struct zorro_dev *z, if (cirrusfb_zorro_table2[btype].id2) z2 = zorro_find_device(cirrusfb_zorro_table2[btype].id2, NULL); size = cirrusfb_zorro_table2[btype].size; - printk(KERN_INFO "cirrusfb: %s board detected; ", - cirrusfb_board_info[btype].name); info = framebuffer_alloc(sizeof(struct cirrusfb_info), &z->dev); if (!info) { @@ -2334,6 +2257,9 @@ static int __devinit cirrusfb_zorro_register(struct zorro_dev *z, goto err_out; } + dev_info(info->device, "%s board detected\n", + cirrusfb_board_info[btype].name); + cinfo = info->par; cinfo->btype = btype; @@ -2345,19 +2271,16 @@ static int __devinit cirrusfb_zorro_register(struct zorro_dev *z, info->screen_size = size; if (!zorro_request_device(z, "cirrusfb")) { - printk(KERN_ERR "cirrusfb: cannot reserve region 0x%lx, " - "abort\n", - board_addr); + dev_err(info->device, "cannot reserve region 0x%lx, abort\n", + board_addr); ret = -EBUSY; goto err_release_fb; } - printk(" RAM (%lu MB) at $%lx, ", board_size / MB_, board_addr); - ret = -EIO; if (btype == BT_PICASSO4) { - printk(KERN_INFO " REG at $%lx\n", board_addr + 0x600000); + dev_info(info->device, " REG at $%lx\n", board_addr + 0x600000); /* To be precise, for the P4 this is not the */ /* begin of the board, but the begin of RAM. */ @@ -2367,7 +2290,7 @@ static int __devinit cirrusfb_zorro_register(struct zorro_dev *z, if (!cinfo->regbase) goto err_release_region; - DPRINTK("cirrusfb: Virtual address for board set to: $%p\n", + dev_dbg(info->device, "Virtual address for board set to: $%p\n", cinfo->regbase); cinfo->regbase += 0x600000; info->fix.mmio_start = board_addr + 0x600000; @@ -2377,8 +2300,8 @@ static int __devinit cirrusfb_zorro_register(struct zorro_dev *z, if (!info->screen_base) goto err_unmap_regbase; } else { - printk(KERN_INFO " REG at $%lx\n", - (unsigned long) z2->resource.start); + dev_info(info->device, " REG at $%lx\n", + (unsigned long) z2->resource.start); info->fix.smem_start = board_addr; if (board_addr > 0x01000000) @@ -2392,12 +2315,15 @@ static int __devinit cirrusfb_zorro_register(struct zorro_dev *z, cinfo->regbase = (caddr_t) ZTWO_VADDR(z2->resource.start); info->fix.mmio_start = z2->resource.start; - DPRINTK("cirrusfb: Virtual address for board set to: $%p\n", + dev_dbg(info->device, "Virtual address for board set to: $%p\n", cinfo->regbase); } cinfo->unmap = cirrusfb_zorro_unmap; - printk(KERN_INFO "Cirrus Logic chipset on Zorro bus\n"); + dev_info(info->device, + "Cirrus Logic chipset on Zorro bus, RAM (%lu MB) at $%lx\n", + board_size / MB_, board_addr); + zorro_set_drvdata(z, info); ret = cirrusfb_register(info); @@ -2424,11 +2350,8 @@ err_out: void __devexit cirrusfb_zorro_unregister(struct zorro_dev *z) { struct fb_info *info = zorro_get_drvdata(z); - DPRINTK("ENTER\n"); cirrusfb_cleanup(info); - - DPRINTK("EXIT\n"); } static struct zorro_driver cirrusfb_zorro_driver = { @@ -2461,11 +2384,10 @@ static int __init cirrusfb_init(void) } #ifndef MODULE -static int __init cirrusfb_setup(char *options) { +static int __init cirrusfb_setup(char *options) +{ char *this_opt; - DPRINTK("ENTER\n"); - if (!options || !*options) return 0; @@ -2473,8 +2395,6 @@ static int __init cirrusfb_setup(char *options) { if (!*this_opt) continue; - DPRINTK("cirrusfb_setup: option '%s'\n", this_opt); - if (!strcmp(this_opt, "noaccel")) noaccel = 1; else if (!strncmp(this_opt, "mode:", 5)) @@ -2560,8 +2480,6 @@ static void AttrOn(const struct cirrusfb_info *cinfo) { assert(cinfo != NULL); - DPRINTK("ENTER\n"); - if (vga_rcrt(cinfo->regbase, CL_CRT24) & 0x80) { /* if we're just in "write value" mode, write back the */ /* same value as before to not modify anything */ @@ -2574,8 +2492,6 @@ static void AttrOn(const struct cirrusfb_info *cinfo) /* dummy write on Reg0 to be on "write index" mode next time */ vga_w(cinfo->regbase, VGA_ATT_IW, 0x00); - - DPRINTK("EXIT\n"); } /*** WHDR() - write into the Hidden DAC register ***/ @@ -2723,8 +2639,6 @@ static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel, u_long nsrc, ndest; u_char bltmode; - DPRINTK("ENTER\n"); - nwidth = width - 1; nheight = height - 1; @@ -2813,8 +2727,6 @@ static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel, /* and finally: GO! */ vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */ - - DPRINTK("EXIT\n"); } /******************************************************************* @@ -2831,8 +2743,6 @@ static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel, u_long ndest; u_char op; - DPRINTK("ENTER\n"); - nwidth = width - 1; nheight = height - 1; @@ -2896,8 +2806,6 @@ static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel, /* and finally: GO! */ vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */ - - DPRINTK("EXIT\n"); } /************************************************************************** @@ -2917,8 +2825,6 @@ static void bestclock(long freq, int *nom, int *den, int *div) *den = 0; *div = 0; - DPRINTK("ENTER\n"); - if (freq < 8000) freq = 8000; @@ -2960,12 +2866,6 @@ static void bestclock(long freq, int *nom, int *den, int *div) } } } - - DPRINTK("Best possible values for given frequency:\n"); - DPRINTK(" freq: %ld kHz nom: %d den: %d div: %d\n", - freq, *nom, *den, *div); - - DPRINTK("EXIT\n"); } /* ------------------------------------------------------------------------- @@ -2977,32 +2877,6 @@ static void bestclock(long freq, int *nom, int *den, int *div) #ifdef CIRRUSFB_DEBUG -/** - * cirrusfb_dbg_print_byte - * @name: name associated with byte value to be displayed - * @val: byte value to be displayed - * - * DESCRIPTION: - * Display an indented string, along with a hexidecimal byte value, and - * its decoded bits. Bits 7 through 0 are listed in left-to-right - * order. - */ - -static -void cirrusfb_dbg_print_byte(const char *name, unsigned char val) -{ - DPRINTK("%8s = 0x%02X (bits 7-0: %c%c%c%c%c%c%c%c)\n", - name, val, - val & 0x80 ? '1' : '0', - val & 0x40 ? '1' : '0', - val & 0x20 ? '1' : '0', - val & 0x10 ? '1' : '0', - val & 0x08 ? '1' : '0', - val & 0x04 ? '1' : '0', - val & 0x02 ? '1' : '0', - val & 0x01 ? '1' : '0'); -} - /** * cirrusfb_dbg_print_regs * @base: If using newmmio, the newmmio base address, otherwise %NULL @@ -3014,9 +2888,9 @@ void cirrusfb_dbg_print_byte(const char *name, unsigned char val) * used at the given @base address to query the information. */ -static -void cirrusfb_dbg_print_regs(caddr_t regbase, - enum cirrusfb_dbg_reg_class reg_class, ...) +static void cirrusfb_dbg_print_regs(struct fb_info *info, + caddr_t regbase, + enum cirrusfb_dbg_reg_class reg_class, ...) { va_list list; unsigned char val = 0; @@ -3042,7 +2916,7 @@ void cirrusfb_dbg_print_regs(caddr_t regbase, break; } - cirrusfb_dbg_print_byte(name, val); + dev_dbg(info->device, "%8s = 0x%02X\n", name, val); name = va_arg(list, char *); } @@ -3050,18 +2924,6 @@ void cirrusfb_dbg_print_regs(caddr_t regbase, va_end(list); } -/** - * cirrusfb_dump - * @cirrusfbinfo: - * - * DESCRIPTION: - */ - -static void cirrusfb_dump(void) -{ - cirrusfb_dbg_reg_dump(NULL); -} - /** * cirrusfb_dbg_reg_dump * @base: If using newmmio, the newmmio base address, otherwise %NULL @@ -3072,12 +2934,11 @@ static void cirrusfb_dump(void) * used at the given @base address to query the information. */ -static -void cirrusfb_dbg_reg_dump(caddr_t regbase) +static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase) { - DPRINTK("CIRRUSFB VGA CRTC register dump:\n"); + dev_dbg(info->device, "VGA CRTC register dump:\n"); - cirrusfb_dbg_print_regs(regbase, CRT, + cirrusfb_dbg_print_regs(info, regbase, CRT, "CR00", 0x00, "CR01", 0x01, "CR02", 0x02, @@ -3127,11 +2988,11 @@ void cirrusfb_dbg_reg_dump(caddr_t regbase) "CR3F", 0x3F, NULL); - DPRINTK("\n"); + dev_dbg(info->device, "\n"); - DPRINTK("CIRRUSFB VGA SEQ register dump:\n"); + dev_dbg(info->device, "VGA SEQ register dump:\n"); - cirrusfb_dbg_print_regs(regbase, SEQ, + cirrusfb_dbg_print_regs(info, regbase, SEQ, "SR00", 0x00, "SR01", 0x01, "SR02", 0x02, @@ -3160,7 +3021,7 @@ void cirrusfb_dbg_reg_dump(caddr_t regbase) "SR1F", 0x1F, NULL); - DPRINTK("\n"); + dev_dbg(info->device, "\n"); } #endif /* CIRRUSFB_DEBUG */ -- cgit v1.2.3 From 55a4ea6ab0fff0c02f101a60d2ba4f1794990499 Mon Sep 17 00:00:00 2001 From: Krzysztof Helt Date: Tue, 31 Mar 2009 15:25:04 -0700 Subject: cirrusfb: fix Laguna chipset memory detection and clock setting Fix memory detection and clock setting for Cirrus Laguna chipsets (GD5464/GD5465). The changes are done after the Xorg code. The driver still does not display anything on the GD5465 but it switches resolutions correctly at least. Signed-off-by: Krzysztof Helt Cc: Geert Uytterhoeven Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/cirrusfb.c | 65 ++++++++++++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 27 deletions(-) (limited to 'drivers') diff --git a/drivers/video/cirrusfb.c b/drivers/video/cirrusfb.c index 3b4b0f1e0615..dd09bae910f0 100644 --- a/drivers/video/cirrusfb.c +++ b/drivers/video/cirrusfb.c @@ -867,19 +867,24 @@ static int cirrusfb_set_par_foo(struct fb_info *info) } } if (nom) { - vga_wseq(regbase, CL_SEQRB, nom); tmp = den << 1; if (div != 0) tmp |= 1; - /* 6 bit denom; ONLY 5434!!! (bugged me 10 days) */ if ((cinfo->btype == BT_SD64) || (cinfo->btype == BT_ALPINE) || (cinfo->btype == BT_GD5480)) tmp |= 0x80; - dev_dbg(info->device, "CL_SEQR1B: %ld\n", (long) tmp); - vga_wseq(regbase, CL_SEQR1B, tmp); + dev_dbg(info->device, "CL_SEQR1B: %d\n", (int) tmp); + /* Laguna chipset has reversed clock registers */ + if (cinfo->btype == BT_LAGUNA) { + vga_wseq(regbase, CL_SEQRE, tmp); + vga_wseq(regbase, CL_SEQR1E, nom); + } else { + vga_wseq(regbase, CL_SEQRB, nom); + vga_wseq(regbase, CL_SEQR1B, tmp); + } } if (yres >= 1024) @@ -1917,31 +1922,37 @@ static unsigned int __devinit cirrusfb_get_memsize(struct fb_info *info, u8 __iomem *regbase) { unsigned long mem; - unsigned char SRF; + struct cirrusfb_info *cinfo = info->par; - SRF = vga_rseq(regbase, CL_SEQRF); - switch ((SRF & 0x18)) { - case 0x08: - mem = 512 * 1024; - break; - case 0x10: - mem = 1024 * 1024; - break; - /* 64-bit DRAM data bus width; assume 2MB. Also indicates 2MB memory - * on the 5430. - */ - case 0x18: - mem = 2048 * 1024; - break; - default: - dev_warn(info->device, "CLgenfb: Unknown memory size!\n"); - mem = 1024 * 1024; + if (cinfo->btype == BT_LAGUNA) { + unsigned char SR14 = vga_rseq(regbase, CL_SEQR14); + + mem = ((SR14 & 7) + 1) << 20; + } else { + unsigned char SRF = vga_rseq(regbase, CL_SEQRF); + switch ((SRF & 0x18)) { + case 0x08: + mem = 512 * 1024; + break; + case 0x10: + mem = 1024 * 1024; + break; + /* 64-bit DRAM data bus width; assume 2MB. + * Also indicates 2MB memory on the 5430. + */ + case 0x18: + mem = 2048 * 1024; + break; + default: + dev_warn(info->device, "Unknown memory size!\n"); + mem = 1024 * 1024; + } + /* If DRAM bank switching is enabled, there must be + * twice as much memory installed. (4MB on the 5434) + */ + if (SRF & 0x80) + mem *= 2; } - if (SRF & 0x80) - /* If DRAM bank switching is enabled, there must be twice as much - * memory installed. (4MB on the 5434) - */ - mem *= 2; /* TODO: Handling of GD5446/5480 (see XF86 sources ...) */ return mem; -- cgit v1.2.3 From 213d4bdd8cd405d9ba59ee78165b8c870f83a018 Mon Sep 17 00:00:00 2001 From: Krzysztof Helt Date: Tue, 31 Mar 2009 15:25:04 -0700 Subject: cirrusfb: add Laguna additional overflow register Add additional overflow register setting for Laguna chips. Also, simplify some code in the cirrusfb_pan_display() and cirrusfb_blank(). Signed-off-by: Krzysztof Helt Cc: Geert Uytterhoeven Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/cirrusfb.c | 68 ++++++++++++++++++++++++++++++------------------ include/video/cirrus.h | 1 + 2 files changed, 43 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/video/cirrusfb.c b/drivers/video/cirrusfb.c index dd09bae910f0..119e49ed6218 100644 --- a/drivers/video/cirrusfb.c +++ b/drivers/video/cirrusfb.c @@ -1259,13 +1259,32 @@ static int cirrusfb_set_par_foo(struct fb_info *info) /* screen start addr #16-18, fastpagemode cycles */ vga_wcrt(regbase, CL_CRT1B, tmp); - if (cinfo->btype == BT_SD64 || - cinfo->btype == BT_PICASSO4 || - cinfo->btype == BT_ALPINE || - cinfo->btype == BT_GD5480) - /* screen start address bit 19 */ + /* screen start address bit 19 */ + if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19) vga_wcrt(regbase, CL_CRT1D, 0x00); + if (cinfo->btype == BT_LAGUNA || + cinfo->btype == BT_GD5480) { + + tmp = 0; + if ((htotal + 5) & 256) + tmp |= 128; + if (hdispend & 256) + tmp |= 64; + if (hsyncstart & 256) + tmp |= 48; + if (vtotal & 1024) + tmp |= 8; + if (vdispend & 1024) + tmp |= 4; + if (vsyncstart & 1024) + tmp |= 3; + + vga_wcrt(regbase, CL_CRT1E, tmp); + dev_dbg(info->device, "CRT1e: %d\n", tmp); + } + + /* text cursor location high */ vga_wcrt(regbase, VGA_CRTC_CURSOR_HI, 0); /* text cursor location low */ @@ -1383,7 +1402,7 @@ static int cirrusfb_pan_display(struct fb_var_screeninfo *var, int xoffset = 0; int yoffset = 0; unsigned long base; - unsigned char tmp = 0, tmp2 = 0, xpix; + unsigned char tmp, xpix; struct cirrusfb_info *cinfo = info->par; dev_dbg(info->device, @@ -1418,6 +1437,8 @@ static int cirrusfb_pan_display(struct fb_var_screeninfo *var, vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI, (unsigned char) (base >> 8)); + /* 0xf2 is %11110010, exclude tmp bits */ + tmp = vga_rcrt(cinfo->regbase, CL_CRT1B) & 0xf2; /* construct bits 16, 17 and 18 of screen start address */ if (base & 0x10000) tmp |= 0x01; @@ -1426,9 +1447,7 @@ static int cirrusfb_pan_display(struct fb_var_screeninfo *var, if (base & 0x40000) tmp |= 0x08; - /* 0xf2 is %11110010, exclude tmp bits */ - tmp2 = (vga_rcrt(cinfo->regbase, CL_CRT1B) & 0xf2) | tmp; - vga_wcrt(cinfo->regbase, CL_CRT1B, tmp2); + vga_wcrt(cinfo->regbase, CL_CRT1B, tmp); /* construct bit 19 of screen start address */ if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19) @@ -1473,47 +1492,44 @@ static int cirrusfb_blank(int blank_mode, struct fb_info *info) /* Undo current */ if (current_mode == FB_BLANK_NORMAL || - current_mode == FB_BLANK_UNBLANK) { - /* unblank the screen */ - val = vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE); + current_mode == FB_BLANK_UNBLANK) /* clear "FullBandwidth" bit */ - vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val & 0xdf); - /* and undo VESA suspend trickery */ - vga_wgfx(cinfo->regbase, CL_GRE, 0x00); - } - - /* set new */ - if (blank_mode > FB_BLANK_NORMAL) { - /* blank the screen */ - val = vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE); + val = 0; + else /* set "FullBandwidth" bit */ - vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val | 0x20); - } + val = 0x20; + + val |= vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE) & 0xdf; + vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val); switch (blank_mode) { case FB_BLANK_UNBLANK: case FB_BLANK_NORMAL: + val = 0x00; break; case FB_BLANK_VSYNC_SUSPEND: - vga_wgfx(cinfo->regbase, CL_GRE, 0x04); + val = 0x04; break; case FB_BLANK_HSYNC_SUSPEND: - vga_wgfx(cinfo->regbase, CL_GRE, 0x02); + val = 0x02; break; case FB_BLANK_POWERDOWN: - vga_wgfx(cinfo->regbase, CL_GRE, 0x06); + val = 0x06; break; default: dev_dbg(info->device, "EXIT, returning 1\n"); return 1; } + vga_wgfx(cinfo->regbase, CL_GRE, val); + cinfo->blank_mode = blank_mode; dev_dbg(info->device, "EXIT, returning 0\n"); /* Let fbcon do a soft blank for us */ return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0; } + /**** END Hardware specific Routines **************************************/ /****************************************************************************/ /**** BEGIN Internal Routines ***********************************************/ diff --git a/include/video/cirrus.h b/include/video/cirrus.h index b2776b6c8679..a1b7985b6b27 100644 --- a/include/video/cirrus.h +++ b/include/video/cirrus.h @@ -71,6 +71,7 @@ #define CL_CRT1B 0x1b /* Extended Display Controls */ #define CL_CRT1C 0x1c /* Sync adjust and genlock register */ #define CL_CRT1D 0x1d /* Overlay Extended Control register */ +#define CL_CRT1E 0x1e /* Another overflow register */ #define CL_CRT25 0x25 /* Part Status Register */ #define CL_CRT27 0x27 /* ID Register */ #define CL_CRT51 0x51 /* P4 disable "flicker fixer" */ -- cgit v1.2.3 From 6e30fc086d000d15abfe5550cc8b286335f7e132 Mon Sep 17 00:00:00 2001 From: Krzysztof Helt Date: Tue, 31 Mar 2009 15:25:05 -0700 Subject: cirrusfb: add mmio registers for Laguna chipsets The Laguna chipsets use special registers which are available through the mmio area. The cirrusfb driver does not use memory mapped registers for the PCI cards. Add the memory mapped area for Laguna chipsets and add basic usage of the special Laguna registers after SVGALIB code. This gives readable console at 16bpp on the GD-5465 (Laguna AGP). The 8bpp and 32bpp depths are still broken. Signed-off-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/cirrusfb.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) (limited to 'drivers') diff --git a/drivers/video/cirrusfb.c b/drivers/video/cirrusfb.c index 119e49ed6218..378d60e01902 100644 --- a/drivers/video/cirrusfb.c +++ b/drivers/video/cirrusfb.c @@ -327,6 +327,7 @@ enum cirrusfb_dbg_reg_class { /* info about board */ struct cirrusfb_info { u8 __iomem *regbase; + u8 __iomem *laguna_mmio; enum cirrus_board btype; unsigned char SFR; /* Shadow of special function register */ @@ -699,6 +700,7 @@ static int cirrusfb_set_par_foo(struct fb_info *info) int yres, vdispend, vsyncstart, vsyncend, vtotal; long freq; int nom, den, div; + unsigned int control, format, threshold; dev_dbg(info->device, "Requested mode: %dx%dx%d\n", var->xres, var->yres, var->bits_per_pixel); @@ -866,6 +868,23 @@ static int cirrusfb_set_par_foo(struct fb_info *info) cirrusfb_set_mclk_as_source(info, divMCLK); } } + if (cinfo->btype == BT_LAGUNA) { + long pcifc = fb_readl(cinfo->laguna_mmio + 0x3fc); + unsigned char tile = fb_readb(cinfo->laguna_mmio + 0x407); + unsigned short tile_control; + + tile_control = fb_readw(cinfo->laguna_mmio + 0x2c4); + fb_writew(tile_control & ~0x80, cinfo->laguna_mmio + 0x2c4); + + fb_writel(pcifc | 0x10000000l, cinfo->laguna_mmio + 0x3fc); + fb_writeb(tile & 0x3f, cinfo->laguna_mmio + 0x407); + control = fb_readw(cinfo->laguna_mmio + 0x402); + threshold = fb_readw(cinfo->laguna_mmio + 0xea); + control &= ~0x6800; + format = 0; + threshold &= 0xffe0; + threshold &= 0x3fbf; + } if (nom) { tmp = den << 1; if (div != 0) @@ -1035,6 +1054,7 @@ static int cirrusfb_set_par_foo(struct fb_info *info) case BT_LAGUNA: vga_wseq(regbase, CL_SEQR7, vga_rseq(regbase, CL_SEQR7) | 0x01); + threshold |= 0x10; break; default: @@ -1146,6 +1166,9 @@ static int cirrusfb_set_par_foo(struct fb_info *info) case BT_LAGUNA: vga_wseq(regbase, CL_SEQR7, vga_rseq(regbase, CL_SEQR7) & ~0x01); + control |= 0x2000; + format |= 0x1400; + threshold |= 0x10; break; default: @@ -1220,6 +1243,9 @@ static int cirrusfb_set_par_foo(struct fb_info *info) case BT_LAGUNA: vga_wseq(regbase, CL_SEQR7, vga_rseq(regbase, CL_SEQR7) & ~0x01); + control |= 0x6000; + format |= 0x3400; + threshold |= 0x20; break; default: @@ -1327,6 +1353,12 @@ static int cirrusfb_set_par_foo(struct fb_info *info) /* graphics cursor attributes: nothing special */ vga_wseq(regbase, CL_SEQR12, 0x0); + if (cinfo->btype == BT_LAGUNA) { + /* no tiles */ + fb_writew(control | 0x1000, cinfo->laguna_mmio + 0x402); + fb_writew(format, cinfo->laguna_mmio + 0xc0); + fb_writew(threshold, cinfo->laguna_mmio + 0xea); + } /* finally, turn on everything - turn off "FullBandwidth" bit */ /* also, set "DotClock%2" bit where requested */ tmp = 0x01; @@ -2000,7 +2032,10 @@ static void get_pci_addrs(const struct pci_dev *pdev, static void cirrusfb_pci_unmap(struct fb_info *info) { struct pci_dev *pdev = to_pci_dev(info->device); + struct cirrusfb_info *cinfo = info->par; + if (cinfo->laguna_mmio == NULL) + iounmap(cinfo->laguna_mmio); iounmap(info->screen_base); #if 0 /* if system didn't claim this region, we would... */ release_mem_region(0xA0000, 65535); @@ -2180,6 +2215,7 @@ static int __devinit cirrusfb_pci_register(struct pci_dev *pdev, get_pci_addrs(pdev, &board_addr, &info->fix.mmio_start); /* FIXME: this forces VGA. alternatives? */ cinfo->regbase = NULL; + cinfo->laguna_mmio = ioremap(info->fix.mmio_start, 0x1000); } dev_dbg(info->device, "Board address: 0x%lx, register address: 0x%lx\n", @@ -2234,6 +2270,8 @@ err_release_regions: #endif pci_release_regions(pdev); err_release_fb: + if (cinfo->laguna_mmio == NULL) + iounmap(cinfo->laguna_mmio); framebuffer_release(info); err_disable: err_out: -- cgit v1.2.3 From 6683e01e2c950f635a6c0e2bbc80db1b1838311f Mon Sep 17 00:00:00 2001 From: Krzysztof Helt Date: Tue, 31 Mar 2009 15:25:06 -0700 Subject: cirrusfb: do not calculate line length twice A line length is calculated twice: first in the cirrusfb_decode_var() then in the cirrusfb_set_par_foo(). Use the first calculated value. A nice side effect is that 32bpp mode works now. Signed-off-by: Krzysztof Helt Cc: Geert Uytterhoeven Cc: Arthur Marsh Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/cirrusfb.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/video/cirrusfb.c b/drivers/video/cirrusfb.c index 378d60e01902..53572c0f3474 100644 --- a/drivers/video/cirrusfb.c +++ b/drivers/video/cirrusfb.c @@ -694,7 +694,8 @@ static int cirrusfb_set_par_foo(struct fb_info *info) struct cirrusfb_regs regs; u8 __iomem *regbase = cinfo->regbase; unsigned char tmp; - int offset = 0, err; + int err; + int pitch; const struct cirrusfb_board_info_rec *bi; int hdispend, hsyncstart, hsyncend, htotal; int yres, vdispend, vsyncstart, vsyncend, vtotal; @@ -1027,7 +1028,6 @@ static int cirrusfb_set_par_foo(struct fb_info *info) vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x06); /* plane mask: only write to first plane */ vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0x01); - offset = var->xres_virtual / 16; } /****************************************************** @@ -1113,7 +1113,6 @@ static int cirrusfb_set_par_foo(struct fb_info *info) vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a); /* plane mask: enable writing to all 4 planes */ vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff); - offset = var->xres_virtual / 8; } /****************************************************** @@ -1190,7 +1189,6 @@ static int cirrusfb_set_par_foo(struct fb_info *info) vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a); /* plane mask: enable writing to all 4 planes */ vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff); - offset = var->xres_virtual / 4; } /****************************************************** @@ -1263,7 +1261,6 @@ static int cirrusfb_set_par_foo(struct fb_info *info) vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a); /* plane mask: enable writing to all 4 planes */ vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff); - offset = var->xres_virtual / 4; } /****************************************************** @@ -1277,9 +1274,10 @@ static int cirrusfb_set_par_foo(struct fb_info *info) "What's this? requested color depth == %d.\n", var->bits_per_pixel); - vga_wcrt(regbase, VGA_CRTC_OFFSET, offset & 0xff); + pitch = info->fix.line_length >> 3; + vga_wcrt(regbase, VGA_CRTC_OFFSET, pitch & 0xff); tmp = 0x22; - if (offset & 0x100) + if (pitch & 0x100) tmp |= 0x10; /* offset overflow bit */ /* screen start addr #16-18, fastpagemode cycles */ @@ -1287,7 +1285,7 @@ static int cirrusfb_set_par_foo(struct fb_info *info) /* screen start address bit 19 */ if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19) - vga_wcrt(regbase, CL_CRT1D, 0x00); + vga_wcrt(regbase, CL_CRT1D, (pitch >> 9) & 1); if (cinfo->btype == BT_LAGUNA || cinfo->btype == BT_GD5480) { -- cgit v1.2.3 From c4dec3962d6bff26010fcfc61500c1241469a6e0 Mon Sep 17 00:00:00 2001 From: Krzysztof Helt Date: Tue, 31 Mar 2009 15:25:07 -0700 Subject: cirrusfb: use 5-6-5 RGB for 16bpp mode Use the 5-6-5 RGB mode instead of the 5-5-5 mode at 16bpp depth. It fixes colors in the 16bpp modes on Cirrus Laguna chips. Signed-off-by: Krzysztof Helt Cc: Geert Uytterhoeven Cc: Arthur Marsh Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/cirrusfb.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/video/cirrusfb.c b/drivers/video/cirrusfb.c index 53572c0f3474..d844c41e2010 100644 --- a/drivers/video/cirrusfb.c +++ b/drivers/video/cirrusfb.c @@ -499,12 +499,12 @@ static int cirrusfb_check_var(struct fb_var_screeninfo *var, var->green.offset = -3; var->blue.offset = 8; } else { - var->red.offset = 10; + var->red.offset = 11; var->green.offset = 5; var->blue.offset = 0; } var->red.length = 5; - var->green.length = 5; + var->green.length = 6; var->blue.length = 5; break; @@ -1180,7 +1180,7 @@ static int cirrusfb_set_par_foo(struct fb_info *info) /* pixel mask: pass-through all planes */ WGen(cinfo, VGA_PEL_MSK, 0xff); #ifdef CONFIG_PCI - WHDR(cinfo, 0xc0); /* Copy Xbh */ + WHDR(cinfo, 0xc1); /* Copy Xbh */ #elif defined(CONFIG_ZORRO) /* FIXME: CONFIG_PCI and CONFIG_ZORRO may be defined both */ WHDR(cinfo, 0xa0); /* hidden dac reg: nothing special */ -- cgit v1.2.3 From 48c329e906f834711906ab4b0986ea0e857aff16 Mon Sep 17 00:00:00 2001 From: Krzysztof Helt Date: Tue, 31 Mar 2009 15:25:08 -0700 Subject: cirrusfb: various improvements Various improvements to the code: - kill a structure with only one field: multiplexing and use the field directly - move the cirrusfb_ops structure down the file to kill forward declarations - move cirrusfb_init() to kill forward declaration - kill register loads done already in the init_vgachip() - kill assigments done by higher layer in the cirrusfb_pan_display() - do not overwrite line pitch bit in the CL_CRT1D register - kill btype variables if they were used only once or twice - add cpu_relax() in the busy waiting loop The fix to the CL_CRT1D register handling makess the 1024x768 32bpp mode work. Previously, only lower resolution modes have worked with 32bpp. Signed-off-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/cirrusfb.c | 202 ++++++++++++++--------------------------------- 1 file changed, 58 insertions(+), 144 deletions(-) (limited to 'drivers') diff --git a/drivers/video/cirrusfb.c b/drivers/video/cirrusfb.c index d844c41e2010..9e52db7d3331 100644 --- a/drivers/video/cirrusfb.c +++ b/drivers/video/cirrusfb.c @@ -313,10 +313,6 @@ static const struct { }; #endif /* CONFIG_ZORRO */ -struct cirrusfb_regs { - int multiplexing; -}; - #ifdef CIRRUSFB_DEBUG enum cirrusfb_dbg_reg_class { CRT, @@ -331,7 +327,7 @@ struct cirrusfb_info { enum cirrus_board btype; unsigned char SFR; /* Shadow of special function register */ - struct cirrusfb_regs currentmode; + int multiplexing; int blank_mode; u32 pseudo_palette[16]; @@ -345,43 +341,8 @@ static char *mode_option __devinitdata = "640x480@60"; /**** BEGIN PROTOTYPES ******************************************************/ /*--- Interface used by the world ------------------------------------------*/ -static int cirrusfb_init(void); -#ifndef MODULE -static int cirrusfb_setup(char *options); -#endif - -static int cirrusfb_open(struct fb_info *info, int user); -static int cirrusfb_release(struct fb_info *info, int user); -static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green, - unsigned blue, unsigned transp, - struct fb_info *info); -static int cirrusfb_check_var(struct fb_var_screeninfo *var, - struct fb_info *info); -static int cirrusfb_set_par(struct fb_info *info); static int cirrusfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info); -static int cirrusfb_blank(int blank_mode, struct fb_info *info); -static void cirrusfb_fillrect(struct fb_info *info, - const struct fb_fillrect *region); -static void cirrusfb_copyarea(struct fb_info *info, - const struct fb_copyarea *area); -static void cirrusfb_imageblit(struct fb_info *info, - const struct fb_image *image); - -/* function table of the above functions */ -static struct fb_ops cirrusfb_ops = { - .owner = THIS_MODULE, - .fb_open = cirrusfb_open, - .fb_release = cirrusfb_release, - .fb_setcolreg = cirrusfb_setcolreg, - .fb_check_var = cirrusfb_check_var, - .fb_set_par = cirrusfb_set_par, - .fb_pan_display = cirrusfb_pan_display, - .fb_blank = cirrusfb_blank, - .fb_fillrect = cirrusfb_fillrect, - .fb_copyarea = cirrusfb_copyarea, - .fb_imageblit = cirrusfb_imageblit, -}; /*--- Internal routines ----------------------------------------------------*/ static void init_vgachip(struct fb_info *info); @@ -587,7 +548,6 @@ static int cirrusfb_check_var(struct fb_var_screeninfo *var, } static int cirrusfb_decode_var(const struct fb_var_screeninfo *var, - struct cirrusfb_regs *regs, struct fb_info *info) { long freq; @@ -628,7 +588,7 @@ static int cirrusfb_decode_var(const struct fb_var_screeninfo *var, dev_dbg(info->device, "desired pixclock: %ld kHz\n", freq); maxclock = cirrusfb_board_info[cinfo->btype].maxclock[maxclockidx]; - regs->multiplexing = 0; + cinfo->multiplexing = 0; /* If the frequency is greater than we can support, we might be able * to use multiplexing for the video mode */ @@ -636,7 +596,7 @@ static int cirrusfb_decode_var(const struct fb_var_screeninfo *var, switch (cinfo->btype) { case BT_ALPINE: case BT_GD5480: - regs->multiplexing = 1; + cinfo->multiplexing = 1; break; default: @@ -691,7 +651,6 @@ static int cirrusfb_set_par_foo(struct fb_info *info) { struct cirrusfb_info *cinfo = info->par; struct fb_var_screeninfo *var = &info->var; - struct cirrusfb_regs regs; u8 __iomem *regbase = cinfo->regbase; unsigned char tmp; int err; @@ -709,7 +668,7 @@ static int cirrusfb_set_par_foo(struct fb_info *info) init_vgachip(info); - err = cirrusfb_decode_var(var, ®s, info); + err = cirrusfb_decode_var(var, info); if (err) { /* should never happen */ dev_dbg(info->device, "mode change aborted. invalid var.\n"); @@ -753,7 +712,7 @@ static int cirrusfb_set_par_foo(struct fb_info *info) vsyncend /= 2; vdispend /= 2; } - if (regs.multiplexing) { + if (cinfo->multiplexing) { htotal /= 2; hsyncstart /= 2; hsyncend /= 2; @@ -964,7 +923,7 @@ static int cirrusfb_set_par_foo(struct fb_info *info) case BT_ALPINE: case BT_GD5480: vga_wseq(regbase, CL_SEQR7, - regs.multiplexing ? + cinfo->multiplexing ? bi->sr07_1bpp_mux : bi->sr07_1bpp); break; @@ -1018,7 +977,7 @@ static int cirrusfb_set_par_foo(struct fb_info *info) /* pixel mask: pass-through for first plane */ WGen(cinfo, VGA_PEL_MSK, 0x01); - if (regs.multiplexing) + if (cinfo->multiplexing) /* hidden dac reg: 1280x1024 */ WHDR(cinfo, 0x4a); else @@ -1047,7 +1006,7 @@ static int cirrusfb_set_par_foo(struct fb_info *info) case BT_ALPINE: case BT_GD5480: vga_wseq(regbase, CL_SEQR7, - regs.multiplexing ? + cinfo->multiplexing ? bi->sr07_8bpp_mux : bi->sr07_8bpp); break; @@ -1101,18 +1060,12 @@ static int cirrusfb_set_par_foo(struct fb_info *info) /* mode register: 256 color mode */ vga_wgfx(regbase, VGA_GFX_MODE, 64); - /* pixel mask: pass-through all planes */ - WGen(cinfo, VGA_PEL_MSK, 0xff); - if (regs.multiplexing) + if (cinfo->multiplexing) /* hidden dac reg: 1280x1024 */ WHDR(cinfo, 0x4a); else /* hidden dac: nothing */ WHDR(cinfo, 0); - /* memory mode: chain4, ext. memory */ - vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a); - /* plane mask: enable writing to all 4 planes */ - vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff); } /****************************************************** @@ -1177,18 +1130,12 @@ static int cirrusfb_set_par_foo(struct fb_info *info) /* mode register: 256 color mode */ vga_wgfx(regbase, VGA_GFX_MODE, 64); - /* pixel mask: pass-through all planes */ - WGen(cinfo, VGA_PEL_MSK, 0xff); #ifdef CONFIG_PCI WHDR(cinfo, 0xc1); /* Copy Xbh */ #elif defined(CONFIG_ZORRO) /* FIXME: CONFIG_PCI and CONFIG_ZORRO may be defined both */ WHDR(cinfo, 0xa0); /* hidden dac reg: nothing special */ #endif - /* memory mode: chain4, ext. memory */ - vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a); - /* plane mask: enable writing to all 4 planes */ - vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff); } /****************************************************** @@ -1253,14 +1200,8 @@ static int cirrusfb_set_par_foo(struct fb_info *info) /* mode register: 256 color mode */ vga_wgfx(regbase, VGA_GFX_MODE, 64); - /* pixel mask: pass-through all planes */ - WGen(cinfo, VGA_PEL_MSK, 0xff); /* hidden dac reg: 8-8-8 mode (24 or 32) */ WHDR(cinfo, 0xc5); - /* memory mode: chain4, ext. memory */ - vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a); - /* plane mask: enable writing to all 4 planes */ - vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff); } /****************************************************** @@ -1309,48 +1250,13 @@ static int cirrusfb_set_par_foo(struct fb_info *info) } - /* text cursor location high */ - vga_wcrt(regbase, VGA_CRTC_CURSOR_HI, 0); - /* text cursor location low */ - vga_wcrt(regbase, VGA_CRTC_CURSOR_LO, 0); - /* underline row scanline = at very bottom */ - vga_wcrt(regbase, VGA_CRTC_UNDERLINE, 0); - - /* controller mode */ - vga_wattr(regbase, VGA_ATC_MODE, 1); - /* overscan (border) color */ - vga_wattr(regbase, VGA_ATC_OVERSCAN, 0); - /* color plane enable */ - vga_wattr(regbase, VGA_ATC_PLANE_ENABLE, 15); /* pixel panning */ vga_wattr(regbase, CL_AR33, 0); - /* color select */ - vga_wattr(regbase, VGA_ATC_COLOR_PAGE, 0); /* [ EGS: SetOffset(); ] */ /* From SetOffset(): Turn on VideoEnable bit in Attribute controller */ AttrOn(cinfo); - /* set/reset register */ - vga_wgfx(regbase, VGA_GFX_SR_VALUE, 0); - /* set/reset enable */ - vga_wgfx(regbase, VGA_GFX_SR_ENABLE, 0); - /* color compare */ - vga_wgfx(regbase, VGA_GFX_COMPARE_VALUE, 0); - /* data rotate */ - vga_wgfx(regbase, VGA_GFX_DATA_ROTATE, 0); - /* read map select */ - vga_wgfx(regbase, VGA_GFX_PLANE_READ, 0); - /* miscellaneous register */ - vga_wgfx(regbase, VGA_GFX_MISC, 1); - /* color don't care */ - vga_wgfx(regbase, VGA_GFX_COMPARE_MASK, 15); - /* bit mask */ - vga_wgfx(regbase, VGA_GFX_BIT_MASK, 255); - - /* graphics cursor attributes: nothing special */ - vga_wseq(regbase, CL_SEQR12, 0x0); - if (cinfo->btype == BT_LAGUNA) { /* no tiles */ fb_writew(control | 0x1000, cinfo->laguna_mmio + 0x402); @@ -1369,8 +1275,6 @@ static int cirrusfb_set_par_foo(struct fb_info *info) vga_wseq(regbase, VGA_SEQ_CLOCK_MODE, tmp); dev_dbg(info->device, "CL_SEQR1: %d\n", tmp); - cinfo->currentmode = regs; - /* pan to requested offset */ cirrusfb_pan_display(var, info); @@ -1443,9 +1347,6 @@ static int cirrusfb_pan_display(struct fb_var_screeninfo *var, if (var->vmode & FB_VMODE_YWRAP) return -EINVAL; - info->var.xoffset = var->xoffset; - info->var.yoffset = var->yoffset; - xoffset = var->xoffset * info->var.bits_per_pixel / 8; yoffset = var->yoffset; @@ -1480,8 +1381,11 @@ static int cirrusfb_pan_display(struct fb_var_screeninfo *var, vga_wcrt(cinfo->regbase, CL_CRT1B, tmp); /* construct bit 19 of screen start address */ - if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19) - vga_wcrt(cinfo->regbase, CL_CRT1D, (base >> 12) & 0x80); + if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19) { + tmp = vga_rcrt(cinfo->regbase, CL_CRT1D) & ~0x80; + tmp |= (base >> 12) & 0x80; + vga_wcrt(cinfo->regbase, CL_CRT1D, tmp); + } /* write pixel panning value to AR33; this does not quite work in 8bpp * @@ -1670,8 +1574,8 @@ static void init_vgachip(struct fb_info *info) vga_wseq(cinfo->regbase, VGA_SEQ_PLANE_WRITE, 0xff); /* character map select: doesn't even matter in gx mode */ vga_wseq(cinfo->regbase, VGA_SEQ_CHARACTER_MAP, 0x00); - /* memory mode: chain-4, no odd/even, ext. memory */ - vga_wseq(cinfo->regbase, VGA_SEQ_MEMORY_MODE, 0x0e); + /* memory mode: chain4, ext. memory */ + vga_wseq(cinfo->regbase, VGA_SEQ_MEMORY_MODE, 0x0a); /* controller-internal base address of video memory */ if (bi->init_sr07) @@ -1784,7 +1688,6 @@ static void init_vgachip(struct fb_info *info) vga_wattr(cinfo->regbase, VGA_ATC_OVERSCAN, 0x00); /* Color Plane enable: Enable all 4 planes */ vga_wattr(cinfo->regbase, VGA_ATC_PLANE_ENABLE, 0x0f); -/* ### vga_wattr(cinfo->regbase, CL_AR33, 0x00); * Pixel Panning: - */ /* Color Select: - */ vga_wattr(cinfo->regbase, VGA_ATC_COLOR_PAGE, 0x00); @@ -2063,6 +1966,21 @@ static void cirrusfb_zorro_unmap(struct fb_info *info) } #endif /* CONFIG_ZORRO */ +/* function table of the above functions */ +static struct fb_ops cirrusfb_ops = { + .owner = THIS_MODULE, + .fb_open = cirrusfb_open, + .fb_release = cirrusfb_release, + .fb_setcolreg = cirrusfb_setcolreg, + .fb_check_var = cirrusfb_check_var, + .fb_set_par = cirrusfb_set_par, + .fb_pan_display = cirrusfb_pan_display, + .fb_blank = cirrusfb_blank, + .fb_fillrect = cirrusfb_fillrect, + .fb_copyarea = cirrusfb_copyarea, + .fb_imageblit = cirrusfb_imageblit, +}; + static int __devinit cirrusfb_set_fbinfo(struct fb_info *info) { struct cirrusfb_info *cinfo = info->par; @@ -2111,12 +2029,9 @@ static int __devinit cirrusfb_register(struct fb_info *info) { struct cirrusfb_info *cinfo = info->par; int err; - enum cirrus_board btype; - - btype = cinfo->btype; /* sanity checks */ - assert(btype != BT_NONE); + assert(cinfo->btype != BT_NONE); /* set all the vital stuff */ cirrusfb_set_fbinfo(info); @@ -2132,7 +2047,7 @@ static int __devinit cirrusfb_register(struct fb_info *info) info->var.activate = FB_ACTIVATE_NOW; - err = cirrusfb_decode_var(&info->var, &cinfo->currentmode, info); + err = cirrusfb_decode_var(&info->var, info); if (err < 0) { /* should never happen */ dev_dbg(info->device, @@ -2174,7 +2089,6 @@ static int __devinit cirrusfb_pci_register(struct pci_dev *pdev, { struct cirrusfb_info *cinfo; struct fb_info *info; - enum cirrus_board btype; unsigned long board_addr, board_size; int ret; @@ -2192,11 +2106,11 @@ static int __devinit cirrusfb_pci_register(struct pci_dev *pdev, } cinfo = info->par; - cinfo->btype = btype = (enum cirrus_board) ent->driver_data; + cinfo->btype = (enum cirrus_board) ent->driver_data; dev_dbg(info->device, " Found PCI device, base address 0 is 0x%Lx, btype set to %d\n", - (unsigned long long)pdev->resource[0].start, btype); + (unsigned long long)pdev->resource[0].start, cinfo->btype); dev_dbg(info->device, " base address 1 is 0x%Lx\n", (unsigned long long)pdev->resource[1].start); @@ -2219,7 +2133,7 @@ static int __devinit cirrusfb_pci_register(struct pci_dev *pdev, dev_dbg(info->device, "Board address: 0x%lx, register address: 0x%lx\n", board_addr, info->fix.mmio_start); - board_size = (btype == BT_GD5480) ? + board_size = (cinfo->btype == BT_GD5480) ? 32 * MB_ : cirrusfb_get_memsize(info, cinfo->regbase); ret = pci_request_regions(pdev, "cirrusfb"); @@ -2425,27 +2339,6 @@ static struct zorro_driver cirrusfb_zorro_driver = { }; #endif /* CONFIG_ZORRO */ -static int __init cirrusfb_init(void) -{ - int error = 0; - -#ifndef MODULE - char *option = NULL; - - if (fb_get_options("cirrusfb", &option)) - return -ENODEV; - cirrusfb_setup(option); -#endif - -#ifdef CONFIG_ZORRO - error |= zorro_register_driver(&cirrusfb_zorro_driver); -#endif -#ifdef CONFIG_PCI - error |= pci_register_driver(&cirrusfb_pci_driver); -#endif - return error; -} - #ifndef MODULE static int __init cirrusfb_setup(char *options) { @@ -2477,6 +2370,27 @@ MODULE_AUTHOR("Copyright 1999,2000 Jeff Garzik "); MODULE_DESCRIPTION("Accelerated FBDev driver for Cirrus Logic chips"); MODULE_LICENSE("GPL"); +static int __init cirrusfb_init(void) +{ + int error = 0; + +#ifndef MODULE + char *option = NULL; + + if (fb_get_options("cirrusfb", &option)) + return -ENODEV; + cirrusfb_setup(option); +#endif + +#ifdef CONFIG_ZORRO + error |= zorro_register_driver(&cirrusfb_zorro_driver); +#endif +#ifdef CONFIG_PCI + error |= pci_register_driver(&cirrusfb_pci_driver); +#endif + return error; +} + static void __exit cirrusfb_exit(void) { #ifdef CONFIG_PCI @@ -2683,7 +2597,7 @@ static void cirrusfb_WaitBLT(u8 __iomem *regbase) { /* now busy-wait until we're done */ while (vga_rgfx(regbase, CL_GR31) & 0x08) - /* do nothing */ ; + cpu_relax(); } /******************************************************************* -- cgit v1.2.3 From 1b48cb563d59e03dbf530174f30c0ed3b6fba513 Mon Sep 17 00:00:00 2001 From: Krzysztof Helt Date: Tue, 31 Mar 2009 15:25:08 -0700 Subject: cirrusfb: Laguna chipset 8bpp fix Fix 8bpp mode by adding handling of the Laguna chipsets to various places and stop trashing a HDR register which probably does not exist on the Laguna. Fix compilation warnings about uninitialized variables also. Finally, all 8bpp, 16bpp and 32bpp modes work on the Laguna chipset. Signed-off-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/cirrusfb.c | 34 +++++++++++++++++++++------------- include/video/cirrus.h | 1 - 2 files changed, 21 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/video/cirrusfb.c b/drivers/video/cirrusfb.c index 9e52db7d3331..12f45520e5d3 100644 --- a/drivers/video/cirrusfb.c +++ b/drivers/video/cirrusfb.c @@ -660,7 +660,7 @@ static int cirrusfb_set_par_foo(struct fb_info *info) int yres, vdispend, vsyncstart, vsyncend, vtotal; long freq; int nom, den, div; - unsigned int control, format, threshold; + unsigned int control = 0, format = 0, threshold = 0; dev_dbg(info->device, "Requested mode: %dx%dx%d\n", var->xres, var->yres, var->bits_per_pixel); @@ -842,8 +842,7 @@ static int cirrusfb_set_par_foo(struct fb_info *info) threshold = fb_readw(cinfo->laguna_mmio + 0xea); control &= ~0x6800; format = 0; - threshold &= 0xffe0; - threshold &= 0x3fbf; + threshold &= 0xffe0 & 0x3fbf; } if (nom) { tmp = den << 1; @@ -893,6 +892,8 @@ static int cirrusfb_set_par_foo(struct fb_info *info) tmp |= 0x40; if (var->sync & FB_SYNC_VERT_HIGH_ACT) tmp |= 0x80; + if (cinfo->btype == BT_LAGUNA) + tmp |= 0xc; WGen(cinfo, VGA_MIS_W, tmp); /* Screen A Preset Row-Scan register */ @@ -1228,9 +1229,7 @@ static int cirrusfb_set_par_foo(struct fb_info *info) if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19) vga_wcrt(regbase, CL_CRT1D, (pitch >> 9) & 1); - if (cinfo->btype == BT_LAGUNA || - cinfo->btype == BT_GD5480) { - + if (cinfo->btype == BT_LAGUNA) { tmp = 0; if ((htotal + 5) & 256) tmp |= 128; @@ -1360,7 +1359,8 @@ static int cirrusfb_pan_display(struct fb_var_screeninfo *var, xpix = (unsigned char) ((xoffset % 4) * 2); } - cirrusfb_WaitBLT(cinfo->regbase); /* make sure all the BLT's are done */ + if (cinfo->btype != BT_LAGUNA) + cirrusfb_WaitBLT(cinfo->regbase); /* lower 8 + 8 bits of screen start address */ vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO, @@ -1394,7 +1394,8 @@ static int cirrusfb_pan_display(struct fb_var_screeninfo *var, if (info->var.bits_per_pixel == 1) vga_wattr(cinfo->regbase, CL_AR33, xpix); - cirrusfb_WaitBLT(cinfo->regbase); + if (cinfo->btype != BT_LAGUNA) + cirrusfb_WaitBLT(cinfo->regbase); return 0; } @@ -1513,6 +1514,7 @@ static void init_vgachip(struct fb_info *info) vga_wgfx(cinfo->regbase, CL_GR2F, 0x00); break; + case BT_LAGUNA: case BT_ALPINE: /* Nothing to do to reset the board. */ break; @@ -1538,7 +1540,7 @@ static void init_vgachip(struct fb_info *info) WGen(cinfo, CL_VSSM2, 0x01); /* reset sequencer logic */ - vga_wseq(cinfo->regbase, CL_SEQR0, 0x03); + vga_wseq(cinfo->regbase, VGA_SEQ_RESET, 0x03); /* FullBandwidth (video off) and 8/9 dot clock */ vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, 0x21); @@ -1560,6 +1562,7 @@ static void init_vgachip(struct fb_info *info) vga_wseq(cinfo->regbase, CL_SEQRF, 0x98); break; case BT_ALPINE: + case BT_LAGUNA: break; case BT_SD64: vga_wseq(cinfo->regbase, CL_SEQRF, 0xb8); @@ -1648,7 +1651,8 @@ static void init_vgachip(struct fb_info *info) vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_MASK, 0x0f); /* Bit Mask: no mask at all */ vga_wgfx(cinfo->regbase, VGA_GFX_BIT_MASK, 0xff); - if (cinfo->btype == BT_ALPINE) + + if (cinfo->btype == BT_ALPINE || cinfo->btype == BT_LAGUNA) /* (5434 can't have bit 3 set for bitblt) */ vga_wgfx(cinfo->regbase, CL_GRB, 0x20); else @@ -1845,7 +1849,8 @@ static void cirrusfb_imageblit(struct fb_info *info, { struct cirrusfb_info *cinfo = info->par; - cirrusfb_WaitBLT(cinfo->regbase); + if (cinfo->btype != BT_LAGUNA) + cirrusfb_WaitBLT(cinfo->regbase); cfb_imageblit(info, image); } @@ -1992,7 +1997,7 @@ static int __devinit cirrusfb_set_fbinfo(struct fb_info *info) | FBINFO_HWACCEL_YPAN | FBINFO_HWACCEL_FILLRECT | FBINFO_HWACCEL_COPYAREA; - if (noaccel) + if (noaccel || cinfo->btype == BT_LAGUNA) info->flags |= FBINFO_HWACCEL_DISABLED; info->fbops = &cirrusfb_ops; if (cinfo->btype == BT_GD5480) { @@ -2481,6 +2486,8 @@ static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val) { unsigned char dummy; + if (cinfo->btype == BT_LAGUNA) + return; if (cinfo->btype == BT_PICASSO) { /* Klaus' hint for correct access to HDR on some boards */ /* first write 0 to pixel mask (3c6) */ @@ -2548,7 +2555,8 @@ static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned ch vga_w(cinfo->regbase, VGA_PEL_IW, regnum); if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 || - cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) { + cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480 || + cinfo->btype == BT_LAGUNA) { /* but DAC data register IS, at least for Picasso II */ if (cinfo->btype == BT_PICASSO) data += 0xfff; diff --git a/include/video/cirrus.h b/include/video/cirrus.h index a1b7985b6b27..9a5e9ee30782 100644 --- a/include/video/cirrus.h +++ b/include/video/cirrus.h @@ -32,7 +32,6 @@ #define CL_VSSM2 0x3c3 /* Motherboard Sleep */ /*** VGA Sequencer Registers ***/ -#define CL_SEQR0 0x0 /* Reset */ /* the following are from the "extension registers" group */ #define CL_SEQR6 0x6 /* Unlock ALL Extensions */ #define CL_SEQR7 0x7 /* Extended Sequencer Mode */ -- cgit v1.2.3 From 99a4584752bb41330342a427d014482525de7433 Mon Sep 17 00:00:00 2001 From: Krzysztof Helt Date: Tue, 31 Mar 2009 15:25:09 -0700 Subject: cirrusfb: check_var improvements Break cirrusfb_decode_var() function into two parts: cirrusfb_check_pixclock() which can be called from the cirrusfb_check_var() aand merge rest into the cirrusfb_set_par_foo(). This allows rejecting modes with too high pixclock before before any change to hardware state (and a console is messed up). Also, fix RGB field's lengths for 8bpp modes to correct ones so X11 works with fbdev driver with cirrusfb. Kill some redundant function calls or register loads. Signed-off-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/cirrusfb.c | 195 ++++++++++++++++++----------------------------- 1 file changed, 74 insertions(+), 121 deletions(-) (limited to 'drivers') diff --git a/drivers/video/cirrusfb.c b/drivers/video/cirrusfb.c index 12f45520e5d3..bab713b63a0c 100644 --- a/drivers/video/cirrusfb.c +++ b/drivers/video/cirrusfb.c @@ -432,6 +432,53 @@ static int cirrusfb_check_mclk(struct fb_info *info, long freq) return 0; } +static int cirrusfb_check_pixclock(const struct fb_var_screeninfo *var, + struct fb_info *info) +{ + long freq; + long maxclock; + struct cirrusfb_info *cinfo = info->par; + unsigned maxclockidx = var->bits_per_pixel >> 3; + + /* convert from ps to kHz */ + freq = PICOS2KHZ(var->pixclock); + + dev_dbg(info->device, "desired pixclock: %ld kHz\n", freq); + + maxclock = cirrusfb_board_info[cinfo->btype].maxclock[maxclockidx]; + cinfo->multiplexing = 0; + + /* If the frequency is greater than we can support, we might be able + * to use multiplexing for the video mode */ + if (freq > maxclock) { + switch (cinfo->btype) { + case BT_ALPINE: + case BT_GD5480: + cinfo->multiplexing = 1; + break; + + default: + dev_err(info->device, + "Frequency greater than maxclock (%ld kHz)\n", + maxclock); + return -EINVAL; + } + } +#if 0 + /* TODO: If we have a 1MB 5434, we need to put ourselves in a mode where + * the VCLK is double the pixel clock. */ + switch (var->bits_per_pixel) { + case 16: + case 32: + if (var->xres <= 800) + /* Xbh has this type of clock for 32-bit */ + freq /= 2; + break; + } +#endif + return 0; +} + static int cirrusfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { @@ -449,7 +496,7 @@ static int cirrusfb_check_var(struct fb_var_screeninfo *var, case 8: var->red.offset = 0; - var->red.length = 6; + var->red.length = 8; var->green = var->red; var->blue = var->red; break; @@ -513,7 +560,6 @@ static int cirrusfb_check_var(struct fb_var_screeninfo *var, return -EINVAL; } - if (var->xoffset < 0) var->xoffset = 0; if (var->yoffset < 0) @@ -544,80 +590,9 @@ static int cirrusfb_check_var(struct fb_var_screeninfo *var, return -EINVAL; } - return 0; -} - -static int cirrusfb_decode_var(const struct fb_var_screeninfo *var, - struct fb_info *info) -{ - long freq; - long maxclock; - int maxclockidx = var->bits_per_pixel >> 3; - struct cirrusfb_info *cinfo = info->par; - - switch (var->bits_per_pixel) { - case 1: - info->fix.line_length = var->xres_virtual / 8; - info->fix.visual = FB_VISUAL_MONO10; - break; - - case 8: - info->fix.line_length = var->xres_virtual; - info->fix.visual = FB_VISUAL_PSEUDOCOLOR; - break; - - case 16: - case 32: - info->fix.line_length = var->xres_virtual * maxclockidx; - info->fix.visual = FB_VISUAL_TRUECOLOR; - break; - - default: - dev_dbg(info->device, - "Unsupported bpp size: %d\n", var->bits_per_pixel); - assert(false); - /* should never occur */ - break; - } - - info->fix.type = FB_TYPE_PACKED_PIXELS; - - /* convert from ps to kHz */ - freq = PICOS2KHZ(var->pixclock); - - dev_dbg(info->device, "desired pixclock: %ld kHz\n", freq); - - maxclock = cirrusfb_board_info[cinfo->btype].maxclock[maxclockidx]; - cinfo->multiplexing = 0; - - /* If the frequency is greater than we can support, we might be able - * to use multiplexing for the video mode */ - if (freq > maxclock) { - switch (cinfo->btype) { - case BT_ALPINE: - case BT_GD5480: - cinfo->multiplexing = 1; - break; + if (cirrusfb_check_pixclock(var, info)) + return -EINVAL; - default: - dev_err(info->device, - "Frequency greater than maxclock (%ld kHz)\n", - maxclock); - return -EINVAL; - } - } -#if 0 - /* TODO: If we have a 1MB 5434, we need to put ourselves in a mode where - * the VCLK is double the pixel clock. */ - switch (var->bits_per_pixel) { - case 16: - case 32: - if (var->xres <= 800) - /* Xbh has this type of clock for 32-bit */ - freq /= 2; - break; - } -#endif return 0; } @@ -653,7 +628,6 @@ static int cirrusfb_set_par_foo(struct fb_info *info) struct fb_var_screeninfo *var = &info->var; u8 __iomem *regbase = cinfo->regbase; unsigned char tmp; - int err; int pitch; const struct cirrusfb_board_info_rec *bi; int hdispend, hsyncstart, hsyncend, htotal; @@ -664,16 +638,28 @@ static int cirrusfb_set_par_foo(struct fb_info *info) dev_dbg(info->device, "Requested mode: %dx%dx%d\n", var->xres, var->yres, var->bits_per_pixel); - dev_dbg(info->device, "pixclock: %d\n", var->pixclock); - init_vgachip(info); + switch (var->bits_per_pixel) { + case 1: + info->fix.line_length = var->xres_virtual / 8; + info->fix.visual = FB_VISUAL_MONO10; + break; - err = cirrusfb_decode_var(var, info); - if (err) { - /* should never happen */ - dev_dbg(info->device, "mode change aborted. invalid var.\n"); - return -EINVAL; + case 8: + info->fix.line_length = var->xres_virtual; + info->fix.visual = FB_VISUAL_PSEUDOCOLOR; + break; + + case 16: + case 32: + info->fix.line_length = var->xres_virtual * + var->bits_per_pixel >> 3; + info->fix.visual = FB_VISUAL_TRUECOLOR; + break; } + info->fix.type = FB_TYPE_PACKED_PIXELS; + + init_vgachip(info); bi = &cirrusfb_board_info[cinfo->btype]; @@ -873,9 +859,6 @@ static int cirrusfb_set_par_foo(struct fb_info *info) * address wrap, no compat. */ vga_wcrt(regbase, VGA_CRTC_MODE, 0xc3); -/* HAEH? vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20); - * previously: 0x00 unlock VGA_CRTC_H_TOTAL..CRT7 */ - /* don't know if it would hurt to also program this if no interlaced */ /* mode is used, but I feel better this way.. :-) */ if (var->vmode & FB_VMODE_INTERLACED) @@ -883,8 +866,6 @@ static int cirrusfb_set_par_foo(struct fb_info *info) else vga_wcrt(regbase, VGA_CRTC_REGS, 0x00); /* interlace control */ - vga_wseq(regbase, VGA_SEQ_CHARACTER_MAP, 0); - /* adjust horizontal/vertical sync type (low/high) */ /* enable display memory & CRTC I/O address for color mode */ tmp = 0x03; @@ -896,8 +877,6 @@ static int cirrusfb_set_par_foo(struct fb_info *info) tmp |= 0xc; WGen(cinfo, VGA_MIS_W, tmp); - /* Screen A Preset Row-Scan register */ - vga_wcrt(regbase, VGA_CRTC_PRESET_ROW, 0); /* text cursor on and start line */ vga_wcrt(regbase, VGA_CRTC_CURSOR_START, 0); /* text cursor end line */ @@ -1248,7 +1227,6 @@ static int cirrusfb_set_par_foo(struct fb_info *info) dev_dbg(info->device, "CRT1e: %d\n", tmp); } - /* pixel panning */ vga_wattr(regbase, CL_AR33, 0); @@ -1274,9 +1252,6 @@ static int cirrusfb_set_par_foo(struct fb_info *info) vga_wseq(regbase, VGA_SEQ_CLOCK_MODE, tmp); dev_dbg(info->device, "CL_SEQR1: %d\n", tmp); - /* pan to requested offset */ - cirrusfb_pan_display(var, info); - #ifdef CIRRUSFB_DEBUG cirrusfb_dbg_reg_dump(info, NULL); #endif @@ -1332,8 +1307,7 @@ static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green, static int cirrusfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) { - int xoffset = 0; - int yoffset = 0; + int xoffset; unsigned long base; unsigned char tmp, xpix; struct cirrusfb_info *cinfo = info->par; @@ -1347,9 +1321,8 @@ static int cirrusfb_pan_display(struct fb_var_screeninfo *var, return -EINVAL; xoffset = var->xoffset * info->var.bits_per_pixel / 8; - yoffset = var->yoffset; - base = yoffset * info->fix.line_length + xoffset; + base = var->yoffset * info->fix.line_length + xoffset; if (info->var.bits_per_pixel == 1) { /* base is already correct */ @@ -1363,10 +1336,8 @@ static int cirrusfb_pan_display(struct fb_var_screeninfo *var, cirrusfb_WaitBLT(cinfo->regbase); /* lower 8 + 8 bits of screen start address */ - vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO, - (unsigned char) (base & 0xff)); - vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI, - (unsigned char) (base >> 8)); + vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO, base & 0xff); + vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI, (base >> 8) & 0xff); /* 0xf2 is %11110010, exclude tmp bits */ tmp = vga_rcrt(cinfo->regbase, CL_CRT1B) & 0xf2; @@ -1544,10 +1515,6 @@ static void init_vgachip(struct fb_info *info) /* FullBandwidth (video off) and 8/9 dot clock */ vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, 0x21); - /* polarity (-/-), disable access to display memory, - * VGA_CRTC_START_HI base address: color - */ - WGen(cinfo, VGA_MIS_W, 0xc1); /* "magic cookie" - doesn't make any sense to me.. */ /* vga_wgfx(cinfo->regbase, CL_GRA, 0xce); */ @@ -1614,10 +1581,6 @@ static void init_vgachip(struct fb_info *info) vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_START, 0x20); /* Text cursor end: - */ vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_END, 0x00); - /* Screen start address high: 0 */ - vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI, 0x00); - /* Screen start address low: 0 */ - vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO, 0x00); /* text cursor location high: 0 */ vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_HI, 0x00); /* text cursor location low: 0 */ @@ -1625,10 +1588,6 @@ static void init_vgachip(struct fb_info *info) /* Underline Row scanline: - */ vga_wcrt(cinfo->regbase, VGA_CRTC_UNDERLINE, 0x00); - /* mode control: timing enable, byte mode, no compat modes */ - vga_wcrt(cinfo->regbase, VGA_CRTC_MODE, 0xc3); - /* Line Compare: not needed */ - vga_wcrt(cinfo->regbase, VGA_CRTC_LINE_COMPARE, 0x00); /* ### add 0x40 for text modes with > 30 MHz pixclock */ /* ext. display controls: ext.adr. wrap */ vga_wcrt(cinfo->regbase, CL_CRT1B, 0x02); @@ -1697,12 +1656,6 @@ static void init_vgachip(struct fb_info *info) WGen(cinfo, VGA_PEL_MSK, 0xff); /* Pixel mask: no mask */ - if (cinfo->btype != BT_ALPINE && cinfo->btype != BT_GD5480) - /* polarity (-/-), enable display mem, - * VGA_CRTC_START_HI i/o base = color - */ - WGen(cinfo, VGA_MIS_W, 0xc3); - /* BLT Start/status: Blitter reset */ vga_wgfx(cinfo->regbase, CL_GR31, 0x04); /* - " - : "end-of-reset" */ @@ -2052,7 +2005,7 @@ static int __devinit cirrusfb_register(struct fb_info *info) info->var.activate = FB_ACTIVATE_NOW; - err = cirrusfb_decode_var(&info->var, info); + err = cirrusfb_check_var(&info->var, info); if (err < 0) { /* should never happen */ dev_dbg(info->device, -- cgit v1.2.3 From 78d780e07247d52d3943b019bf9459bc9e95de1e Mon Sep 17 00:00:00 2001 From: Krzysztof Helt Date: Tue, 31 Mar 2009 15:25:10 -0700 Subject: cirrusfb: various Laguna fixes - The Laguna GD5465 (AGP) has one register more than non-AGP chips. Recognize the AGP version and write a tile control register only on the AGP version. Tested only on an AGP card. - Bump up RAMDAC frequencies after X11 code. This allow to drive a flat panel resolution 1680x1050 at 16bpp from the 4MB card. - Fix screen start address overflow bits on Laguna cards (CRT1D register). - Fix exit path in the cirrusfb_pci_register() in case of error. Signed-off-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/cirrusfb.c | 84 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 57 insertions(+), 27 deletions(-) (limited to 'drivers') diff --git a/drivers/video/cirrusfb.c b/drivers/video/cirrusfb.c index bab713b63a0c..cac4e2b0cbae 100644 --- a/drivers/video/cirrusfb.c +++ b/drivers/video/cirrusfb.c @@ -102,7 +102,8 @@ enum cirrus_board { BT_PICASSO4, /* GD5446 */ BT_ALPINE, /* GD543x/4x */ BT_GD5480, - BT_LAGUNA, /* GD546x */ + BT_LAGUNA, /* GD5462/64 */ + BT_LAGUNAB, /* GD5465 */ }; /* @@ -234,8 +235,18 @@ static const struct cirrusfb_board_info_rec { [BT_LAGUNA] = { .name = "CL Laguna", .maxclock = { - /* guess */ - 135100, 135100, 135100, 135100, 135100, + /* taken from X11 code */ + 170000, 170000, 170000, 170000, 135100, + }, + .init_sr07 = false, + .init_sr1f = false, + .scrn_start_bit19 = true, + }, + [BT_LAGUNAB] = { + .name = "CL Laguna AGP", + .maxclock = { + /* taken from X11 code */ + 170000, 250000, 170000, 170000, 135100, }, .init_sr07 = false, .init_sr1f = false, @@ -258,7 +269,7 @@ static struct pci_device_id cirrusfb_pci_table[] = { CHIP(PCI_DEVICE_ID_CIRRUS_5446, BT_PICASSO4), /* Picasso 4 is 5446 */ CHIP(PCI_DEVICE_ID_CIRRUS_5462, BT_LAGUNA), /* CL Laguna */ CHIP(PCI_DEVICE_ID_CIRRUS_5464, BT_LAGUNA), /* CL Laguna 3D */ - CHIP(PCI_DEVICE_ID_CIRRUS_5465, BT_LAGUNA), /* CL Laguna 3DA*/ + CHIP(PCI_DEVICE_ID_CIRRUS_5465, BT_LAGUNAB), /* CL Laguna 3DA*/ { 0, } }; MODULE_DEVICE_TABLE(pci, cirrusfb_pci_table); @@ -385,6 +396,11 @@ static void cirrusfb_dbg_print_regs(struct fb_info *info, /*****************************************************************************/ /*** BEGIN Interface Used by the World ***************************************/ +static inline int is_laguna(const struct cirrusfb_info *cinfo) +{ + return cinfo->btype == BT_LAGUNA || cinfo->btype == BT_LAGUNAB; +} + static int opencount; /*--- Open /dev/fbx ---------------------------------------------------------*/ @@ -814,13 +830,16 @@ static int cirrusfb_set_par_foo(struct fb_info *info) cirrusfb_set_mclk_as_source(info, divMCLK); } } - if (cinfo->btype == BT_LAGUNA) { + if (is_laguna(cinfo)) { long pcifc = fb_readl(cinfo->laguna_mmio + 0x3fc); unsigned char tile = fb_readb(cinfo->laguna_mmio + 0x407); unsigned short tile_control; - tile_control = fb_readw(cinfo->laguna_mmio + 0x2c4); - fb_writew(tile_control & ~0x80, cinfo->laguna_mmio + 0x2c4); + if (cinfo->btype == BT_LAGUNAB) { + tile_control = fb_readw(cinfo->laguna_mmio + 0x2c4); + tile_control &= ~0x80; + fb_writew(tile_control, cinfo->laguna_mmio + 0x2c4); + } fb_writel(pcifc | 0x10000000l, cinfo->laguna_mmio + 0x3fc); fb_writeb(tile & 0x3f, cinfo->laguna_mmio + 0x407); @@ -842,7 +861,7 @@ static int cirrusfb_set_par_foo(struct fb_info *info) dev_dbg(info->device, "CL_SEQR1B: %d\n", (int) tmp); /* Laguna chipset has reversed clock registers */ - if (cinfo->btype == BT_LAGUNA) { + if (is_laguna(cinfo)) { vga_wseq(regbase, CL_SEQRE, tmp); vga_wseq(regbase, CL_SEQR1E, nom); } else { @@ -873,7 +892,7 @@ static int cirrusfb_set_par_foo(struct fb_info *info) tmp |= 0x40; if (var->sync & FB_SYNC_VERT_HIGH_ACT) tmp |= 0x80; - if (cinfo->btype == BT_LAGUNA) + if (is_laguna(cinfo)) tmp |= 0xc; WGen(cinfo, VGA_MIS_W, tmp); @@ -908,6 +927,7 @@ static int cirrusfb_set_par_foo(struct fb_info *info) break; case BT_LAGUNA: + case BT_LAGUNAB: vga_wseq(regbase, CL_SEQR7, vga_rseq(regbase, CL_SEQR7) & ~0x01); break; @@ -947,6 +967,7 @@ static int cirrusfb_set_par_foo(struct fb_info *info) case BT_ALPINE: case BT_GD5480: case BT_LAGUNA: + case BT_LAGUNAB: /* do nothing */ break; @@ -991,6 +1012,7 @@ static int cirrusfb_set_par_foo(struct fb_info *info) break; case BT_LAGUNA: + case BT_LAGUNAB: vga_wseq(regbase, CL_SEQR7, vga_rseq(regbase, CL_SEQR7) | 0x01); threshold |= 0x10; @@ -1030,6 +1052,7 @@ static int cirrusfb_set_par_foo(struct fb_info *info) case BT_GD5480: case BT_LAGUNA: + case BT_LAGUNAB: /* do nothing */ break; @@ -1096,6 +1119,7 @@ static int cirrusfb_set_par_foo(struct fb_info *info) break; case BT_LAGUNA: + case BT_LAGUNAB: vga_wseq(regbase, CL_SEQR7, vga_rseq(regbase, CL_SEQR7) & ~0x01); control |= 0x2000; @@ -1166,6 +1190,7 @@ static int cirrusfb_set_par_foo(struct fb_info *info) break; case BT_LAGUNA: + case BT_LAGUNAB: vga_wseq(regbase, CL_SEQR7, vga_rseq(regbase, CL_SEQR7) & ~0x01); control |= 0x6000; @@ -1208,7 +1233,7 @@ static int cirrusfb_set_par_foo(struct fb_info *info) if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19) vga_wcrt(regbase, CL_CRT1D, (pitch >> 9) & 1); - if (cinfo->btype == BT_LAGUNA) { + if (is_laguna(cinfo)) { tmp = 0; if ((htotal + 5) & 256) tmp |= 128; @@ -1234,7 +1259,7 @@ static int cirrusfb_set_par_foo(struct fb_info *info) /* From SetOffset(): Turn on VideoEnable bit in Attribute controller */ AttrOn(cinfo); - if (cinfo->btype == BT_LAGUNA) { + if (is_laguna(cinfo)) { /* no tiles */ fb_writew(control | 0x1000, cinfo->laguna_mmio + 0x402); fb_writew(format, cinfo->laguna_mmio + 0xc0); @@ -1332,7 +1357,7 @@ static int cirrusfb_pan_display(struct fb_var_screeninfo *var, xpix = (unsigned char) ((xoffset % 4) * 2); } - if (cinfo->btype != BT_LAGUNA) + if (!is_laguna(cinfo)) cirrusfb_WaitBLT(cinfo->regbase); /* lower 8 + 8 bits of screen start address */ @@ -1353,8 +1378,11 @@ static int cirrusfb_pan_display(struct fb_var_screeninfo *var, /* construct bit 19 of screen start address */ if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19) { - tmp = vga_rcrt(cinfo->regbase, CL_CRT1D) & ~0x80; - tmp |= (base >> 12) & 0x80; + tmp = vga_rcrt(cinfo->regbase, CL_CRT1D); + if (is_laguna(cinfo)) + tmp = (tmp & ~0x18) | ((base >> 16) & 0x18); + else + tmp = (tmp & ~0x80) | ((base >> 12) & 0x80); vga_wcrt(cinfo->regbase, CL_CRT1D, tmp); } @@ -1365,7 +1393,7 @@ static int cirrusfb_pan_display(struct fb_var_screeninfo *var, if (info->var.bits_per_pixel == 1) vga_wattr(cinfo->regbase, CL_AR33, xpix); - if (cinfo->btype != BT_LAGUNA) + if (!is_laguna(cinfo)) cirrusfb_WaitBLT(cinfo->regbase); return 0; @@ -1486,6 +1514,7 @@ static void init_vgachip(struct fb_info *info) break; case BT_LAGUNA: + case BT_LAGUNAB: case BT_ALPINE: /* Nothing to do to reset the board. */ break; @@ -1530,6 +1559,7 @@ static void init_vgachip(struct fb_info *info) break; case BT_ALPINE: case BT_LAGUNA: + case BT_LAGUNAB: break; case BT_SD64: vga_wseq(cinfo->regbase, CL_SEQRF, 0xb8); @@ -1611,7 +1641,7 @@ static void init_vgachip(struct fb_info *info) /* Bit Mask: no mask at all */ vga_wgfx(cinfo->regbase, VGA_GFX_BIT_MASK, 0xff); - if (cinfo->btype == BT_ALPINE || cinfo->btype == BT_LAGUNA) + if (cinfo->btype == BT_ALPINE || is_laguna(cinfo)) /* (5434 can't have bit 3 set for bitblt) */ vga_wgfx(cinfo->regbase, CL_GRB, 0x20); else @@ -1802,7 +1832,7 @@ static void cirrusfb_imageblit(struct fb_info *info, { struct cirrusfb_info *cinfo = info->par; - if (cinfo->btype != BT_LAGUNA) + if (!is_laguna(cinfo)) cirrusfb_WaitBLT(cinfo->regbase); cfb_imageblit(info, image); } @@ -1831,7 +1861,7 @@ static unsigned int __devinit cirrusfb_get_memsize(struct fb_info *info, unsigned long mem; struct cirrusfb_info *cinfo = info->par; - if (cinfo->btype == BT_LAGUNA) { + if (is_laguna(cinfo)) { unsigned char SR14 = vga_rseq(regbase, CL_SEQR14); mem = ((SR14 & 7) + 1) << 20; @@ -1950,7 +1980,7 @@ static int __devinit cirrusfb_set_fbinfo(struct fb_info *info) | FBINFO_HWACCEL_YPAN | FBINFO_HWACCEL_FILLRECT | FBINFO_HWACCEL_COPYAREA; - if (noaccel || cinfo->btype == BT_LAGUNA) + if (noaccel || is_laguna(cinfo)) info->flags |= FBINFO_HWACCEL_DISABLED; info->fbops = &cirrusfb_ops; if (cinfo->btype == BT_GD5480) { @@ -2060,7 +2090,7 @@ static int __devinit cirrusfb_pci_register(struct pci_dev *pdev, if (!info) { printk(KERN_ERR "cirrusfb: could not allocate memory\n"); ret = -ENOMEM; - goto err_disable; + goto err_out; } cinfo = info->par; @@ -2127,10 +2157,11 @@ static int __devinit cirrusfb_pci_register(struct pci_dev *pdev, pci_set_drvdata(pdev, info); ret = cirrusfb_register(info); - if (ret) - iounmap(info->screen_base); - return ret; + if (!ret) + return 0; + pci_set_drvdata(pdev, NULL); + iounmap(info->screen_base); err_release_legacy: if (release_io_ports) release_region(0x3C0, 32); @@ -2140,10 +2171,9 @@ err_release_regions: #endif pci_release_regions(pdev); err_release_fb: - if (cinfo->laguna_mmio == NULL) + if (cinfo->laguna_mmio != NULL) iounmap(cinfo->laguna_mmio); framebuffer_release(info); -err_disable: err_out: return ret; } @@ -2439,7 +2469,7 @@ static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val) { unsigned char dummy; - if (cinfo->btype == BT_LAGUNA) + if (is_laguna(cinfo)) return; if (cinfo->btype == BT_PICASSO) { /* Klaus' hint for correct access to HDR on some boards */ @@ -2509,7 +2539,7 @@ static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned ch if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 || cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480 || - cinfo->btype == BT_LAGUNA) { + is_laguna(cinfo)) { /* but DAC data register IS, at least for Picasso II */ if (cinfo->btype == BT_PICASSO) data += 0xfff; -- cgit v1.2.3 From 8343c89c4f1aac4fced7bb6919b0bdd0c13edcdc Mon Sep 17 00:00:00 2001 From: Krzysztof Helt Date: Tue, 31 Mar 2009 15:25:11 -0700 Subject: cirrusfb: acceleration improvements - Fix color expansion for 16bpp and 32bpp modes in the cirrusfb_RectFill(). - Make a function with a common blitter code (cirrusfb_set_blitter). - Add fb_sync function to allow a higher layer synchronize with the blitter. - Kill one redundant blitter reset. Signed-off-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/cirrusfb.c | 186 +++++++++++++++++++---------------------------- 1 file changed, 75 insertions(+), 111 deletions(-) (limited to 'drivers') diff --git a/drivers/video/cirrusfb.c b/drivers/video/cirrusfb.c index cac4e2b0cbae..e4ac2f6d4d97 100644 --- a/drivers/video/cirrusfb.c +++ b/drivers/video/cirrusfb.c @@ -381,7 +381,7 @@ static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel, static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel, u_short x, u_short y, u_short width, u_short height, - u_char color, u_short line_length); + u32 color, u_short line_length); static void bestclock(long freq, int *nom, int *den, int *div); @@ -1550,9 +1550,6 @@ static void init_vgachip(struct fb_info *info) /* unlock all extension registers */ vga_wseq(cinfo->regbase, CL_SEQR6, 0x12); - /* reset blitter */ - vga_wgfx(cinfo->regbase, CL_GR31, 0x04); - switch (cinfo->btype) { case BT_GD5480: vga_wseq(cinfo->regbase, CL_SEQRF, 0x98); @@ -1747,6 +1744,17 @@ static void switch_monitor(struct cirrusfb_info *cinfo, int on) /* Linux 2.6-style accelerated functions */ /******************************************/ +static int cirrusfb_sync(struct fb_info *info) +{ + struct cirrusfb_info *cinfo = info->par; + + if (!is_laguna(cinfo)) { + while (vga_rgfx(cinfo->regbase, CL_GR31) & 0x03) + cpu_relax(); + } + return 0; +} + static void cirrusfb_fillrect(struct fb_info *info, const struct fb_fillrect *region) { @@ -1966,6 +1974,7 @@ static struct fb_ops cirrusfb_ops = { .fb_blank = cirrusfb_blank, .fb_fillrect = cirrusfb_fillrect, .fb_copyarea = cirrusfb_copyarea, + .fb_sync = cirrusfb_sync, .fb_imageblit = cirrusfb_imageblit, }; @@ -2586,7 +2595,6 @@ static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned ch /* FIXME: use interrupts instead */ static void cirrusfb_WaitBLT(u8 __iomem *regbase) { - /* now busy-wait until we're done */ while (vga_rgfx(regbase, CL_GR31) & 0x08) cpu_relax(); } @@ -2597,58 +2605,12 @@ static void cirrusfb_WaitBLT(u8 __iomem *regbase) perform accelerated "scrolling" ********************************************************************/ -static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel, - u_short curx, u_short cury, - u_short destx, u_short desty, - u_short width, u_short height, - u_short line_length) -{ - u_short nwidth, nheight; - u_long nsrc, ndest; - u_char bltmode; - - nwidth = width - 1; - nheight = height - 1; - - bltmode = 0x00; - /* if source adr < dest addr, do the Blt backwards */ - if (cury <= desty) { - if (cury == desty) { - /* if src and dest are on the same line, check x */ - if (curx < destx) - bltmode |= 0x01; - } else - bltmode |= 0x01; - } - if (!bltmode) { - /* standard case: forward blitting */ - nsrc = (cury * line_length) + curx; - ndest = (desty * line_length) + destx; - } else { - /* this means start addresses are at the end, - * counting backwards - */ - nsrc = cury * line_length + curx + - nheight * line_length + nwidth; - ndest = desty * line_length + destx + - nheight * line_length + nwidth; - } - - /* - run-down of registers to be programmed: - destination pitch - source pitch - BLT width/height - source start - destination start - BLT mode - BLT ROP - VGA_GFX_SR_VALUE / VGA_GFX_SR_ENABLE: "fill color" - start/stop - */ - - cirrusfb_WaitBLT(regbase); +static void cirrusfb_set_blitter(u8 __iomem *regbase, + u_short nwidth, u_short nheight, + u_long nsrc, u_long ndest, + u_short bltmode, u_short line_length) +{ /* pitch: set to line_length */ /* dest pitch low */ vga_wgfx(regbase, CL_GR24, line_length & 0xff); @@ -2694,56 +2656,67 @@ static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel, vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */ /* and finally: GO! */ - vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */ + vga_wgfx(regbase, CL_GR31, 0x82); /* BLT Start/status */ } /******************************************************************* - cirrusfb_RectFill() + cirrusfb_BitBLT() - perform accelerated rectangle fill + perform accelerated "scrolling" ********************************************************************/ -static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel, - u_short x, u_short y, u_short width, u_short height, - u_char color, u_short line_length) +static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel, + u_short curx, u_short cury, + u_short destx, u_short desty, + u_short width, u_short height, + u_short line_length) { - u_short nwidth, nheight; - u_long ndest; - u_char op; - - nwidth = width - 1; - nheight = height - 1; + u_short nwidth = width - 1; + u_short nheight = height - 1; + u_long nsrc, ndest; + u_char bltmode; - ndest = (y * line_length) + x; + bltmode = 0x00; + /* if source adr < dest addr, do the Blt backwards */ + if (cury <= desty) { + if (cury == desty) { + /* if src and dest are on the same line, check x */ + if (curx < destx) + bltmode |= 0x01; + } else + bltmode |= 0x01; + } + /* standard case: forward blitting */ + nsrc = (cury * line_length) + curx; + ndest = (desty * line_length) + destx; + if (bltmode) { + /* this means start addresses are at the end, + * counting backwards + */ + nsrc += nheight * line_length + nwidth; + ndest += nheight * line_length + nwidth; + } cirrusfb_WaitBLT(regbase); - /* pitch: set to line_length */ - vga_wgfx(regbase, CL_GR24, line_length & 0xff); /* dest pitch low */ - vga_wgfx(regbase, CL_GR25, line_length >> 8); /* dest pitch hi */ - vga_wgfx(regbase, CL_GR26, line_length & 0xff); /* source pitch low */ - vga_wgfx(regbase, CL_GR27, line_length >> 8); /* source pitch hi */ + cirrusfb_set_blitter(regbase, nwidth, nheight, + nsrc, ndest, bltmode, line_length); +} - /* BLT width: actual number of pixels - 1 */ - vga_wgfx(regbase, CL_GR20, nwidth & 0xff); /* BLT width low */ - vga_wgfx(regbase, CL_GR21, nwidth >> 8); /* BLT width hi */ +/******************************************************************* + cirrusfb_RectFill() - /* BLT height: actual number of lines -1 */ - vga_wgfx(regbase, CL_GR22, nheight & 0xff); /* BLT height low */ - vga_wgfx(regbase, CL_GR23, nheight >> 8); /* BLT width hi */ + perform accelerated rectangle fill +********************************************************************/ - /* BLT destination */ - /* BLT dest low */ - vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff)); - /* BLT dest mid */ - vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8)); - /* BLT dest hi */ - vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16)); +static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel, + u_short x, u_short y, u_short width, u_short height, + u32 color, u_short line_length) +{ + u_long ndest = (y * line_length) + x; + u_char op; - /* BLT source: set to 0 (is a dummy here anyway) */ - vga_wgfx(regbase, CL_GR2C, 0x00); /* BLT src low */ - vga_wgfx(regbase, CL_GR2D, 0x00); /* BLT src mid */ - vga_wgfx(regbase, CL_GR2E, 0x00); /* BLT src hi */ + cirrusfb_WaitBLT(regbase); /* This is a ColorExpand Blt, using the */ /* same color for foreground and background */ @@ -2751,29 +2724,20 @@ static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel, vga_wgfx(regbase, VGA_GFX_SR_ENABLE, color); /* background color */ op = 0xc0; - if (bits_per_pixel == 16) { - vga_wgfx(regbase, CL_GR10, color); /* foreground color */ - vga_wgfx(regbase, CL_GR11, color); /* background color */ - op = 0x50; + if (bits_per_pixel >= 16) { + vga_wgfx(regbase, CL_GR10, color >> 8); /* foreground color */ + vga_wgfx(regbase, CL_GR11, color >> 8); /* background color */ op = 0xd0; - } else if (bits_per_pixel == 32) { - vga_wgfx(regbase, CL_GR10, color); /* foreground color */ - vga_wgfx(regbase, CL_GR11, color); /* background color */ - vga_wgfx(regbase, CL_GR12, color); /* foreground color */ - vga_wgfx(regbase, CL_GR13, color); /* background color */ - vga_wgfx(regbase, CL_GR14, 0); /* foreground color */ - vga_wgfx(regbase, CL_GR15, 0); /* background color */ - op = 0x50; + } + if (bits_per_pixel == 32) { + vga_wgfx(regbase, CL_GR12, color >> 16);/* foreground color */ + vga_wgfx(regbase, CL_GR13, color >> 16);/* background color */ + vga_wgfx(regbase, CL_GR14, color >> 24);/* foreground color */ + vga_wgfx(regbase, CL_GR15, color >> 24);/* background color */ op = 0xf0; } - /* BLT mode: color expand, Enable 8x8 copy (faster?) */ - vga_wgfx(regbase, CL_GR30, op); /* BLT mode */ - - /* BLT ROP: SrcCopy */ - vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */ - - /* and finally: GO! */ - vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */ + cirrusfb_set_blitter(regbase, width - 1, height - 1, + 0, ndest, op, line_length); } /************************************************************************** -- cgit v1.2.3 From 9e848062533207130667f6eaa748549367ccbedf Mon Sep 17 00:00:00 2001 From: Krzysztof Helt Date: Tue, 31 Mar 2009 15:25:11 -0700 Subject: cirrusfb: add imageblit function Add hardware color expansion (imageblit) function. It roughly doubles scrolling speed of my Alpine card (GD5430). Signed-off-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/cirrusfb.c | 64 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 46 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/video/cirrusfb.c b/drivers/video/cirrusfb.c index e4ac2f6d4d97..a2a09b39c59a 100644 --- a/drivers/video/cirrusfb.c +++ b/drivers/video/cirrusfb.c @@ -381,7 +381,8 @@ static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel, static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel, u_short x, u_short y, u_short width, u_short height, - u32 color, u_short line_length); + u32 fg_color, u32 bg_color, + u_short line_length, u_char blitmode); static void bestclock(long freq, int *nom, int *den, int *div); @@ -1790,8 +1791,8 @@ static void cirrusfb_fillrect(struct fb_info *info, info->var.bits_per_pixel, (region->dx * m) / 8, region->dy, (region->width * m) / 8, region->height, - color, - info->fix.line_length); + color, color, + info->fix.line_length, 0x40); } static void cirrusfb_copyarea(struct fb_info *info, @@ -1840,9 +1841,33 @@ static void cirrusfb_imageblit(struct fb_info *info, { struct cirrusfb_info *cinfo = info->par; - if (!is_laguna(cinfo)) + if (info->state != FBINFO_STATE_RUNNING) + return; + if (info->flags & FBINFO_HWACCEL_DISABLED) + cfb_imageblit(info, image); + else { + unsigned size = ((image->width + 7) >> 3) * image->height; + int m = info->var.bits_per_pixel; + u32 fg, bg; + + if (info->var.bits_per_pixel == 8) { + fg = image->fg_color; + bg = image->bg_color; + } else { + fg = ((u32 *)(info->pseudo_palette))[image->fg_color]; + bg = ((u32 *)(info->pseudo_palette))[image->bg_color]; + } cirrusfb_WaitBLT(cinfo->regbase); - cfb_imageblit(info, image); + /* byte rounded scanlines */ + vga_wgfx(cinfo->regbase, CL_GR33, 0x00); + cirrusfb_RectFill(cinfo->regbase, + info->var.bits_per_pixel, + (image->dx * m) / 8, image->dy, + (image->width * m) / 8, image->height, + fg, bg, + info->fix.line_length, 0x04); + memcpy(info->screen_base, image->data, size); + } } #ifdef CONFIG_PPC_PREP @@ -1988,10 +2013,12 @@ static int __devinit cirrusfb_set_fbinfo(struct fb_info *info) | FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN | FBINFO_HWACCEL_FILLRECT + | FBINFO_HWACCEL_IMAGEBLIT | FBINFO_HWACCEL_COPYAREA; if (noaccel || is_laguna(cinfo)) info->flags |= FBINFO_HWACCEL_DISABLED; info->fbops = &cirrusfb_ops; + if (cinfo->btype == BT_GD5480) { if (var->bits_per_pixel == 16) info->screen_base += 1 * MB_; @@ -2711,7 +2738,8 @@ static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel, static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel, u_short x, u_short y, u_short width, u_short height, - u32 color, u_short line_length) + u32 fg_color, u32 bg_color, u_short line_length, + u_char blitmode) { u_long ndest = (y * line_length) + x; u_char op; @@ -2720,24 +2748,24 @@ static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel, /* This is a ColorExpand Blt, using the */ /* same color for foreground and background */ - vga_wgfx(regbase, VGA_GFX_SR_VALUE, color); /* foreground color */ - vga_wgfx(regbase, VGA_GFX_SR_ENABLE, color); /* background color */ + vga_wgfx(regbase, VGA_GFX_SR_VALUE, bg_color); + vga_wgfx(regbase, VGA_GFX_SR_ENABLE, fg_color); - op = 0xc0; + op = 0x80; if (bits_per_pixel >= 16) { - vga_wgfx(regbase, CL_GR10, color >> 8); /* foreground color */ - vga_wgfx(regbase, CL_GR11, color >> 8); /* background color */ - op = 0xd0; + vga_wgfx(regbase, CL_GR10, bg_color >> 8); + vga_wgfx(regbase, CL_GR11, fg_color >> 8); + op = 0x90; } if (bits_per_pixel == 32) { - vga_wgfx(regbase, CL_GR12, color >> 16);/* foreground color */ - vga_wgfx(regbase, CL_GR13, color >> 16);/* background color */ - vga_wgfx(regbase, CL_GR14, color >> 24);/* foreground color */ - vga_wgfx(regbase, CL_GR15, color >> 24);/* background color */ - op = 0xf0; + vga_wgfx(regbase, CL_GR12, bg_color >> 16); + vga_wgfx(regbase, CL_GR13, fg_color >> 16); + vga_wgfx(regbase, CL_GR14, bg_color >> 24); + vga_wgfx(regbase, CL_GR15, fg_color >> 24); + op = 0xb0; } cirrusfb_set_blitter(regbase, width - 1, height - 1, - 0, ndest, op, line_length); + 0, ndest, op | blitmode, line_length); } /************************************************************************** -- cgit v1.2.3 From bc5d8ac02f24d68efe8e267c96dd75c0531009ab Mon Sep 17 00:00:00 2001 From: Krzysztof Helt Date: Tue, 31 Mar 2009 15:25:12 -0700 Subject: cirrusfb: fix error paths in cirrusfb_xxx_register() Balance iomap and iounmap and alloc and free calls in case of error druing device register (probing). Signed-off-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/cirrusfb.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/video/cirrusfb.c b/drivers/video/cirrusfb.c index a2a09b39c59a..ffc514df2452 100644 --- a/drivers/video/cirrusfb.c +++ b/drivers/video/cirrusfb.c @@ -2090,8 +2090,6 @@ static int __devinit cirrusfb_register(struct fb_info *info) err_dealloc_cmap: fb_dealloc_cmap(&info->cmap); - cinfo->unmap(info); - framebuffer_release(info); return err; } @@ -2328,18 +2326,15 @@ static int __devinit cirrusfb_zorro_register(struct zorro_dev *z, zorro_set_drvdata(z, info); ret = cirrusfb_register(info); - if (ret) { - if (btype == BT_PICASSO4) { - iounmap(info->screen_base); - iounmap(cinfo->regbase - 0x600000); - } else if (board_addr > 0x01000000) - iounmap(info->screen_base); - } - return ret; + if (!ret) + return 0; + + if (btype == BT_PICASSO4 || board_addr > 0x01000000) + iounmap(info->screen_base); err_unmap_regbase: - /* Parental advisory: explicit hack */ - iounmap(cinfo->regbase - 0x600000); + if (btype == BT_PICASSO4) + iounmap(cinfo->regbase - 0x600000); err_release_region: release_region(board_addr, board_size); err_release_fb: -- cgit v1.2.3 From 527410ff7fc5d45fe41523c0ba061113dea22017 Mon Sep 17 00:00:00 2001 From: Krzysztof Helt Date: Tue, 31 Mar 2009 15:25:13 -0700 Subject: cirrusfb: GD5446 fixes Various fixes to make Cirrus GD5446 chip work. Another Cirrus chip works with the cirrusfb. The gd5446 seems very similar to Alpine chips. Signed-off-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/cirrusfb.c | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/video/cirrusfb.c b/drivers/video/cirrusfb.c index ffc514df2452..6603273f4ce5 100644 --- a/drivers/video/cirrusfb.c +++ b/drivers/video/cirrusfb.c @@ -198,9 +198,11 @@ static const struct cirrusfb_board_info_rec { .init_sr07 = true, .init_sr1f = false, .scrn_start_bit19 = true, - .sr07 = 0x20, - .sr07_1bpp = 0x20, - .sr07_8bpp = 0x21, + .sr07 = 0xA0, + .sr07_1bpp = 0xA0, + .sr07_1bpp_mux = 0xA6, + .sr07_8bpp = 0xA1, + .sr07_8bpp_mux = 0xA7, .sr1f = 0 }, [BT_ALPINE] = { @@ -213,8 +215,8 @@ static const struct cirrusfb_board_info_rec { .init_sr1f = true, .scrn_start_bit19 = true, .sr07 = 0xA0, - .sr07_1bpp = 0xA1, - .sr07_1bpp_mux = 0xA7, + .sr07_1bpp = 0xA0, + .sr07_1bpp_mux = 0xA6, .sr07_8bpp = 0xA1, .sr07_8bpp_mux = 0xA7, .sr1f = 0x1C @@ -821,7 +823,7 @@ static int cirrusfb_set_par_foo(struct fb_info *info) /* formula: VClk = (OSC * N) / (D * (1+P)) */ /* Example: VClk = (14.31818 * 91) / (23 * (1+1)) = 28.325 MHz */ - if (cinfo->btype == BT_ALPINE) { + if (cinfo->btype == BT_ALPINE || cinfo->btype == BT_PICASSO4) { /* if freq is close to mclk or mclk/2 select mclk * as clock source */ @@ -1044,9 +1046,6 @@ static int cirrusfb_set_par_foo(struct fb_info *info) /* ### INCOMPLETE!! */ vga_wseq(regbase, CL_SEQRF, 0xb8); #endif -/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */ - break; - case BT_ALPINE: /* We already set SRF and SR1F */ break; @@ -1106,10 +1105,6 @@ static int cirrusfb_set_par_foo(struct fb_info *info) break; case BT_PICASSO4: - vga_wseq(regbase, CL_SEQR7, 0x27); -/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */ - break; - case BT_ALPINE: vga_wseq(regbase, CL_SEQR7, 0xa7); break; @@ -1177,10 +1172,6 @@ static int cirrusfb_set_par_foo(struct fb_info *info) break; case BT_PICASSO4: - vga_wseq(regbase, CL_SEQR7, 0x25); -/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */ - break; - case BT_ALPINE: vga_wseq(regbase, CL_SEQR7, 0xa9); break; @@ -2678,7 +2669,7 @@ static void cirrusfb_set_blitter(u8 __iomem *regbase, vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */ /* and finally: GO! */ - vga_wgfx(regbase, CL_GR31, 0x82); /* BLT Start/status */ + vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */ } /******************************************************************* -- cgit v1.2.3 From 7cade31cabec33c396b1dfd9c2842e793c2648ef Mon Sep 17 00:00:00 2001 From: Krzysztof Helt Date: Tue, 31 Mar 2009 15:25:13 -0700 Subject: cirrusfb: use 24bpp instead of 32bpp The 32bpp is supported only on the latest Cirrus Logic chips. Use the 24bpp which is supported at least since Alpine chips (GD543x). Change 32bpp mode setting to 24bpp mode. Change acceleration as well. Signed-off-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/cirrusfb.c | 79 +++++++++++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 35 deletions(-) (limited to 'drivers') diff --git a/drivers/video/cirrusfb.c b/drivers/video/cirrusfb.c index 6603273f4ce5..65a1831ddd0d 100644 --- a/drivers/video/cirrusfb.c +++ b/drivers/video/cirrusfb.c @@ -95,10 +95,10 @@ /* board types */ enum cirrus_board { BT_NONE = 0, - BT_SD64, - BT_PICCOLO, - BT_PICASSO, - BT_SPECTRUM, + BT_SD64, /* GD5434 */ + BT_PICCOLO, /* GD5426 */ + BT_PICASSO, /* GD5426 or GD5428 */ + BT_SPECTRUM, /* GD5426 or GD5428 */ BT_PICASSO4, /* GD5446 */ BT_ALPINE, /* GD543x/4x */ BT_GD5480, @@ -488,7 +488,7 @@ static int cirrusfb_check_pixclock(const struct fb_var_screeninfo *var, * the VCLK is double the pixel clock. */ switch (var->bits_per_pixel) { case 16: - case 32: + case 24: if (var->xres <= 800) /* Xbh has this type of clock for 32-bit */ freq /= 2; @@ -535,11 +535,11 @@ static int cirrusfb_check_var(struct fb_var_screeninfo *var, var->blue.length = 5; break; - case 32: + case 24: if (isPReP) { - var->red.offset = 8; - var->green.offset = 16; - var->blue.offset = 24; + var->red.offset = 0; + var->green.offset = 8; + var->blue.offset = 16; } else { var->red.offset = 16; var->green.offset = 8; @@ -670,7 +670,7 @@ static int cirrusfb_set_par_foo(struct fb_info *info) break; case 16: - case 32: + case 24: info->fix.line_length = var->xres_virtual * var->bits_per_pixel >> 3; info->fix.visual = FB_VISUAL_TRUECOLOR; @@ -813,6 +813,9 @@ static int cirrusfb_set_par_foo(struct fb_info *info) vga_wcrt(regbase, CL_CRT1A, tmp); freq = PICOS2KHZ(var->pixclock); + if (cinfo->btype == BT_ALPINE && var->bits_per_pixel == 24) + freq *= 3; + bestclock(freq, &nom, &den, &div); dev_dbg(info->device, "VCLK freq: %ld kHz nom: %d den: %d div: %d\n", @@ -1140,16 +1143,16 @@ static int cirrusfb_set_par_foo(struct fb_info *info) /****************************************************** * - * 32 bpp + * 24 bpp * */ - else if (var->bits_per_pixel == 32) { - dev_dbg(info->device, "preparing for 32 bit deep display\n"); + else if (var->bits_per_pixel == 24) { + dev_dbg(info->device, "preparing for 24 bit deep display\n"); switch (cinfo->btype) { case BT_SD64: /* Extended Sequencer Mode: 256c col. mode */ - vga_wseq(regbase, CL_SEQR7, 0xf9); + vga_wseq(regbase, CL_SEQR7, 0xf5); /* MCLK select */ vga_wseq(regbase, CL_SEQR1F, 0x1e); break; @@ -1173,11 +1176,11 @@ static int cirrusfb_set_par_foo(struct fb_info *info) case BT_PICASSO4: case BT_ALPINE: - vga_wseq(regbase, CL_SEQR7, 0xa9); + vga_wseq(regbase, CL_SEQR7, 0xa5); break; case BT_GD5480: - vga_wseq(regbase, CL_SEQR7, 0x19); + vga_wseq(regbase, CL_SEQR7, 0x15); /* We already set SRF and SR1F */ break; @@ -1185,8 +1188,8 @@ static int cirrusfb_set_par_foo(struct fb_info *info) case BT_LAGUNAB: vga_wseq(regbase, CL_SEQR7, vga_rseq(regbase, CL_SEQR7) & ~0x01); - control |= 0x6000; - format |= 0x3400; + control |= 0x4000; + format |= 0x2400; threshold |= 0x20; break; @@ -1385,9 +1388,6 @@ static int cirrusfb_pan_display(struct fb_var_screeninfo *var, if (info->var.bits_per_pixel == 1) vga_wattr(cinfo->regbase, CL_AR33, xpix); - if (!is_laguna(cinfo)) - cirrusfb_WaitBLT(cinfo->regbase); - return 0; } @@ -1492,22 +1492,18 @@ static void init_vgachip(struct fb_info *info) /* disable flickerfixer */ vga_wcrt(cinfo->regbase, CL_CRT51, 0x00); mdelay(100); - /* from Klaus' NetBSD driver: */ - vga_wgfx(cinfo->regbase, CL_GR2F, 0x00); - /* put blitter into 542x compat */ - vga_wgfx(cinfo->regbase, CL_GR33, 0x00); /* mode */ vga_wgfx(cinfo->regbase, CL_GR31, 0x00); - break; - - case BT_GD5480: + case BT_GD5480: /* fall through */ /* from Klaus' NetBSD driver: */ vga_wgfx(cinfo->regbase, CL_GR2F, 0x00); + case BT_ALPINE: /* fall through */ + /* put blitter into 542x compat */ + vga_wgfx(cinfo->regbase, CL_GR33, 0x00); break; case BT_LAGUNA: case BT_LAGUNAB: - case BT_ALPINE: /* Nothing to do to reset the board. */ break; @@ -1831,10 +1827,13 @@ static void cirrusfb_imageblit(struct fb_info *info, const struct fb_image *image) { struct cirrusfb_info *cinfo = info->par; + unsigned char op = (info->var.bits_per_pixel == 24) ? 0xc : 0x4; if (info->state != FBINFO_STATE_RUNNING) return; - if (info->flags & FBINFO_HWACCEL_DISABLED) + /* Alpine acceleration does not work at 24bpp ?!? */ + if (info->flags & FBINFO_HWACCEL_DISABLED || image->depth != 1 || + (cinfo->btype == BT_ALPINE && op == 0xc)) cfb_imageblit(info, image); else { unsigned size = ((image->width + 7) >> 3) * image->height; @@ -1848,15 +1847,22 @@ static void cirrusfb_imageblit(struct fb_info *info, fg = ((u32 *)(info->pseudo_palette))[image->fg_color]; bg = ((u32 *)(info->pseudo_palette))[image->bg_color]; } - cirrusfb_WaitBLT(cinfo->regbase); - /* byte rounded scanlines */ - vga_wgfx(cinfo->regbase, CL_GR33, 0x00); + if (info->var.bits_per_pixel == 24) { + /* clear background first */ + cirrusfb_RectFill(cinfo->regbase, + info->var.bits_per_pixel, + (image->dx * m) / 8, image->dy, + (image->width * m) / 8, + image->height, + bg, bg, + info->fix.line_length, 0x40); + } cirrusfb_RectFill(cinfo->regbase, info->var.bits_per_pixel, (image->dx * m) / 8, image->dy, (image->width * m) / 8, image->height, fg, bg, - info->fix.line_length, 0x04); + info->fix.line_length, op); memcpy(info->screen_base, image->data, size); } } @@ -2743,9 +2749,12 @@ static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel, vga_wgfx(regbase, CL_GR11, fg_color >> 8); op = 0x90; } - if (bits_per_pixel == 32) { + if (bits_per_pixel >= 24) { vga_wgfx(regbase, CL_GR12, bg_color >> 16); vga_wgfx(regbase, CL_GR13, fg_color >> 16); + op = 0xa0; + } + if (bits_per_pixel == 32) { vga_wgfx(regbase, CL_GR14, bg_color >> 24); vga_wgfx(regbase, CL_GR15, fg_color >> 24); op = 0xb0; -- cgit v1.2.3 From dd14f71cc62dd07b588cc6de935155e6fd3911c9 Mon Sep 17 00:00:00 2001 From: Krzysztof Helt Date: Tue, 31 Mar 2009 15:25:14 -0700 Subject: cirrusfb: fix clock doubling Cirrus' Alpine and Picasso4 chips uses DAC clock doubling to achieve full range of pixclock frequencies. [akpm@linux-foundation.org: fix spelling, use usual comment layout] Signed-off-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/cirrusfb.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/video/cirrusfb.c b/drivers/video/cirrusfb.c index 65a1831ddd0d..15e2e6bfcbff 100644 --- a/drivers/video/cirrusfb.c +++ b/drivers/video/cirrusfb.c @@ -470,10 +470,25 @@ static int cirrusfb_check_pixclock(const struct fb_var_screeninfo *var, /* If the frequency is greater than we can support, we might be able * to use multiplexing for the video mode */ if (freq > maxclock) { + dev_err(info->device, + "Frequency greater than maxclock (%ld kHz)\n", + maxclock); + return -EINVAL; + } + /* + * Additional constraint: 8bpp uses DAC clock doubling to allow maximum + * pixel clock + */ + if (var->bits_per_pixel == 8) { switch (cinfo->btype) { case BT_ALPINE: + case BT_PICASSO4: + if (freq > 85500) + cinfo->multiplexing = 1; + break; case BT_GD5480: - cinfo->multiplexing = 1; + if (freq > 135100) + cinfo->multiplexing = 1; break; default: @@ -815,6 +830,8 @@ static int cirrusfb_set_par_foo(struct fb_info *info) freq = PICOS2KHZ(var->pixclock); if (cinfo->btype == BT_ALPINE && var->bits_per_pixel == 24) freq *= 3; + if (cinfo->multiplexing) + freq /= 2; bestclock(freq, &nom, &den, &div); -- cgit v1.2.3 From 614c0dc93284404be2a4d5750c79bb95f2b6c980 Mon Sep 17 00:00:00 2001 From: Krzysztof Helt Date: Tue, 31 Mar 2009 15:25:15 -0700 Subject: cirrusfb: add accelerator constant Add an accelerator constant so almost all Cirrus are recognized as accelerators by the fbset command. Signed-off-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/cirrusfb.c | 11 +++++++++-- include/linux/fb.h | 1 + 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/video/cirrusfb.c b/drivers/video/cirrusfb.c index 15e2e6bfcbff..e9a2661669eb 100644 --- a/drivers/video/cirrusfb.c +++ b/drivers/video/cirrusfb.c @@ -519,6 +519,7 @@ static int cirrusfb_check_var(struct fb_var_screeninfo *var, int yres; /* memory size in pixels */ unsigned pixels = info->screen_size * 8 / var->bits_per_pixel; + struct cirrusfb_info *cinfo = info->par; switch (var->bits_per_pixel) { case 1: @@ -627,6 +628,9 @@ static int cirrusfb_check_var(struct fb_var_screeninfo *var, if (cirrusfb_check_pixclock(var, info)) return -EINVAL; + if (!is_laguna(cinfo)) + var->accel_flags = FB_ACCELF_TEXT; + return 0; } @@ -2029,8 +2033,12 @@ static int __devinit cirrusfb_set_fbinfo(struct fb_info *info) | FBINFO_HWACCEL_FILLRECT | FBINFO_HWACCEL_IMAGEBLIT | FBINFO_HWACCEL_COPYAREA; - if (noaccel || is_laguna(cinfo)) + if (noaccel || is_laguna(cinfo)) { info->flags |= FBINFO_HWACCEL_DISABLED; + info->fix.accel = FB_ACCEL_NONE; + } else + info->fix.accel = FB_ACCEL_CIRRUS_ALPINE; + info->fbops = &cirrusfb_ops; if (cinfo->btype == BT_GD5480) { @@ -2056,7 +2064,6 @@ static int __devinit cirrusfb_set_fbinfo(struct fb_info *info) /* FIXME: map region at 0xB8000 if available, fill in here */ info->fix.mmio_len = 0; - info->fix.accel = FB_ACCEL_NONE; fb_alloc_cmap(&info->cmap, 256, 0); diff --git a/include/linux/fb.h b/include/linux/fb.h index 31527e17076b..fe7d0d7907ab 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -123,6 +123,7 @@ struct dentry; #define FB_ACCEL_TRIDENT_3DIMAGE 51 /* Trident 3DImage */ #define FB_ACCEL_TRIDENT_BLADE3D 52 /* Trident Blade3D */ #define FB_ACCEL_TRIDENT_BLADEXP 53 /* Trident BladeXP */ +#define FB_ACCEL_CIRRUS_ALPINE 53 /* Cirrus Logic 543x/544x/5480 */ #define FB_ACCEL_NEOMAGIC_NM2070 90 /* NeoMagic NM2070 */ #define FB_ACCEL_NEOMAGIC_NM2090 91 /* NeoMagic NM2090 */ #define FB_ACCEL_NEOMAGIC_NM2093 92 /* NeoMagic NM2093 */ -- cgit v1.2.3 From 8f19e15b8ad23e28add5760ed049be2359f39fe8 Mon Sep 17 00:00:00 2001 From: Krzysztof Helt Date: Tue, 31 Mar 2009 15:25:15 -0700 Subject: cirrusfb: set MCLK in one place A memory clock (MCLK) is set at various places. Move the setting into one place. Set the MCLK only for Zorro cards as the x86 cards should be initialized by BIOS. Improve handling of the GD5434 (SD64). Kill one annoying debug output "virtual offset: ...". Signed-off-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/cirrusfb.c | 70 +++++++++++------------------------------------- 1 file changed, 16 insertions(+), 54 deletions(-) (limited to 'drivers') diff --git a/drivers/video/cirrusfb.c b/drivers/video/cirrusfb.c index e9a2661669eb..620327436082 100644 --- a/drivers/video/cirrusfb.c +++ b/drivers/video/cirrusfb.c @@ -146,7 +146,7 @@ static const struct cirrusfb_board_info_rec { .sr07 = 0xF0, .sr07_1bpp = 0xF0, .sr07_8bpp = 0xF1, - .sr1f = 0x20 + .sr1f = 0x1E }, [BT_PICCOLO] = { .name = "CL Piccolo", @@ -482,6 +482,7 @@ static int cirrusfb_check_pixclock(const struct fb_var_screeninfo *var, if (var->bits_per_pixel == 8) { switch (cinfo->btype) { case BT_ALPINE: + case BT_SD64: case BT_PICASSO4: if (freq > 85500) cinfo->multiplexing = 1; @@ -492,10 +493,7 @@ static int cirrusfb_check_pixclock(const struct fb_var_screeninfo *var, break; default: - dev_err(info->device, - "Frequency greater than maxclock (%ld kHz)\n", - maxclock); - return -EINVAL; + break; } } #if 0 @@ -847,7 +845,8 @@ static int cirrusfb_set_par_foo(struct fb_info *info) /* formula: VClk = (OSC * N) / (D * (1+P)) */ /* Example: VClk = (14.31818 * 91) / (23 * (1+1)) = 28.325 MHz */ - if (cinfo->btype == BT_ALPINE || cinfo->btype == BT_PICASSO4) { + if (cinfo->btype == BT_ALPINE || cinfo->btype == BT_PICASSO4 || + cinfo->btype == BT_SD64) { /* if freq is close to mclk or mclk/2 select mclk * as clock source */ @@ -966,30 +965,19 @@ static int cirrusfb_set_par_foo(struct fb_info *info) /* Extended Sequencer Mode */ switch (cinfo->btype) { - case BT_SD64: - /* setting the SEQRF on SD64 is not necessary - * (only during init) - */ - /* MCLK select */ - vga_wseq(regbase, CL_SEQR1F, 0x1a); - break; case BT_PICCOLO: case BT_SPECTRUM: - /* ### ueberall 0x22? */ - /* ##vorher 1c MCLK select */ - vga_wseq(regbase, CL_SEQR1F, 0x22); /* evtl d0 bei 1 bit? avoid FIFO underruns..? */ vga_wseq(regbase, CL_SEQRF, 0xb0); break; case BT_PICASSO: - /* ##vorher 22 MCLK select */ - vga_wseq(regbase, CL_SEQR1F, 0x22); /* ## vorher d0 avoid FIFO underruns..? */ vga_wseq(regbase, CL_SEQRF, 0xd0); break; + case BT_SD64: case BT_PICASSO4: case BT_ALPINE: case BT_GD5480: @@ -1051,16 +1039,9 @@ static int cirrusfb_set_par_foo(struct fb_info *info) } switch (cinfo->btype) { - case BT_SD64: - /* MCLK select */ - vga_wseq(regbase, CL_SEQR1F, 0x1d); - break; - case BT_PICCOLO: case BT_PICASSO: case BT_SPECTRUM: - /* ### vorher 1c MCLK select */ - vga_wseq(regbase, CL_SEQR1F, 0x22); /* Fast Page-Mode writes */ vga_wseq(regbase, CL_SEQRF, 0xb0); break; @@ -1074,6 +1055,7 @@ static int cirrusfb_set_par_foo(struct fb_info *info) /* We already set SRF and SR1F */ break; + case BT_SD64: case BT_GD5480: case BT_LAGUNA: case BT_LAGUNAB: @@ -1104,32 +1086,23 @@ static int cirrusfb_set_par_foo(struct fb_info *info) else if (var->bits_per_pixel == 16) { dev_dbg(info->device, "preparing for 16 bit deep display\n"); switch (cinfo->btype) { - case BT_SD64: - /* Extended Sequencer Mode: 256c col. mode */ - vga_wseq(regbase, CL_SEQR7, 0xf7); - /* MCLK select */ - vga_wseq(regbase, CL_SEQR1F, 0x1e); - break; - case BT_PICCOLO: case BT_SPECTRUM: vga_wseq(regbase, CL_SEQR7, 0x87); /* Fast Page-Mode writes */ vga_wseq(regbase, CL_SEQRF, 0xb0); - /* MCLK select */ - vga_wseq(regbase, CL_SEQR1F, 0x22); break; case BT_PICASSO: vga_wseq(regbase, CL_SEQR7, 0x27); /* Fast Page-Mode writes */ vga_wseq(regbase, CL_SEQRF, 0xb0); - /* MCLK select */ - vga_wseq(regbase, CL_SEQR1F, 0x22); break; + case BT_SD64: case BT_PICASSO4: case BT_ALPINE: + /* Extended Sequencer Mode: 256c col. mode */ vga_wseq(regbase, CL_SEQR7, 0xa7); break; @@ -1171,32 +1144,23 @@ static int cirrusfb_set_par_foo(struct fb_info *info) else if (var->bits_per_pixel == 24) { dev_dbg(info->device, "preparing for 24 bit deep display\n"); switch (cinfo->btype) { - case BT_SD64: - /* Extended Sequencer Mode: 256c col. mode */ - vga_wseq(regbase, CL_SEQR7, 0xf5); - /* MCLK select */ - vga_wseq(regbase, CL_SEQR1F, 0x1e); - break; - case BT_PICCOLO: case BT_SPECTRUM: vga_wseq(regbase, CL_SEQR7, 0x85); /* Fast Page-Mode writes */ vga_wseq(regbase, CL_SEQRF, 0xb0); - /* MCLK select */ - vga_wseq(regbase, CL_SEQR1F, 0x22); break; case BT_PICASSO: vga_wseq(regbase, CL_SEQR7, 0x25); /* Fast Page-Mode writes */ vga_wseq(regbase, CL_SEQRF, 0xb0); - /* MCLK select */ - vga_wseq(regbase, CL_SEQR1F, 0x22); break; + case BT_SD64: case BT_PICASSO4: case BT_ALPINE: + /* Extended Sequencer Mode: 256c col. mode */ vga_wseq(regbase, CL_SEQR7, 0xa5); break; @@ -1353,9 +1317,6 @@ static int cirrusfb_pan_display(struct fb_var_screeninfo *var, unsigned char tmp, xpix; struct cirrusfb_info *cinfo = info->par; - dev_dbg(info->device, - "virtual offset: (%d,%d)\n", var->xoffset, var->yoffset); - /* no range checks for xoffset and yoffset, */ /* as fb_pan_display has already done this */ if (var->vmode & FB_VMODE_YWRAP) @@ -1607,10 +1568,6 @@ static void init_vgachip(struct fb_info *info) vga_wseq(cinfo->regbase, CL_SEQR18, 0x02); } - /* MCLK select etc. */ - if (bi->init_sr1f) - vga_wseq(cinfo->regbase, CL_SEQR1F, bi->sr1f); - /* Screen A preset row scan: none */ vga_wcrt(cinfo->regbase, VGA_CRTC_PRESET_ROW, 0x00); /* Text cursor start: disable text cursor */ @@ -2346,6 +2303,11 @@ static int __devinit cirrusfb_zorro_register(struct zorro_dev *z, zorro_set_drvdata(z, info); + /* MCLK select etc. */ + if (cirrusfb_board_info[btype].init_sr1f) + vga_wseq(cinfo->regbase, CL_SEQR1F, + cirrusfb_board_info[btype].sr1f); + ret = cirrusfb_register(info); if (!ret) return 0; -- cgit v1.2.3 From df3aafd57d590d6f3d95310fc3430f3a536d1e59 Mon Sep 17 00:00:00 2001 From: Krzysztof Helt Date: Tue, 31 Mar 2009 15:25:16 -0700 Subject: cirrusfb: GD5434 (aka SD64) support fixed Fix handling of the Cirrus Logic GD5434 chip. Distinguish this chip from the GD5430. It allows detecting memory size for both models correctly. Signed-off-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/cirrusfb.c | 72 +++++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 35 deletions(-) (limited to 'drivers') diff --git a/drivers/video/cirrusfb.c b/drivers/video/cirrusfb.c index 620327436082..a364e1b0dcb7 100644 --- a/drivers/video/cirrusfb.c +++ b/drivers/video/cirrusfb.c @@ -145,7 +145,9 @@ static const struct cirrusfb_board_info_rec { .scrn_start_bit19 = true, .sr07 = 0xF0, .sr07_1bpp = 0xF0, + .sr07_1bpp_mux = 0xF6, .sr07_8bpp = 0xF1, + .sr07_8bpp_mux = 0xF7, .sr1f = 0x1E }, [BT_PICCOLO] = { @@ -262,8 +264,8 @@ static const struct cirrusfb_board_info_rec { static struct pci_device_id cirrusfb_pci_table[] = { CHIP(PCI_DEVICE_ID_CIRRUS_5436, BT_ALPINE), - CHIP(PCI_DEVICE_ID_CIRRUS_5434_8, BT_ALPINE), - CHIP(PCI_DEVICE_ID_CIRRUS_5434_4, BT_ALPINE), + CHIP(PCI_DEVICE_ID_CIRRUS_5434_8, BT_SD64), + CHIP(PCI_DEVICE_ID_CIRRUS_5434_4, BT_SD64), CHIP(PCI_DEVICE_ID_CIRRUS_5430, BT_ALPINE), /* GD-5440 is same id */ CHIP(PCI_DEVICE_ID_CIRRUS_7543, BT_ALPINE), CHIP(PCI_DEVICE_ID_CIRRUS_7548, BT_ALPINE), @@ -341,6 +343,7 @@ struct cirrusfb_info { unsigned char SFR; /* Shadow of special function register */ int multiplexing; + int doubleVCLK; int blank_mode; u32 pseudo_palette[16]; @@ -496,18 +499,15 @@ static int cirrusfb_check_pixclock(const struct fb_var_screeninfo *var, break; } } -#if 0 - /* TODO: If we have a 1MB 5434, we need to put ourselves in a mode where + + /* If we have a 1MB 5434, we need to put ourselves in a mode where * the VCLK is double the pixel clock. */ - switch (var->bits_per_pixel) { - case 16: - case 24: - if (var->xres <= 800) - /* Xbh has this type of clock for 32-bit */ - freq /= 2; - break; + cinfo->doubleVCLK = 0; + if (cinfo->btype == BT_SD64 && info->fix.smem_len <= MB_ && + var->bits_per_pixel == 16) { + cinfo->doubleVCLK = 1; } -#endif + return 0; } @@ -830,10 +830,13 @@ static int cirrusfb_set_par_foo(struct fb_info *info) vga_wcrt(regbase, CL_CRT1A, tmp); freq = PICOS2KHZ(var->pixclock); - if (cinfo->btype == BT_ALPINE && var->bits_per_pixel == 24) - freq *= 3; + if (var->bits_per_pixel == 24) + if (cinfo->btype == BT_ALPINE || cinfo->btype == BT_SD64) + freq *= 3; if (cinfo->multiplexing) freq /= 2; + if (cinfo->doubleVCLK) + freq *= 2; bestclock(freq, &nom, &den, &div); @@ -851,10 +854,9 @@ static int cirrusfb_set_par_foo(struct fb_info *info) * as clock source */ int divMCLK = cirrusfb_check_mclk(info, freq); - if (divMCLK) { + if (divMCLK) nom = 0; - cirrusfb_set_mclk_as_source(info, divMCLK); - } + cirrusfb_set_mclk_as_source(info, divMCLK); } if (is_laguna(cinfo)) { long pcifc = fb_readl(cinfo->laguna_mmio + 0x3fc); @@ -885,14 +887,13 @@ static int cirrusfb_set_par_foo(struct fb_info *info) (cinfo->btype == BT_GD5480)) tmp |= 0x80; - dev_dbg(info->device, "CL_SEQR1B: %d\n", (int) tmp); /* Laguna chipset has reversed clock registers */ if (is_laguna(cinfo)) { vga_wseq(regbase, CL_SEQRE, tmp); vga_wseq(regbase, CL_SEQR1E, nom); } else { - vga_wseq(regbase, CL_SEQRB, nom); - vga_wseq(regbase, CL_SEQR1B, tmp); + vga_wseq(regbase, CL_SEQRE, nom); + vga_wseq(regbase, CL_SEQR1E, tmp); } } @@ -911,15 +912,13 @@ static int cirrusfb_set_par_foo(struct fb_info *info) else vga_wcrt(regbase, VGA_CRTC_REGS, 0x00); /* interlace control */ - /* adjust horizontal/vertical sync type (low/high) */ + /* adjust horizontal/vertical sync type (low/high), use VCLK3 */ /* enable display memory & CRTC I/O address for color mode */ - tmp = 0x03; + tmp = 0x03 | 0xc; if (var->sync & FB_SYNC_HOR_HIGH_ACT) tmp |= 0x40; if (var->sync & FB_SYNC_VERT_HIGH_ACT) tmp |= 0x80; - if (is_laguna(cinfo)) - tmp |= 0xc; WGen(cinfo, VGA_MIS_W, tmp); /* text cursor on and start line */ @@ -1052,9 +1051,6 @@ static int cirrusfb_set_par_foo(struct fb_info *info) vga_wseq(regbase, CL_SEQRF, 0xb8); #endif case BT_ALPINE: - /* We already set SRF and SR1F */ - break; - case BT_SD64: case BT_GD5480: case BT_LAGUNA: @@ -1103,7 +1099,8 @@ static int cirrusfb_set_par_foo(struct fb_info *info) case BT_PICASSO4: case BT_ALPINE: /* Extended Sequencer Mode: 256c col. mode */ - vga_wseq(regbase, CL_SEQR7, 0xa7); + vga_wseq(regbase, CL_SEQR7, + cinfo->doubleVCLK ? 0xa3 : 0xa7); break; case BT_GD5480: @@ -1128,7 +1125,7 @@ static int cirrusfb_set_par_foo(struct fb_info *info) /* mode register: 256 color mode */ vga_wgfx(regbase, VGA_GFX_MODE, 64); #ifdef CONFIG_PCI - WHDR(cinfo, 0xc1); /* Copy Xbh */ + WHDR(cinfo, cinfo->doubleVCLK ? 0xe1 : 0xc1); #elif defined(CONFIG_ZORRO) /* FIXME: CONFIG_PCI and CONFIG_ZORRO may be defined both */ WHDR(cinfo, 0xa0); /* hidden dac reg: nothing special */ @@ -1529,7 +1526,9 @@ static void init_vgachip(struct fb_info *info) case BT_LAGUNAB: break; case BT_SD64: +#ifdef CONFIG_ZORRO vga_wseq(cinfo->regbase, CL_SEQRF, 0xb8); +#endif break; default: vga_wseq(cinfo->regbase, CL_SEQR16, 0x0f); @@ -1604,7 +1603,8 @@ static void init_vgachip(struct fb_info *info) /* Bit Mask: no mask at all */ vga_wgfx(cinfo->regbase, VGA_GFX_BIT_MASK, 0xff); - if (cinfo->btype == BT_ALPINE || is_laguna(cinfo)) + if (cinfo->btype == BT_ALPINE || cinfo->btype == BT_SD64 || + is_laguna(cinfo)) /* (5434 can't have bit 3 set for bitblt) */ vga_wgfx(cinfo->regbase, CL_GRB, 0x20); else @@ -1809,9 +1809,11 @@ static void cirrusfb_imageblit(struct fb_info *info, if (info->state != FBINFO_STATE_RUNNING) return; - /* Alpine acceleration does not work at 24bpp ?!? */ - if (info->flags & FBINFO_HWACCEL_DISABLED || image->depth != 1 || - (cinfo->btype == BT_ALPINE && op == 0xc)) + /* Alpine/SD64 does not work at 24bpp ??? */ + if (info->flags & FBINFO_HWACCEL_DISABLED || image->depth != 1) + cfb_imageblit(info, image); + else if ((cinfo->btype == BT_ALPINE || cinfo->btype == BT_SD64) && + op == 0xc) cfb_imageblit(info, image); else { unsigned size = ((image->width + 7) >> 3) * image->height; @@ -1895,7 +1897,7 @@ static unsigned int __devinit cirrusfb_get_memsize(struct fb_info *info, /* If DRAM bank switching is enabled, there must be * twice as much memory installed. (4MB on the 5434) */ - if (SRF & 0x80) + if (cinfo->btype != BT_ALPINE && (SRF & 0x80) != 0) mem *= 2; } @@ -2553,7 +2555,7 @@ static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned ch if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 || cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480 || - is_laguna(cinfo)) { + cinfo->btype == BT_SD64 || is_laguna(cinfo)) { /* but DAC data register IS, at least for Picasso II */ if (cinfo->btype == BT_PICASSO) data += 0xfff; -- cgit v1.2.3 From 4242a23c9e6b8e2462bb49bf78b76bfdf32158b5 Mon Sep 17 00:00:00 2001 From: Krzysztof Helt Date: Tue, 31 Mar 2009 15:25:17 -0700 Subject: cirrusfb: fix threshold register mask for Laguna chips Fix threshold register mask for Laguna chips otherwise some 8bpp modes are garbled after selecting a 24bpp mode. Signed-off-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/cirrusfb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/video/cirrusfb.c b/drivers/video/cirrusfb.c index a364e1b0dcb7..9bb811d56721 100644 --- a/drivers/video/cirrusfb.c +++ b/drivers/video/cirrusfb.c @@ -875,7 +875,7 @@ static int cirrusfb_set_par_foo(struct fb_info *info) threshold = fb_readw(cinfo->laguna_mmio + 0xea); control &= ~0x6800; format = 0; - threshold &= 0xffe0 & 0x3fbf; + threshold &= 0xffc0 & 0x3fbf; } if (nom) { tmp = den << 1; -- cgit v1.2.3 From 8636a9240cc93efa6b36f4cfe6253e0574f832c6 Mon Sep 17 00:00:00 2001 From: Krzysztof Helt Date: Tue, 31 Mar 2009 15:25:17 -0700 Subject: cirrusfb: fix interlaced modes Fix calculations of timings for interlaced modes. Signed-off-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/cirrusfb.c | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/video/cirrusfb.c b/drivers/video/cirrusfb.c index 9bb811d56721..d42e385f091c 100644 --- a/drivers/video/cirrusfb.c +++ b/drivers/video/cirrusfb.c @@ -701,45 +701,52 @@ static int cirrusfb_set_par_foo(struct fb_info *info) hsyncstart = var->xres + var->right_margin; hsyncend = hsyncstart + var->hsync_len; - htotal = (hsyncend + var->left_margin) / 8 - 5; - hdispend = var->xres / 8 - 1; - hsyncstart = hsyncstart / 8 + 1; - hsyncend = hsyncend / 8 + 1; + htotal = (hsyncend + var->left_margin) / 8; + hdispend = var->xres / 8; + hsyncstart = hsyncstart / 8; + hsyncend = hsyncend / 8; - yres = var->yres; - vsyncstart = yres + var->lower_margin; + vdispend = var->yres; + vsyncstart = vdispend + var->lower_margin; vsyncend = vsyncstart + var->vsync_len; vtotal = vsyncend + var->upper_margin; - vdispend = yres - 1; if (var->vmode & FB_VMODE_DOUBLE) { - yres *= 2; + vdispend *= 2; vsyncstart *= 2; vsyncend *= 2; vtotal *= 2; } else if (var->vmode & FB_VMODE_INTERLACED) { - yres = (yres + 1) / 2; + vdispend = (vdispend + 1) / 2; vsyncstart = (vsyncstart + 1) / 2; vsyncend = (vsyncend + 1) / 2; vtotal = (vtotal + 1) / 2; } - - vtotal -= 2; - vsyncstart -= 1; - vsyncend -= 1; - + yres = vdispend; if (yres >= 1024) { vtotal /= 2; vsyncstart /= 2; vsyncend /= 2; vdispend /= 2; } + + vdispend -= 1; + vsyncstart -= 1; + vsyncend -= 1; + vtotal -= 2; + if (cinfo->multiplexing) { htotal /= 2; hsyncstart /= 2; hsyncend /= 2; hdispend /= 2; } + + htotal -= 5; + hdispend -= 1; + hsyncstart += 1; + hsyncend += 1; + /* unlock register VGA_CRTC_H_TOTAL..CRT7 */ vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20); /* previously: 0x00) */ -- cgit v1.2.3 From 66c1ca019078220dc1bf968f2bb18421100ef147 Mon Sep 17 00:00:00 2001 From: Andrea Righi Date: Tue, 31 Mar 2009 15:25:18 -0700 Subject: fbmem: fix fb_info->lock and mm->mmap_sem circular locking dependency Fix a circular locking dependency in the frame buffer console driver pushing down the mutex fb_info->lock. Circular locking dependecies occur calling the blocking fb_notifier_call_chain() with fb_info->lock held. Notifier callbacks can try to acquire mm->mmap_sem, while fb_mmap() acquires the locks in the reverse order mm->mmap_sem => fb_info->lock. Tested-by: Andrey Borzenkov Signed-off-by: Andrea Righi Cc: Geert Uytterhoeven Cc: Krzysztof Helt Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/backlight/backlight.c | 3 ++ drivers/video/backlight/lcd.c | 3 ++ drivers/video/console/fbcon.c | 73 ++++++++++++++++++++++++++++++++----- drivers/video/fbmem.c | 11 +----- 4 files changed, 70 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/video/backlight/backlight.c b/drivers/video/backlight/backlight.c index 157057c79ca3..dd37cbcaf8ce 100644 --- a/drivers/video/backlight/backlight.c +++ b/drivers/video/backlight/backlight.c @@ -35,6 +35,8 @@ static int fb_notifier_callback(struct notifier_block *self, return 0; bd = container_of(self, struct backlight_device, fb_notif); + if (!lock_fb_info(evdata->info)) + return -ENODEV; mutex_lock(&bd->ops_lock); if (bd->ops) if (!bd->ops->check_fb || @@ -47,6 +49,7 @@ static int fb_notifier_callback(struct notifier_block *self, backlight_update_status(bd); } mutex_unlock(&bd->ops_lock); + unlock_fb_info(evdata->info); return 0; } diff --git a/drivers/video/backlight/lcd.c b/drivers/video/backlight/lcd.c index b6449470106c..0bb13df0fa89 100644 --- a/drivers/video/backlight/lcd.c +++ b/drivers/video/backlight/lcd.c @@ -40,6 +40,8 @@ static int fb_notifier_callback(struct notifier_block *self, if (!ld->ops) return 0; + if (!lock_fb_info(evdata->info)) + return -ENODEV; mutex_lock(&ld->ops_lock); if (!ld->ops->check_fb || ld->ops->check_fb(ld, evdata->info)) { if (event == FB_EVENT_BLANK) { @@ -51,6 +53,7 @@ static int fb_notifier_callback(struct notifier_block *self, } } mutex_unlock(&ld->ops_lock); + unlock_fb_info(evdata->info); return 0; } diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index 1657b9608b04..2cd500a304f2 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -2954,8 +2954,11 @@ static int fbcon_fb_unbind(int idx) static int fbcon_fb_unregistered(struct fb_info *info) { - int i, idx = info->node; + int i, idx; + if (!lock_fb_info(info)) + return -ENODEV; + idx = info->node; for (i = first_fb_vc; i <= last_fb_vc; i++) { if (con2fb_map[i] == idx) con2fb_map[i] = -1; @@ -2979,13 +2982,14 @@ static int fbcon_fb_unregistered(struct fb_info *info) } } - if (!num_registered_fb) - unregister_con_driver(&fb_con); - - if (primary_device == idx) primary_device = -1; + unlock_fb_info(info); + + if (!num_registered_fb) + unregister_con_driver(&fb_con); + return 0; } @@ -3021,9 +3025,13 @@ static inline void fbcon_select_primary(struct fb_info *info) static int fbcon_fb_registered(struct fb_info *info) { - int ret = 0, i, idx = info->node; + int ret = 0, i, idx; + if (!lock_fb_info(info)) + return -ENODEV; + idx = info->node; fbcon_select_primary(info); + unlock_fb_info(info); if (info_idx == -1) { for (i = first_fb_vc; i <= last_fb_vc; i++) { @@ -3124,7 +3132,7 @@ static void fbcon_get_requirement(struct fb_info *info, } } -static int fbcon_event_notify(struct notifier_block *self, +static int fbcon_event_notify(struct notifier_block *self, unsigned long action, void *data) { struct fb_event *event = data; @@ -3132,7 +3140,7 @@ static int fbcon_event_notify(struct notifier_block *self, struct fb_videomode *mode; struct fb_con2fbmap *con2fb; struct fb_blit_caps *caps; - int ret = 0; + int idx, ret = 0; /* * ignore all events except driver registration and deregistration @@ -3144,23 +3152,54 @@ static int fbcon_event_notify(struct notifier_block *self, switch(action) { case FB_EVENT_SUSPEND: + if (!lock_fb_info(info)) { + ret = -ENODEV; + goto done; + } fbcon_suspended(info); + unlock_fb_info(info); break; case FB_EVENT_RESUME: + if (!lock_fb_info(info)) { + ret = -ENODEV; + goto done; + } fbcon_resumed(info); + unlock_fb_info(info); break; case FB_EVENT_MODE_CHANGE: + if (!lock_fb_info(info)) { + ret = -ENODEV; + goto done; + } fbcon_modechanged(info); + unlock_fb_info(info); break; case FB_EVENT_MODE_CHANGE_ALL: + if (!lock_fb_info(info)) { + ret = -ENODEV; + goto done; + } fbcon_set_all_vcs(info); + unlock_fb_info(info); break; case FB_EVENT_MODE_DELETE: mode = event->data; + if (!lock_fb_info(info)) { + ret = -ENODEV; + goto done; + } ret = fbcon_mode_deleted(info, mode); + unlock_fb_info(info); break; case FB_EVENT_FB_UNBIND: - ret = fbcon_fb_unbind(info->node); + if (!lock_fb_info(info)) { + ret = -ENODEV; + goto done; + } + idx = info->node; + unlock_fb_info(info); + ret = fbcon_fb_unbind(idx); break; case FB_EVENT_FB_REGISTERED: ret = fbcon_fb_registered(info); @@ -3178,17 +3217,31 @@ static int fbcon_event_notify(struct notifier_block *self, con2fb->framebuffer = con2fb_map[con2fb->console - 1]; break; case FB_EVENT_BLANK: + if (!lock_fb_info(info)) { + ret = -ENODEV; + goto done; + } fbcon_fb_blanked(info, *(int *)event->data); + unlock_fb_info(info); break; case FB_EVENT_NEW_MODELIST: + if (!lock_fb_info(info)) { + ret = -ENODEV; + goto done; + } fbcon_new_modelist(info); + unlock_fb_info(info); break; case FB_EVENT_GET_REQ: caps = event->data; + if (!lock_fb_info(info)) { + ret = -ENODEV; + goto done; + } fbcon_get_requirement(info, caps); + unlock_fb_info(info); break; } - done: return ret; } diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index cfd9dce1ce0b..b64f061dd447 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -1086,13 +1086,8 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, return -EINVAL; con2fb.framebuffer = -1; event.data = &con2fb; - - if (!lock_fb_info(info)) - return -ENODEV; event.info = info; fb_notifier_call_chain(FB_EVENT_GET_CONSOLE_MAP, &event); - unlock_fb_info(info); - ret = copy_to_user(argp, &con2fb, sizeof(con2fb)) ? -EFAULT : 0; break; case FBIOPUT_CON2FBMAP: @@ -1109,12 +1104,8 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, break; } event.data = &con2fb; - if (!lock_fb_info(info)) - return -ENODEV; event.info = info; - ret = fb_notifier_call_chain(FB_EVENT_SET_CONSOLE_MAP, - &event); - unlock_fb_info(info); + ret = fb_notifier_call_chain(FB_EVENT_SET_CONSOLE_MAP, &event); break; case FBIOBLANK: if (!lock_fb_info(info)) -- cgit v1.2.3 From 6a7f2829b5f8be124e168265f176dbbbea8861a0 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Tue, 31 Mar 2009 15:25:19 -0700 Subject: fbdev: uninline lock_fb_info() Before: text data bss dec hex filename 3648 2910 32 6590 19be drivers/video/backlight/backlight.o 3226 2812 32 6070 17b6 drivers/video/backlight/lcd.o 30990 16688 8480 56158 db5e drivers/video/console/fbcon.o 15488 8400 24 23912 5d68 drivers/video/fbmem.o After: text data bss dec hex filename 3537 2870 32 6439 1927 drivers/video/backlight/backlight.o 3131 2772 32 5935 172f drivers/video/backlight/lcd.o 30876 16648 8480 56004 dac4 drivers/video/console/fbcon.o 15506 8400 24 23930 5d7a drivers/video/fbmem.o Cc: Andrea Righi Cc: Geert Uytterhoeven Cc: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/fbmem.c | 11 +++++++++++ include/linux/fb.h | 10 +--------- 2 files changed, 12 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index b64f061dd447..2ac32e6b5953 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -46,6 +46,17 @@ struct fb_info *registered_fb[FB_MAX] __read_mostly; int num_registered_fb __read_mostly; +int lock_fb_info(struct fb_info *info) +{ + mutex_lock(&info->lock); + if (!info->fbops) { + mutex_unlock(&info->lock); + return 0; + } + return 1; +} +EXPORT_SYMBOL(lock_fb_info); + /* * Helpers */ diff --git a/include/linux/fb.h b/include/linux/fb.h index fe7d0d7907ab..f563c5013932 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -961,15 +961,7 @@ extern struct fb_info *registered_fb[FB_MAX]; extern int num_registered_fb; extern struct class *fb_class; -static inline int lock_fb_info(struct fb_info *info) -{ - mutex_lock(&info->lock); - if (!info->fbops) { - mutex_unlock(&info->lock); - return 0; - } - return 1; -} +extern int lock_fb_info(struct fb_info *info); static inline void unlock_fb_info(struct fb_info *info) { -- cgit v1.2.3 From d4bc4e8af0a4a34c713f8c1a33a78cedffe8e0b7 Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Tue, 31 Mar 2009 15:25:20 -0700 Subject: drivers/video/sgivwfb.c: fix memory leaks in removal path We were leaking both the cmap memory and the info struct memory. Signed-off-by: Andres Salomon Acked-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/sgivwfb.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/video/sgivwfb.c b/drivers/video/sgivwfb.c index f5252c2552fd..bba53714a7b1 100644 --- a/drivers/video/sgivwfb.c +++ b/drivers/video/sgivwfb.c @@ -837,6 +837,8 @@ static int sgivwfb_remove(struct platform_device *dev) iounmap(par->regs); iounmap(info->screen_base); release_mem_region(DBE_REG_PHYS, DBE_REG_SIZE); + fb_dealloc_cmap(&info->cmap); + framebuffer_release(info); } return 0; } -- cgit v1.2.3 From 895d72279da7f24f266f9583c239e7b22230127c Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Tue, 31 Mar 2009 15:25:21 -0700 Subject: tdfxfb: fix memory leaks in removal path We were leaking the cmap memory. Signed-off-by: Andres Salomon Acked-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/tdfxfb.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/video/tdfxfb.c b/drivers/video/tdfxfb.c index 14bd3f3680b8..ee64771fbe3d 100644 --- a/drivers/video/tdfxfb.c +++ b/drivers/video/tdfxfb.c @@ -1393,6 +1393,7 @@ static void __devexit tdfxfb_remove(struct pci_dev *pdev) release_mem_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); pci_set_drvdata(pdev, NULL); + fb_dealloc_cmap(&info->cmap); framebuffer_release(info); } -- cgit v1.2.3 From 07b39b49b402355a7172c113102a8b68aafb17dd Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Tue, 31 Mar 2009 15:25:22 -0700 Subject: tridentfb: fix memory leaks in removal path We were leaking the cmap memory. Signed-off-by: Andres Salomon Acked-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/tridentfb.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/video/tridentfb.c b/drivers/video/tridentfb.c index 479b2e79ad68..c5369ba9485a 100644 --- a/drivers/video/tridentfb.c +++ b/drivers/video/tridentfb.c @@ -1563,6 +1563,7 @@ static void __devexit trident_pci_remove(struct pci_dev *dev) release_mem_region(tridentfb_fix.mmio_start, tridentfb_fix.mmio_len); pci_set_drvdata(dev, NULL); kfree(info->pixmap.addr); + fb_dealloc_cmap(&info->cmap); framebuffer_release(info); } -- cgit v1.2.3 From 5e266e2e0e19532c1b8e2e2bff1eb6ccf42e478a Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Tue, 31 Mar 2009 15:25:22 -0700 Subject: vfb: fix memory leaks in removal path We were leaking the cmap memory. Signed-off-by: Andres Salomon Acked-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/vfb.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/video/vfb.c b/drivers/video/vfb.c index 93fe08d6c78f..cc919ae46571 100644 --- a/drivers/video/vfb.c +++ b/drivers/video/vfb.c @@ -543,6 +543,7 @@ static int vfb_remove(struct platform_device *dev) if (info) { unregister_framebuffer(info); rvfree(videomemory, videomemorysize); + fb_dealloc_cmap(&info->cmap); framebuffer_release(info); } return 0; -- cgit v1.2.3 From 0fd853118dd821de59106c5b9a0a2a6f488bc4b5 Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Tue, 31 Mar 2009 15:25:23 -0700 Subject: skeletonfb: check fb_alloc_cmap return value and handle failure properly Bad example code, no cookie! Signed-off-by: Andres Salomon Cc: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/skeletonfb.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/video/skeletonfb.c b/drivers/video/skeletonfb.c index df5336561d13..a439159204a8 100644 --- a/drivers/video/skeletonfb.c +++ b/drivers/video/skeletonfb.c @@ -795,8 +795,9 @@ static int __devinit xxxfb_probe(struct pci_dev *dev, if (!retval || retval == 4) return -EINVAL; - /* This has to been done !!! */ - fb_alloc_cmap(&info->cmap, cmap_len, 0); + /* This has to be done! */ + if (fb_alloc_cmap(&info->cmap, cmap_len, 0)) + return -ENOMEM; /* * The following is done in the case of having hardware with a static @@ -820,8 +821,10 @@ static int __devinit xxxfb_probe(struct pci_dev *dev, */ /* xxxfb_set_par(info); */ - if (register_framebuffer(info) < 0) + if (register_framebuffer(info) < 0) { + fb_dealloc_cmap(&info->cmap); return -EINVAL; + } printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, info->fix.id); pci_set_drvdata(dev, info); /* or platform_set_drvdata(pdev, info) */ -- cgit v1.2.3 From 0a5d924e5954e81a905907512f8c7a1cbf81d700 Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Tue, 31 Mar 2009 15:25:24 -0700 Subject: sm501fb: check fb_alloc_cmap return value and handle failure properly Signed-off-by: Andres Salomon Acked-by: Krzysztof Helt Cc: Ben Dooks Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/sm501fb.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/video/sm501fb.c b/drivers/video/sm501fb.c index dcd98793d568..eb5d73a06702 100644 --- a/drivers/video/sm501fb.c +++ b/drivers/video/sm501fb.c @@ -1525,7 +1525,10 @@ static int sm501fb_init_fb(struct fb_info *fb, } /* initialise and set the palette */ - fb_alloc_cmap(&fb->cmap, NR_PALETTE, 0); + if (fb_alloc_cmap(&fb->cmap, NR_PALETTE, 0)) { + dev_err(info->dev, "failed to allocate cmap memory\n"); + return -ENOMEM; + } fb_set_cmap(&fb->cmap, fb); ret = (fb->fbops->fb_check_var)(&fb->var, fb); -- cgit v1.2.3 From c23124277e58998703278c26c53b159cea0f9643 Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Tue, 31 Mar 2009 15:25:25 -0700 Subject: sstfb: check fb_alloc_cmap return value and handle failure properly Signed-off-by: Andres Salomon Acked-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/sstfb.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/video/sstfb.c b/drivers/video/sstfb.c index 5b11a00f49bc..609d0a521ca2 100644 --- a/drivers/video/sstfb.c +++ b/drivers/video/sstfb.c @@ -1421,13 +1421,16 @@ static int __devinit sstfb_probe(struct pci_dev *pdev, goto fail; } - fb_alloc_cmap(&info->cmap, 256, 0); + if (fb_alloc_cmap(&info->cmap, 256, 0)) { + printk(KERN_ERR "sstfb: can't alloc cmap memory.\n"); + goto fail; + } /* register fb */ info->device = &pdev->dev; if (register_framebuffer(info) < 0) { printk(KERN_ERR "sstfb: can't register framebuffer.\n"); - goto fail; + goto fail_register; } sstfb_clear_screen(info); @@ -1441,8 +1444,9 @@ static int __devinit sstfb_probe(struct pci_dev *pdev, return 0; -fail: +fail_register: fb_dealloc_cmap(&info->cmap); +fail: iounmap(info->screen_base); fail_fb_remap: iounmap(par->mmio_vbase); -- cgit v1.2.3 From 175b39fb7e145e1aa06f6369c1fbea16873dee9e Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Tue, 31 Mar 2009 15:25:26 -0700 Subject: stifb: check fb_alloc_cmap return value and handle failure properly Signed-off-by: Andres Salomon Acked-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/stifb.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/video/stifb.c b/drivers/video/stifb.c index 166481402412..eabaad765aeb 100644 --- a/drivers/video/stifb.c +++ b/drivers/video/stifb.c @@ -1262,24 +1262,25 @@ static int __init stifb_init_fb(struct sti_struct *sti, int bpp_pref) info->flags = FBINFO_DEFAULT; info->pseudo_palette = &fb->pseudo_palette; - /* This has to been done !!! */ - fb_alloc_cmap(&info->cmap, NR_PALETTE, 0); + /* This has to be done !!! */ + if (fb_alloc_cmap(&info->cmap, NR_PALETTE, 0)) + goto out_err1; stifb_init_display(fb); if (!request_mem_region(fix->smem_start, fix->smem_len, "stifb fb")) { printk(KERN_ERR "stifb: cannot reserve fb region 0x%04lx-0x%04lx\n", fix->smem_start, fix->smem_start+fix->smem_len); - goto out_err1; + goto out_err2; } if (!request_mem_region(fix->mmio_start, fix->mmio_len, "stifb mmio")) { printk(KERN_ERR "stifb: cannot reserve sti mmio region 0x%04lx-0x%04lx\n", fix->mmio_start, fix->mmio_start+fix->mmio_len); - goto out_err2; + goto out_err3; } if (register_framebuffer(&fb->info) < 0) - goto out_err3; + goto out_err4; sti->info = info; /* save for unregister_framebuffer() */ @@ -1297,13 +1298,14 @@ static int __init stifb_init_fb(struct sti_struct *sti, int bpp_pref) return 0; -out_err3: +out_err4: release_mem_region(fix->mmio_start, fix->mmio_len); -out_err2: +out_err3: release_mem_region(fix->smem_start, fix->smem_len); +out_err2: + fb_dealloc_cmap(&info->cmap); out_err1: iounmap(info->screen_base); - fb_dealloc_cmap(&info->cmap); out_err0: kfree(fb); return -ENXIO; -- cgit v1.2.3 From ccb121e6958eca5f58938e56523fc589fed36fa8 Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Tue, 31 Mar 2009 15:25:26 -0700 Subject: valkyriefb: check fb_alloc_cmap return value and handle failure properly Signed-off-by: Andres Salomon Acked-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/valkyriefb.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/video/valkyriefb.c b/drivers/video/valkyriefb.c index 7b0cef9ca8f9..4bb9a0b18950 100644 --- a/drivers/video/valkyriefb.c +++ b/drivers/video/valkyriefb.c @@ -119,7 +119,7 @@ static void set_valkyrie_clock(unsigned char *params); static int valkyrie_var_to_par(struct fb_var_screeninfo *var, struct fb_par_valkyrie *par, const struct fb_info *fb_info); -static void valkyrie_init_info(struct fb_info *info, struct fb_info_valkyrie *p); +static int valkyrie_init_info(struct fb_info *info, struct fb_info_valkyrie *p); static void valkyrie_par_to_fix(struct fb_par_valkyrie *par, struct fb_fix_screeninfo *fix); static void valkyrie_init_fix(struct fb_fix_screeninfo *fix, struct fb_info_valkyrie *p); @@ -381,18 +381,22 @@ int __init valkyriefb_init(void) valkyrie_choose_mode(p); mac_vmode_to_var(default_vmode, default_cmode, &p->info.var); - valkyrie_init_info(&p->info, p); + err = valkyrie_init_info(&p->info, p); + if (err < 0) + goto out_free; valkyrie_init_fix(&p->info.fix, p); if (valkyriefb_set_par(&p->info)) /* "can't happen" */ printk(KERN_ERR "valkyriefb: can't set default video mode\n"); if ((err = register_framebuffer(&p->info)) != 0) - goto out_free; + goto out_cmap_free; printk(KERN_INFO "fb%d: valkyrie frame buffer device\n", p->info.node); return 0; + out_cmap_free: + fb_dealloc_cmap(&p->info.cmap); out_free: if (p->frame_buffer) iounmap(p->frame_buffer); @@ -538,14 +542,15 @@ static void valkyrie_par_to_fix(struct fb_par_valkyrie *par, /* ywrapstep, xpanstep, ypanstep */ } -static void __init valkyrie_init_info(struct fb_info *info, struct fb_info_valkyrie *p) +static int __init valkyrie_init_info(struct fb_info *info, + struct fb_info_valkyrie *p) { info->fbops = &valkyriefb_ops; info->screen_base = p->frame_buffer + 0x1000; info->flags = FBINFO_DEFAULT; info->pseudo_palette = p->pseudo_palette; - fb_alloc_cmap(&info->cmap, 256, 0); info->par = &p->par; + return fb_alloc_cmap(&info->cmap, 256, 0); } -- cgit v1.2.3 From cc880a715782fe31116284d90e0b5bfb1411535b Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Tue, 31 Mar 2009 15:25:27 -0700 Subject: sunxvr500: fix cmap memory leaks - fix cmap leak in removal path - fix cmap leak when register_framebuffer fails Signed-off-by: Andres Salomon Cc: "David S. Miller" Cc: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/sunxvr500.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/video/sunxvr500.c b/drivers/video/sunxvr500.c index c2ba51b7ea18..18b950706cad 100644 --- a/drivers/video/sunxvr500.c +++ b/drivers/video/sunxvr500.c @@ -349,11 +349,14 @@ static int __devinit e3d_pci_register(struct pci_dev *pdev, if (err < 0) { printk(KERN_ERR "e3d: Could not register framebuffer %s\n", pci_name(pdev)); - goto err_unmap_fb; + goto err_free_cmap; } return 0; +err_free_cmap: + fb_dealloc_cmap(&info->cmap); + err_unmap_fb: iounmap(ep->fb_base); @@ -389,6 +392,7 @@ static void __devexit e3d_pci_unregister(struct pci_dev *pdev) pci_release_region(pdev, 0); pci_release_region(pdev, 1); + fb_dealloc_cmap(&info->cmap); framebuffer_release(info); pci_disable_device(pdev); -- cgit v1.2.3 From 327fc8752a3c08fc7dc7d382883e65aad2f03bde Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Tue, 31 Mar 2009 15:25:28 -0700 Subject: tgafb: fix cmap memory leak Fix cmap leak when register_framebuffer fails. Signed-off-by: Andres Salomon Acked-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/tgafb.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/video/tgafb.c b/drivers/video/tgafb.c index 680642c089c9..a86046ff60ad 100644 --- a/drivers/video/tgafb.c +++ b/drivers/video/tgafb.c @@ -1663,7 +1663,7 @@ tgafb_register(struct device *dev) if (register_framebuffer(info) < 0) { printk(KERN_ERR "tgafb: Could not register framebuffer\n"); ret = -EINVAL; - goto err1; + goto err2; } if (tga_bus_pci) { @@ -1682,6 +1682,8 @@ tgafb_register(struct device *dev) return 0; + err2: + fb_dealloc_cmap(&info->cmap); err1: if (mem_base) iounmap(mem_base); -- cgit v1.2.3 From e98d9b407c248ba1419bed0823488d3cc71a2c31 Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Tue, 31 Mar 2009 15:25:28 -0700 Subject: 68328fb: fix cmap memory leaks - fix cmap leak in removal path - fix cmap leak when register_framebuffer fails - check return value of fb_alloc_cmap Signed-off-by: Andres Salomon Acked-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/68328fb.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/video/68328fb.c b/drivers/video/68328fb.c index 7f907fb23b8a..0b17824b0eb5 100644 --- a/drivers/video/68328fb.c +++ b/drivers/video/68328fb.c @@ -471,9 +471,11 @@ int __init mc68x328fb_init(void) fb_info.pseudo_palette = &mc68x328fb_pseudo_palette; fb_info.flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN; - fb_alloc_cmap(&fb_info.cmap, 256, 0); + if (fb_alloc_cmap(&fb_info.cmap, 256, 0)) + return -ENOMEM; if (register_framebuffer(&fb_info) < 0) { + fb_dealloc_cmap(&fb_info.cmap); return -EINVAL; } @@ -494,6 +496,7 @@ module_init(mc68x328fb_init); static void __exit mc68x328fb_cleanup(void) { unregister_framebuffer(&fb_info); + fb_dealloc_cmap(&fb_info.cmap); } module_exit(mc68x328fb_cleanup); -- cgit v1.2.3 From 909baf0092545e5c2082b045303e7a4b1d2a0522 Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Tue, 31 Mar 2009 15:25:29 -0700 Subject: amba-clcd: fix cmap memory leaks - fix cmap leak in removal path - fix cmap leak when register_framebuffer fails Signed-off-by: Andres Salomon Acked-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/amba-clcd.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/video/amba-clcd.c b/drivers/video/amba-clcd.c index 4e046fed1380..61050ab14128 100644 --- a/drivers/video/amba-clcd.c +++ b/drivers/video/amba-clcd.c @@ -408,7 +408,9 @@ static int clcdfb_register(struct clcd_fb *fb) /* * Allocate colourmap. */ - fb_alloc_cmap(&fb->fb.cmap, 256, 0); + ret = fb_alloc_cmap(&fb->fb.cmap, 256, 0); + if (ret) + goto unmap; /* * Ensure interrupts are disabled. @@ -426,6 +428,8 @@ static int clcdfb_register(struct clcd_fb *fb) printk(KERN_ERR "CLCD: cannot register framebuffer (%d)\n", ret); + fb_dealloc_cmap(&fb->fb.cmap); + unmap: iounmap(fb->regs); free_clk: clk_put(fb->clk); @@ -485,6 +489,8 @@ static int clcdfb_remove(struct amba_device *dev) clcdfb_disable(fb); unregister_framebuffer(&fb->fb); + if (fb->fb.cmap.len) + fb_dealloc_cmap(&fb->fb.cmap); iounmap(fb->regs); clk_put(fb->clk); -- cgit v1.2.3 From eb8972b4407f81b07ea6fc71fd91f9fc7a35a81e Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Tue, 31 Mar 2009 15:25:30 -0700 Subject: amifb: check fb_alloc_cmap return value and handle failure properly Signed-off-by: Andres Salomon Acked-by: Krzysztof Helt Cc: Geert Uytterhoeven Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/amifb.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/video/amifb.c b/drivers/video/amifb.c index 100f23661465..82bedd7f7789 100644 --- a/drivers/video/amifb.c +++ b/drivers/video/amifb.c @@ -2437,7 +2437,9 @@ default_chipset: goto amifb_error; } - fb_alloc_cmap(&fb_info.cmap, 1< Date: Tue, 31 Mar 2009 15:25:31 -0700 Subject: fbdev: update s1d13xxxfb to differ between revisions and production ids The s1d13xxx chip provides two values of identification value: the Production id (e.g 13506/13505/13806..) and a revision number 0,1,2,3). Together these can help us to differentiate between similiar setups. This patch adds the proper way of grabbing both those values and save them for future reference (in order to decide what functions a card supports, e.g acceleration). We also move away from the concept of all s1d13xxx = s1d13806 when we really support alot more. [akpm@linux-foundation.org: coding-style fixes] [akpm@linux-foundation.org: simplify s1d13xxxfb_probe()] Signed-off-by: Kristoffer Ericson Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/s1d13xxxfb.c | 48 ++++++++++++++++++++++++++++++++++------------ include/video/s1d13xxxfb.h | 16 ++++++++++------ 2 files changed, 46 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/video/s1d13xxxfb.c b/drivers/video/s1d13xxxfb.c index a7b01d2724b5..0726aecf3b7e 100644 --- a/drivers/video/s1d13xxxfb.c +++ b/drivers/video/s1d13xxxfb.c @@ -50,9 +50,22 @@ #define dbg(fmt, args...) do { } while (0) #endif -static const int __devinitconst s1d13xxxfb_revisions[] = { - S1D13506_CHIP_REV, /* Rev.4 on HP Jornada 7xx S1D13506 */ - S1D13806_CHIP_REV, /* Rev.7 on .. */ +/* + * List of card production ids + */ +static const int s1d13xxxfb_prod_ids[] = { + S1D13505_PROD_ID, + S1D13506_PROD_ID, + S1D13806_PROD_ID, +}; + +/* + * List of card strings + */ +static const char *s1d13xxxfb_prod_names[] = { + "S1D13505", + "S1D13506", + "S1D13806", }; /* @@ -377,7 +390,6 @@ s1d13xxxfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) return 0; } - /* framebuffer information structures */ static struct fb_ops s1d13xxxfb_fbops = { @@ -544,7 +556,7 @@ s1d13xxxfb_probe(struct platform_device *pdev) struct s1d13xxxfb_pdata *pdata = NULL; int ret = 0; int i; - u8 revision; + u8 revision, prod_id; dbg("probe called: device is %p\n", pdev); @@ -613,19 +625,31 @@ s1d13xxxfb_probe(struct platform_device *pdev) goto bail; } - revision = s1d13xxxfb_readreg(default_par, S1DREG_REV_CODE) >> 2; - + /* production id is top 6 bits */ + prod_id = s1d13xxxfb_readreg(default_par, S1DREG_REV_CODE) >> 2; + /* revision id is lower 2 bits */ + revision = s1d13xxxfb_readreg(default_par, S1DREG_REV_CODE) & 0x3; ret = -ENODEV; - for (i = 0; i < ARRAY_SIZE(s1d13xxxfb_revisions); i++) { - if (revision == s1d13xxxfb_revisions[i]) + for (i = 0; i < ARRAY_SIZE(s1d13xxxfb_prod_ids); i++) { + if (prod_id == s1d13xxxfb_prod_ids[i]) { + /* looks like we got it in our list */ + default_par->prod_id = prod_id; + default_par->revision = revision; ret = 0; + break; + } } - if (!ret) + if (!ret) { + printk(KERN_INFO PFX "chip production id %i = %s\n", + prod_id, s1d13xxxfb_prod_names[i]); printk(KERN_INFO PFX "chip revision %i\n", revision); - else { - printk(KERN_INFO PFX "unknown chip revision %i\n", revision); + } else { + printk(KERN_INFO PFX + "unknown chip production id %i, revision %i\n", + prod_id, revision); + printk(KERN_INFO PFX "please contant maintainer\n"); goto bail; } diff --git a/include/video/s1d13xxxfb.h b/include/video/s1d13xxxfb.h index fe41b8407946..c3b2a2aa7140 100644 --- a/include/video/s1d13xxxfb.h +++ b/include/video/s1d13xxxfb.h @@ -14,13 +14,16 @@ #define S1D13XXXFB_H #define S1D_PALETTE_SIZE 256 -#define S1D13506_CHIP_REV 4 /* expected chip revision number for s1d13506 */ -#define S1D13806_CHIP_REV 7 /* expected chip revision number for s1d13806 */ -#define S1D_FBID "S1D13806" -#define S1D_DEVICENAME "s1d13806fb" +#define S1D_FBID "S1D13xxx" +#define S1D_DEVICENAME "s1d13xxxfb" + +/* S1DREG_REV_CODE register = prod_id (6 bits) + revision (2 bits) */ +#define S1D13505_PROD_ID 0x3 /* 000011 */ +#define S1D13506_PROD_ID 0x4 /* 000100 */ +#define S1D13806_PROD_ID 0x7 /* 000111 */ /* register definitions (tested on s1d13896) */ -#define S1DREG_REV_CODE 0x0000 /* Revision Code Register */ +#define S1DREG_REV_CODE 0x0000 /* Prod + Rev Code Register */ #define S1DREG_MISC 0x0001 /* Miscellaneous Register */ #define S1DREG_GPIO_CNF0 0x0004 /* General IO Pins Configuration Register 0 */ #define S1DREG_GPIO_CNF1 0x0005 /* General IO Pins Configuration Register 1 */ @@ -141,10 +144,11 @@ struct s1d13xxxfb_regval { u8 value; }; - struct s1d13xxxfb_par { void __iomem *regs; unsigned char display; + unsigned char prod_id; + unsigned char revision; unsigned int pseudo_palette[16]; #ifdef CONFIG_PM -- cgit v1.2.3 From ba78289343226773b27dc25e7d1e739d0162b9e8 Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Tue, 31 Mar 2009 15:25:32 -0700 Subject: drivers/video/omap/hwa742.c: div reaches max_clk_div With for(div = 0; div < max_clk_div; div++) { ... } div reaches max_clk_div. Signed-off-by: Roel Kluin Cc: Joe Perches Acked-by: Trilok Soni Cc: Russell King Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/omap/hwa742.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/video/omap/hwa742.c b/drivers/video/omap/hwa742.c index f24df0b54e1c..8aa6e47202b9 100644 --- a/drivers/video/omap/hwa742.c +++ b/drivers/video/omap/hwa742.c @@ -742,7 +742,7 @@ static int calc_extif_timings(unsigned long sysclk, int *extif_mem_div) if (calc_reg_timing(sysclk, div) == 0) break; } - if (div > max_clk_div) + if (div >= max_clk_div) goto err; *extif_mem_div = div; @@ -752,7 +752,7 @@ static int calc_extif_timings(unsigned long sysclk, int *extif_mem_div) break; } - if (div > max_clk_div) + if (div >= max_clk_div) goto err; return 0; -- cgit v1.2.3 From 032220ba310204be9cb2ddbbf848020fadc63ce6 Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Tue, 31 Mar 2009 15:25:33 -0700 Subject: asiliantfb: fix cmap memory leaks - fix cmap leak in removal path - fix cmap leak when register_framebuffer fails - check return value of fb_alloc_cmap - don't continue with driver setup if register_framebuffer fails [krzysztof.h1@wp.pl: spotted missing iounmap] [randy.dunlap@oracle.com: move data declaration before any code] Signed-off-by: Andres Salomon Cc: Krzysztof Helt Signed-off-by: Randy Dunlap Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/asiliantfb.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/video/asiliantfb.c b/drivers/video/asiliantfb.c index 1fd22f460b0f..1a1f946d8fef 100644 --- a/drivers/video/asiliantfb.c +++ b/drivers/video/asiliantfb.c @@ -505,19 +505,27 @@ static struct fb_var_screeninfo asiliantfb_var __devinitdata = { .vsync_len = 2, }; -static void __devinit init_asiliant(struct fb_info *p, unsigned long addr) +static int __devinit init_asiliant(struct fb_info *p, unsigned long addr) { + int err; + p->fix = asiliantfb_fix; p->fix.smem_start = addr; p->var = asiliantfb_var; p->fbops = &asiliantfb_ops; p->flags = FBINFO_DEFAULT; - fb_alloc_cmap(&p->cmap, 256, 0); + err = fb_alloc_cmap(&p->cmap, 256, 0); + if (err) { + printk(KERN_ERR "C&T 69000 fb failed to alloc cmap memory\n"); + return err; + } - if (register_framebuffer(p) < 0) { + err = register_framebuffer(p); + if (err < 0) { printk(KERN_ERR "C&T 69000 framebuffer failed to register\n"); - return; + fb_dealloc_cmap(&p->cmap); + return err; } printk(KERN_INFO "fb%d: Asiliant 69000 frame buffer (%dK RAM detected)\n", @@ -532,6 +540,7 @@ asiliantfb_pci_init(struct pci_dev *dp, const struct pci_device_id *ent) { unsigned long addr, size; struct fb_info *p; + int err; if ((dp->resource[0].flags & IORESOURCE_MEM) == 0) return -ENODEV; @@ -560,7 +569,13 @@ asiliantfb_pci_init(struct pci_dev *dp, const struct pci_device_id *ent) pci_write_config_dword(dp, 4, 0x02800083); writeb(3, p->screen_base + 0x400784); - init_asiliant(p, addr); + err = init_asiliant(p, addr); + if (err) { + iounmap(p->screen_base); + release_mem_region(addr, size); + framebuffer_release(p); + return err; + } pci_set_drvdata(dp, p); return 0; @@ -571,6 +586,7 @@ static void __devexit asiliantfb_remove(struct pci_dev *dp) struct fb_info *p = pci_get_drvdata(dp); unregister_framebuffer(p); + fb_dealloc_cmap(&p->cmap); iounmap(p->screen_base); release_mem_region(pci_resource_start(dp, 0), pci_resource_len(dp, 0)); pci_set_drvdata(dp, NULL); -- cgit v1.2.3 From b935257b1f98291ec1c8cbf7dbccbe0b20665bf6 Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Tue, 31 Mar 2009 15:25:34 -0700 Subject: arkfb: fix misplaced parentheses Signed-off-by: Roel Kluin Acked-by: Ondrej Zajicek Cc: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/arkfb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/video/arkfb.c b/drivers/video/arkfb.c index 314d18694b6a..d583bea608fd 100644 --- a/drivers/video/arkfb.c +++ b/drivers/video/arkfb.c @@ -470,7 +470,7 @@ static void ark_dac_read_regs(void *data, u8 *code, int count) while (count != 0) { - vga_wseq(NULL, 0x1C, regval | (code[0] & 4) ? 0x80 : 0); + vga_wseq(NULL, 0x1C, regval | (code[0] & 4 ? 0x80 : 0)); code[1] = vga_r(NULL, dac_regs[code[0] & 3]); count--; code += 2; @@ -485,7 +485,7 @@ static void ark_dac_write_regs(void *data, u8 *code, int count) while (count != 0) { - vga_wseq(NULL, 0x1C, regval | (code[0] & 4) ? 0x80 : 0); + vga_wseq(NULL, 0x1C, regval | (code[0] & 4 ? 0x80 : 0)); vga_w(NULL, dac_regs[code[0] & 3], code[1]); count--; code += 2; -- cgit v1.2.3 From 1cc9fb6dbf915e5c7e7e59bb7fab10572ddbb349 Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Tue, 31 Mar 2009 15:25:35 -0700 Subject: uvesafb: bitwise OR has higher precedence than ?: Signed-off-by: Roel Kluin Acked-by: Michal Januszewski Cc: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/uvesafb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/video/uvesafb.c b/drivers/video/uvesafb.c index 398fd25e0810..c288270e42f7 100644 --- a/drivers/video/uvesafb.c +++ b/drivers/video/uvesafb.c @@ -1552,7 +1552,7 @@ static void __devinit uvesafb_init_info(struct fb_info *info, } info->flags = FBINFO_FLAG_DEFAULT | - (par->ypan) ? FBINFO_HWACCEL_YPAN : 0; + (par->ypan ? FBINFO_HWACCEL_YPAN : 0); if (!par->ypan) info->fbops->fb_pan_display = NULL; -- cgit v1.2.3 From b83734ec0975e1f53420b7a2d454612fc905a9d0 Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Tue, 31 Mar 2009 15:25:36 -0700 Subject: vesafb: bitwise OR has higher precedence than ?: Signed-off-by: Roel Kluin Acked-by: Michal Januszewski Cc: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/vesafb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/video/vesafb.c b/drivers/video/vesafb.c index e16322d157d0..d6856f43d241 100644 --- a/drivers/video/vesafb.c +++ b/drivers/video/vesafb.c @@ -438,7 +438,7 @@ static int __init vesafb_probe(struct platform_device *dev) info->var = vesafb_defined; info->fix = vesafb_fix; info->flags = FBINFO_FLAG_DEFAULT | - (ypan) ? FBINFO_HWACCEL_YPAN : 0; + (ypan ? FBINFO_HWACCEL_YPAN : 0); if (!ypan) info->fbops->fb_pan_display = NULL; -- cgit v1.2.3 From 2bd8c47597b2522795f5eb2e61c22dcfec5dfa6a Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Tue, 31 Mar 2009 15:25:36 -0700 Subject: viafb: returns 0 two too early Otherwise this will already return 0 if iteration MAXLOOP-2 occurs in the first loop. Signed-off-by: Roel Kluin Cc: Joseph Chan Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/via/accel.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/video/via/accel.c b/drivers/video/via/accel.c index 632523ff1fb7..45c54bfe99bb 100644 --- a/drivers/video/via/accel.c +++ b/drivers/video/via/accel.c @@ -267,13 +267,17 @@ int viafb_wait_engine_idle(void) int loop = 0; while (!(readl(viaparinfo->io_virt + VIA_REG_STATUS) & - VIA_VR_QUEUE_BUSY) && (loop++ < MAXLOOP)) + VIA_VR_QUEUE_BUSY) && (loop < MAXLOOP)) { + loop++; cpu_relax(); + } while ((readl(viaparinfo->io_virt + VIA_REG_STATUS) & (VIA_CMD_RGTR_BUSY | VIA_2D_ENG_BUSY | VIA_3D_ENG_BUSY)) && - (loop++ < MAXLOOP)) + (loop < MAXLOOP)) { + loop++; cpu_relax(); + } return loop >= MAXLOOP; } -- cgit v1.2.3 From 4c8714310afbaabd94ac30db1e499a90e4a69c4e Mon Sep 17 00:00:00 2001 From: Herton Ronaldo Krzesinski Date: Tue, 31 Mar 2009 15:25:37 -0700 Subject: n411: add missing Makefile entry There is no entry for n411.c to be built, include one in Makefile. Signed-off-by: Herton Ronaldo Krzesinski Cc: Jaya Kumar Cc: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/Makefile | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/video/Makefile b/drivers/video/Makefile index bb265eca7d57..77cfa807d169 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -76,6 +76,7 @@ obj-$(CONFIG_FB_ATARI) += atafb.o c2p_iplan2.o atafb_mfb.o \ atafb_iplan2p2.o atafb_iplan2p4.o atafb_iplan2p8.o obj-$(CONFIG_FB_MAC) += macfb.o obj-$(CONFIG_FB_HECUBA) += hecubafb.o +obj-$(CONFIG_FB_N411) += n411.o obj-$(CONFIG_FB_HGA) += hgafb.o obj-$(CONFIG_FB_XVR500) += sunxvr500.o obj-$(CONFIG_FB_XVR2500) += sunxvr2500.o -- cgit v1.2.3 From ec549a0fdc32171b26677f1ef0b5309faa743362 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 31 Mar 2009 15:25:39 -0700 Subject: fb: add s3c-fb driver for newer Samsung SoC framebuffer devices Add support for the newer Samsung devices, such as found in the S3C2443, S3C6400 or S3C6410 series SoC. It currently does not support all the alpha- or chroma-key options but it will support more exporting more than one framebuffer ready for adding overlay and blending functions. Signed-off-by: Ben Dooks Cc: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/Kconfig | 24 ++ drivers/video/Makefile | 1 + drivers/video/s3c-fb.c | 1036 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1061 insertions(+) create mode 100644 drivers/video/s3c-fb.c (limited to 'drivers') diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 41c27a44bd82..c19f6feb4e53 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -1920,6 +1920,30 @@ config FB_TMIO_ACCELL depends on FB_TMIO default y +config FB_S3C + tristate "Samsung S3C framebuffer support" + depends on FB && ARCH_S3C64XX + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + ---help--- + Frame buffer driver for the built-in FB controller in the Samsung + SoC line from the S3C2443 onwards, including the S3C2416, S3C2450, + and the S3C64XX series such as the S3C6400 and S3C6410. + + These chips all have the same basic framebuffer design with the + actual capabilities depending on the chip. For instance the S3C6400 + and S3C6410 support 4 hardware windows whereas the S3C24XX series + currently only have two. + + Currently the support is only for the S3C6400 and S3C6410 SoCs. + +config FB_S3C_DEBUG_REGWRITE + bool "Debug register writes" + depends on FB_S3C + ---help--- + Show all register writes via printk(KERN_DEBUG) + config FB_S3C2410 tristate "S3C2410 LCD framebuffer support" depends on FB && ARCH_S3C2410 diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 77cfa807d169..0dbd6c68d76b 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -111,6 +111,7 @@ obj-$(CONFIG_FB_BROADSHEET) += broadsheetfb.o obj-$(CONFIG_FB_S1D13XXX) += s1d13xxxfb.o obj-$(CONFIG_FB_SH7760) += sh7760fb.o obj-$(CONFIG_FB_IMX) += imxfb.o +obj-$(CONFIG_FB_S3C) += s3c-fb.o obj-$(CONFIG_FB_S3C2410) += s3c2410fb.o obj-$(CONFIG_FB_FSL_DIU) += fsl-diu-fb.o obj-$(CONFIG_FB_COBALT) += cobalt_lcdfb.o diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c new file mode 100644 index 000000000000..5e9c6302433b --- /dev/null +++ b/drivers/video/s3c-fb.c @@ -0,0 +1,1036 @@ +/* linux/drivers/video/s3c-fb.c + * + * Copyright 2008 Openmoko Inc. + * Copyright 2008 Simtec Electronics + * Ben Dooks + * http://armlinux.simtec.co.uk/ + * + * Samsung SoC Framebuffer driver + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* This driver will export a number of framebuffer interfaces depending + * on the configuration passed in via the platform data. Each fb instance + * maps to a hardware window. Currently there is no support for runtime + * setting of the alpha-blending functions that each window has, so only + * window 0 is actually useful. + * + * Window 0 is treated specially, it is used for the basis of the LCD + * output timings and as the control for the output power-down state. +*/ + +/* note, some of the functions that get called are derived from including + * as they are specific to the architecture that the code + * is being built for. +*/ + +#ifdef CONFIG_FB_S3C_DEBUG_REGWRITE +#undef writel +#define writel(v, r) do { \ + printk(KERN_DEBUG "%s: %08x => %p\n", __func__, (unsigned int)v, r); \ + __raw_writel(v, r); } while(0) +#endif /* FB_S3C_DEBUG_REGWRITE */ + +struct s3c_fb; + +/** + * struct s3c_fb_win - per window private data for each framebuffer. + * @windata: The platform data supplied for the window configuration. + * @parent: The hardware that this window is part of. + * @fbinfo: Pointer pack to the framebuffer info for this window. + * @palette_buffer: Buffer/cache to hold palette entries. + * @pseudo_palette: For use in TRUECOLOUR modes for entries 0..15/ + * @index: The window number of this window. + * @palette: The bitfields for changing r/g/b into a hardware palette entry. + */ +struct s3c_fb_win { + struct s3c_fb_pd_win *windata; + struct s3c_fb *parent; + struct fb_info *fbinfo; + struct s3c_fb_palette palette; + + u32 *palette_buffer; + u32 pseudo_palette[16]; + unsigned int index; +}; + +/** + * struct s3c_fb - overall hardware state of the hardware + * @dev: The device that we bound to, for printing, etc. + * @regs_res: The resource we claimed for the IO registers. + * @bus_clk: The clk (hclk) feeding our interface and possibly pixclk. + * @regs: The mapped hardware registers. + * @enabled: A bitmask of enabled hardware windows. + * @pdata: The platform configuration data passed with the device. + * @windows: The hardware windows that have been claimed. + */ +struct s3c_fb { + struct device *dev; + struct resource *regs_res; + struct clk *bus_clk; + void __iomem *regs; + + unsigned char enabled; + + struct s3c_fb_platdata *pdata; + struct s3c_fb_win *windows[S3C_FB_MAX_WIN]; +}; + +/** + * s3c_fb_win_has_palette() - determine if a mode has a palette + * @win: The window number being queried. + * @bpp: The number of bits per pixel to test. + * + * Work out if the given window supports palletised data at the specified bpp. + */ +static int s3c_fb_win_has_palette(unsigned int win, unsigned int bpp) +{ + return s3c_fb_win_pal_size(win) <= (1 << bpp); +} + +/** + * s3c_fb_check_var() - framebuffer layer request to verify a given mode. + * @var: The screen information to verify. + * @info: The framebuffer device. + * + * Framebuffer layer call to verify the given information and allow us to + * update various information depending on the hardware capabilities. + */ +static int s3c_fb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct s3c_fb_win *win = info->par; + struct s3c_fb_pd_win *windata = win->windata; + struct s3c_fb *sfb = win->parent; + + dev_dbg(sfb->dev, "checking parameters\n"); + + var->xres_virtual = max((unsigned int)windata->virtual_x, var->xres); + var->yres_virtual = max((unsigned int)windata->virtual_y, var->yres); + + if (!s3c_fb_validate_win_bpp(win->index, var->bits_per_pixel)) { + dev_dbg(sfb->dev, "win %d: unsupported bpp %d\n", + win->index, var->bits_per_pixel); + return -EINVAL; + } + + /* always ensure these are zero, for drop through cases below */ + var->transp.offset = 0; + var->transp.length = 0; + + switch (var->bits_per_pixel) { + case 1: + case 2: + case 4: + case 8: + if (!s3c_fb_win_has_palette(win->index, var->bits_per_pixel)) { + /* non palletised, A:1,R:2,G:3,B:2 mode */ + var->red.offset = 4; + var->green.offset = 2; + var->blue.offset = 0; + var->red.length = 5; + var->green.length = 3; + var->blue.length = 2; + var->transp.offset = 7; + var->transp.length = 1; + } else { + var->red.offset = 0; + var->red.length = var->bits_per_pixel; + var->green = var->red; + var->blue = var->red; + } + break; + + case 19: + /* 666 with one bit alpha/transparency */ + var->transp.offset = 18; + var->transp.length = 1; + case 18: + var->bits_per_pixel = 32; + + /* 666 format */ + var->red.offset = 12; + var->green.offset = 6; + var->blue.offset = 0; + var->red.length = 6; + var->green.length = 6; + var->blue.length = 6; + break; + + case 16: + /* 16 bpp, 565 format */ + var->red.offset = 11; + var->green.offset = 5; + var->blue.offset = 0; + var->red.length = 5; + var->green.length = 6; + var->blue.length = 5; + break; + + case 28: + case 25: + var->transp.length = var->bits_per_pixel - 24; + var->transp.offset = 24; + /* drop through */ + case 24: + /* our 24bpp is unpacked, so 32bpp */ + var->bits_per_pixel = 32; + case 32: + var->red.offset = 16; + var->red.length = 8; + var->green.offset = 8; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + break; + + default: + dev_err(sfb->dev, "invalid bpp\n"); + } + + dev_dbg(sfb->dev, "%s: verified parameters\n", __func__); + return 0; +} + +/** + * s3c_fb_calc_pixclk() - calculate the divider to create the pixel clock. + * @sfb: The hardware state. + * @pixclock: The pixel clock wanted, in picoseconds. + * + * Given the specified pixel clock, work out the necessary divider to get + * close to the output frequency. + */ +static int s3c_fb_calc_pixclk(struct s3c_fb *sfb, unsigned int pixclk) +{ + unsigned long clk = clk_get_rate(sfb->bus_clk); + unsigned long long tmp; + unsigned int result; + + tmp = (unsigned long long)clk; + tmp *= pixclk; + + do_div(tmp, 1000000000UL); + result = (unsigned int)tmp / 1000; + + dev_dbg(sfb->dev, "pixclk=%u, clk=%lu, div=%d (%lu)\n", + pixclk, clk, result, clk / result); + + return result; +} + +/** + * s3c_fb_align_word() - align pixel count to word boundary + * @bpp: The number of bits per pixel + * @pix: The value to be aligned. + * + * Align the given pixel count so that it will start on an 32bit word + * boundary. + */ +static int s3c_fb_align_word(unsigned int bpp, unsigned int pix) +{ + int pix_per_word; + + if (bpp > 16) + return pix; + + pix_per_word = (8 * 32) / bpp; + return ALIGN(pix, pix_per_word); +} + +/** + * s3c_fb_set_par() - framebuffer request to set new framebuffer state. + * @info: The framebuffer to change. + * + * Framebuffer layer request to set a new mode for the specified framebuffer + */ +static int s3c_fb_set_par(struct fb_info *info) +{ + struct fb_var_screeninfo *var = &info->var; + struct s3c_fb_win *win = info->par; + struct s3c_fb *sfb = win->parent; + void __iomem *regs = sfb->regs; + int win_no = win->index; + u32 data; + u32 pagewidth; + int clkdiv; + + dev_dbg(sfb->dev, "setting framebuffer parameters\n"); + + switch (var->bits_per_pixel) { + case 32: + case 24: + case 16: + case 12: + info->fix.visual = FB_VISUAL_TRUECOLOR; + break; + case 8: + if (s3c_fb_win_has_palette(win_no, 8)) + info->fix.visual = FB_VISUAL_PSEUDOCOLOR; + else + info->fix.visual = FB_VISUAL_TRUECOLOR; + break; + case 1: + info->fix.visual = FB_VISUAL_MONO01; + break; + default: + info->fix.visual = FB_VISUAL_PSEUDOCOLOR; + break; + } + + info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8; + + /* disable the window whilst we update it */ + writel(0, regs + WINCON(win_no)); + + /* use window 0 as the basis for the lcd output timings */ + + if (win_no == 0) { + clkdiv = s3c_fb_calc_pixclk(sfb, var->pixclock); + + data = sfb->pdata->vidcon0; + data &= ~(VIDCON0_CLKVAL_F_MASK | VIDCON0_CLKDIR); + + if (clkdiv > 1) + data |= VIDCON0_CLKVAL_F(clkdiv-1) | VIDCON0_CLKDIR; + else + data &= ~VIDCON0_CLKDIR; /* 1:1 clock */ + + /* write the timing data to the panel */ + + data |= VIDCON0_ENVID | VIDCON0_ENVID_F; + writel(data, regs + VIDCON0); + + data = VIDTCON0_VBPD(var->upper_margin - 1) | + VIDTCON0_VFPD(var->lower_margin - 1) | + VIDTCON0_VSPW(var->vsync_len - 1); + + writel(data, regs + VIDTCON0); + + data = VIDTCON1_HBPD(var->left_margin - 1) | + VIDTCON1_HFPD(var->right_margin - 1) | + VIDTCON1_HSPW(var->hsync_len - 1); + + writel(data, regs + VIDTCON1); + + data = VIDTCON2_LINEVAL(var->yres - 1) | + VIDTCON2_HOZVAL(var->xres - 1); + writel(data, regs + VIDTCON2); + } + + /* write the buffer address */ + + writel(info->fix.smem_start, regs + VIDW_BUF_START(win_no)); + + data = info->fix.smem_start + info->fix.line_length * var->yres; + writel(data, regs + VIDW_BUF_END(win_no)); + + pagewidth = (var->xres * var->bits_per_pixel) >> 3; + data = VIDW_BUF_SIZE_OFFSET(info->fix.line_length - pagewidth) | + VIDW_BUF_SIZE_PAGEWIDTH(pagewidth); + writel(data, regs + VIDW_BUF_SIZE(win_no)); + + /* write 'OSD' registers to control position of framebuffer */ + + data = VIDOSDxA_TOPLEFT_X(0) | VIDOSDxA_TOPLEFT_Y(0); + writel(data, regs + VIDOSD_A(win_no)); + + data = VIDOSDxB_BOTRIGHT_X(s3c_fb_align_word(var->bits_per_pixel, + var->xres - 1)) | + VIDOSDxB_BOTRIGHT_Y(var->yres - 1); + + writel(data, regs + VIDOSD_B(win_no)); + + data = var->xres * var->yres; + if (s3c_fb_has_osd_d(win_no)) { + writel(data, regs + VIDOSD_D(win_no)); + writel(0, regs + VIDOSD_C(win_no)); + } else + writel(data, regs + VIDOSD_C(win_no)); + + data = WINCONx_ENWIN; + + /* note, since we have to round up the bits-per-pixel, we end up + * relying on the bitfield information for r/g/b/a to work out + * exactly which mode of operation is intended. */ + + switch (var->bits_per_pixel) { + case 1: + data |= WINCON0_BPPMODE_1BPP; + data |= WINCONx_BITSWP; + data |= WINCONx_BURSTLEN_4WORD; + break; + case 2: + data |= WINCON0_BPPMODE_2BPP; + data |= WINCONx_BITSWP; + data |= WINCONx_BURSTLEN_8WORD; + break; + case 4: + data |= WINCON0_BPPMODE_4BPP; + data |= WINCONx_BITSWP; + data |= WINCONx_BURSTLEN_8WORD; + break; + case 8: + if (var->transp.length != 0) + data |= WINCON1_BPPMODE_8BPP_1232; + else + data |= WINCON0_BPPMODE_8BPP_PALETTE; + data |= WINCONx_BURSTLEN_8WORD; + data |= WINCONx_BYTSWP; + break; + case 16: + if (var->transp.length != 0) + data |= WINCON1_BPPMODE_16BPP_A1555; + else + data |= WINCON0_BPPMODE_16BPP_565; + data |= WINCONx_HAWSWP; + data |= WINCONx_BURSTLEN_16WORD; + break; + case 24: + case 32: + if (var->red.length == 6) { + if (var->transp.length != 0) + data |= WINCON1_BPPMODE_19BPP_A1666; + else + data |= WINCON1_BPPMODE_18BPP_666; + } else if (var->transp.length != 0) + data |= WINCON1_BPPMODE_25BPP_A1888; + else + data |= WINCON0_BPPMODE_24BPP_888; + + data |= WINCONx_BURSTLEN_16WORD; + break; + } + + writel(data, regs + WINCON(win_no)); + writel(0x0, regs + WINxMAP(win_no)); + + return 0; +} + +/** + * s3c_fb_update_palette() - set or schedule a palette update. + * @sfb: The hardware information. + * @win: The window being updated. + * @reg: The palette index being changed. + * @value: The computed palette value. + * + * Change the value of a palette register, either by directly writing to + * the palette (this requires the palette RAM to be disconnected from the + * hardware whilst this is in progress) or schedule the update for later. + * + * At the moment, since we have no VSYNC interrupt support, we simply set + * the palette entry directly. + */ +static void s3c_fb_update_palette(struct s3c_fb *sfb, + struct s3c_fb_win *win, + unsigned int reg, + u32 value) +{ + void __iomem *palreg; + u32 palcon; + + palreg = sfb->regs + s3c_fb_pal_reg(win->index, reg); + + dev_dbg(sfb->dev, "%s: win %d, reg %d (%p): %08x\n", + __func__, win->index, reg, palreg, value); + + win->palette_buffer[reg] = value; + + palcon = readl(sfb->regs + WPALCON); + writel(palcon | WPALCON_PAL_UPDATE, sfb->regs + WPALCON); + + if (s3c_fb_pal_is16(win->index)) + writew(value, palreg); + else + writel(value, palreg); + + writel(palcon, sfb->regs + WPALCON); +} + +static inline unsigned int chan_to_field(unsigned int chan, + struct fb_bitfield *bf) +{ + chan &= 0xffff; + chan >>= 16 - bf->length; + return chan << bf->offset; +} + +/** + * s3c_fb_setcolreg() - framebuffer layer request to change palette. + * @regno: The palette index to change. + * @red: The red field for the palette data. + * @green: The green field for the palette data. + * @blue: The blue field for the palette data. + * @trans: The transparency (alpha) field for the palette data. + * @info: The framebuffer being changed. + */ +static int s3c_fb_setcolreg(unsigned regno, + unsigned red, unsigned green, unsigned blue, + unsigned transp, struct fb_info *info) +{ + struct s3c_fb_win *win = info->par; + struct s3c_fb *sfb = win->parent; + unsigned int val; + + dev_dbg(sfb->dev, "%s: win %d: %d => rgb=%d/%d/%d\n", + __func__, win->index, regno, red, green, blue); + + switch (info->fix.visual) { + case FB_VISUAL_TRUECOLOR: + /* true-colour, use pseudo-palette */ + + if (regno < 16) { + u32 *pal = info->pseudo_palette; + + val = chan_to_field(red, &info->var.red); + val |= chan_to_field(green, &info->var.green); + val |= chan_to_field(blue, &info->var.blue); + + pal[regno] = val; + } + break; + + case FB_VISUAL_PSEUDOCOLOR: + if (regno < s3c_fb_win_pal_size(win->index)) { + val = chan_to_field(red, &win->palette.r); + val |= chan_to_field(green, &win->palette.g); + val |= chan_to_field(blue, &win->palette.b); + + s3c_fb_update_palette(sfb, win, regno, val); + } + + break; + + default: + return 1; /* unknown type */ + } + + return 0; +} + +/** + * s3c_fb_enable() - Set the state of the main LCD output + * @sfb: The main framebuffer state. + * @enable: The state to set. + */ +static void s3c_fb_enable(struct s3c_fb *sfb, int enable) +{ + u32 vidcon0 = readl(sfb->regs + VIDCON0); + + if (enable) + vidcon0 |= VIDCON0_ENVID | VIDCON0_ENVID_F; + else { + /* see the note in the framebuffer datasheet about + * why you cannot take both of these bits down at the + * same time. */ + + if (!(vidcon0 & VIDCON0_ENVID)) + return; + + vidcon0 |= VIDCON0_ENVID; + vidcon0 &= ~VIDCON0_ENVID_F; + } + + writel(vidcon0, sfb->regs + VIDCON0); +} + +/** + * s3c_fb_blank() - blank or unblank the given window + * @blank_mode: The blank state from FB_BLANK_* + * @info: The framebuffer to blank. + * + * Framebuffer layer request to change the power state. + */ +static int s3c_fb_blank(int blank_mode, struct fb_info *info) +{ + struct s3c_fb_win *win = info->par; + struct s3c_fb *sfb = win->parent; + unsigned int index = win->index; + u32 wincon; + + dev_dbg(sfb->dev, "blank mode %d\n", blank_mode); + + wincon = readl(sfb->regs + WINCON(index)); + + switch (blank_mode) { + case FB_BLANK_POWERDOWN: + wincon &= ~WINCONx_ENWIN; + sfb->enabled &= ~(1 << index); + /* fall through to FB_BLANK_NORMAL */ + + case FB_BLANK_NORMAL: + /* disable the DMA and display 0x0 (black) */ + writel(WINxMAP_MAP | WINxMAP_MAP_COLOUR(0x0), + sfb->regs + WINxMAP(index)); + break; + + case FB_BLANK_UNBLANK: + writel(0x0, sfb->regs + WINxMAP(index)); + wincon |= WINCONx_ENWIN; + sfb->enabled |= (1 << index); + break; + + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_HSYNC_SUSPEND: + default: + return 1; + } + + writel(wincon, sfb->regs + WINCON(index)); + + /* Check the enabled state to see if we need to be running the + * main LCD interface, as if there are no active windows then + * it is highly likely that we also do not need to output + * anything. + */ + + /* We could do something like the following code, but the current + * system of using framebuffer events means that we cannot make + * the distinction between just window 0 being inactive and all + * the windows being down. + * + * s3c_fb_enable(sfb, sfb->enabled ? 1 : 0); + */ + + /* we're stuck with this until we can do something about overriding + * the power control using the blanking event for a single fb. + */ + if (index == 0) + s3c_fb_enable(sfb, blank_mode != FB_BLANK_POWERDOWN ? 1 : 0); + + return 0; +} + +static struct fb_ops s3c_fb_ops = { + .owner = THIS_MODULE, + .fb_check_var = s3c_fb_check_var, + .fb_set_par = s3c_fb_set_par, + .fb_blank = s3c_fb_blank, + .fb_setcolreg = s3c_fb_setcolreg, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, +}; + +/** + * s3c_fb_alloc_memory() - allocate display memory for framebuffer window + * @sfb: The base resources for the hardware. + * @win: The window to initialise memory for. + * + * Allocate memory for the given framebuffer. + */ +static int __devinit s3c_fb_alloc_memory(struct s3c_fb *sfb, + struct s3c_fb_win *win) +{ + struct s3c_fb_pd_win *windata = win->windata; + unsigned int real_size, virt_size, size; + struct fb_info *fbi = win->fbinfo; + dma_addr_t map_dma; + + dev_dbg(sfb->dev, "allocating memory for display\n"); + + real_size = windata->win_mode.xres * windata->win_mode.yres; + virt_size = windata->virtual_x * windata->virtual_y; + + dev_dbg(sfb->dev, "real_size=%u (%u.%u), virt_size=%u (%u.%u)\n", + real_size, windata->win_mode.xres, windata->win_mode.yres, + virt_size, windata->virtual_x, windata->virtual_y); + + size = (real_size > virt_size) ? real_size : virt_size; + size *= (windata->max_bpp > 16) ? 32 : windata->max_bpp; + size /= 8; + + fbi->fix.smem_len = size; + size = PAGE_ALIGN(size); + + dev_dbg(sfb->dev, "want %u bytes for window\n", size); + + fbi->screen_base = dma_alloc_writecombine(sfb->dev, size, + &map_dma, GFP_KERNEL); + if (!fbi->screen_base) + return -ENOMEM; + + dev_dbg(sfb->dev, "mapped %x to %p\n", + (unsigned int)map_dma, fbi->screen_base); + + memset(fbi->screen_base, 0x0, size); + fbi->fix.smem_start = map_dma; + + return 0; +} + +/** + * s3c_fb_free_memory() - free the display memory for the given window + * @sfb: The base resources for the hardware. + * @win: The window to free the display memory for. + * + * Free the display memory allocated by s3c_fb_alloc_memory(). + */ +static void s3c_fb_free_memory(struct s3c_fb *sfb, struct s3c_fb_win *win) +{ + struct fb_info *fbi = win->fbinfo; + + dma_free_writecombine(sfb->dev, PAGE_ALIGN(fbi->fix.smem_len), + fbi->screen_base, fbi->fix.smem_start); +} + +/** + * s3c_fb_release_win() - release resources for a framebuffer window. + * @win: The window to cleanup the resources for. + * + * Release the resources that where claimed for the hardware window, + * such as the framebuffer instance and any memory claimed for it. + */ +static void s3c_fb_release_win(struct s3c_fb *sfb, struct s3c_fb_win *win) +{ + fb_dealloc_cmap(&win->fbinfo->cmap); + unregister_framebuffer(win->fbinfo); + s3c_fb_free_memory(sfb, win); +} + +/** + * s3c_fb_probe_win() - register an hardware window + * @sfb: The base resources for the hardware + * @res: Pointer to where to place the resultant window. + * + * Allocate and do the basic initialisation for one of the hardware's graphics + * windows. + */ +static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no, + struct s3c_fb_win **res) +{ + struct fb_var_screeninfo *var; + struct fb_videomode *initmode; + struct s3c_fb_pd_win *windata; + struct s3c_fb_win *win; + struct fb_info *fbinfo; + int palette_size; + int ret; + + dev_dbg(sfb->dev, "probing window %d\n", win_no); + + palette_size = s3c_fb_win_pal_size(win_no); + + fbinfo = framebuffer_alloc(sizeof(struct s3c_fb_win) + + palette_size * sizeof(u32), sfb->dev); + if (!fbinfo) { + dev_err(sfb->dev, "failed to allocate framebuffer\n"); + return -ENOENT; + } + + windata = sfb->pdata->win[win_no]; + initmode = &windata->win_mode; + + WARN_ON(windata->max_bpp == 0); + WARN_ON(windata->win_mode.xres == 0); + WARN_ON(windata->win_mode.yres == 0); + + win = fbinfo->par; + var = &fbinfo->var; + win->fbinfo = fbinfo; + win->parent = sfb; + win->windata = windata; + win->index = win_no; + win->palette_buffer = (u32 *)(win + 1); + + ret = s3c_fb_alloc_memory(sfb, win); + if (ret) { + dev_err(sfb->dev, "failed to allocate display memory\n"); + goto err_framebuffer; + } + + /* setup the r/b/g positions for the window's palette */ + s3c_fb_init_palette(win_no, &win->palette); + + /* setup the initial video mode from the window */ + fb_videomode_to_var(&fbinfo->var, initmode); + + fbinfo->fix.type = FB_TYPE_PACKED_PIXELS; + fbinfo->fix.accel = FB_ACCEL_NONE; + fbinfo->var.activate = FB_ACTIVATE_NOW; + fbinfo->var.vmode = FB_VMODE_NONINTERLACED; + fbinfo->var.bits_per_pixel = windata->default_bpp; + fbinfo->fbops = &s3c_fb_ops; + fbinfo->flags = FBINFO_FLAG_DEFAULT; + fbinfo->pseudo_palette = &win->pseudo_palette; + + /* prepare to actually start the framebuffer */ + + ret = s3c_fb_check_var(&fbinfo->var, fbinfo); + if (ret < 0) { + dev_err(sfb->dev, "check_var failed on initial video params\n"); + goto err_alloc_mem; + } + + /* create initial colour map */ + + ret = fb_alloc_cmap(&fbinfo->cmap, s3c_fb_win_pal_size(win_no), 1); + if (ret == 0) + fb_set_cmap(&fbinfo->cmap, fbinfo); + else + dev_err(sfb->dev, "failed to allocate fb cmap\n"); + + s3c_fb_set_par(fbinfo); + + dev_dbg(sfb->dev, "about to register framebuffer\n"); + + /* run the check_var and set_par on our configuration. */ + + ret = register_framebuffer(fbinfo); + if (ret < 0) { + dev_err(sfb->dev, "failed to register framebuffer\n"); + goto err_alloc_mem; + } + + *res = win; + dev_info(sfb->dev, "window %d: fb %s\n", win_no, fbinfo->fix.id); + + return 0; + +err_alloc_mem: + s3c_fb_free_memory(sfb, win); + +err_framebuffer: + unregister_framebuffer(fbinfo); + return ret; +} + +/** + * s3c_fb_clear_win() - clear hardware window registers. + * @sfb: The base resources for the hardware. + * @win: The window to process. + * + * Reset the specific window registers to a known state. + */ +static void s3c_fb_clear_win(struct s3c_fb *sfb, int win) +{ + void __iomem *regs = sfb->regs; + + writel(0, regs + WINCON(win)); + writel(0xffffff, regs + WxKEYCONy(win, 0)); + writel(0xffffff, regs + WxKEYCONy(win, 1)); + + writel(0, regs + VIDOSD_A(win)); + writel(0, regs + VIDOSD_B(win)); + writel(0, regs + VIDOSD_C(win)); +} + +static int __devinit s3c_fb_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct s3c_fb_platdata *pd; + struct s3c_fb *sfb; + struct resource *res; + int win; + int ret = 0; + + pd = pdev->dev.platform_data; + if (!pd) { + dev_err(dev, "no platform data specified\n"); + return -EINVAL; + } + + sfb = kzalloc(sizeof(struct s3c_fb), GFP_KERNEL); + if (!sfb) { + dev_err(dev, "no memory for framebuffers\n"); + return -ENOMEM; + } + + sfb->dev = dev; + sfb->pdata = pd; + + sfb->bus_clk = clk_get(dev, "lcd"); + if (IS_ERR(sfb->bus_clk)) { + dev_err(dev, "failed to get bus clock\n"); + goto err_sfb; + } + + clk_enable(sfb->bus_clk); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "failed to find registers\n"); + ret = -ENOENT; + goto err_clk; + } + + sfb->regs_res = request_mem_region(res->start, resource_size(res), + dev_name(dev)); + if (!sfb->regs_res) { + dev_err(dev, "failed to claim register region\n"); + ret = -ENOENT; + goto err_clk; + } + + sfb->regs = ioremap(res->start, resource_size(res)); + if (!sfb->regs) { + dev_err(dev, "failed to map registers\n"); + ret = -ENXIO; + goto err_req_region; + } + + dev_dbg(dev, "got resources (regs %p), probing windows\n", sfb->regs); + + /* setup gpio and output polarity controls */ + + pd->setup_gpio(); + + writel(pd->vidcon1, sfb->regs + VIDCON1); + + /* zero all windows before we do anything */ + + for (win = 0; win < S3C_FB_MAX_WIN; win++) + s3c_fb_clear_win(sfb, win); + + /* we have the register setup, start allocating framebuffers */ + + for (win = 0; win < S3C_FB_MAX_WIN; win++) { + if (!pd->win[win]) + continue; + + ret = s3c_fb_probe_win(sfb, win, &sfb->windows[win]); + if (ret < 0) { + dev_err(dev, "failed to create window %d\n", win); + for (; win >= 0; win--) + s3c_fb_release_win(sfb, sfb->windows[win]); + goto err_ioremap; + } + } + + platform_set_drvdata(pdev, sfb); + + return 0; + +err_ioremap: + iounmap(sfb->regs); + +err_req_region: + release_resource(sfb->regs_res); + kfree(sfb->regs_res); + +err_clk: + clk_disable(sfb->bus_clk); + clk_put(sfb->bus_clk); + +err_sfb: + kfree(sfb); + return ret; +} + +/** + * s3c_fb_remove() - Cleanup on module finalisation + * @pdev: The platform device we are bound to. + * + * Shutdown and then release all the resources that the driver allocated + * on initialisation. + */ +static int __devexit s3c_fb_remove(struct platform_device *pdev) +{ + struct s3c_fb *sfb = platform_get_drvdata(pdev); + int win; + + for (win = 0; win <= S3C_FB_MAX_WIN; win++) + s3c_fb_release_win(sfb, sfb->windows[win]); + + iounmap(sfb->regs); + + clk_disable(sfb->bus_clk); + clk_put(sfb->bus_clk); + + release_resource(sfb->regs_res); + kfree(sfb->regs_res); + + kfree(sfb); + + return 0; +} + +#ifdef CONFIG_PM +static int s3c_fb_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct s3c_fb *sfb = platform_get_drvdata(pdev); + struct s3c_fb_win *win; + int win_no; + + for (win_no = S3C_FB_MAX_WIN; win_no >= 0; win_no--) { + win = sfb->windows[win_no]; + if (!win) + continue; + + /* use the blank function to push into power-down */ + s3c_fb_blank(FB_BLANK_POWERDOWN, win->fbinfo); + } + + clk_disable(sfb->bus_clk); + return 0; +} + +static int s3c_fb_resume(struct platform_device *pdev) +{ + struct s3c_fb *sfb = platform_get_drvdata(pdev); + struct s3c_fb_win *win; + int win_no; + + clk_enable(sfb->bus_clk); + + for (win_no = 0; win_no < S3C_FB_MAX_WIN; win_no++) { + win = sfb->windows[win_no]; + if (!win) + continue; + + dev_dbg(&pdev->dev, "resuming window %d\n", win_no); + s3c_fb_set_par(win->fbinfo); + } + + return 0; +} +#else +#define s3c_fb_suspend NULL +#define s3c_fb_resume NULL +#endif + +static struct platform_driver s3c_fb_driver = { + .probe = s3c_fb_probe, + .remove = s3c_fb_remove, + .suspend = s3c_fb_suspend, + .resume = s3c_fb_resume, + .driver = { + .name = "s3c-fb", + .owner = THIS_MODULE, + }, +}; + +static int __init s3c_fb_init(void) +{ + return platform_driver_register(&s3c_fb_driver); +} + +static void __exit s3c_fb_cleanup(void) +{ + platform_driver_unregister(&s3c_fb_driver); +} + +module_init(s3c_fb_init); +module_exit(s3c_fb_cleanup); + +MODULE_AUTHOR("Ben Dooks "); +MODULE_DESCRIPTION("Samsung S3C SoC Framebuffer driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:s3c-fb"); -- cgit v1.2.3 From ddb53d48da5b0e691f35e703ac29118747f86c99 Mon Sep 17 00:00:00 2001 From: Krzysztof Helt Date: Tue, 31 Mar 2009 15:25:40 -0700 Subject: fbdev: remove cyblafb driver A tridentfb driver has all the functionality of the cyblafb driver without the bugs of the latter. Changes to the tridentfb driver: - FBINFO_READS_FAST added to the tridentfb. The cyblafb used a blitter for scrolling which is faster than color expansion on Cyberblade chipsets. The blitter is slower on a discrete Blade3D core. Use the blitter for scrolling in the tridentfb only for integrated Blade3D cores. Now, scrolling speed is about equal for the tridentfb and the cyblafb. - a copyright notice addition is done on request of Jani Monoses (the first author of the tridentfb). Tested on AGP Blade3D card and PCChips M787CLR motherboard: VIA C3 cpu + VT8601 north bridge (aka Cyberblade/i1). Signed-off-by: Krzysztof Helt Cc: "Jani Monoses" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/fb/00-INDEX | 2 - Documentation/fb/cyblafb/bugs | 13 - Documentation/fb/cyblafb/credits | 7 - Documentation/fb/cyblafb/documentation | 17 - Documentation/fb/cyblafb/fb.modes | 154 --- Documentation/fb/cyblafb/performance | 79 -- Documentation/fb/cyblafb/todo | 31 - Documentation/fb/cyblafb/usage | 217 ---- Documentation/fb/cyblafb/whatsnew | 29 - Documentation/fb/cyblafb/whycyblafb | 85 -- drivers/video/Kconfig | 28 +- drivers/video/cyblafb.c | 1683 -------------------------------- drivers/video/tridentfb.c | 6 +- 13 files changed, 7 insertions(+), 2344 deletions(-) delete mode 100644 Documentation/fb/cyblafb/bugs delete mode 100644 Documentation/fb/cyblafb/credits delete mode 100644 Documentation/fb/cyblafb/documentation delete mode 100644 Documentation/fb/cyblafb/fb.modes delete mode 100644 Documentation/fb/cyblafb/performance delete mode 100644 Documentation/fb/cyblafb/todo delete mode 100644 Documentation/fb/cyblafb/usage delete mode 100644 Documentation/fb/cyblafb/whatsnew delete mode 100644 Documentation/fb/cyblafb/whycyblafb delete mode 100644 drivers/video/cyblafb.c (limited to 'drivers') diff --git a/Documentation/fb/00-INDEX b/Documentation/fb/00-INDEX index caabbd395e61..a618fd99c9f0 100644 --- a/Documentation/fb/00-INDEX +++ b/Documentation/fb/00-INDEX @@ -11,8 +11,6 @@ aty128fb.txt - info on the ATI Rage128 frame buffer driver. cirrusfb.txt - info on the driver for Cirrus Logic chipsets. -cyblafb/ - - directory with documentation files related to the cyblafb driver. deferred_io.txt - an introduction to deferred IO. fbcon.txt diff --git a/Documentation/fb/cyblafb/bugs b/Documentation/fb/cyblafb/bugs deleted file mode 100644 index 9443a6d72cdd..000000000000 --- a/Documentation/fb/cyblafb/bugs +++ /dev/null @@ -1,13 +0,0 @@ -Bugs -==== - -I currently don't know of any bug. Please do send reports to: - - linux-fbdev-devel@lists.sourceforge.net - - Knut_Petersen@t-online.de. - - -Untested features -================= - -All LCD stuff is untested. If it worked in tridentfb, it should work in -cyblafb. Please test and report the results to Knut_Petersen@t-online.de. diff --git a/Documentation/fb/cyblafb/credits b/Documentation/fb/cyblafb/credits deleted file mode 100644 index 0eb3b443dc2b..000000000000 --- a/Documentation/fb/cyblafb/credits +++ /dev/null @@ -1,7 +0,0 @@ -Thanks to -========= - * Alan Hourihane, for writing the X trident driver - * Jani Monoses, for writing the tridentfb driver - * Antonino A. Daplas, for review of the first published - version of cyblafb and some code - * Jochen Hein, for testing and a helpfull bug report diff --git a/Documentation/fb/cyblafb/documentation b/Documentation/fb/cyblafb/documentation deleted file mode 100644 index bb1aac048425..000000000000 --- a/Documentation/fb/cyblafb/documentation +++ /dev/null @@ -1,17 +0,0 @@ -Available Documentation -======================= - -Apollo PLE 133 Chipset VT8601A North Bridge Datasheet, Rev. 1.82, October 22, -2001, available from VIA: - - http://www.viavpsd.com/product/6/15/DS8601A182.pdf - -The datasheet is incomplete, some registers that need to be programmed are not -explained at all and important bits are listed as "reserved". But you really -need the datasheet to understand the code. "p. xxx" comments refer to page -numbers of this document. - -XFree/XOrg drivers are available and of good quality, looking at the code -there is a good idea if the datasheet does not provide enough information -or if the datasheet seems to be wrong. - diff --git a/Documentation/fb/cyblafb/fb.modes b/Documentation/fb/cyblafb/fb.modes deleted file mode 100644 index fe0e5223ba86..000000000000 --- a/Documentation/fb/cyblafb/fb.modes +++ /dev/null @@ -1,154 +0,0 @@ -# -# Sample fb.modes file -# -# Provides an incomplete list of working modes for -# the cyberblade/i1 graphics core. -# -# The value 4294967256 is used instead of -40. Of course, -40 is not -# a really reasonable value, but chip design does not always follow -# logic. Believe me, it's ok, and it's the way the BIOS does it. -# -# fbset requires 4294967256 in fb.modes and -40 as an argument to -# the -t parameter. That's also not too reasonable, and it might change -# in the future or might even be differt for your current version. -# - -mode "640x480-50" - geometry 640 480 2048 4096 8 - timings 47619 4294967256 24 17 0 216 3 -endmode - -mode "640x480-60" - geometry 640 480 2048 4096 8 - timings 39682 4294967256 24 17 0 216 3 -endmode - -mode "640x480-70" - geometry 640 480 2048 4096 8 - timings 34013 4294967256 24 17 0 216 3 -endmode - -mode "640x480-72" - geometry 640 480 2048 4096 8 - timings 33068 4294967256 24 17 0 216 3 -endmode - -mode "640x480-75" - geometry 640 480 2048 4096 8 - timings 31746 4294967256 24 17 0 216 3 -endmode - -mode "640x480-80" - geometry 640 480 2048 4096 8 - timings 29761 4294967256 24 17 0 216 3 -endmode - -mode "640x480-85" - geometry 640 480 2048 4096 8 - timings 28011 4294967256 24 17 0 216 3 -endmode - -mode "800x600-50" - geometry 800 600 2048 4096 8 - timings 30303 96 24 14 0 136 11 -endmode - -mode "800x600-60" - geometry 800 600 2048 4096 8 - timings 25252 96 24 14 0 136 11 -endmode - -mode "800x600-70" - geometry 800 600 2048 4096 8 - timings 21645 96 24 14 0 136 11 -endmode - -mode "800x600-72" - geometry 800 600 2048 4096 8 - timings 21043 96 24 14 0 136 11 -endmode - -mode "800x600-75" - geometry 800 600 2048 4096 8 - timings 20202 96 24 14 0 136 11 -endmode - -mode "800x600-80" - geometry 800 600 2048 4096 8 - timings 18939 96 24 14 0 136 11 -endmode - -mode "800x600-85" - geometry 800 600 2048 4096 8 - timings 17825 96 24 14 0 136 11 -endmode - -mode "1024x768-50" - geometry 1024 768 2048 4096 8 - timings 19054 144 24 29 0 120 3 -endmode - -mode "1024x768-60" - geometry 1024 768 2048 4096 8 - timings 15880 144 24 29 0 120 3 -endmode - -mode "1024x768-70" - geometry 1024 768 2048 4096 8 - timings 13610 144 24 29 0 120 3 -endmode - -mode "1024x768-72" - geometry 1024 768 2048 4096 8 - timings 13232 144 24 29 0 120 3 -endmode - -mode "1024x768-75" - geometry 1024 768 2048 4096 8 - timings 12703 144 24 29 0 120 3 -endmode - -mode "1024x768-80" - geometry 1024 768 2048 4096 8 - timings 11910 144 24 29 0 120 3 -endmode - -mode "1024x768-85" - geometry 1024 768 2048 4096 8 - timings 11209 144 24 29 0 120 3 -endmode - -mode "1280x1024-50" - geometry 1280 1024 2048 4096 8 - timings 11114 232 16 39 0 160 3 -endmode - -mode "1280x1024-60" - geometry 1280 1024 2048 4096 8 - timings 9262 232 16 39 0 160 3 -endmode - -mode "1280x1024-70" - geometry 1280 1024 2048 4096 8 - timings 7939 232 16 39 0 160 3 -endmode - -mode "1280x1024-72" - geometry 1280 1024 2048 4096 8 - timings 7719 232 16 39 0 160 3 -endmode - -mode "1280x1024-75" - geometry 1280 1024 2048 4096 8 - timings 7410 232 16 39 0 160 3 -endmode - -mode "1280x1024-80" - geometry 1280 1024 2048 4096 8 - timings 6946 232 16 39 0 160 3 -endmode - -mode "1280x1024-85" - geometry 1280 1024 2048 4096 8 - timings 6538 232 16 39 0 160 3 -endmode diff --git a/Documentation/fb/cyblafb/performance b/Documentation/fb/cyblafb/performance deleted file mode 100644 index 8d15d5dfc6b3..000000000000 --- a/Documentation/fb/cyblafb/performance +++ /dev/null @@ -1,79 +0,0 @@ -Speed -===== - -CyBlaFB is much faster than tridentfb and vesafb. Compare the performance data -for mode 1280x1024-[8,16,32]@61 Hz. - -Test 1: Cat a file with 2000 lines of 0 characters. -Test 2: Cat a file with 2000 lines of 80 characters. -Test 3: Cat a file with 2000 lines of 160 characters. - -All values show system time use in seconds, kernel 2.6.12 was used for -the measurements. 2.6.13 is a bit slower, 2.6.14 hopefully will include a -patch that speeds up kernel bitblitting a lot ( > 20%). - -+-----------+-----------------------------------------------------+ -| | not accelerated | -| TRIDENTFB +-----------------+-----------------+-----------------+ -| of 2.6.12 | 8 bpp | 16 bpp | 32 bpp | -| | noypan | ypan | noypan | ypan | noypan | ypan | -+-----------+--------+--------+--------+--------+--------+--------+ -| Test 1 | 4.31 | 4.33 | 6.05 | 12.81 | ---- | ---- | -| Test 2 | 67.94 | 5.44 | 123.16 | 14.79 | ---- | ---- | -| Test 3 | 131.36 | 6.55 | 240.12 | 16.76 | ---- | ---- | -+-----------+--------+--------+--------+--------+--------+--------+ -| Comments | | | completely bro- | -| | | | ken, monitor | -| | | | switches off | -+-----------+-----------------+-----------------+-----------------+ - - -+-----------+-----------------------------------------------------+ -| | accelerated | -| TRIDENTFB +-----------------+-----------------+-----------------+ -| of 2.6.12 | 8 bpp | 16 bpp | 32 bpp | -| | noypan | ypan | noypan | ypan | noypan | ypan | -+-----------+--------+--------+--------+--------+--------+--------+ -| Test 1 | ---- | ---- | 20.62 | 1.22 | ---- | ---- | -| Test 2 | ---- | ---- | 22.61 | 3.19 | ---- | ---- | -| Test 3 | ---- | ---- | 24.59 | 5.16 | ---- | ---- | -+-----------+--------+--------+--------+--------+--------+--------+ -| Comments | broken, writing | broken, ok only | completely bro- | -| | to wrong places | if bgcolor is | ken, monitor | -| | on screen + bug | black, bug in | switches off | -| | in fillrect() | fillrect() | | -+-----------+-----------------+-----------------+-----------------+ - - -+-----------+-----------------------------------------------------+ -| | not accelerated | -| VESAFB +-----------------+-----------------+-----------------+ -| of 2.6.12 | 8 bpp | 16 bpp | 32 bpp | -| | noypan | ypan | noypan | ypan | noypan | ypan | -+-----------+--------+--------+--------+--------+--------+--------+ -| Test 1 | 4.26 | 3.76 | 5.99 | 7.23 | ---- | ---- | -| Test 2 | 65.65 | 4.89 | 120.88 | 9.08 | ---- | ---- | -| Test 3 | 126.91 | 5.94 | 235.77 | 11.03 | ---- | ---- | -+-----------+--------+--------+--------+--------+--------+--------+ -| Comments | vga=0x307 | vga=0x31a | vga=0x31b not | -| | fh=80kHz | fh=80kHz | supported by | -| | fv=75kHz | fv=75kHz | video BIOS and | -| | | | hardware | -+-----------+-----------------+-----------------+-----------------+ - - -+-----------+-----------------------------------------------------+ -| | accelerated | -| CYBLAFB +-----------------+-----------------+-----------------+ -| | 8 bpp | 16 bpp | 32 bpp | -| | noypan | ypan | noypan | ypan | noypan | ypan | -+-----------+--------+--------+--------+--------+--------+--------+ -| Test 1 | 8.02 | 0.23 | 19.04 | 0.61 | 57.12 | 2.74 | -| Test 2 | 8.38 | 0.55 | 19.39 | 0.92 | 57.54 | 3.13 | -| Test 3 | 8.73 | 0.86 | 19.74 | 1.24 | 57.95 | 3.51 | -+-----------+--------+--------+--------+--------+--------+--------+ -| Comments | | | | -| | | | | -| | | | | -| | | | | -+-----------+-----------------+-----------------+-----------------+ diff --git a/Documentation/fb/cyblafb/todo b/Documentation/fb/cyblafb/todo deleted file mode 100644 index c5f6d0eae545..000000000000 --- a/Documentation/fb/cyblafb/todo +++ /dev/null @@ -1,31 +0,0 @@ -TODO / Missing features -======================= - -Verify LCD stuff "stretch" and "center" options are - completely untested ... this code needs to be - verified. As I don't have access to such - hardware, please contact me if you are - willing run some tests. - -Interlaced video modes The reason that interleaved - modes are disabled is that I do not know - the meaning of the vertical interlace - parameter. Also the datasheet mentions a - bit d8 of a horizontal interlace parameter, - but nowhere the lower 8 bits. Please help - if you can. - -low-res double scan modes Who needs it? - -accelerated color blitting Who needs it? The console driver does use color - blitting for nothing but drawing the penguine, - everything else is done using color expanding - blitting of 1bpp character bitmaps. - -ioctls Who needs it? - -TV-out Will be done later. Use "vga= " at boot time - to set a suitable video mode. - -??? Feel free to contact me if you have any - feature requests diff --git a/Documentation/fb/cyblafb/usage b/Documentation/fb/cyblafb/usage deleted file mode 100644 index a39bb3d402a2..000000000000 --- a/Documentation/fb/cyblafb/usage +++ /dev/null @@ -1,217 +0,0 @@ -CyBlaFB is a framebuffer driver for the Cyberblade/i1 graphics core integrated -into the VIA Apollo PLE133 (aka vt8601) south bridge. It is developed and -tested using a VIA EPIA 5000 board. - -Cyblafb - compiled into the kernel or as a module? -================================================== - -You might compile cyblafb either as a module or compile it permanently into the -kernel. - -Unless you have a real reason to do so you should not compile both vesafb and -cyblafb permanently into the kernel. It's possible and it helps during the -developement cycle, but it's useless and will at least block some otherwise -usefull memory for ordinary users. - -Selecting Modes -=============== - - Startup Mode - ============ - - First of all, you might use the "vga=???" boot parameter as it is - documented in vesafb.txt and svga.txt. Cyblafb will detect the video - mode selected and will use the geometry and timings found by - inspecting the hardware registers. - - video=cyblafb vga=0x317 - - Alternatively you might use a combination of the mode, ref and bpp - parameters. If you compiled the driver into the kernel, add something - like this to the kernel command line: - - video=cyblafb:1280x1024,bpp=16,ref=50 ... - - If you compiled the driver as a module, the same mode would be - selected by the following command: - - modprobe cyblafb mode=1280x1024 bpp=16 ref=50 ... - - None of the modes possible to select as startup modes are affected by - the problems described at the end of the next subsection. - - For all startup modes cyblafb chooses a virtual x resolution of 2048, - the only exception is mode 1280x1024 in combination with 32 bpp. This - allows ywrap scrolling for all those modes if rotation is 0 or 2, and - also fast scrolling if rotation is 1 or 3. The default virtual y reso- - lution is 4096 for bpp == 8, 2048 for bpp==16 and 1024 for bpp == 32, - again with the only exception of 1280x1024 at 32 bpp. - - Please do set your video memory size to 8 Mb in the Bios setup. Other - values will work, but performace is decreased for a lot of modes. - - Mode changes using fbset - ======================== - - You might use fbset to change the video mode, see "man fbset". Cyblafb - generally does assume that you know what you are doing. But it does - some checks, especially those that are needed to prevent you from - damaging your hardware. - - - only 8, 16, 24 and 32 bpp video modes are accepted - - interlaced video modes are not accepted - - double scan video modes are not accepted - - if a flat panel is found, cyblafb does not allow you - to program a resolution higher than the physical - resolution of the flat panel monitor - - cyblafb does not allow vclk to exceed 230 MHz. As 32 bpp - and (currently) 24 bit modes use a doubled vclk internally, - the dotclock limit as seen by fbset is 115 MHz for those - modes and 230 MHz for 8 and 16 bpp modes. - - cyblafb will allow you to select very high resolutions as - long as the hardware can be programmed to these modes. The - documented limit 1600x1200 is not enforced, but don't expect - perfect signal quality. - - Any request that violates the rules given above will be either changed - to something the hardware supports or an error value will be returned. - - If you program a virtual y resolution higher than the hardware limit, - cyblafb will silently decrease that value to the highest possible - value. The same is true for a virtual x resolution that is not - supported by the hardware. Cyblafb tries to adapt vyres first because - vxres decides if ywrap scrolling is possible or not. - - Attempts to disable acceleration are ignored, I believe that this is - safe. - - Some video modes that should work do not work as expected. If you use - the standard fb.modes, fbset 640x480-60 will program that mode, but - you will see a vertical area, about two characters wide, with only - much darker characters than the other characters on the screen. - Cyblafb does allow that mode to be set, as it does not violate the - official specifications. It would need a lot of code to reliably sort - out all invalid modes, playing around with the margin values will - give a valid mode quickly. And if cyblafb would detect such an invalid - mode, should it silently alter the requested values or should it - report an error? Both options have some pros and cons. As stated - above, none of the startup modes are affected, and if you set - verbosity to 1 or higher, cyblafb will print the fbset command that - would be needed to program that mode using fbset. - - -Other Parameters -================ - - -crt don't autodetect, assume monitor connected to - standard VGA connector - -fp don't autodetect, assume flat panel display - connected to flat panel monitor interface - -nativex inform driver about native x resolution of - flat panel monitor connected to special - interface (should be autodetected) - -stretch stretch image to adapt low resolution modes to - higer resolutions of flat panel monitors - connected to special interface - -center center image to adapt low resolution modes to - higer resolutions of flat panel monitors - connected to special interface - -memsize use if autodetected memsize is wrong ... - should never be necessary - -nopcirr disable PCI read retry -nopciwr disable PCI write retry -nopcirb disable PCI read bursts -nopciwb disable PCI write bursts - -bpp bpp for specified modes - valid values: 8 || 16 || 24 || 32 - -ref refresh rate for specified mode - valid values: 50 <= ref <= 85 - -mode 640x480 or 800x600 or 1024x768 or 1280x1024 - if not specified, the startup mode will be detected - and used, so you might also use the vga=??? parameter - described in vesafb.txt. If you do not specify a mode, - bpp and ref parameters are ignored. - -verbosity 0 is the default, increase to at least 2 for every - bug report! - -Development hints -================= - -It's much faster do compile a module and to load the new version after -unloading the old module than to compile a new kernel and to reboot. So if you -try to work on cyblafb, it might be a good idea to use cyblafb as a module. -In real life, fast often means dangerous, and that's also the case here. If -you introduce a serious bug when cyblafb is compiled into the kernel, the -kernel will lock or oops with a high probability before the file system is -mounted, and the danger for your data is low. If you load a broken own version -of cyblafb on a running system, the danger for the integrity of the file -system is much higher as you might need a hard reset afterwards. Decide -yourself. - -Module unloading, the vfb method -================================ - -If you want to unload/reload cyblafb using the virtual framebuffer, you need -to enable vfb support in the kernel first. After that, load the modules as -shown below: - - modprobe vfb vfb_enable=1 - modprobe fbcon - modprobe cyblafb - fbset -fb /dev/fb1 1280x1024-60 -vyres 2662 - con2fb /dev/fb1 /dev/tty1 - ... - -If you now made some changes to cyblafb and want to reload it, you might do it -as show below: - - con2fb /dev/fb0 /dev/tty1 - ... - rmmod cyblafb - modprobe cyblafb - con2fb /dev/fb1 /dev/tty1 - ... - -Of course, you might choose another mode, and most certainly you also want to -map some other /dev/tty* to the real framebuffer device. You might also choose -to compile fbcon as a kernel module or place it permanently in the kernel. - -I do not know of any way to unload fbcon, and fbcon will prevent the -framebuffer device loaded first from unloading. [If there is a way, then -please add a description here!] - -Module unloading, the vesafb method -=================================== - -Configure the kernel: - - <*> Support for frame buffer devices - [*] VESA VGA graphics support - Cyberblade/i1 support - -Add e.g. "video=vesafb:ypan vga=0x307" to the kernel parameters. The ypan -parameter is important, choose any vga parameter you like as long as it is -a graphics mode. - -After booting, load cyblafb without any mode and bpp parameter and assign -cyblafb to individual ttys using con2fb, e.g.: - - modprobe cyblafb - con2fb /dev/fb1 /dev/tty1 - -Unloading cyblafb works without problems after you assign vesafb to all -ttys again, e.g.: - - con2fb /dev/fb0 /dev/tty1 - rmmod cyblafb diff --git a/Documentation/fb/cyblafb/whatsnew b/Documentation/fb/cyblafb/whatsnew deleted file mode 100644 index 76c07a26e044..000000000000 --- a/Documentation/fb/cyblafb/whatsnew +++ /dev/null @@ -1,29 +0,0 @@ -0.62 -==== - - - the vesafb parameter has been removed as I decided to allow the - feature without any special parameter. - - - Cyblafb does not use the vga style of panning any longer, now the - "right view" register in the graphics engine IO space is used. Without - that change it was impossible to use all available memory, and without - access to all available memory it is impossible to ywrap. - - - The imageblit function now uses hardware acceleration for all font - widths. Hardware blitting across pixel column 2048 is broken in the - cyberblade/i1 graphics core, but we work around that hardware bug. - - - modes with vxres != xres are supported now. - - - ywrap scrolling is supported now and the default. This is a big - performance gain. - - - default video modes use vyres > yres and vxres > xres to allow - almost optimal scrolling speed for normal and rotated screens - - - some features mainly usefull for debugging the upper layers of the - framebuffer system have been added, have a look at the code - - - fixed: Oops after unloading cyblafb when reading /proc/io* - - - we work around some bugs of the higher framebuffer layers. diff --git a/Documentation/fb/cyblafb/whycyblafb b/Documentation/fb/cyblafb/whycyblafb deleted file mode 100644 index a123bc11e698..000000000000 --- a/Documentation/fb/cyblafb/whycyblafb +++ /dev/null @@ -1,85 +0,0 @@ -I tried the following framebuffer drivers: - - - TRIDENTFB is full of bugs. Acceleration is broken for Blade3D - graphics cores like the cyberblade/i1. It claims to support a great - number of devices, but documentation for most of these devices is - unfortunately not available. There is _no_ reason to use tridentfb - for cyberblade/i1 + CRT users. VESAFB is faster, and the one - advantage, mode switching, is broken in tridentfb. - - - VESAFB is used by many distributions as a standard. Vesafb does - not support mode switching. VESAFB is a bit faster than the working - configurations of TRIDENTFB, but it is still too slow, even if you - use ypan. - - - EPIAFB (you'll find it on sourceforge) supports the Cyberblade/i1 - graphics core, but it still has serious bugs and developement seems - to have stopped. This is the one driver with TV-out support. If you - do need this feature, try epiafb. - -None of these drivers was a real option for me. - -I believe that is unreasonable to change code that announces to support 20 -devices if I only have more or less sufficient documentation for exactly one -of these. The risk of breaking device foo while fixing device bar is too high. - -So I decided to start CyBlaFB as a stripped down tridentfb. - -All code specific to other Trident chips has been removed. After that there -were a lot of cosmetic changes to increase the readability of the code. All -register names were changed to those mnemonics used in the datasheet. Function -and macro names were changed if they hindered easy understanding of the code. - -After that I debugged the code and implemented some new features. I'll try to -give a little summary of the main changes: - - - calculation of vertical and horizontal timings was fixed - - - video signal quality has been improved dramatically - - - acceleration: - - - fillrect and copyarea were fixed and reenabled - - - color expanding imageblit was newly implemented, color - imageblit (only used to draw the penguine) still uses the - generic code. - - - init of the acceleration engine was improved and moved to a - place where it really works ... - - - sync function has a timeout now and tries to reset and - reinit the accel engine if necessary - - - fewer slow copyarea calls when doing ypan scrolling by using - undocumented bit d21 of screen start address stored in - CR2B[5]. BIOS does use it also, so this should be safe. - - - cyblafb rejects any attempt to set modes that would cause vclk - values above reasonable 230 MHz. 32bit modes use a clock - multiplicator of 2, so fbset does show the correct values for - pixclock but not for vclk in this case. The fbset limit is 115 MHz - for 32 bpp modes. - - - cyblafb rejects modes known to be broken or unimplemented (all - interlaced modes, all doublescan modes for now) - - - cyblafb now works independant of the video mode in effect at startup - time (tridentfb does not init all needed registers to reasonable - values) - - - switching between video modes does work reliably now - - - the first video mode now is the one selected on startup using the - vga=???? mechanism or any of - - 640x480, 800x600, 1024x768, 1280x1024 - - 8, 16, 24 or 32 bpp - - refresh between 50 Hz and 85 Hz, 1 Hz steps (1280x1024-32 - is limited to 63Hz) - - - pci retry and pci burst mode are settable (try to disable if you - experience latency problems) - - - built as a module cyblafb might be unloaded and reloaded using - the vfb module and con2vt or might be used together with vesafb - diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index c19f6feb4e53..db7a4f42edad 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -1597,30 +1597,6 @@ config FB_VT8623 Driver for CastleRock integrated graphics core in the VIA VT8623 [Apollo CLE266] chipset. -config FB_CYBLA - tristate "Cyberblade/i1 support" - depends on FB && PCI && X86_32 && !64BIT - select FB_CFB_IMAGEBLIT - ---help--- - This driver is supposed to support the Trident Cyberblade/i1 - graphics core integrated in the VIA VT8601A North Bridge, - also known as VIA Apollo PLE133. - - Status: - - Developed, tested and working on EPIA 5000 and EPIA 800. - - Does work reliable on all systems with CRT/LCD connected to - normal VGA ports. - - Should work on systems that do use the internal LCD port, but - this is absolutely not tested. - - Character imageblit, copyarea and rectangle fill are hw accelerated, - ypan scrolling is used by default. - - Please do read . - - To compile this driver as a module, choose M here: the - module will be called cyblafb. - config FB_TRIDENT tristate "Trident support" depends on FB && PCI @@ -1633,8 +1609,8 @@ config FB_TRIDENT and Blade XP. There are also integrated versions of these chips called CyberXXXX, CyberImage or CyberBlade. These chips are mostly found in laptops - but also on some motherboards. For more information, read - + but also on some motherboards including early VIA EPIA motherboards. + For more information, read Say Y if you have such a graphics board. diff --git a/drivers/video/cyblafb.c b/drivers/video/cyblafb.c deleted file mode 100644 index 9704b73135f5..000000000000 --- a/drivers/video/cyblafb.c +++ /dev/null @@ -1,1683 +0,0 @@ -/* - * Frame buffer driver for Trident Cyberblade/i1 graphics core - * - * Copyright 2005 Knut Petersen - * - * CREDITS: - * tridentfb.c by Jani Monoses - * see files above for further credits - * - */ - -#define CYBLAFB_DEBUG 0 -#define CYBLAFB_KD_GRAPHICS_QUIRK 1 - -#define CYBLAFB_PIXMAPSIZE 8192 - -#include -#include -#include -#include -#include -#include -#include