diff options
Diffstat (limited to 'drivers')
309 files changed, 15434 insertions, 6386 deletions
diff --git a/drivers/acpi/acpi_memhotplug.c b/drivers/acpi/acpi_memhotplug.c index 7a0f4aa4fa1..9a62224cc27 100644 --- a/drivers/acpi/acpi_memhotplug.c +++ b/drivers/acpi/acpi_memhotplug.c @@ -38,6 +38,9 @@ #define _COMPONENT ACPI_MEMORY_DEVICE_COMPONENT +#undef PREFIX +#define PREFIX "ACPI:memory_hp:" + ACPI_MODULE_NAME("acpi_memhotplug"); MODULE_AUTHOR("Naveen B S <naveen.b.s@intel.com>"); MODULE_DESCRIPTION("Hotplug Mem Driver"); @@ -153,6 +156,7 @@ acpi_memory_get_device(acpi_handle handle, acpi_handle phandle; struct acpi_device *device = NULL; struct acpi_device *pdevice = NULL; + int result; if (!acpi_bus_get_device(handle, &device) && device) @@ -165,9 +169,9 @@ acpi_memory_get_device(acpi_handle handle, } /* Get the parent device */ - status = acpi_bus_get_device(phandle, &pdevice); - if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, "Cannot get acpi bus device")); + result = acpi_bus_get_device(phandle, &pdevice); + if (result) { + printk(KERN_WARNING PREFIX "Cannot get acpi bus device"); return -EINVAL; } @@ -175,9 +179,9 @@ acpi_memory_get_device(acpi_handle handle, * Now add the notified device. This creates the acpi_device * and invokes .add function */ - status = acpi_bus_add(&device, pdevice, handle, ACPI_BUS_TYPE_DEVICE); - if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, "Cannot add acpi bus")); + result = acpi_bus_add(&device, pdevice, handle, ACPI_BUS_TYPE_DEVICE); + if (result) { + printk(KERN_WARNING PREFIX "Cannot add acpi bus"); return -EINVAL; } @@ -238,7 +242,12 @@ static int acpi_memory_enable_device(struct acpi_memory_device *mem_device) num_enabled++; continue; } - + /* + * If the memory block size is zero, please ignore it. + * Don't try to do the following memory hotplug flowchart. + */ + if (!info->length) + continue; if (node < 0) node = memory_add_physaddr_to_nid(info->start_addr); @@ -253,8 +262,15 @@ static int acpi_memory_enable_device(struct acpi_memory_device *mem_device) mem_device->state = MEMORY_INVALID_STATE; return -EINVAL; } - - return result; + /* + * Sometimes the memory device will contain several memory blocks. + * When one memory block is hot-added to the system memory, it will + * be regarded as a success. + * Otherwise if the last memory block can't be hot-added to the system + * memory, it will be failure and the memory device can't be bound with + * driver. + */ + return 0; } static int acpi_memory_powerdown_device(struct acpi_memory_device *mem_device) diff --git a/drivers/acpi/acpica/acobject.h b/drivers/acpi/acpica/acobject.h index 544dcf83492..eb6f038b03d 100644 --- a/drivers/acpi/acpica/acobject.h +++ b/drivers/acpi/acpica/acobject.h @@ -97,6 +97,7 @@ #define AOPOBJ_OBJECT_INITIALIZED 0x08 #define AOPOBJ_SETUP_COMPLETE 0x10 #define AOPOBJ_SINGLE_DATUM 0x20 +#define AOPOBJ_INVALID 0x40 /* Used if host OS won't allow an op_region address */ /****************************************************************************** * diff --git a/drivers/acpi/acpica/dsopcode.c b/drivers/acpi/acpica/dsopcode.c index 584d766e6f1..b79978f7bc7 100644 --- a/drivers/acpi/acpica/dsopcode.c +++ b/drivers/acpi/acpica/dsopcode.c @@ -397,6 +397,30 @@ acpi_status acpi_ds_get_region_arguments(union acpi_operand_object *obj_desc) status = acpi_ds_execute_arguments(node, acpi_ns_get_parent_node(node), extra_desc->extra.aml_length, extra_desc->extra.aml_start); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Validate the region address/length via the host OS */ + + status = acpi_os_validate_address(obj_desc->region.space_id, + obj_desc->region.address, + (acpi_size) obj_desc->region.length, + acpi_ut_get_node_name(node)); + + if (ACPI_FAILURE(status)) { + /* + * Invalid address/length. We will emit an error message and mark + * the region as invalid, so that it will cause an additional error if + * it is ever used. Then return AE_OK. + */ + ACPI_EXCEPTION((AE_INFO, status, + "During address validation of OpRegion [%4.4s]", + node->name.ascii)); + obj_desc->common.flags |= AOPOBJ_INVALID; + status = AE_OK; + } + return_ACPI_STATUS(status); } diff --git a/drivers/acpi/acpica/exfldio.c b/drivers/acpi/acpica/exfldio.c index d4075b82102..6687be167f5 100644 --- a/drivers/acpi/acpica/exfldio.c +++ b/drivers/acpi/acpica/exfldio.c @@ -113,6 +113,12 @@ acpi_ex_setup_region(union acpi_operand_object *obj_desc, } } + /* Exit if Address/Length have been disallowed by the host OS */ + + if (rgn_desc->common.flags & AOPOBJ_INVALID) { + return_ACPI_STATUS(AE_AML_ILLEGAL_ADDRESS); + } + /* * Exit now for SMBus address space, it has a non-linear address space * and the request cannot be directly validated diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 71670719d61..5691f165a95 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -189,11 +189,36 @@ acpi_status __init acpi_os_initialize(void) return AE_OK; } +static void bind_to_cpu0(struct work_struct *work) +{ + set_cpus_allowed(current, cpumask_of_cpu(0)); + kfree(work); +} + +static void bind_workqueue(struct workqueue_struct *wq) +{ + struct work_struct *work; + + work = kzalloc(sizeof(struct work_struct), GFP_KERNEL); + INIT_WORK(work, bind_to_cpu0); + queue_work(wq, work); +} + acpi_status acpi_os_initialize1(void) { + /* + * On some machines, a software-initiated SMI causes corruption unless + * the SMI runs on CPU 0. An SMI can be initiated by any AML, but + * typically it's done in GPE-related methods that are run via + * workqueues, so we can avoid the known corruption cases by binding + * the workqueues to CPU 0. + */ kacpid_wq = create_singlethread_workqueue("kacpid"); + bind_workqueue(kacpid_wq); kacpi_notify_wq = create_singlethread_workqueue("kacpi_notify"); + bind_workqueue(kacpi_notify_wq); kacpi_hotplug_wq = create_singlethread_workqueue("kacpi_hotplug"); + bind_workqueue(kacpi_hotplug_wq); BUG_ON(!kacpid_wq); BUG_ON(!kacpi_notify_wq); BUG_ON(!kacpi_hotplug_wq); diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index 01574a06653..42159a28f43 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -397,6 +397,14 @@ static struct dmi_system_id __initdata acpisleep_dmi_table[] = { }, }, { + .callback = init_set_sci_en_on_resume, + .ident = "Hewlett-Packard HP G7000 Notebook PC", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP G7000 Notebook PC"), + }, + }, + { .callback = init_old_suspend_ordering, .ident = "Panasonic CF51-2L", .matches = { diff --git a/drivers/acpi/system.c b/drivers/acpi/system.c index 0944daec064..9c61ab2177c 100644 --- a/drivers/acpi/system.c +++ b/drivers/acpi/system.c @@ -121,7 +121,7 @@ static void acpi_table_attr_init(struct acpi_table_attr *table_attr, table_attr->attr.size = 0; table_attr->attr.read = acpi_table_show; table_attr->attr.attr.name = table_attr->name; - table_attr->attr.attr.mode = 0444; + table_attr->attr.attr.mode = 0400; return; } diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 336eb1ed73c..958c1fa4190 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -515,10 +515,14 @@ static const struct pci_device_id ahci_pci_tbl[] = { { PCI_VDEVICE(INTEL, 0x3a05), board_ahci }, /* ICH10 */ { PCI_VDEVICE(INTEL, 0x3a22), board_ahci }, /* ICH10 */ { PCI_VDEVICE(INTEL, 0x3a25), board_ahci }, /* ICH10 */ + { PCI_VDEVICE(INTEL, 0x3b22), board_ahci }, /* PCH AHCI */ + { PCI_VDEVICE(INTEL, 0x3b23), board_ahci }, /* PCH AHCI */ { PCI_VDEVICE(INTEL, 0x3b24), board_ahci }, /* PCH RAID */ { PCI_VDEVICE(INTEL, 0x3b25), board_ahci }, /* PCH RAID */ + { PCI_VDEVICE(INTEL, 0x3b29), board_ahci }, /* PCH AHCI */ { PCI_VDEVICE(INTEL, 0x3b2b), board_ahci }, /* PCH RAID */ { PCI_VDEVICE(INTEL, 0x3b2c), board_ahci }, /* PCH RAID */ + { PCI_VDEVICE(INTEL, 0x3b2f), board_ahci }, /* PCH AHCI */ /* JMicron 360/1/3/5/6, match class to avoid IDE function */ { PCI_VENDOR_ID_JMICRON, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, diff --git a/drivers/ata/ata_piix.c b/drivers/ata/ata_piix.c index d0a14cf2bd7..56b8a3ff128 100644 --- a/drivers/ata/ata_piix.c +++ b/drivers/ata/ata_piix.c @@ -596,9 +596,12 @@ static const struct ich_laptop ich_laptop[] = { { 0x27DF, 0x0005, 0x0280 }, /* ICH7 on Acer 5602WLMi */ { 0x27DF, 0x1025, 0x0102 }, /* ICH7 on Acer 5602aWLMi */ { 0x27DF, 0x1025, 0x0110 }, /* ICH7 on Acer 3682WLMi */ + { 0x27DF, 0x1028, 0x02b0 }, /* ICH7 on unknown Dell */ { 0x27DF, 0x1043, 0x1267 }, /* ICH7 on Asus W5F */ { 0x27DF, 0x103C, 0x30A1 }, /* ICH7 on HP Compaq nc2400 */ + { 0x27DF, 0x103C, 0x361a }, /* ICH7 on unkown HP */ { 0x27DF, 0x1071, 0xD221 }, /* ICH7 on Hercules EC-900 */ + { 0x27DF, 0x152D, 0x0778 }, /* ICH7 on unknown Intel */ { 0x24CA, 0x1025, 0x0061 }, /* ICH4 on ACER Aspire 2023WLMi */ { 0x24CA, 0x1025, 0x003d }, /* ICH4 on ACER TM290 */ { 0x266F, 0x1025, 0x0066 }, /* ICH6 on ACER Aspire 1694WLMi */ diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 2c6aedaef71..8ac98ff16d7 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -1515,6 +1515,7 @@ static int ata_hpa_resize(struct ata_device *dev) return rc; } + dev->n_native_sectors = native_sectors; /* nothing to do? */ if (native_sectors <= sectors || !ata_ignore_hpa) { @@ -4099,6 +4100,7 @@ int ata_dev_revalidate(struct ata_device *dev, unsigned int new_class, unsigned int readid_flags) { u64 n_sectors = dev->n_sectors; + u64 n_native_sectors = dev->n_native_sectors; int rc; if (!ata_dev_enabled(dev)) @@ -4128,16 +4130,30 @@ int ata_dev_revalidate(struct ata_device *dev, unsigned int new_class, /* verify n_sectors hasn't changed */ if (dev->class == ATA_DEV_ATA && n_sectors && dev->n_sectors != n_sectors) { - ata_dev_printk(dev, KERN_INFO, "n_sectors mismatch " + ata_dev_printk(dev, KERN_WARNING, "n_sectors mismatch " "%llu != %llu\n", (unsigned long long)n_sectors, (unsigned long long)dev->n_sectors); - - /* restore original n_sectors */ - dev->n_sectors = n_sectors; - - rc = -ENODEV; - goto fail; + /* + * Something could have caused HPA to be unlocked + * involuntarily. If n_native_sectors hasn't changed + * and the new size matches it, keep the device. + */ + if (dev->n_native_sectors == n_native_sectors && + dev->n_sectors > n_sectors && + dev->n_sectors == n_native_sectors) { + ata_dev_printk(dev, KERN_WARNING, + "new n_sectors matches native, probably " + "late HPA unlock, continuing\n"); + /* keep using the old n_sectors */ + dev->n_sectors = n_sectors; + } else { + /* restore original n_[native]_sectors and fail */ + dev->n_native_sectors = n_native_sectors; + dev->n_sectors = n_sectors; + rc = -ENODEV; + goto fail; + } } return 0; diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 1a07c061f64..79711b64054 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -2327,7 +2327,7 @@ int ata_eh_reset(struct ata_link *link, int classify, struct ata_port *ap = link->ap; struct ata_link *slave = ap->slave_link; struct ata_eh_context *ehc = &link->eh_context; - struct ata_eh_context *sehc = &slave->eh_context; + struct ata_eh_context *sehc = slave ? &slave->eh_context : NULL; unsigned int *classes = ehc->classes; unsigned int lflags = link->flags; int verbose = !(ehc->i.flags & ATA_EHI_QUIET); diff --git a/drivers/ata/pata_at91.c b/drivers/ata/pata_at91.c index 8561a9f195c..5702affcb32 100644 --- a/drivers/ata/pata_at91.c +++ b/drivers/ata/pata_at91.c @@ -26,9 +26,7 @@ #include <linux/platform_device.h> #include <linux/ata_platform.h> -#include <mach/at91sam9260_matrix.h> #include <mach/at91sam9_smc.h> -#include <mach/at91sam9260.h> #include <mach/board.h> #include <mach/gpio.h> @@ -44,65 +42,62 @@ struct at91_ide_info { unsigned long mode; unsigned int cs; + struct clk *mck; + void __iomem *ide_addr; void __iomem *alt_addr; }; -const struct ata_timing initial_timing = +static const struct ata_timing initial_timing = {XFER_PIO_0, 70, 290, 240, 600, 165, 150, 600, 0}; -static unsigned int calc_mck_cycles(unsigned int ns, unsigned int mck_hz) +static unsigned long calc_mck_cycles(unsigned long ns, unsigned long mck_hz) { unsigned long mul; - /* - * cycles = x [nsec] * f [Hz] / 10^9 [ns in sec] = - * x * (f / 1_000_000_000) = - * x * ((f * 65536) / 1_000_000_000) / 65536 = - * x * (((f / 10_000) * 65536) / 100_000) / 65536 = - */ + /* + * cycles = x [nsec] * f [Hz] / 10^9 [ns in sec] = + * x * (f / 1_000_000_000) = + * x * ((f * 65536) / 1_000_000_000) / 65536 = + * x * (((f / 10_000) * 65536) / 100_000) / 65536 = + */ - mul = (mck_hz / 10000) << 16; - mul /= 100000; + mul = (mck_hz / 10000) << 16; + mul /= 100000; - return (ns * mul + 65536) >> 16; /* rounding */ + return (ns * mul + 65536) >> 16; /* rounding */ } static void set_smc_mode(struct at91_ide_info *info) { - at91_sys_write(AT91_SMC_MODE(info->cs), info->mode); - return; + at91_sys_write(AT91_SMC_MODE(info->cs), info->mode); + return; } static void set_smc_timing(struct device *dev, struct at91_ide_info *info, const struct ata_timing *ata) { - int read_cycle, write_cycle, active, recover; - int nrd_setup, nrd_pulse, nrd_recover; - int nwe_setup, nwe_pulse; + unsigned long read_cycle, write_cycle, active, recover; + unsigned long nrd_setup, nrd_pulse, nrd_recover; + unsigned long nwe_setup, nwe_pulse; - int ncs_write_setup, ncs_write_pulse; - int ncs_read_setup, ncs_read_pulse; + unsigned long ncs_write_setup, ncs_write_pulse; + unsigned long ncs_read_setup, ncs_read_pulse; - unsigned int mck_hz; - struct clk *mck; + unsigned long mck_hz; read_cycle = ata->cyc8b; nrd_setup = ata->setup; nrd_pulse = ata->act8b; nrd_recover = ata->rec8b; - mck = clk_get(NULL, "mck"); - BUG_ON(IS_ERR(mck)); - mck_hz = clk_get_rate(mck); + mck_hz = clk_get_rate(info->mck); read_cycle = calc_mck_cycles(read_cycle, mck_hz); nrd_setup = calc_mck_cycles(nrd_setup, mck_hz); nrd_pulse = calc_mck_cycles(nrd_pulse, mck_hz); nrd_recover = calc_mck_cycles(nrd_recover, mck_hz); - clk_put(mck); - active = nrd_setup + nrd_pulse; recover = read_cycle - active; @@ -121,13 +116,13 @@ static void set_smc_timing(struct device *dev, ncs_write_setup = ncs_read_setup; ncs_write_pulse = ncs_read_pulse; - dev_dbg(dev, "ATA timings: nrd_setup = %d nrd_pulse = %d nrd_cycle = %d\n", + dev_dbg(dev, "ATA timings: nrd_setup = %lu nrd_pulse = %lu nrd_cycle = %lu\n", nrd_setup, nrd_pulse, read_cycle); - dev_dbg(dev, "ATA timings: nwe_setup = %d nwe_pulse = %d nwe_cycle = %d\n", + dev_dbg(dev, "ATA timings: nwe_setup = %lu nwe_pulse = %lu nwe_cycle = %lu\n", nwe_setup, nwe_pulse, write_cycle); - dev_dbg(dev, "ATA timings: ncs_read_setup = %d ncs_read_pulse = %d\n", + dev_dbg(dev, "ATA timings: ncs_read_setup = %lu ncs_read_pulse = %lu\n", ncs_read_setup, ncs_read_pulse); - dev_dbg(dev, "ATA timings: ncs_write_setup = %d ncs_write_pulse = %d\n", + dev_dbg(dev, "ATA timings: ncs_write_setup = %lu ncs_write_pulse = %lu\n", ncs_write_setup, ncs_write_pulse); at91_sys_write(AT91_SMC_SETUP(info->cs), @@ -217,6 +212,7 @@ static int __devinit pata_at91_probe(struct platform_device *pdev) struct resource *mem_res; struct ata_host *host; struct ata_port *ap; + int irq_flags = 0; int irq = 0; int ret; @@ -261,6 +257,13 @@ static int __devinit pata_at91_probe(struct platform_device *pdev) return -ENOMEM; } + info->mck = clk_get(NULL, "mck"); + + if (IS_ERR(info->mck)) { + dev_err(dev, "failed to get access to mck clock\n"); + return -ENODEV; + } + info->cs = board->chipselect; info->mode = AT91_SMC_READMODE | AT91_SMC_WRITEMODE | AT91_SMC_EXNWMODE_READY | AT91_SMC_BAT_SELECT | @@ -304,6 +307,7 @@ err_alt_ioremap: devm_iounmap(dev, info->ide_addr); err_ide_ioremap: + clk_put(info->mck); kfree(info); return ret; @@ -326,6 +330,7 @@ static int __devexit pata_at91_remove(struct platform_device *pdev) devm_iounmap(dev, info->ide_addr); devm_iounmap(dev, info->alt_addr); + clk_put(info->mck); kfree(info); return 0; diff --git a/drivers/ata/pata_octeon_cf.c b/drivers/ata/pata_octeon_cf.c index 8d9343accf3..abdd19fe990 100644 --- a/drivers/ata/pata_octeon_cf.c +++ b/drivers/ata/pata_octeon_cf.c @@ -653,7 +653,8 @@ static irqreturn_t octeon_cf_interrupt(int irq, void *dev_instance) ap = host->ports[i]; ocd = ap->dev->platform_data; - if (!ap || (ap->flags & ATA_FLAG_DISABLED)) + + if (ap->flags & ATA_FLAG_DISABLED) continue; ocd = ap->dev->platform_data; diff --git a/drivers/ata/pata_pcmcia.c b/drivers/ata/pata_pcmcia.c index f4d009ed50a..dc99e26f8e5 100644 --- a/drivers/ata/pata_pcmcia.c +++ b/drivers/ata/pata_pcmcia.c @@ -411,6 +411,7 @@ static struct pcmcia_device_id pcmcia_devices[] = { PCMCIA_DEVICE_PROD_ID123("PCMCIA", "IDE CARD", "F1", 0x281f1c5d, 0x1907960c, 0xf7fde8b9), PCMCIA_DEVICE_PROD_ID12("ARGOSY", "CD-ROM", 0x78f308dc, 0x66536591), PCMCIA_DEVICE_PROD_ID12("ARGOSY", "PnPIDE", 0x78f308dc, 0x0c694728), + PCMCIA_DEVICE_PROD_ID12("CNF ", "CD-ROM", 0x46d7db81, 0x66536591), PCMCIA_DEVICE_PROD_ID12("CNF CD-M", "CD-ROM", 0x7d93b852, 0x66536591), PCMCIA_DEVICE_PROD_ID12("Creative Technology Ltd.", "PCMCIA CD-ROM Interface Card", 0xff8c8a45, 0xfe8020c4), PCMCIA_DEVICE_PROD_ID12("Digital Equipment Corporation.", "Digital Mobile Media CD-ROM", 0x17692a66, 0xef1dcbde), diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index 23714aefb82..c19417e0220 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -2514,7 +2514,7 @@ static void mv_unexpected_intr(struct ata_port *ap, int edma_was_enabled) char *when = "idle"; ata_ehi_clear_desc(ehi); - if (!ap || (ap->flags & ATA_FLAG_DISABLED)) { + if (ap->flags & ATA_FLAG_DISABLED) { when = "disabled"; } else if (edma_was_enabled) { when = "EDMA enabled"; diff --git a/drivers/ata/sata_sil.c b/drivers/ata/sata_sil.c index 030ec079b18..35bd5cc7f28 100644 --- a/drivers/ata/sata_sil.c +++ b/drivers/ata/sata_sil.c @@ -532,7 +532,7 @@ static irqreturn_t sil_interrupt(int irq, void *dev_instance) struct ata_port *ap = host->ports[i]; u32 bmdma2 = readl(mmio_base + sil_port[ap->port_no].bmdma2); - if (unlikely(!ap || ap->flags & ATA_FLAG_DISABLED)) + if (unlikely(ap->flags & ATA_FLAG_DISABLED)) continue; /* turn off SATA_IRQ if not supported */ diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index f285f441fab..7376367bcb8 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -180,7 +180,6 @@ static ssize_t firmware_loading_store(struct device *dev, goto err; } /* Pages will be freed by vfree() */ - fw_priv->pages = NULL; fw_priv->page_array_size = 0; fw_priv->nr_pages = 0; complete(&fw_priv->completion); diff --git a/drivers/base/sys.c b/drivers/base/sys.c index 79a9ae5238a..0d903909af7 100644 --- a/drivers/base/sys.c +++ b/drivers/base/sys.c @@ -275,9 +275,9 @@ int sysdev_register(struct sys_device *sysdev) drv->add(sysdev); } mutex_unlock(&sysdev_drivers_lock); + kobject_uevent(&sysdev->kobj, KOBJ_ADD); } - kobject_uevent(&sysdev->kobj, KOBJ_ADD); return error; } diff --git a/drivers/block/ataflop.c b/drivers/block/ataflop.c index f5e7180d7f4..3ff02941b3d 100644 --- a/drivers/block/ataflop.c +++ b/drivers/block/ataflop.c @@ -1627,7 +1627,7 @@ static int fd_ioctl(struct block_device *bdev, fmode_t mode, drive, dtp->blocks, dtp->spt, dtp->stretch); /* sanity check */ - if (!dtp || setprm.track != dtp->blocks/dtp->spt/2 || + if (setprm.track != dtp->blocks/dtp->spt/2 || setprm.head != 2) { redo_fd_request(); return -EINVAL; diff --git a/drivers/block/mg_disk.c b/drivers/block/mg_disk.c index f703f547824..6d7fbaa9224 100644 --- a/drivers/block/mg_disk.c +++ b/drivers/block/mg_disk.c @@ -36,7 +36,6 @@ /* Register offsets */ #define MG_BUFF_OFFSET 0x8000 -#define MG_STORAGE_BUFFER_SIZE 0x200 #define MG_REG_OFFSET 0xC000 #define MG_REG_FEATURE (MG_REG_OFFSET + 2) /* write case */ #define MG_REG_ERROR (MG_REG_OFFSET + 2) /* read case */ @@ -219,6 +218,16 @@ static unsigned int mg_wait(struct mg_host *host, u32 expect, u32 msec) host->error = MG_ERR_NONE; expire = jiffies + msecs_to_jiffies(msec); + /* These 2 times dummy status read prevents reading invalid + * status. A very little time (3 times of mflash operating clk) + * is required for busy bit is set. Use dummy read instead of + * busy wait, because mflash's PLL is machine dependent. + */ + if (prv_data->use_polling) { + status = inb((unsigned long)host->dev_base + MG_REG_STATUS); + status = inb((unsigned long)host->dev_base + MG_REG_STATUS); + } + status = inb((unsigned long)host->dev_base + MG_REG_STATUS); do { @@ -245,8 +254,6 @@ static unsigned int mg_wait(struct mg_host *host, u32 expect, u32 msec) mg_dump_status("not ready", status, host); return MG_ERR_INV_STAT; } - if (prv_data->use_polling) - msleep(1); status = inb((unsigned long)host->dev_base + MG_REG_STATUS); } while (time_before(cur_jiffies, expire)); @@ -469,9 +476,18 @@ static unsigned int mg_out(struct mg_host *host, return MG_ERR_NONE; } +static void mg_read_one(struct mg_host *host, struct request *req) +{ + u16 *buff = (u16 *)req->buffer; + u32 i; + + for (i = 0; i < MG_SECTOR_SIZE >> 1; i++) + *buff++ = inw((unsigned long)host->dev_base + MG_BUFF_OFFSET + + (i << 1)); +} + static void mg_read(struct request *req) { - u32 j; struct mg_host *host = req->rq_disk->private_data; if (mg_out(host, blk_rq_pos(req), blk_rq_sectors(req), @@ -482,49 +498,65 @@ static void mg_read(struct request *req) blk_rq_sectors(req), blk_rq_pos(req), req->buffer); do { - u16 *buff = (u16 *)req->buffer; - if (mg_wait(host, ATA_DRQ, MG_TMAX_WAIT_RD_DRQ) != MG_ERR_NONE) { mg_bad_rw_intr(host); return; } - for (j = 0; j < MG_SECTOR_SIZE >> 1; j++) - *buff++ = inw((unsigned long)host->dev_base + - MG_BUFF_OFFSET + (j << 1)); + + mg_read_one(host, req); outb(MG_CMD_RD_CONF, (unsigned long)host->dev_base + MG_REG_COMMAND); } while (mg_end_request(host, 0, MG_SECTOR_SIZE)); } +static void mg_write_one(struct mg_host *host, struct request *req) +{ + u16 *buff = (u16 *)req->buffer; + u32 i; + + for (i = 0; i < MG_SECTOR_SIZE >> 1; i++) + outw(*buff++, (unsigned long)host->dev_base + MG_BUFF_OFFSET + + (i << 1)); +} + static void mg_write(struct request *req) { - u32 j; struct mg_host *host = req->rq_disk->private_data; + unsigned int rem = blk_rq_sectors(req); - if (mg_out(host, blk_rq_pos(req), blk_rq_sectors(req), + if (mg_out(host, blk_rq_pos(req), rem, MG_CMD_WR, NULL) != MG_ERR_NONE) { mg_bad_rw_intr(host); return; } MG_DBG("requested %d sects (from %ld), buffer=0x%p\n", - blk_rq_sectors(req), blk_rq_pos(req), req->buffer); + rem, blk_rq_pos(req), req->buffer); + + if (mg_wait(host, ATA_DRQ, + MG_TMAX_WAIT_WR_DRQ) != MG_ERR_NONE) { + mg_bad_rw_intr(host); + return; + } do { - u16 *buff = (u16 *)req->buffer; + mg_write_one(host, req); - if (mg_wait(host, ATA_DRQ, MG_TMAX_WAIT_WR_DRQ) != MG_ERR_NONE) { + outb(MG_CMD_WR_CONF, (unsigned long)host->dev_base + + MG_REG_COMMAND); + + rem--; + if (rem > 1 && mg_wait(host, ATA_DRQ, + MG_TMAX_WAIT_WR_DRQ) != MG_ERR_NONE) { + mg_bad_rw_intr(host); + return; + } else if (mg_wait(host, MG_STAT_READY, + MG_TMAX_WAIT_WR_DRQ) != MG_ERR_NONE) { mg_bad_rw_intr(host); return; } - for (j = 0; j < MG_SECTOR_SIZE >> 1; j++) - outw(*buff++, (unsigned long)host->dev_base + - MG_BUFF_OFFSET + (j << 1)); - - outb(MG_CMD_WR_CONF, (unsigned long)host->dev_base + - MG_REG_COMMAND); } while (mg_end_request(host, 0, MG_SECTOR_SIZE)); } @@ -532,7 +564,6 @@ static void mg_read_intr(struct mg_host *host) { struct request *req = host->req; u32 i; - u16 *buff; /* check status */ do { @@ -550,13 +581,7 @@ static void mg_read_intr(struct mg_host *host) return; ok_to_read: - /* get current segment of request */ - buff = (u16 *)req->buffer; - - /* read 1 sector */ - for (i = 0; i < MG_SECTOR_SIZE >> 1; i++) - *buff++ = inw((unsigned long)host->dev_base + MG_BUFF_OFFSET + - (i << 1)); + mg_read_one(host, req); MG_DBG("sector %ld, remaining=%ld, buffer=0x%p\n", blk_rq_pos(req), blk_rq_sectors(req) - 1, req->buffer); @@ -575,8 +600,7 @@ ok_to_read: static void mg_write_intr(struct mg_host *host) { struct request *req = host->req; - u32 i, j; - u16 *buff; + u32 i; bool rem; /* check status */ @@ -597,12 +621,7 @@ static void mg_write_intr(struct mg_host *host) ok_to_write: if ((rem = mg_end_request(host, 0, MG_SECTOR_SIZE))) { /* write 1 sector and set handler if remains */ - buff = (u16 *)req->buffer; - for (j = 0; j < MG_STORAGE_BUFFER_SIZE >> 1; j++) { - outw(*buff, (unsigned long)host->dev_base + - MG_BUFF_OFFSET + (j << 1)); - buff++; - } + mg_write_one(host, req); MG_DBG("sector %ld, remaining=%ld, buffer=0x%p\n", blk_rq_pos(req), blk_rq_sectors(req), req->buffer); host->mg_do_intr = mg_write_intr; @@ -667,9 +686,6 @@ static unsigned int mg_issue_req(struct request *req, unsigned int sect_num, unsigned int sect_cnt) { - u16 *buff; - u32 i; - switch (rq_data_dir(req)) { case READ: if (mg_out(host, sect_num, sect_cnt, MG_CMD_RD, &mg_read_intr) @@ -693,12 +709,7 @@ static unsigned int mg_issue_req(struct request *req, mg_bad_rw_intr(host); return host->error; } - buff = (u16 *)req->buffer; - for (i = 0; i < MG_SECTOR_SIZE >> 1; i++) { - outw(*buff, (unsigned long)host->dev_base + - MG_BUFF_OFFSET + (i << 1)); - buff++; - } + mg_write_one(host, req); mod_timer(&host->timer, jiffies + 3 * HZ); outb(MG_CMD_WR_CONF, (unsigned long)host->dev_base + MG_REG_COMMAND); diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index 43db3ea15b5..aa1a3d5a3e2 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -213,7 +213,7 @@ static int virtblk_ioctl(struct block_device *bdev, fmode_t mode, * Only allow the generic SCSI ioctls if the host can support it. */ if (!virtio_has_feature(vblk->vdev, VIRTIO_BLK_F_SCSI)) - return -ENOIOCTLCMD; + return -ENOTTY; return scsi_cmd_ioctl(disk->queue, disk, mode, cmd, argp); } @@ -360,6 +360,9 @@ static int __devinit virtblk_probe(struct virtio_device *vdev) blk_queue_max_phys_segments(vblk->disk->queue, vblk->sg_elems-2); blk_queue_max_hw_segments(vblk->disk->queue, vblk->sg_elems-2); + /* No need to bounce any requests */ + blk_queue_bounce_limit(vblk->disk->queue, BLK_BOUNCE_ANY); + /* No real sector limit. */ blk_queue_max_sectors(vblk->disk->queue, -1U); @@ -424,7 +427,12 @@ static unsigned int features[] = { VIRTIO_BLK_F_SCSI, VIRTIO_BLK_F_IDENTIFY }; -static struct virtio_driver virtio_blk = { +/* + * virtio_blk causes spurious section mismatch warning by + * simultaneously referring to a __devinit and a __devexit function. + * Use __refdata to avoid this warning. + */ +static struct virtio_driver __refdata virtio_blk = { .feature_table = features, .feature_table_size = ARRAY_SIZE(features), .driver.name = KBUILD_MODNAME, diff --git a/drivers/block/z2ram.c b/drivers/block/z2ram.c index 4575171e5be..b2590409f25 100644 --- a/drivers/block/z2ram.c +++ b/drivers/block/z2ram.c @@ -374,7 +374,7 @@ err: static void __exit z2_exit(void) { int i, j; - blk_unregister_region(MKDEV(Z2RAM_MAJOR, 0), 256); + blk_unregister_region(MKDEV(Z2RAM_MAJOR, 0), Z2MINOR_COUNT); unregister_blkdev(Z2RAM_MAJOR, DEVICE_NAME); del_gendisk(z2ram_gendisk); put_disk(z2ram_gendisk); diff --git a/drivers/char/agp/parisc-agp.c b/drivers/char/agp/parisc-agp.c index f4bb43fb801..e077701ae3d 100644 --- a/drivers/char/agp/parisc-agp.c +++ b/drivers/char/agp/parisc-agp.c @@ -225,7 +225,7 @@ static const struct agp_bridge_driver parisc_agp_driver = { .configure = parisc_agp_configure, .fetch_size = parisc_agp_fetch_size, .tlb_flush = parisc_agp_tlbflush, - .mask_memory = parisc_agp_mask_memory, + .mask_memory = parisc_agp_page_mask_memory, .masks = parisc_agp_masks, .agp_enable = parisc_agp_enable, .cache_flush = global_cache_flush, diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c index 94e7e3c8c05..d97779ef72c 100644 --- a/drivers/char/hvc_console.c +++ b/drivers/char/hvc_console.c @@ -552,7 +552,7 @@ static int hvc_chars_in_buffer(struct tty_struct *tty) struct hvc_struct *hp = tty->driver_data; if (!hp) - return -1; + return 0; return hp->n_outbuf; } diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c index 94a5d5020ab..973be2f4419 100644 --- a/drivers/char/n_tty.c +++ b/drivers/char/n_tty.c @@ -1331,9 +1331,6 @@ handle_newline: static void n_tty_write_wakeup(struct tty_struct *tty) { - /* Write out any echoed characters that are still pending */ - process_echoes(tty); - if (tty->fasync && test_and_clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) kill_fasync(&tty->fasync, SIGIO, POLL_OUT); } @@ -1586,6 +1583,7 @@ static int n_tty_open(struct tty_struct *tty) static inline int input_available_p(struct tty_struct *tty, int amt) { + tty_flush_to_ldisc(tty); if (tty->icanon) { if (tty->canon_data) return 1; diff --git a/drivers/char/nozomi.c b/drivers/char/nozomi.c index 574f1c79b6e..ec58d8c387f 100644 --- a/drivers/char/nozomi.c +++ b/drivers/char/nozomi.c @@ -828,7 +828,7 @@ static int receive_data(enum port_type index, struct nozomi *dc) struct port *port = &dc->port[index]; void __iomem *addr = port->dl_addr[port->toggle_dl]; struct tty_struct *tty = tty_port_tty_get(&port->port); - int i; + int i, ret; if (unlikely(!tty)) { DBG1("tty not open for port: %d?", index); @@ -844,12 +844,14 @@ static int receive_data(enum port_type index, struct nozomi *dc) /* disable interrupt in downlink... */ disable_transmit_dl(index, dc); - return 0; + ret = 0; + goto put; } if (unlikely(size == 0)) { dev_err(&dc->pdev->dev, "size == 0?\n"); - return 1; + ret = 1; + goto put; } tty_buffer_request_room(tty, size); @@ -871,8 +873,10 @@ static int receive_data(enum port_type index, struct nozomi *dc) } set_bit(index, &dc->flip); + ret = 1; +put: tty_kref_put(tty); - return 1; + return ret; } /* Debug for interrupts */ @@ -1862,16 +1866,14 @@ static s32 ntty_chars_in_buffer(struct tty_struct *tty) { struct port *port = tty->driver_data; struct nozomi *dc = get_dc_by_tty(tty); - s32 rval; + s32 rval = 0; if (unlikely(!dc || !port)) { - rval = -ENODEV; goto exit_in_buffer; } if (unlikely(!port->port.count)) { dev_err(&dc->pdev->dev, "No tty open?\n"); - rval = -ENODEV; goto exit_in_buffer; } diff --git a/drivers/char/pcmcia/ipwireless/tty.c b/drivers/char/pcmcia/ipwireless/tty.c index 569f2f7743a..674b3ab3587 100644 --- a/drivers/char/pcmcia/ipwireless/tty.c +++ b/drivers/char/pcmcia/ipwireless/tty.c @@ -320,10 +320,10 @@ static int ipw_chars_in_buffer(struct tty_struct *linux_tty) struct ipw_tty *tty = linux_tty->driver_data; if (!tty) - return -ENODEV; + return 0; if (!tty->open_count) - return -EINVAL; + return 0; return tty->tx_bytes_queued; } diff --git a/drivers/char/specialix.c b/drivers/char/specialix.c index bfe4cdb2feb..268e17f9ec3 100644 --- a/drivers/char/specialix.c +++ b/drivers/char/specialix.c @@ -1809,10 +1809,10 @@ static int sx_tiocmset(struct tty_struct *tty, struct file *file, if (clear & TIOCM_DTR) port->MSVR &= ~MSVR_DTR; } - spin_lock_irqsave(&bp->lock, flags); + spin_lock(&bp->lock); sx_out(bp, CD186x_CAR, port_No(port)); sx_out(bp, CD186x_MSVR, port->MSVR); - spin_unlock_irqrestore(&bp->lock, flags); + spin_unlock(&bp->lock); spin_unlock_irqrestore(&port->lock, flags); func_exit(); return 0; @@ -1833,11 +1833,11 @@ static int sx_send_break(struct tty_struct *tty, int length) port->break_length = SPECIALIX_TPS / HZ * length; port->COR2 |= COR2_ETC; port->IER |= IER_TXRDY; - spin_lock_irqsave(&bp->lock, flags); + spin_lock(&bp->lock); sx_out(bp, CD186x_CAR, port_No(port)); sx_out(bp, CD186x_COR2, port->COR2); sx_out(bp, CD186x_IER, port->IER); - spin_unlock_irqrestore(&bp->lock, flags); + spin_unlock(&bp->lock); spin_unlock_irqrestore(&port->lock, flags); sx_wait_CCR(bp); spin_lock_irqsave(&bp->lock, flags); @@ -2023,9 +2023,9 @@ static void sx_unthrottle(struct tty_struct *tty) if (sx_crtscts(tty)) port->MSVR |= MSVR_DTR; /* Else clause: see remark in "sx_throttle"... */ - spin_lock_irqsave(&bp->lock, flags); + spin_lock(&bp->lock); sx_out(bp, CD186x_CAR, port_No(port)); - spin_unlock_irqrestore(&bp->lock, flags); + spin_unlock(&bp->lock); if (I_IXOFF(tty)) { spin_unlock_irqrestore(&port->lock, flags); sx_wait_CCR(bp); @@ -2035,9 +2035,9 @@ static void sx_unthrottle(struct tty_struct *tty) sx_wait_CCR(bp); spin_lock_irqsave(&port->lock, flags); } - spin_lock_irqsave(&bp->lock, flags); + spin_lock(&bp->lock); sx_out(bp, CD186x_MSVR, port->MSVR); - spin_unlock_irqrestore(&bp->lock, flags); + spin_unlock(&bp->lock); spin_unlock_irqrestore(&port->lock, flags); func_exit(); @@ -2061,10 +2061,10 @@ static void sx_stop(struct tty_struct *tty) spin_lock_irqsave(&port->lock, flags); port->IER &= ~IER_TXRDY; - spin_lock_irqsave(&bp->lock, flags); + spin_lock(&bp->lock); sx_out(bp, CD186x_CAR, port_No(port)); sx_out(bp, CD186x_IER, port->IER); - spin_unlock_irqrestore(&bp->lock, flags); + spin_unlock(&bp->lock); spin_unlock_irqrestore(&port->lock, flags); func_exit(); @@ -2089,10 +2089,10 @@ static void sx_start(struct tty_struct *tty) spin_lock_irqsave(&port->lock, flags); if (port->xmit_cnt && port->xmit_buf && !(port->IER & IER_TXRDY)) { port->IER |= IER_TXRDY; - spin_lock_irqsave(&bp->lock, flags); + spin_lock(&bp->lock); sx_out(bp, CD186x_CAR, port_No(port)); sx_out(bp, CD186x_IER, port->IER); - spin_unlock_irqrestore(&bp->lock, flags); + spin_unlock(&bp->lock); } spin_unlock_irqrestore(&port->lock, flags); diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c index 0db35857e4d..5d7a02f63e1 100644 --- a/drivers/char/sysrq.c +++ b/drivers/char/sysrq.c @@ -35,7 +35,6 @@ #include <linux/spinlock.h> #include <linux/vt_kern.h> #include <linux/workqueue.h> -#include <linux/kexec.h> #include <linux/hrtimer.h> #include <linux/oom.h> @@ -124,9 +123,12 @@ static struct sysrq_key_op sysrq_unraw_op = { static void sysrq_handle_crash(int key, struct tty_struct *tty) { char *killer = NULL; + + panic_on_oops = 1; /* force panic */ + wmb(); *killer = 1; } -static struct sysrq_key_op sysrq_crashdump_op = { +static struct sysrq_key_op sysrq_crash_op = { .handler = sysrq_handle_crash, .help_msg = "Crash", .action_msg = "Trigger a crash", @@ -401,7 +403,7 @@ static struct sysrq_key_op *sysrq_key_table[36] = { */ NULL, /* a */ &sysrq_reboot_op, /* b */ - &sysrq_crashdump_op, /* c & ibm_emac driver debug */ + &sysrq_crash_op, /* c & ibm_emac driver debug */ &sysrq_showlocks_op, /* d */ &sysrq_term_op, /* e */ &sysrq_moom_op, /* f */ diff --git a/drivers/char/tty_buffer.c b/drivers/char/tty_buffer.c index 810ee25d66a..3108991c5c8 100644 --- a/drivers/char/tty_buffer.c +++ b/drivers/char/tty_buffer.c @@ -462,6 +462,19 @@ static void flush_to_ldisc(struct work_struct *work) } /** + * tty_flush_to_ldisc + * @tty: tty to push + * + * Push the terminal flip buffers to the line discipline. + * + * Must not be called from IRQ context. + */ +void tty_flush_to_ldisc(struct tty_struct *tty) +{ + flush_to_ldisc(&tty->buf.work.work); +} + +/** * tty_flip_buffer_push - terminal * @tty: tty to push * diff --git a/drivers/char/tty_ldisc.c b/drivers/char/tty_ldisc.c index 0ef0dc97ba2..acd76b767d4 100644 --- a/drivers/char/tty_ldisc.c +++ b/drivers/char/tty_ldisc.c @@ -790,17 +790,20 @@ void tty_ldisc_hangup(struct tty_struct *tty) * N_TTY. */ if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) { - /* Avoid racing set_ldisc */ + /* Avoid racing set_ldisc or tty_ldisc_release */ mutex_lock(&tty->ldisc_mutex); - /* Switch back to N_TTY */ - tty_ldisc_halt(tty); - tty_ldisc_wait_idle(tty); - tty_ldisc_reinit(tty); - /* At this point we have a closed ldisc and we want to - reopen it. We could defer this to the next open but - it means auditing a lot of other paths so this is a FIXME */ - WARN_ON(tty_ldisc_open(tty, tty->ldisc)); - tty_ldisc_enable(tty); + if (tty->ldisc) { /* Not yet closed */ + /* Switch back to N_TTY */ + tty_ldisc_halt(tty); + tty_ldisc_wait_idle(tty); + tty_ldisc_reinit(tty); + /* At this point we have a closed ldisc and we want to + reopen it. We could defer this to the next open but + it means auditing a lot of other paths so this is + a FIXME */ + WARN_ON(tty_ldisc_open(tty, tty->ldisc)); + tty_ldisc_enable(tty); + } mutex_unlock(&tty->ldisc_mutex); tty_reset_termios(tty); } @@ -865,6 +868,7 @@ void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty) tty_ldisc_wait_idle(tty); + mutex_lock(&tty->ldisc_mutex); /* * Now kill off the ldisc */ @@ -875,6 +879,7 @@ void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty) /* Ensure the next open requests the N_TTY ldisc */ tty_set_termios_ldisc(tty, N_TTY); + mutex_unlock(&tty->ldisc_mutex); /* This will need doing differently if we need to lock */ if (o_tty) diff --git a/drivers/char/tty_port.c b/drivers/char/tty_port.c index 4e862a75f7f..9769b1149f7 100644 --- a/drivers/char/tty_port.c +++ b/drivers/char/tty_port.c @@ -267,7 +267,7 @@ int tty_port_block_til_ready(struct tty_port *port, if (retval == 0) port->flags |= ASYNC_NORMAL_ACTIVE; spin_unlock_irqrestore(&port->lock, flags); - return 0; + return retval; } EXPORT_SYMBOL(tty_port_block_til_ready); diff --git a/drivers/char/vc_screen.c b/drivers/char/vc_screen.c index d94d25c12aa..c1791a63d99 100644 --- a/drivers/char/vc_screen.c +++ b/drivers/char/vc_screen.c @@ -495,11 +495,15 @@ void vcs_remove_sysfs(int index) int __init vcs_init(void) { + unsigned int i; + if (register_chrdev(VCS_MAJOR, "vcs", &vcs_fops)) panic("unable to get major %d for vcs device", VCS_MAJOR); vc_class = class_create(THIS_MODULE, "vc"); device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 0), NULL, "vcs"); device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 128), NULL, "vcsa"); + for (i = 0; i < MIN_NR_CONSOLES; i++) + vcs_make_sysfs(i); return 0; } diff --git a/drivers/char/vr41xx_giu.c b/drivers/char/vr41xx_giu.c deleted file mode 100644 index e69de29bb2d..00000000000 --- a/drivers/char/vr41xx_giu.c +++ /dev/null diff --git a/drivers/char/vt.c b/drivers/char/vt.c index 7947bd1b4cf..404f4c1ee43 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -770,14 +770,12 @@ int vc_allocate(unsigned int currcons) /* return 0 on success */ visual_init(vc, currcons, 1); if (!*vc->vc_uni_pagedir_loc) con_set_default_unimap(vc); - if (!vc->vc_kmalloced) - vc->vc_screenbuf = kmalloc(vc->vc_screenbuf_size, GFP_KERNEL); + vc->vc_screenbuf = kmalloc(vc->vc_screenbuf_size, GFP_KERNEL); if (!vc->vc_screenbuf) { kfree(vc); vc_cons[currcons].d = NULL; return -ENOMEM; } - vc->vc_kmalloced = 1; vc_init(vc, vc->vc_rows, vc->vc_cols, 1); vcs_make_sysfs(currcons); atomic_notifier_call_chain(&vt_notifier_list, VT_ALLOCATE, ¶m); @@ -913,10 +911,8 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc, if (new_scr_end > new_origin) scr_memsetw((void *)new_origin, vc->vc_video_erase_char, new_scr_end - new_origin); - if (vc->vc_kmalloced) - kfree(vc->vc_screenbuf); + kfree(vc->vc_screenbuf); vc->vc_screenbuf = newscreen; - vc->vc_kmalloced = 1; vc->vc_screenbuf_size = new_screen_size; set_origin(vc); @@ -995,8 +991,7 @@ void vc_deallocate(unsigned int currcons) vc->vc_sw->con_deinit(vc); put_pid(vc->vt_pid); module_put(vc->vc_sw->owner); - if (vc->vc_kmalloced) - kfree(vc->vc_screenbuf); + kfree(vc->vc_screenbuf); if (currcons >= MIN_NR_CONSOLES) kfree(vc); vc_cons[currcons].d = NULL; @@ -2881,7 +2876,6 @@ static int __init con_init(void) INIT_WORK(&vc_cons[currcons].SAK_work, vc_SAK); visual_init(vc, currcons, 1); vc->vc_screenbuf = kzalloc(vc->vc_screenbuf_size, GFP_NOWAIT); - vc->vc_kmalloced = 0; vc_init(vc, vc->vc_rows, vc->vc_cols, currcons || !vc->vc_sw->con_save_screen); } diff --git a/drivers/connector/cn_queue.c b/drivers/connector/cn_queue.c index c769ef269fb..408c2af25d5 100644 --- a/drivers/connector/cn_queue.c +++ b/drivers/connector/cn_queue.c @@ -1,7 +1,7 @@ /* * cn_queue.c * - * 2004-2005 Copyright (c) Evgeniy Polyakov <johnpol@2ka.mipt.ru> + * 2004+ Copyright (c) Evgeniy Polyakov <zbr@ioremap.net> * All rights reserved. * * This program is free software; you can redistribute it and/or modify diff --git a/drivers/connector/connector.c b/drivers/connector/connector.c index fd336c5a905..08b2500f21e 100644 --- a/drivers/connector/connector.c +++ b/drivers/connector/connector.c @@ -1,7 +1,7 @@ /* * connector.c * - * 2004-2005 Copyright (c) Evgeniy Polyakov <johnpol@2ka.mipt.ru> + * 2004+ Copyright (c) Evgeniy Polyakov <zbr@ioremap.net> * All rights reserved. * * This program is free software; you can redistribute it and/or modify @@ -33,7 +33,7 @@ #include <net/sock.h> MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Evgeniy Polyakov <johnpol@2ka.mipt.ru>"); +MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>"); MODULE_DESCRIPTION("Generic userspace <-> kernelspace connector."); static u32 cn_idx = CN_IDX_CONNECTOR; diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index b90eda8b344..fd69086d08d 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -858,6 +858,8 @@ static int cpufreq_add_dev(struct sys_device *sys_dev) /* Check for existing affected CPUs. * They may not be aware of it due to CPU Hotplug. + * cpufreq_cpu_put is called when the device is removed + * in __cpufreq_remove_dev() */ managed_policy = cpufreq_cpu_get(j); if (unlikely(managed_policy)) { @@ -884,7 +886,7 @@ static int cpufreq_add_dev(struct sys_device *sys_dev) ret = sysfs_create_link(&sys_dev->kobj, &managed_policy->kobj, "cpufreq"); - if (!ret) + if (ret) cpufreq_cpu_put(managed_policy); /* * Success. We only needed to be added to the mask. @@ -924,6 +926,8 @@ static int cpufreq_add_dev(struct sys_device *sys_dev) spin_lock_irqsave(&cpufreq_driver_lock, flags); for_each_cpu(j, policy->cpus) { + if (!cpu_online(j)) + continue; per_cpu(cpufreq_cpu_data, j) = policy; per_cpu(policy_cpu, j) = policy->cpu; } @@ -1244,13 +1248,22 @@ EXPORT_SYMBOL(cpufreq_get); static int cpufreq_suspend(struct sys_device *sysdev, pm_message_t pmsg) { - int cpu = sysdev->id; int ret = 0; + +#ifdef __powerpc__ + int cpu = sysdev->id; unsigned int cur_freq = 0; struct cpufreq_policy *cpu_policy; dprintk("suspending cpu %u\n", cpu); + /* + * This whole bogosity is here because Powerbooks are made of fail. + * No sane platform should need any of the code below to be run. + * (it's entirely the wrong thing to do, as driver->get may + * reenable interrupts on some architectures). + */ + if (!cpu_online(cpu)) return 0; @@ -1309,6 +1322,7 @@ static int cpufreq_suspend(struct sys_device *sysdev, pm_message_t pmsg) out: cpufreq_cpu_put(cpu_policy); +#endif /* __powerpc__ */ return ret; } @@ -1322,12 +1336,18 @@ out: */ static int cpufreq_resume(struct sys_device *sysdev) { - int cpu = sysdev->id; int ret = 0; + +#ifdef __powerpc__ + int cpu = sysdev->id; struct cpufreq_policy *cpu_policy; dprintk("resuming cpu %u\n", cpu); + /* As with the ->suspend method, all the code below is + * only necessary because Powerbooks suck. + * See commit 42d4dc3f4e1e for jokes. */ + if (!cpu_online(cpu)) return 0; @@ -1391,6 +1411,7 @@ out: schedule_work(&cpu_policy->update); fail: cpufreq_cpu_put(cpu_policy); +#endif /* __powerpc__ */ return ret; } diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c index 57490502b21..bdea7e2f94b 100644 --- a/drivers/cpufreq/cpufreq_conservative.c +++ b/drivers/cpufreq/cpufreq_conservative.c @@ -63,6 +63,7 @@ struct cpu_dbs_info_s { unsigned int down_skip; unsigned int requested_freq; int cpu; + unsigned int enable:1; /* * percpu mutex that serializes governor limit change with * do_dbs_timer invocation. We do not want do_dbs_timer to run @@ -141,6 +142,9 @@ dbs_cpufreq_notifier(struct notifier_block *nb, unsigned long val, struct cpufreq_policy *policy; + if (!this_dbs_info->enable) + return 0; + policy = this_dbs_info->cur_policy; /* @@ -497,6 +501,7 @@ static inline void dbs_timer_init(struct cpu_dbs_info_s *dbs_info) int delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate); delay -= jiffies % delay; + dbs_info->enable = 1; INIT_DELAYED_WORK_DEFERRABLE(&dbs_info->work, do_dbs_timer); queue_delayed_work_on(dbs_info->cpu, kconservative_wq, &dbs_info->work, delay); @@ -504,6 +509,7 @@ static inline void dbs_timer_init(struct cpu_dbs_info_s *dbs_info) static inline void dbs_timer_exit(struct cpu_dbs_info_s *dbs_info) { + dbs_info->enable = 0; cancel_delayed_work_sync(&dbs_info->work); } diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 070357aaedb..81e1020fb51 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -4,7 +4,7 @@ menuconfig DMADEVICES bool "DMA Engine support" - depends on !HIGHMEM64G && HAS_DMA + depends on HAS_DMA help DMA engines can do asynchronous data transfers without involving the host CPU. Currently, this framework can be @@ -46,6 +46,14 @@ config DW_DMAC Support the Synopsys DesignWare AHB DMA controller. This can be integrated in chips such as the Atmel AT32ap7000. +config AT_HDMAC + tristate "Atmel AHB DMA support" + depends on ARCH_AT91SAM9RL + select DMA_ENGINE + help + Support the Atmel AHB DMA controller. This can be integrated in + chips such as the Atmel AT91SAM9RL. + config FSL_DMA tristate "Freescale Elo and Elo Plus DMA support" depends on FSL_SOC @@ -108,7 +116,7 @@ config NET_DMA config ASYNC_TX_DMA bool "Async_tx: Offload support for the async_tx api" - depends on DMA_ENGINE + depends on DMA_ENGINE && !HIGHMEM64G help This allows the async_tx api to take advantage of offload engines for memcpy, memset, xor, and raid6 p+q operations. If your platform has diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index a0b6564800c..40e1e008357 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -7,5 +7,6 @@ obj-$(CONFIG_INTEL_IOP_ADMA) += iop-adma.o obj-$(CONFIG_FSL_DMA) += fsldma.o obj-$(CONFIG_MV_XOR) += mv_xor.o obj-$(CONFIG_DW_DMAC) += dw_dmac.o +obj-$(CONFIG_AT_HDMAC) += at_hdmac.o obj-$(CONFIG_MX3_IPU) += ipu/ obj-$(CONFIG_TXX9_DMAC) += txx9dmac.o diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c new file mode 100644 index 00000000000..9a1e5fb412e --- /dev/null +++ b/drivers/dma/at_hdmac.c @@ -0,0 +1,1213 @@ +/* + * Driver for the Atmel AHB DMA Controller (aka HDMA or DMAC on AT91 systems) + * + * Copyright (C) 2008 Atmel Corporation + * + * 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 supports the Atmel AHB DMA Controller, + * + * The driver has currently been tested with the Atmel AT91SAM9RL + * and AT91SAM9G45 series. + */ + +#include <linux/clk.h> +#include <linux/dmaengine.h> +#include <linux/dma-mapping.h> +#include <linux/dmapool.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include "at_hdmac_regs.h" + +/* + * Glossary + * -------- + * + * at_hdmac : Name of the ATmel AHB DMA Controller + * at_dma_ / atdma : ATmel DMA controller entity related + * atc_ / atchan : ATmel DMA Channel entity related + */ + +#define ATC_DEFAULT_CFG (ATC_FIFOCFG_HALFFIFO) +#define ATC_DEFAULT_CTRLA (0) +#define ATC_DEFAULT_CTRLB (ATC_SIF(0) \ + |ATC_DIF(1)) + +/* + * Initial number of descriptors to allocate for each channel. This could + * be increased during dma usage. + */ +static unsigned int init_nr_desc_per_channel = 64; +module_param(init_nr_desc_per_channel, uint, 0644); +MODULE_PARM_DESC(init_nr_desc_per_channel, + "initial descriptors per channel (default: 64)"); + + +/* prototypes */ +static dma_cookie_t atc_tx_submit(struct dma_async_tx_descriptor *tx); + + +/*----------------------------------------------------------------------*/ + +static struct at_desc *atc_first_active(struct at_dma_chan *atchan) +{ + return list_first_entry(&atchan->active_list, + struct at_desc, desc_node); +} + +static struct at_desc *atc_first_queued(struct at_dma_chan *atchan) +{ + return list_first_entry(&atchan->queue, + struct at_desc, desc_node); +} + +/** + * atc_alloc_descriptor - allocate and return an initilized descriptor + * @chan: the channel to allocate descriptors for + * @gfp_flags: GFP allocation flags + * + * Note: The ack-bit is positioned in the descriptor flag at creation time + * to make initial allocation more convenient. This bit will be cleared + * and control will be given to client at usage time (during + * preparation functions). + */ +static struct at_desc *atc_alloc_descriptor(struct dma_chan *chan, + gfp_t gfp_flags) +{ + struct at_desc *desc = NULL; + struct at_dma *atdma = to_at_dma(chan->device); + dma_addr_t phys; + + desc = dma_pool_alloc(atdma->dma_desc_pool, gfp_flags, &phys); + if (desc) { + memset(desc, 0, sizeof(struct at_desc)); + dma_async_tx_descriptor_init(&desc->txd, chan); + /* txd.flags will be overwritten in prep functions */ + desc->txd.flags = DMA_CTRL_ACK; + desc->txd.tx_submit = atc_tx_submit; + desc->txd.phys = phys; + } + + return desc; +} + +/** + * atc_desc_get - get a unsused descriptor from free_list + * @atchan: channel we want a new descriptor for + */ +static struct at_desc *atc_desc_get(struct at_dma_chan *atchan) +{ + struct at_desc *desc, *_desc; + struct at_desc *ret = NULL; + unsigned int i = 0; + LIST_HEAD(tmp_list); + + spin_lock_bh(&atchan->lock); + list_for_each_entry_safe(desc, _desc, &atchan->free_list, desc_node) { + i++; + if (async_tx_test_ack(&desc->txd)) { + list_del(&desc->desc_node); + ret = desc; + break; + } + dev_dbg(chan2dev(&atchan->chan_common), + "desc %p not ACKed\n", desc); + } + spin_unlock_bh(&atchan->lock); + dev_vdbg(chan2dev(&atchan->chan_common), + "scanned %u descriptors on freelist\n", i); + + /* no more descriptor available in initial pool: create one more */ + if (!ret) { + ret = atc_alloc_descriptor(&atchan->chan_common, GFP_ATOMIC); + if (ret) { + spin_lock_bh(&atchan->lock); + atchan->descs_allocated++; + spin_unlock_bh(&atchan->lock); + } else { + dev_err(chan2dev(&atchan->chan_common), + "not enough descriptors available\n"); + } + } + + return ret; +} + +/** + * atc_desc_put - move a descriptor, including any children, to the free list + * @atchan: channel we work on + * @desc: descriptor, at the head of a chain, to move to free list + */ +static void atc_desc_put(struct at_dma_chan *atchan, struct at_desc *desc) +{ + if (desc) { + struct at_desc *child; + + spin_lock_bh(&atchan->lock); + list_for_each_entry(child, &desc->txd.tx_list, desc_node) + dev_vdbg(chan2dev(&atchan->chan_common), + "moving child desc %p to freelist\n", + child); + list_splice_init(&desc->txd.tx_list, &atchan->free_list); + dev_vdbg(chan2dev(&atchan->chan_common), + "moving desc %p to freelist\n", desc); + list_add(&desc->desc_node, &atchan->free_list); + spin_unlock_bh(&atchan->lock); + } +} + +/** + * atc_assign_cookie - compute and assign new cookie + * @atchan: channel we work on + * @desc: descriptor to asign cookie for + * + * Called with atchan->lock held and bh disabled + */ +static dma_cookie_t +atc_assign_cookie(struct at_dma_chan *atchan, struct at_desc *desc) +{ + dma_cookie_t cookie = atchan->chan_common.cookie; + + if (++cookie < 0) + cookie = 1; + + atchan->chan_common.cookie = cookie; + desc->txd.cookie = cookie; + + return cookie; +} + +/** + * atc_dostart - starts the DMA engine for real + * @atchan: the channel we want to start + * @first: first descriptor in the list we want to begin with + * + * Called with atchan->lock held and bh disabled + */ +static void atc_dostart(struct at_dma_chan *atchan, struct at_desc *first) +{ + struct at_dma *atdma = to_at_dma(atchan->chan_common.device); + + /* ASSERT: channel is idle */ + if (atc_chan_is_enabled(atchan)) { + dev_err(chan2dev(&atchan->chan_common), + "BUG: Attempted to start non-idle channel\n"); + dev_err(chan2dev(&atchan->chan_common), + " channel: s0x%x d0x%x ctrl0x%x:0x%x l0x%x\n", + channel_readl(atchan, SADDR), + channel_readl(atchan, DADDR), + channel_readl(atchan, CTRLA), + channel_readl(atchan, CTRLB), + channel_readl(atchan, DSCR)); + + /* The tasklet will hopefully advance the queue... */ + return; + } + + vdbg_dump_regs(atchan); + + /* clear any pending interrupt */ + while (dma_readl(atdma, EBCISR)) + cpu_relax(); + + channel_writel(atchan, SADDR, 0); + channel_writel(atchan, DADDR, 0); + channel_writel(atchan, CTRLA, 0); + channel_writel(atchan, CTRLB, 0); + channel_writel(atchan, DSCR, first->txd.phys); + dma_writel(atdma, CHER, atchan->mask); + + vdbg_dump_regs(atchan); +} + +/** + * atc_chain_complete - finish work for one transaction chain + * @atchan: channel we work on + * @desc: descriptor at the head of the chain we want do complete + * + * Called with atchan->lock held and bh disabled */ +static void +atc_chain_complete(struct at_dma_chan *atchan, struct at_desc *desc) +{ + dma_async_tx_callback callback; + void *param; + struct dma_async_tx_descriptor *txd = &desc->txd; + + dev_vdbg(chan2dev(&atchan->chan_common), + "descriptor %u complete\n", txd->cookie); + + atchan->completed_cookie = txd->cookie; + callback = txd->callback; + param = txd->callback_param; + + /* move children to free_list */ + list_splice_init(&txd->tx_list, &atchan->free_list); + /* move myself to free_list */ + list_move(&desc->desc_node, &atchan->free_list); + + /* unmap dma addresses */ + if (!(txd->flags & DMA_COMPL_SKIP_DEST_UNMAP)) { + if (txd->flags & DMA_COMPL_DEST_UNMAP_SINGLE) + dma_unmap_single(chan2parent(&atchan->chan_common), + desc->lli.daddr, + desc->len, DMA_FROM_DEVICE); + else + dma_unmap_page(chan2parent(&atchan->chan_common), + desc->lli.daddr, + desc->len, DMA_FROM_DEVICE); + } + if (!(txd->flags & DMA_COMPL_SKIP_SRC_UNMAP)) { + if (txd->flags & DMA_COMPL_SRC_UNMAP_SINGLE) + dma_unmap_single(chan2parent(&atchan->chan_common), + desc->lli.saddr, + desc->len, DMA_TO_DEVICE); + else + dma_unmap_page(chan2parent(&atchan->chan_common), + desc->lli.saddr, + desc->len, DMA_TO_DEVICE); + } + + /* + * The API requires that no submissions are done from a + * callback, so we don't need to drop the lock here + */ + if (callback) + callback(param); + + dma_run_dependencies(txd); +} + +/** + * atc_complete_all - finish work for all transactions + * @atchan: channel to complete transactions for + * + * Eventually submit queued descriptors if any + * + * Assume channel is idle while calling this function + * Called with atchan->lock held and bh disabled + */ +static void atc_complete_all(struct at_dma_chan *atchan) +{ + struct at_desc *desc, *_desc; + LIST_HEAD(list); + + dev_vdbg(chan2dev(&atchan->chan_common), "complete all\n"); + + BUG_ON(atc_chan_is_enabled(atchan)); + + /* + * Submit queued descriptors ASAP, i.e. before we go through + * the completed ones. + */ + if (!list_empty(&atchan->queue)) + atc_dostart(atchan, atc_first_queued(atchan)); + /* empty active_list now it is completed */ + list_splice_init(&atchan->active_list, &list); + /* empty queue list by moving descriptors (if any) to active_list */ + list_splice_init(&atchan->queue, &atchan->active_list); + + list_for_each_entry_safe(desc, _desc, &list, desc_node) + atc_chain_complete(atchan, desc); +} + +/** + * atc_cleanup_descriptors - cleanup up finished descriptors in active_list + * @atchan: channel to be cleaned up + * + * Called with atchan->lock held and bh disabled + */ +static void atc_cleanup_descriptors(struct at_dma_chan *atchan) +{ + struct at_desc *desc, *_desc; + struct at_desc *child; + + dev_vdbg(chan2dev(&atchan->chan_common), "cleanup descriptors\n"); + + list_for_each_entry_safe(desc, _desc, &atchan->active_list, desc_node) { + if (!(desc->lli.ctrla & ATC_DONE)) + /* This one is currently in progress */ + return; + + list_for_each_entry(child, &desc->txd.tx_list, desc_node) + if (!(child->lli.ctrla & ATC_DONE)) + /* Currently in progress */ + return; + + /* + * No descriptors so far seem to be in progress, i.e. + * this chain must be done. + */ + atc_chain_complete(atchan, desc); + } +} + +/** + * atc_advance_work - at the end of a transaction, move forward + * @atchan: channel where the transaction ended + * + * Called with atchan->lock held and bh disabled + */ +static void atc_advance_work(struct at_dma_chan *atchan) +{ + dev_vdbg(chan2dev(&atchan->chan_common), "advance_work\n"); + + if (list_empty(&atchan->active_list) || + list_is_singular(&atchan->active_list)) { + atc_complete_all(atchan); + } else { + atc_chain_complete(atchan, atc_first_active(atchan)); + /* advance work */ + atc_dostart(atchan, atc_first_active(atchan)); + } +} + + +/** + * atc_handle_error - handle errors reported by DMA controller + * @atchan: channel where error occurs + * + * Called with atchan->lock held and bh disabled + */ +static void atc_handle_error(struct at_dma_chan *atchan) +{ + struct at_desc *bad_desc; + struct at_desc *child; + + /* + * The descriptor currently at the head of the active list is + * broked. Since we don't have any way to report errors, we'll + * just have to scream loudly and try to carry on. + */ + bad_desc = atc_first_active(atchan); + list_del_init(&bad_desc->desc_node); + + /* As we are stopped, take advantage to push queued descriptors + * in active_list */ + list_splice_init(&atchan->queue, atchan->active_list.prev); + + /* Try to restart the controller */ + if (!list_empty(&atchan->active_list)) + atc_dostart(atchan, atc_first_active(atchan)); + + /* + * KERN_CRITICAL may seem harsh, but since this only happens + * when someone submits a bad physical address in a + * descriptor, we should consider ourselves lucky that the + * controller flagged an error instead of scribbling over + * random memory locations. + */ + dev_crit(chan2dev(&atchan->chan_common), + "Bad descriptor submitted for DMA!\n"); + dev_crit(chan2dev(&atchan->chan_common), + " cookie: %d\n", bad_desc->txd.cookie); + atc_dump_lli(atchan, &bad_desc->lli); + list_for_each_entry(child, &bad_desc->txd.tx_list, desc_node) + atc_dump_lli(atchan, &child->lli); + + /* Pretend the descriptor completed successfully */ + atc_chain_complete(atchan, bad_desc); +} + + +/*-- IRQ & Tasklet ---------------------------------------------------*/ + +static void atc_tasklet(unsigned long data) +{ + struct at_dma_chan *atchan = (struct at_dma_chan *)data; + + /* Channel cannot be enabled here */ + if (atc_chan_is_enabled(atchan)) { + dev_err(chan2dev(&atchan->chan_common), + "BUG: channel enabled in tasklet\n"); + return; + } + + spin_lock(&atchan->lock); + if (test_and_clear_bit(0, &atchan->error_status)) + atc_handle_error(atchan); + else + atc_advance_work(atchan); + + spin_unlock(&atchan->lock); +} + +static irqreturn_t at_dma_interrupt(int irq, void *dev_id) +{ + struct at_dma *atdma = (struct at_dma *)dev_id; + struct at_dma_chan *atchan; + int i; + u32 status, pending, imr; + int ret = IRQ_NONE; + + do { + imr = dma_readl(atdma, EBCIMR); + status = dma_readl(atdma, EBCISR); + pending = status & imr; + + if (!pending) + break; + + dev_vdbg(atdma->dma_common.dev, + "interrupt: status = 0x%08x, 0x%08x, 0x%08x\n", + status, imr, pending); + + for (i = 0; i < atdma->dma_common.chancnt; i++) { + atchan = &atdma->chan[i]; + if (pending & (AT_DMA_CBTC(i) | AT_DMA_ERR(i))) { + if (pending & AT_DMA_ERR(i)) { + /* Disable channel on AHB error */ + dma_writel(atdma, CHDR, atchan->mask); + /* Give information to tasklet */ + set_bit(0, &atchan->error_status); + } + tasklet_schedule(&atchan->tasklet); + ret = IRQ_HANDLED; + } + } + + } while (pending); + + return ret; +} + + +/*-- DMA Engine API --------------------------------------------------*/ + +/** + * atc_tx_submit - set the prepared descriptor(s) to be executed by the engine + * @desc: descriptor at the head of the transaction chain + * + * Queue chain if DMA engine is working already + * + * Cookie increment and adding to active_list or queue must be atomic + */ +static dma_cookie_t atc_tx_submit(struct dma_async_tx_descriptor *tx) +{ + struct at_desc *desc = txd_to_at_desc(tx); + struct at_dma_chan *atchan = to_at_dma_chan(tx->chan); + dma_cookie_t cookie; + + spin_lock_bh(&atchan->lock); + cookie = atc_assign_cookie(atchan, desc); + + if (list_empty(&atchan->active_list)) { + dev_vdbg(chan2dev(tx->chan), "tx_submit: started %u\n", + desc->txd.cookie); + atc_dostart(atchan, desc); + list_add_tail(&desc->desc_node, &atchan->active_list); + } else { + dev_vdbg(chan2dev(tx->chan), "tx_submit: queued %u\n", + desc->txd.cookie); + list_add_tail(&desc->desc_node, &atchan->queue); + } + + spin_unlock_bh(&atchan->lock); + + return cookie; +} + +/** + * atc_prep_dma_memcpy - prepare a memcpy operation + * @chan: the channel to prepare operation on + * @dest: operation virtual destination address + * @src: operation virtual source address + * @len: operation length + * @flags: tx descriptor status flags + */ +static struct dma_async_tx_descriptor * +atc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, + size_t len, unsigned long flags) +{ + struct at_dma_chan *atchan = to_at_dma_chan(chan); + struct at_desc *desc = NULL; + struct at_desc *first = NULL; + struct at_desc *prev = NULL; + size_t xfer_count; + size_t offset; + unsigned int src_width; + unsigned int dst_width; + u32 ctrla; + u32 ctrlb; + + dev_vdbg(chan2dev(chan), "prep_dma_memcpy: d0x%x s0x%x l0x%zx f0x%lx\n", + dest, src, len, flags); + + if (unlikely(!len)) { + dev_dbg(chan2dev(chan), "prep_dma_memcpy: length is zero!\n"); + return NULL; + } + + ctrla = ATC_DEFAULT_CTRLA; + ctrlb = ATC_DEFAULT_CTRLB + | ATC_SRC_ADDR_MODE_INCR + | ATC_DST_ADDR_MODE_INCR + | ATC_FC_MEM2MEM; + + /* + * We can be a lot more clever here, but this should take care + * of the most common optimization. + */ + if (!((src | dest | len) & 3)) { + ctrla |= ATC_SRC_WIDTH_WORD | ATC_DST_WIDTH_WORD; + src_width = dst_width = 2; + } else if (!((src | dest | len) & 1)) { + ctrla |= ATC_SRC_WIDTH_HALFWORD | ATC_DST_WIDTH_HALFWORD; + src_width = dst_width = 1; + } else { + ctrla |= ATC_SRC_WIDTH_BYTE | ATC_DST_WIDTH_BYTE; + src_width = dst_width = 0; + } + + for (offset = 0; offset < len; offset += xfer_count << src_width) { + xfer_count = min_t(size_t, (len - offset) >> src_width, + ATC_BTSIZE_MAX); + + desc = atc_desc_get(atchan); + if (!desc) + goto err_desc_get; + + desc->lli.saddr = src + offset; + desc->lli.daddr = dest + offset; + desc->lli.ctrla = ctrla | xfer_count; + desc->lli.ctrlb = ctrlb; + + desc->txd.cookie = 0; + async_tx_ack(&desc->txd); + + if (!first) { + first = desc; + } else { + /* inform the HW lli about chaining */ + prev->lli.dscr = desc->txd.phys; + /* insert the link descriptor to the LD ring */ + list_add_tail(&desc->desc_node, + &first->txd.tx_list); + } + prev = desc; + } + + /* First descriptor of the chain embedds additional information */ + first->txd.cookie = -EBUSY; + first->len = len; + + /* set end-of-link to the last link descriptor of list*/ + set_desc_eol(desc); + + desc->txd.flags = flags; /* client is in control of this ack */ + + return &first->txd; + +err_desc_get: + atc_desc_put(atchan, first); + return NULL; +} + + +/** + * atc_prep_slave_sg - prepare descriptors for a DMA_SLAVE transaction + * @chan: DMA channel + * @sgl: scatterlist to transfer to/from + * @sg_len: number of entries in @scatterlist + * @direction: DMA direction + * @flags: tx descriptor status flags + */ +static struct dma_async_tx_descriptor * +atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, + unsigned int sg_len, enum dma_data_direction direction, + unsigned long flags) +{ + struct at_dma_chan *atchan = to_at_dma_chan(chan); + struct at_dma_slave *atslave = chan->private; + struct at_desc *first = NULL; + struct at_desc *prev = NULL; + u32 ctrla; + u32 ctrlb; + dma_addr_t reg; + unsigned int reg_width; + unsigned int mem_width; + unsigned int i; + struct scatterlist *sg; + size_t total_len = 0; + + dev_vdbg(chan2dev(chan), "prep_slave_sg: %s f0x%lx\n", + direction == DMA_TO_DEVICE ? "TO DEVICE" : "FROM DEVICE", + flags); + + if (unlikely(!atslave || !sg_len)) { + dev_dbg(chan2dev(chan), "prep_dma_memcpy: length is zero!\n"); + return NULL; + } + + reg_width = atslave->reg_width; + + sg_len = dma_map_sg(chan2parent(chan), sgl, sg_len, direction); + + ctrla = ATC_DEFAULT_CTRLA | atslave->ctrla; + ctrlb = ATC_DEFAULT_CTRLB | ATC_IEN; + + switch (direction) { + case DMA_TO_DEVICE: + ctrla |= ATC_DST_WIDTH(reg_width); + ctrlb |= ATC_DST_ADDR_MODE_FIXED + | ATC_SRC_ADDR_MODE_INCR + | ATC_FC_MEM2PER; + reg = atslave->tx_reg; + for_each_sg(sgl, sg, sg_len, i) { + struct at_desc *desc; + u32 len; + u32 mem; + + desc = atc_desc_get(atchan); + if (!desc) + goto err_desc_get; + + mem = sg_phys(sg); + len = sg_dma_len(sg); + mem_width = 2; + if (unlikely(mem & 3 || len & 3)) + mem_width = 0; + + desc->lli.saddr = mem; + desc->lli.daddr = reg; + desc->lli.ctrla = ctrla + | ATC_SRC_WIDTH(mem_width) + | len >> mem_width; + desc->lli.ctrlb = ctrlb; + + if (!first) { + first = desc; + } else { + /* inform the HW lli about chaining */ + prev->lli.dscr = desc->txd.phys; + /* insert the link descriptor to the LD ring */ + list_add_tail(&desc->desc_node, + &first->txd.tx_list); + } + prev = desc; + total_len += len; + } + break; + case DMA_FROM_DEVICE: + ctrla |= ATC_SRC_WIDTH(reg_width); + ctrlb |= ATC_DST_ADDR_MODE_INCR + | ATC_SRC_ADDR_MODE_FIXED + | ATC_FC_PER2MEM; + + reg = atslave->rx_reg; + for_each_sg(sgl, sg, sg_len, i) { + struct at_desc *desc; + u32 len; + u32 mem; + + desc = atc_desc_get(atchan); + if (!desc) + goto err_desc_get; + + mem = sg_phys(sg); + len = sg_dma_len(sg); + mem_width = 2; + if (unlikely(mem & 3 || len & 3)) + mem_width = 0; + + desc->lli.saddr = reg; + desc->lli.daddr = mem; + desc->lli.ctrla = ctrla + | ATC_DST_WIDTH(mem_width) + | len >> mem_width; + desc->lli.ctrlb = ctrlb; + + if (!first) { + first = desc; + } else { + /* inform the HW lli about chaining */ + prev->lli.dscr = desc->txd.phys; + /* insert the link descriptor to the LD ring */ + list_add_tail(&desc->desc_node, + &first->txd.tx_list); + } + prev = desc; + total_len += len; + } + break; + default: + return NULL; + } + + /* set end-of-link to the last link descriptor of list*/ + set_desc_eol(prev); + + /* First descriptor of the chain embedds additional information */ + first->txd.cookie = -EBUSY; + first->len = total_len; + + /* last link descriptor of list is responsible of flags */ + prev->txd.flags = flags; /* client is in control of this ack */ + + return &first->txd; + +err_desc_get: + dev_err(chan2dev(chan), "not enough descriptors available\n"); + atc_desc_put(atchan, first); + return NULL; +} + +static void atc_terminate_all(struct dma_chan *chan) +{ + struct at_dma_chan *atchan = to_at_dma_chan(chan); + struct at_dma *atdma = to_at_dma(chan->device); + struct at_desc *desc, *_desc; + LIST_HEAD(list); + + /* + * This is only called when something went wrong elsewhere, so + * we don't really care about the data. Just disable the + * channel. We still have to poll the channel enable bit due + * to AHB/HSB limitations. + */ + spin_lock_bh(&atchan->lock); + + dma_writel(atdma, CHDR, atchan->mask); + + /* confirm that this channel is disabled */ + while (dma_readl(atdma, CHSR) & atchan->mask) + cpu_relax(); + + /* active_list entries will end up before queued entries */ + list_splice_init(&atchan->queue, &list); + list_splice_init(&atchan->active_list, &list); + + spin_unlock_bh(&atchan->lock); + + /* Flush all pending and queued descriptors */ + list_for_each_entry_safe(desc, _desc, &list, desc_node) + atc_chain_complete(atchan, desc); +} + +/** + * atc_is_tx_complete - poll for transaction completion + * @chan: DMA channel + * @cookie: transaction identifier to check status of + * @done: if not %NULL, updated with last completed transaction + * @used: if not %NULL, updated with last used transaction + * + * If @done and @used are passed in, upon return they reflect the driver + * internal state and can be used with dma_async_is_complete() to check + * the status of multiple cookies without re-checking hardware state. + */ +static enum dma_status +atc_is_tx_complete(struct dma_chan *chan, + dma_cookie_t cookie, + dma_cookie_t *done, dma_cookie_t *used) +{ + struct at_dma_chan *atchan = to_at_dma_chan(chan); + dma_cookie_t last_used; + dma_cookie_t last_complete; + enum dma_status ret; + + dev_vdbg(chan2dev(chan), "is_tx_complete: %d (d%d, u%d)\n", + cookie, done ? *done : 0, used ? *used : 0); + + spin_lock_bh(atchan->lock); + + last_complete = atchan->completed_cookie; + last_used = chan->cookie; + + ret = dma_async_is_complete(cookie, last_complete, last_used); + if (ret != DMA_SUCCESS) { + atc_cleanup_descriptors(atchan); + + last_complete = atchan->completed_cookie; + last_used = chan->cookie; + + ret = dma_async_is_complete(cookie, last_complete, last_used); + } + + spin_unlock_bh(atchan->lock); + + if (done) + *done = last_complete; + if (used) + *used = last_used; + + return ret; +} + +/** + * atc_issue_pending - try to finish work + * @chan: target DMA channel + */ +static void atc_issue_pending(struct dma_chan *chan) +{ + struct at_dma_chan *atchan = to_at_dma_chan(chan); + + dev_vdbg(chan2dev(chan), "issue_pending\n"); + + if (!atc_chan_is_enabled(atchan)) { + spin_lock_bh(&atchan->lock); + atc_advance_work(atchan); + spin_unlock_bh(&atchan->lock); + } +} + +/** + * atc_alloc_chan_resources - allocate resources for DMA channel + * @chan: allocate descriptor resources for this channel + * @client: current client requesting the channel be ready for requests + * + * return - the number of allocated descriptors + */ +static int atc_alloc_chan_resources(struct dma_chan *chan) +{ + struct at_dma_chan *atchan = to_at_dma_chan(chan); + struct at_dma *atdma = to_at_dma(chan->device); + struct at_desc *desc; + struct at_dma_slave *atslave; + int i; + u32 cfg; + LIST_HEAD(tmp_list); + + dev_vdbg(chan2dev(chan), "alloc_chan_resources\n"); + + /* ASSERT: channel is idle */ + if (atc_chan_is_enabled(atchan)) { + dev_dbg(chan2dev(chan), "DMA channel not idle ?\n"); + return -EIO; + } + + cfg = ATC_DEFAULT_CFG; + + atslave = chan->private; + if (atslave) { + /* + * We need controller-specific data to set up slave + * transfers. + */ + BUG_ON(!atslave->dma_dev || atslave->dma_dev != atdma->dma_common.dev); + + /* if cfg configuration specified take it instad of default */ + if (atslave->cfg) + cfg = atslave->cfg; + } + + /* have we already been set up? + * reconfigure channel but no need to reallocate descriptors */ + if (!list_empty(&atchan->free_list)) + return atchan->descs_allocated; + + /* Allocate initial pool of descriptors */ + for (i = 0; i < init_nr_desc_per_channel; i++) { + desc = atc_alloc_descriptor(chan, GFP_KERNEL); + if (!desc) { + dev_err(atdma->dma_common.dev, + "Only %d initial descriptors\n", i); + break; + } + list_add_tail(&desc->desc_node, &tmp_list); + } + + spin_lock_bh(&atchan->lock); + atchan->descs_allocated = i; + list_splice(&tmp_list, &atchan->free_list); + atchan->completed_cookie = chan->cookie = 1; + spin_unlock_bh(&atchan->lock); + + /* channel parameters */ + channel_writel(atchan, CFG, cfg); + + dev_dbg(chan2dev(chan), + "alloc_chan_resources: allocated %d descriptors\n", + atchan->descs_allocated); + + return atchan->descs_allocated; +} + +/** + * atc_free_chan_resources - free all channel resources + * @chan: DMA channel + */ +static void atc_free_chan_resources(struct dma_chan *chan) +{ + struct at_dma_chan *atchan = to_at_dma_chan(chan); + struct at_dma *atdma = to_at_dma(chan->device); + struct at_desc *desc, *_desc; + LIST_HEAD(list); + + dev_dbg(chan2dev(chan), "free_chan_resources: (descs allocated=%u)\n", + atchan->descs_allocated); + + /* ASSERT: channel is idle */ + BUG_ON(!list_empty(&atchan->active_list)); + BUG_ON(!list_empty(&atchan->queue)); + BUG_ON(atc_chan_is_enabled(atchan)); + + list_for_each_entry_safe(desc, _desc, &atchan->free_list, desc_node) { + dev_vdbg(chan2dev(chan), " freeing descriptor %p\n", desc); + list_del(&desc->desc_node); + /* free link descriptor */ + dma_pool_free(atdma->dma_desc_pool, desc, desc->txd.phys); + } + list_splice_init(&atchan->free_list, &list); + atchan->descs_allocated = 0; + + dev_vdbg(chan2dev(chan), "free_chan_resources: done\n"); +} + + +/*-- Module Management -----------------------------------------------*/ + +/** + * at_dma_off - disable DMA controller + * @atdma: the Atmel HDAMC device + */ +static void at_dma_off(struct at_dma *atdma) +{ + dma_writel(atdma, EN, 0); + + /* disable all interrupts */ + dma_writel(atdma, EBCIDR, -1L); + + /* confirm that all channels are disabled */ + while (dma_readl(atdma, CHSR) & atdma->all_chan_mask) + cpu_relax(); +} + +static int __init at_dma_probe(struct platform_device *pdev) +{ + struct at_dma_platform_data *pdata; + struct resource *io; + struct at_dma *atdma; + size_t size; + int irq; + int err; + int i; + + /* get DMA Controller parameters from platform */ + pdata = pdev->dev.platform_data; + if (!pdata || pdata->nr_channels > AT_DMA_MAX_NR_CHANNELS) + return -EINVAL; + + io = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!io) + return -EINVAL; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + size = sizeof(struct at_dma); + size += pdata->nr_channels * sizeof(struct at_dma_chan); + atdma = kzalloc(size, GFP_KERNEL); + if (!atdma) + return -ENOMEM; + + /* discover transaction capabilites from the platform data */ + atdma->dma_common.cap_mask = pdata->cap_mask; + atdma->all_chan_mask = (1 << pdata->nr_channels) - 1; + + size = io->end - io->start + 1; + if (!request_mem_region(io->start, size, pdev->dev.driver->name)) { + err = -EBUSY; + goto err_kfree; + } + + atdma->regs = ioremap(io->start, size); + if (!atdma->regs) { + err = -ENOMEM; + goto err_release_r; + } + + atdma->clk = clk_get(&pdev->dev, "dma_clk"); + if (IS_ERR(atdma->clk)) { + err = PTR_ERR(atdma->clk); + goto err_clk; + } + clk_enable(atdma->clk); + + /* force dma off, just in case */ + at_dma_off(atdma); + + err = request_irq(irq, at_dma_interrupt, 0, "at_hdmac", atdma); + if (err) + goto err_irq; + + platform_set_drvdata(pdev, atdma); + + /* create a pool of consistent memory blocks for hardware descriptors */ + atdma->dma_desc_pool = dma_pool_create("at_hdmac_desc_pool", + &pdev->dev, sizeof(struct at_desc), + 4 /* word alignment */, 0); + if (!atdma->dma_desc_pool) { + dev_err(&pdev->dev, "No memory for descriptors dma pool\n"); + err = -ENOMEM; + goto err_pool_create; + } + + /* clear any pending interrupt */ + while (dma_readl(atdma, EBCISR)) + cpu_relax(); + + /* initialize channels related values */ + INIT_LIST_HEAD(&atdma->dma_common.channels); + for (i = 0; i < pdata->nr_channels; i++, atdma->dma_common.chancnt++) { + struct at_dma_chan *atchan = &atdma->chan[i]; + + atchan->chan_common.device = &atdma->dma_common; + atchan->chan_common.cookie = atchan->completed_cookie = 1; + atchan->chan_common.chan_id = i; + list_add_tail(&atchan->chan_common.device_node, + &atdma->dma_common.channels); + + atchan->ch_regs = atdma->regs + ch_regs(i); + spin_lock_init(&atchan->lock); + atchan->mask = 1 << i; + + INIT_LIST_HEAD(&atchan->active_list); + INIT_LIST_HEAD(&atchan->queue); + INIT_LIST_HEAD(&atchan->free_list); + + tasklet_init(&atchan->tasklet, atc_tasklet, + (unsigned long)atchan); + atc_enable_irq(atchan); + } + + /* set base routines */ + atdma->dma_common.device_alloc_chan_resources = atc_alloc_chan_resources; + atdma->dma_common.device_free_chan_resources = atc_free_chan_resources; + atdma->dma_common.device_is_tx_complete = atc_is_tx_complete; + atdma->dma_common.device_issue_pending = atc_issue_pending; + atdma->dma_common.dev = &pdev->dev; + + /* set prep routines based on capability */ + if (dma_has_cap(DMA_MEMCPY, atdma->dma_common.cap_mask)) + atdma->dma_common.device_prep_dma_memcpy = atc_prep_dma_memcpy; + + if (dma_has_cap(DMA_SLAVE, atdma->dma_common.cap_mask)) { + atdma->dma_common.device_prep_slave_sg = atc_prep_slave_sg; + atdma->dma_common.device_terminate_all = atc_terminate_all; + } + + dma_writel(atdma, EN, AT_DMA_ENABLE); + + dev_info(&pdev->dev, "Atmel AHB DMA Controller ( %s%s), %d channels\n", + dma_has_cap(DMA_MEMCPY, atdma->dma_common.cap_mask) ? "cpy " : "", + dma_has_cap(DMA_SLAVE, atdma->dma_common.cap_mask) ? "slave " : "", + atdma->dma_common.chancnt); + + dma_async_device_register(&atdma->dma_common); + + return 0; + +err_pool_create: + platform_set_drvdata(pdev, NULL); + free_irq(platform_get_irq(pdev, 0), atdma); +err_irq: + clk_disable(atdma->clk); + clk_put(atdma->clk); +err_clk: + iounmap(atdma->regs); + atdma->regs = NULL; +err_release_r: + release_mem_region(io->start, size); +err_kfree: + kfree(atdma); + return err; +} + +static int __exit at_dma_remove(struct platform_device *pdev) +{ + struct at_dma *atdma = platform_get_drvdata(pdev); + struct dma_chan *chan, *_chan; + struct resource *io; + + at_dma_off(atdma); + dma_async_device_unregister(&atdma->dma_common); + + dma_pool_destroy(atdma->dma_desc_pool); + platform_set_drvdata(pdev, NULL); + free_irq(platform_get_irq(pdev, 0), atdma); + + list_for_each_entry_safe(chan, _chan, &atdma->dma_common.channels, + device_node) { + struct at_dma_chan *atchan = to_at_dma_chan(chan); + + /* Disable interrupts */ + atc_disable_irq(atchan); + tasklet_disable(&atchan->tasklet); + + tasklet_kill(&atchan->tasklet); + list_del(&chan->device_node); + } + + clk_disable(atdma->clk); + clk_put(atdma->clk); + + iounmap(atdma->regs); + atdma->regs = NULL; + + io = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(io->start, io->end - io->start + 1); + + kfree(atdma); + + return 0; +} + +static void at_dma_shutdown(struct platform_device *pdev) +{ + struct at_dma *atdma = platform_get_drvdata(pdev); + + at_dma_off(platform_get_drvdata(pdev)); + clk_disable(atdma->clk); +} + +static int at_dma_suspend_late(struct platform_device *pdev, pm_message_t mesg) +{ + struct at_dma *atdma = platform_get_drvdata(pdev); + + at_dma_off(platform_get_drvdata(pdev)); + clk_disable(atdma->clk); + return 0; +} + +static int at_dma_resume_early(struct platform_device *pdev) +{ + struct at_dma *atdma = platform_get_drvdata(pdev); + + clk_enable(atdma->clk); + dma_writel(atdma, EN, AT_DMA_ENABLE); + return 0; + +} + +static struct platform_driver at_dma_driver = { + .remove = __exit_p(at_dma_remove), + .shutdown = at_dma_shutdown, + .suspend_late = at_dma_suspend_late, + .resume_early = at_dma_resume_early, + .driver = { + .name = "at_hdmac", + }, +}; + +static int __init at_dma_init(void) +{ + return platform_driver_probe(&at_dma_driver, at_dma_probe); +} +module_init(at_dma_init); + +static void __exit at_dma_exit(void) +{ + platform_driver_unregister(&at_dma_driver); +} +module_exit(at_dma_exit); + +MODULE_DESCRIPTION("Atmel AHB DMA Controller driver"); +MODULE_AUTHOR("Nicolas Ferre <nicolas.ferre@atmel.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:at_hdmac"); diff --git a/drivers/dma/at_hdmac_regs.h b/drivers/dma/at_hdmac_regs.h new file mode 100644 index 00000000000..4c972afc49e --- /dev/null +++ b/drivers/dma/at_hdmac_regs.h @@ -0,0 +1,353 @@ +/* + * Header file for the Atmel AHB DMA Controller driver + * + * Copyright (C) 2008 Atmel Corporation + * + * 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. + */ +#ifndef AT_HDMAC_REGS_H +#define AT_HDMAC_REGS_H + +#include <mach/at_hdmac.h> + +#define AT_DMA_MAX_NR_CHANNELS 8 + + +#define AT_DMA_GCFG 0x00 /* Global Configuration Register */ +#define AT_DMA_IF_BIGEND(i) (0x1 << (i)) /* AHB-Lite Interface i in Big-endian mode */ +#define AT_DMA_ARB_CFG (0x1 << 4) /* Arbiter mode. */ +#define AT_DMA_ARB_CFG_FIXED (0x0 << 4) +#define AT_DMA_ARB_CFG_ROUND_ROBIN (0x1 << 4) + +#define AT_DMA_EN 0x04 /* Controller Enable Register */ +#define AT_DMA_ENABLE (0x1 << 0) + +#define AT_DMA_SREQ 0x08 /* Software Single Request Register */ +#define AT_DMA_SSREQ(x) (0x1 << ((x) << 1)) /* Request a source single transfer on channel x */ +#define AT_DMA_DSREQ(x) (0x1 << (1 + ((x) << 1))) /* Request a destination single transfer on channel x */ + +#define AT_DMA_CREQ 0x0C /* Software Chunk Transfer Request Register */ +#define AT_DMA_SCREQ(x) (0x1 << ((x) << 1)) /* Request a source chunk transfer on channel x */ +#define AT_DMA_DCREQ(x) (0x1 << (1 + ((x) << 1))) /* Request a destination chunk transfer on channel x */ + +#define AT_DMA_LAST 0x10 /* Software Last Transfer Flag Register */ +#define AT_DMA_SLAST(x) (0x1 << ((x) << 1)) /* This src rq is last tx of buffer on channel x */ +#define AT_DMA_DLAST(x) (0x1 << (1 + ((x) << 1))) /* This dst rq is last tx of buffer on channel x */ + +#define AT_DMA_SYNC 0x14 /* Request Synchronization Register */ +#define AT_DMA_SYR(h) (0x1 << (h)) /* Synchronize handshake line h */ + +/* Error, Chained Buffer transfer completed and Buffer transfer completed Interrupt registers */ +#define AT_DMA_EBCIER 0x18 /* Enable register */ +#define AT_DMA_EBCIDR 0x1C /* Disable register */ +#define AT_DMA_EBCIMR 0x20 /* Mask Register */ +#define AT_DMA_EBCISR 0x24 /* Status Register */ +#define AT_DMA_CBTC_OFFSET 8 +#define AT_DMA_ERR_OFFSET 16 +#define AT_DMA_BTC(x) (0x1 << (x)) +#define AT_DMA_CBTC(x) (0x1 << (AT_DMA_CBTC_OFFSET + (x))) +#define AT_DMA_ERR(x) (0x1 << (AT_DMA_ERR_OFFSET + (x))) + +#define AT_DMA_CHER 0x28 /* Channel Handler Enable Register */ +#define AT_DMA_ENA(x) (0x1 << (x)) +#define AT_DMA_SUSP(x) (0x1 << ( 8 + (x))) +#define AT_DMA_KEEP(x) (0x1 << (24 + (x))) + +#define AT_DMA_CHDR 0x2C /* Channel Handler Disable Register */ +#define AT_DMA_DIS(x) (0x1 << (x)) +#define AT_DMA_RES(x) (0x1 << ( 8 + (x))) + +#define AT_DMA_CHSR 0x30 /* Channel Handler Status Register */ +#define AT_DMA_EMPT(x) (0x1 << (16 + (x))) +#define AT_DMA_STAL(x) (0x1 << (24 + (x))) + + +#define AT_DMA_CH_REGS_BASE 0x3C /* Channel registers base address */ +#define ch_regs(x) (AT_DMA_CH_REGS_BASE + (x) * 0x28) /* Channel x base addr */ + +/* Hardware register offset for each channel */ +#define ATC_SADDR_OFFSET 0x00 /* Source Address Register */ +#define ATC_DADDR_OFFSET 0x04 /* Destination Address Register */ +#define ATC_DSCR_OFFSET 0x08 /* Descriptor Address Register */ +#define ATC_CTRLA_OFFSET 0x0C /* Control A Register */ +#define ATC_CTRLB_OFFSET 0x10 /* Control B Register */ +#define ATC_CFG_OFFSET 0x14 /* Configuration Register */ +#define ATC_SPIP_OFFSET 0x18 /* Src PIP Configuration Register */ +#define ATC_DPIP_OFFSET 0x1C /* Dst PIP Configuration Register */ + + +/* Bitfield definitions */ + +/* Bitfields in DSCR */ +#define ATC_DSCR_IF(i) (0x3 & (i)) /* Dsc feched via AHB-Lite Interface i */ + +/* Bitfields in CTRLA */ +#define ATC_BTSIZE_MAX 0xFFFFUL /* Maximum Buffer Transfer Size */ +#define ATC_BTSIZE(x) (ATC_BTSIZE_MAX & (x)) /* Buffer Transfer Size */ +/* Chunck Tranfer size definitions are in at_hdmac.h */ +#define ATC_SRC_WIDTH_MASK (0x3 << 24) /* Source Single Transfer Size */ +#define ATC_SRC_WIDTH(x) ((x) << 24) +#define ATC_SRC_WIDTH_BYTE (0x0 << 24) +#define ATC_SRC_WIDTH_HALFWORD (0x1 << 24) +#define ATC_SRC_WIDTH_WORD (0x2 << 24) +#define ATC_DST_WIDTH_MASK (0x3 << 28) /* Destination Single Transfer Size */ +#define ATC_DST_WIDTH(x) ((x) << 28) +#define ATC_DST_WIDTH_BYTE (0x0 << 28) +#define ATC_DST_WIDTH_HALFWORD (0x1 << 28) +#define ATC_DST_WIDTH_WORD (0x2 << 28) +#define ATC_DONE (0x1 << 31) /* Tx Done (only written back in descriptor) */ + +/* Bitfields in CTRLB */ +#define ATC_SIF(i) (0x3 & (i)) /* Src tx done via AHB-Lite Interface i */ +#define ATC_DIF(i) ((0x3 & (i)) << 4) /* Dst tx done via AHB-Lite Interface i */ +#define ATC_SRC_PIP (0x1 << 8) /* Source Picture-in-Picture enabled */ +#define ATC_DST_PIP (0x1 << 12) /* Destination Picture-in-Picture enabled */ +#define ATC_SRC_DSCR_DIS (0x1 << 16) /* Src Descriptor fetch disable */ +#define ATC_DST_DSCR_DIS (0x1 << 20) /* Dst Descriptor fetch disable */ +#define ATC_FC_MASK (0x7 << 21) /* Choose Flow Controller */ +#define ATC_FC_MEM2MEM (0x0 << 21) /* Mem-to-Mem (DMA) */ +#define ATC_FC_MEM2PER (0x1 << 21) /* Mem-to-Periph (DMA) */ +#define ATC_FC_PER2MEM (0x2 << 21) /* Periph-to-Mem (DMA) */ +#define ATC_FC_PER2PER (0x3 << 21) /* Periph-to-Periph (DMA) */ +#define ATC_FC_PER2MEM_PER (0x4 << 21) /* Periph-to-Mem (Peripheral) */ +#define ATC_FC_MEM2PER_PER (0x5 << 21) /* Mem-to-Periph (Peripheral) */ +#define ATC_FC_PER2PER_SRCPER (0x6 << 21) /* Periph-to-Periph (Src Peripheral) */ +#define ATC_FC_PER2PER_DSTPER (0x7 << 21) /* Periph-to-Periph (Dst Peripheral) */ +#define ATC_SRC_ADDR_MODE_MASK (0x3 << 24) +#define ATC_SRC_ADDR_MODE_INCR (0x0 << 24) /* Incrementing Mode */ +#define ATC_SRC_ADDR_MODE_DECR (0x1 << 24) /* Decrementing Mode */ +#define ATC_SRC_ADDR_MODE_FIXED (0x2 << 24) /* Fixed Mode */ +#define ATC_DST_ADDR_MODE_MASK (0x3 << 28) +#define ATC_DST_ADDR_MODE_INCR (0x0 << 28) /* Incrementing Mode */ +#define ATC_DST_ADDR_MODE_DECR (0x1 << 28) /* Decrementing Mode */ +#define ATC_DST_ADDR_MODE_FIXED (0x2 << 28) /* Fixed Mode */ +#define ATC_IEN (0x1 << 30) /* BTC interrupt enable (active low) */ +#define ATC_AUTO (0x1 << 31) /* Auto multiple buffer tx enable */ + +/* Bitfields in CFG */ +/* are in at_hdmac.h */ + +/* Bitfields in SPIP */ +#define ATC_SPIP_HOLE(x) (0xFFFFU & (x)) +#define ATC_SPIP_BOUNDARY(x) ((0x3FF & (x)) << 16) + +/* Bitfields in DPIP */ +#define ATC_DPIP_HOLE(x) (0xFFFFU & (x)) +#define ATC_DPIP_BOUNDARY(x) ((0x3FF & (x)) << 16) + + +/*-- descriptors -----------------------------------------------------*/ + +/* LLI == Linked List Item; aka DMA buffer descriptor */ +struct at_lli { + /* values that are not changed by hardware */ + dma_addr_t saddr; + dma_addr_t daddr; + /* value that may get written back: */ + u32 ctrla; + /* more values that are not changed by hardware */ + u32 ctrlb; + dma_addr_t dscr; /* chain to next lli */ +}; + +/** + * struct at_desc - software descriptor + * @at_lli: hardware lli structure + * @txd: support for the async_tx api + * @desc_node: node on the channed descriptors list + * @len: total transaction bytecount + */ +struct at_desc { + /* FIRST values the hardware uses */ + struct at_lli lli; + + /* THEN values for driver housekeeping */ + struct dma_async_tx_descriptor txd; + struct list_head desc_node; + size_t len; +}; + +static inline struct at_desc * +txd_to_at_desc(struct dma_async_tx_descriptor *txd) +{ + return container_of(txd, struct at_desc, txd); +} + + +/*-- Channels --------------------------------------------------------*/ + +/** + * struct at_dma_chan - internal representation of an Atmel HDMAC channel + * @chan_common: common dmaengine channel object members + * @device: parent device + * @ch_regs: memory mapped register base + * @mask: channel index in a mask + * @error_status: transmit error status information from irq handler + * to tasklet (use atomic operations) + * @tasklet: bottom half to finish transaction work + * @lock: serializes enqueue/dequeue operations to descriptors lists + * @completed_cookie: identifier for the most recently completed operation + * @active_list: list of descriptors dmaengine is being running on + * @queue: list of descriptors ready to be submitted to engine + * @free_list: list of descriptors usable by the channel + * @descs_allocated: records the actual size of the descriptor pool + */ +struct at_dma_chan { + struct dma_chan chan_common; + struct at_dma *device; + void __iomem *ch_regs; + u8 mask; + unsigned long error_status; + struct tasklet_struct tasklet; + + spinlock_t lock; + + /* these other elements are all protected by lock */ + dma_cookie_t completed_cookie; + struct list_head active_list; + struct list_head queue; + struct list_head free_list; + unsigned int descs_allocated; +}; + +#define channel_readl(atchan, name) \ + __raw_readl((atchan)->ch_regs + ATC_##name##_OFFSET) + +#define channel_writel(atchan, name, val) \ + __raw_writel((val), (atchan)->ch_regs + ATC_##name##_OFFSET) + +static inline struct at_dma_chan *to_at_dma_chan(struct dma_chan *dchan) +{ + return container_of(dchan, struct at_dma_chan, chan_common); +} + + +/*-- Controller ------------------------------------------------------*/ + +/** + * struct at_dma - internal representation of an Atmel HDMA Controller + * @chan_common: common dmaengine dma_device object members + * @ch_regs: memory mapped register base + * @clk: dma controller clock + * @all_chan_mask: all channels availlable in a mask + * @dma_desc_pool: base of DMA descriptor region (DMA address) + * @chan: channels table to store at_dma_chan structures + */ +struct at_dma { + struct dma_device dma_common; + void __iomem *regs; + struct clk *clk; + + u8 all_chan_mask; + + struct dma_pool *dma_desc_pool; + /* AT THE END channels table */ + struct at_dma_chan chan[0]; +}; + +#define dma_readl(atdma, name) \ + __raw_readl((atdma)->regs + AT_DMA_##name) +#define dma_writel(atdma, name, val) \ + __raw_writel((val), (atdma)->regs + AT_DMA_##name) + +static inline struct at_dma *to_at_dma(struct dma_device *ddev) +{ + return container_of(ddev, struct at_dma, dma_common); +} + + +/*-- Helper functions ------------------------------------------------*/ + +static struct device *chan2dev(struct dma_chan *chan) +{ + return &chan->dev->device; +} +static struct device *chan2parent(struct dma_chan *chan) +{ + return chan->dev->device.parent; +} + +#if defined(VERBOSE_DEBUG) +static void vdbg_dump_regs(struct at_dma_chan *atchan) +{ + struct at_dma *atdma = to_at_dma(atchan->chan_common.device); + + dev_err(chan2dev(&atchan->chan_common), + " channel %d : imr = 0x%x, chsr = 0x%x\n", + atchan->chan_common.chan_id, + dma_readl(atdma, EBCIMR), + dma_readl(atdma, CHSR)); + + dev_err(chan2dev(&atchan->chan_common), + " channel: s0x%x d0x%x ctrl0x%x:0x%x cfg0x%x l0x%x\n", + channel_readl(atchan, SADDR), + channel_readl(atchan, DADDR), + channel_readl(atchan, CTRLA), + channel_readl(atchan, CTRLB), + channel_readl(atchan, CFG), + channel_readl(atchan, DSCR)); +} +#else +static void vdbg_dump_regs(struct at_dma_chan *atchan) {} +#endif + +static void atc_dump_lli(struct at_dma_chan *atchan, struct at_lli *lli) +{ + dev_printk(KERN_CRIT, chan2dev(&atchan->chan_common), + " desc: s0x%x d0x%x ctrl0x%x:0x%x l0x%x\n", + lli->saddr, lli->daddr, + lli->ctrla, lli->ctrlb, lli->dscr); +} + + +static void atc_setup_irq(struct at_dma_chan *atchan, int on) +{ + struct at_dma *atdma = to_at_dma(atchan->chan_common.device); + u32 ebci; + + /* enable interrupts on buffer chain completion & error */ + ebci = AT_DMA_CBTC(atchan->chan_common.chan_id) + | AT_DMA_ERR(atchan->chan_common.chan_id); + if (on) + dma_writel(atdma, EBCIER, ebci); + else + dma_writel(atdma, EBCIDR, ebci); +} + +static inline void atc_enable_irq(struct at_dma_chan *atchan) +{ + atc_setup_irq(atchan, 1); +} + +static inline void atc_disable_irq(struct at_dma_chan *atchan) +{ + atc_setup_irq(atchan, 0); +} + + +/** + * atc_chan_is_enabled - test if given channel is enabled + * @atchan: channel we want to test status + */ +static inline int atc_chan_is_enabled(struct at_dma_chan *atchan) +{ + struct at_dma *atdma = to_at_dma(atchan->chan_common.device); + + return !!(dma_readl(atdma, CHSR) & atchan->mask); +} + + +/** + * set_desc_eol - set end-of-link to descriptor so it will end transfer + * @desc: descriptor, signle or at the end of a chain, to end chain on + */ +static void set_desc_eol(struct at_desc *desc) +{ + desc->lli.ctrlb |= ATC_SRC_DSCR_DIS | ATC_DST_DSCR_DIS; + desc->lli.dscr = 0; +} + +#endif /* AT_HDMAC_REGS_H */ diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c index fb7da5141e9..d93017fc787 100644 --- a/drivers/dma/dmatest.c +++ b/drivers/dma/dmatest.c @@ -38,6 +38,11 @@ module_param(max_channels, uint, S_IRUGO); MODULE_PARM_DESC(max_channels, "Maximum number of channels to use (default: all)"); +static unsigned int iterations; +module_param(iterations, uint, S_IRUGO); +MODULE_PARM_DESC(iterations, + "Iterations before stopping test (default: infinite)"); + static unsigned int xor_sources = 3; module_param(xor_sources, uint, S_IRUGO); MODULE_PARM_DESC(xor_sources, @@ -114,7 +119,7 @@ static void dmatest_init_srcs(u8 **bufs, unsigned int start, unsigned int len) buf[i] = PATTERN_SRC | (~i & PATTERN_COUNT_MASK); for ( ; i < start + len; i++) buf[i] = PATTERN_SRC | PATTERN_COPY - | (~i & PATTERN_COUNT_MASK);; + | (~i & PATTERN_COUNT_MASK); for ( ; i < test_buf_size; i++) buf[i] = PATTERN_SRC | (~i & PATTERN_COUNT_MASK); buf++; @@ -270,7 +275,8 @@ static int dmatest_func(void *data) flags = DMA_CTRL_ACK | DMA_COMPL_SKIP_DEST_UNMAP | DMA_PREP_INTERRUPT; - while (!kthread_should_stop()) { + while (!kthread_should_stop() + && !(iterations && total_tests >= iterations)) { struct dma_device *dev = chan->device; struct dma_async_tx_descriptor *tx = NULL; dma_addr_t dma_srcs[src_cnt]; @@ -416,6 +422,13 @@ err_srcbuf: err_srcs: pr_notice("%s: terminating after %u tests, %u failures (status %d)\n", thread_name, total_tests, failed_tests, ret); + + if (iterations > 0) + while (!kthread_should_stop()) { + DECLARE_WAIT_QUEUE_HEAD(wait_dmatest_exit); + interruptible_sleep_on(&wait_dmatest_exit); + } + return ret; } @@ -495,11 +508,11 @@ static int dmatest_add_channel(struct dma_chan *chan) if (dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask)) { cnt = dmatest_add_threads(dtc, DMA_MEMCPY); - thread_count += cnt > 0 ?: 0; + thread_count += cnt > 0 ? cnt : 0; } if (dma_has_cap(DMA_XOR, dma_dev->cap_mask)) { cnt = dmatest_add_threads(dtc, DMA_XOR); - thread_count += cnt > 0 ?: 0; + thread_count += cnt > 0 ? cnt : 0; } pr_info("dmatest: Started %u threads using %s\n", diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index f18d1bde043..ef87a898414 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -12,6 +12,11 @@ * also fit for MPC8560, MPC8555, MPC8548, MPC8641, and etc. * The support for MPC8349 DMA contorller is also added. * + * This driver instructs the DMA controller to issue the PCI Read Multiple + * command for PCI read operations, instead of using the default PCI Read Line + * command. Please be aware that this setting may result in read pre-fetching + * on some platforms. + * * This 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 @@ -49,9 +54,10 @@ static void dma_init(struct fsl_dma_chan *fsl_chan) case FSL_DMA_IP_83XX: /* Set the channel to below modes: * EOTIE - End-of-transfer interrupt enable + * PRC_RM - PCI read multiple */ - DMA_OUT(fsl_chan, &fsl_chan->reg_base->mr, FSL_DMA_MR_EOTIE, - 32); + DMA_OUT(fsl_chan, &fsl_chan->reg_base->mr, FSL_DMA_MR_EOTIE + | FSL_DMA_MR_PRC_RM, 32); break; } @@ -136,15 +142,16 @@ static int dma_is_idle(struct fsl_dma_chan *fsl_chan) static void dma_start(struct fsl_dma_chan *fsl_chan) { - u32 mr_set = 0;; + u32 mr_set = 0; if (fsl_chan->feature & FSL_DMA_CHAN_PAUSE_EXT) { DMA_OUT(fsl_chan, &fsl_chan->reg_base->bcr, 0, 32); mr_set |= FSL_DMA_MR_EMP_EN; - } else + } else if ((fsl_chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_85XX) { DMA_OUT(fsl_chan, &fsl_chan->reg_base->mr, DMA_IN(fsl_chan, &fsl_chan->reg_base->mr, 32) & ~FSL_DMA_MR_EMP_EN, 32); + } if (fsl_chan->feature & FSL_DMA_CHAN_START_EXT) mr_set |= FSL_DMA_MR_EMS_EN; @@ -871,9 +878,9 @@ static int __devinit fsl_dma_chan_probe(struct fsl_dma_device *fdev, switch (new_fsl_chan->feature & FSL_DMA_IP_MASK) { case FSL_DMA_IP_85XX: - new_fsl_chan->toggle_ext_start = fsl_chan_toggle_ext_start; new_fsl_chan->toggle_ext_pause = fsl_chan_toggle_ext_pause; case FSL_DMA_IP_83XX: + new_fsl_chan->toggle_ext_start = fsl_chan_toggle_ext_start; new_fsl_chan->set_src_loop_size = fsl_chan_set_src_loop_size; new_fsl_chan->set_dest_loop_size = fsl_chan_set_dest_loop_size; } diff --git a/drivers/dma/fsldma.h b/drivers/dma/fsldma.h index 4f21a512d84..dc7f2686579 100644 --- a/drivers/dma/fsldma.h +++ b/drivers/dma/fsldma.h @@ -38,6 +38,7 @@ /* Special MR definition for MPC8349 */ #define FSL_DMA_MR_EOTIE 0x00000080 +#define FSL_DMA_MR_PRC_RM 0x00000800 #define FSL_DMA_SR_CH 0x00000020 #define FSL_DMA_SR_PE 0x00000010 diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c index ddab94f5122..3f23eabe09f 100644 --- a/drivers/dma/mv_xor.c +++ b/drivers/dma/mv_xor.c @@ -1176,7 +1176,7 @@ static int __devinit mv_xor_probe(struct platform_device *pdev) if (dma_has_cap(DMA_MEMSET, dma_dev->cap_mask)) dma_dev->device_prep_dma_memset = mv_xor_prep_dma_memset; if (dma_has_cap(DMA_XOR, dma_dev->cap_mask)) { - dma_dev->max_xor = 8; ; + dma_dev->max_xor = 8; dma_dev->device_prep_dma_xor = mv_xor_prep_dma_xor; } diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index 858fe603722..e2a10bcba7a 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c @@ -868,6 +868,8 @@ static void amd64_read_dbam_reg(struct amd64_pvt *pvt) goto err_reg; } + return; + err_reg: debugf0("Error reading F2x%03x.\n", reg); } @@ -970,7 +972,7 @@ static void amd64_read_dct_base_mask(struct amd64_pvt *pvt) } for (cs = 0; cs < pvt->num_dcsm; cs++) { - reg = K8_DCSB0 + (cs * 4); + reg = K8_DCSM0 + (cs * 4); err = pci_read_config_dword(pvt->dram_f2_ctl, reg, &pvt->dcsm0[cs]); if (unlikely(err)) @@ -2634,6 +2636,8 @@ static void amd64_read_mc_registers(struct amd64_pvt *pvt) amd64_dump_misc_regs(pvt); + return; + err_reg: debugf0("Reading an MC register failed\n"); @@ -2977,6 +2981,9 @@ static int amd64_check_ecc_enabled(struct amd64_pvt *pvt) "ECC is enabled by BIOS, Proceeding " "with EDAC module initialization\n"); + /* Signal good ECC status */ + ret = 0; + /* CLEAR the override, since BIOS controlled it */ ecc_enable_override = 0; } diff --git a/drivers/edac/x38_edac.c b/drivers/edac/x38_edac.c index 2406c2ce284..d4ec6059317 100644 --- a/drivers/edac/x38_edac.c +++ b/drivers/edac/x38_edac.c @@ -30,7 +30,7 @@ /* Intel X38 register addresses - device 0 function 0 - DRAM Controller */ #define X38_MCHBAR_LOW 0x48 /* MCH Memory Mapped Register BAR */ -#define X38_MCHBAR_HIGH 0x4b +#define X38_MCHBAR_HIGH 0x4c #define X38_MCHBAR_MASK 0xfffffc000ULL /* bits 35:14 */ #define X38_MMR_WINDOW_SIZE 16384 diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 8fab7890a36..33be210d672 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -1461,7 +1461,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, goto out; } - if (crtc_req->count_connectors > 0 && !mode && !fb) { + if (crtc_req->count_connectors > 0 && (!mode || !fb)) { DRM_DEBUG("Count connectors is %d but no mode or fb set\n", crtc_req->count_connectors); ret = -EINVAL; diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index 3da9cfa3184..6aaa2cb2336 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -706,8 +706,8 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) struct drm_encoder **save_encoders, *new_encoder; struct drm_framebuffer *old_fb = NULL; bool save_enabled; - bool mode_changed = false; - bool fb_changed = false; + bool mode_changed = false; /* if true do a full mode set */ + bool fb_changed = false; /* if true and !mode_changed just do a flip */ struct drm_connector *connector; int count = 0, ro, fail = 0; struct drm_crtc_helper_funcs *crtc_funcs; @@ -758,6 +758,8 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) if (set->crtc->fb == NULL) { DRM_DEBUG("crtc has no fb, full mode set\n"); mode_changed = true; + } else if (set->fb == NULL) { + mode_changed = true; } else if ((set->fb->bits_per_pixel != set->crtc->fb->bits_per_pixel) || set->fb->depth != set->crtc->fb->depth) diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c index 2960b6d7345..9903f270e44 100644 --- a/drivers/gpu/drm/drm_debugfs.c +++ b/drivers/gpu/drm/drm_debugfs.c @@ -101,6 +101,10 @@ int drm_debugfs_create_files(struct drm_info_list *files, int count, continue; tmp = kmalloc(sizeof(struct drm_info_node), GFP_KERNEL); + if (tmp == NULL) { + ret = -1; + goto fail; + } ent = debugfs_create_file(files[i].name, S_IFREG | S_IRUGO, root, tmp, &drm_debugfs_fops); if (!ent) { diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 8104ecaea26..ffe8f4394d5 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -134,26 +134,29 @@ drm_gem_object_alloc(struct drm_device *dev, size_t size) BUG_ON((size & (PAGE_SIZE - 1)) != 0); obj = kzalloc(sizeof(*obj), GFP_KERNEL); + if (!obj) + goto free; obj->dev = dev; obj->filp = shmem_file_setup("drm mm object", size, VM_NORESERVE); - if (IS_ERR(obj->filp)) { - kfree(obj); - return NULL; - } + if (IS_ERR(obj->filp)) + goto free; kref_init(&obj->refcount); kref_init(&obj->handlecount); obj->size = size; if (dev->driver->gem_init_object != NULL && dev->driver->gem_init_object(obj) != 0) { - fput(obj->filp); - kfree(obj); - return NULL; + goto fput; } atomic_inc(&dev->object_count); atomic_add(obj->size, &dev->object_memory); return obj; +fput: + fput(obj->filp); +free: + kfree(obj); + return NULL; } EXPORT_SYMBOL(drm_gem_object_alloc); diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index 155a5bbce68..55bb8a82d61 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c @@ -489,7 +489,7 @@ int drm_put_minor(struct drm_minor **minor_p) */ void drm_put_dev(struct drm_device *dev) { - struct drm_driver *driver = dev->driver; + struct drm_driver *driver; struct drm_map_list *r_list, *list_temp; DRM_DEBUG("\n"); @@ -498,6 +498,7 @@ void drm_put_dev(struct drm_device *dev) DRM_ERROR("cleanup called no dev\n"); return; } + driver = dev->driver; drm_vblank_cleanup(dev); diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile index 5fae1e074b4..013d3805994 100644 --- a/drivers/gpu/drm/radeon/Makefile +++ b/drivers/gpu/drm/radeon/Makefile @@ -13,7 +13,8 @@ radeon-$(CONFIG_DRM_RADEON_KMS) += radeon_device.o radeon_kms.o \ radeon_encoders.o radeon_display.o radeon_cursor.o radeon_i2c.o \ radeon_clocks.o radeon_fb.o radeon_gem.o radeon_ring.o radeon_irq_kms.o \ radeon_cs.o radeon_bios.o radeon_benchmark.o r100.o r300.o r420.o \ - rs400.o rs600.o rs690.o rv515.o r520.o r600.o rs780.o rv770.o + rs400.o rs600.o rs690.o rv515.o r520.o r600.o rs780.o rv770.o \ + radeon_test.o radeon-$(CONFIG_COMPAT) += radeon_ioc32.o diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c index c0080cc9bf8..74d034f77c6 100644 --- a/drivers/gpu/drm/radeon/atombios_crtc.c +++ b/drivers/gpu/drm/radeon/atombios_crtc.c @@ -31,6 +31,132 @@ #include "atom.h" #include "atom-bits.h" +static void atombios_overscan_setup(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + SET_CRTC_OVERSCAN_PS_ALLOCATION args; + int index = GetIndexIntoMasterTable(COMMAND, SetCRTC_OverScan); + int a1, a2; + + memset(&args, 0, sizeof(args)); + + args.usOverscanRight = 0; + args.usOverscanLeft = 0; + args.usOverscanBottom = 0; + args.usOverscanTop = 0; + args.ucCRTC = radeon_crtc->crtc_id; + + switch (radeon_crtc->rmx_type) { + case RMX_CENTER: + args.usOverscanTop = (adjusted_mode->crtc_vdisplay - mode->crtc_vdisplay) / 2; + args.usOverscanBottom = (adjusted_mode->crtc_vdisplay - mode->crtc_vdisplay) / 2; + args.usOverscanLeft = (adjusted_mode->crtc_hdisplay - mode->crtc_hdisplay) / 2; + args.usOverscanRight = (adjusted_mode->crtc_hdisplay - mode->crtc_hdisplay) / 2; + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); + break; + case RMX_ASPECT: + a1 = mode->crtc_vdisplay * adjusted_mode->crtc_hdisplay; + a2 = adjusted_mode->crtc_vdisplay * mode->crtc_hdisplay; + + if (a1 > a2) { + args.usOverscanLeft = (adjusted_mode->crtc_hdisplay - (a2 / mode->crtc_vdisplay)) / 2; + args.usOverscanRight = (adjusted_mode->crtc_hdisplay - (a2 / mode->crtc_vdisplay)) / 2; + } else if (a2 > a1) { + args.usOverscanLeft = (adjusted_mode->crtc_vdisplay - (a1 / mode->crtc_hdisplay)) / 2; + args.usOverscanRight = (adjusted_mode->crtc_vdisplay - (a1 / mode->crtc_hdisplay)) / 2; + } + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); + break; + case RMX_FULL: + default: + args.usOverscanRight = 0; + args.usOverscanLeft = 0; + args.usOverscanBottom = 0; + args.usOverscanTop = 0; + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); + break; + } +} + +static void atombios_scaler_setup(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + ENABLE_SCALER_PS_ALLOCATION args; + int index = GetIndexIntoMasterTable(COMMAND, EnableScaler); + /* fixme - fill in enc_priv for atom dac */ + enum radeon_tv_std tv_std = TV_STD_NTSC; + + if (!ASIC_IS_AVIVO(rdev) && radeon_crtc->crtc_id) + return; + + memset(&args, 0, sizeof(args)); + + args.ucScaler = radeon_crtc->crtc_id; + + if (radeon_crtc->devices & (ATOM_DEVICE_TV_SUPPORT)) { + switch (tv_std) { + case TV_STD_NTSC: + default: + args.ucTVStandard = ATOM_TV_NTSC; + break; + case TV_STD_PAL: + args.ucTVStandard = ATOM_TV_PAL; + break; + case TV_STD_PAL_M: + args.ucTVStandard = ATOM_TV_PALM; + break; + case TV_STD_PAL_60: + args.ucTVStandard = ATOM_TV_PAL60; + break; + case TV_STD_NTSC_J: + args.ucTVStandard = ATOM_TV_NTSCJ; + break; + case TV_STD_SCART_PAL: + args.ucTVStandard = ATOM_TV_PAL; /* ??? */ + break; + case TV_STD_SECAM: + args.ucTVStandard = ATOM_TV_SECAM; + break; + case TV_STD_PAL_CN: + args.ucTVStandard = ATOM_TV_PALCN; + break; + } + args.ucEnable = SCALER_ENABLE_MULTITAP_MODE; + } else if (radeon_crtc->devices & (ATOM_DEVICE_CV_SUPPORT)) { + args.ucTVStandard = ATOM_TV_CV; + args.ucEnable = SCALER_ENABLE_MULTITAP_MODE; + } else { + switch (radeon_crtc->rmx_type) { + case RMX_FULL: + args.ucEnable = ATOM_SCALER_EXPANSION; + break; + case RMX_CENTER: + args.ucEnable = ATOM_SCALER_CENTER; + break; + case RMX_ASPECT: + args.ucEnable = ATOM_SCALER_EXPANSION; + break; + default: + if (ASIC_IS_AVIVO(rdev)) + args.ucEnable = ATOM_SCALER_DISABLE; + else + args.ucEnable = ATOM_SCALER_CENTER; + break; + } + } + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); + if (radeon_crtc->devices & (ATOM_DEVICE_CV_SUPPORT | ATOM_DEVICE_TV_SUPPORT) + && rdev->family >= CHIP_RV515 && rdev->family <= CHIP_RV570) { + atom_rv515_force_tv_scaler(rdev); + } +} + static void atombios_lock_crtc(struct drm_crtc *crtc, int lock) { struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); @@ -203,6 +329,12 @@ void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode) if (ASIC_IS_AVIVO(rdev)) { uint32_t ss_cntl; + if ((rdev->family == CHIP_RS600) || + (rdev->family == CHIP_RS690) || + (rdev->family == CHIP_RS740)) + pll_flags |= (RADEON_PLL_USE_FRAC_FB_DIV | + RADEON_PLL_PREFER_CLOSEST_LOWER); + if (ASIC_IS_DCE32(rdev) && mode->clock > 200000) /* range limits??? */ pll_flags |= RADEON_PLL_PREFER_HIGH_FB_DIV; else @@ -321,7 +453,7 @@ int atombios_crtc_set_base(struct drm_crtc *crtc, int x, int y, struct drm_gem_object *obj; struct drm_radeon_gem_object *obj_priv; uint64_t fb_location; - uint32_t fb_format, fb_pitch_pixels; + uint32_t fb_format, fb_pitch_pixels, tiling_flags; if (!crtc->fb) return -EINVAL; @@ -358,7 +490,14 @@ int atombios_crtc_set_base(struct drm_crtc *crtc, int x, int y, return -EINVAL; } - /* TODO tiling */ + radeon_object_get_tiling_flags(obj->driver_private, + &tiling_flags, NULL); + if (tiling_flags & RADEON_TILING_MACRO) + fb_format |= AVIVO_D1GRPH_MACRO_ADDRESS_MODE; + + if (tiling_flags & RADEON_TILING_MICRO) + fb_format |= AVIVO_D1GRPH_TILED; + if (radeon_crtc->crtc_id == 0) WREG32(AVIVO_D1VGA_CONTROL, 0); else @@ -509,6 +648,9 @@ int atombios_crtc_mode_set(struct drm_crtc *crtc, radeon_crtc_set_base(crtc, x, y, old_fb); radeon_legacy_atom_set_surface(crtc); } + atombios_overscan_setup(crtc, mode, adjusted_mode); + atombios_scaler_setup(crtc); + radeon_bandwidth_update(rdev); return 0; } @@ -516,6 +658,8 @@ static bool atombios_crtc_mode_fixup(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { + if (!radeon_crtc_scaling_mode_fixup(crtc, mode, adjusted_mode)) + return false; return true; } @@ -548,148 +692,3 @@ void radeon_atombios_init_crtc(struct drm_device *dev, AVIVO_D2CRTC_H_TOTAL - AVIVO_D1CRTC_H_TOTAL; drm_crtc_helper_add(&radeon_crtc->base, &atombios_helper_funcs); } - -void radeon_init_disp_bw_avivo(struct drm_device *dev, - struct drm_display_mode *mode1, - uint32_t pixel_bytes1, - struct drm_display_mode *mode2, - uint32_t pixel_bytes2) -{ - struct radeon_device *rdev = dev->dev_private; - fixed20_12 min_mem_eff; - fixed20_12 peak_disp_bw, mem_bw, pix_clk, pix_clk2, temp_ff; - fixed20_12 sclk_ff, mclk_ff; - uint32_t dc_lb_memory_split, temp; - - min_mem_eff.full = rfixed_const_8(0); - if (rdev->disp_priority == 2) { - uint32_t mc_init_misc_lat_timer = 0; - if (rdev->family == CHIP_RV515) - mc_init_misc_lat_timer = - RREG32_MC(RV515_MC_INIT_MISC_LAT_TIMER); - else if (rdev->family == CHIP_RS690) - mc_init_misc_lat_timer = - RREG32_MC(RS690_MC_INIT_MISC_LAT_TIMER); - - mc_init_misc_lat_timer &= - ~(R300_MC_DISP1R_INIT_LAT_MASK << - R300_MC_DISP1R_INIT_LAT_SHIFT); - mc_init_misc_lat_timer &= - ~(R300_MC_DISP0R_INIT_LAT_MASK << - R300_MC_DISP0R_INIT_LAT_SHIFT); - - if (mode2) - mc_init_misc_lat_timer |= - (1 << R300_MC_DISP1R_INIT_LAT_SHIFT); - if (mode1) - mc_init_misc_lat_timer |= - (1 << R300_MC_DISP0R_INIT_LAT_SHIFT); - - if (rdev->family == CHIP_RV515) - WREG32_MC(RV515_MC_INIT_MISC_LAT_TIMER, - mc_init_misc_lat_timer); - else if (rdev->family == CHIP_RS690) - WREG32_MC(RS690_MC_INIT_MISC_LAT_TIMER, - mc_init_misc_lat_timer); - } - - /* - * determine is there is enough bw for current mode - */ - temp_ff.full = rfixed_const(100); - mclk_ff.full = rfixed_const(rdev->clock.default_mclk); - mclk_ff.full = rfixed_div(mclk_ff, temp_ff); - sclk_ff.full = rfixed_const(rdev->clock.default_sclk); - sclk_ff.full = rfixed_div(sclk_ff, temp_ff); - - temp = (rdev->mc.vram_width / 8) * (rdev->mc.vram_is_ddr ? 2 : 1); - temp_ff.full = rfixed_const(temp); - mem_bw.full = rfixed_mul(mclk_ff, temp_ff); - mem_bw.full = rfixed_mul(mem_bw, min_mem_eff); - - pix_clk.full = 0; - pix_clk2.full = 0; - peak_disp_bw.full = 0; - if (mode1) { - temp_ff.full = rfixed_const(1000); - pix_clk.full = rfixed_const(mode1->clock); /* convert to fixed point */ - pix_clk.full = rfixed_div(pix_clk, temp_ff); - temp_ff.full = rfixed_const(pixel_bytes1); - peak_disp_bw.full += rfixed_mul(pix_clk, temp_ff); - } - if (mode2) { - temp_ff.full = rfixed_const(1000); - pix_clk2.full = rfixed_const(mode2->clock); /* convert to fixed point */ - pix_clk2.full = rfixed_div(pix_clk2, temp_ff); - temp_ff.full = rfixed_const(pixel_bytes2); - peak_disp_bw.full += rfixed_mul(pix_clk2, temp_ff); - } - - if (peak_disp_bw.full >= mem_bw.full) { - DRM_ERROR - ("You may not have enough display bandwidth for current mode\n" - "If you have flickering problem, try to lower resolution, refresh rate, or color depth\n"); - printk("peak disp bw %d, mem_bw %d\n", - rfixed_trunc(peak_disp_bw), rfixed_trunc(mem_bw)); - } - - /* - * Line Buffer Setup - * There is a single line buffer shared by both display controllers. - * DC_LB_MEMORY_SPLIT controls how that line buffer is shared between the display - * controllers. The paritioning can either be done manually or via one of four - * preset allocations specified in bits 1:0: - * 0 - line buffer is divided in half and shared between each display controller - * 1 - D1 gets 3/4 of the line buffer, D2 gets 1/4 - * 2 - D1 gets the whole buffer - * 3 - D1 gets 1/4 of the line buffer, D2 gets 3/4 - * Setting bit 2 of DC_LB_MEMORY_SPLIT controls switches to manual allocation mode. - * In manual allocation mode, D1 always starts at 0, D1 end/2 is specified in bits - * 14:4; D2 allocation follows D1. - */ - - /* is auto or manual better ? */ - dc_lb_memory_split = - RREG32(AVIVO_DC_LB_MEMORY_SPLIT) & ~AVIVO_DC_LB_MEMORY_SPLIT_MASK; - dc_lb_memory_split &= ~AVIVO_DC_LB_MEMORY_SPLIT_SHIFT_MODE; -#if 1 - /* auto */ - if (mode1 && mode2) { - if (mode1->hdisplay > mode2->hdisplay) { - if (mode1->hdisplay > 2560) - dc_lb_memory_split |= - AVIVO_DC_LB_MEMORY_SPLIT_D1_3Q_D2_1Q; - else - dc_lb_memory_split |= - AVIVO_DC_LB_MEMORY_SPLIT_D1HALF_D2HALF; - } else if (mode2->hdisplay > mode1->hdisplay) { - if (mode2->hdisplay > 2560) - dc_lb_memory_split |= - AVIVO_DC_LB_MEMORY_SPLIT_D1_1Q_D2_3Q; - else - dc_lb_memory_split |= - AVIVO_DC_LB_MEMORY_SPLIT_D1HALF_D2HALF; - } else - dc_lb_memory_split |= - AVIVO_DC_LB_MEMORY_SPLIT_D1HALF_D2HALF; - } else if (mode1) { - dc_lb_memory_split |= AVIVO_DC_LB_MEMORY_SPLIT_D1_ONLY; - } else if (mode2) { - dc_lb_memory_split |= AVIVO_DC_LB_MEMORY_SPLIT_D1_1Q_D2_3Q; - } -#else - /* manual */ - dc_lb_memory_split |= AVIVO_DC_LB_MEMORY_SPLIT_SHIFT_MODE; - dc_lb_memory_split &= - ~(AVIVO_DC_LB_DISP1_END_ADR_MASK << - AVIVO_DC_LB_DISP1_END_ADR_SHIFT); - if (mode1) { - dc_lb_memory_split |= - ((((mode1->hdisplay / 2) + 64) & AVIVO_DC_LB_DISP1_END_ADR_MASK) - << AVIVO_DC_LB_DISP1_END_ADR_SHIFT); - } else if (mode2) { - dc_lb_memory_split |= (0 << AVIVO_DC_LB_DISP1_END_ADR_SHIFT); - } -#endif - WREG32(AVIVO_DC_LB_MEMORY_SPLIT, dc_lb_memory_split); -} diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c index c550932a108..f1ba8ff4113 100644 --- a/drivers/gpu/drm/radeon/r100.c +++ b/drivers/gpu/drm/radeon/r100.c @@ -110,7 +110,7 @@ int r100_pci_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr) if (i < 0 || i > rdev->gart.num_gpu_pages) { return -EINVAL; } - rdev->gart.table.ram.ptr[i] = cpu_to_le32((uint32_t)addr); + rdev->gart.table.ram.ptr[i] = cpu_to_le32(lower_32_bits(addr)); return 0; } @@ -173,8 +173,12 @@ void r100_mc_setup(struct radeon_device *rdev) DRM_ERROR("Failed to register debugfs file for R100 MC !\n"); } /* Write VRAM size in case we are limiting it */ - WREG32(RADEON_CONFIG_MEMSIZE, rdev->mc.vram_size); - tmp = rdev->mc.vram_location + rdev->mc.vram_size - 1; + WREG32(RADEON_CONFIG_MEMSIZE, rdev->mc.real_vram_size); + /* Novell bug 204882 for RN50/M6/M7 with 8/16/32MB VRAM, + * if the aperture is 64MB but we have 32MB VRAM + * we report only 32MB VRAM but we have to set MC_FB_LOCATION + * to 64MB, otherwise the gpu accidentially dies */ + tmp = rdev->mc.vram_location + rdev->mc.mc_vram_size - 1; tmp = REG_SET(RADEON_MC_FB_TOP, tmp >> 16); tmp |= REG_SET(RADEON_MC_FB_START, rdev->mc.vram_location >> 16); WREG32(RADEON_MC_FB_LOCATION, tmp); @@ -215,7 +219,6 @@ int r100_mc_init(struct radeon_device *rdev) r100_pci_gart_disable(rdev); /* Setup GPU memory space */ - rdev->mc.vram_location = 0xFFFFFFFFUL; rdev->mc.gtt_location = 0xFFFFFFFFUL; if (rdev->flags & RADEON_IS_AGP) { r = radeon_agp_init(rdev); @@ -719,13 +722,14 @@ int r100_cs_packet_parse(struct radeon_cs_parser *p, unsigned idx) { struct radeon_cs_chunk *ib_chunk = &p->chunks[p->chunk_ib_idx]; - uint32_t header = ib_chunk->kdata[idx]; + uint32_t header; if (idx >= ib_chunk->length_dw) { DRM_ERROR("Can not parse packet at %d after CS end %d !\n", idx, ib_chunk->length_dw); return -EINVAL; } + header = ib_chunk->kdata[idx]; pkt->idx = idx; pkt->type = CP_PACKET_GET_TYPE(header); pkt->count = CP_PACKET_GET_COUNT(header); @@ -753,6 +757,102 @@ int r100_cs_packet_parse(struct radeon_cs_parser *p, } /** + * r100_cs_packet_next_vline() - parse userspace VLINE packet + * @parser: parser structure holding parsing context. + * + * Userspace sends a special sequence for VLINE waits. + * PACKET0 - VLINE_START_END + value + * PACKET0 - WAIT_UNTIL +_value + * RELOC (P3) - crtc_id in reloc. + * + * This function parses this and relocates the VLINE START END + * and WAIT UNTIL packets to the correct crtc. + * It also detects a switched off crtc and nulls out the + * wait in that case. + */ +int r100_cs_packet_parse_vline(struct radeon_cs_parser *p) +{ + struct radeon_cs_chunk *ib_chunk; + struct drm_mode_object *obj; + struct drm_crtc *crtc; + struct radeon_crtc *radeon_crtc; + struct radeon_cs_packet p3reloc, waitreloc; + int crtc_id; + int r; + uint32_t header, h_idx, reg; + + ib_chunk = &p->chunks[p->chunk_ib_idx]; + + /* parse the wait until */ + r = r100_cs_packet_parse(p, &waitreloc, p->idx); + if (r) + return r; + + /* check its a wait until and only 1 count */ + if (waitreloc.reg != RADEON_WAIT_UNTIL || + waitreloc.count != 0) { + DRM_ERROR("vline wait had illegal wait until segment\n"); + r = -EINVAL; + return r; + } + + if (ib_chunk->kdata[waitreloc.idx + 1] != RADEON_WAIT_CRTC_VLINE) { + DRM_ERROR("vline wait had illegal wait until\n"); + r = -EINVAL; + return r; + } + + /* jump over the NOP */ + r = r100_cs_packet_parse(p, &p3reloc, p->idx); + if (r) + return r; + + h_idx = p->idx - 2; + p->idx += waitreloc.count; + p->idx += p3reloc.count; + + header = ib_chunk->kdata[h_idx]; + crtc_id = ib_chunk->kdata[h_idx + 5]; + reg = ib_chunk->kdata[h_idx] >> 2; + mutex_lock(&p->rdev->ddev->mode_config.mutex); + obj = drm_mode_object_find(p->rdev->ddev, crtc_id, DRM_MODE_OBJECT_CRTC); + if (!obj) { + DRM_ERROR("cannot find crtc %d\n", crtc_id); + r = -EINVAL; + goto out; + } + crtc = obj_to_crtc(obj); + radeon_crtc = to_radeon_crtc(crtc); + crtc_id = radeon_crtc->crtc_id; + + if (!crtc->enabled) { + /* if the CRTC isn't enabled - we need to nop out the wait until */ + ib_chunk->kdata[h_idx + 2] = PACKET2(0); + ib_chunk->kdata[h_idx + 3] = PACKET2(0); + } else if (crtc_id == 1) { + switch (reg) { + case AVIVO_D1MODE_VLINE_START_END: + header &= R300_CP_PACKET0_REG_MASK; + header |= AVIVO_D2MODE_VLINE_START_END >> 2; + break; + case RADEON_CRTC_GUI_TRIG_VLINE: + header &= R300_CP_PACKET0_REG_MASK; + header |= RADEON_CRTC2_GUI_TRIG_VLINE >> 2; + break; + default: + DRM_ERROR("unknown crtc reloc\n"); + r = -EINVAL; + goto out; + } + ib_chunk->kdata[h_idx] = header; + ib_chunk->kdata[h_idx + 3] |= RADEON_ENG_DISPLAY_SELECT_CRTC1; + } +out: + mutex_unlock(&p->rdev->ddev->mode_config.mutex); + return r; +} + +/** * r100_cs_packet_next_reloc() - parse next packet which should be reloc packet3 * @parser: parser structure holding parsing context. * @data: pointer to relocation data @@ -814,6 +914,7 @@ static int r100_packet0_check(struct radeon_cs_parser *p, unsigned idx; bool onereg; int r; + u32 tile_flags = 0; ib = p->ib->ptr; ib_chunk = &p->chunks[p->chunk_ib_idx]; @@ -825,6 +926,15 @@ static int r100_packet0_check(struct radeon_cs_parser *p, } for (i = 0; i <= pkt->count; i++, idx++, reg += 4) { switch (reg) { + case RADEON_CRTC_GUI_TRIG_VLINE: + r = r100_cs_packet_parse_vline(p); + if (r) { + DRM_ERROR("No reloc for ib[%d]=0x%04X\n", + idx, reg); + r100_cs_dump_packet(p, pkt); + return r; + } + break; /* FIXME: only allow PACKET3 blit? easier to check for out of * range access */ case RADEON_DST_PITCH_OFFSET: @@ -838,7 +948,20 @@ static int r100_packet0_check(struct radeon_cs_parser *p, } tmp = ib_chunk->kdata[idx] & 0x003fffff; tmp += (((u32)reloc->lobj.gpu_offset) >> 10); - ib[idx] = (ib_chunk->kdata[idx] & 0xffc00000) | tmp; + + if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) + tile_flags |= RADEON_DST_TILE_MACRO; + if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) { + if (reg == RADEON_SRC_PITCH_OFFSET) { + DRM_ERROR("Cannot src blit from microtiled surface\n"); + r100_cs_dump_packet(p, pkt); + return -EINVAL; + } + tile_flags |= RADEON_DST_TILE_MICRO; + } + + tmp |= tile_flags; + ib[idx] = (ib_chunk->kdata[idx] & 0x3fc00000) | tmp; break; case RADEON_RB3D_DEPTHOFFSET: case RADEON_RB3D_COLOROFFSET: @@ -869,6 +992,11 @@ static int r100_packet0_check(struct radeon_cs_parser *p, case R300_TX_OFFSET_0+52: case R300_TX_OFFSET_0+56: case R300_TX_OFFSET_0+60: + /* rn50 has no 3D engine so fail on any 3d setup */ + if (ASIC_IS_RN50(p->rdev)) { + DRM_ERROR("attempt to use RN50 3D engine failed\n"); + return -EINVAL; + } r = r100_cs_packet_next_reloc(p, &reloc); if (r) { DRM_ERROR("No reloc for ib[%d]=0x%04X\n", @@ -878,6 +1006,25 @@ static int r100_packet0_check(struct radeon_cs_parser *p, } ib[idx] = ib_chunk->kdata[idx] + ((u32)reloc->lobj.gpu_offset); break; + case R300_RB3D_COLORPITCH0: + case RADEON_RB3D_COLORPITCH: + r = r100_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("No reloc for ib[%d]=0x%04X\n", + idx, reg); + r100_cs_dump_packet(p, pkt); + return r; + } + + if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) + tile_flags |= RADEON_COLOR_TILE_ENABLE; + if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) + tile_flags |= RADEON_COLOR_MICROTILE_ENABLE; + + tmp = ib_chunk->kdata[idx] & ~(0x7 << 16); + tmp |= tile_flags; + ib[idx] = tmp; + break; default: /* FIXME: we don't want to allow anyothers packet */ break; @@ -1256,29 +1403,100 @@ static void r100_vram_get_type(struct radeon_device *rdev) } } -void r100_vram_info(struct radeon_device *rdev) +static u32 r100_get_accessible_vram(struct radeon_device *rdev) { - r100_vram_get_type(rdev); + u32 aper_size; + u8 byte; + + aper_size = RREG32(RADEON_CONFIG_APER_SIZE); + + /* Set HDP_APER_CNTL only on cards that are known not to be broken, + * that is has the 2nd generation multifunction PCI interface + */ + if (rdev->family == CHIP_RV280 || + rdev->family >= CHIP_RV350) { + WREG32_P(RADEON_HOST_PATH_CNTL, RADEON_HDP_APER_CNTL, + ~RADEON_HDP_APER_CNTL); + DRM_INFO("Generation 2 PCI interface, using max accessible memory\n"); + return aper_size * 2; + } + + /* Older cards have all sorts of funny issues to deal with. First + * check if it's a multifunction card by reading the PCI config + * header type... Limit those to one aperture size + */ + pci_read_config_byte(rdev->pdev, 0xe, &byte); + if (byte & 0x80) { + DRM_INFO("Generation 1 PCI interface in multifunction mode\n"); + DRM_INFO("Limiting VRAM to one aperture\n"); + return aper_size; + } + + /* Single function older card. We read HDP_APER_CNTL to see how the BIOS + * have set it up. We don't write this as it's broken on some ASICs but + * we expect the BIOS to have done the right thing (might be too optimistic...) + */ + if (RREG32(RADEON_HOST_PATH_CNTL) & RADEON_HDP_APER_CNTL) + return aper_size * 2; + return aper_size; +} + +void r100_vram_init_sizes(struct radeon_device *rdev) +{ + u64 config_aper_size; + u32 accessible; + + config_aper_size = RREG32(RADEON_CONFIG_APER_SIZE); if (rdev->flags & RADEON_IS_IGP) { uint32_t tom; /* read NB_TOM to get the amount of ram stolen for the GPU */ tom = RREG32(RADEON_NB_TOM); - rdev->mc.vram_size = (((tom >> 16) - (tom & 0xffff) + 1) << 16); - WREG32(RADEON_CONFIG_MEMSIZE, rdev->mc.vram_size); + rdev->mc.real_vram_size = (((tom >> 16) - (tom & 0xffff) + 1) << 16); + /* for IGPs we need to keep VRAM where it was put by the BIOS */ + rdev->mc.vram_location = (tom & 0xffff) << 16; + WREG32(RADEON_CONFIG_MEMSIZE, rdev->mc.real_vram_size); + rdev->mc.mc_vram_size = rdev->mc.real_vram_size; } else { - rdev->mc.vram_size = RREG32(RADEON_CONFIG_MEMSIZE); + rdev->mc.real_vram_size = RREG32(RADEON_CONFIG_MEMSIZE); /* Some production boards of m6 will report 0 * if it's 8 MB */ - if (rdev->mc.vram_size == 0) { - rdev->mc.vram_size = 8192 * 1024; - WREG32(RADEON_CONFIG_MEMSIZE, rdev->mc.vram_size); + if (rdev->mc.real_vram_size == 0) { + rdev->mc.real_vram_size = 8192 * 1024; + WREG32(RADEON_CONFIG_MEMSIZE, rdev->mc.real_vram_size); } + /* let driver place VRAM */ + rdev->mc.vram_location = 0xFFFFFFFFUL; + /* Fix for RN50, M6, M7 with 8/16/32(??) MBs of VRAM - + * Novell bug 204882 + along with lots of ubuntu ones */ + if (config_aper_size > rdev->mc.real_vram_size) + rdev->mc.mc_vram_size = config_aper_size; + else + rdev->mc.mc_vram_size = rdev->mc.real_vram_size; } + /* work out accessible VRAM */ + accessible = r100_get_accessible_vram(rdev); + rdev->mc.aper_base = drm_get_resource_start(rdev->ddev, 0); rdev->mc.aper_size = drm_get_resource_len(rdev->ddev, 0); + + if (accessible > rdev->mc.aper_size) + accessible = rdev->mc.aper_size; + + if (rdev->mc.mc_vram_size > rdev->mc.aper_size) + rdev->mc.mc_vram_size = rdev->mc.aper_size; + + if (rdev->mc.real_vram_size > rdev->mc.aper_size) + rdev->mc.real_vram_size = rdev->mc.aper_size; +} + +void r100_vram_info(struct radeon_device *rdev) +{ + r100_vram_get_type(rdev); + + r100_vram_init_sizes(rdev); } @@ -1533,3 +1751,530 @@ int r100_debugfs_mc_info_init(struct radeon_device *rdev) return 0; #endif } + +int r100_set_surface_reg(struct radeon_device *rdev, int reg, + uint32_t tiling_flags, uint32_t pitch, + uint32_t offset, uint32_t obj_size) +{ + int surf_index = reg * 16; + int flags = 0; + + /* r100/r200 divide by 16 */ + if (rdev->family < CHIP_R300) + flags = pitch / 16; + else + flags = pitch / 8; + + if (rdev->family <= CHIP_RS200) { + if ((tiling_flags & (RADEON_TILING_MACRO|RADEON_TILING_MICRO)) + == (RADEON_TILING_MACRO|RADEON_TILING_MICRO)) + flags |= RADEON_SURF_TILE_COLOR_BOTH; + if (tiling_flags & RADEON_TILING_MACRO) + flags |= RADEON_SURF_TILE_COLOR_MACRO; + } else if (rdev->family <= CHIP_RV280) { + if (tiling_flags & (RADEON_TILING_MACRO)) + flags |= R200_SURF_TILE_COLOR_MACRO; + if (tiling_flags & RADEON_TILING_MICRO) + flags |= R200_SURF_TILE_COLOR_MICRO; + } else { + if (tiling_flags & RADEON_TILING_MACRO) + flags |= R300_SURF_TILE_MACRO; + if (tiling_flags & RADEON_TILING_MICRO) + flags |= R300_SURF_TILE_MICRO; + } + + DRM_DEBUG("writing surface %d %d %x %x\n", reg, flags, offset, offset+obj_size-1); + WREG32(RADEON_SURFACE0_INFO + surf_index, flags); + WREG32(RADEON_SURFACE0_LOWER_BOUND + surf_index, offset); + WREG32(RADEON_SURFACE0_UPPER_BOUND + surf_index, offset + obj_size - 1); + return 0; +} + +void r100_clear_surface_reg(struct radeon_device *rdev, int reg) +{ + int surf_index = reg * 16; + WREG32(RADEON_SURFACE0_INFO + surf_index, 0); +} + +void r100_bandwidth_update(struct radeon_device *rdev) +{ + fixed20_12 trcd_ff, trp_ff, tras_ff, trbs_ff, tcas_ff; + fixed20_12 sclk_ff, mclk_ff, sclk_eff_ff, sclk_delay_ff; + fixed20_12 peak_disp_bw, mem_bw, pix_clk, pix_clk2, temp_ff, crit_point_ff; + uint32_t temp, data, mem_trcd, mem_trp, mem_tras; + fixed20_12 memtcas_ff[8] = { + fixed_init(1), + fixed_init(2), + fixed_init(3), + fixed_init(0), + fixed_init_half(1), + fixed_init_half(2), + fixed_init(0), + }; + fixed20_12 memtcas_rs480_ff[8] = { + fixed_init(0), + fixed_init(1), + fixed_init(2), + fixed_init(3), + fixed_init(0), + fixed_init_half(1), + fixed_init_half(2), + fixed_init_half(3), + }; + fixed20_12 memtcas2_ff[8] = { + fixed_init(0), + fixed_init(1), + fixed_init(2), + fixed_init(3), + fixed_init(4), + fixed_init(5), + fixed_init(6), + fixed_init(7), + }; + fixed20_12 memtrbs[8] = { + fixed_init(1), + fixed_init_half(1), + fixed_init(2), + fixed_init_half(2), + fixed_init(3), + fixed_init_half(3), + fixed_init(4), + fixed_init_half(4) + }; + fixed20_12 memtrbs_r4xx[8] = { + fixed_init(4), + fixed_init(5), + fixed_init(6), + fixed_init(7), + fixed_init(8), + fixed_init(9), + fixed_init(10), + fixed_init(11) + }; + fixed20_12 min_mem_eff; + fixed20_12 mc_latency_sclk, mc_latency_mclk, k1; + fixed20_12 cur_latency_mclk, cur_latency_sclk; + fixed20_12 disp_latency, disp_latency_overhead, disp_drain_rate, + disp_drain_rate2, read_return_rate; + fixed20_12 time_disp1_drop_priority; + int c; + int cur_size = 16; /* in octawords */ + int critical_point = 0, critical_point2; +/* uint32_t read_return_rate, time_disp1_drop_priority; */ + int stop_req, max_stop_req; + struct drm_display_mode *mode1 = NULL; + struct drm_display_mode *mode2 = NULL; + uint32_t pixel_bytes1 = 0; + uint32_t pixel_bytes2 = 0; + + if (rdev->mode_info.crtcs[0]->base.enabled) { + mode1 = &rdev->mode_info.crtcs[0]->base.mode; + pixel_bytes1 = rdev->mode_info.crtcs[0]->base.fb->bits_per_pixel / 8; + } + if (rdev->mode_info.crtcs[1]->base.enabled) { + mode2 = &rdev->mode_info.crtcs[1]->base.mode; + pixel_bytes2 = rdev->mode_info.crtcs[1]->base.fb->bits_per_pixel / 8; + } + + min_mem_eff.full = rfixed_const_8(0); + /* get modes */ + if ((rdev->disp_priority == 2) && ASIC_IS_R300(rdev)) { + uint32_t mc_init_misc_lat_timer = RREG32(R300_MC_INIT_MISC_LAT_TIMER); + mc_init_misc_lat_timer &= ~(R300_MC_DISP1R_INIT_LAT_MASK << R300_MC_DISP1R_INIT_LAT_SHIFT); + mc_init_misc_lat_timer &= ~(R300_MC_DISP0R_INIT_LAT_MASK << R300_MC_DISP0R_INIT_LAT_SHIFT); + /* check crtc enables */ + if (mode2) + mc_init_misc_lat_timer |= (1 << R300_MC_DISP1R_INIT_LAT_SHIFT); + if (mode1) + mc_init_misc_lat_timer |= (1 << R300_MC_DISP0R_INIT_LAT_SHIFT); + WREG32(R300_MC_INIT_MISC_LAT_TIMER, mc_init_misc_lat_timer); + } + + /* + * determine is there is enough bw for current mode + */ + mclk_ff.full = rfixed_const(rdev->clock.default_mclk); + temp_ff.full = rfixed_const(100); + mclk_ff.full = rfixed_div(mclk_ff, temp_ff); + sclk_ff.full = rfixed_const(rdev->clock.default_sclk); + sclk_ff.full = rfixed_div(sclk_ff, temp_ff); + + temp = (rdev->mc.vram_width / 8) * (rdev->mc.vram_is_ddr ? 2 : 1); + temp_ff.full = rfixed_const(temp); + mem_bw.full = rfixed_mul(mclk_ff, temp_ff); + + pix_clk.full = 0; + pix_clk2.full = 0; + peak_disp_bw.full = 0; + if (mode1) { + temp_ff.full = rfixed_const(1000); + pix_clk.full = rfixed_const(mode1->clock); /* convert to fixed point */ + pix_clk.full = rfixed_div(pix_clk, temp_ff); + temp_ff.full = rfixed_const(pixel_bytes1); + peak_disp_bw.full += rfixed_mul(pix_clk, temp_ff); + } + if (mode2) { + temp_ff.full = rfixed_const(1000); + pix_clk2.full = rfixed_const(mode2->clock); /* convert to fixed point */ + pix_clk2.full = rfixed_div(pix_clk2, temp_ff); + temp_ff.full = rfixed_const(pixel_bytes2); + peak_disp_bw.full += rfixed_mul(pix_clk2, temp_ff); + } + + mem_bw.full = rfixed_mul(mem_bw, min_mem_eff); + if (peak_disp_bw.full >= mem_bw.full) { + DRM_ERROR("You may not have enough display bandwidth for current mode\n" + "If you have flickering problem, try to lower resolution, refresh rate, or color depth\n"); + } + + /* Get values from the EXT_MEM_CNTL register...converting its contents. */ + temp = RREG32(RADEON_MEM_TIMING_CNTL); + if ((rdev->family == CHIP_RV100) || (rdev->flags & RADEON_IS_IGP)) { /* RV100, M6, IGPs */ + mem_trcd = ((temp >> 2) & 0x3) + 1; + mem_trp = ((temp & 0x3)) + 1; + mem_tras = ((temp & 0x70) >> 4) + 1; + } else if (rdev->family == CHIP_R300 || + rdev->family == CHIP_R350) { /* r300, r350 */ + mem_trcd = (temp & 0x7) + 1; + mem_trp = ((temp >> 8) & 0x7) + 1; + mem_tras = ((temp >> 11) & 0xf) + 4; + } else if (rdev->family == CHIP_RV350 || + rdev->family <= CHIP_RV380) { + /* rv3x0 */ + mem_trcd = (temp & 0x7) + 3; + mem_trp = ((temp >> 8) & 0x7) + 3; + mem_tras = ((temp >> 11) & 0xf) + 6; + } else if (rdev->family == CHIP_R420 || + rdev->family == CHIP_R423 || + rdev->family == CHIP_RV410) { + /* r4xx */ + mem_trcd = (temp & 0xf) + 3; + if (mem_trcd > 15) + mem_trcd = 15; + mem_trp = ((temp >> 8) & 0xf) + 3; + if (mem_trp > 15) + mem_trp = 15; + mem_tras = ((temp >> 12) & 0x1f) + 6; + if (mem_tras > 31) + mem_tras = 31; + } else { /* RV200, R200 */ + mem_trcd = (temp & 0x7) + 1; + mem_trp = ((temp >> 8) & 0x7) + 1; + mem_tras = ((temp >> 12) & 0xf) + 4; + } + /* convert to FF */ + trcd_ff.full = rfixed_const(mem_trcd); + trp_ff.full = rfixed_const(mem_trp); + tras_ff.full = rfixed_const(mem_tras); + + /* Get values from the MEM_SDRAM_MODE_REG register...converting its */ + temp = RREG32(RADEON_MEM_SDRAM_MODE_REG); + data = (temp & (7 << 20)) >> 20; + if ((rdev->family == CHIP_RV100) || rdev->flags & RADEON_IS_IGP) { + if (rdev->family == CHIP_RS480) /* don't think rs400 */ + tcas_ff = memtcas_rs480_ff[data]; + else + tcas_ff = memtcas_ff[data]; + } else + tcas_ff = memtcas2_ff[data]; + + if (rdev->family == CHIP_RS400 || + rdev->family == CHIP_RS480) { + /* extra cas latency stored in bits 23-25 0-4 clocks */ + data = (temp >> 23) & 0x7; + if (data < 5) + tcas_ff.full += rfixed_const(data); + } + + if (ASIC_IS_R300(rdev) && !(rdev->flags & RADEON_IS_IGP)) { + /* on the R300, Tcas is included in Trbs. + */ + temp = RREG32(RADEON_MEM_CNTL); + data = (R300_MEM_NUM_CHANNELS_MASK & temp); + if (data == 1) { + if (R300_MEM_USE_CD_CH_ONLY & temp) { + temp = RREG32(R300_MC_IND_INDEX); + temp &= ~R300_MC_IND_ADDR_MASK; + temp |= R300_MC_READ_CNTL_CD_mcind; + WREG32(R300_MC_IND_INDEX, temp); + temp = RREG32(R300_MC_IND_DATA); + data = (R300_MEM_RBS_POSITION_C_MASK & temp); + } else { + temp = RREG32(R300_MC_READ_CNTL_AB); + data = (R300_MEM_RBS_POSITION_A_MASK & temp); + } + } else { + temp = RREG32(R300_MC_READ_CNTL_AB); + data = (R300_MEM_RBS_POSITION_A_MASK & temp); + } + if (rdev->family == CHIP_RV410 || + rdev->family == CHIP_R420 || + rdev->family == CHIP_R423) + trbs_ff = memtrbs_r4xx[data]; + else + trbs_ff = memtrbs[data]; + tcas_ff.full += trbs_ff.full; + } + + sclk_eff_ff.full = sclk_ff.full; + + if (rdev->flags & RADEON_IS_AGP) { + fixed20_12 agpmode_ff; + agpmode_ff.full = rfixed_const(radeon_agpmode); + temp_ff.full = rfixed_const_666(16); + sclk_eff_ff.full -= rfixed_mul(agpmode_ff, temp_ff); + } + /* TODO PCIE lanes may affect this - agpmode == 16?? */ + + if (ASIC_IS_R300(rdev)) { + sclk_delay_ff.full = rfixed_const(250); + } else { + if ((rdev->family == CHIP_RV100) || + rdev->flags & RADEON_IS_IGP) { + if (rdev->mc.vram_is_ddr) + sclk_delay_ff.full = rfixed_const(41); + else + sclk_delay_ff.full = rfixed_const(33); + } else { + if (rdev->mc.vram_width == 128) + sclk_delay_ff.full = rfixed_const(57); + else + sclk_delay_ff.full = rfixed_const(41); + } + } + + mc_latency_sclk.full = rfixed_div(sclk_delay_ff, sclk_eff_ff); + + if (rdev->mc.vram_is_ddr) { + if (rdev->mc.vram_width == 32) { + k1.full = rfixed_const(40); + c = 3; + } else { + k1.full = rfixed_const(20); + c = 1; + } + } else { + k1.full = rfixed_const(40); + c = 3; + } + + temp_ff.full = rfixed_const(2); + mc_latency_mclk.full = rfixed_mul(trcd_ff, temp_ff); + temp_ff.full = rfixed_const(c); + mc_latency_mclk.full += rfixed_mul(tcas_ff, temp_ff); + temp_ff.full = rfixed_const(4); + mc_latency_mclk.full += rfixed_mul(tras_ff, temp_ff); + mc_latency_mclk.full += rfixed_mul(trp_ff, temp_ff); + mc_latency_mclk.full += k1.full; + + mc_latency_mclk.full = rfixed_div(mc_latency_mclk, mclk_ff); + mc_latency_mclk.full += rfixed_div(temp_ff, sclk_eff_ff); + + /* + HW cursor time assuming worst case of full size colour cursor. + */ + temp_ff.full = rfixed_const((2 * (cur_size - (rdev->mc.vram_is_ddr + 1)))); + temp_ff.full += trcd_ff.full; + if (temp_ff.full < tras_ff.full) + temp_ff.full = tras_ff.full; + cur_latency_mclk.full = rfixed_div(temp_ff, mclk_ff); + + temp_ff.full = rfixed_const(cur_size); + cur_latency_sclk.full = rfixed_div(temp_ff, sclk_eff_ff); + /* + Find the total latency for the display data. + */ + disp_latency_overhead.full = rfixed_const(80); + disp_latency_overhead.full = rfixed_div(disp_latency_overhead, sclk_ff); + mc_latency_mclk.full += disp_latency_overhead.full + cur_latency_mclk.full; + mc_latency_sclk.full += disp_latency_overhead.full + cur_latency_sclk.full; + + if (mc_latency_mclk.full > mc_latency_sclk.full) + disp_latency.full = mc_latency_mclk.full; + else + disp_latency.full = mc_latency_sclk.full; + + /* setup Max GRPH_STOP_REQ default value */ + if (ASIC_IS_RV100(rdev)) + max_stop_req = 0x5c; + else + max_stop_req = 0x7c; + + if (mode1) { + /* CRTC1 + Set GRPH_BUFFER_CNTL register using h/w defined optimal values. + GRPH_STOP_REQ <= MIN[ 0x7C, (CRTC_H_DISP + 1) * (bit depth) / 0x10 ] + */ + stop_req = mode1->hdisplay * pixel_bytes1 / 16; + + if (stop_req > max_stop_req) + stop_req = max_stop_req; + + /* + Find the drain rate of the display buffer. + */ + temp_ff.full = rfixed_const((16/pixel_bytes1)); + disp_drain_rate.full = rfixed_div(pix_clk, temp_ff); + + /* + Find the critical point of the display buffer. + */ + crit_point_ff.full = rfixed_mul(disp_drain_rate, disp_latency); + crit_point_ff.full += rfixed_const_half(0); + + critical_point = rfixed_trunc(crit_point_ff); + + if (rdev->disp_priority == 2) { + critical_point = 0; + } + + /* + The critical point should never be above max_stop_req-4. Setting + GRPH_CRITICAL_CNTL = 0 will thus force high priority all the time. + */ + if (max_stop_req - critical_point < 4) + critical_point = 0; + + if (critical_point == 0 && mode2 && rdev->family == CHIP_R300) { + /* some R300 cards have problem with this set to 0, when CRTC2 is enabled.*/ + critical_point = 0x10; + } + + temp = RREG32(RADEON_GRPH_BUFFER_CNTL); + temp &= ~(RADEON_GRPH_STOP_REQ_MASK); + temp |= (stop_req << RADEON_GRPH_STOP_REQ_SHIFT); + temp &= ~(RADEON_GRPH_START_REQ_MASK); + if ((rdev->family == CHIP_R350) && + (stop_req > 0x15)) { + stop_req -= 0x10; + } + temp |= (stop_req << RADEON_GRPH_START_REQ_SHIFT); + temp |= RADEON_GRPH_BUFFER_SIZE; + temp &= ~(RADEON_GRPH_CRITICAL_CNTL | + RADEON_GRPH_CRITICAL_AT_SOF | + RADEON_GRPH_STOP_CNTL); + /* + Write the result into the register. + */ + WREG32(RADEON_GRPH_BUFFER_CNTL, ((temp & ~RADEON_GRPH_CRITICAL_POINT_MASK) | + (critical_point << RADEON_GRPH_CRITICAL_POINT_SHIFT))); + +#if 0 + if ((rdev->family == CHIP_RS400) || + (rdev->family == CHIP_RS480)) { + /* attempt to program RS400 disp regs correctly ??? */ + temp = RREG32(RS400_DISP1_REG_CNTL); + temp &= ~(RS400_DISP1_START_REQ_LEVEL_MASK | + RS400_DISP1_STOP_REQ_LEVEL_MASK); + WREG32(RS400_DISP1_REQ_CNTL1, (temp | + (critical_point << RS400_DISP1_START_REQ_LEVEL_SHIFT) | + (critical_point << RS400_DISP1_STOP_REQ_LEVEL_SHIFT))); + temp = RREG32(RS400_DMIF_MEM_CNTL1); + temp &= ~(RS400_DISP1_CRITICAL_POINT_START_MASK | + RS400_DISP1_CRITICAL_POINT_STOP_MASK); + WREG32(RS400_DMIF_MEM_CNTL1, (temp | + (critical_point << RS400_DISP1_CRITICAL_POINT_START_SHIFT) | + (critical_point << RS400_DISP1_CRITICAL_POINT_STOP_SHIFT))); + } +#endif + + DRM_DEBUG("GRPH_BUFFER_CNTL from to %x\n", + /* (unsigned int)info->SavedReg->grph_buffer_cntl, */ + (unsigned int)RREG32(RADEON_GRPH_BUFFER_CNTL)); + } + + if (mode2) { + u32 grph2_cntl; + stop_req = mode2->hdisplay * pixel_bytes2 / 16; + + if (stop_req > max_stop_req) + stop_req = max_stop_req; + + /* + Find the drain rate of the display buffer. + */ + temp_ff.full = rfixed_const((16/pixel_bytes2)); + disp_drain_rate2.full = rfixed_div(pix_clk2, temp_ff); + + grph2_cntl = RREG32(RADEON_GRPH2_BUFFER_CNTL); + grph2_cntl &= ~(RADEON_GRPH_STOP_REQ_MASK); + grph2_cntl |= (stop_req << RADEON_GRPH_STOP_REQ_SHIFT); + grph2_cntl &= ~(RADEON_GRPH_START_REQ_MASK); + if ((rdev->family == CHIP_R350) && + (stop_req > 0x15)) { + stop_req -= 0x10; + } + grph2_cntl |= (stop_req << RADEON_GRPH_START_REQ_SHIFT); + grph2_cntl |= RADEON_GRPH_BUFFER_SIZE; + grph2_cntl &= ~(RADEON_GRPH_CRITICAL_CNTL | + RADEON_GRPH_CRITICAL_AT_SOF | + RADEON_GRPH_STOP_CNTL); + + if ((rdev->family == CHIP_RS100) || + (rdev->family == CHIP_RS200)) + critical_point2 = 0; + else { + temp = (rdev->mc.vram_width * rdev->mc.vram_is_ddr + 1)/128; + temp_ff.full = rfixed_const(temp); + temp_ff.full = rfixed_mul(mclk_ff, temp_ff); + if (sclk_ff.full < temp_ff.full) + temp_ff.full = sclk_ff.full; + + read_return_rate.full = temp_ff.full; + + if (mode1) { + temp_ff.full = read_return_rate.full - disp_drain_rate.full; + time_disp1_drop_priority.full = rfixed_div(crit_point_ff, temp_ff); + } else { + time_disp1_drop_priority.full = 0; + } + crit_point_ff.full = disp_latency.full + time_disp1_drop_priority.full + disp_latency.full; + crit_point_ff.full = rfixed_mul(crit_point_ff, disp_drain_rate2); + crit_point_ff.full += rfixed_const_half(0); + + critical_point2 = rfixed_trunc(crit_point_ff); + + if (rdev->disp_priority == 2) { + critical_point2 = 0; + } + + if (max_stop_req - critical_point2 < 4) + critical_point2 = 0; + + } + + if (critical_point2 == 0 && rdev->family == CHIP_R300) { + /* some R300 cards have problem with this set to 0 */ + critical_point2 = 0x10; + } + + WREG32(RADEON_GRPH2_BUFFER_CNTL, ((grph2_cntl & ~RADEON_GRPH_CRITICAL_POINT_MASK) | + (critical_point2 << RADEON_GRPH_CRITICAL_POINT_SHIFT))); + + if ((rdev->family == CHIP_RS400) || + (rdev->family == CHIP_RS480)) { +#if 0 + /* attempt to program RS400 disp2 regs correctly ??? */ + temp = RREG32(RS400_DISP2_REQ_CNTL1); + temp &= ~(RS400_DISP2_START_REQ_LEVEL_MASK | + RS400_DISP2_STOP_REQ_LEVEL_MASK); + WREG32(RS400_DISP2_REQ_CNTL1, (temp | + (critical_point2 << RS400_DISP1_START_REQ_LEVEL_SHIFT) | + (critical_point2 << RS400_DISP1_STOP_REQ_LEVEL_SHIFT))); + temp = RREG32(RS400_DISP2_REQ_CNTL2); + temp &= ~(RS400_DISP2_CRITICAL_POINT_START_MASK | + RS400_DISP2_CRITICAL_POINT_STOP_MASK); + WREG32(RS400_DISP2_REQ_CNTL2, (temp | + (critical_point2 << RS400_DISP2_CRITICAL_POINT_START_SHIFT) | + (critical_point2 << RS400_DISP2_CRITICAL_POINT_STOP_SHIFT))); +#endif + WREG32(RS400_DISP2_REQ_CNTL1, 0x105DC1CC); + WREG32(RS400_DISP2_REQ_CNTL2, 0x2749D000); + WREG32(RS400_DMIF_MEM_CNTL1, 0x29CA71DC); + WREG32(RS400_DISP1_REQ_CNTL1, 0x28FBC3AC); + } + + DRM_DEBUG("GRPH2_BUFFER_CNTL from to %x\n", + (unsigned int)RREG32(RADEON_GRPH2_BUFFER_CNTL)); + } +} diff --git a/drivers/gpu/drm/radeon/r300.c b/drivers/gpu/drm/radeon/r300.c index e2ed5bc0817..9c8d41534a5 100644 --- a/drivers/gpu/drm/radeon/r300.c +++ b/drivers/gpu/drm/radeon/r300.c @@ -30,6 +30,8 @@ #include "drm.h" #include "radeon_reg.h" #include "radeon.h" +#include "radeon_drm.h" +#include "radeon_share.h" /* r300,r350,rv350,rv370,rv380 depends on : */ void r100_hdp_reset(struct radeon_device *rdev); @@ -44,6 +46,7 @@ int r100_gui_wait_for_idle(struct radeon_device *rdev); int r100_cs_packet_parse(struct radeon_cs_parser *p, struct radeon_cs_packet *pkt, unsigned idx); +int r100_cs_packet_parse_vline(struct radeon_cs_parser *p); int r100_cs_packet_next_reloc(struct radeon_cs_parser *p, struct radeon_cs_reloc **cs_reloc); int r100_cs_parse_packet0(struct radeon_cs_parser *p, @@ -150,8 +153,13 @@ int rv370_pcie_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr) if (i < 0 || i > rdev->gart.num_gpu_pages) { return -EINVAL; } - addr = (((u32)addr) >> 8) | ((upper_32_bits(addr) & 0xff) << 4) | 0xC; - writel(cpu_to_le32(addr), ((void __iomem *)ptr) + (i * 4)); + addr = (lower_32_bits(addr) >> 8) | + ((upper_32_bits(addr) & 0xff) << 24) | + 0xc; + /* on x86 we want this to be CPU endian, on powerpc + * on powerpc without HW swappers, it'll get swapped on way + * into VRAM - so no need for cpu_to_le32 on VRAM tables */ + writel(addr, ((void __iomem *)ptr) + (i * 4)); return 0; } @@ -579,10 +587,8 @@ void r300_vram_info(struct radeon_device *rdev) } else { rdev->mc.vram_width = 64; } - rdev->mc.vram_size = RREG32(RADEON_CONFIG_MEMSIZE); - rdev->mc.aper_base = drm_get_resource_start(rdev->ddev, 0); - rdev->mc.aper_size = drm_get_resource_len(rdev->ddev, 0); + r100_vram_init_sizes(rdev); } @@ -970,7 +976,7 @@ static inline void r300_cs_track_clear(struct r300_cs_track *track) static const unsigned r300_reg_safe_bm[159] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFBF, 0xFFFFFFFF, 0xFFFFFFBF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, @@ -1019,7 +1025,7 @@ static int r300_packet0_check(struct radeon_cs_parser *p, struct radeon_cs_reloc *reloc; struct r300_cs_track *track; volatile uint32_t *ib; - uint32_t tmp; + uint32_t tmp, tile_flags = 0; unsigned i; int r; @@ -1027,6 +1033,16 @@ static int r300_packet0_check(struct radeon_cs_parser *p, ib_chunk = &p->chunks[p->chunk_ib_idx]; track = (struct r300_cs_track*)p->track; switch(reg) { + case AVIVO_D1MODE_VLINE_START_END: + case RADEON_CRTC_GUI_TRIG_VLINE: + r = r100_cs_packet_parse_vline(p); + if (r) { + DRM_ERROR("No reloc for ib[%d]=0x%04X\n", + idx, reg); + r100_cs_dump_packet(p, pkt); + return r; + } + break; case RADEON_DST_PITCH_OFFSET: case RADEON_SRC_PITCH_OFFSET: r = r100_cs_packet_next_reloc(p, &reloc); @@ -1038,7 +1054,19 @@ static int r300_packet0_check(struct radeon_cs_parser *p, } tmp = ib_chunk->kdata[idx] & 0x003fffff; tmp += (((u32)reloc->lobj.gpu_offset) >> 10); - ib[idx] = (ib_chunk->kdata[idx] & 0xffc00000) | tmp; + + if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) + tile_flags |= RADEON_DST_TILE_MACRO; + if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) { + if (reg == RADEON_SRC_PITCH_OFFSET) { + DRM_ERROR("Cannot src blit from microtiled surface\n"); + r100_cs_dump_packet(p, pkt); + return -EINVAL; + } + tile_flags |= RADEON_DST_TILE_MICRO; + } + tmp |= tile_flags; + ib[idx] = (ib_chunk->kdata[idx] & 0x3fc00000) | tmp; break; case R300_RB3D_COLOROFFSET0: case R300_RB3D_COLOROFFSET1: @@ -1127,6 +1155,23 @@ static int r300_packet0_check(struct radeon_cs_parser *p, /* RB3D_COLORPITCH1 */ /* RB3D_COLORPITCH2 */ /* RB3D_COLORPITCH3 */ + r = r100_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("No reloc for ib[%d]=0x%04X\n", + idx, reg); + r100_cs_dump_packet(p, pkt); + return r; + } + + if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) + tile_flags |= R300_COLOR_TILE_ENABLE; + if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) + tile_flags |= R300_COLOR_MICROTILE_ENABLE; + + tmp = ib_chunk->kdata[idx] & ~(0x7 << 16); + tmp |= tile_flags; + ib[idx] = tmp; + i = (reg - 0x4E38) >> 2; track->cb[i].pitch = ib_chunk->kdata[idx] & 0x3FFE; switch (((ib_chunk->kdata[idx] >> 21) & 0xF)) { @@ -1182,6 +1227,23 @@ static int r300_packet0_check(struct radeon_cs_parser *p, break; case 0x4F24: /* ZB_DEPTHPITCH */ + r = r100_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("No reloc for ib[%d]=0x%04X\n", + idx, reg); + r100_cs_dump_packet(p, pkt); + return r; + } + + if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) + tile_flags |= R300_DEPTHMACROTILE_ENABLE; + if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) + tile_flags |= R300_DEPTHMICROTILE_TILED;; + + tmp = ib_chunk->kdata[idx] & ~(0x7 << 16); + tmp |= tile_flags; + ib[idx] = tmp; + track->zb.pitch = ib_chunk->kdata[idx] & 0x3FFC; break; case 0x4104: diff --git a/drivers/gpu/drm/radeon/r300_reg.h b/drivers/gpu/drm/radeon/r300_reg.h index 70f48609515..4b7afef35a6 100644 --- a/drivers/gpu/drm/radeon/r300_reg.h +++ b/drivers/gpu/drm/radeon/r300_reg.h @@ -27,7 +27,9 @@ #ifndef _R300_REG_H_ #define _R300_REG_H_ - +#define R300_SURF_TILE_MACRO (1<<16) +#define R300_SURF_TILE_MICRO (2<<16) +#define R300_SURF_TILE_BOTH (3<<16) #define R300_MC_INIT_MISC_LAT_TIMER 0x180 diff --git a/drivers/gpu/drm/radeon/r500_reg.h b/drivers/gpu/drm/radeon/r500_reg.h index 9070a1c2ce2..036691b38cb 100644 --- a/drivers/gpu/drm/radeon/r500_reg.h +++ b/drivers/gpu/drm/radeon/r500_reg.h @@ -445,6 +445,7 @@ #define AVIVO_D1MODE_DATA_FORMAT 0x6528 # define AVIVO_D1MODE_INTERLEAVE_EN (1 << 0) #define AVIVO_D1MODE_DESKTOP_HEIGHT 0x652C +#define AVIVO_D1MODE_VLINE_START_END 0x6538 #define AVIVO_D1MODE_VIEWPORT_START 0x6580 #define AVIVO_D1MODE_VIEWPORT_SIZE 0x6584 #define AVIVO_D1MODE_EXT_OVERSCAN_LEFT_RIGHT 0x6588 @@ -496,6 +497,7 @@ #define AVIVO_D2CUR_SIZE 0x6c10 #define AVIVO_D2CUR_POSITION 0x6c14 +#define AVIVO_D2MODE_VLINE_START_END 0x6d38 #define AVIVO_D2MODE_VIEWPORT_START 0x6d80 #define AVIVO_D2MODE_VIEWPORT_SIZE 0x6d84 #define AVIVO_D2MODE_EXT_OVERSCAN_LEFT_RIGHT 0x6d88 diff --git a/drivers/gpu/drm/radeon/r520.c b/drivers/gpu/drm/radeon/r520.c index 570a244bd88..09fb0b6ec7d 100644 --- a/drivers/gpu/drm/radeon/r520.c +++ b/drivers/gpu/drm/radeon/r520.c @@ -28,6 +28,7 @@ #include "drmP.h" #include "radeon_reg.h" #include "radeon.h" +#include "radeon_share.h" /* r520,rv530,rv560,rv570,r580 depends on : */ void r100_hdp_reset(struct radeon_device *rdev); @@ -94,8 +95,8 @@ int r520_mc_init(struct radeon_device *rdev) "programming pipes. Bad things might happen.\n"); } /* Write VRAM size in case we are limiting it */ - WREG32(RADEON_CONFIG_MEMSIZE, rdev->mc.vram_size); - tmp = rdev->mc.vram_location + rdev->mc.vram_size - 1; + WREG32(RADEON_CONFIG_MEMSIZE, rdev->mc.real_vram_size); + tmp = rdev->mc.vram_location + rdev->mc.mc_vram_size - 1; tmp = REG_SET(R520_MC_FB_TOP, tmp >> 16); tmp |= REG_SET(R520_MC_FB_START, rdev->mc.vram_location >> 16); WREG32_MC(R520_MC_FB_LOCATION, tmp); @@ -226,9 +227,20 @@ static void r520_vram_get_type(struct radeon_device *rdev) void r520_vram_info(struct radeon_device *rdev) { + fixed20_12 a; + r520_vram_get_type(rdev); - rdev->mc.vram_size = RREG32(RADEON_CONFIG_MEMSIZE); - rdev->mc.aper_base = drm_get_resource_start(rdev->ddev, 0); - rdev->mc.aper_size = drm_get_resource_len(rdev->ddev, 0); + r100_vram_init_sizes(rdev); + /* FIXME: we should enforce default clock in case GPU is not in + * default setup + */ + a.full = rfixed_const(100); + rdev->pm.sclk.full = rfixed_const(rdev->clock.default_sclk); + rdev->pm.sclk.full = rfixed_div(rdev->pm.sclk, a); +} + +void r520_bandwidth_update(struct radeon_device *rdev) +{ + rv515_bandwidth_avivo_update(rdev); } diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c index c45559fc97f..538cd907df6 100644 --- a/drivers/gpu/drm/radeon/r600.c +++ b/drivers/gpu/drm/radeon/r600.c @@ -67,7 +67,7 @@ int r600_mc_init(struct radeon_device *rdev) "programming pipes. Bad things might happen.\n"); } - tmp = rdev->mc.vram_location + rdev->mc.vram_size - 1; + tmp = rdev->mc.vram_location + rdev->mc.mc_vram_size - 1; tmp = REG_SET(R600_MC_FB_TOP, tmp >> 24); tmp |= REG_SET(R600_MC_FB_BASE, rdev->mc.vram_location >> 24); WREG32(R600_MC_VM_FB_LOCATION, tmp); @@ -140,7 +140,8 @@ void r600_vram_get_type(struct radeon_device *rdev) void r600_vram_info(struct radeon_device *rdev) { r600_vram_get_type(rdev); - rdev->mc.vram_size = RREG32(R600_CONFIG_MEMSIZE); + rdev->mc.real_vram_size = RREG32(R600_CONFIG_MEMSIZE); + rdev->mc.mc_vram_size = rdev->mc.real_vram_size; /* Could aper size report 0 ? */ rdev->mc.aper_base = drm_get_resource_start(rdev->ddev, 0); diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index d61f2fc61df..b1d945b8ed6 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -64,6 +64,7 @@ extern int radeon_agpmode; extern int radeon_vram_limit; extern int radeon_gart_size; extern int radeon_benchmarking; +extern int radeon_testing; extern int radeon_connector_table; /* @@ -113,6 +114,7 @@ enum radeon_family { CHIP_RV770, CHIP_RV730, CHIP_RV710, + CHIP_RS880, CHIP_LAST, }; @@ -201,6 +203,14 @@ int radeon_fence_wait_last(struct radeon_device *rdev); struct radeon_fence *radeon_fence_ref(struct radeon_fence *fence); void radeon_fence_unref(struct radeon_fence **fence); +/* + * Tiling registers + */ +struct radeon_surface_reg { + struct radeon_object *robj; +}; + +#define RADEON_GEM_MAX_SURFACES 8 /* * Radeon buffer. @@ -213,6 +223,7 @@ struct radeon_object_list { uint64_t gpu_offset; unsigned rdomain; unsigned wdomain; + uint32_t tiling_flags; }; int radeon_object_init(struct radeon_device *rdev); @@ -242,8 +253,15 @@ void radeon_object_list_clean(struct list_head *head); int radeon_object_fbdev_mmap(struct radeon_object *robj, struct vm_area_struct *vma); unsigned long radeon_object_size(struct radeon_object *robj); - - +void radeon_object_clear_surface_reg(struct radeon_object *robj); +int radeon_object_check_tiling(struct radeon_object *robj, bool has_moved, + bool force_drop); +void radeon_object_set_tiling_flags(struct radeon_object *robj, + uint32_t tiling_flags, uint32_t pitch); +void radeon_object_get_tiling_flags(struct radeon_object *robj, uint32_t *tiling_flags, uint32_t *pitch); +void radeon_bo_move_notify(struct ttm_buffer_object *bo, + struct ttm_mem_reg *mem); +void radeon_bo_fault_reserve_notify(struct ttm_buffer_object *bo); /* * GEM objects. */ @@ -315,8 +333,11 @@ struct radeon_mc { unsigned gtt_location; unsigned gtt_size; unsigned vram_location; - unsigned vram_size; + /* for some chips with <= 32MB we need to lie + * about vram size near mc fb location */ + unsigned mc_vram_size; unsigned vram_width; + unsigned real_vram_size; int vram_mtrr; bool vram_is_ddr; }; @@ -474,6 +495,39 @@ struct radeon_wb { uint64_t gpu_addr; }; +/** + * struct radeon_pm - power management datas + * @max_bandwidth: maximum bandwidth the gpu has (MByte/s) + * @igp_sideport_mclk: sideport memory clock Mhz (rs690,rs740,rs780,rs880) + * @igp_system_mclk: system clock Mhz (rs690,rs740,rs780,rs880) + * @igp_ht_link_clk: ht link clock Mhz (rs690,rs740,rs780,rs880) + * @igp_ht_link_width: ht link width in bits (rs690,rs740,rs780,rs880) + * @k8_bandwidth: k8 bandwidth the gpu has (MByte/s) (IGP) + * @sideport_bandwidth: sideport bandwidth the gpu has (MByte/s) (IGP) + * @ht_bandwidth: ht bandwidth the gpu has (MByte/s) (IGP) + * @core_bandwidth: core GPU bandwidth the gpu has (MByte/s) (IGP) + * @sclk: GPU clock Mhz (core bandwith depends of this clock) + * @needed_bandwidth: current bandwidth needs + * + * It keeps track of various data needed to take powermanagement decision. + * Bandwith need is used to determine minimun clock of the GPU and memory. + * Equation between gpu/memory clock and available bandwidth is hw dependent + * (type of memory, bus size, efficiency, ...) + */ +struct radeon_pm { + fixed20_12 max_bandwidth; + fixed20_12 igp_sideport_mclk; + fixed20_12 igp_system_mclk; + fixed20_12 igp_ht_link_clk; + fixed20_12 igp_ht_link_width; + fixed20_12 k8_bandwidth; + fixed20_12 sideport_bandwidth; + fixed20_12 ht_bandwidth; + fixed20_12 core_bandwidth; + fixed20_12 sclk; + fixed20_12 needed_bandwidth; +}; + /* * Benchmarking @@ -482,6 +536,12 @@ void radeon_benchmark(struct radeon_device *rdev); /* + * Testing + */ +void radeon_test_moves(struct radeon_device *rdev); + + +/* * Debugfs */ int radeon_debugfs_add_files(struct radeon_device *rdev, @@ -535,6 +595,11 @@ struct radeon_asic { void (*set_memory_clock)(struct radeon_device *rdev, uint32_t mem_clock); void (*set_pcie_lanes)(struct radeon_device *rdev, int lanes); void (*set_clock_gating)(struct radeon_device *rdev, int enable); + int (*set_surface_reg)(struct radeon_device *rdev, int reg, + uint32_t tiling_flags, uint32_t pitch, + uint32_t offset, uint32_t obj_size); + int (*clear_surface_reg)(struct radeon_device *rdev, int reg); + void (*bandwidth_update)(struct radeon_device *rdev); }; union radeon_asic_config { @@ -566,6 +631,10 @@ int radeon_gem_busy_ioctl(struct drm_device *dev, void *data, int radeon_gem_wait_idle_ioctl(struct drm_device *dev, void *data, struct drm_file *filp); int radeon_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp); +int radeon_gem_set_tiling_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp); +int radeon_gem_get_tiling_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp); /* @@ -594,8 +663,8 @@ struct radeon_device { struct radeon_object *fbdev_robj; struct radeon_framebuffer *fbdev_rfb; /* Register mmio */ - unsigned long rmmio_base; - unsigned long rmmio_size; + resource_size_t rmmio_base; + resource_size_t rmmio_size; void *rmmio; radeon_rreg_t mm_rreg; radeon_wreg_t mm_wreg; @@ -619,11 +688,14 @@ struct radeon_device { struct radeon_irq irq; struct radeon_asic *asic; struct radeon_gem gem; + struct radeon_pm pm; struct mutex cs_mutex; struct radeon_wb wb; bool gpu_lockup; bool shutdown; bool suspend; + bool need_dma32; + struct radeon_surface_reg surface_regs[RADEON_GEM_MAX_SURFACES]; }; int radeon_device_init(struct radeon_device *rdev, @@ -670,6 +742,8 @@ void r100_pll_errata_after_index(struct radeon_device *rdev); /* * ASICs helpers. */ +#define ASIC_IS_RN50(rdev) ((rdev->pdev->device == 0x515e) || \ + (rdev->pdev->device == 0x5969)) #define ASIC_IS_RV100(rdev) ((rdev->family == CHIP_RV100) || \ (rdev->family == CHIP_RV200) || \ (rdev->family == CHIP_RS100) || \ @@ -796,5 +870,8 @@ static inline void radeon_ring_write(struct radeon_device *rdev, uint32_t v) #define radeon_set_memory_clock(rdev, e) (rdev)->asic->set_engine_clock((rdev), (e)) #define radeon_set_pcie_lanes(rdev, l) (rdev)->asic->set_pcie_lanes((rdev), (l)) #define radeon_set_clock_gating(rdev, e) (rdev)->asic->set_clock_gating((rdev), (e)) +#define radeon_set_surface_reg(rdev, r, f, p, o, s) ((rdev)->asic->set_surface_reg((rdev), (r), (f), (p), (o), (s))) +#define radeon_clear_surface_reg(rdev, r) ((rdev)->asic->clear_surface_reg((rdev), (r))) +#define radeon_bandwidth_update(rdev) (rdev)->asic->bandwidth_update((rdev)) #endif diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index e2e567395df..9a75876e0c3 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -71,6 +71,11 @@ int r100_copy_blit(struct radeon_device *rdev, uint64_t dst_offset, unsigned num_pages, struct radeon_fence *fence); +int r100_set_surface_reg(struct radeon_device *rdev, int reg, + uint32_t tiling_flags, uint32_t pitch, + uint32_t offset, uint32_t obj_size); +int r100_clear_surface_reg(struct radeon_device *rdev, int reg); +void r100_bandwidth_update(struct radeon_device *rdev); static struct radeon_asic r100_asic = { .init = &r100_init, @@ -100,6 +105,9 @@ static struct radeon_asic r100_asic = { .set_memory_clock = NULL, .set_pcie_lanes = NULL, .set_clock_gating = &radeon_legacy_set_clock_gating, + .set_surface_reg = r100_set_surface_reg, + .clear_surface_reg = r100_clear_surface_reg, + .bandwidth_update = &r100_bandwidth_update, }; @@ -128,6 +136,7 @@ int r300_copy_dma(struct radeon_device *rdev, uint64_t dst_offset, unsigned num_pages, struct radeon_fence *fence); + static struct radeon_asic r300_asic = { .init = &r300_init, .errata = &r300_errata, @@ -156,6 +165,9 @@ static struct radeon_asic r300_asic = { .set_memory_clock = NULL, .set_pcie_lanes = &rv370_set_pcie_lanes, .set_clock_gating = &radeon_legacy_set_clock_gating, + .set_surface_reg = r100_set_surface_reg, + .clear_surface_reg = r100_clear_surface_reg, + .bandwidth_update = &r100_bandwidth_update, }; /* @@ -193,6 +205,9 @@ static struct radeon_asic r420_asic = { .set_memory_clock = &radeon_atom_set_memory_clock, .set_pcie_lanes = &rv370_set_pcie_lanes, .set_clock_gating = &radeon_atom_set_clock_gating, + .set_surface_reg = r100_set_surface_reg, + .clear_surface_reg = r100_clear_surface_reg, + .bandwidth_update = &r100_bandwidth_update, }; @@ -237,6 +252,9 @@ static struct radeon_asic rs400_asic = { .set_memory_clock = NULL, .set_pcie_lanes = NULL, .set_clock_gating = &radeon_legacy_set_clock_gating, + .set_surface_reg = r100_set_surface_reg, + .clear_surface_reg = r100_clear_surface_reg, + .bandwidth_update = &r100_bandwidth_update, }; @@ -254,6 +272,7 @@ void rs600_gart_tlb_flush(struct radeon_device *rdev); int rs600_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr); uint32_t rs600_mc_rreg(struct radeon_device *rdev, uint32_t reg); void rs600_mc_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v); +void rs600_bandwidth_update(struct radeon_device *rdev); static struct radeon_asic rs600_asic = { .init = &r300_init, .errata = &rs600_errata, @@ -282,6 +301,7 @@ static struct radeon_asic rs600_asic = { .set_memory_clock = &radeon_atom_set_memory_clock, .set_pcie_lanes = NULL, .set_clock_gating = &radeon_atom_set_clock_gating, + .bandwidth_update = &rs600_bandwidth_update, }; @@ -294,6 +314,7 @@ int rs690_mc_init(struct radeon_device *rdev); void rs690_mc_fini(struct radeon_device *rdev); uint32_t rs690_mc_rreg(struct radeon_device *rdev, uint32_t reg); void rs690_mc_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v); +void rs690_bandwidth_update(struct radeon_device *rdev); static struct radeon_asic rs690_asic = { .init = &r300_init, .errata = &rs690_errata, @@ -322,6 +343,9 @@ static struct radeon_asic rs690_asic = { .set_memory_clock = &radeon_atom_set_memory_clock, .set_pcie_lanes = NULL, .set_clock_gating = &radeon_atom_set_clock_gating, + .set_surface_reg = r100_set_surface_reg, + .clear_surface_reg = r100_clear_surface_reg, + .bandwidth_update = &rs690_bandwidth_update, }; @@ -339,6 +363,7 @@ void rv515_mc_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v); void rv515_ring_start(struct radeon_device *rdev); uint32_t rv515_pcie_rreg(struct radeon_device *rdev, uint32_t reg); void rv515_pcie_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v); +void rv515_bandwidth_update(struct radeon_device *rdev); static struct radeon_asic rv515_asic = { .init = &rv515_init, .errata = &rv515_errata, @@ -367,6 +392,9 @@ static struct radeon_asic rv515_asic = { .set_memory_clock = &radeon_atom_set_memory_clock, .set_pcie_lanes = &rv370_set_pcie_lanes, .set_clock_gating = &radeon_atom_set_clock_gating, + .set_surface_reg = r100_set_surface_reg, + .clear_surface_reg = r100_clear_surface_reg, + .bandwidth_update = &rv515_bandwidth_update, }; @@ -377,6 +405,7 @@ void r520_errata(struct radeon_device *rdev); void r520_vram_info(struct radeon_device *rdev); int r520_mc_init(struct radeon_device *rdev); void r520_mc_fini(struct radeon_device *rdev); +void r520_bandwidth_update(struct radeon_device *rdev); static struct radeon_asic r520_asic = { .init = &rv515_init, .errata = &r520_errata, @@ -405,6 +434,9 @@ static struct radeon_asic r520_asic = { .set_memory_clock = &radeon_atom_set_memory_clock, .set_pcie_lanes = &rv370_set_pcie_lanes, .set_clock_gating = &radeon_atom_set_clock_gating, + .set_surface_reg = r100_set_surface_reg, + .clear_surface_reg = r100_clear_surface_reg, + .bandwidth_update = &r520_bandwidth_update, }; /* diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c index 1f5a1a49098..fcfe5c02d74 100644 --- a/drivers/gpu/drm/radeon/radeon_atombios.c +++ b/drivers/gpu/drm/radeon/radeon_atombios.c @@ -103,7 +103,8 @@ static inline struct radeon_i2c_bus_rec radeon_lookup_gpio(struct drm_device static bool radeon_atom_apply_quirks(struct drm_device *dev, uint32_t supported_device, int *connector_type, - struct radeon_i2c_bus_rec *i2c_bus) + struct radeon_i2c_bus_rec *i2c_bus, + uint8_t *line_mux) { /* Asus M2A-VM HDMI board lists the DVI port as HDMI */ @@ -127,8 +128,10 @@ static bool radeon_atom_apply_quirks(struct drm_device *dev, if ((dev->pdev->device == 0x5653) && (dev->pdev->subsystem_vendor == 0x1462) && (dev->pdev->subsystem_device == 0x0291)) { - if (*connector_type == DRM_MODE_CONNECTOR_LVDS) + if (*connector_type == DRM_MODE_CONNECTOR_LVDS) { i2c_bus->valid = false; + *line_mux = 53; + } } /* Funky macbooks */ @@ -526,7 +529,7 @@ bool radeon_get_atom_connector_info_from_supported_devices_table(struct if (!radeon_atom_apply_quirks (dev, (1 << i), &bios_connectors[i].connector_type, - &bios_connectors[i].ddc_bus)) + &bios_connectors[i].ddc_bus, &bios_connectors[i].line_mux)) continue; bios_connectors[i].valid = true; diff --git a/drivers/gpu/drm/radeon/radeon_benchmark.c b/drivers/gpu/drm/radeon/radeon_benchmark.c index c44403a2ca7..2e938f7496f 100644 --- a/drivers/gpu/drm/radeon/radeon_benchmark.c +++ b/drivers/gpu/drm/radeon/radeon_benchmark.c @@ -63,7 +63,7 @@ void radeon_benchmark_move(struct radeon_device *rdev, unsigned bsize, if (r) { goto out_cleanup; } - r = radeon_copy_dma(rdev, saddr, daddr, size >> 14, fence); + r = radeon_copy_dma(rdev, saddr, daddr, size / 4096, fence); if (r) { goto out_cleanup; } @@ -88,7 +88,7 @@ void radeon_benchmark_move(struct radeon_device *rdev, unsigned bsize, if (r) { goto out_cleanup; } - r = radeon_copy_blit(rdev, saddr, daddr, size >> 14, fence); + r = radeon_copy_blit(rdev, saddr, daddr, size / 4096, fence); if (r) { goto out_cleanup; } diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c index b843f9bdfb1..a169067efc4 100644 --- a/drivers/gpu/drm/radeon/radeon_cs.c +++ b/drivers/gpu/drm/radeon/radeon_cs.c @@ -127,17 +127,23 @@ int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data) sizeof(struct drm_radeon_cs_chunk))) { return -EFAULT; } + p->chunks[i].length_dw = user_chunk.length_dw; + p->chunks[i].kdata = NULL; p->chunks[i].chunk_id = user_chunk.chunk_id; + if (p->chunks[i].chunk_id == RADEON_CHUNK_ID_RELOCS) { p->chunk_relocs_idx = i; } if (p->chunks[i].chunk_id == RADEON_CHUNK_ID_IB) { p->chunk_ib_idx = i; + /* zero length IB isn't useful */ + if (p->chunks[i].length_dw == 0) + return -EINVAL; } + p->chunks[i].length_dw = user_chunk.length_dw; cdata = (uint32_t *)(unsigned long)user_chunk.chunk_data; - p->chunks[i].kdata = NULL; size = p->chunks[i].length_dw * sizeof(uint32_t); p->chunks[i].kdata = kzalloc(size, GFP_KERNEL); if (p->chunks[i].kdata == NULL) { diff --git a/drivers/gpu/drm/radeon/radeon_cursor.c b/drivers/gpu/drm/radeon/radeon_cursor.c index 5232441f119..b13c79e38bc 100644 --- a/drivers/gpu/drm/radeon/radeon_cursor.c +++ b/drivers/gpu/drm/radeon/radeon_cursor.c @@ -111,9 +111,11 @@ static void radeon_set_cursor(struct drm_crtc *crtc, struct drm_gem_object *obj, if (ASIC_IS_AVIVO(rdev)) WREG32(AVIVO_D1CUR_SURFACE_ADDRESS + radeon_crtc->crtc_offset, gpu_addr); - else + else { + radeon_crtc->legacy_cursor_offset = gpu_addr - radeon_crtc->legacy_display_base_addr; /* offset is from DISP(2)_BASE_ADDRESS */ - WREG32(RADEON_CUR_OFFSET + radeon_crtc->crtc_offset, gpu_addr); + WREG32(RADEON_CUR_OFFSET + radeon_crtc->crtc_offset, radeon_crtc->legacy_cursor_offset); + } } int radeon_crtc_cursor_set(struct drm_crtc *crtc, @@ -245,6 +247,9 @@ int radeon_crtc_cursor_move(struct drm_crtc *crtc, (RADEON_CUR_LOCK | ((xorigin ? 0 : x) << 16) | (yorigin ? 0 : y))); + /* offset is from DISP(2)_BASE_ADDRESS */ + WREG32(RADEON_CUR_OFFSET + radeon_crtc->crtc_offset, (radeon_crtc->legacy_cursor_offset + + (yorigin * 256))); } radeon_lock_cursor(crtc, false); diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index f97563db4e5..a162ade74b7 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -48,6 +48,8 @@ static void radeon_surface_init(struct radeon_device *rdev) i * (RADEON_SURFACE1_INFO - RADEON_SURFACE0_INFO), 0); } + /* enable surfaces */ + WREG32(RADEON_SURFACE_CNTL, 0); } } @@ -119,7 +121,7 @@ int radeon_mc_setup(struct radeon_device *rdev) if (rdev->mc.vram_location != 0xFFFFFFFFUL) { /* vram location was already setup try to put gtt after * if it fits */ - tmp = rdev->mc.vram_location + rdev->mc.vram_size; + tmp = rdev->mc.vram_location + rdev->mc.mc_vram_size; tmp = (tmp + rdev->mc.gtt_size - 1) & ~(rdev->mc.gtt_size - 1); if ((0xFFFFFFFFUL - tmp) >= rdev->mc.gtt_size) { rdev->mc.gtt_location = tmp; @@ -134,13 +136,13 @@ int radeon_mc_setup(struct radeon_device *rdev) } else if (rdev->mc.gtt_location != 0xFFFFFFFFUL) { /* gtt location was already setup try to put vram before * if it fits */ - if (rdev->mc.vram_size < rdev->mc.gtt_location) { + if (rdev->mc.mc_vram_size < rdev->mc.gtt_location) { rdev->mc.vram_location = 0; } else { tmp = rdev->mc.gtt_location + rdev->mc.gtt_size; - tmp += (rdev->mc.vram_size - 1); - tmp &= ~(rdev->mc.vram_size - 1); - if ((0xFFFFFFFFUL - tmp) >= rdev->mc.vram_size) { + tmp += (rdev->mc.mc_vram_size - 1); + tmp &= ~(rdev->mc.mc_vram_size - 1); + if ((0xFFFFFFFFUL - tmp) >= rdev->mc.mc_vram_size) { rdev->mc.vram_location = tmp; } else { printk(KERN_ERR "[drm] vram too big to fit " @@ -150,12 +152,14 @@ int radeon_mc_setup(struct radeon_device *rdev) } } else { rdev->mc.vram_location = 0; - rdev->mc.gtt_location = rdev->mc.vram_size; + rdev->mc.gtt_location = rdev->mc.mc_vram_size; } - DRM_INFO("radeon: VRAM %uM\n", rdev->mc.vram_size >> 20); + DRM_INFO("radeon: VRAM %uM\n", rdev->mc.real_vram_size >> 20); DRM_INFO("radeon: VRAM from 0x%08X to 0x%08X\n", rdev->mc.vram_location, - rdev->mc.vram_location + rdev->mc.vram_size - 1); + rdev->mc.vram_location + rdev->mc.mc_vram_size - 1); + if (rdev->mc.real_vram_size != rdev->mc.mc_vram_size) + DRM_INFO("radeon: VRAM less than aperture workaround enabled\n"); DRM_INFO("radeon: GTT %uM\n", rdev->mc.gtt_size >> 20); DRM_INFO("radeon: GTT from 0x%08X to 0x%08X\n", rdev->mc.gtt_location, @@ -450,6 +454,7 @@ int radeon_device_init(struct radeon_device *rdev, uint32_t flags) { int r, ret; + int dma_bits; DRM_INFO("radeon: Initializing kernel modesetting.\n"); rdev->shutdown = false; @@ -492,8 +497,20 @@ int radeon_device_init(struct radeon_device *rdev, return r; } - /* Report DMA addressing limitation */ - r = pci_set_dma_mask(rdev->pdev, DMA_BIT_MASK(32)); + /* set DMA mask + need_dma32 flags. + * PCIE - can handle 40-bits. + * IGP - can handle 40-bits (in theory) + * AGP - generally dma32 is safest + * PCI - only dma32 + */ + rdev->need_dma32 = false; + if (rdev->flags & RADEON_IS_AGP) + rdev->need_dma32 = true; + if (rdev->flags & RADEON_IS_PCI) + rdev->need_dma32 = true; + + dma_bits = rdev->need_dma32 ? 32 : 40; + r = pci_set_dma_mask(rdev->pdev, DMA_BIT_MASK(dma_bits)); if (r) { printk(KERN_WARNING "radeon: No suitable DMA available.\n"); } @@ -546,27 +563,22 @@ int radeon_device_init(struct radeon_device *rdev, radeon_combios_asic_init(rdev->ddev); } } + /* Initialize clocks */ + r = radeon_clocks_init(rdev); + if (r) { + return r; + } /* Get vram informations */ radeon_vram_info(rdev); - /* Device is severly broken if aper size > vram size. - * for RN50/M6/M7 - Novell bug 204882 ? - */ - if (rdev->mc.vram_size < rdev->mc.aper_size) { - rdev->mc.aper_size = rdev->mc.vram_size; - } + /* Add an MTRR for the VRAM */ rdev->mc.vram_mtrr = mtrr_add(rdev->mc.aper_base, rdev->mc.aper_size, MTRR_TYPE_WRCOMB, 1); DRM_INFO("Detected VRAM RAM=%uM, BAR=%uM\n", - rdev->mc.vram_size >> 20, + rdev->mc.real_vram_size >> 20, (unsigned)rdev->mc.aper_size >> 20); DRM_INFO("RAM width %dbits %cDR\n", rdev->mc.vram_width, rdev->mc.vram_is_ddr ? 'D' : 'S'); - /* Initialize clocks */ - r = radeon_clocks_init(rdev); - if (r) { - return r; - } /* Initialize memory controller (also test AGP) */ r = radeon_mc_init(rdev); if (r) { @@ -626,6 +638,9 @@ int radeon_device_init(struct radeon_device *rdev, if (!ret) { DRM_INFO("radeon: kernel modesetting successfully initialized.\n"); } + if (radeon_testing) { + radeon_test_moves(rdev); + } if (radeon_benchmarking) { radeon_benchmark(rdev); } diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index 3efcf1a526b..a8fa1bb84cf 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -187,6 +187,7 @@ static void radeon_crtc_init(struct drm_device *dev, int index) drm_mode_crtc_set_gamma_size(&radeon_crtc->base, 256); radeon_crtc->crtc_id = index; + rdev->mode_info.crtcs[index] = radeon_crtc; radeon_crtc->mode_set.crtc = &radeon_crtc->base; radeon_crtc->mode_set.connectors = (struct drm_connector **)(radeon_crtc + 1); @@ -491,7 +492,11 @@ void radeon_compute_pll(struct radeon_pll *pll, tmp += (uint64_t)pll->reference_freq * 1000 * frac_feedback_div; current_freq = radeon_div(tmp, ref_div * post_div); - error = abs(current_freq - freq); + if (flags & RADEON_PLL_PREFER_CLOSEST_LOWER) { + error = freq - current_freq; + error = error < 0 ? 0xffffffff : error; + } else + error = abs(current_freq - freq); vco_diff = abs(vco - best_vco); if ((best_vco == 0 && error < best_error) || @@ -657,36 +662,51 @@ void radeon_modeset_fini(struct radeon_device *rdev) } } -void radeon_init_disp_bandwidth(struct drm_device *dev) +bool radeon_crtc_scaling_mode_fixup(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) { - struct radeon_device *rdev = dev->dev_private; - struct drm_display_mode *modes[2]; - int pixel_bytes[2]; - struct drm_crtc *crtc; - - pixel_bytes[0] = pixel_bytes[1] = 0; - modes[0] = modes[1] = NULL; - - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct drm_encoder *encoder; + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct radeon_encoder *radeon_encoder; + bool first = true; - if (crtc->enabled && crtc->fb) { - modes[radeon_crtc->crtc_id] = &crtc->mode; - pixel_bytes[radeon_crtc->crtc_id] = crtc->fb->bits_per_pixel / 8; + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + radeon_encoder = to_radeon_encoder(encoder); + if (encoder->crtc != crtc) + continue; + if (first) { + radeon_crtc->rmx_type = radeon_encoder->rmx_type; + radeon_crtc->devices = radeon_encoder->devices; + memcpy(&radeon_crtc->native_mode, + &radeon_encoder->native_mode, + sizeof(struct radeon_native_mode)); + first = false; + } else { + if (radeon_crtc->rmx_type != radeon_encoder->rmx_type) { + /* WARNING: Right now this can't happen but + * in the future we need to check that scaling + * are consistent accross different encoder + * (ie all encoder can work with the same + * scaling). + */ + DRM_ERROR("Scaling not consistent accross encoder.\n"); + return false; + } } } - - if (ASIC_IS_AVIVO(rdev)) { - radeon_init_disp_bw_avivo(dev, - modes[0], - pixel_bytes[0], - modes[1], - pixel_bytes[1]); + if (radeon_crtc->rmx_type != RMX_OFF) { + fixed20_12 a, b; + a.full = rfixed_const(crtc->mode.vdisplay); + b.full = rfixed_const(radeon_crtc->native_mode.panel_xres); + radeon_crtc->vsc.full = rfixed_div(a, b); + a.full = rfixed_const(crtc->mode.hdisplay); + b.full = rfixed_const(radeon_crtc->native_mode.panel_yres); + radeon_crtc->hsc.full = rfixed_div(a, b); } else { - radeon_init_disp_bw_legacy(dev, - modes[0], - pixel_bytes[0], - modes[1], - pixel_bytes[1]); + radeon_crtc->vsc.full = rfixed_const(1); + radeon_crtc->hsc.full = rfixed_const(1); } + return true; } diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index 84ba69f4878..0bd5879a495 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -89,6 +89,7 @@ int radeon_agpmode = 0; int radeon_vram_limit = 0; int radeon_gart_size = 512; /* default gart size */ int radeon_benchmarking = 0; +int radeon_testing = 0; int radeon_connector_table = 0; #endif @@ -117,6 +118,9 @@ module_param_named(gartsize, radeon_gart_size, int, 0600); MODULE_PARM_DESC(benchmark, "Run benchmark"); module_param_named(benchmark, radeon_benchmarking, int, 0444); +MODULE_PARM_DESC(test, "Run tests"); +module_param_named(test, radeon_testing, int, 0444); + MODULE_PARM_DESC(connector_table, "Force connector table"); module_param_named(connector_table, radeon_connector_table, int, 0444); #endif @@ -314,6 +318,14 @@ static int __init radeon_init(void) driver = &driver_old; driver->num_ioctls = radeon_max_ioctl; #if defined(CONFIG_DRM_RADEON_KMS) +#ifdef CONFIG_VGA_CONSOLE + if (vgacon_text_force() && radeon_modeset == -1) { + DRM_INFO("VGACON disable radeon kernel modesetting.\n"); + driver = &driver_old; + driver->driver_features &= ~DRIVER_MODESET; + radeon_modeset = 0; + } +#endif /* if enabled by default */ if (radeon_modeset == -1) { DRM_INFO("radeon default to kernel modesetting.\n"); @@ -325,17 +337,8 @@ static int __init radeon_init(void) driver->driver_features |= DRIVER_MODESET; driver->num_ioctls = radeon_max_kms_ioctl; } - /* if the vga console setting is enabled still * let modprobe override it */ -#ifdef CONFIG_VGA_CONSOLE - if (vgacon_text_force() && radeon_modeset == -1) { - DRM_INFO("VGACON disable radeon kernel modesetting.\n"); - driver = &driver_old; - driver->driver_features &= ~DRIVER_MODESET; - radeon_modeset = 0; - } -#endif #endif return drm_init(driver); } diff --git a/drivers/gpu/drm/radeon/radeon_encoders.c b/drivers/gpu/drm/radeon/radeon_encoders.c index c8ef0d14ffa..0a92706eac1 100644 --- a/drivers/gpu/drm/radeon/radeon_encoders.c +++ b/drivers/gpu/drm/radeon/radeon_encoders.c @@ -154,7 +154,6 @@ void radeon_rmx_mode_fixup(struct drm_encoder *encoder, if (mode->hdisplay < native_mode->panel_xres || mode->vdisplay < native_mode->panel_yres) { - radeon_encoder->flags |= RADEON_USE_RMX; if (ASIC_IS_AVIVO(rdev)) { adjusted_mode->hdisplay = native_mode->panel_xres; adjusted_mode->vdisplay = native_mode->panel_yres; @@ -197,15 +196,13 @@ void radeon_rmx_mode_fixup(struct drm_encoder *encoder, } } + static bool radeon_atom_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { - struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); - radeon_encoder->flags &= ~RADEON_USE_RMX; - drm_mode_set_crtcinfo(adjusted_mode, 0); if (radeon_encoder->rmx_type != RMX_OFF) @@ -808,234 +805,6 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action) } -static void atom_rv515_force_tv_scaler(struct radeon_device *rdev) -{ - - WREG32(0x659C, 0x0); - WREG32(0x6594, 0x705); - WREG32(0x65A4, 0x10001); - WREG32(0x65D8, 0x0); - WREG32(0x65B0, 0x0); - WREG32(0x65C0, 0x0); - WREG32(0x65D4, 0x0); - WREG32(0x6578, 0x0); - WREG32(0x657C, 0x841880A8); - WREG32(0x6578, 0x1); - WREG32(0x657C, 0x84208680); - WREG32(0x6578, 0x2); - WREG32(0x657C, 0xBFF880B0); - WREG32(0x6578, 0x100); - WREG32(0x657C, 0x83D88088); - WREG32(0x6578, 0x101); - WREG32(0x657C, 0x84608680); - WREG32(0x6578, 0x102); - WREG32(0x657C, 0xBFF080D0); - WREG32(0x6578, 0x200); - WREG32(0x657C, 0x83988068); - WREG32(0x6578, 0x201); - WREG32(0x657C, 0x84A08680); - WREG32(0x6578, 0x202); - WREG32(0x657C, 0xBFF080F8); - WREG32(0x6578, 0x300); - WREG32(0x657C, 0x83588058); - WREG32(0x6578, 0x301); - WREG32(0x657C, 0x84E08660); - WREG32(0x6578, 0x302); - WREG32(0x657C, 0xBFF88120); - WREG32(0x6578, 0x400); - WREG32(0x657C, 0x83188040); - WREG32(0x6578, 0x401); - WREG32(0x657C, 0x85008660); - WREG32(0x6578, 0x402); - WREG32(0x657C, 0xBFF88150); - WREG32(0x6578, 0x500); - WREG32(0x657C, 0x82D88030); - WREG32(0x6578, 0x501); - WREG32(0x657C, 0x85408640); - WREG32(0x6578, 0x502); - WREG32(0x657C, 0xBFF88180); - WREG32(0x6578, 0x600); - WREG32(0x657C, 0x82A08018); - WREG32(0x6578, 0x601); - WREG32(0x657C, 0x85808620); - WREG32(0x6578, 0x602); - WREG32(0x657C, 0xBFF081B8); - WREG32(0x6578, 0x700); - WREG32(0x657C, 0x82608010); - WREG32(0x6578, 0x701); - WREG32(0x657C, 0x85A08600); - WREG32(0x6578, 0x702); - WREG32(0x657C, 0x800081F0); - WREG32(0x6578, 0x800); - WREG32(0x657C, 0x8228BFF8); - WREG32(0x6578, 0x801); - WREG32(0x657C, 0x85E085E0); - WREG32(0x6578, 0x802); - WREG32(0x657C, 0xBFF88228); - WREG32(0x6578, 0x10000); - WREG32(0x657C, 0x82A8BF00); - WREG32(0x6578, 0x10001); - WREG32(0x657C, 0x82A08CC0); - WREG32(0x6578, 0x10002); - WREG32(0x657C, 0x8008BEF8); - WREG32(0x6578, 0x10100); - WREG32(0x657C, 0x81F0BF28); - WREG32(0x6578, 0x10101); - WREG32(0x657C, 0x83608CA0); - WREG32(0x6578, 0x10102); - WREG32(0x657C, 0x8018BED0); - WREG32(0x6578, 0x10200); - WREG32(0x657C, 0x8148BF38); - WREG32(0x6578, 0x10201); - WREG32(0x657C, 0x84408C80); - WREG32(0x6578, 0x10202); - WREG32(0x657C, 0x8008BEB8); - WREG32(0x6578, 0x10300); - WREG32(0x657C, 0x80B0BF78); - WREG32(0x6578, 0x10301); - WREG32(0x657C, 0x85008C20); - WREG32(0x6578, 0x10302); - WREG32(0x657C, 0x8020BEA0); - WREG32(0x6578, 0x10400); - WREG32(0x657C, 0x8028BF90); - WREG32(0x6578, 0x10401); - WREG32(0x657C, 0x85E08BC0); - WREG32(0x6578, 0x10402); - WREG32(0x657C, 0x8018BE90); - WREG32(0x6578, 0x10500); - WREG32(0x657C, 0xBFB8BFB0); - WREG32(0x6578, 0x10501); - WREG32(0x657C, 0x86C08B40); - WREG32(0x6578, 0x10502); - WREG32(0x657C, 0x8010BE90); - WREG32(0x6578, 0x10600); - WREG32(0x657C, 0xBF58BFC8); - WREG32(0x6578, 0x10601); - WREG32(0x657C, 0x87A08AA0); - WREG32(0x6578, 0x10602); - WREG32(0x657C, 0x8010BE98); - WREG32(0x6578, 0x10700); - WREG32(0x657C, 0xBF10BFF0); - WREG32(0x6578, 0x10701); - WREG32(0x657C, 0x886089E0); - WREG32(0x6578, 0x10702); - WREG32(0x657C, 0x8018BEB0); - WREG32(0x6578, 0x10800); - WREG32(0x657C, 0xBED8BFE8); - WREG32(0x6578, 0x10801); - WREG32(0x657C, 0x89408940); - WREG32(0x6578, 0x10802); - WREG32(0x657C, 0xBFE8BED8); - WREG32(0x6578, 0x20000); - WREG32(0x657C, 0x80008000); - WREG32(0x6578, 0x20001); - WREG32(0x657C, 0x90008000); - WREG32(0x6578, 0x20002); - WREG32(0x657C, 0x80008000); - WREG32(0x6578, 0x20003); - WREG32(0x657C, 0x80008000); - WREG32(0x6578, 0x20100); - WREG32(0x657C, 0x80108000); - WREG32(0x6578, 0x20101); - WREG32(0x657C, 0x8FE0BF70); - WREG32(0x6578, 0x20102); - WREG32(0x657C, 0xBFE880C0); - WREG32(0x6578, 0x20103); - WREG32(0x657C, 0x80008000); - WREG32(0x6578, 0x20200); - WREG32(0x657C, 0x8018BFF8); - WREG32(0x6578, 0x20201); - WREG32(0x657C, 0x8F80BF08); - WREG32(0x6578, 0x20202); - WREG32(0x657C, 0xBFD081A0); - WREG32(0x6578, 0x20203); - WREG32(0x657C, 0xBFF88000); - WREG32(0x6578, 0x20300); - WREG32(0x657C, 0x80188000); - WREG32(0x6578, 0x20301); - WREG32(0x657C, 0x8EE0BEC0); - WREG32(0x6578, 0x20302); - WREG32(0x657C, 0xBFB082A0); - WREG32(0x6578, 0x20303); - WREG32(0x657C, 0x80008000); - WREG32(0x6578, 0x20400); - WREG32(0x657C, 0x80188000); - WREG32(0x6578, 0x20401); - WREG32(0x657C, 0x8E00BEA0); - WREG32(0x6578, 0x20402); - WREG32(0x657C, 0xBF8883C0); - WREG32(0x6578, 0x20403); - WREG32(0x657C, 0x80008000); - WREG32(0x6578, 0x20500); - WREG32(0x657C, 0x80188000); - WREG32(0x6578, 0x20501); - WREG32(0x657C, 0x8D00BE90); - WREG32(0x6578, 0x20502); - WREG32(0x657C, 0xBF588500); - WREG32(0x6578, 0x20503); - WREG32(0x657C, 0x80008008); - WREG32(0x6578, 0x20600); - WREG32(0x657C, 0x80188000); - WREG32(0x6578, 0x20601); - WREG32(0x657C, 0x8BC0BE98); - WREG32(0x6578, 0x20602); - WREG32(0x657C, 0xBF308660); - WREG32(0x6578, 0x20603); - WREG32(0x657C, 0x80008008); - WREG32(0x6578, 0x20700); - WREG32(0x657C, 0x80108000); - WREG32(0x6578, 0x20701); - WREG32(0x657C, 0x8A80BEB0); - WREG32(0x6578, 0x20702); - WREG32(0x657C, 0xBF0087C0); - WREG32(0x6578, 0x20703); - WREG32(0x657C, 0x80008008); - WREG32(0x6578, 0x20800); - WREG32(0x657C, 0x80108000); - WREG32(0x6578, 0x20801); - WREG32(0x657C, 0x8920BED0); - WREG32(0x6578, 0x20802); - WREG32(0x657C, 0xBED08920); - WREG32(0x6578, 0x20803); - WREG32(0x657C, 0x80008010); - WREG32(0x6578, 0x30000); - WREG32(0x657C, 0x90008000); - WREG32(0x6578, 0x30001); - WREG32(0x657C, 0x80008000); - WREG32(0x6578, 0x30100); - WREG32(0x657C, 0x8FE0BF90); - WREG32(0x6578, 0x30101); - WREG32(0x657C, 0xBFF880A0); - WREG32(0x6578, 0x30200); - WREG32(0x657C, 0x8F60BF40); - WREG32(0x6578, 0x30201); - WREG32(0x657C, 0xBFE88180); - WREG32(0x6578, 0x30300); - WREG32(0x657C, 0x8EC0BF00); - WREG32(0x6578, 0x30301); - WREG32(0x657C, 0xBFC88280); - WREG32(0x6578, 0x30400); - WREG32(0x657C, 0x8DE0BEE0); - WREG32(0x6578, 0x30401); - WREG32(0x657C, 0xBFA083A0); - WREG32(0x6578, 0x30500); - WREG32(0x657C, 0x8CE0BED0); - WREG32(0x6578, 0x30501); - WREG32(0x657C, 0xBF7884E0); - WREG32(0x6578, 0x30600); - WREG32(0x657C, 0x8BA0BED8); - WREG32(0x6578, 0x30601); - WREG32(0x657C, 0xBF508640); - WREG32(0x6578, 0x30700); - WREG32(0x657C, 0x8A60BEE8); - WREG32(0x6578, 0x30701); - WREG32(0x657C, 0xBF2087A0); - WREG32(0x6578, 0x30800); - WREG32(0x657C, 0x8900BF00); - WREG32(0x6578, 0x30801); - WREG32(0x657C, 0xBF008900); -} - static void atombios_yuv_setup(struct drm_encoder *encoder, bool enable) { @@ -1074,129 +843,6 @@ atombios_yuv_setup(struct drm_encoder *encoder, bool enable) } static void -atombios_overscan_setup(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - struct drm_device *dev = encoder->dev; - struct radeon_device *rdev = dev->dev_private; - struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); - struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc); - SET_CRTC_OVERSCAN_PS_ALLOCATION args; - int index = GetIndexIntoMasterTable(COMMAND, SetCRTC_OverScan); - - memset(&args, 0, sizeof(args)); - - args.usOverscanRight = 0; - args.usOverscanLeft = 0; - args.usOverscanBottom = 0; - args.usOverscanTop = 0; - args.ucCRTC = radeon_crtc->crtc_id; - - if (radeon_encoder->flags & RADEON_USE_RMX) { - if (radeon_encoder->rmx_type == RMX_FULL) { - args.usOverscanRight = 0; - args.usOverscanLeft = 0; - args.usOverscanBottom = 0; - args.usOverscanTop = 0; - } else if (radeon_encoder->rmx_type == RMX_CENTER) { - args.usOverscanTop = (adjusted_mode->crtc_vdisplay - mode->crtc_vdisplay) / 2; - args.usOverscanBottom = (adjusted_mode->crtc_vdisplay - mode->crtc_vdisplay) / 2; - args.usOverscanLeft = (adjusted_mode->crtc_hdisplay - mode->crtc_hdisplay) / 2; - args.usOverscanRight = (adjusted_mode->crtc_hdisplay - mode->crtc_hdisplay) / 2; - } else if (radeon_encoder->rmx_type == RMX_ASPECT) { - int a1 = mode->crtc_vdisplay * adjusted_mode->crtc_hdisplay; - int a2 = adjusted_mode->crtc_vdisplay * mode->crtc_hdisplay; - - if (a1 > a2) { - args.usOverscanLeft = (adjusted_mode->crtc_hdisplay - (a2 / mode->crtc_vdisplay)) / 2; - args.usOverscanRight = (adjusted_mode->crtc_hdisplay - (a2 / mode->crtc_vdisplay)) / 2; - } else if (a2 > a1) { - args.usOverscanLeft = (adjusted_mode->crtc_vdisplay - (a1 / mode->crtc_hdisplay)) / 2; - args.usOverscanRight = (adjusted_mode->crtc_vdisplay - (a1 / mode->crtc_hdisplay)) / 2; - } - } - } - - atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); - -} - -static void -atombios_scaler_setup(struct drm_encoder *encoder) -{ - struct drm_device *dev = encoder->dev; - struct radeon_device *rdev = dev->dev_private; - struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); - struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc); - ENABLE_SCALER_PS_ALLOCATION args; - int index = GetIndexIntoMasterTable(COMMAND, EnableScaler); - /* fixme - fill in enc_priv for atom dac */ - enum radeon_tv_std tv_std = TV_STD_NTSC; - - if (!ASIC_IS_AVIVO(rdev) && radeon_crtc->crtc_id) - return; - - memset(&args, 0, sizeof(args)); - - args.ucScaler = radeon_crtc->crtc_id; - - if (radeon_encoder->devices & (ATOM_DEVICE_TV_SUPPORT)) { - switch (tv_std) { - case TV_STD_NTSC: - default: - args.ucTVStandard = ATOM_TV_NTSC; - break; - case TV_STD_PAL: - args.ucTVStandard = ATOM_TV_PAL; - break; - case TV_STD_PAL_M: - args.ucTVStandard = ATOM_TV_PALM; - break; - case TV_STD_PAL_60: - args.ucTVStandard = ATOM_TV_PAL60; - break; - case TV_STD_NTSC_J: - args.ucTVStandard = ATOM_TV_NTSCJ; - break; - case TV_STD_SCART_PAL: - args.ucTVStandard = ATOM_TV_PAL; /* ??? */ - break; - case TV_STD_SECAM: - args.ucTVStandard = ATOM_TV_SECAM; - break; - case TV_STD_PAL_CN: - args.ucTVStandard = ATOM_TV_PALCN; - break; - } - args.ucEnable = SCALER_ENABLE_MULTITAP_MODE; - } else if (radeon_encoder->devices & (ATOM_DEVICE_CV_SUPPORT)) { - args.ucTVStandard = ATOM_TV_CV; - args.ucEnable = SCALER_ENABLE_MULTITAP_MODE; - } else if (radeon_encoder->flags & RADEON_USE_RMX) { - if (radeon_encoder->rmx_type == RMX_FULL) - args.ucEnable = ATOM_SCALER_EXPANSION; - else if (radeon_encoder->rmx_type == RMX_CENTER) - args.ucEnable = ATOM_SCALER_CENTER; - else if (radeon_encoder->rmx_type == RMX_ASPECT) - args.ucEnable = ATOM_SCALER_EXPANSION; - } else { - if (ASIC_IS_AVIVO(rdev)) - args.ucEnable = ATOM_SCALER_DISABLE; - else - args.ucEnable = ATOM_SCALER_CENTER; - } - - atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); - - if (radeon_encoder->devices & (ATOM_DEVICE_CV_SUPPORT | ATOM_DEVICE_TV_SUPPORT) - && rdev->family >= CHIP_RV515 && rdev->family <= CHIP_RV570) { - atom_rv515_force_tv_scaler(rdev); - } - -} - -static void radeon_atom_encoder_dpms(struct drm_encoder *encoder, int mode) { struct drm_device *dev = encoder->dev; @@ -1448,8 +1094,6 @@ radeon_atom_encoder_mode_set(struct drm_encoder *encoder, radeon_encoder->pixel_clock = adjusted_mode->clock; radeon_atombios_encoder_crtc_scratch_regs(encoder, radeon_crtc->crtc_id); - atombios_overscan_setup(encoder, mode, adjusted_mode); - atombios_scaler_setup(encoder); atombios_set_encoder_crtc_source(encoder); if (ASIC_IS_AVIVO(rdev)) { @@ -1667,6 +1311,7 @@ radeon_add_atom_encoder(struct drm_device *dev, uint32_t encoder_id, uint32_t su radeon_encoder->encoder_id = encoder_id; radeon_encoder->devices = supported_device; + radeon_encoder->rmx_type = RMX_OFF; switch (radeon_encoder->encoder_id) { case ENCODER_OBJECT_ID_INTERNAL_LVDS: diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c index 9e8f191eb64..3206c0ad7b6 100644 --- a/drivers/gpu/drm/radeon/radeon_fb.c +++ b/drivers/gpu/drm/radeon/radeon_fb.c @@ -101,9 +101,10 @@ static int radeonfb_setcolreg(unsigned regno, break; case 24: case 32: - fb->pseudo_palette[regno] = ((red & 0xff00) << 8) | - (green & 0xff00) | - ((blue & 0xff00) >> 8); + fb->pseudo_palette[regno] = + (((red >> 8) & 0xff) << info->var.red.offset) | + (((green >> 8) & 0xff) << info->var.green.offset) | + (((blue >> 8) & 0xff) << info->var.blue.offset); break; } } @@ -154,6 +155,7 @@ static int radeonfb_check_var(struct fb_var_screeninfo *var, var->transp.length = 0; var->transp.offset = 0; break; +#ifdef __LITTLE_ENDIAN case 15: var->red.offset = 10; var->green.offset = 5; @@ -194,6 +196,28 @@ static int radeonfb_check_var(struct fb_var_screeninfo *var, var->transp.length = 8; var->transp.offset = 24; break; +#else + case 24: + var->red.offset = 8; + var->green.offset = 16; + var->blue.offset = 24; + var->red.length = 8; + var->green.length = 8; + var->blue.length = 8; + var->transp.length = 0; + var->transp.offset = 0; + break; + case 32: + var->red.offset = 8; + var->green.offset = 16; + var->blue.offset = 24; + var->red.length = 8; + var->green.length = 8; + var->blue.length = 8; + var->transp.length = 8; + var->transp.offset = 0; + break; +#endif default: return -EINVAL; } @@ -447,10 +471,10 @@ static struct notifier_block paniced = { .notifier_call = radeonfb_panic, }; -static int radeon_align_pitch(struct radeon_device *rdev, int width, int bpp) +static int radeon_align_pitch(struct radeon_device *rdev, int width, int bpp, bool tiled) { int aligned = width; - int align_large = (ASIC_IS_AVIVO(rdev)); + int align_large = (ASIC_IS_AVIVO(rdev)) || tiled; int pitch_mask = 0; switch (bpp / 8) { @@ -488,12 +512,13 @@ int radeonfb_create(struct radeon_device *rdev, u64 fb_gpuaddr; void *fbptr = NULL; unsigned long tmp; + bool fb_tiled = false; /* useful for testing */ mode_cmd.width = surface_width; mode_cmd.height = surface_height; mode_cmd.bpp = 32; /* need to align pitch with crtc limits */ - mode_cmd.pitch = radeon_align_pitch(rdev, mode_cmd.width, mode_cmd.bpp) * ((mode_cmd.bpp + 1) / 8); + mode_cmd.pitch = radeon_align_pitch(rdev, mode_cmd.width, mode_cmd.bpp, fb_tiled) * ((mode_cmd.bpp + 1) / 8); mode_cmd.depth = 24; size = mode_cmd.pitch * mode_cmd.height; @@ -511,6 +536,8 @@ int radeonfb_create(struct radeon_device *rdev, } robj = gobj->driver_private; + if (fb_tiled) + radeon_object_set_tiling_flags(robj, RADEON_TILING_MACRO|RADEON_TILING_SURFACE, mode_cmd.pitch); mutex_lock(&rdev->ddev->struct_mutex); fb = radeon_framebuffer_create(rdev->ddev, &mode_cmd, gobj); if (fb == NULL) { @@ -539,6 +566,9 @@ int radeonfb_create(struct radeon_device *rdev, } rfbdev = info->par; + if (fb_tiled) + radeon_object_check_tiling(robj, 0, 0); + ret = radeon_object_kmap(robj, &fbptr); if (ret) { goto out_unref; @@ -572,6 +602,11 @@ int radeonfb_create(struct radeon_device *rdev, info->var.width = -1; info->var.xres = fb_width; info->var.yres = fb_height; + + /* setup aperture base/size for vesafb takeover */ + info->aperture_base = rdev->ddev->mode_config.fb_base; + info->aperture_size = rdev->mc.real_vram_size; + info->fix.mmio_start = 0; info->fix.mmio_len = 0; info->pixmap.size = 64*1024; @@ -600,6 +635,7 @@ int radeonfb_create(struct radeon_device *rdev, info->var.transp.offset = 0; info->var.transp.length = 0; break; +#ifdef __LITTLE_ENDIAN case 15: info->var.red.offset = 10; info->var.green.offset = 5; @@ -639,7 +675,29 @@ int radeonfb_create(struct radeon_device *rdev, info->var.transp.offset = 24; info->var.transp.length = 8; break; +#else + case 24: + info->var.red.offset = 8; + info->var.green.offset = 16; + info->var.blue.offset = 24; + info->var.red.length = 8; + info->var.green.length = 8; + info->var.blue.length = 8; + info->var.transp.offset = 0; + info->var.transp.length = 0; + break; + case 32: + info->var.red.offset = 8; + info->var.green.offset = 16; + info->var.blue.offset = 24; + info->var.red.length = 8; + info->var.green.length = 8; + info->var.blue.length = 8; + info->var.transp.offset = 0; + info->var.transp.length = 8; + break; default: +#endif break; } diff --git a/drivers/gpu/drm/radeon/radeon_fence.c b/drivers/gpu/drm/radeon/radeon_fence.c index 96afbf5ae2a..b4e48dd2e85 100644 --- a/drivers/gpu/drm/radeon/radeon_fence.c +++ b/drivers/gpu/drm/radeon/radeon_fence.c @@ -195,7 +195,7 @@ retry: r = wait_event_interruptible_timeout(rdev->fence_drv.queue, radeon_fence_signaled(fence), timeout); if (unlikely(r == -ERESTARTSYS)) { - return -ERESTART; + return -EBUSY; } } else { r = wait_event_timeout(rdev->fence_drv.queue, diff --git a/drivers/gpu/drm/radeon/radeon_gart.c b/drivers/gpu/drm/radeon/radeon_gart.c index d343a15316e..2977539880f 100644 --- a/drivers/gpu/drm/radeon/radeon_gart.c +++ b/drivers/gpu/drm/radeon/radeon_gart.c @@ -177,7 +177,7 @@ int radeon_gart_bind(struct radeon_device *rdev, unsigned offset, return -ENOMEM; } rdev->gart.pages[p] = pagelist[i]; - page_base = (uint32_t)rdev->gart.pages_addr[p]; + page_base = rdev->gart.pages_addr[p]; for (j = 0; j < (PAGE_SIZE / 4096); j++, t++) { radeon_gart_set_page(rdev, t, page_base); page_base += 4096; diff --git a/drivers/gpu/drm/radeon/radeon_gem.c b/drivers/gpu/drm/radeon/radeon_gem.c index eb516034235..cded5180c75 100644 --- a/drivers/gpu/drm/radeon/radeon_gem.c +++ b/drivers/gpu/drm/radeon/radeon_gem.c @@ -157,9 +157,9 @@ int radeon_gem_info_ioctl(struct drm_device *dev, void *data, struct radeon_device *rdev = dev->dev_private; struct drm_radeon_gem_info *args = data; - args->vram_size = rdev->mc.vram_size; + args->vram_size = rdev->mc.real_vram_size; /* FIXME: report somethings that makes sense */ - args->vram_visible = rdev->mc.vram_size - (4 * 1024 * 1024); + args->vram_visible = rdev->mc.real_vram_size - (4 * 1024 * 1024); args->gart_size = rdev->mc.gtt_size; return 0; } @@ -285,3 +285,44 @@ int radeon_gem_wait_idle_ioctl(struct drm_device *dev, void *data, mutex_unlock(&dev->struct_mutex); return r; } + +int radeon_gem_set_tiling_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp) +{ + struct drm_radeon_gem_set_tiling *args = data; + struct drm_gem_object *gobj; + struct radeon_object *robj; + int r = 0; + + DRM_DEBUG("%d \n", args->handle); + gobj = drm_gem_object_lookup(dev, filp, args->handle); + if (gobj == NULL) + return -EINVAL; + robj = gobj->driver_private; + radeon_object_set_tiling_flags(robj, args->tiling_flags, args->pitch); + mutex_lock(&dev->struct_mutex); + drm_gem_object_unreference(gobj); + mutex_unlock(&dev->struct_mutex); + return r; +} + +int radeon_gem_get_tiling_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp) +{ + struct drm_radeon_gem_get_tiling *args = data; + struct drm_gem_object *gobj; + struct radeon_object *robj; + int r = 0; + + DRM_DEBUG("\n"); + gobj = drm_gem_object_lookup(dev, filp, args->handle); + if (gobj == NULL) + return -EINVAL; + robj = gobj->driver_private; + radeon_object_get_tiling_flags(robj, &args->tiling_flags, + &args->pitch); + mutex_lock(&dev->struct_mutex); + drm_gem_object_unreference(gobj); + mutex_unlock(&dev->struct_mutex); + return r; +} diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c index 4612a7c146d..3357110e30c 100644 --- a/drivers/gpu/drm/radeon/radeon_kms.c +++ b/drivers/gpu/drm/radeon/radeon_kms.c @@ -58,6 +58,8 @@ int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags) if (r) { DRM_ERROR("Failed to initialize radeon, disabling IOCTL\n"); radeon_device_fini(rdev); + kfree(rdev); + dev->dev_private = NULL; return r; } return 0; @@ -291,5 +293,7 @@ struct drm_ioctl_desc radeon_ioctls_kms[] = { DRM_IOCTL_DEF(DRM_RADEON_GEM_WAIT_IDLE, radeon_gem_wait_idle_ioctl, DRM_AUTH), DRM_IOCTL_DEF(DRM_RADEON_CS, radeon_cs_ioctl, DRM_AUTH), DRM_IOCTL_DEF(DRM_RADEON_INFO, radeon_info_ioctl, DRM_AUTH), + DRM_IOCTL_DEF(DRM_RADEON_GEM_SET_TILING, radeon_gem_set_tiling_ioctl, DRM_AUTH), + DRM_IOCTL_DEF(DRM_RADEON_GEM_GET_TILING, radeon_gem_get_tiling_ioctl, DRM_AUTH), }; int radeon_max_kms_ioctl = DRM_ARRAY_SIZE(radeon_ioctls_kms); diff --git a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c index 8086ecf7f03..7d06dc98a42 100644 --- a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c +++ b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c @@ -29,6 +29,171 @@ #include "radeon_fixed.h" #include "radeon.h" +static void radeon_legacy_rmx_mode_set(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + int xres = mode->hdisplay; + int yres = mode->vdisplay; + bool hscale = true, vscale = true; + int hsync_wid; + int vsync_wid; + int hsync_start; + int blank_width; + u32 scale, inc, crtc_more_cntl; + u32 fp_horz_stretch, fp_vert_stretch, fp_horz_vert_active; + u32 fp_h_sync_strt_wid, fp_crtc_h_total_disp; + u32 fp_v_sync_strt_wid, fp_crtc_v_total_disp; + struct radeon_native_mode *native_mode = &radeon_crtc->native_mode; + + fp_vert_stretch = RREG32(RADEON_FP_VERT_STRETCH) & + (RADEON_VERT_STRETCH_RESERVED | + RADEON_VERT_AUTO_RATIO_INC); + fp_horz_stretch = RREG32(RADEON_FP_HORZ_STRETCH) & + (RADEON_HORZ_FP_LOOP_STRETCH | + RADEON_HORZ_AUTO_RATIO_INC); + + crtc_more_cntl = 0; + if ((rdev->family == CHIP_RS100) || + (rdev->family == CHIP_RS200)) { + /* This is to workaround the asic bug for RMX, some versions + of BIOS dosen't have this register initialized correctly. */ + crtc_more_cntl |= RADEON_CRTC_H_CUTOFF_ACTIVE_EN; + } + + + fp_crtc_h_total_disp = ((((mode->crtc_htotal / 8) - 1) & 0x3ff) + | ((((mode->crtc_hdisplay / 8) - 1) & 0x1ff) << 16)); + + hsync_wid = (mode->crtc_hsync_end - mode->crtc_hsync_start) / 8; + if (!hsync_wid) + hsync_wid = 1; + hsync_start = mode->crtc_hsync_start - 8; + + fp_h_sync_strt_wid = ((hsync_start & 0x1fff) + | ((hsync_wid & 0x3f) << 16) + | ((mode->flags & DRM_MODE_FLAG_NHSYNC) + ? RADEON_CRTC_H_SYNC_POL + : 0)); + + fp_crtc_v_total_disp = (((mode->crtc_vtotal - 1) & 0xffff) + | ((mode->crtc_vdisplay - 1) << 16)); + + vsync_wid = mode->crtc_vsync_end - mode->crtc_vsync_start; + if (!vsync_wid) + vsync_wid = 1; + + fp_v_sync_strt_wid = (((mode->crtc_vsync_start - 1) & 0xfff) + | ((vsync_wid & 0x1f) << 16) + | ((mode->flags & DRM_MODE_FLAG_NVSYNC) + ? RADEON_CRTC_V_SYNC_POL + : 0)); + + fp_horz_vert_active = 0; + + if (native_mode->panel_xres == 0 || + native_mode->panel_yres == 0) { + hscale = false; + vscale = false; + } else { + if (xres > native_mode->panel_xres) + xres = native_mode->panel_xres; + if (yres > native_mode->panel_yres) + yres = native_mode->panel_yres; + + if (xres == native_mode->panel_xres) + hscale = false; + if (yres == native_mode->panel_yres) + vscale = false; + } + + switch (radeon_crtc->rmx_type) { + case RMX_FULL: + case RMX_ASPECT: + if (!hscale) + fp_horz_stretch |= ((xres/8-1) << 16); + else { + inc = (fp_horz_stretch & RADEON_HORZ_AUTO_RATIO_INC) ? 1 : 0; + scale = ((xres + inc) * RADEON_HORZ_STRETCH_RATIO_MAX) + / native_mode->panel_xres + 1; + fp_horz_stretch |= (((scale) & RADEON_HORZ_STRETCH_RATIO_MASK) | + RADEON_HORZ_STRETCH_BLEND | + RADEON_HORZ_STRETCH_ENABLE | + ((native_mode->panel_xres/8-1) << 16)); + } + + if (!vscale) + fp_vert_stretch |= ((yres-1) << 12); + else { + inc = (fp_vert_stretch & RADEON_VERT_AUTO_RATIO_INC) ? 1 : 0; + scale = ((yres + inc) * RADEON_VERT_STRETCH_RATIO_MAX) + / native_mode->panel_yres + 1; + fp_vert_stretch |= (((scale) & RADEON_VERT_STRETCH_RATIO_MASK) | + RADEON_VERT_STRETCH_ENABLE | + RADEON_VERT_STRETCH_BLEND | + ((native_mode->panel_yres-1) << 12)); + } + break; + case RMX_CENTER: + fp_horz_stretch |= ((xres/8-1) << 16); + fp_vert_stretch |= ((yres-1) << 12); + + crtc_more_cntl |= (RADEON_CRTC_AUTO_HORZ_CENTER_EN | + RADEON_CRTC_AUTO_VERT_CENTER_EN); + + blank_width = (mode->crtc_hblank_end - mode->crtc_hblank_start) / 8; + if (blank_width > 110) + blank_width = 110; + + fp_crtc_h_total_disp = (((blank_width) & 0x3ff) + | ((((mode->crtc_hdisplay / 8) - 1) & 0x1ff) << 16)); + + hsync_wid = (mode->crtc_hsync_end - mode->crtc_hsync_start) / 8; + if (!hsync_wid) + hsync_wid = 1; + + fp_h_sync_strt_wid = ((((mode->crtc_hsync_start - mode->crtc_hblank_start) / 8) & 0x1fff) + | ((hsync_wid & 0x3f) << 16) + | ((mode->flags & DRM_MODE_FLAG_NHSYNC) + ? RADEON_CRTC_H_SYNC_POL + : 0)); + + fp_crtc_v_total_disp = (((mode->crtc_vblank_end - mode->crtc_vblank_start) & 0xffff) + | ((mode->crtc_vdisplay - 1) << 16)); + + vsync_wid = mode->crtc_vsync_end - mode->crtc_vsync_start; + if (!vsync_wid) + vsync_wid = 1; + + fp_v_sync_strt_wid = ((((mode->crtc_vsync_start - mode->crtc_vblank_start) & 0xfff) + | ((vsync_wid & 0x1f) << 16) + | ((mode->flags & DRM_MODE_FLAG_NVSYNC) + ? RADEON_CRTC_V_SYNC_POL + : 0))); + + fp_horz_vert_active = (((native_mode->panel_yres) & 0xfff) | + (((native_mode->panel_xres / 8) & 0x1ff) << 16)); + break; + case RMX_OFF: + default: + fp_horz_stretch |= ((xres/8-1) << 16); + fp_vert_stretch |= ((yres-1) << 12); + break; + } + + WREG32(RADEON_FP_HORZ_STRETCH, fp_horz_stretch); + WREG32(RADEON_FP_VERT_STRETCH, fp_vert_stretch); + WREG32(RADEON_CRTC_MORE_CNTL, crtc_more_cntl); + WREG32(RADEON_FP_HORZ_VERT_ACTIVE, fp_horz_vert_active); + WREG32(RADEON_FP_H_SYNC_STRT_WID, fp_h_sync_strt_wid); + WREG32(RADEON_FP_V_SYNC_STRT_WID, fp_v_sync_strt_wid); + WREG32(RADEON_FP_CRTC_H_TOTAL_DISP, fp_crtc_h_total_disp); + WREG32(RADEON_FP_CRTC_V_TOTAL_DISP, fp_crtc_v_total_disp); +} + void radeon_restore_common_regs(struct drm_device *dev) { /* don't need this yet */ @@ -235,6 +400,7 @@ int radeon_crtc_set_base(struct drm_crtc *crtc, int x, int y, uint64_t base; uint32_t crtc_offset, crtc_offset_cntl, crtc_tile_x0_y0 = 0; uint32_t crtc_pitch, pitch_pixels; + uint32_t tiling_flags; DRM_DEBUG("\n"); @@ -244,7 +410,12 @@ int radeon_crtc_set_base(struct drm_crtc *crtc, int x, int y, if (radeon_gem_object_pin(obj, RADEON_GEM_DOMAIN_VRAM, &base)) { return -EINVAL; } - crtc_offset = (u32)base; + /* if scanout was in GTT this really wouldn't work */ + /* crtc offset is from display base addr not FB location */ + radeon_crtc->legacy_display_base_addr = rdev->mc.vram_location; + + base -= radeon_crtc->legacy_display_base_addr; + crtc_offset_cntl = 0; pitch_pixels = crtc->fb->pitch / (crtc->fb->bits_per_pixel / 8); @@ -253,8 +424,12 @@ int radeon_crtc_set_base(struct drm_crtc *crtc, int x, int y, (crtc->fb->bits_per_pixel * 8)); crtc_pitch |= crtc_pitch << 16; - /* TODO tiling */ - if (0) { + radeon_object_get_tiling_flags(obj->driver_private, + &tiling_flags, NULL); + if (tiling_flags & RADEON_TILING_MICRO) + DRM_ERROR("trying to scanout microtiled buffer\n"); + + if (tiling_flags & RADEON_TILING_MACRO) { if (ASIC_IS_R300(rdev)) crtc_offset_cntl |= (R300_CRTC_X_Y_MODE_EN | R300_CRTC_MICRO_TILE_BUFFER_DIS | @@ -270,15 +445,13 @@ int radeon_crtc_set_base(struct drm_crtc *crtc, int x, int y, crtc_offset_cntl &= ~RADEON_CRTC_TILE_EN; } - - /* TODO more tiling */ - if (0) { + if (tiling_flags & RADEON_TILING_MACRO) { if (ASIC_IS_R300(rdev)) { crtc_tile_x0_y0 = x | (y << 16); base &= ~0x7ff; } else { int byteshift = crtc->fb->bits_per_pixel >> 4; - int tile_addr = (((y >> 3) * crtc->fb->width + x) >> (8 - byteshift)) << 11; + int tile_addr = (((y >> 3) * pitch_pixels + x) >> (8 - byteshift)) << 11; base += tile_addr + ((x << byteshift) % 256) + ((y % 8) << 8); crtc_offset_cntl |= (y % 16); } @@ -303,11 +476,9 @@ int radeon_crtc_set_base(struct drm_crtc *crtc, int x, int y, base &= ~7; - /* update sarea TODO */ - crtc_offset = (u32)base; - WREG32(RADEON_DISPLAY_BASE_ADDR + radeon_crtc->crtc_offset, rdev->mc.vram_location); + WREG32(RADEON_DISPLAY_BASE_ADDR + radeon_crtc->crtc_offset, radeon_crtc->legacy_display_base_addr); if (ASIC_IS_R300(rdev)) { if (radeon_crtc->crtc_id) @@ -751,6 +922,8 @@ static bool radeon_crtc_mode_fixup(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { + if (!radeon_crtc_scaling_mode_fixup(crtc, mode, adjusted_mode)) + return false; return true; } @@ -759,16 +932,25 @@ static int radeon_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *adjusted_mode, int x, int y, struct drm_framebuffer *old_fb) { - - DRM_DEBUG("\n"); + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; /* TODO TV */ - radeon_crtc_set_base(crtc, x, y, old_fb); radeon_set_crtc_timing(crtc, adjusted_mode); radeon_set_pll(crtc, adjusted_mode); - radeon_init_disp_bandwidth(crtc->dev); - + radeon_bandwidth_update(rdev); + if (radeon_crtc->crtc_id == 0) { + radeon_legacy_rmx_mode_set(crtc, mode, adjusted_mode); + } else { + if (radeon_crtc->rmx_type != RMX_OFF) { + /* FIXME: only first crtc has rmx what should we + * do ? + */ + DRM_ERROR("Mode need scaling but only first crtc can do that.\n"); + } + } return 0; } @@ -799,478 +981,3 @@ void radeon_legacy_init_crtc(struct drm_device *dev, radeon_crtc->crtc_offset = RADEON_CRTC2_H_TOTAL_DISP - RADEON_CRTC_H_TOTAL_DISP; drm_crtc_helper_add(&radeon_crtc->base, &legacy_helper_funcs); } - -void radeon_init_disp_bw_legacy(struct drm_device *dev, - struct drm_display_mode *mode1, - uint32_t pixel_bytes1, - struct drm_display_mode *mode2, - uint32_t pixel_bytes2) -{ - struct radeon_device *rdev = dev->dev_private; - fixed20_12 trcd_ff, trp_ff, tras_ff, trbs_ff, tcas_ff; - fixed20_12 sclk_ff, mclk_ff, sclk_eff_ff, sclk_delay_ff; - fixed20_12 peak_disp_bw, mem_bw, pix_clk, pix_clk2, temp_ff, crit_point_ff; - uint32_t temp, data, mem_trcd, mem_trp, mem_tras; - fixed20_12 memtcas_ff[8] = { - fixed_init(1), - fixed_init(2), - fixed_init(3), - fixed_init(0), - fixed_init_half(1), - fixed_init_half(2), - fixed_init(0), - }; - fixed20_12 memtcas_rs480_ff[8] = { - fixed_init(0), - fixed_init(1), - fixed_init(2), - fixed_init(3), - fixed_init(0), - fixed_init_half(1), - fixed_init_half(2), - fixed_init_half(3), - }; - fixed20_12 memtcas2_ff[8] = { - fixed_init(0), - fixed_init(1), - fixed_init(2), - fixed_init(3), - fixed_init(4), - fixed_init(5), - fixed_init(6), - fixed_init(7), - }; - fixed20_12 memtrbs[8] = { - fixed_init(1), - fixed_init_half(1), - fixed_init(2), - fixed_init_half(2), - fixed_init(3), - fixed_init_half(3), - fixed_init(4), - fixed_init_half(4) - }; - fixed20_12 memtrbs_r4xx[8] = { - fixed_init(4), - fixed_init(5), - fixed_init(6), - fixed_init(7), - fixed_init(8), - fixed_init(9), - fixed_init(10), - fixed_init(11) - }; - fixed20_12 min_mem_eff; - fixed20_12 mc_latency_sclk, mc_latency_mclk, k1; - fixed20_12 cur_latency_mclk, cur_latency_sclk; - fixed20_12 disp_latency, disp_latency_overhead, disp_drain_rate, - disp_drain_rate2, read_return_rate; - fixed20_12 time_disp1_drop_priority; - int c; - int cur_size = 16; /* in octawords */ - int critical_point = 0, critical_point2; -/* uint32_t read_return_rate, time_disp1_drop_priority; */ - int stop_req, max_stop_req; - - min_mem_eff.full = rfixed_const_8(0); - /* get modes */ - if ((rdev->disp_priority == 2) && ASIC_IS_R300(rdev)) { - uint32_t mc_init_misc_lat_timer = RREG32(R300_MC_INIT_MISC_LAT_TIMER); - mc_init_misc_lat_timer &= ~(R300_MC_DISP1R_INIT_LAT_MASK << R300_MC_DISP1R_INIT_LAT_SHIFT); - mc_init_misc_lat_timer &= ~(R300_MC_DISP0R_INIT_LAT_MASK << R300_MC_DISP0R_INIT_LAT_SHIFT); - /* check crtc enables */ - if (mode2) - mc_init_misc_lat_timer |= (1 << R300_MC_DISP1R_INIT_LAT_SHIFT); - if (mode1) - mc_init_misc_lat_timer |= (1 << R300_MC_DISP0R_INIT_LAT_SHIFT); - WREG32(R300_MC_INIT_MISC_LAT_TIMER, mc_init_misc_lat_timer); - } - - /* - * determine is there is enough bw for current mode - */ - mclk_ff.full = rfixed_const(rdev->clock.default_mclk); - temp_ff.full = rfixed_const(100); - mclk_ff.full = rfixed_div(mclk_ff, temp_ff); - sclk_ff.full = rfixed_const(rdev->clock.default_sclk); - sclk_ff.full = rfixed_div(sclk_ff, temp_ff); - - temp = (rdev->mc.vram_width / 8) * (rdev->mc.vram_is_ddr ? 2 : 1); - temp_ff.full = rfixed_const(temp); - mem_bw.full = rfixed_mul(mclk_ff, temp_ff); - - pix_clk.full = 0; - pix_clk2.full = 0; - peak_disp_bw.full = 0; - if (mode1) { - temp_ff.full = rfixed_const(1000); - pix_clk.full = rfixed_const(mode1->clock); /* convert to fixed point */ - pix_clk.full = rfixed_div(pix_clk, temp_ff); - temp_ff.full = rfixed_const(pixel_bytes1); - peak_disp_bw.full += rfixed_mul(pix_clk, temp_ff); - } - if (mode2) { - temp_ff.full = rfixed_const(1000); - pix_clk2.full = rfixed_const(mode2->clock); /* convert to fixed point */ - pix_clk2.full = rfixed_div(pix_clk2, temp_ff); - temp_ff.full = rfixed_const(pixel_bytes2); - peak_disp_bw.full += rfixed_mul(pix_clk2, temp_ff); - } - - mem_bw.full = rfixed_mul(mem_bw, min_mem_eff); - if (peak_disp_bw.full >= mem_bw.full) { - DRM_ERROR("You may not have enough display bandwidth for current mode\n" - "If you have flickering problem, try to lower resolution, refresh rate, or color depth\n"); - } - - /* Get values from the EXT_MEM_CNTL register...converting its contents. */ - temp = RREG32(RADEON_MEM_TIMING_CNTL); - if ((rdev->family == CHIP_RV100) || (rdev->flags & RADEON_IS_IGP)) { /* RV100, M6, IGPs */ - mem_trcd = ((temp >> 2) & 0x3) + 1; - mem_trp = ((temp & 0x3)) + 1; - mem_tras = ((temp & 0x70) >> 4) + 1; - } else if (rdev->family == CHIP_R300 || - rdev->family == CHIP_R350) { /* r300, r350 */ - mem_trcd = (temp & 0x7) + 1; - mem_trp = ((temp >> 8) & 0x7) + 1; - mem_tras = ((temp >> 11) & 0xf) + 4; - } else if (rdev->family == CHIP_RV350 || - rdev->family <= CHIP_RV380) { - /* rv3x0 */ - mem_trcd = (temp & 0x7) + 3; - mem_trp = ((temp >> 8) & 0x7) + 3; - mem_tras = ((temp >> 11) & 0xf) + 6; - } else if (rdev->family == CHIP_R420 || - rdev->family == CHIP_R423 || - rdev->family == CHIP_RV410) { - /* r4xx */ - mem_trcd = (temp & 0xf) + 3; - if (mem_trcd > 15) - mem_trcd = 15; - mem_trp = ((temp >> 8) & 0xf) + 3; - if (mem_trp > 15) - mem_trp = 15; - mem_tras = ((temp >> 12) & 0x1f) + 6; - if (mem_tras > 31) - mem_tras = 31; - } else { /* RV200, R200 */ - mem_trcd = (temp & 0x7) + 1; - mem_trp = ((temp >> 8) & 0x7) + 1; - mem_tras = ((temp >> 12) & 0xf) + 4; - } - /* convert to FF */ - trcd_ff.full = rfixed_const(mem_trcd); - trp_ff.full = rfixed_const(mem_trp); - tras_ff.full = rfixed_const(mem_tras); - - /* Get values from the MEM_SDRAM_MODE_REG register...converting its */ - temp = RREG32(RADEON_MEM_SDRAM_MODE_REG); - data = (temp & (7 << 20)) >> 20; - if ((rdev->family == CHIP_RV100) || rdev->flags & RADEON_IS_IGP) { - if (rdev->family == CHIP_RS480) /* don't think rs400 */ - tcas_ff = memtcas_rs480_ff[data]; - else - tcas_ff = memtcas_ff[data]; - } else - tcas_ff = memtcas2_ff[data]; - - if (rdev->family == CHIP_RS400 || - rdev->family == CHIP_RS480) { - /* extra cas latency stored in bits 23-25 0-4 clocks */ - data = (temp >> 23) & 0x7; - if (data < 5) - tcas_ff.full += rfixed_const(data); - } - - if (ASIC_IS_R300(rdev) && !(rdev->flags & RADEON_IS_IGP)) { - /* on the R300, Tcas is included in Trbs. - */ - temp = RREG32(RADEON_MEM_CNTL); - data = (R300_MEM_NUM_CHANNELS_MASK & temp); - if (data == 1) { - if (R300_MEM_USE_CD_CH_ONLY & temp) { - temp = RREG32(R300_MC_IND_INDEX); - temp &= ~R300_MC_IND_ADDR_MASK; - temp |= R300_MC_READ_CNTL_CD_mcind; - WREG32(R300_MC_IND_INDEX, temp); - temp = RREG32(R300_MC_IND_DATA); - data = (R300_MEM_RBS_POSITION_C_MASK & temp); - } else { - temp = RREG32(R300_MC_READ_CNTL_AB); - data = (R300_MEM_RBS_POSITION_A_MASK & temp); - } - } else { - temp = RREG32(R300_MC_READ_CNTL_AB); - data = (R300_MEM_RBS_POSITION_A_MASK & temp); - } - if (rdev->family == CHIP_RV410 || - rdev->family == CHIP_R420 || - rdev->family == CHIP_R423) - trbs_ff = memtrbs_r4xx[data]; - else - trbs_ff = memtrbs[data]; - tcas_ff.full += trbs_ff.full; - } - - sclk_eff_ff.full = sclk_ff.full; - - if (rdev->flags & RADEON_IS_AGP) { - fixed20_12 agpmode_ff; - agpmode_ff.full = rfixed_const(radeon_agpmode); - temp_ff.full = rfixed_const_666(16); - sclk_eff_ff.full -= rfixed_mul(agpmode_ff, temp_ff); - } - /* TODO PCIE lanes may affect this - agpmode == 16?? */ - - if (ASIC_IS_R300(rdev)) { - sclk_delay_ff.full = rfixed_const(250); - } else { - if ((rdev->family == CHIP_RV100) || - rdev->flags & RADEON_IS_IGP) { - if (rdev->mc.vram_is_ddr) - sclk_delay_ff.full = rfixed_const(41); - else - sclk_delay_ff.full = rfixed_const(33); - } else { - if (rdev->mc.vram_width == 128) - sclk_delay_ff.full = rfixed_const(57); - else - sclk_delay_ff.full = rfixed_const(41); - } - } - - mc_latency_sclk.full = rfixed_div(sclk_delay_ff, sclk_eff_ff); - - if (rdev->mc.vram_is_ddr) { - if (rdev->mc.vram_width == 32) { - k1.full = rfixed_const(40); - c = 3; - } else { - k1.full = rfixed_const(20); - c = 1; - } - } else { - k1.full = rfixed_const(40); - c = 3; - } - - temp_ff.full = rfixed_const(2); - mc_latency_mclk.full = rfixed_mul(trcd_ff, temp_ff); - temp_ff.full = rfixed_const(c); - mc_latency_mclk.full += rfixed_mul(tcas_ff, temp_ff); - temp_ff.full = rfixed_const(4); - mc_latency_mclk.full += rfixed_mul(tras_ff, temp_ff); - mc_latency_mclk.full += rfixed_mul(trp_ff, temp_ff); - mc_latency_mclk.full += k1.full; - - mc_latency_mclk.full = rfixed_div(mc_latency_mclk, mclk_ff); - mc_latency_mclk.full += rfixed_div(temp_ff, sclk_eff_ff); - - /* - HW cursor time assuming worst case of full size colour cursor. - */ - temp_ff.full = rfixed_const((2 * (cur_size - (rdev->mc.vram_is_ddr + 1)))); - temp_ff.full += trcd_ff.full; - if (temp_ff.full < tras_ff.full) - temp_ff.full = tras_ff.full; - cur_latency_mclk.full = rfixed_div(temp_ff, mclk_ff); - - temp_ff.full = rfixed_const(cur_size); - cur_latency_sclk.full = rfixed_div(temp_ff, sclk_eff_ff); - /* - Find the total latency for the display data. - */ - disp_latency_overhead.full = rfixed_const(80); - disp_latency_overhead.full = rfixed_div(disp_latency_overhead, sclk_ff); - mc_latency_mclk.full += disp_latency_overhead.full + cur_latency_mclk.full; - mc_latency_sclk.full += disp_latency_overhead.full + cur_latency_sclk.full; - - if (mc_latency_mclk.full > mc_latency_sclk.full) - disp_latency.full = mc_latency_mclk.full; - else - disp_latency.full = mc_latency_sclk.full; - - /* setup Max GRPH_STOP_REQ default value */ - if (ASIC_IS_RV100(rdev)) - max_stop_req = 0x5c; - else - max_stop_req = 0x7c; - - if (mode1) { - /* CRTC1 - Set GRPH_BUFFER_CNTL register using h/w defined optimal values. - GRPH_STOP_REQ <= MIN[ 0x7C, (CRTC_H_DISP + 1) * (bit depth) / 0x10 ] - */ - stop_req = mode1->hdisplay * pixel_bytes1 / 16; - - if (stop_req > max_stop_req) - stop_req = max_stop_req; - - /* - Find the drain rate of the display buffer. - */ - temp_ff.full = rfixed_const((16/pixel_bytes1)); - disp_drain_rate.full = rfixed_div(pix_clk, temp_ff); - - /* - Find the critical point of the display buffer. - */ - crit_point_ff.full = rfixed_mul(disp_drain_rate, disp_latency); - crit_point_ff.full += rfixed_const_half(0); - - critical_point = rfixed_trunc(crit_point_ff); - - if (rdev->disp_priority == 2) { - critical_point = 0; - } - - /* - The critical point should never be above max_stop_req-4. Setting - GRPH_CRITICAL_CNTL = 0 will thus force high priority all the time. - */ - if (max_stop_req - critical_point < 4) - critical_point = 0; - - if (critical_point == 0 && mode2 && rdev->family == CHIP_R300) { - /* some R300 cards have problem with this set to 0, when CRTC2 is enabled.*/ - critical_point = 0x10; - } - - temp = RREG32(RADEON_GRPH_BUFFER_CNTL); - temp &= ~(RADEON_GRPH_STOP_REQ_MASK); - temp |= (stop_req << RADEON_GRPH_STOP_REQ_SHIFT); - temp &= ~(RADEON_GRPH_START_REQ_MASK); - if ((rdev->family == CHIP_R350) && - (stop_req > 0x15)) { - stop_req -= 0x10; - } - temp |= (stop_req << RADEON_GRPH_START_REQ_SHIFT); - temp |= RADEON_GRPH_BUFFER_SIZE; - temp &= ~(RADEON_GRPH_CRITICAL_CNTL | - RADEON_GRPH_CRITICAL_AT_SOF | - RADEON_GRPH_STOP_CNTL); - /* - Write the result into the register. - */ - WREG32(RADEON_GRPH_BUFFER_CNTL, ((temp & ~RADEON_GRPH_CRITICAL_POINT_MASK) | - (critical_point << RADEON_GRPH_CRITICAL_POINT_SHIFT))); - -#if 0 - if ((rdev->family == CHIP_RS400) || - (rdev->family == CHIP_RS480)) { - /* attempt to program RS400 disp regs correctly ??? */ - temp = RREG32(RS400_DISP1_REG_CNTL); - temp &= ~(RS400_DISP1_START_REQ_LEVEL_MASK | - RS400_DISP1_STOP_REQ_LEVEL_MASK); - WREG32(RS400_DISP1_REQ_CNTL1, (temp | - (critical_point << RS400_DISP1_START_REQ_LEVEL_SHIFT) | - (critical_point << RS400_DISP1_STOP_REQ_LEVEL_SHIFT))); - temp = RREG32(RS400_DMIF_MEM_CNTL1); - temp &= ~(RS400_DISP1_CRITICAL_POINT_START_MASK | - RS400_DISP1_CRITICAL_POINT_STOP_MASK); - WREG32(RS400_DMIF_MEM_CNTL1, (temp | - (critical_point << RS400_DISP1_CRITICAL_POINT_START_SHIFT) | - (critical_point << RS400_DISP1_CRITICAL_POINT_STOP_SHIFT))); - } -#endif - - DRM_DEBUG("GRPH_BUFFER_CNTL from to %x\n", - /* (unsigned int)info->SavedReg->grph_buffer_cntl, */ - (unsigned int)RREG32(RADEON_GRPH_BUFFER_CNTL)); - } - - if (mode2) { - u32 grph2_cntl; - stop_req = mode2->hdisplay * pixel_bytes2 / 16; - - if (stop_req > max_stop_req) - stop_req = max_stop_req; - - /* - Find the drain rate of the display buffer. - */ - temp_ff.full = rfixed_const((16/pixel_bytes2)); - disp_drain_rate2.full = rfixed_div(pix_clk2, temp_ff); - - grph2_cntl = RREG32(RADEON_GRPH2_BUFFER_CNTL); - grph2_cntl &= ~(RADEON_GRPH_STOP_REQ_MASK); - grph2_cntl |= (stop_req << RADEON_GRPH_STOP_REQ_SHIFT); - grph2_cntl &= ~(RADEON_GRPH_START_REQ_MASK); - if ((rdev->family == CHIP_R350) && - (stop_req > 0x15)) { - stop_req -= 0x10; - } - grph2_cntl |= (stop_req << RADEON_GRPH_START_REQ_SHIFT); - grph2_cntl |= RADEON_GRPH_BUFFER_SIZE; - grph2_cntl &= ~(RADEON_GRPH_CRITICAL_CNTL | - RADEON_GRPH_CRITICAL_AT_SOF | - RADEON_GRPH_STOP_CNTL); - - if ((rdev->family == CHIP_RS100) || - (rdev->family == CHIP_RS200)) - critical_point2 = 0; - else { - temp = (rdev->mc.vram_width * rdev->mc.vram_is_ddr + 1)/128; - temp_ff.full = rfixed_const(temp); - temp_ff.full = rfixed_mul(mclk_ff, temp_ff); - if (sclk_ff.full < temp_ff.full) - temp_ff.full = sclk_ff.full; - - read_return_rate.full = temp_ff.full; - - if (mode1) { - temp_ff.full = read_return_rate.full - disp_drain_rate.full; - time_disp1_drop_priority.full = rfixed_div(crit_point_ff, temp_ff); - } else { - time_disp1_drop_priority.full = 0; - } - crit_point_ff.full = disp_latency.full + time_disp1_drop_priority.full + disp_latency.full; - crit_point_ff.full = rfixed_mul(crit_point_ff, disp_drain_rate2); - crit_point_ff.full += rfixed_const_half(0); - - critical_point2 = rfixed_trunc(crit_point_ff); - - if (rdev->disp_priority == 2) { - critical_point2 = 0; - } - - if (max_stop_req - critical_point2 < 4) - critical_point2 = 0; - - } - - if (critical_point2 == 0 && rdev->family == CHIP_R300) { - /* some R300 cards have problem with this set to 0 */ - critical_point2 = 0x10; - } - - WREG32(RADEON_GRPH2_BUFFER_CNTL, ((grph2_cntl & ~RADEON_GRPH_CRITICAL_POINT_MASK) | - (critical_point2 << RADEON_GRPH_CRITICAL_POINT_SHIFT))); - - if ((rdev->family == CHIP_RS400) || - (rdev->family == CHIP_RS480)) { -#if 0 - /* attempt to program RS400 disp2 regs correctly ??? */ - temp = RREG32(RS400_DISP2_REQ_CNTL1); - temp &= ~(RS400_DISP2_START_REQ_LEVEL_MASK | - RS400_DISP2_STOP_REQ_LEVEL_MASK); - WREG32(RS400_DISP2_REQ_CNTL1, (temp | - (critical_point2 << RS400_DISP1_START_REQ_LEVEL_SHIFT) | - (critical_point2 << RS400_DISP1_STOP_REQ_LEVEL_SHIFT))); - temp = RREG32(RS400_DISP2_REQ_CNTL2); - temp &= ~(RS400_DISP2_CRITICAL_POINT_START_MASK | - RS400_DISP2_CRITICAL_POINT_STOP_MASK); - WREG32(RS400_DISP2_REQ_CNTL2, (temp | - (critical_point2 << RS400_DISP2_CRITICAL_POINT_START_SHIFT) | - (critical_point2 << RS400_DISP2_CRITICAL_POINT_STOP_SHIFT))); -#endif - WREG32(RS400_DISP2_REQ_CNTL1, 0x105DC1CC); - WREG32(RS400_DISP2_REQ_CNTL2, 0x2749D000); - WREG32(RS400_DMIF_MEM_CNTL1, 0x29CA71DC); - WREG32(RS400_DISP1_REQ_CNTL1, 0x28FBC3AC); - } - - DRM_DEBUG("GRPH2_BUFFER_CNTL from to %x\n", - (unsigned int)RREG32(RADEON_GRPH2_BUFFER_CNTL)); - } -} diff --git a/drivers/gpu/drm/radeon/radeon_legacy_encoders.c b/drivers/gpu/drm/radeon/radeon_legacy_encoders.c index 2c2f42de1d4..34d0f58eb94 100644 --- a/drivers/gpu/drm/radeon/radeon_legacy_encoders.c +++ b/drivers/gpu/drm/radeon/radeon_legacy_encoders.c @@ -30,170 +30,6 @@ #include "atom.h" -static void radeon_legacy_rmx_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - struct drm_device *dev = encoder->dev; - struct radeon_device *rdev = dev->dev_private; - struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); - int xres = mode->hdisplay; - int yres = mode->vdisplay; - bool hscale = true, vscale = true; - int hsync_wid; - int vsync_wid; - int hsync_start; - uint32_t scale, inc; - uint32_t fp_horz_stretch, fp_vert_stretch, crtc_more_cntl, fp_horz_vert_active; - uint32_t fp_h_sync_strt_wid, fp_v_sync_strt_wid, fp_crtc_h_total_disp, fp_crtc_v_total_disp; - struct radeon_native_mode *native_mode = &radeon_encoder->native_mode; - - DRM_DEBUG("\n"); - - fp_vert_stretch = RREG32(RADEON_FP_VERT_STRETCH) & - (RADEON_VERT_STRETCH_RESERVED | - RADEON_VERT_AUTO_RATIO_INC); - fp_horz_stretch = RREG32(RADEON_FP_HORZ_STRETCH) & - (RADEON_HORZ_FP_LOOP_STRETCH | - RADEON_HORZ_AUTO_RATIO_INC); - - crtc_more_cntl = 0; - if ((rdev->family == CHIP_RS100) || - (rdev->family == CHIP_RS200)) { - /* This is to workaround the asic bug for RMX, some versions - of BIOS dosen't have this register initialized correctly. */ - crtc_more_cntl |= RADEON_CRTC_H_CUTOFF_ACTIVE_EN; - } - - - fp_crtc_h_total_disp = ((((mode->crtc_htotal / 8) - 1) & 0x3ff) - | ((((mode->crtc_hdisplay / 8) - 1) & 0x1ff) << 16)); - - hsync_wid = (mode->crtc_hsync_end - mode->crtc_hsync_start) / 8; - if (!hsync_wid) - hsync_wid = 1; - hsync_start = mode->crtc_hsync_start - 8; - - fp_h_sync_strt_wid = ((hsync_start & 0x1fff) - | ((hsync_wid & 0x3f) << 16) - | ((mode->flags & DRM_MODE_FLAG_NHSYNC) - ? RADEON_CRTC_H_SYNC_POL - : 0)); - - fp_crtc_v_total_disp = (((mode->crtc_vtotal - 1) & 0xffff) - | ((mode->crtc_vdisplay - 1) << 16)); - - vsync_wid = mode->crtc_vsync_end - mode->crtc_vsync_start; - if (!vsync_wid) - vsync_wid = 1; - - fp_v_sync_strt_wid = (((mode->crtc_vsync_start - 1) & 0xfff) - | ((vsync_wid & 0x1f) << 16) - | ((mode->flags & DRM_MODE_FLAG_NVSYNC) - ? RADEON_CRTC_V_SYNC_POL - : 0)); - - fp_horz_vert_active = 0; - - if (native_mode->panel_xres == 0 || - native_mode->panel_yres == 0) { - hscale = false; - vscale = false; - } else { - if (xres > native_mode->panel_xres) - xres = native_mode->panel_xres; - if (yres > native_mode->panel_yres) - yres = native_mode->panel_yres; - - if (xres == native_mode->panel_xres) - hscale = false; - if (yres == native_mode->panel_yres) - vscale = false; - } - - if (radeon_encoder->flags & RADEON_USE_RMX) { - if (radeon_encoder->rmx_type != RMX_CENTER) { - if (!hscale) - fp_horz_stretch |= ((xres/8-1) << 16); - else { - inc = (fp_horz_stretch & RADEON_HORZ_AUTO_RATIO_INC) ? 1 : 0; - scale = ((xres + inc) * RADEON_HORZ_STRETCH_RATIO_MAX) - / native_mode->panel_xres + 1; - fp_horz_stretch |= (((scale) & RADEON_HORZ_STRETCH_RATIO_MASK) | - RADEON_HORZ_STRETCH_BLEND | - RADEON_HORZ_STRETCH_ENABLE | - ((native_mode->panel_xres/8-1) << 16)); - } - - if (!vscale) - fp_vert_stretch |= ((yres-1) << 12); - else { - inc = (fp_vert_stretch & RADEON_VERT_AUTO_RATIO_INC) ? 1 : 0; - scale = ((yres + inc) * RADEON_VERT_STRETCH_RATIO_MAX) - / native_mode->panel_yres + 1; - fp_vert_stretch |= (((scale) & RADEON_VERT_STRETCH_RATIO_MASK) | - RADEON_VERT_STRETCH_ENABLE | - RADEON_VERT_STRETCH_BLEND | - ((native_mode->panel_yres-1) << 12)); - } - } else if (radeon_encoder->rmx_type == RMX_CENTER) { - int blank_width; - - fp_horz_stretch |= ((xres/8-1) << 16); - fp_vert_stretch |= ((yres-1) << 12); - - crtc_more_cntl |= (RADEON_CRTC_AUTO_HORZ_CENTER_EN | - RADEON_CRTC_AUTO_VERT_CENTER_EN); - - blank_width = (mode->crtc_hblank_end - mode->crtc_hblank_start) / 8; - if (blank_width > 110) - blank_width = 110; - - fp_crtc_h_total_disp = (((blank_width) & 0x3ff) - | ((((mode->crtc_hdisplay / 8) - 1) & 0x1ff) << 16)); - - hsync_wid = (mode->crtc_hsync_end - mode->crtc_hsync_start) / 8; - if (!hsync_wid) - hsync_wid = 1; - - fp_h_sync_strt_wid = ((((mode->crtc_hsync_start - mode->crtc_hblank_start) / 8) & 0x1fff) - | ((hsync_wid & 0x3f) << 16) - | ((mode->flags & DRM_MODE_FLAG_NHSYNC) - ? RADEON_CRTC_H_SYNC_POL - : 0)); - - fp_crtc_v_total_disp = (((mode->crtc_vblank_end - mode->crtc_vblank_start) & 0xffff) - | ((mode->crtc_vdisplay - 1) << 16)); - - vsync_wid = mode->crtc_vsync_end - mode->crtc_vsync_start; - if (!vsync_wid) - vsync_wid = 1; - - fp_v_sync_strt_wid = ((((mode->crtc_vsync_start - mode->crtc_vblank_start) & 0xfff) - | ((vsync_wid & 0x1f) << 16) - | ((mode->flags & DRM_MODE_FLAG_NVSYNC) - ? RADEON_CRTC_V_SYNC_POL - : 0))); - - fp_horz_vert_active = (((native_mode->panel_yres) & 0xfff) | - (((native_mode->panel_xres / 8) & 0x1ff) << 16)); - } - } else { - fp_horz_stretch |= ((xres/8-1) << 16); - fp_vert_stretch |= ((yres-1) << 12); - } - - WREG32(RADEON_FP_HORZ_STRETCH, fp_horz_stretch); - WREG32(RADEON_FP_VERT_STRETCH, fp_vert_stretch); - WREG32(RADEON_CRTC_MORE_CNTL, crtc_more_cntl); - WREG32(RADEON_FP_HORZ_VERT_ACTIVE, fp_horz_vert_active); - WREG32(RADEON_FP_H_SYNC_STRT_WID, fp_h_sync_strt_wid); - WREG32(RADEON_FP_V_SYNC_STRT_WID, fp_v_sync_strt_wid); - WREG32(RADEON_FP_CRTC_H_TOTAL_DISP, fp_crtc_h_total_disp); - WREG32(RADEON_FP_CRTC_V_TOTAL_DISP, fp_crtc_v_total_disp); - -} - static void radeon_legacy_lvds_dpms(struct drm_encoder *encoder, int mode) { struct drm_device *dev = encoder->dev; @@ -287,9 +123,6 @@ static void radeon_legacy_lvds_mode_set(struct drm_encoder *encoder, DRM_DEBUG("\n"); - if (radeon_crtc->crtc_id == 0) - radeon_legacy_rmx_mode_set(encoder, mode, adjusted_mode); - lvds_pll_cntl = RREG32(RADEON_LVDS_PLL_CNTL); lvds_pll_cntl &= ~RADEON_LVDS_PLL_EN; @@ -318,7 +151,7 @@ static void radeon_legacy_lvds_mode_set(struct drm_encoder *encoder, if (radeon_crtc->crtc_id == 0) { if (ASIC_IS_R300(rdev)) { - if (radeon_encoder->flags & RADEON_USE_RMX) + if (radeon_encoder->rmx_type != RMX_OFF) lvds_pll_cntl |= R300_LVDS_SRC_SEL_RMX; } else lvds_gen_cntl &= ~RADEON_LVDS_SEL_CRTC2; @@ -350,8 +183,6 @@ static bool radeon_legacy_lvds_mode_fixup(struct drm_encoder *encoder, drm_mode_set_crtcinfo(adjusted_mode, 0); - radeon_encoder->flags &= ~RADEON_USE_RMX; - if (radeon_encoder->rmx_type != RMX_OFF) radeon_rmx_mode_fixup(encoder, mode, adjusted_mode); @@ -455,9 +286,6 @@ static void radeon_legacy_primary_dac_mode_set(struct drm_encoder *encoder, DRM_DEBUG("\n"); - if (radeon_crtc->crtc_id == 0) - radeon_legacy_rmx_mode_set(encoder, mode, adjusted_mode); - if (radeon_crtc->crtc_id == 0) { if (rdev->family == CHIP_R200 || ASIC_IS_R300(rdev)) { disp_output_cntl = RREG32(RADEON_DISP_OUTPUT_CNTL) & @@ -653,9 +481,6 @@ static void radeon_legacy_tmds_int_mode_set(struct drm_encoder *encoder, DRM_DEBUG("\n"); - if (radeon_crtc->crtc_id == 0) - radeon_legacy_rmx_mode_set(encoder, mode, adjusted_mode); - tmp = tmds_pll_cntl = RREG32(RADEON_TMDS_PLL_CNTL); tmp &= 0xfffff; if (rdev->family == CHIP_RV280) { @@ -711,7 +536,7 @@ static void radeon_legacy_tmds_int_mode_set(struct drm_encoder *encoder, if (radeon_crtc->crtc_id == 0) { if (ASIC_IS_R300(rdev) || rdev->family == CHIP_R200) { fp_gen_cntl &= ~R200_FP_SOURCE_SEL_MASK; - if (radeon_encoder->flags & RADEON_USE_RMX) + if (radeon_encoder->rmx_type != RMX_OFF) fp_gen_cntl |= R200_FP_SOURCE_SEL_RMX; else fp_gen_cntl |= R200_FP_SOURCE_SEL_CRTC1; @@ -820,9 +645,6 @@ static void radeon_legacy_tmds_ext_mode_set(struct drm_encoder *encoder, DRM_DEBUG("\n"); - if (radeon_crtc->crtc_id == 0) - radeon_legacy_rmx_mode_set(encoder, mode, adjusted_mode); - if (rdev->is_atom_bios) { radeon_encoder->pixel_clock = adjusted_mode->clock; atombios_external_tmds_setup(encoder, ATOM_ENABLE); @@ -856,7 +678,7 @@ static void radeon_legacy_tmds_ext_mode_set(struct drm_encoder *encoder, if (radeon_crtc->crtc_id == 0) { if ((rdev->family == CHIP_R200) || ASIC_IS_R300(rdev)) { fp2_gen_cntl &= ~R200_FP2_SOURCE_SEL_MASK; - if (radeon_encoder->flags & RADEON_USE_RMX) + if (radeon_encoder->rmx_type != RMX_OFF) fp2_gen_cntl |= R200_FP2_SOURCE_SEL_RMX; else fp2_gen_cntl |= R200_FP2_SOURCE_SEL_CRTC1; @@ -1014,9 +836,6 @@ static void radeon_legacy_tv_dac_mode_set(struct drm_encoder *encoder, DRM_DEBUG("\n"); - if (radeon_crtc->crtc_id == 0) - radeon_legacy_rmx_mode_set(encoder, mode, adjusted_mode); - if (rdev->family != CHIP_R200) { tv_dac_cntl = RREG32(RADEON_TV_DAC_CNTL); if (rdev->family == CHIP_R420 || @@ -1243,6 +1062,7 @@ radeon_add_legacy_encoder(struct drm_device *dev, uint32_t encoder_id, uint32_t radeon_encoder->encoder_id = encoder_id; radeon_encoder->devices = supported_device; + radeon_encoder->rmx_type = RMX_OFF; switch (radeon_encoder->encoder_id) { case ENCODER_OBJECT_ID_INTERNAL_LVDS: diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h index 9173b687462..3b09a1f2d8f 100644 --- a/drivers/gpu/drm/radeon/radeon_mode.h +++ b/drivers/gpu/drm/radeon/radeon_mode.h @@ -36,6 +36,9 @@ #include <linux/i2c.h> #include <linux/i2c-id.h> #include <linux/i2c-algo-bit.h> +#include "radeon_fixed.h" + +struct radeon_device; #define to_radeon_crtc(x) container_of(x, struct radeon_crtc, base) #define to_radeon_connector(x) container_of(x, struct radeon_connector, base) @@ -124,6 +127,7 @@ struct radeon_tmds_pll { #define RADEON_PLL_PREFER_LOW_POST_DIV (1 << 8) #define RADEON_PLL_PREFER_HIGH_POST_DIV (1 << 9) #define RADEON_PLL_USE_FRAC_FB_DIV (1 << 10) +#define RADEON_PLL_PREFER_CLOSEST_LOWER (1 << 11) struct radeon_pll { uint16_t reference_freq; @@ -170,6 +174,18 @@ struct radeon_mode_info { struct atom_context *atom_context; enum radeon_connector_table connector_table; bool mode_config_initialized; + struct radeon_crtc *crtcs[2]; +}; + +struct radeon_native_mode { + /* preferred mode */ + uint32_t panel_xres, panel_yres; + uint32_t hoverplus, hsync_width; + uint32_t hblank; + uint32_t voverplus, vsync_width; + uint32_t vblank; + uint32_t dotclock; + uint32_t flags; }; struct radeon_crtc { @@ -185,19 +201,13 @@ struct radeon_crtc { uint64_t cursor_addr; int cursor_width; int cursor_height; -}; - -#define RADEON_USE_RMX 1 - -struct radeon_native_mode { - /* preferred mode */ - uint32_t panel_xres, panel_yres; - uint32_t hoverplus, hsync_width; - uint32_t hblank; - uint32_t voverplus, vsync_width; - uint32_t vblank; - uint32_t dotclock; - uint32_t flags; + uint32_t legacy_display_base_addr; + uint32_t legacy_cursor_offset; + enum radeon_rmx_type rmx_type; + uint32_t devices; + fixed20_12 vsc; + fixed20_12 hsc; + struct radeon_native_mode native_mode; }; struct radeon_encoder_primary_dac { @@ -383,16 +393,9 @@ void radeon_enc_destroy(struct drm_encoder *encoder); void radeon_copy_fb(struct drm_device *dev, struct drm_gem_object *dst_obj); void radeon_combios_asic_init(struct drm_device *dev); extern int radeon_static_clocks_init(struct drm_device *dev); -void radeon_init_disp_bw_legacy(struct drm_device *dev, - struct drm_display_mode *mode1, - uint32_t pixel_bytes1, - struct drm_display_mode *mode2, - uint32_t pixel_bytes2); -void radeon_init_disp_bw_avivo(struct drm_device *dev, - struct drm_display_mode *mode1, - uint32_t pixel_bytes1, - struct drm_display_mode *mode2, - uint32_t pixel_bytes2); -void radeon_init_disp_bandwidth(struct drm_device *dev); +bool radeon_crtc_scaling_mode_fixup(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); +void atom_rv515_force_tv_scaler(struct radeon_device *rdev); #endif diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c index bac0d06c52a..dd9ac2fed6d 100644 --- a/drivers/gpu/drm/radeon/radeon_object.c +++ b/drivers/gpu/drm/radeon/radeon_object.c @@ -44,6 +44,9 @@ struct radeon_object { uint64_t gpu_addr; void *kptr; bool is_iomem; + uint32_t tiling_flags; + uint32_t pitch; + int surface_reg; }; int radeon_ttm_init(struct radeon_device *rdev); @@ -70,6 +73,7 @@ static void radeon_ttm_object_object_destroy(struct ttm_buffer_object *tobj) robj = container_of(tobj, struct radeon_object, tobj); list_del_init(&robj->list); + radeon_object_clear_surface_reg(robj); kfree(robj); } @@ -99,16 +103,16 @@ static inline uint32_t radeon_object_flags_from_domain(uint32_t domain) { uint32_t flags = 0; if (domain & RADEON_GEM_DOMAIN_VRAM) { - flags |= TTM_PL_FLAG_VRAM; + flags |= TTM_PL_FLAG_VRAM | TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED; } if (domain & RADEON_GEM_DOMAIN_GTT) { - flags |= TTM_PL_FLAG_TT; + flags |= TTM_PL_FLAG_TT | TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED; } if (domain & RADEON_GEM_DOMAIN_CPU) { - flags |= TTM_PL_FLAG_SYSTEM; + flags |= TTM_PL_FLAG_SYSTEM | TTM_PL_MASK_CACHING; } if (!flags) { - flags |= TTM_PL_FLAG_SYSTEM; + flags |= TTM_PL_FLAG_SYSTEM | TTM_PL_MASK_CACHING; } return flags; } @@ -141,6 +145,7 @@ int radeon_object_create(struct radeon_device *rdev, } robj->rdev = rdev; robj->gobj = gobj; + robj->surface_reg = -1; INIT_LIST_HEAD(&robj->list); flags = radeon_object_flags_from_domain(domain); @@ -304,7 +309,7 @@ int radeon_object_wait(struct radeon_object *robj) } spin_lock(&robj->tobj.lock); if (robj->tobj.sync_obj) { - r = ttm_bo_wait(&robj->tobj, true, false, false); + r = ttm_bo_wait(&robj->tobj, true, true, false); } spin_unlock(&robj->tobj.lock); radeon_object_unreserve(robj); @@ -403,7 +408,6 @@ int radeon_object_list_validate(struct list_head *head, void *fence) struct radeon_object *robj; struct radeon_fence *old_fence = NULL; struct list_head *i; - uint32_t flags; int r; r = radeon_object_list_reserve(head); @@ -414,27 +418,25 @@ int radeon_object_list_validate(struct list_head *head, void *fence) list_for_each(i, head) { lobj = list_entry(i, struct radeon_object_list, list); robj = lobj->robj; - if (lobj->wdomain) { - flags = radeon_object_flags_from_domain(lobj->wdomain); - flags |= TTM_PL_FLAG_TT; - } else { - flags = radeon_object_flags_from_domain(lobj->rdomain); - flags |= TTM_PL_FLAG_TT; - flags |= TTM_PL_FLAG_VRAM; - } if (!robj->pin_count) { - robj->tobj.proposed_placement = flags | TTM_PL_MASK_CACHING; + if (lobj->wdomain) { + robj->tobj.proposed_placement = + radeon_object_flags_from_domain(lobj->wdomain); + } else { + robj->tobj.proposed_placement = + radeon_object_flags_from_domain(lobj->rdomain); + } r = ttm_buffer_object_validate(&robj->tobj, robj->tobj.proposed_placement, true, false); if (unlikely(r)) { - radeon_object_list_unreserve(head); DRM_ERROR("radeon: failed to validate.\n"); return r; } radeon_object_gpu_addr(robj); } lobj->gpu_offset = robj->gpu_addr; + lobj->tiling_flags = robj->tiling_flags; if (fence) { old_fence = (struct radeon_fence *)robj->tobj.sync_obj; robj->tobj.sync_obj = radeon_fence_ref(fence); @@ -479,3 +481,127 @@ unsigned long radeon_object_size(struct radeon_object *robj) { return robj->tobj.num_pages << PAGE_SHIFT; } + +int radeon_object_get_surface_reg(struct radeon_object *robj) +{ + struct radeon_device *rdev = robj->rdev; + struct radeon_surface_reg *reg; + struct radeon_object *old_object; + int steal; + int i; + + if (!robj->tiling_flags) + return 0; + + if (robj->surface_reg >= 0) { + reg = &rdev->surface_regs[robj->surface_reg]; + i = robj->surface_reg; + goto out; + } + + steal = -1; + for (i = 0; i < RADEON_GEM_MAX_SURFACES; i++) { + + reg = &rdev->surface_regs[i]; + if (!reg->robj) + break; + + old_object = reg->robj; + if (old_object->pin_count == 0) + steal = i; + } + + /* if we are all out */ + if (i == RADEON_GEM_MAX_SURFACES) { + if (steal == -1) + return -ENOMEM; + /* find someone with a surface reg and nuke their BO */ + reg = &rdev->surface_regs[steal]; + old_object = reg->robj; + /* blow away the mapping */ + DRM_DEBUG("stealing surface reg %d from %p\n", steal, old_object); + ttm_bo_unmap_virtual(&old_object->tobj); + old_object->surface_reg = -1; + i = steal; + } + + robj->surface_reg = i; + reg->robj = robj; + +out: + radeon_set_surface_reg(rdev, i, robj->tiling_flags, robj->pitch, + robj->tobj.mem.mm_node->start << PAGE_SHIFT, + robj->tobj.num_pages << PAGE_SHIFT); + return 0; +} + +void radeon_object_clear_surface_reg(struct radeon_object *robj) +{ + struct radeon_device *rdev = robj->rdev; + struct radeon_surface_reg *reg; + + if (robj->surface_reg == -1) + return; + + reg = &rdev->surface_regs[robj->surface_reg]; + radeon_clear_surface_reg(rdev, robj->surface_reg); + + reg->robj = NULL; + robj->surface_reg = -1; +} + +void radeon_object_set_tiling_flags(struct radeon_object *robj, + uint32_t tiling_flags, uint32_t pitch) +{ + robj->tiling_flags = tiling_flags; + robj->pitch = pitch; +} + +void radeon_object_get_tiling_flags(struct radeon_object *robj, + uint32_t *tiling_flags, + uint32_t *pitch) +{ + if (tiling_flags) + *tiling_flags = robj->tiling_flags; + if (pitch) + *pitch = robj->pitch; +} + +int radeon_object_check_tiling(struct radeon_object *robj, bool has_moved, + bool force_drop) +{ + if (!(robj->tiling_flags & RADEON_TILING_SURFACE)) + return 0; + + if (force_drop) { + radeon_object_clear_surface_reg(robj); + return 0; + } + + if (robj->tobj.mem.mem_type != TTM_PL_VRAM) { + if (!has_moved) + return 0; + + if (robj->surface_reg >= 0) + radeon_object_clear_surface_reg(robj); + return 0; + } + + if ((robj->surface_reg >= 0) && !has_moved) + return 0; + + return radeon_object_get_surface_reg(robj); +} + +void radeon_bo_move_notify(struct ttm_buffer_object *bo, + struct ttm_mem_reg *mem) +{ + struct radeon_object *robj = container_of(bo, struct radeon_object, tobj); + radeon_object_check_tiling(robj, 0, 1); +} + +void radeon_bo_fault_reserve_notify(struct ttm_buffer_object *bo) +{ + struct radeon_object *robj = container_of(bo, struct radeon_object, tobj); + radeon_object_check_tiling(robj, 0, 0); +} diff --git a/drivers/gpu/drm/radeon/radeon_ring.c b/drivers/gpu/drm/radeon/radeon_ring.c index a853261d188..60d159308b8 100644 --- a/drivers/gpu/drm/radeon/radeon_ring.c +++ b/drivers/gpu/drm/radeon/radeon_ring.c @@ -126,32 +126,19 @@ static void radeon_ib_align(struct radeon_device *rdev, struct radeon_ib *ib) } } -static void radeon_ib_cpu_flush(struct radeon_device *rdev, - struct radeon_ib *ib) -{ - unsigned long tmp; - unsigned i; - - /* To force CPU cache flush ugly but seems reliable */ - for (i = 0; i < ib->length_dw; i += (rdev->cp.align_mask + 1)) { - tmp = readl(&ib->ptr[i]); - } -} - int radeon_ib_schedule(struct radeon_device *rdev, struct radeon_ib *ib) { int r = 0; mutex_lock(&rdev->ib_pool.mutex); radeon_ib_align(rdev, ib); - radeon_ib_cpu_flush(rdev, ib); if (!ib->length_dw || !rdev->cp.ready) { /* TODO: Nothings in the ib we should report. */ mutex_unlock(&rdev->ib_pool.mutex); DRM_ERROR("radeon: couldn't schedule IB(%lu).\n", ib->idx); return -EINVAL; } - /* 64 dwords should be enought for fence too */ + /* 64 dwords should be enough for fence too */ r = radeon_ring_lock(rdev, 64); if (r) { DRM_ERROR("radeon: scheduling IB failled (%d).\n", r); diff --git a/drivers/gpu/drm/radeon/radeon_share.h b/drivers/gpu/drm/radeon/radeon_share.h new file mode 100644 index 00000000000..63a773578f1 --- /dev/null +++ b/drivers/gpu/drm/radeon/radeon_share.h @@ -0,0 +1,39 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ +#ifndef __RADEON_SHARE_H__ +#define __RADEON_SHARE_H__ + +void r100_vram_init_sizes(struct radeon_device *rdev); + +void rs690_line_buffer_adjust(struct radeon_device *rdev, + struct drm_display_mode *mode1, + struct drm_display_mode *mode2); + +void rv515_bandwidth_avivo_update(struct radeon_device *rdev); + +#endif diff --git a/drivers/gpu/drm/radeon/radeon_test.c b/drivers/gpu/drm/radeon/radeon_test.c new file mode 100644 index 00000000000..03c33cf4e14 --- /dev/null +++ b/drivers/gpu/drm/radeon/radeon_test.c @@ -0,0 +1,209 @@ +/* + * Copyright 2009 VMware, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Michel Dänzer + */ +#include <drm/drmP.h> +#include <drm/radeon_drm.h> +#include "radeon_reg.h" +#include "radeon.h" + + +/* Test BO GTT->VRAM and VRAM->GTT GPU copies across the whole GTT aperture */ +void radeon_test_moves(struct radeon_device *rdev) +{ + struct radeon_object *vram_obj = NULL; + struct radeon_object **gtt_obj = NULL; + struct radeon_fence *fence = NULL; + uint64_t gtt_addr, vram_addr; + unsigned i, n, size; + int r; + + size = 1024 * 1024; + + /* Number of tests = + * (Total GTT - IB pool - writeback page - ring buffer) / test size + */ + n = (rdev->mc.gtt_size - RADEON_IB_POOL_SIZE*64*1024 - 4096 - + rdev->cp.ring_size) / size; + + gtt_obj = kzalloc(n * sizeof(*gtt_obj), GFP_KERNEL); + if (!gtt_obj) { + DRM_ERROR("Failed to allocate %d pointers\n", n); + r = 1; + goto out_cleanup; + } + + r = radeon_object_create(rdev, NULL, size, true, RADEON_GEM_DOMAIN_VRAM, + false, &vram_obj); + if (r) { + DRM_ERROR("Failed to create VRAM object\n"); + goto out_cleanup; + } + + r = radeon_object_pin(vram_obj, RADEON_GEM_DOMAIN_VRAM, &vram_addr); + if (r) { + DRM_ERROR("Failed to pin VRAM object\n"); + goto out_cleanup; + } + + for (i = 0; i < n; i++) { + void *gtt_map, *vram_map; + void **gtt_start, **gtt_end; + void **vram_start, **vram_end; + + r = radeon_object_create(rdev, NULL, size, true, + RADEON_GEM_DOMAIN_GTT, false, gtt_obj + i); + if (r) { + DRM_ERROR("Failed to create GTT object %d\n", i); + goto out_cleanup; + } + + r = radeon_object_pin(gtt_obj[i], RADEON_GEM_DOMAIN_GTT, >t_addr); + if (r) { + DRM_ERROR("Failed to pin GTT object %d\n", i); + goto out_cleanup; + } + + r = radeon_object_kmap(gtt_obj[i], >t_map); + if (r) { + DRM_ERROR("Failed to map GTT object %d\n", i); + goto out_cleanup; + } + + for (gtt_start = gtt_map, gtt_end = gtt_map + size; + gtt_start < gtt_end; + gtt_start++) + *gtt_start = gtt_start; + + radeon_object_kunmap(gtt_obj[i]); + + r = radeon_fence_create(rdev, &fence); + if (r) { + DRM_ERROR("Failed to create GTT->VRAM fence %d\n", i); + goto out_cleanup; + } + + r = radeon_copy(rdev, gtt_addr, vram_addr, size / 4096, fence); + if (r) { + DRM_ERROR("Failed GTT->VRAM copy %d\n", i); + goto out_cleanup; + } + + r = radeon_fence_wait(fence, false); + if (r) { + DRM_ERROR("Failed to wait for GTT->VRAM fence %d\n", i); + goto out_cleanup; + } + + radeon_fence_unref(&fence); + + r = radeon_object_kmap(vram_obj, &vram_map); + if (r) { + DRM_ERROR("Failed to map VRAM object after copy %d\n", i); + goto out_cleanup; + } + + for (gtt_start = gtt_map, gtt_end = gtt_map + size, + vram_start = vram_map, vram_end = vram_map + size; + vram_start < vram_end; + gtt_start++, vram_start++) { + if (*vram_start != gtt_start) { + DRM_ERROR("Incorrect GTT->VRAM copy %d: Got 0x%p, " + "expected 0x%p (GTT map 0x%p-0x%p)\n", + i, *vram_start, gtt_start, gtt_map, + gtt_end); + radeon_object_kunmap(vram_obj); + goto out_cleanup; + } + *vram_start = vram_start; + } + + radeon_object_kunmap(vram_obj); + + r = radeon_fence_create(rdev, &fence); + if (r) { + DRM_ERROR("Failed to create VRAM->GTT fence %d\n", i); + goto out_cleanup; + } + + r = radeon_copy(rdev, vram_addr, gtt_addr, size / 4096, fence); + if (r) { + DRM_ERROR("Failed VRAM->GTT copy %d\n", i); + goto out_cleanup; + } + + r = radeon_fence_wait(fence, false); + if (r) { + DRM_ERROR("Failed to wait for VRAM->GTT fence %d\n", i); + goto out_cleanup; + } + + radeon_fence_unref(&fence); + + r = radeon_object_kmap(gtt_obj[i], >t_map); + if (r) { + DRM_ERROR("Failed to map GTT object after copy %d\n", i); + goto out_cleanup; + } + + for (gtt_start = gtt_map, gtt_end = gtt_map + size, + vram_start = vram_map, vram_end = vram_map + size; + gtt_start < gtt_end; + gtt_start++, vram_start++) { + if (*gtt_start != vram_start) { + DRM_ERROR("Incorrect VRAM->GTT copy %d: Got 0x%p, " + "expected 0x%p (VRAM map 0x%p-0x%p)\n", + i, *gtt_start, vram_start, vram_map, + vram_end); + radeon_object_kunmap(gtt_obj[i]); + goto out_cleanup; + } + } + + radeon_object_kunmap(gtt_obj[i]); + + DRM_INFO("Tested GTT->VRAM and VRAM->GTT copy for GTT offset 0x%llx\n", + gtt_addr - rdev->mc.gtt_location); + } + +out_cleanup: + if (vram_obj) { + radeon_object_unpin(vram_obj); + radeon_object_unref(&vram_obj); + } + if (gtt_obj) { + for (i = 0; i < n; i++) { + if (gtt_obj[i]) { + radeon_object_unpin(gtt_obj[i]); + radeon_object_unref(>t_obj[i]); + } + } + kfree(gtt_obj); + } + if (fence) { + radeon_fence_unref(&fence); + } + if (r) { + printk(KERN_WARNING "Error while testing BO move.\n"); + } +} + diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c index 1227a97f516..15c3531377e 100644 --- a/drivers/gpu/drm/radeon/radeon_ttm.c +++ b/drivers/gpu/drm/radeon/radeon_ttm.c @@ -355,23 +355,26 @@ static int radeon_bo_move(struct ttm_buffer_object *bo, if (!rdev->cp.ready) { /* use memcpy */ DRM_ERROR("CP is not ready use memcpy.\n"); - return ttm_bo_move_memcpy(bo, evict, no_wait, new_mem); + goto memcpy; } if (old_mem->mem_type == TTM_PL_VRAM && new_mem->mem_type == TTM_PL_SYSTEM) { - return radeon_move_vram_ram(bo, evict, interruptible, + r = radeon_move_vram_ram(bo, evict, interruptible, no_wait, new_mem); } else if (old_mem->mem_type == TTM_PL_SYSTEM && new_mem->mem_type == TTM_PL_VRAM) { - return radeon_move_ram_vram(bo, evict, interruptible, + r = radeon_move_ram_vram(bo, evict, interruptible, no_wait, new_mem); } else { r = radeon_move_blit(bo, evict, no_wait, new_mem, old_mem); - if (unlikely(r)) { - return r; - } } + + if (r) { +memcpy: + r = ttm_bo_move_memcpy(bo, evict, no_wait, new_mem); + } + return r; } @@ -429,6 +432,8 @@ static struct ttm_bo_driver radeon_bo_driver = { .sync_obj_flush = &radeon_sync_obj_flush, .sync_obj_unref = &radeon_sync_obj_unref, .sync_obj_ref = &radeon_sync_obj_ref, + .move_notify = &radeon_bo_move_notify, + .fault_reserve_notify = &radeon_bo_fault_reserve_notify, }; int radeon_ttm_init(struct radeon_device *rdev) @@ -442,13 +447,14 @@ int radeon_ttm_init(struct radeon_device *rdev) /* No others user of address space so set it to 0 */ r = ttm_bo_device_init(&rdev->mman.bdev, rdev->mman.mem_global_ref.object, - &radeon_bo_driver, DRM_FILE_PAGE_OFFSET); + &radeon_bo_driver, DRM_FILE_PAGE_OFFSET, + rdev->need_dma32); if (r) { DRM_ERROR("failed initializing buffer object driver(%d).\n", r); return r; } r = ttm_bo_init_mm(&rdev->mman.bdev, TTM_PL_VRAM, 0, - ((rdev->mc.aper_size) >> PAGE_SHIFT)); + ((rdev->mc.real_vram_size) >> PAGE_SHIFT)); if (r) { DRM_ERROR("Failed initializing VRAM heap.\n"); return r; @@ -465,7 +471,7 @@ int radeon_ttm_init(struct radeon_device *rdev) return r; } DRM_INFO("radeon: %uM of VRAM memory ready\n", - rdev->mc.vram_size / (1024 * 1024)); + rdev->mc.real_vram_size / (1024 * 1024)); r = ttm_bo_init_mm(&rdev->mman.bdev, TTM_PL_TT, 0, ((rdev->mc.gtt_size) >> PAGE_SHIFT)); if (r) { diff --git a/drivers/gpu/drm/radeon/rs400.c b/drivers/gpu/drm/radeon/rs400.c index cc074b5a8f7..b29affd9c5d 100644 --- a/drivers/gpu/drm/radeon/rs400.c +++ b/drivers/gpu/drm/radeon/rs400.c @@ -29,6 +29,7 @@ #include <drm/drmP.h> #include "radeon_reg.h" #include "radeon.h" +#include "radeon_share.h" /* rs400,rs480 depends on : */ void r100_hdp_reset(struct radeon_device *rdev); @@ -164,7 +165,9 @@ int rs400_gart_enable(struct radeon_device *rdev) WREG32(RADEON_BUS_CNTL, tmp); } /* Table should be in 32bits address space so ignore bits above. */ - tmp = rdev->gart.table_addr & 0xfffff000; + tmp = (u32)rdev->gart.table_addr & 0xfffff000; + tmp |= (upper_32_bits(rdev->gart.table_addr) & 0xff) << 4; + WREG32_MC(RS480_GART_BASE, tmp); /* TODO: more tweaking here */ WREG32_MC(RS480_GART_FEATURE_ID, @@ -201,10 +204,17 @@ void rs400_gart_disable(struct radeon_device *rdev) int rs400_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr) { + uint32_t entry; + if (i < 0 || i > rdev->gart.num_gpu_pages) { return -EINVAL; } - rdev->gart.table.ram.ptr[i] = cpu_to_le32(((uint32_t)addr) | 0xC); + + entry = (lower_32_bits(addr) & PAGE_MASK) | + ((upper_32_bits(addr) & 0xff) << 4) | + 0xc; + entry = cpu_to_le32(entry); + rdev->gart.table.ram.ptr[i] = entry; return 0; } @@ -223,10 +233,9 @@ int rs400_mc_init(struct radeon_device *rdev) rs400_gpu_init(rdev); rs400_gart_disable(rdev); - rdev->mc.gtt_location = rdev->mc.vram_size; + rdev->mc.gtt_location = rdev->mc.mc_vram_size; rdev->mc.gtt_location += (rdev->mc.gtt_size - 1); rdev->mc.gtt_location &= ~(rdev->mc.gtt_size - 1); - rdev->mc.vram_location = 0xFFFFFFFFUL; r = radeon_mc_setup(rdev); if (r) { return r; @@ -238,7 +247,7 @@ int rs400_mc_init(struct radeon_device *rdev) "programming pipes. Bad things might happen.\n"); } - tmp = rdev->mc.vram_location + rdev->mc.vram_size - 1; + tmp = rdev->mc.vram_location + rdev->mc.mc_vram_size - 1; tmp = REG_SET(RADEON_MC_FB_TOP, tmp >> 16); tmp |= REG_SET(RADEON_MC_FB_START, rdev->mc.vram_location >> 16); WREG32(RADEON_MC_FB_LOCATION, tmp); @@ -284,21 +293,12 @@ void rs400_gpu_init(struct radeon_device *rdev) */ void rs400_vram_info(struct radeon_device *rdev) { - uint32_t tom; - rs400_gart_adjust_size(rdev); /* DDR for all card after R300 & IGP */ rdev->mc.vram_is_ddr = true; rdev->mc.vram_width = 128; - /* read NB_TOM to get the amount of ram stolen for the GPU */ - tom = RREG32(RADEON_NB_TOM); - rdev->mc.vram_size = (((tom >> 16) - (tom & 0xffff) + 1) << 16); - WREG32(RADEON_CONFIG_MEMSIZE, rdev->mc.vram_size); - - /* Could aper size report 0 ? */ - rdev->mc.aper_base = drm_get_resource_start(rdev->ddev, 0); - rdev->mc.aper_size = drm_get_resource_len(rdev->ddev, 0); + r100_vram_init_sizes(rdev); } diff --git a/drivers/gpu/drm/radeon/rs600.c b/drivers/gpu/drm/radeon/rs600.c index ab0c967553e..bbea6dee4a9 100644 --- a/drivers/gpu/drm/radeon/rs600.c +++ b/drivers/gpu/drm/radeon/rs600.c @@ -223,7 +223,7 @@ int rs600_mc_init(struct radeon_device *rdev) printk(KERN_WARNING "Failed to wait MC idle while " "programming pipes. Bad things might happen.\n"); } - tmp = rdev->mc.vram_location + rdev->mc.vram_size - 1; + tmp = rdev->mc.vram_location + rdev->mc.mc_vram_size - 1; tmp = REG_SET(RS600_MC_FB_TOP, tmp >> 16); tmp |= REG_SET(RS600_MC_FB_START, rdev->mc.vram_location >> 16); WREG32_MC(RS600_MC_FB_LOCATION, tmp); @@ -301,6 +301,11 @@ void rs600_vram_info(struct radeon_device *rdev) rdev->mc.vram_width = 128; } +void rs600_bandwidth_update(struct radeon_device *rdev) +{ + /* FIXME: implement, should this be like rs690 ? */ +} + /* * Indirect registers accessor diff --git a/drivers/gpu/drm/radeon/rs690.c b/drivers/gpu/drm/radeon/rs690.c index 79ba85042b5..839595b0072 100644 --- a/drivers/gpu/drm/radeon/rs690.c +++ b/drivers/gpu/drm/radeon/rs690.c @@ -28,6 +28,9 @@ #include "drmP.h" #include "radeon_reg.h" #include "radeon.h" +#include "rs690r.h" +#include "atom.h" +#include "atom-bits.h" /* rs690,rs740 depends on : */ void r100_hdp_reset(struct radeon_device *rdev); @@ -64,7 +67,7 @@ int rs690_mc_init(struct radeon_device *rdev) rs400_gart_disable(rdev); /* Setup GPU memory space */ - rdev->mc.gtt_location = rdev->mc.vram_size; + rdev->mc.gtt_location = rdev->mc.mc_vram_size; rdev->mc.gtt_location += (rdev->mc.gtt_size - 1); rdev->mc.gtt_location &= ~(rdev->mc.gtt_size - 1); rdev->mc.vram_location = 0xFFFFFFFFUL; @@ -79,7 +82,7 @@ int rs690_mc_init(struct radeon_device *rdev) printk(KERN_WARNING "Failed to wait MC idle while " "programming pipes. Bad things might happen.\n"); } - tmp = rdev->mc.vram_location + rdev->mc.vram_size - 1; + tmp = rdev->mc.vram_location + rdev->mc.mc_vram_size - 1; tmp = REG_SET(RS690_MC_FB_TOP, tmp >> 16); tmp |= REG_SET(RS690_MC_FB_START, rdev->mc.vram_location >> 16); WREG32_MC(RS690_MCCFG_FB_LOCATION, tmp); @@ -138,9 +141,82 @@ void rs690_gpu_init(struct radeon_device *rdev) /* * VRAM info. */ +void rs690_pm_info(struct radeon_device *rdev) +{ + int index = GetIndexIntoMasterTable(DATA, IntegratedSystemInfo); + struct _ATOM_INTEGRATED_SYSTEM_INFO *info; + struct _ATOM_INTEGRATED_SYSTEM_INFO_V2 *info_v2; + void *ptr; + uint16_t data_offset; + uint8_t frev, crev; + fixed20_12 tmp; + + atom_parse_data_header(rdev->mode_info.atom_context, index, NULL, + &frev, &crev, &data_offset); + ptr = rdev->mode_info.atom_context->bios + data_offset; + info = (struct _ATOM_INTEGRATED_SYSTEM_INFO *)ptr; + info_v2 = (struct _ATOM_INTEGRATED_SYSTEM_INFO_V2 *)ptr; + /* Get various system informations from bios */ + switch (crev) { + case 1: + tmp.full = rfixed_const(100); + rdev->pm.igp_sideport_mclk.full = rfixed_const(info->ulBootUpMemoryClock); + rdev->pm.igp_sideport_mclk.full = rfixed_div(rdev->pm.igp_sideport_mclk, tmp); + rdev->pm.igp_system_mclk.full = rfixed_const(le16_to_cpu(info->usK8MemoryClock)); + rdev->pm.igp_ht_link_clk.full = rfixed_const(le16_to_cpu(info->usFSBClock)); + rdev->pm.igp_ht_link_width.full = rfixed_const(info->ucHTLinkWidth); + break; + case 2: + tmp.full = rfixed_const(100); + rdev->pm.igp_sideport_mclk.full = rfixed_const(info_v2->ulBootUpSidePortClock); + rdev->pm.igp_sideport_mclk.full = rfixed_div(rdev->pm.igp_sideport_mclk, tmp); + rdev->pm.igp_system_mclk.full = rfixed_const(info_v2->ulBootUpUMAClock); + rdev->pm.igp_system_mclk.full = rfixed_div(rdev->pm.igp_system_mclk, tmp); + rdev->pm.igp_ht_link_clk.full = rfixed_const(info_v2->ulHTLinkFreq); + rdev->pm.igp_ht_link_clk.full = rfixed_div(rdev->pm.igp_ht_link_clk, tmp); + rdev->pm.igp_ht_link_width.full = rfixed_const(le16_to_cpu(info_v2->usMinHTLinkWidth)); + break; + default: + tmp.full = rfixed_const(100); + /* We assume the slower possible clock ie worst case */ + /* DDR 333Mhz */ + rdev->pm.igp_sideport_mclk.full = rfixed_const(333); + /* FIXME: system clock ? */ + rdev->pm.igp_system_mclk.full = rfixed_const(100); + rdev->pm.igp_system_mclk.full = rfixed_div(rdev->pm.igp_system_mclk, tmp); + rdev->pm.igp_ht_link_clk.full = rfixed_const(200); + rdev->pm.igp_ht_link_width.full = rfixed_const(8); + DRM_ERROR("No integrated system info for your GPU, using safe default\n"); + break; + } + /* Compute various bandwidth */ + /* k8_bandwidth = (memory_clk / 2) * 2 * 8 * 0.5 = memory_clk * 4 */ + tmp.full = rfixed_const(4); + rdev->pm.k8_bandwidth.full = rfixed_mul(rdev->pm.igp_system_mclk, tmp); + /* ht_bandwidth = ht_clk * 2 * ht_width / 8 * 0.8 + * = ht_clk * ht_width / 5 + */ + tmp.full = rfixed_const(5); + rdev->pm.ht_bandwidth.full = rfixed_mul(rdev->pm.igp_ht_link_clk, + rdev->pm.igp_ht_link_width); + rdev->pm.ht_bandwidth.full = rfixed_div(rdev->pm.ht_bandwidth, tmp); + if (tmp.full < rdev->pm.max_bandwidth.full) { + /* HT link is a limiting factor */ + rdev->pm.max_bandwidth.full = tmp.full; + } + /* sideport_bandwidth = (sideport_clk / 2) * 2 * 2 * 0.7 + * = (sideport_clk * 14) / 10 + */ + tmp.full = rfixed_const(14); + rdev->pm.sideport_bandwidth.full = rfixed_mul(rdev->pm.igp_sideport_mclk, tmp); + tmp.full = rfixed_const(10); + rdev->pm.sideport_bandwidth.full = rfixed_div(rdev->pm.sideport_bandwidth, tmp); +} + void rs690_vram_info(struct radeon_device *rdev) { uint32_t tmp; + fixed20_12 a; rs400_gart_adjust_size(rdev); /* DDR for all card after R300 & IGP */ @@ -152,12 +228,409 @@ void rs690_vram_info(struct radeon_device *rdev) } else { rdev->mc.vram_width = 64; } - rdev->mc.vram_size = RREG32(RADEON_CONFIG_MEMSIZE); + rdev->mc.real_vram_size = RREG32(RADEON_CONFIG_MEMSIZE); + rdev->mc.mc_vram_size = rdev->mc.real_vram_size; rdev->mc.aper_base = drm_get_resource_start(rdev->ddev, 0); rdev->mc.aper_size = drm_get_resource_len(rdev->ddev, 0); + rs690_pm_info(rdev); + /* FIXME: we should enforce default clock in case GPU is not in + * default setup + */ + a.full = rfixed_const(100); + rdev->pm.sclk.full = rfixed_const(rdev->clock.default_sclk); + rdev->pm.sclk.full = rfixed_div(rdev->pm.sclk, a); + a.full = rfixed_const(16); + /* core_bandwidth = sclk(Mhz) * 16 */ + rdev->pm.core_bandwidth.full = rfixed_div(rdev->pm.sclk, a); +} + +void rs690_line_buffer_adjust(struct radeon_device *rdev, + struct drm_display_mode *mode1, + struct drm_display_mode *mode2) +{ + u32 tmp; + + /* + * Line Buffer Setup + * There is a single line buffer shared by both display controllers. + * DC_LB_MEMORY_SPLIT controls how that line buffer is shared between + * the display controllers. The paritioning can either be done + * manually or via one of four preset allocations specified in bits 1:0: + * 0 - line buffer is divided in half and shared between crtc + * 1 - D1 gets 3/4 of the line buffer, D2 gets 1/4 + * 2 - D1 gets the whole buffer + * 3 - D1 gets 1/4 of the line buffer, D2 gets 3/4 + * Setting bit 2 of DC_LB_MEMORY_SPLIT controls switches to manual + * allocation mode. In manual allocation mode, D1 always starts at 0, + * D1 end/2 is specified in bits 14:4; D2 allocation follows D1. + */ + tmp = RREG32(DC_LB_MEMORY_SPLIT) & ~DC_LB_MEMORY_SPLIT_MASK; + tmp &= ~DC_LB_MEMORY_SPLIT_SHIFT_MODE; + /* auto */ + if (mode1 && mode2) { + if (mode1->hdisplay > mode2->hdisplay) { + if (mode1->hdisplay > 2560) + tmp |= DC_LB_MEMORY_SPLIT_D1_3Q_D2_1Q; + else + tmp |= DC_LB_MEMORY_SPLIT_D1HALF_D2HALF; + } else if (mode2->hdisplay > mode1->hdisplay) { + if (mode2->hdisplay > 2560) + tmp |= DC_LB_MEMORY_SPLIT_D1_1Q_D2_3Q; + else + tmp |= DC_LB_MEMORY_SPLIT_D1HALF_D2HALF; + } else + tmp |= AVIVO_DC_LB_MEMORY_SPLIT_D1HALF_D2HALF; + } else if (mode1) { + tmp |= DC_LB_MEMORY_SPLIT_D1_ONLY; + } else if (mode2) { + tmp |= DC_LB_MEMORY_SPLIT_D1_1Q_D2_3Q; + } + WREG32(DC_LB_MEMORY_SPLIT, tmp); } +struct rs690_watermark { + u32 lb_request_fifo_depth; + fixed20_12 num_line_pair; + fixed20_12 estimated_width; + fixed20_12 worst_case_latency; + fixed20_12 consumption_rate; + fixed20_12 active_time; + fixed20_12 dbpp; + fixed20_12 priority_mark_max; + fixed20_12 priority_mark; + fixed20_12 sclk; +}; + +void rs690_crtc_bandwidth_compute(struct radeon_device *rdev, + struct radeon_crtc *crtc, + struct rs690_watermark *wm) +{ + struct drm_display_mode *mode = &crtc->base.mode; + fixed20_12 a, b, c; + fixed20_12 pclk, request_fifo_depth, tolerable_latency, estimated_width; + fixed20_12 consumption_time, line_time, chunk_time, read_delay_latency; + /* FIXME: detect IGP with sideport memory, i don't think there is any + * such product available + */ + bool sideport = false; + + if (!crtc->base.enabled) { + /* FIXME: wouldn't it better to set priority mark to maximum */ + wm->lb_request_fifo_depth = 4; + return; + } + + if (crtc->vsc.full > rfixed_const(2)) + wm->num_line_pair.full = rfixed_const(2); + else + wm->num_line_pair.full = rfixed_const(1); + + b.full = rfixed_const(mode->crtc_hdisplay); + c.full = rfixed_const(256); + a.full = rfixed_mul(wm->num_line_pair, b); + request_fifo_depth.full = rfixed_div(a, c); + if (a.full < rfixed_const(4)) { + wm->lb_request_fifo_depth = 4; + } else { + wm->lb_request_fifo_depth = rfixed_trunc(request_fifo_depth); + } + + /* Determine consumption rate + * pclk = pixel clock period(ns) = 1000 / (mode.clock / 1000) + * vtaps = number of vertical taps, + * vsc = vertical scaling ratio, defined as source/destination + * hsc = horizontal scaling ration, defined as source/destination + */ + a.full = rfixed_const(mode->clock); + b.full = rfixed_const(1000); + a.full = rfixed_div(a, b); + pclk.full = rfixed_div(b, a); + if (crtc->rmx_type != RMX_OFF) { + b.full = rfixed_const(2); + if (crtc->vsc.full > b.full) + b.full = crtc->vsc.full; + b.full = rfixed_mul(b, crtc->hsc); + c.full = rfixed_const(2); + b.full = rfixed_div(b, c); + consumption_time.full = rfixed_div(pclk, b); + } else { + consumption_time.full = pclk.full; + } + a.full = rfixed_const(1); + wm->consumption_rate.full = rfixed_div(a, consumption_time); + + + /* Determine line time + * LineTime = total time for one line of displayhtotal + * LineTime = total number of horizontal pixels + * pclk = pixel clock period(ns) + */ + a.full = rfixed_const(crtc->base.mode.crtc_htotal); + line_time.full = rfixed_mul(a, pclk); + + /* Determine active time + * ActiveTime = time of active region of display within one line, + * hactive = total number of horizontal active pixels + * htotal = total number of horizontal pixels + */ + a.full = rfixed_const(crtc->base.mode.crtc_htotal); + b.full = rfixed_const(crtc->base.mode.crtc_hdisplay); + wm->active_time.full = rfixed_mul(line_time, b); + wm->active_time.full = rfixed_div(wm->active_time, a); + + /* Maximun bandwidth is the minimun bandwidth of all component */ + rdev->pm.max_bandwidth = rdev->pm.core_bandwidth; + if (sideport) { + if (rdev->pm.max_bandwidth.full > rdev->pm.sideport_bandwidth.full && + rdev->pm.sideport_bandwidth.full) + rdev->pm.max_bandwidth = rdev->pm.sideport_bandwidth; + read_delay_latency.full = rfixed_const(370 * 800 * 1000); + read_delay_latency.full = rfixed_div(read_delay_latency, + rdev->pm.igp_sideport_mclk); + } else { + if (rdev->pm.max_bandwidth.full > rdev->pm.k8_bandwidth.full && + rdev->pm.k8_bandwidth.full) + rdev->pm.max_bandwidth = rdev->pm.k8_bandwidth; + if (rdev->pm.max_bandwidth.full > rdev->pm.ht_bandwidth.full && + rdev->pm.ht_bandwidth.full) + rdev->pm.max_bandwidth = rdev->pm.ht_bandwidth; + read_delay_latency.full = rfixed_const(5000); + } + + /* sclk = system clocks(ns) = 1000 / max_bandwidth / 16 */ + a.full = rfixed_const(16); + rdev->pm.sclk.full = rfixed_mul(rdev->pm.max_bandwidth, a); + a.full = rfixed_const(1000); + rdev->pm.sclk.full = rfixed_div(a, rdev->pm.sclk); + /* Determine chunk time + * ChunkTime = the time it takes the DCP to send one chunk of data + * to the LB which consists of pipeline delay and inter chunk gap + * sclk = system clock(ns) + */ + a.full = rfixed_const(256 * 13); + chunk_time.full = rfixed_mul(rdev->pm.sclk, a); + a.full = rfixed_const(10); + chunk_time.full = rfixed_div(chunk_time, a); + + /* Determine the worst case latency + * NumLinePair = Number of line pairs to request(1=2 lines, 2=4 lines) + * WorstCaseLatency = worst case time from urgent to when the MC starts + * to return data + * READ_DELAY_IDLE_MAX = constant of 1us + * ChunkTime = time it takes the DCP to send one chunk of data to the LB + * which consists of pipeline delay and inter chunk gap + */ + if (rfixed_trunc(wm->num_line_pair) > 1) { + a.full = rfixed_const(3); + wm->worst_case_latency.full = rfixed_mul(a, chunk_time); + wm->worst_case_latency.full += read_delay_latency.full; + } else { + a.full = rfixed_const(2); + wm->worst_case_latency.full = rfixed_mul(a, chunk_time); + wm->worst_case_latency.full += read_delay_latency.full; + } + + /* Determine the tolerable latency + * TolerableLatency = Any given request has only 1 line time + * for the data to be returned + * LBRequestFifoDepth = Number of chunk requests the LB can + * put into the request FIFO for a display + * LineTime = total time for one line of display + * ChunkTime = the time it takes the DCP to send one chunk + * of data to the LB which consists of + * pipeline delay and inter chunk gap + */ + if ((2+wm->lb_request_fifo_depth) >= rfixed_trunc(request_fifo_depth)) { + tolerable_latency.full = line_time.full; + } else { + tolerable_latency.full = rfixed_const(wm->lb_request_fifo_depth - 2); + tolerable_latency.full = request_fifo_depth.full - tolerable_latency.full; + tolerable_latency.full = rfixed_mul(tolerable_latency, chunk_time); + tolerable_latency.full = line_time.full - tolerable_latency.full; + } + /* We assume worst case 32bits (4 bytes) */ + wm->dbpp.full = rfixed_const(4 * 8); + + /* Determine the maximum priority mark + * width = viewport width in pixels + */ + a.full = rfixed_const(16); + wm->priority_mark_max.full = rfixed_const(crtc->base.mode.crtc_hdisplay); + wm->priority_mark_max.full = rfixed_div(wm->priority_mark_max, a); + + /* Determine estimated width */ + estimated_width.full = tolerable_latency.full - wm->worst_case_latency.full; + estimated_width.full = rfixed_div(estimated_width, consumption_time); + if (rfixed_trunc(estimated_width) > crtc->base.mode.crtc_hdisplay) { + wm->priority_mark.full = rfixed_const(10); + } else { + a.full = rfixed_const(16); + wm->priority_mark.full = rfixed_div(estimated_width, a); + wm->priority_mark.full = wm->priority_mark_max.full - wm->priority_mark.full; + } +} + +void rs690_bandwidth_update(struct radeon_device *rdev) +{ + struct drm_display_mode *mode0 = NULL; + struct drm_display_mode *mode1 = NULL; + struct rs690_watermark wm0; + struct rs690_watermark wm1; + u32 tmp; + fixed20_12 priority_mark02, priority_mark12, fill_rate; + fixed20_12 a, b; + + if (rdev->mode_info.crtcs[0]->base.enabled) + mode0 = &rdev->mode_info.crtcs[0]->base.mode; + if (rdev->mode_info.crtcs[1]->base.enabled) + mode1 = &rdev->mode_info.crtcs[1]->base.mode; + /* + * Set display0/1 priority up in the memory controller for + * modes if the user specifies HIGH for displaypriority + * option. + */ + if (rdev->disp_priority == 2) { + tmp = RREG32_MC(MC_INIT_MISC_LAT_TIMER); + tmp &= ~MC_DISP1R_INIT_LAT_MASK; + tmp &= ~MC_DISP0R_INIT_LAT_MASK; + if (mode1) + tmp |= (1 << MC_DISP1R_INIT_LAT_SHIFT); + if (mode0) + tmp |= (1 << MC_DISP0R_INIT_LAT_SHIFT); + WREG32_MC(MC_INIT_MISC_LAT_TIMER, tmp); + } + rs690_line_buffer_adjust(rdev, mode0, mode1); + + if ((rdev->family == CHIP_RS690) || (rdev->family == CHIP_RS740)) + WREG32(DCP_CONTROL, 0); + if ((rdev->family == CHIP_RS780) || (rdev->family == CHIP_RS880)) + WREG32(DCP_CONTROL, 2); + + rs690_crtc_bandwidth_compute(rdev, rdev->mode_info.crtcs[0], &wm0); + rs690_crtc_bandwidth_compute(rdev, rdev->mode_info.crtcs[1], &wm1); + + tmp = (wm0.lb_request_fifo_depth - 1); + tmp |= (wm1.lb_request_fifo_depth - 1) << 16; + WREG32(LB_MAX_REQ_OUTSTANDING, tmp); + + if (mode0 && mode1) { + if (rfixed_trunc(wm0.dbpp) > 64) + a.full = rfixed_mul(wm0.dbpp, wm0.num_line_pair); + else + a.full = wm0.num_line_pair.full; + if (rfixed_trunc(wm1.dbpp) > 64) + b.full = rfixed_mul(wm1.dbpp, wm1.num_line_pair); + else + b.full = wm1.num_line_pair.full; + a.full += b.full; + fill_rate.full = rfixed_div(wm0.sclk, a); + if (wm0.consumption_rate.full > fill_rate.full) { + b.full = wm0.consumption_rate.full - fill_rate.full; + b.full = rfixed_mul(b, wm0.active_time); + a.full = rfixed_mul(wm0.worst_case_latency, + wm0.consumption_rate); + a.full = a.full + b.full; + b.full = rfixed_const(16 * 1000); + priority_mark02.full = rfixed_div(a, b); + } else { + a.full = rfixed_mul(wm0.worst_case_latency, + wm0.consumption_rate); + b.full = rfixed_const(16 * 1000); + priority_mark02.full = rfixed_div(a, b); + } + if (wm1.consumption_rate.full > fill_rate.full) { + b.full = wm1.consumption_rate.full - fill_rate.full; + b.full = rfixed_mul(b, wm1.active_time); + a.full = rfixed_mul(wm1.worst_case_latency, + wm1.consumption_rate); + a.full = a.full + b.full; + b.full = rfixed_const(16 * 1000); + priority_mark12.full = rfixed_div(a, b); + } else { + a.full = rfixed_mul(wm1.worst_case_latency, + wm1.consumption_rate); + b.full = rfixed_const(16 * 1000); + priority_mark12.full = rfixed_div(a, b); + } + if (wm0.priority_mark.full > priority_mark02.full) + priority_mark02.full = wm0.priority_mark.full; + if (rfixed_trunc(priority_mark02) < 0) + priority_mark02.full = 0; + if (wm0.priority_mark_max.full > priority_mark02.full) + priority_mark02.full = wm0.priority_mark_max.full; + if (wm1.priority_mark.full > priority_mark12.full) + priority_mark12.full = wm1.priority_mark.full; + if (rfixed_trunc(priority_mark12) < 0) + priority_mark12.full = 0; + if (wm1.priority_mark_max.full > priority_mark12.full) + priority_mark12.full = wm1.priority_mark_max.full; + WREG32(D1MODE_PRIORITY_A_CNT, rfixed_trunc(priority_mark02)); + WREG32(D1MODE_PRIORITY_B_CNT, rfixed_trunc(priority_mark02)); + WREG32(D2MODE_PRIORITY_A_CNT, rfixed_trunc(priority_mark12)); + WREG32(D2MODE_PRIORITY_B_CNT, rfixed_trunc(priority_mark12)); + } else if (mode0) { + if (rfixed_trunc(wm0.dbpp) > 64) + a.full = rfixed_mul(wm0.dbpp, wm0.num_line_pair); + else + a.full = wm0.num_line_pair.full; + fill_rate.full = rfixed_div(wm0.sclk, a); + if (wm0.consumption_rate.full > fill_rate.full) { + b.full = wm0.consumption_rate.full - fill_rate.full; + b.full = rfixed_mul(b, wm0.active_time); + a.full = rfixed_mul(wm0.worst_case_latency, + wm0.consumption_rate); + a.full = a.full + b.full; + b.full = rfixed_const(16 * 1000); + priority_mark02.full = rfixed_div(a, b); + } else { + a.full = rfixed_mul(wm0.worst_case_latency, + wm0.consumption_rate); + b.full = rfixed_const(16 * 1000); + priority_mark02.full = rfixed_div(a, b); + } + if (wm0.priority_mark.full > priority_mark02.full) + priority_mark02.full = wm0.priority_mark.full; + if (rfixed_trunc(priority_mark02) < 0) + priority_mark02.full = 0; + if (wm0.priority_mark_max.full > priority_mark02.full) + priority_mark02.full = wm0.priority_mark_max.full; + WREG32(D1MODE_PRIORITY_A_CNT, rfixed_trunc(priority_mark02)); + WREG32(D1MODE_PRIORITY_B_CNT, rfixed_trunc(priority_mark02)); + WREG32(D2MODE_PRIORITY_A_CNT, MODE_PRIORITY_OFF); + WREG32(D2MODE_PRIORITY_B_CNT, MODE_PRIORITY_OFF); + } else { + if (rfixed_trunc(wm1.dbpp) > 64) + a.full = rfixed_mul(wm1.dbpp, wm1.num_line_pair); + else + a.full = wm1.num_line_pair.full; + fill_rate.full = rfixed_div(wm1.sclk, a); + if (wm1.consumption_rate.full > fill_rate.full) { + b.full = wm1.consumption_rate.full - fill_rate.full; + b.full = rfixed_mul(b, wm1.active_time); + a.full = rfixed_mul(wm1.worst_case_latency, + wm1.consumption_rate); + a.full = a.full + b.full; + b.full = rfixed_const(16 * 1000); + priority_mark12.full = rfixed_div(a, b); + } else { + a.full = rfixed_mul(wm1.worst_case_latency, + wm1.consumption_rate); + b.full = rfixed_const(16 * 1000); + priority_mark12.full = rfixed_div(a, b); + } + if (wm1.priority_mark.full > priority_mark12.full) + priority_mark12.full = wm1.priority_mark.full; + if (rfixed_trunc(priority_mark12) < 0) + priority_mark12.full = 0; + if (wm1.priority_mark_max.full > priority_mark12.full) + priority_mark12.full = wm1.priority_mark_max.full; + WREG32(D1MODE_PRIORITY_A_CNT, MODE_PRIORITY_OFF); + WREG32(D1MODE_PRIORITY_B_CNT, MODE_PRIORITY_OFF); + WREG32(D2MODE_PRIORITY_A_CNT, rfixed_trunc(priority_mark12)); + WREG32(D2MODE_PRIORITY_B_CNT, rfixed_trunc(priority_mark12)); + } +} /* * Indirect registers accessor diff --git a/drivers/gpu/drm/radeon/rs690r.h b/drivers/gpu/drm/radeon/rs690r.h new file mode 100644 index 00000000000..c0d9faa2175 --- /dev/null +++ b/drivers/gpu/drm/radeon/rs690r.h @@ -0,0 +1,99 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ +#ifndef RS690R_H +#define RS690R_H + +/* RS690/RS740 registers */ +#define MC_INDEX 0x0078 +# define MC_INDEX_MASK 0x1FF +# define MC_INDEX_WR_EN (1 << 9) +# define MC_INDEX_WR_ACK 0x7F +#define MC_DATA 0x007C +#define HDP_FB_LOCATION 0x0134 +#define DC_LB_MEMORY_SPLIT 0x6520 +#define DC_LB_MEMORY_SPLIT_MASK 0x00000003 +#define DC_LB_MEMORY_SPLIT_SHIFT 0 +#define DC_LB_MEMORY_SPLIT_D1HALF_D2HALF 0 +#define DC_LB_MEMORY_SPLIT_D1_3Q_D2_1Q 1 +#define DC_LB_MEMORY_SPLIT_D1_ONLY 2 +#define DC_LB_MEMORY_SPLIT_D1_1Q_D2_3Q 3 +#define DC_LB_MEMORY_SPLIT_SHIFT_MODE (1 << 2) +#define DC_LB_DISP1_END_ADR_SHIFT 4 +#define DC_LB_DISP1_END_ADR_MASK 0x00007FF0 +#define D1MODE_PRIORITY_A_CNT 0x6548 +#define MODE_PRIORITY_MARK_MASK 0x00007FFF +#define MODE_PRIORITY_OFF (1 << 16) +#define MODE_PRIORITY_ALWAYS_ON (1 << 20) +#define MODE_PRIORITY_FORCE_MASK (1 << 24) +#define D1MODE_PRIORITY_B_CNT 0x654C +#define LB_MAX_REQ_OUTSTANDING 0x6D58 +#define LB_D1_MAX_REQ_OUTSTANDING_MASK 0x0000000F +#define LB_D1_MAX_REQ_OUTSTANDING_SHIFT 0 +#define LB_D2_MAX_REQ_OUTSTANDING_MASK 0x000F0000 +#define LB_D2_MAX_REQ_OUTSTANDING_SHIFT 16 +#define DCP_CONTROL 0x6C9C +#define D2MODE_PRIORITY_A_CNT 0x6D48 +#define D2MODE_PRIORITY_B_CNT 0x6D4C + +/* MC indirect registers */ +#define MC_STATUS_IDLE (1 << 0) +#define MC_MISC_CNTL 0x18 +#define DISABLE_GTW (1 << 1) +#define GART_INDEX_REG_EN (1 << 12) +#define BLOCK_GFX_D3_EN (1 << 14) +#define GART_FEATURE_ID 0x2B +#define HANG_EN (1 << 11) +#define TLB_ENABLE (1 << 18) +#define P2P_ENABLE (1 << 19) +#define GTW_LAC_EN (1 << 25) +#define LEVEL2_GART (0 << 30) +#define LEVEL1_GART (1 << 30) +#define PDC_EN (1 << 31) +#define GART_BASE 0x2C +#define GART_CACHE_CNTRL 0x2E +# define GART_CACHE_INVALIDATE (1 << 0) +#define MC_STATUS 0x90 +#define MCCFG_FB_LOCATION 0x100 +#define MC_FB_START_MASK 0x0000FFFF +#define MC_FB_START_SHIFT 0 +#define MC_FB_TOP_MASK 0xFFFF0000 +#define MC_FB_TOP_SHIFT 16 +#define MCCFG_AGP_LOCATION 0x101 +#define MC_AGP_START_MASK 0x0000FFFF +#define MC_AGP_START_SHIFT 0 +#define MC_AGP_TOP_MASK 0xFFFF0000 +#define MC_AGP_TOP_SHIFT 16 +#define MCCFG_AGP_BASE 0x102 +#define MCCFG_AGP_BASE_2 0x103 +#define MC_INIT_MISC_LAT_TIMER 0x104 +#define MC_DISP0R_INIT_LAT_SHIFT 8 +#define MC_DISP0R_INIT_LAT_MASK 0x00000F00 +#define MC_DISP1R_INIT_LAT_SHIFT 12 +#define MC_DISP1R_INIT_LAT_MASK 0x0000F000 + +#endif diff --git a/drivers/gpu/drm/radeon/rv515.c b/drivers/gpu/drm/radeon/rv515.c index ffea37b1b3e..fd8f3ca716e 100644 --- a/drivers/gpu/drm/radeon/rv515.c +++ b/drivers/gpu/drm/radeon/rv515.c @@ -27,8 +27,9 @@ */ #include <linux/seq_file.h> #include "drmP.h" -#include "radeon_reg.h" +#include "rv515r.h" #include "radeon.h" +#include "radeon_share.h" /* rv515 depends on : */ void r100_hdp_reset(struct radeon_device *rdev); @@ -99,26 +100,26 @@ int rv515_mc_init(struct radeon_device *rdev) "programming pipes. Bad things might happen.\n"); } /* Write VRAM size in case we are limiting it */ - WREG32(RADEON_CONFIG_MEMSIZE, rdev->mc.vram_size); - tmp = REG_SET(RV515_MC_FB_START, rdev->mc.vram_location >> 16); + WREG32(RADEON_CONFIG_MEMSIZE, rdev->mc.real_vram_size); + tmp = REG_SET(MC_FB_START, rdev->mc.vram_location >> 16); WREG32(0x134, tmp); - tmp = rdev->mc.vram_location + rdev->mc.vram_size - 1; - tmp = REG_SET(RV515_MC_FB_TOP, tmp >> 16); - tmp |= REG_SET(RV515_MC_FB_START, rdev->mc.vram_location >> 16); - WREG32_MC(RV515_MC_FB_LOCATION, tmp); - WREG32(RS690_HDP_FB_LOCATION, rdev->mc.vram_location >> 16); + tmp = rdev->mc.vram_location + rdev->mc.mc_vram_size - 1; + tmp = REG_SET(MC_FB_TOP, tmp >> 16); + tmp |= REG_SET(MC_FB_START, rdev->mc.vram_location >> 16); + WREG32_MC(MC_FB_LOCATION, tmp); + WREG32(HDP_FB_LOCATION, rdev->mc.vram_location >> 16); WREG32(0x310, rdev->mc.vram_location); if (rdev->flags & RADEON_IS_AGP) { tmp = rdev->mc.gtt_location + rdev->mc.gtt_size - 1; - tmp = REG_SET(RV515_MC_AGP_TOP, tmp >> 16); - tmp |= REG_SET(RV515_MC_AGP_START, rdev->mc.gtt_location >> 16); - WREG32_MC(RV515_MC_AGP_LOCATION, tmp); - WREG32_MC(RV515_MC_AGP_BASE, rdev->mc.agp_base); - WREG32_MC(RV515_MC_AGP_BASE_2, 0); + tmp = REG_SET(MC_AGP_TOP, tmp >> 16); + tmp |= REG_SET(MC_AGP_START, rdev->mc.gtt_location >> 16); + WREG32_MC(MC_AGP_LOCATION, tmp); + WREG32_MC(MC_AGP_BASE, rdev->mc.agp_base); + WREG32_MC(MC_AGP_BASE_2, 0); } else { - WREG32_MC(RV515_MC_AGP_LOCATION, 0x0FFFFFFF); - WREG32_MC(RV515_MC_AGP_BASE, 0); - WREG32_MC(RV515_MC_AGP_BASE_2, 0); + WREG32_MC(MC_AGP_LOCATION, 0x0FFFFFFF); + WREG32_MC(MC_AGP_BASE, 0); + WREG32_MC(MC_AGP_BASE_2, 0); } return 0; } @@ -136,95 +137,67 @@ void rv515_mc_fini(struct radeon_device *rdev) */ void rv515_ring_start(struct radeon_device *rdev) { - unsigned gb_tile_config; int r; - /* Sub pixel 1/12 so we can have 4K rendering according to doc */ - gb_tile_config = R300_ENABLE_TILING | R300_TILE_SIZE_16; - switch (rdev->num_gb_pipes) { - case 2: - gb_tile_config |= R300_PIPE_COUNT_R300; - break; - case 3: - gb_tile_config |= R300_PIPE_COUNT_R420_3P; - break; - case 4: - gb_tile_config |= R300_PIPE_COUNT_R420; - break; - case 1: - default: - gb_tile_config |= R300_PIPE_COUNT_RV350; - break; - } - r = radeon_ring_lock(rdev, 64); if (r) { return; } - radeon_ring_write(rdev, PACKET0(RADEON_ISYNC_CNTL, 0)); - radeon_ring_write(rdev, - RADEON_ISYNC_ANY2D_IDLE3D | - RADEON_ISYNC_ANY3D_IDLE2D | - RADEON_ISYNC_WAIT_IDLEGUI | - RADEON_ISYNC_CPSCRATCH_IDLEGUI); - radeon_ring_write(rdev, PACKET0(R300_GB_TILE_CONFIG, 0)); - radeon_ring_write(rdev, gb_tile_config); - radeon_ring_write(rdev, PACKET0(RADEON_WAIT_UNTIL, 0)); + radeon_ring_write(rdev, PACKET0(ISYNC_CNTL, 0)); radeon_ring_write(rdev, - RADEON_WAIT_2D_IDLECLEAN | - RADEON_WAIT_3D_IDLECLEAN); + ISYNC_ANY2D_IDLE3D | + ISYNC_ANY3D_IDLE2D | + ISYNC_WAIT_IDLEGUI | + ISYNC_CPSCRATCH_IDLEGUI); + radeon_ring_write(rdev, PACKET0(WAIT_UNTIL, 0)); + radeon_ring_write(rdev, WAIT_2D_IDLECLEAN | WAIT_3D_IDLECLEAN); radeon_ring_write(rdev, PACKET0(0x170C, 0)); radeon_ring_write(rdev, 1 << 31); - radeon_ring_write(rdev, PACKET0(R300_GB_SELECT, 0)); + radeon_ring_write(rdev, PACKET0(GB_SELECT, 0)); radeon_ring_write(rdev, 0); - radeon_ring_write(rdev, PACKET0(R300_GB_ENABLE, 0)); + radeon_ring_write(rdev, PACKET0(GB_ENABLE, 0)); radeon_ring_write(rdev, 0); radeon_ring_write(rdev, PACKET0(0x42C8, 0)); radeon_ring_write(rdev, (1 << rdev->num_gb_pipes) - 1); - radeon_ring_write(rdev, PACKET0(R500_VAP_INDEX_OFFSET, 0)); + radeon_ring_write(rdev, PACKET0(VAP_INDEX_OFFSET, 0)); radeon_ring_write(rdev, 0); - radeon_ring_write(rdev, PACKET0(R300_RB3D_DSTCACHE_CTLSTAT, 0)); - radeon_ring_write(rdev, R300_RB3D_DC_FLUSH | R300_RB3D_DC_FREE); - radeon_ring_write(rdev, PACKET0(R300_RB3D_ZCACHE_CTLSTAT, 0)); - radeon_ring_write(rdev, R300_ZC_FLUSH | R300_ZC_FREE); - radeon_ring_write(rdev, PACKET0(RADEON_WAIT_UNTIL, 0)); - radeon_ring_write(rdev, - RADEON_WAIT_2D_IDLECLEAN | - RADEON_WAIT_3D_IDLECLEAN); - radeon_ring_write(rdev, PACKET0(R300_GB_AA_CONFIG, 0)); + radeon_ring_write(rdev, PACKET0(RB3D_DSTCACHE_CTLSTAT, 0)); + radeon_ring_write(rdev, RB3D_DC_FLUSH | RB3D_DC_FREE); + radeon_ring_write(rdev, PACKET0(ZB_ZCACHE_CTLSTAT, 0)); + radeon_ring_write(rdev, ZC_FLUSH | ZC_FREE); + radeon_ring_write(rdev, PACKET0(WAIT_UNTIL, 0)); + radeon_ring_write(rdev, WAIT_2D_IDLECLEAN | WAIT_3D_IDLECLEAN); + radeon_ring_write(rdev, PACKET0(GB_AA_CONFIG, 0)); radeon_ring_write(rdev, 0); - radeon_ring_write(rdev, PACKET0(R300_RB3D_DSTCACHE_CTLSTAT, 0)); - radeon_ring_write(rdev, R300_RB3D_DC_FLUSH | R300_RB3D_DC_FREE); - radeon_ring_write(rdev, PACKET0(R300_RB3D_ZCACHE_CTLSTAT, 0)); - radeon_ring_write(rdev, R300_ZC_FLUSH | R300_ZC_FREE); - radeon_ring_write(rdev, PACKET0(R300_GB_MSPOS0, 0)); - radeon_ring_write(rdev, - ((6 << R300_MS_X0_SHIFT) | - (6 << R300_MS_Y0_SHIFT) | - (6 << R300_MS_X1_SHIFT) | - (6 << R300_MS_Y1_SHIFT) | - (6 << R300_MS_X2_SHIFT) | - (6 << R300_MS_Y2_SHIFT) | - (6 << R300_MSBD0_Y_SHIFT) | - (6 << R300_MSBD0_X_SHIFT))); - radeon_ring_write(rdev, PACKET0(R300_GB_MSPOS1, 0)); + radeon_ring_write(rdev, PACKET0(RB3D_DSTCACHE_CTLSTAT, 0)); + radeon_ring_write(rdev, RB3D_DC_FLUSH | RB3D_DC_FREE); + radeon_ring_write(rdev, PACKET0(ZB_ZCACHE_CTLSTAT, 0)); + radeon_ring_write(rdev, ZC_FLUSH | ZC_FREE); + radeon_ring_write(rdev, PACKET0(GB_MSPOS0, 0)); radeon_ring_write(rdev, - ((6 << R300_MS_X3_SHIFT) | - (6 << R300_MS_Y3_SHIFT) | - (6 << R300_MS_X4_SHIFT) | - (6 << R300_MS_Y4_SHIFT) | - (6 << R300_MS_X5_SHIFT) | - (6 << R300_MS_Y5_SHIFT) | - (6 << R300_MSBD1_SHIFT))); - radeon_ring_write(rdev, PACKET0(R300_GA_ENHANCE, 0)); - radeon_ring_write(rdev, R300_GA_DEADLOCK_CNTL | R300_GA_FASTSYNC_CNTL); - radeon_ring_write(rdev, PACKET0(R300_GA_POLY_MODE, 0)); + ((6 << MS_X0_SHIFT) | + (6 << MS_Y0_SHIFT) | + (6 << MS_X1_SHIFT) | + (6 << MS_Y1_SHIFT) | + (6 << MS_X2_SHIFT) | + (6 << MS_Y2_SHIFT) | + (6 << MSBD0_Y_SHIFT) | + (6 << MSBD0_X_SHIFT))); + radeon_ring_write(rdev, PACKET0(GB_MSPOS1, 0)); radeon_ring_write(rdev, - R300_FRONT_PTYPE_TRIANGE | R300_BACK_PTYPE_TRIANGE); - radeon_ring_write(rdev, PACKET0(R300_GA_ROUND_MODE, 0)); - radeon_ring_write(rdev, - R300_GEOMETRY_ROUND_NEAREST | - R300_COLOR_ROUND_NEAREST); + ((6 << MS_X3_SHIFT) | + (6 << MS_Y3_SHIFT) | + (6 << MS_X4_SHIFT) | + (6 << MS_Y4_SHIFT) | + (6 << MS_X5_SHIFT) | + (6 << MS_Y5_SHIFT) | + (6 << MSBD1_SHIFT))); + radeon_ring_write(rdev, PACKET0(GA_ENHANCE, 0)); + radeon_ring_write(rdev, GA_DEADLOCK_CNTL | GA_FASTSYNC_CNTL); + radeon_ring_write(rdev, PACKET0(GA_POLY_MODE, 0)); + radeon_ring_write(rdev, FRONT_PTYPE_TRIANGE | BACK_PTYPE_TRIANGE); + radeon_ring_write(rdev, PACKET0(GA_ROUND_MODE, 0)); + radeon_ring_write(rdev, GEOMETRY_ROUND_NEAREST | COLOR_ROUND_NEAREST); radeon_ring_write(rdev, PACKET0(0x20C8, 0)); radeon_ring_write(rdev, 0); radeon_ring_unlock_commit(rdev); @@ -242,8 +215,8 @@ int rv515_mc_wait_for_idle(struct radeon_device *rdev) for (i = 0; i < rdev->usec_timeout; i++) { /* read MC_STATUS */ - tmp = RREG32_MC(RV515_MC_STATUS); - if (tmp & RV515_MC_STATUS_IDLE) { + tmp = RREG32_MC(MC_STATUS); + if (tmp & MC_STATUS_IDLE) { return 0; } DRM_UDELAY(1); @@ -291,33 +264,33 @@ int rv515_ga_reset(struct radeon_device *rdev) reinit_cp = rdev->cp.ready; rdev->cp.ready = false; for (i = 0; i < rdev->usec_timeout; i++) { - WREG32(RADEON_CP_CSQ_MODE, 0); - WREG32(RADEON_CP_CSQ_CNTL, 0); - WREG32(RADEON_RBBM_SOFT_RESET, 0x32005); - (void)RREG32(RADEON_RBBM_SOFT_RESET); + WREG32(CP_CSQ_MODE, 0); + WREG32(CP_CSQ_CNTL, 0); + WREG32(RBBM_SOFT_RESET, 0x32005); + (void)RREG32(RBBM_SOFT_RESET); udelay(200); - WREG32(RADEON_RBBM_SOFT_RESET, 0); + WREG32(RBBM_SOFT_RESET, 0); /* Wait to prevent race in RBBM_STATUS */ mdelay(1); - tmp = RREG32(RADEON_RBBM_STATUS); + tmp = RREG32(RBBM_STATUS); if (tmp & ((1 << 20) | (1 << 26))) { DRM_ERROR("VAP & CP still busy (RBBM_STATUS=0x%08X)\n", tmp); /* GA still busy soft reset it */ WREG32(0x429C, 0x200); - WREG32(R300_VAP_PVS_STATE_FLUSH_REG, 0); + WREG32(VAP_PVS_STATE_FLUSH_REG, 0); WREG32(0x43E0, 0); WREG32(0x43E4, 0); WREG32(0x24AC, 0); } /* Wait to prevent race in RBBM_STATUS */ mdelay(1); - tmp = RREG32(RADEON_RBBM_STATUS); + tmp = RREG32(RBBM_STATUS); if (!(tmp & ((1 << 20) | (1 << 26)))) { break; } } for (i = 0; i < rdev->usec_timeout; i++) { - tmp = RREG32(RADEON_RBBM_STATUS); + tmp = RREG32(RBBM_STATUS); if (!(tmp & ((1 << 20) | (1 << 26)))) { DRM_INFO("GA reset succeed (RBBM_STATUS=0x%08X)\n", tmp); @@ -331,7 +304,7 @@ int rv515_ga_reset(struct radeon_device *rdev) } DRM_UDELAY(1); } - tmp = RREG32(RADEON_RBBM_STATUS); + tmp = RREG32(RBBM_STATUS); DRM_ERROR("Failed to reset GA ! (RBBM_STATUS=0x%08X)\n", tmp); return -1; } @@ -341,7 +314,7 @@ int rv515_gpu_reset(struct radeon_device *rdev) uint32_t status; /* reset order likely matter */ - status = RREG32(RADEON_RBBM_STATUS); + status = RREG32(RBBM_STATUS); /* reset HDP */ r100_hdp_reset(rdev); /* reset rb2d */ @@ -353,12 +326,12 @@ int rv515_gpu_reset(struct radeon_device *rdev) rv515_ga_reset(rdev); } /* reset CP */ - status = RREG32(RADEON_RBBM_STATUS); + status = RREG32(RBBM_STATUS); if (status & (1 << 16)) { r100_cp_reset(rdev); } /* Check if GPU is idle */ - status = RREG32(RADEON_RBBM_STATUS); + status = RREG32(RBBM_STATUS); if (status & (1 << 31)) { DRM_ERROR("Failed to reset GPU (RBBM_STATUS=0x%08X)\n", status); return -1; @@ -377,8 +350,7 @@ static void rv515_vram_get_type(struct radeon_device *rdev) rdev->mc.vram_width = 128; rdev->mc.vram_is_ddr = true; - tmp = RREG32_MC(RV515_MC_CNTL); - tmp &= RV515_MEM_NUM_CHANNELS_MASK; + tmp = RREG32_MC(RV515_MC_CNTL) & MEM_NUM_CHANNELS_MASK; switch (tmp) { case 0: rdev->mc.vram_width = 64; @@ -394,11 +366,17 @@ static void rv515_vram_get_type(struct radeon_device *rdev) void rv515_vram_info(struct radeon_device *rdev) { + fixed20_12 a; + rv515_vram_get_type(rdev); - rdev->mc.vram_size = RREG32(RADEON_CONFIG_MEMSIZE); - rdev->mc.aper_base = drm_get_resource_start(rdev->ddev, 0); - rdev->mc.aper_size = drm_get_resource_len(rdev->ddev, 0); + r100_vram_init_sizes(rdev); + /* FIXME: we should enforce default clock in case GPU is not in + * default setup + */ + a.full = rfixed_const(100); + rdev->pm.sclk.full = rfixed_const(rdev->clock.default_sclk); + rdev->pm.sclk.full = rfixed_div(rdev->pm.sclk, a); } @@ -409,35 +387,35 @@ uint32_t rv515_mc_rreg(struct radeon_device *rdev, uint32_t reg) { uint32_t r; - WREG32(R520_MC_IND_INDEX, 0x7f0000 | (reg & 0xffff)); - r = RREG32(R520_MC_IND_DATA); - WREG32(R520_MC_IND_INDEX, 0); + WREG32(MC_IND_INDEX, 0x7f0000 | (reg & 0xffff)); + r = RREG32(MC_IND_DATA); + WREG32(MC_IND_INDEX, 0); return r; } void rv515_mc_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v) { - WREG32(R520_MC_IND_INDEX, 0xff0000 | ((reg) & 0xffff)); - WREG32(R520_MC_IND_DATA, (v)); - WREG32(R520_MC_IND_INDEX, 0); + WREG32(MC_IND_INDEX, 0xff0000 | ((reg) & 0xffff)); + WREG32(MC_IND_DATA, (v)); + WREG32(MC_IND_INDEX, 0); } uint32_t rv515_pcie_rreg(struct radeon_device *rdev, uint32_t reg) { uint32_t r; - WREG32(RADEON_PCIE_INDEX, ((reg) & 0x7ff)); - (void)RREG32(RADEON_PCIE_INDEX); - r = RREG32(RADEON_PCIE_DATA); + WREG32(PCIE_INDEX, ((reg) & 0x7ff)); + (void)RREG32(PCIE_INDEX); + r = RREG32(PCIE_DATA); return r; } void rv515_pcie_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v) { - WREG32(RADEON_PCIE_INDEX, ((reg) & 0x7ff)); - (void)RREG32(RADEON_PCIE_INDEX); - WREG32(RADEON_PCIE_DATA, (v)); - (void)RREG32(RADEON_PCIE_DATA); + WREG32(PCIE_INDEX, ((reg) & 0x7ff)); + (void)RREG32(PCIE_INDEX); + WREG32(PCIE_DATA, (v)); + (void)RREG32(PCIE_DATA); } @@ -452,13 +430,13 @@ static int rv515_debugfs_pipes_info(struct seq_file *m, void *data) struct radeon_device *rdev = dev->dev_private; uint32_t tmp; - tmp = RREG32(R400_GB_PIPE_SELECT); + tmp = RREG32(GB_PIPE_SELECT); seq_printf(m, "GB_PIPE_SELECT 0x%08x\n", tmp); - tmp = RREG32(R500_SU_REG_DEST); + tmp = RREG32(SU_REG_DEST); seq_printf(m, "SU_REG_DEST 0x%08x\n", tmp); - tmp = RREG32(R300_GB_TILE_CONFIG); + tmp = RREG32(GB_TILE_CONFIG); seq_printf(m, "GB_TILE_CONFIG 0x%08x\n", tmp); - tmp = RREG32(R300_DST_PIPE_CONFIG); + tmp = RREG32(DST_PIPE_CONFIG); seq_printf(m, "DST_PIPE_CONFIG 0x%08x\n", tmp); return 0; } @@ -509,9 +487,9 @@ int rv515_debugfs_ga_info_init(struct radeon_device *rdev) /* * Asic initialization */ -static const unsigned r500_reg_safe_bm[159] = { +static const unsigned r500_reg_safe_bm[219] = { + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFBF, 0xFFFFFFFF, 0xFFFFFFBF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, @@ -549,14 +527,575 @@ static const unsigned r500_reg_safe_bm[159] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF80FFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x0003FC01, 0x3FFFFCF8, 0xFE800B19, + 0x0003FC01, 0x3FFFFCF8, 0xFE800B19, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, }; - - int rv515_init(struct radeon_device *rdev) { rdev->config.r300.reg_safe_bm = r500_reg_safe_bm; rdev->config.r300.reg_safe_bm_size = ARRAY_SIZE(r500_reg_safe_bm); return 0; } + +void atom_rv515_force_tv_scaler(struct radeon_device *rdev) +{ + + WREG32(0x659C, 0x0); + WREG32(0x6594, 0x705); + WREG32(0x65A4, 0x10001); + WREG32(0x65D8, 0x0); + WREG32(0x65B0, 0x0); + WREG32(0x65C0, 0x0); + WREG32(0x65D4, 0x0); + WREG32(0x6578, 0x0); + WREG32(0x657C, 0x841880A8); + WREG32(0x6578, 0x1); + WREG32(0x657C, 0x84208680); + WREG32(0x6578, 0x2); + WREG32(0x657C, 0xBFF880B0); + WREG32(0x6578, 0x100); + WREG32(0x657C, 0x83D88088); + WREG32(0x6578, 0x101); + WREG32(0x657C, 0x84608680); + WREG32(0x6578, 0x102); + WREG32(0x657C, 0xBFF080D0); + WREG32(0x6578, 0x200); + WREG32(0x657C, 0x83988068); + WREG32(0x6578, 0x201); + WREG32(0x657C, 0x84A08680); + WREG32(0x6578, 0x202); + WREG32(0x657C, 0xBFF080F8); + WREG32(0x6578, 0x300); + WREG32(0x657C, 0x83588058); + WREG32(0x6578, 0x301); + WREG32(0x657C, 0x84E08660); + WREG32(0x6578, 0x302); + WREG32(0x657C, 0xBFF88120); + WREG32(0x6578, 0x400); + WREG32(0x657C, 0x83188040); + WREG32(0x6578, 0x401); + WREG32(0x657C, 0x85008660); + WREG32(0x6578, 0x402); + WREG32(0x657C, 0xBFF88150); + WREG32(0x6578, 0x500); + WREG32(0x657C, 0x82D88030); + WREG32(0x6578, 0x501); + WREG32(0x657C, 0x85408640); + WREG32(0x6578, 0x502); + WREG32(0x657C, 0xBFF88180); + WREG32(0x6578, 0x600); + WREG32(0x657C, 0x82A08018); + WREG32(0x6578, 0x601); + WREG32(0x657C, 0x85808620); + WREG32(0x6578, 0x602); + WREG32(0x657C, 0xBFF081B8); + WREG32(0x6578, 0x700); + WREG32(0x657C, 0x82608010); + WREG32(0x6578, 0x701); + WREG32(0x657C, 0x85A08600); + WREG32(0x6578, 0x702); + WREG32(0x657C, 0x800081F0); + WREG32(0x6578, 0x800); + WREG32(0x657C, 0x8228BFF8); + WREG32(0x6578, 0x801); + WREG32(0x657C, 0x85E085E0); + WREG32(0x6578, 0x802); + WREG32(0x657C, 0xBFF88228); + WREG32(0x6578, 0x10000); + WREG32(0x657C, 0x82A8BF00); + WREG32(0x6578, 0x10001); + WREG32(0x657C, 0x82A08CC0); + WREG32(0x6578, 0x10002); + WREG32(0x657C, 0x8008BEF8); + WREG32(0x6578, 0x10100); + WREG32(0x657C, 0x81F0BF28); + WREG32(0x6578, 0x10101); + WREG32(0x657C, 0x83608CA0); + WREG32(0x6578, 0x10102); + WREG32(0x657C, 0x8018BED0); + WREG32(0x6578, 0x10200); + WREG32(0x657C, 0x8148BF38); + WREG32(0x6578, 0x10201); + WREG32(0x657C, 0x84408C80); + WREG32(0x6578, 0x10202); + WREG32(0x657C, 0x8008BEB8); + WREG32(0x6578, 0x10300); + WREG32(0x657C, 0x80B0BF78); + WREG32(0x6578, 0x10301); + WREG32(0x657C, 0x85008C20); + WREG32(0x6578, 0x10302); + WREG32(0x657C, 0x8020BEA0); + WREG32(0x6578, 0x10400); + WREG32(0x657C, 0x8028BF90); + WREG32(0x6578, 0x10401); + WREG32(0x657C, 0x85E08BC0); + WREG32(0x6578, 0x10402); + WREG32(0x657C, 0x8018BE90); + WREG32(0x6578, 0x10500); + WREG32(0x657C, 0xBFB8BFB0); + WREG32(0x6578, 0x10501); + WREG32(0x657C, 0x86C08B40); + WREG32(0x6578, 0x10502); + WREG32(0x657C, 0x8010BE90); + WREG32(0x6578, 0x10600); + WREG32(0x657C, 0xBF58BFC8); + WREG32(0x6578, 0x10601); + WREG32(0x657C, 0x87A08AA0); + WREG32(0x6578, 0x10602); + WREG32(0x657C, 0x8010BE98); + WREG32(0x6578, 0x10700); + WREG32(0x657C, 0xBF10BFF0); + WREG32(0x6578, 0x10701); + WREG32(0x657C, 0x886089E0); + WREG32(0x6578, 0x10702); + WREG32(0x657C, 0x8018BEB0); + WREG32(0x6578, 0x10800); + WREG32(0x657C, 0xBED8BFE8); + WREG32(0x6578, 0x10801); + WREG32(0x657C, 0x89408940); + WREG32(0x6578, 0x10802); + WREG32(0x657C, 0xBFE8BED8); + WREG32(0x6578, 0x20000); + WREG32(0x657C, 0x80008000); + WREG32(0x6578, 0x20001); + WREG32(0x657C, 0x90008000); + WREG32(0x6578, 0x20002); + WREG32(0x657C, 0x80008000); + WREG32(0x6578, 0x20003); + WREG32(0x657C, 0x80008000); + WREG32(0x6578, 0x20100); + WREG32(0x657C, 0x80108000); + WREG32(0x6578, 0x20101); + WREG32(0x657C, 0x8FE0BF70); + WREG32(0x6578, 0x20102); + WREG32(0x657C, 0xBFE880C0); + WREG32(0x6578, 0x20103); + WREG32(0x657C, 0x80008000); + WREG32(0x6578, 0x20200); + WREG32(0x657C, 0x8018BFF8); + WREG32(0x6578, 0x20201); + WREG32(0x657C, 0x8F80BF08); + WREG32(0x6578, 0x20202); + WREG32(0x657C, 0xBFD081A0); + WREG32(0x6578, 0x20203); + WREG32(0x657C, 0xBFF88000); + WREG32(0x6578, 0x20300); + WREG32(0x657C, 0x80188000); + WREG32(0x6578, 0x20301); + WREG32(0x657C, 0x8EE0BEC0); + WREG32(0x6578, 0x20302); + WREG32(0x657C, 0xBFB082A0); + WREG32(0x6578, 0x20303); + WREG32(0x657C, 0x80008000); + WREG32(0x6578, 0x20400); + WREG32(0x657C, 0x80188000); + WREG32(0x6578, 0x20401); + WREG32(0x657C, 0x8E00BEA0); + WREG32(0x6578, 0x20402); + WREG32(0x657C, 0xBF8883C0); + WREG32(0x6578, 0x20403); + WREG32(0x657C, 0x80008000); + WREG32(0x6578, 0x20500); + WREG32(0x657C, 0x80188000); + WREG32(0x6578, 0x20501); + WREG32(0x657C, 0x8D00BE90); + WREG32(0x6578, 0x20502); + WREG32(0x657C, 0xBF588500); + WREG32(0x6578, 0x20503); + WREG32(0x657C, 0x80008008); + WREG32(0x6578, 0x20600); + WREG32(0x657C, 0x80188000); + WREG32(0x6578, 0x20601); + WREG32(0x657C, 0x8BC0BE98); + WREG32(0x6578, 0x20602); + WREG32(0x657C, 0xBF308660); + WREG32(0x6578, 0x20603); + WREG32(0x657C, 0x80008008); + WREG32(0x6578, 0x20700); + WREG32(0x657C, 0x80108000); + WREG32(0x6578, 0x20701); + WREG32(0x657C, 0x8A80BEB0); + WREG32(0x6578, 0x20702); + WREG32(0x657C, 0xBF0087C0); + WREG32(0x6578, 0x20703); + WREG32(0x657C, 0x80008008); + WREG32(0x6578, 0x20800); + WREG32(0x657C, 0x80108000); + WREG32(0x6578, 0x20801); + WREG32(0x657C, 0x8920BED0); + WREG32(0x6578, 0x20802); + WREG32(0x657C, 0xBED08920); + WREG32(0x6578, 0x20803); + WREG32(0x657C, 0x80008010); + WREG32(0x6578, 0x30000); + WREG32(0x657C, 0x90008000); + WREG32(0x6578, 0x30001); + WREG32(0x657C, 0x80008000); + WREG32(0x6578, 0x30100); + WREG32(0x657C, 0x8FE0BF90); + WREG32(0x6578, 0x30101); + WREG32(0x657C, 0xBFF880A0); + WREG32(0x6578, 0x30200); + WREG32(0x657C, 0x8F60BF40); + WREG32(0x6578, 0x30201); + WREG32(0x657C, 0xBFE88180); + WREG32(0x6578, 0x30300); + WREG32(0x657C, 0x8EC0BF00); + WREG32(0x6578, 0x30301); + WREG32(0x657C, 0xBFC88280); + WREG32(0x6578, 0x30400); + WREG32(0x657C, 0x8DE0BEE0); + WREG32(0x6578, 0x30401); + WREG32(0x657C, 0xBFA083A0); + WREG32(0x6578, 0x30500); + WREG32(0x657C, 0x8CE0BED0); + WREG32(0x6578, 0x30501); + WREG32(0x657C, 0xBF7884E0); + WREG32(0x6578, 0x30600); + WREG32(0x657C, 0x8BA0BED8); + WREG32(0x6578, 0x30601); + WREG32(0x657C, 0xBF508640); + WREG32(0x6578, 0x30700); + WREG32(0x657C, 0x8A60BEE8); + WREG32(0x6578, 0x30701); + WREG32(0x657C, 0xBF2087A0); + WREG32(0x6578, 0x30800); + WREG32(0x657C, 0x8900BF00); + WREG32(0x6578, 0x30801); + WREG32(0x657C, 0xBF008900); +} + +struct rv515_watermark { + u32 lb_request_fifo_depth; + fixed20_12 num_line_pair; + fixed20_12 estimated_width; + fixed20_12 worst_case_latency; + fixed20_12 consumption_rate; + fixed20_12 active_time; + fixed20_12 dbpp; + fixed20_12 priority_mark_max; + fixed20_12 priority_mark; + fixed20_12 sclk; +}; + +void rv515_crtc_bandwidth_compute(struct radeon_device *rdev, + struct radeon_crtc *crtc, + struct rv515_watermark *wm) +{ + struct drm_display_mode *mode = &crtc->base.mode; + fixed20_12 a, b, c; + fixed20_12 pclk, request_fifo_depth, tolerable_latency, estimated_width; + fixed20_12 consumption_time, line_time, chunk_time, read_delay_latency; + + if (!crtc->base.enabled) { + /* FIXME: wouldn't it better to set priority mark to maximum */ + wm->lb_request_fifo_depth = 4; + return; + } + + if (crtc->vsc.full > rfixed_const(2)) + wm->num_line_pair.full = rfixed_const(2); + else + wm->num_line_pair.full = rfixed_const(1); + + b.full = rfixed_const(mode->crtc_hdisplay); + c.full = rfixed_const(256); + a.full = rfixed_mul(wm->num_line_pair, b); + request_fifo_depth.full = rfixed_div(a, c); + if (a.full < rfixed_const(4)) { + wm->lb_request_fifo_depth = 4; + } else { + wm->lb_request_fifo_depth = rfixed_trunc(request_fifo_depth); + } + + /* Determine consumption rate + * pclk = pixel clock period(ns) = 1000 / (mode.clock / 1000) + * vtaps = number of vertical taps, + * vsc = vertical scaling ratio, defined as source/destination + * hsc = horizontal scaling ration, defined as source/destination + */ + a.full = rfixed_const(mode->clock); + b.full = rfixed_const(1000); + a.full = rfixed_div(a, b); + pclk.full = rfixed_div(b, a); + if (crtc->rmx_type != RMX_OFF) { + b.full = rfixed_const(2); + if (crtc->vsc.full > b.full) + b.full = crtc->vsc.full; + b.full = rfixed_mul(b, crtc->hsc); + c.full = rfixed_const(2); + b.full = rfixed_div(b, c); + consumption_time.full = rfixed_div(pclk, b); + } else { + consumption_time.full = pclk.full; + } + a.full = rfixed_const(1); + wm->consumption_rate.full = rfixed_div(a, consumption_time); + + + /* Determine line time + * LineTime = total time for one line of displayhtotal + * LineTime = total number of horizontal pixels + * pclk = pixel clock period(ns) + */ + a.full = rfixed_const(crtc->base.mode.crtc_htotal); + line_time.full = rfixed_mul(a, pclk); + + /* Determine active time + * ActiveTime = time of active region of display within one line, + * hactive = total number of horizontal active pixels + * htotal = total number of horizontal pixels + */ + a.full = rfixed_const(crtc->base.mode.crtc_htotal); + b.full = rfixed_const(crtc->base.mode.crtc_hdisplay); + wm->active_time.full = rfixed_mul(line_time, b); + wm->active_time.full = rfixed_div(wm->active_time, a); + + /* Determine chunk time + * ChunkTime = the time it takes the DCP to send one chunk of data + * to the LB which consists of pipeline delay and inter chunk gap + * sclk = system clock(Mhz) + */ + a.full = rfixed_const(600 * 1000); + chunk_time.full = rfixed_div(a, rdev->pm.sclk); + read_delay_latency.full = rfixed_const(1000); + + /* Determine the worst case latency + * NumLinePair = Number of line pairs to request(1=2 lines, 2=4 lines) + * WorstCaseLatency = worst case time from urgent to when the MC starts + * to return data + * READ_DELAY_IDLE_MAX = constant of 1us + * ChunkTime = time it takes the DCP to send one chunk of data to the LB + * which consists of pipeline delay and inter chunk gap + */ + if (rfixed_trunc(wm->num_line_pair) > 1) { + a.full = rfixed_const(3); + wm->worst_case_latency.full = rfixed_mul(a, chunk_time); + wm->worst_case_latency.full += read_delay_latency.full; + } else { + wm->worst_case_latency.full = chunk_time.full + read_delay_latency.full; + } + + /* Determine the tolerable latency + * TolerableLatency = Any given request has only 1 line time + * for the data to be returned + * LBRequestFifoDepth = Number of chunk requests the LB can + * put into the request FIFO for a display + * LineTime = total time for one line of display + * ChunkTime = the time it takes the DCP to send one chunk + * of data to the LB which consists of + * pipeline delay and inter chunk gap + */ + if ((2+wm->lb_request_fifo_depth) >= rfixed_trunc(request_fifo_depth)) { + tolerable_latency.full = line_time.full; + } else { + tolerable_latency.full = rfixed_const(wm->lb_request_fifo_depth - 2); + tolerable_latency.full = request_fifo_depth.full - tolerable_latency.full; + tolerable_latency.full = rfixed_mul(tolerable_latency, chunk_time); + tolerable_latency.full = line_time.full - tolerable_latency.full; + } + /* We assume worst case 32bits (4 bytes) */ + wm->dbpp.full = rfixed_const(2 * 16); + + /* Determine the maximum priority mark + * width = viewport width in pixels + */ + a.full = rfixed_const(16); + wm->priority_mark_max.full = rfixed_const(crtc->base.mode.crtc_hdisplay); + wm->priority_mark_max.full = rfixed_div(wm->priority_mark_max, a); + + /* Determine estimated width */ + estimated_width.full = tolerable_latency.full - wm->worst_case_latency.full; + estimated_width.full = rfixed_div(estimated_width, consumption_time); + if (rfixed_trunc(estimated_width) > crtc->base.mode.crtc_hdisplay) { + wm->priority_mark.full = rfixed_const(10); + } else { + a.full = rfixed_const(16); + wm->priority_mark.full = rfixed_div(estimated_width, a); + wm->priority_mark.full = wm->priority_mark_max.full - wm->priority_mark.full; + } +} + +void rv515_bandwidth_avivo_update(struct radeon_device *rdev) +{ + struct drm_display_mode *mode0 = NULL; + struct drm_display_mode *mode1 = NULL; + struct rv515_watermark wm0; + struct rv515_watermark wm1; + u32 tmp; + fixed20_12 priority_mark02, priority_mark12, fill_rate; + fixed20_12 a, b; + + if (rdev->mode_info.crtcs[0]->base.enabled) + mode0 = &rdev->mode_info.crtcs[0]->base.mode; + if (rdev->mode_info.crtcs[1]->base.enabled) + mode1 = &rdev->mode_info.crtcs[1]->base.mode; + rs690_line_buffer_adjust(rdev, mode0, mode1); + + rv515_crtc_bandwidth_compute(rdev, rdev->mode_info.crtcs[0], &wm0); + rv515_crtc_bandwidth_compute(rdev, rdev->mode_info.crtcs[1], &wm1); + + tmp = wm0.lb_request_fifo_depth; + tmp |= wm1.lb_request_fifo_depth << 16; + WREG32(LB_MAX_REQ_OUTSTANDING, tmp); + + if (mode0 && mode1) { + if (rfixed_trunc(wm0.dbpp) > 64) + a.full = rfixed_div(wm0.dbpp, wm0.num_line_pair); + else + a.full = wm0.num_line_pair.full; + if (rfixed_trunc(wm1.dbpp) > 64) + b.full = rfixed_div(wm1.dbpp, wm1.num_line_pair); + else + b.full = wm1.num_line_pair.full; + a.full += b.full; + fill_rate.full = rfixed_div(wm0.sclk, a); + if (wm0.consumption_rate.full > fill_rate.full) { + b.full = wm0.consumption_rate.full - fill_rate.full; + b.full = rfixed_mul(b, wm0.active_time); + a.full = rfixed_const(16); + b.full = rfixed_div(b, a); + a.full = rfixed_mul(wm0.worst_case_latency, + wm0.consumption_rate); + priority_mark02.full = a.full + b.full; + } else { + a.full = rfixed_mul(wm0.worst_case_latency, + wm0.consumption_rate); + b.full = rfixed_const(16 * 1000); + priority_mark02.full = rfixed_div(a, b); + } + if (wm1.consumption_rate.full > fill_rate.full) { + b.full = wm1.consumption_rate.full - fill_rate.full; + b.full = rfixed_mul(b, wm1.active_time); + a.full = rfixed_const(16); + b.full = rfixed_div(b, a); + a.full = rfixed_mul(wm1.worst_case_latency, + wm1.consumption_rate); + priority_mark12.full = a.full + b.full; + } else { + a.full = rfixed_mul(wm1.worst_case_latency, + wm1.consumption_rate); + b.full = rfixed_const(16 * 1000); + priority_mark12.full = rfixed_div(a, b); + } + if (wm0.priority_mark.full > priority_mark02.full) + priority_mark02.full = wm0.priority_mark.full; + if (rfixed_trunc(priority_mark02) < 0) + priority_mark02.full = 0; + if (wm0.priority_mark_max.full > priority_mark02.full) + priority_mark02.full = wm0.priority_mark_max.full; + if (wm1.priority_mark.full > priority_mark12.full) + priority_mark12.full = wm1.priority_mark.full; + if (rfixed_trunc(priority_mark12) < 0) + priority_mark12.full = 0; + if (wm1.priority_mark_max.full > priority_mark12.full) + priority_mark12.full = wm1.priority_mark_max.full; + WREG32(D1MODE_PRIORITY_A_CNT, rfixed_trunc(priority_mark02)); + WREG32(D1MODE_PRIORITY_B_CNT, rfixed_trunc(priority_mark02)); + WREG32(D2MODE_PRIORITY_A_CNT, rfixed_trunc(priority_mark12)); + WREG32(D2MODE_PRIORITY_B_CNT, rfixed_trunc(priority_mark12)); + } else if (mode0) { + if (rfixed_trunc(wm0.dbpp) > 64) + a.full = rfixed_div(wm0.dbpp, wm0.num_line_pair); + else + a.full = wm0.num_line_pair.full; + fill_rate.full = rfixed_div(wm0.sclk, a); + if (wm0.consumption_rate.full > fill_rate.full) { + b.full = wm0.consumption_rate.full - fill_rate.full; + b.full = rfixed_mul(b, wm0.active_time); + a.full = rfixed_const(16); + b.full = rfixed_div(b, a); + a.full = rfixed_mul(wm0.worst_case_latency, + wm0.consumption_rate); + priority_mark02.full = a.full + b.full; + } else { + a.full = rfixed_mul(wm0.worst_case_latency, + wm0.consumption_rate); + b.full = rfixed_const(16); + priority_mark02.full = rfixed_div(a, b); + } + if (wm0.priority_mark.full > priority_mark02.full) + priority_mark02.full = wm0.priority_mark.full; + if (rfixed_trunc(priority_mark02) < 0) + priority_mark02.full = 0; + if (wm0.priority_mark_max.full > priority_mark02.full) + priority_mark02.full = wm0.priority_mark_max.full; + WREG32(D1MODE_PRIORITY_A_CNT, rfixed_trunc(priority_mark02)); + WREG32(D1MODE_PRIORITY_B_CNT, rfixed_trunc(priority_mark02)); + WREG32(D2MODE_PRIORITY_A_CNT, MODE_PRIORITY_OFF); + WREG32(D2MODE_PRIORITY_B_CNT, MODE_PRIORITY_OFF); + } else { + if (rfixed_trunc(wm1.dbpp) > 64) + a.full = rfixed_div(wm1.dbpp, wm1.num_line_pair); + else + a.full = wm1.num_line_pair.full; + fill_rate.full = rfixed_div(wm1.sclk, a); + if (wm1.consumption_rate.full > fill_rate.full) { + b.full = wm1.consumption_rate.full - fill_rate.full; + b.full = rfixed_mul(b, wm1.active_time); + a.full = rfixed_const(16); + b.full = rfixed_div(b, a); + a.full = rfixed_mul(wm1.worst_case_latency, + wm1.consumption_rate); + priority_mark12.full = a.full + b.full; + } else { + a.full = rfixed_mul(wm1.worst_case_latency, + wm1.consumption_rate); + b.full = rfixed_const(16 * 1000); + priority_mark12.full = rfixed_div(a, b); + } + if (wm1.priority_mark.full > priority_mark12.full) + priority_mark12.full = wm1.priority_mark.full; + if (rfixed_trunc(priority_mark12) < 0) + priority_mark12.full = 0; + if (wm1.priority_mark_max.full > priority_mark12.full) + priority_mark12.full = wm1.priority_mark_max.full; + WREG32(D1MODE_PRIORITY_A_CNT, MODE_PRIORITY_OFF); + WREG32(D1MODE_PRIORITY_B_CNT, MODE_PRIORITY_OFF); + WREG32(D2MODE_PRIORITY_A_CNT, rfixed_trunc(priority_mark12)); + WREG32(D2MODE_PRIORITY_B_CNT, rfixed_trunc(priority_mark12)); + } +} + +void rv515_bandwidth_update(struct radeon_device *rdev) +{ + uint32_t tmp; + struct drm_display_mode *mode0 = NULL; + struct drm_display_mode *mode1 = NULL; + + if (rdev->mode_info.crtcs[0]->base.enabled) + mode0 = &rdev->mode_info.crtcs[0]->base.mode; + if (rdev->mode_info.crtcs[1]->base.enabled) + mode1 = &rdev->mode_info.crtcs[1]->base.mode; + /* + * Set display0/1 priority up in the memory controller for + * modes if the user specifies HIGH for displaypriority + * option. + */ + if (rdev->disp_priority == 2) { + tmp = RREG32_MC(MC_MISC_LAT_TIMER); + tmp &= ~MC_DISP1R_INIT_LAT_MASK; + tmp &= ~MC_DISP0R_INIT_LAT_MASK; + if (mode1) + tmp |= (1 << MC_DISP1R_INIT_LAT_SHIFT); + if (mode0) + tmp |= (1 << MC_DISP0R_INIT_LAT_SHIFT); + WREG32_MC(MC_MISC_LAT_TIMER, tmp); + } + rv515_bandwidth_avivo_update(rdev); +} diff --git a/drivers/gpu/drm/radeon/rv515r.h b/drivers/gpu/drm/radeon/rv515r.h new file mode 100644 index 00000000000..f3cf8403990 --- /dev/null +++ b/drivers/gpu/drm/radeon/rv515r.h @@ -0,0 +1,170 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ +#ifndef RV515R_H +#define RV515R_H + +/* RV515 registers */ +#define PCIE_INDEX 0x0030 +#define PCIE_DATA 0x0034 +#define MC_IND_INDEX 0x0070 +#define MC_IND_WR_EN (1 << 24) +#define MC_IND_DATA 0x0074 +#define RBBM_SOFT_RESET 0x00F0 +#define CONFIG_MEMSIZE 0x00F8 +#define HDP_FB_LOCATION 0x0134 +#define CP_CSQ_CNTL 0x0740 +#define CP_CSQ_MODE 0x0744 +#define CP_CSQ_ADDR 0x07F0 +#define CP_CSQ_DATA 0x07F4 +#define CP_CSQ_STAT 0x07F8 +#define CP_CSQ2_STAT 0x07FC +#define RBBM_STATUS 0x0E40 +#define DST_PIPE_CONFIG 0x170C +#define WAIT_UNTIL 0x1720 +#define WAIT_2D_IDLE (1 << 14) +#define WAIT_3D_IDLE (1 << 15) +#define WAIT_2D_IDLECLEAN (1 << 16) +#define WAIT_3D_IDLECLEAN (1 << 17) +#define ISYNC_CNTL 0x1724 +#define ISYNC_ANY2D_IDLE3D (1 << 0) +#define ISYNC_ANY3D_IDLE2D (1 << 1) +#define ISYNC_TRIG2D_IDLE3D (1 << 2) +#define ISYNC_TRIG3D_IDLE2D (1 << 3) +#define ISYNC_WAIT_IDLEGUI (1 << 4) +#define ISYNC_CPSCRATCH_IDLEGUI (1 << 5) +#define VAP_INDEX_OFFSET 0x208C +#define VAP_PVS_STATE_FLUSH_REG 0x2284 +#define GB_ENABLE 0x4008 +#define GB_MSPOS0 0x4010 +#define MS_X0_SHIFT 0 +#define MS_Y0_SHIFT 4 +#define MS_X1_SHIFT 8 +#define MS_Y1_SHIFT 12 +#define MS_X2_SHIFT 16 +#define MS_Y2_SHIFT 20 +#define MSBD0_Y_SHIFT 24 +#define MSBD0_X_SHIFT 28 +#define GB_MSPOS1 0x4014 +#define MS_X3_SHIFT 0 +#define MS_Y3_SHIFT 4 +#define MS_X4_SHIFT 8 +#define MS_Y4_SHIFT 12 +#define MS_X5_SHIFT 16 +#define MS_Y5_SHIFT 20 +#define MSBD1_SHIFT 24 +#define GB_TILE_CONFIG 0x4018 +#define ENABLE_TILING (1 << 0) +#define PIPE_COUNT_MASK 0x0000000E +#define PIPE_COUNT_SHIFT 1 +#define TILE_SIZE_8 (0 << 4) +#define TILE_SIZE_16 (1 << 4) +#define TILE_SIZE_32 (2 << 4) +#define SUBPIXEL_1_12 (0 << 16) +#define SUBPIXEL_1_16 (1 << 16) +#define GB_SELECT 0x401C +#define GB_AA_CONFIG 0x4020 +#define GB_PIPE_SELECT 0x402C +#define GA_ENHANCE 0x4274 +#define GA_DEADLOCK_CNTL (1 << 0) +#define GA_FASTSYNC_CNTL (1 << 1) +#define GA_POLY_MODE 0x4288 +#define FRONT_PTYPE_POINT (0 << 4) +#define FRONT_PTYPE_LINE (1 << 4) +#define FRONT_PTYPE_TRIANGE (2 << 4) +#define BACK_PTYPE_POINT (0 << 7) +#define BACK_PTYPE_LINE (1 << 7) +#define BACK_PTYPE_TRIANGE (2 << 7) +#define GA_ROUND_MODE 0x428C +#define GEOMETRY_ROUND_TRUNC (0 << 0) +#define GEOMETRY_ROUND_NEAREST (1 << 0) +#define COLOR_ROUND_TRUNC (0 << 2) +#define COLOR_ROUND_NEAREST (1 << 2) +#define SU_REG_DEST 0x42C8 +#define RB3D_DSTCACHE_CTLSTAT 0x4E4C +#define RB3D_DC_FLUSH (2 << 0) +#define RB3D_DC_FREE (2 << 2) +#define RB3D_DC_FINISH (1 << 4) +#define ZB_ZCACHE_CTLSTAT 0x4F18 +#define ZC_FLUSH (1 << 0) +#define ZC_FREE (1 << 1) +#define DC_LB_MEMORY_SPLIT 0x6520 +#define DC_LB_MEMORY_SPLIT_MASK 0x00000003 +#define DC_LB_MEMORY_SPLIT_SHIFT 0 +#define DC_LB_MEMORY_SPLIT_D1HALF_D2HALF 0 +#define DC_LB_MEMORY_SPLIT_D1_3Q_D2_1Q 1 +#define DC_LB_MEMORY_SPLIT_D1_ONLY 2 +#define DC_LB_MEMORY_SPLIT_D1_1Q_D2_3Q 3 +#define DC_LB_MEMORY_SPLIT_SHIFT_MODE (1 << 2) +#define DC_LB_DISP1_END_ADR_SHIFT 4 +#define DC_LB_DISP1_END_ADR_MASK 0x00007FF0 +#define D1MODE_PRIORITY_A_CNT 0x6548 +#define MODE_PRIORITY_MARK_MASK 0x00007FFF +#define MODE_PRIORITY_OFF (1 << 16) +#define MODE_PRIORITY_ALWAYS_ON (1 << 20) +#define MODE_PRIORITY_FORCE_MASK (1 << 24) +#define D1MODE_PRIORITY_B_CNT 0x654C +#define LB_MAX_REQ_OUTSTANDING 0x6D58 +#define LB_D1_MAX_REQ_OUTSTANDING_MASK 0x0000000F +#define LB_D1_MAX_REQ_OUTSTANDING_SHIFT 0 +#define LB_D2_MAX_REQ_OUTSTANDING_MASK 0x000F0000 +#define LB_D2_MAX_REQ_OUTSTANDING_SHIFT 16 +#define D2MODE_PRIORITY_A_CNT 0x6D48 +#define D2MODE_PRIORITY_B_CNT 0x6D4C + +/* ix[MC] registers */ +#define MC_FB_LOCATION 0x01 +#define MC_FB_START_MASK 0x0000FFFF +#define MC_FB_START_SHIFT 0 +#define MC_FB_TOP_MASK 0xFFFF0000 +#define MC_FB_TOP_SHIFT 16 +#define MC_AGP_LOCATION 0x02 +#define MC_AGP_START_MASK 0x0000FFFF +#define MC_AGP_START_SHIFT 0 +#define MC_AGP_TOP_MASK 0xFFFF0000 +#define MC_AGP_TOP_SHIFT 16 +#define MC_AGP_BASE 0x03 +#define MC_AGP_BASE_2 0x04 +#define MC_CNTL 0x5 +#define MEM_NUM_CHANNELS_MASK 0x00000003 +#define MC_STATUS 0x08 +#define MC_STATUS_IDLE (1 << 4) +#define MC_MISC_LAT_TIMER 0x09 +#define MC_CPR_INIT_LAT_MASK 0x0000000F +#define MC_VF_INIT_LAT_MASK 0x000000F0 +#define MC_DISP0R_INIT_LAT_MASK 0x00000F00 +#define MC_DISP0R_INIT_LAT_SHIFT 8 +#define MC_DISP1R_INIT_LAT_MASK 0x0000F000 +#define MC_DISP1R_INIT_LAT_SHIFT 12 +#define MC_FIXED_INIT_LAT_MASK 0x000F0000 +#define MC_E2R_INIT_LAT_MASK 0x00F00000 +#define SAME_PAGE_PRIO_MASK 0x0F000000 +#define MC_GLOBW_INIT_LAT_MASK 0xF0000000 + + +#endif + diff --git a/drivers/gpu/drm/radeon/rv770.c b/drivers/gpu/drm/radeon/rv770.c index da50cc51ede..21d8ffd5730 100644 --- a/drivers/gpu/drm/radeon/rv770.c +++ b/drivers/gpu/drm/radeon/rv770.c @@ -67,7 +67,7 @@ int rv770_mc_init(struct radeon_device *rdev) "programming pipes. Bad things might happen.\n"); } - tmp = rdev->mc.vram_location + rdev->mc.vram_size - 1; + tmp = rdev->mc.vram_location + rdev->mc.mc_vram_size - 1; tmp = REG_SET(R700_MC_FB_TOP, tmp >> 24); tmp |= REG_SET(R700_MC_FB_BASE, rdev->mc.vram_location >> 24); WREG32(R700_MC_VM_FB_LOCATION, tmp); diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index c1c407f7cca..c2b0d710d10 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -43,7 +43,6 @@ #define TTM_BO_HASH_ORDER 13 static int ttm_bo_setup_vm(struct ttm_buffer_object *bo); -static void ttm_bo_unmap_virtual(struct ttm_buffer_object *bo); static int ttm_bo_swapout(struct ttm_mem_shrink *shrink); static inline uint32_t ttm_bo_type_flags(unsigned type) @@ -224,6 +223,9 @@ static int ttm_bo_add_ttm(struct ttm_buffer_object *bo, bool zero_alloc) TTM_ASSERT_LOCKED(&bo->mutex); bo->ttm = NULL; + if (bdev->need_dma32) + page_flags |= TTM_PAGE_FLAG_DMA32; + switch (bo->type) { case ttm_bo_type_device: if (zero_alloc) @@ -304,6 +306,9 @@ static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo, } + if (bdev->driver->move_notify) + bdev->driver->move_notify(bo, mem); + if (!(old_man->flags & TTM_MEMTYPE_FLAG_FIXED) && !(new_man->flags & TTM_MEMTYPE_FLAG_FIXED)) ret = ttm_bo_move_ttm(bo, evict, no_wait, mem); @@ -655,31 +660,52 @@ retry_pre_get: return 0; } +static uint32_t ttm_bo_select_caching(struct ttm_mem_type_manager *man, + uint32_t cur_placement, + uint32_t proposed_placement) +{ + uint32_t caching = proposed_placement & TTM_PL_MASK_CACHING; + uint32_t result = proposed_placement & ~TTM_PL_MASK_CACHING; + + /** + * Keep current caching if possible. + */ + + if ((cur_placement & caching) != 0) + result |= (cur_placement & caching); + else if ((man->default_caching & caching) != 0) + result |= man->default_caching; + else if ((TTM_PL_FLAG_CACHED & caching) != 0) + result |= TTM_PL_FLAG_CACHED; + else if ((TTM_PL_FLAG_WC & caching) != 0) + result |= TTM_PL_FLAG_WC; + else if ((TTM_PL_FLAG_UNCACHED & caching) != 0) + result |= TTM_PL_FLAG_UNCACHED; + + return result; +} + + static bool ttm_bo_mt_compatible(struct ttm_mem_type_manager *man, bool disallow_fixed, uint32_t mem_type, - uint32_t mask, uint32_t *res_mask) + uint32_t proposed_placement, + uint32_t *masked_placement) { uint32_t cur_flags = ttm_bo_type_flags(mem_type); if ((man->flags & TTM_MEMTYPE_FLAG_FIXED) && disallow_fixed) return false; - if ((cur_flags & mask & TTM_PL_MASK_MEM) == 0) + if ((cur_flags & proposed_placement & TTM_PL_MASK_MEM) == 0) return false; - if ((mask & man->available_caching) == 0) + if ((proposed_placement & man->available_caching) == 0) return false; - if (mask & man->default_caching) - cur_flags |= man->default_caching; - else if (mask & TTM_PL_FLAG_CACHED) - cur_flags |= TTM_PL_FLAG_CACHED; - else if (mask & TTM_PL_FLAG_WC) - cur_flags |= TTM_PL_FLAG_WC; - else - cur_flags |= TTM_PL_FLAG_UNCACHED; - *res_mask = cur_flags; + cur_flags |= (proposed_placement & man->available_caching); + + *masked_placement = cur_flags; return true; } @@ -723,6 +749,9 @@ int ttm_bo_mem_space(struct ttm_buffer_object *bo, if (!type_ok) continue; + cur_flags = ttm_bo_select_caching(man, bo->mem.placement, + cur_flags); + if (mem_type == TTM_PL_SYSTEM) break; @@ -779,6 +808,9 @@ int ttm_bo_mem_space(struct ttm_buffer_object *bo, proposed_placement, &cur_flags)) continue; + cur_flags = ttm_bo_select_caching(man, bo->mem.placement, + cur_flags); + ret = ttm_bo_mem_force_space(bdev, mem, mem_type, interruptible, no_wait); @@ -1150,13 +1182,14 @@ static int ttm_bo_force_list_clean(struct ttm_bo_device *bdev, int ttm_bo_clean_mm(struct ttm_bo_device *bdev, unsigned mem_type) { - struct ttm_mem_type_manager *man = &bdev->man[mem_type]; + struct ttm_mem_type_manager *man; int ret = -EINVAL; if (mem_type >= TTM_NUM_MEM_TYPES) { printk(KERN_ERR TTM_PFX "Illegal memory type %d\n", mem_type); return ret; } + man = &bdev->man[mem_type]; if (!man->has_type) { printk(KERN_ERR TTM_PFX "Trying to take down uninitialized " @@ -1305,7 +1338,8 @@ EXPORT_SYMBOL(ttm_bo_device_release); int ttm_bo_device_init(struct ttm_bo_device *bdev, struct ttm_mem_global *mem_glob, - struct ttm_bo_driver *driver, uint64_t file_page_offset) + struct ttm_bo_driver *driver, uint64_t file_page_offset, + bool need_dma32) { int ret = -EINVAL; @@ -1342,6 +1376,7 @@ int ttm_bo_device_init(struct ttm_bo_device *bdev, INIT_LIST_HEAD(&bdev->ddestroy); INIT_LIST_HEAD(&bdev->swap_lru); bdev->dev_mapping = NULL; + bdev->need_dma32 = need_dma32; ttm_mem_init_shrink(&bdev->shrink, ttm_bo_swapout); ret = ttm_mem_register_shrink(mem_glob, &bdev->shrink); if (unlikely(ret != 0)) { @@ -1419,6 +1454,7 @@ void ttm_bo_unmap_virtual(struct ttm_buffer_object *bo) unmap_mapping_range(bdev->dev_mapping, offset, holelen, 1); } +EXPORT_SYMBOL(ttm_bo_unmap_virtual); static void ttm_bo_vm_insert_rb(struct ttm_buffer_object *bo) { @@ -1540,6 +1576,10 @@ int ttm_bo_wait(struct ttm_buffer_object *bo, driver->sync_obj_unref(&sync_obj); driver->sync_obj_unref(&tmp_obj); spin_lock(&bo->lock); + } else { + spin_unlock(&bo->lock); + driver->sync_obj_unref(&sync_obj); + spin_lock(&bo->lock); } } return 0; diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c index bdec583901e..ad4ada07c6c 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_util.c +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c @@ -136,7 +136,8 @@ static int ttm_copy_io_page(void *dst, void *src, unsigned long page) } static int ttm_copy_io_ttm_page(struct ttm_tt *ttm, void *src, - unsigned long page) + unsigned long page, + pgprot_t prot) { struct page *d = ttm_tt_get_page(ttm, page); void *dst; @@ -145,17 +146,35 @@ static int ttm_copy_io_ttm_page(struct ttm_tt *ttm, void *src, return -ENOMEM; src = (void *)((unsigned long)src + (page << PAGE_SHIFT)); - dst = kmap(d); + +#ifdef CONFIG_X86 + dst = kmap_atomic_prot(d, KM_USER0, prot); +#else + if (pgprot_val(prot) != pgprot_val(PAGE_KERNEL)) + dst = vmap(&d, 1, 0, prot); + else + dst = kmap(d); +#endif if (!dst) return -ENOMEM; memcpy_fromio(dst, src, PAGE_SIZE); - kunmap(d); + +#ifdef CONFIG_X86 + kunmap_atomic(dst, KM_USER0); +#else + if (pgprot_val(prot) != pgprot_val(PAGE_KERNEL)) + vunmap(dst); + else + kunmap(d); +#endif + return 0; } static int ttm_copy_ttm_io_page(struct ttm_tt *ttm, void *dst, - unsigned long page) + unsigned long page, + pgprot_t prot) { struct page *s = ttm_tt_get_page(ttm, page); void *src; @@ -164,12 +183,28 @@ static int ttm_copy_ttm_io_page(struct ttm_tt *ttm, void *dst, return -ENOMEM; dst = (void *)((unsigned long)dst + (page << PAGE_SHIFT)); - src = kmap(s); +#ifdef CONFIG_X86 + src = kmap_atomic_prot(s, KM_USER0, prot); +#else + if (pgprot_val(prot) != pgprot_val(PAGE_KERNEL)) + src = vmap(&s, 1, 0, prot); + else + src = kmap(s); +#endif if (!src) return -ENOMEM; memcpy_toio(dst, src, PAGE_SIZE); - kunmap(s); + +#ifdef CONFIG_X86 + kunmap_atomic(src, KM_USER0); +#else + if (pgprot_val(prot) != pgprot_val(PAGE_KERNEL)) + vunmap(src); + else + kunmap(s); +#endif + return 0; } @@ -214,11 +249,17 @@ int ttm_bo_move_memcpy(struct ttm_buffer_object *bo, for (i = 0; i < new_mem->num_pages; ++i) { page = i * dir + add; - if (old_iomap == NULL) - ret = ttm_copy_ttm_io_page(ttm, new_iomap, page); - else if (new_iomap == NULL) - ret = ttm_copy_io_ttm_page(ttm, old_iomap, page); - else + if (old_iomap == NULL) { + pgprot_t prot = ttm_io_prot(old_mem->placement, + PAGE_KERNEL); + ret = ttm_copy_ttm_io_page(ttm, new_iomap, page, + prot); + } else if (new_iomap == NULL) { + pgprot_t prot = ttm_io_prot(new_mem->placement, + PAGE_KERNEL); + ret = ttm_copy_io_ttm_page(ttm, old_iomap, page, + prot); + } else ret = ttm_copy_io_page(new_iomap, old_iomap, page); if (ret) goto out1; @@ -509,8 +550,8 @@ int ttm_bo_move_accel_cleanup(struct ttm_buffer_object *bo, if (evict) { ret = ttm_bo_wait(bo, false, false, false); spin_unlock(&bo->lock); - driver->sync_obj_unref(&bo->sync_obj); - + if (tmp_obj) + driver->sync_obj_unref(&tmp_obj); if (ret) return ret; @@ -532,6 +573,8 @@ int ttm_bo_move_accel_cleanup(struct ttm_buffer_object *bo, set_bit(TTM_BO_PRIV_FLAG_MOVING, &bo->priv_flags); spin_unlock(&bo->lock); + if (tmp_obj) + driver->sync_obj_unref(&tmp_obj); ret = ttm_buffer_object_transfer(bo, &ghost_obj); if (ret) diff --git a/drivers/gpu/drm/ttm/ttm_bo_vm.c b/drivers/gpu/drm/ttm/ttm_bo_vm.c index 40b75032ea4..33de7637c0c 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_vm.c +++ b/drivers/gpu/drm/ttm/ttm_bo_vm.c @@ -101,6 +101,9 @@ static int ttm_bo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) return VM_FAULT_NOPAGE; } + if (bdev->driver->fault_reserve_notify) + bdev->driver->fault_reserve_notify(bo); + /* * Wait for buffer data in transit, due to a pipelined * move. @@ -327,7 +330,7 @@ ssize_t ttm_bo_io(struct ttm_bo_device *bdev, struct file *filp, goto out_unref; kmap_offset = dev_offset - bo->vm_node->start; - if (unlikely(kmap_offset) >= bo->num_pages) { + if (unlikely(kmap_offset >= bo->num_pages)) { ret = -EFBIG; goto out_unref; } @@ -401,7 +404,7 @@ ssize_t ttm_bo_fbdev_io(struct ttm_buffer_object *bo, const char __user *wbuf, bool dummy; kmap_offset = (*f_pos >> PAGE_SHIFT); - if (unlikely(kmap_offset) >= bo->num_pages) + if (unlikely(kmap_offset >= bo->num_pages)) return -EFBIG; page_offset = *f_pos & ~PAGE_MASK; diff --git a/drivers/gpu/drm/ttm/ttm_tt.c b/drivers/gpu/drm/ttm/ttm_tt.c index 75dc8bd2459..b8b6c4a5f98 100644 --- a/drivers/gpu/drm/ttm/ttm_tt.c +++ b/drivers/gpu/drm/ttm/ttm_tt.c @@ -86,10 +86,16 @@ void ttm_tt_cache_flush(struct page *pages[], unsigned long num_pages) unsigned long i; for (i = 0; i < num_pages; ++i) { - if (pages[i]) { - unsigned long start = (unsigned long)page_address(pages[i]); - flush_dcache_range(start, start + PAGE_SIZE); - } + struct page *page = pages[i]; + void *page_virtual; + + if (unlikely(page == NULL)) + continue; + + page_virtual = kmap_atomic(page, KM_USER0); + flush_dcache_range((unsigned long) page_virtual, + (unsigned long) page_virtual + PAGE_SIZE); + kunmap_atomic(page_virtual, KM_USER0); } #else if (on_each_cpu(ttm_tt_ipi_handler, NULL, 1) != 0) @@ -131,10 +137,17 @@ static void ttm_tt_free_page_directory(struct ttm_tt *ttm) static struct page *ttm_tt_alloc_page(unsigned page_flags) { + gfp_t gfp_flags = GFP_USER; + if (page_flags & TTM_PAGE_FLAG_ZERO_ALLOC) - return alloc_page(GFP_HIGHUSER | __GFP_ZERO); + gfp_flags |= __GFP_ZERO; + + if (page_flags & TTM_PAGE_FLAG_DMA32) + gfp_flags |= __GFP_DMA32; + else + gfp_flags |= __GFP_HIGHMEM; - return alloc_page(GFP_HIGHUSER); + return alloc_page(gfp_flags); } static void ttm_tt_free_user_pages(struct ttm_tt *ttm) diff --git a/drivers/gpu/drm/via/via_irq.c b/drivers/gpu/drm/via/via_irq.c index c248c1d3726..5935b8842e8 100644 --- a/drivers/gpu/drm/via/via_irq.c +++ b/drivers/gpu/drm/via/via_irq.c @@ -183,7 +183,7 @@ int via_enable_vblank(struct drm_device *dev, int crtc) } status = VIA_READ(VIA_REG_INTERRUPT); - VIA_WRITE(VIA_REG_INTERRUPT, status & VIA_IRQ_VBLANK_ENABLE); + VIA_WRITE(VIA_REG_INTERRUPT, status | VIA_IRQ_VBLANK_ENABLE); VIA_WRITE8(0x83d4, 0x11); VIA_WRITE8(0x83d5, VIA_READ8(0x83d5) | 0x30); @@ -194,6 +194,10 @@ int via_enable_vblank(struct drm_device *dev, int crtc) void via_disable_vblank(struct drm_device *dev, int crtc) { drm_via_private_t *dev_priv = dev->dev_private; + u32 status; + + status = VIA_READ(VIA_REG_INTERRUPT); + VIA_WRITE(VIA_REG_INTERRUPT, status & ~VIA_IRQ_VBLANK_ENABLE); VIA_WRITE8(0x83d4, 0x11); VIA_WRITE8(0x83d5, VIA_READ8(0x83d5) & ~0x30); diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index f2c21d5d24e..5eb10c2ce66 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1075,14 +1075,16 @@ EXPORT_SYMBOL_GPL(hid_report_raw_event); */ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int interrupt) { - struct hid_report_enum *report_enum = hid->report_enum + type; - struct hid_driver *hdrv = hid->driver; + struct hid_report_enum *report_enum; + struct hid_driver *hdrv; struct hid_report *report; unsigned int i; int ret; if (!hid || !hid->driver) return -ENODEV; + report_enum = hid->report_enum + type; + hdrv = hid->driver; if (!size) { dbg_hid("empty report\n"); diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c index 9e9421525fb..215b2addddb 100644 --- a/drivers/hid/usbhid/hiddev.c +++ b/drivers/hid/usbhid/hiddev.c @@ -527,8 +527,10 @@ static noinline int hiddev_ioctl_usage(struct hiddev *hiddev, unsigned int cmd, goto goodreturn; case HIDIOCGCOLLECTIONINDEX: + i = field->usage[uref->usage_index].collection_index; + unlock_kernel(); kfree(uref_multi); - return field->usage[uref->usage_index].collection_index; + return i; case HIDIOCGUSAGES: for (i = 0; i < uref_multi->num_values; i++) uref_multi->values[i] = diff --git a/drivers/hwmon/asus_atk0110.c b/drivers/hwmon/asus_atk0110.c index bff0103610c..fe4fa29c921 100644 --- a/drivers/hwmon/asus_atk0110.c +++ b/drivers/hwmon/asus_atk0110.c @@ -593,7 +593,11 @@ static int atk_add_sensor(struct atk_data *data, union acpi_object *obj) sensor->data = data; sensor->id = flags->integer.value; sensor->limit1 = limit1->integer.value; - sensor->limit2 = limit2->integer.value; + if (data->old_interface) + sensor->limit2 = limit2->integer.value; + else + /* The upper limit is expressed as delta from lower limit */ + sensor->limit2 = sensor->limit1 + limit2->integer.value; snprintf(sensor->input_attr_name, ATTR_NAME_SIZE, "%s%d_input", base_name, start + *num); diff --git a/drivers/hwmon/smsc47m1.c b/drivers/hwmon/smsc47m1.c index a92dbb97ee9..ba75bfcf14c 100644 --- a/drivers/hwmon/smsc47m1.c +++ b/drivers/hwmon/smsc47m1.c @@ -86,6 +86,7 @@ superio_exit(void) #define SUPERIO_REG_ACT 0x30 #define SUPERIO_REG_BASE 0x60 #define SUPERIO_REG_DEVID 0x20 +#define SUPERIO_REG_DEVREV 0x21 /* Logical device registers */ @@ -429,6 +430,9 @@ static int __init smsc47m1_find(unsigned short *addr, * The LPC47M292 (device id 0x6B) is somewhat compatible, but it * supports a 3rd fan, and the pin configuration registers are * unfortunately different. + * The LPC47M233 has the same device id (0x6B) but is not compatible. + * We check the high bit of the device revision register to + * differentiate them. */ switch (val) { case 0x51: @@ -448,6 +452,13 @@ static int __init smsc47m1_find(unsigned short *addr, sio_data->type = smsc47m1; break; case 0x6B: + if (superio_inb(SUPERIO_REG_DEVREV) & 0x80) { + pr_debug(DRVNAME ": " + "Found SMSC LPC47M233, unsupported\n"); + superio_exit(); + return -ENODEV; + } + pr_info(DRVNAME ": Found SMSC LPC47M292\n"); sio_data->type = smsc47m2; break; diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index fdd83277c8a..d258b02aef4 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -672,9 +672,10 @@ omap_i2c_isr(int this_irq, void *dev_id) break; } + err = 0; +complete: omap_i2c_write_reg(dev, OMAP_I2C_STAT_REG, stat); - err = 0; if (stat & OMAP_I2C_STAT_NACK) { err |= OMAP_I2C_STAT_NACK; omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, @@ -685,16 +686,19 @@ omap_i2c_isr(int this_irq, void *dev_id) err |= OMAP_I2C_STAT_AL; } if (stat & (OMAP_I2C_STAT_ARDY | OMAP_I2C_STAT_NACK | - OMAP_I2C_STAT_AL)) + OMAP_I2C_STAT_AL)) { omap_i2c_complete_cmd(dev, err); + return IRQ_HANDLED; + } if (stat & (OMAP_I2C_STAT_RRDY | OMAP_I2C_STAT_RDR)) { u8 num_bytes = 1; if (dev->fifo_size) { if (stat & OMAP_I2C_STAT_RRDY) num_bytes = dev->fifo_size; - else - num_bytes = omap_i2c_read_reg(dev, - OMAP_I2C_BUFSTAT_REG); + else /* read RXSTAT on RDR interrupt */ + num_bytes = (omap_i2c_read_reg(dev, + OMAP_I2C_BUFSTAT_REG) + >> 8) & 0x3F; } while (num_bytes) { num_bytes--; @@ -731,9 +735,10 @@ omap_i2c_isr(int this_irq, void *dev_id) if (dev->fifo_size) { if (stat & OMAP_I2C_STAT_XRDY) num_bytes = dev->fifo_size; - else + else /* read TXSTAT on XDR interrupt */ num_bytes = omap_i2c_read_reg(dev, - OMAP_I2C_BUFSTAT_REG); + OMAP_I2C_BUFSTAT_REG) + & 0x3F; } while (num_bytes) { num_bytes--; @@ -760,6 +765,27 @@ omap_i2c_isr(int this_irq, void *dev_id) "data to send\n"); break; } + + /* + * OMAP3430 Errata 1.153: When an XRDY/XDR + * is hit, wait for XUDF before writing data + * to DATA_REG. Otherwise some data bytes can + * be lost while transferring them from the + * memory to the I2C interface. + */ + + if (cpu_is_omap34xx()) { + while (!(stat & OMAP_I2C_STAT_XUDF)) { + if (stat & (OMAP_I2C_STAT_NACK | OMAP_I2C_STAT_AL)) { + omap_i2c_ack_stat(dev, stat & (OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR)); + err |= OMAP_I2C_STAT_XUDF; + goto complete; + } + cpu_relax(); + stat = omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG); + } + } + omap_i2c_write_reg(dev, OMAP_I2C_DATA_REG, w); } omap_i2c_ack_stat(dev, @@ -879,7 +905,7 @@ omap_i2c_probe(struct platform_device *pdev) i2c_set_adapdata(adap, dev); adap->owner = THIS_MODULE; adap->class = I2C_CLASS_HWMON; - strncpy(adap->name, "OMAP I2C adapter", sizeof(adap->name)); + strlcpy(adap->name, "OMAP I2C adapter", sizeof(adap->name)); adap->algo = &omap_i2c_algo; adap->dev.parent = &pdev->dev; diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index 8f42a4536cd..20bb0ceb027 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -763,11 +763,6 @@ static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c) dev_info(i2c->dev, "bus frequency set to %d KHz\n", freq); dev_dbg(i2c->dev, "S3C2410_IICCON=0x%02lx\n", iicon); - /* check for s3c2440 i2c controller */ - - if (s3c24xx_i2c_is2440(i2c)) - writel(0x0, i2c->regs + S3C2440_IICLC); - return 0; } diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index 4f3d99cd169..820487d0d5c 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -637,7 +637,7 @@ static void __exit sh_mobile_i2c_adap_exit(void) platform_driver_unregister(&sh_mobile_i2c_driver); } -module_init(sh_mobile_i2c_adap_init); +subsys_initcall(sh_mobile_i2c_adap_init); module_exit(sh_mobile_i2c_adap_exit); MODULE_DESCRIPTION("SuperH Mobile I2C Bus Controller driver"); diff --git a/drivers/i2c/chips/tsl2550.c b/drivers/i2c/chips/tsl2550.c index 1a9cc135219..b96f3025e58 100644 --- a/drivers/i2c/chips/tsl2550.c +++ b/drivers/i2c/chips/tsl2550.c @@ -27,7 +27,7 @@ #include <linux/delay.h> #define TSL2550_DRV_NAME "tsl2550" -#define DRIVER_VERSION "1.1.1" +#define DRIVER_VERSION "1.1.2" /* * Defines @@ -189,13 +189,16 @@ static int tsl2550_calculate_lux(u8 ch0, u8 ch1) u8 r = 128; /* Avoid division by 0 and count 1 cannot be greater than count 0 */ - if (c0 && (c1 <= c0)) - r = c1 * 128 / c0; + if (c1 <= c0) + if (c0) { + r = c1 * 128 / c0; + + /* Calculate LUX */ + lux = ((c0 - c1) * ratio_lut[r]) / 256; + } else + lux = 0; else - return -1; - - /* Calculate LUX */ - lux = ((c0 - c1) * ratio_lut[r]) / 256; + return -EAGAIN; /* LUX range check */ return lux > TSL2550_MAX_LUX ? TSL2550_MAX_LUX : lux; diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index 695181120cd..7f878017b73 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -455,6 +455,7 @@ static void idedisk_prepare_flush(struct request_queue *q, struct request *rq) rq->cmd_type = REQ_TYPE_ATA_TASKFILE; rq->special = cmd; + cmd->rq = rq; } ide_devset_get(multcount, mult_count); diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index 013dc595fab..bc5fb12b913 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -1064,6 +1064,7 @@ static int idetape_blkdev_ioctl(ide_drive_t *drive, unsigned int cmd, tape->best_dsc_rw_freq = config.dsc_rw_frequency; break; case 0x0350: + memset(&config, 0, sizeof(config)); config.dsc_rw_frequency = (int) tape->best_dsc_rw_freq; config.nr_stages = 1; if (copy_to_user(argp, &config, sizeof(config))) diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 114efd8dc8f..1148140d08a 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -608,8 +608,7 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, p, compat_mode); if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0))) - return str_to_user(dev_name(&evdev->dev), - _IOC_SIZE(cmd), p); + return str_to_user(dev->name, _IOC_SIZE(cmd), p); if (_IOC_NR(cmd) == _IOC_NR(EVIOCGPHYS(0))) return str_to_user(dev->phys, _IOC_SIZE(cmd), p); diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c index 0e12f89276a..4cfd084fa89 100644 --- a/drivers/input/joydev.c +++ b/drivers/input/joydev.c @@ -536,7 +536,7 @@ static int joydev_ioctl_common(struct joydev *joydev, default: if ((cmd & ~IOCSIZE_MASK) == JSIOCGNAME(0)) { int len; - const char *name = dev_name(&dev->dev); + const char *name = dev->name; if (!name) return 0; diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index b868b8d5fbb..f155ad8cdae 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -470,20 +470,20 @@ static void xpad_irq_out(struct urb *urb) status = urb->status; switch (status) { - case 0: + case 0: /* success */ - break; - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - /* this urb is terminated, clean up */ - dbg("%s - urb shutting down with status: %d", - __func__, status); - return; - default: - dbg("%s - nonzero urb status received: %d", - __func__, status); - goto exit; + return; + + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", __func__, status); + return; + + default: + dbg("%s - nonzero urb status received: %d", __func__, status); + goto exit; } exit: diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index df3f8aa6811..95fe0452dae 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -895,6 +895,13 @@ static unsigned int atkbd_amilo_pa1510_forced_release_keys[] = { }; /* + * Amilo Pi 3525 key release for Fn+Volume keys not working + */ +static unsigned int atkbd_amilo_pi3525_forced_release_keys[] = { + 0x20, 0xa0, 0x2e, 0xae, 0x30, 0xb0, -1U +}; + +/* * Amilo Xi 3650 key release for light touch bar not working */ static unsigned int atkbd_amilo_xi3650_forced_release_keys[] = { @@ -902,6 +909,13 @@ static unsigned int atkbd_amilo_xi3650_forced_release_keys[] = { }; /* + * Soltech TA12 system with broken key release on volume keys and mute key + */ +static unsigned int atkdb_soltech_ta12_forced_release_keys[] = { + 0xa0, 0xae, 0xb0, -1U +}; + +/* * atkbd_set_keycode_table() initializes keyboard's keycode table * according to the selected scancode set */ @@ -1568,6 +1582,15 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { .driver_data = atkbd_amilo_pa1510_forced_release_keys, }, { + .ident = "Fujitsu Amilo Pi 3525", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pi 3525"), + }, + .callback = atkbd_setup_forced_release, + .driver_data = atkbd_amilo_pi3525_forced_release_keys, + }, + { .ident = "Fujitsu Amilo Xi 3650", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), @@ -1576,6 +1599,15 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { .callback = atkbd_setup_forced_release, .driver_data = atkbd_amilo_xi3650_forced_release_keys, }, + { + .ident = "Soltech Corporation TA12", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Soltech Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "TA12"), + }, + .callback = atkbd_setup_forced_release, + .driver_data = atkdb_soltech_ta12_forced_release_keys, + }, { } }; diff --git a/drivers/input/misc/pcspkr.c b/drivers/input/misc/pcspkr.c index 6d67af5387a..21cb755a54f 100644 --- a/drivers/input/misc/pcspkr.c +++ b/drivers/input/misc/pcspkr.c @@ -114,7 +114,7 @@ static int __devexit pcspkr_remove(struct platform_device *dev) return 0; } -static int pcspkr_suspend(struct platform_device *dev, pm_message_t state) +static int pcspkr_suspend(struct device *dev) { pcspkr_event(NULL, EV_SND, SND_BELL, 0); @@ -127,14 +127,18 @@ static void pcspkr_shutdown(struct platform_device *dev) pcspkr_event(NULL, EV_SND, SND_BELL, 0); } +static struct dev_pm_ops pcspkr_pm_ops = { + .suspend = pcspkr_suspend, +}; + static struct platform_driver pcspkr_platform_driver = { .driver = { .name = "pcspkr", .owner = THIS_MODULE, + .pm = &pcspkr_pm_ops, }, .probe = pcspkr_probe, .remove = __devexit_p(pcspkr_remove), - .suspend = pcspkr_suspend, .shutdown = pcspkr_shutdown, }; diff --git a/drivers/input/misc/wistron_btns.c b/drivers/input/misc/wistron_btns.c index 7c8957dd22c..26e17a9a22e 100644 --- a/drivers/input/misc/wistron_btns.c +++ b/drivers/input/misc/wistron_btns.c @@ -646,6 +646,15 @@ static struct dmi_system_id dmi_ids[] __initdata = { }, { .callback = dmi_matched, + .ident = "Maxdata Pro 7000 DX", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "MAXDATA"), + DMI_MATCH(DMI_PRODUCT_NAME, "Pro 7000"), + }, + .driver_data = keymap_fs_amilo_pro_v2000 + }, + { + .callback = dmi_matched, .ident = "Fujitsu N3510", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), diff --git a/drivers/input/serio/hp_sdc_mlc.c b/drivers/input/serio/hp_sdc_mlc.c index b587e2d576a..820e51673b2 100644 --- a/drivers/input/serio/hp_sdc_mlc.c +++ b/drivers/input/serio/hp_sdc_mlc.c @@ -296,7 +296,7 @@ static void hp_sdc_mlc_out(hil_mlc *mlc) priv->tseq[3] = 0; if (mlc->opacket & HIL_CTRL_APE) { priv->tseq[3] |= HP_SDC_LPC_APE_IPF; - down_trylock(&mlc->csem); + BUG_ON(down_trylock(&mlc->csem)); } enqueue: hp_sdc_enqueue_transaction(&priv->trans); diff --git a/drivers/isdn/gigaset/interface.c b/drivers/isdn/gigaset/interface.c index 1ebfcab7466..8ff7e35c706 100644 --- a/drivers/isdn/gigaset/interface.c +++ b/drivers/isdn/gigaset/interface.c @@ -408,6 +408,8 @@ static int if_write_room(struct tty_struct *tty) return retval; } +/* FIXME: This function does not have error returns */ + static int if_chars_in_buffer(struct tty_struct *tty) { struct cardstate *cs; diff --git a/drivers/isdn/mISDN/l1oip_core.c b/drivers/isdn/mISDN/l1oip_core.c index 990e6a7e667..c3b661a666c 100644 --- a/drivers/isdn/mISDN/l1oip_core.c +++ b/drivers/isdn/mISDN/l1oip_core.c @@ -731,10 +731,10 @@ l1oip_socket_thread(void *data) while (!signal_pending(current)) { struct kvec iov = { .iov_base = recvbuf, - .iov_len = sizeof(recvbuf), + .iov_len = recvbuf_size, }; recvlen = kernel_recvmsg(socket, &msg, &iov, 1, - sizeof(recvbuf), 0); + recvbuf_size, 0); if (recvlen > 0) { l1oip_socket_parse(hc, &sin_rx, recvbuf, recvlen); } else { diff --git a/drivers/lguest/core.c b/drivers/lguest/core.c index a6974e9b8eb..1e2cb846b3c 100644 --- a/drivers/lguest/core.c +++ b/drivers/lguest/core.c @@ -1,6 +1,8 @@ -/*P:400 This contains run_guest() which actually calls into the Host<->Guest +/*P:400 + * This contains run_guest() which actually calls into the Host<->Guest * Switcher and analyzes the return, such as determining if the Guest wants the - * Host to do something. This file also contains useful helper routines. :*/ + * Host to do something. This file also contains useful helper routines. +:*/ #include <linux/module.h> #include <linux/stringify.h> #include <linux/stddef.h> @@ -24,7 +26,8 @@ static struct page **switcher_page; /* This One Big lock protects all inter-guest data structures. */ DEFINE_MUTEX(lguest_lock); -/*H:010 We need to set up the Switcher at a high virtual address. Remember the +/*H:010 + * We need to set up the Switcher at a high virtual address. Remember the * Switcher is a few hundred bytes of assembler code which actually changes the * CPU to run the Guest, and then changes back to the Host when a trap or * interrupt happens. @@ -33,7 +36,8 @@ DEFINE_MUTEX(lguest_lock); * Host since it will be running as the switchover occurs. * * Trying to map memory at a particular address is an unusual thing to do, so - * it's not a simple one-liner. */ + * it's not a simple one-liner. + */ static __init int map_switcher(void) { int i, err; @@ -47,8 +51,10 @@ static __init int map_switcher(void) * easy. */ - /* We allocate an array of struct page pointers. map_vm_area() wants - * this, rather than just an array of pages. */ + /* + * We allocate an array of struct page pointers. map_vm_area() wants + * this, rather than just an array of pages. + */ switcher_page = kmalloc(sizeof(switcher_page[0])*TOTAL_SWITCHER_PAGES, GFP_KERNEL); if (!switcher_page) { @@ -56,8 +62,10 @@ static __init int map_switcher(void) goto out; } - /* Now we actually allocate the pages. The Guest will see these pages, - * so we make sure they're zeroed. */ + /* + * Now we actually allocate the pages. The Guest will see these pages, + * so we make sure they're zeroed. + */ for (i = 0; i < TOTAL_SWITCHER_PAGES; i++) { unsigned long addr = get_zeroed_page(GFP_KERNEL); if (!addr) { @@ -67,19 +75,23 @@ static __init int map_switcher(void) switcher_page[i] = virt_to_page(addr); } - /* First we check that the Switcher won't overlap the fixmap area at + /* + * First we check that the Switcher won't overlap the fixmap area at * the top of memory. It's currently nowhere near, but it could have - * very strange effects if it ever happened. */ + * very strange effects if it ever happened. + */ if (SWITCHER_ADDR + (TOTAL_SWITCHER_PAGES+1)*PAGE_SIZE > FIXADDR_START){ err = -ENOMEM; printk("lguest: mapping switcher would thwack fixmap\n"); goto free_pages; } - /* Now we reserve the "virtual memory area" we want: 0xFFC00000 + /* + * Now we reserve the "virtual memory area" we want: 0xFFC00000 * (SWITCHER_ADDR). We might not get it in theory, but in practice * it's worked so far. The end address needs +1 because __get_vm_area - * allocates an extra guard page, so we need space for that. */ + * allocates an extra guard page, so we need space for that. + */ switcher_vma = __get_vm_area(TOTAL_SWITCHER_PAGES * PAGE_SIZE, VM_ALLOC, SWITCHER_ADDR, SWITCHER_ADDR + (TOTAL_SWITCHER_PAGES+1) * PAGE_SIZE); @@ -89,11 +101,13 @@ static __init int map_switcher(void) goto free_pages; } - /* This code actually sets up the pages we've allocated to appear at + /* + * This code actually sets up the pages we've allocated to appear at * SWITCHER_ADDR. map_vm_area() takes the vma we allocated above, the * kind of pages we're mapping (kernel pages), and a pointer to our * array of struct pages. It increments that pointer, but we don't - * care. */ + * care. + */ pagep = switcher_page; err = map_vm_area(switcher_vma, PAGE_KERNEL_EXEC, &pagep); if (err) { @@ -101,8 +115,10 @@ static __init int map_switcher(void) goto free_vma; } - /* Now the Switcher is mapped at the right address, we can't fail! - * Copy in the compiled-in Switcher code (from <arch>_switcher.S). */ + /* + * Now the Switcher is mapped at the right address, we can't fail! + * Copy in the compiled-in Switcher code (from <arch>_switcher.S). + */ memcpy(switcher_vma->addr, start_switcher_text, end_switcher_text - start_switcher_text); @@ -124,8 +140,7 @@ out: } /*:*/ -/* Cleaning up the mapping when the module is unloaded is almost... - * too easy. */ +/* Cleaning up the mapping when the module is unloaded is almost... too easy. */ static void unmap_switcher(void) { unsigned int i; @@ -151,16 +166,19 @@ static void unmap_switcher(void) * But we can't trust the Guest: it might be trying to access the Launcher * 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. */ + * positive by overflowing, too. + */ 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); } -/* This routine copies memory from the Guest. Here we can see how useful the +/* + * This routine copies memory from the Guest. Here we can see how useful the * kill_lguest() routine we met in the Launcher can be: we return a random - * value (all zeroes) instead of needing to return an error. */ + * value (all zeroes) instead of needing to return an error. + */ void __lgread(struct lg_cpu *cpu, void *b, unsigned long addr, unsigned bytes) { if (!lguest_address_ok(cpu->lg, addr, bytes) @@ -181,9 +199,11 @@ void __lgwrite(struct lg_cpu *cpu, unsigned long addr, const void *b, } /*:*/ -/*H:030 Let's jump straight to the the main loop which runs the Guest. +/*H:030 + * Let's jump straight to the the main loop which runs the Guest. * Remember, this is called by the Launcher reading /dev/lguest, and we keep - * going around and around until something interesting happens. */ + * going around and around until something interesting happens. + */ int run_guest(struct lg_cpu *cpu, unsigned long __user *user) { /* We stop running once the Guest is dead. */ @@ -195,10 +215,17 @@ int run_guest(struct lg_cpu *cpu, unsigned long __user *user) if (cpu->hcall) do_hypercalls(cpu); - /* It's possible the Guest did a NOTIFY hypercall to the - * Launcher, in which case we return from the read() now. */ + /* + * It's possible the Guest did a NOTIFY hypercall to the + * Launcher. + */ if (cpu->pending_notify) { + /* + * Does it just needs to write to a registered + * eventfd (ie. the appropriate virtqueue thread)? + */ if (!send_notify_to_eventfd(cpu)) { + /* OK, we tell the main Laucher. */ if (put_user(cpu->pending_notify, user)) return -EFAULT; return sizeof(cpu->pending_notify); @@ -209,29 +236,39 @@ int run_guest(struct lg_cpu *cpu, unsigned long __user *user) if (signal_pending(current)) return -ERESTARTSYS; - /* Check if there are any interrupts which can be delivered now: + /* + * Check if there are any interrupts which can be delivered now: * if so, this sets up the hander to be executed when we next - * run the Guest. */ + * run the Guest. + */ irq = interrupt_pending(cpu, &more); if (irq < LGUEST_IRQS) try_deliver_interrupt(cpu, irq, more); - /* All long-lived kernel loops need to check with this horrible + /* + * All long-lived kernel loops need to check with this horrible * thing called the freezer. If the Host is trying to suspend, - * it stops us. */ + * it stops us. + */ try_to_freeze(); - /* Just make absolutely sure the Guest is still alive. One of - * those hypercalls could have been fatal, for example. */ + /* + * Just make absolutely sure the Guest is still alive. One of + * those hypercalls could have been fatal, for example. + */ if (cpu->lg->dead) break; - /* If the Guest asked to be stopped, we sleep. The Guest's - * clock timer will wake us. */ + /* + * If the Guest asked to be stopped, we sleep. The Guest's + * clock timer will wake us. + */ if (cpu->halted) { set_current_state(TASK_INTERRUPTIBLE); - /* Just before we sleep, make sure no interrupt snuck in - * which we should be doing. */ + /* + * Just before we sleep, make sure no interrupt snuck in + * which we should be doing. + */ if (interrupt_pending(cpu, &more) < LGUEST_IRQS) set_current_state(TASK_RUNNING); else @@ -239,8 +276,10 @@ int run_guest(struct lg_cpu *cpu, unsigned long __user *user) continue; } - /* OK, now we're ready to jump into the Guest. First we put up - * the "Do Not Disturb" sign: */ + /* + * OK, now we're ready to jump into the Guest. First we put up + * the "Do Not Disturb" sign: + */ local_irq_disable(); /* Actually run the Guest until something happens. */ @@ -327,8 +366,10 @@ static void __exit fini(void) } /*:*/ -/* The Host side of lguest can be a module. This is a nice way for people to - * play with it. */ +/* + * The Host side of lguest can be a module. This is a nice way for people to + * play with it. + */ module_init(init); module_exit(fini); MODULE_LICENSE("GPL"); diff --git a/drivers/lguest/hypercalls.c b/drivers/lguest/hypercalls.c index c29ffa19cb7..83511eb0923 100644 --- a/drivers/lguest/hypercalls.c +++ b/drivers/lguest/hypercalls.c @@ -1,8 +1,10 @@ -/*P:500 Just as userspace programs request kernel operations through a system +/*P:500 + * Just as userspace programs request kernel operations through a system * call, the Guest requests Host operations through a "hypercall". You might * notice this nomenclature doesn't really follow any logic, but the name has * been around for long enough that we're stuck with it. As you'd expect, this - * code is basically a one big switch statement. :*/ + * code is basically a one big switch statement. +:*/ /* Copyright (C) 2006 Rusty Russell IBM Corporation @@ -28,30 +30,41 @@ #include <asm/pgtable.h> #include "lg.h" -/*H:120 This is the core hypercall routine: where the Guest gets what it wants. - * Or gets killed. Or, in the case of LHCALL_SHUTDOWN, both. */ +/*H:120 + * This is the core hypercall routine: where the Guest gets what it wants. + * Or gets killed. Or, in the case of LHCALL_SHUTDOWN, both. + */ static void do_hcall(struct lg_cpu *cpu, struct hcall_args *args) { switch (args->arg0) { case LHCALL_FLUSH_ASYNC: - /* This call does nothing, except by breaking out of the Guest - * it makes us process all the asynchronous hypercalls. */ + /* + * This call does nothing, except by breaking out of the Guest + * it makes us process all the asynchronous hypercalls. + */ break; case LHCALL_SEND_INTERRUPTS: - /* This call does nothing too, but by breaking out of the Guest - * it makes us process any pending interrupts. */ + /* + * This call does nothing too, but by breaking out of the Guest + * it makes us process any pending interrupts. + */ break; case LHCALL_LGUEST_INIT: - /* You can't get here unless you're already initialized. Don't - * do that. */ + /* + * You can't get here unless you're already initialized. Don't + * do that. + */ kill_guest(cpu, "already have lguest_data"); break; case LHCALL_SHUTDOWN: { - /* Shutdown is such a trivial hypercall that we do it in four - * lines right here. */ char msg[128]; - /* If the lgread fails, it will call kill_guest() itself; the - * kill_guest() with the message will be ignored. */ + /* + * Shutdown is such a trivial hypercall that we do it in five + * lines right here. + * + * If the lgread fails, it will call kill_guest() itself; the + * kill_guest() with the message will be ignored. + */ __lgread(cpu, msg, args->arg1, sizeof(msg)); msg[sizeof(msg)-1] = '\0'; kill_guest(cpu, "CRASH: %s", msg); @@ -60,16 +73,17 @@ static void do_hcall(struct lg_cpu *cpu, struct hcall_args *args) break; } case LHCALL_FLUSH_TLB: - /* FLUSH_TLB comes in two flavors, depending on the - * argument: */ + /* FLUSH_TLB comes in two flavors, depending on the argument: */ if (args->arg1) guest_pagetable_clear_all(cpu); else guest_pagetable_flush_user(cpu); break; - /* All these calls simply pass the arguments through to the right - * routines. */ + /* + * All these calls simply pass the arguments through to the right + * routines. + */ case LHCALL_NEW_PGTABLE: guest_new_pagetable(cpu, args->arg1); break; @@ -112,15 +126,16 @@ static void do_hcall(struct lg_cpu *cpu, struct hcall_args *args) kill_guest(cpu, "Bad hypercall %li\n", args->arg0); } } -/*:*/ -/*H:124 Asynchronous hypercalls are easy: we just look in the array in the +/*H:124 + * Asynchronous hypercalls are easy: we just look in the array in the * Guest's "struct lguest_data" to see if any new ones are marked "ready". * * We are careful to do these in order: obviously we respect the order the * Guest put them in the ring, but we also promise the Guest that they will * happen before any normal hypercall (which is why we check this before - * checking for a normal hcall). */ + * checking for a normal hcall). + */ static void do_async_hcalls(struct lg_cpu *cpu) { unsigned int i; @@ -133,22 +148,28 @@ static void do_async_hcalls(struct lg_cpu *cpu) /* We process "struct lguest_data"s hcalls[] ring once. */ for (i = 0; i < ARRAY_SIZE(st); i++) { struct hcall_args args; - /* We remember where we were up to from last time. This makes + /* + * We remember where we were up to from last time. This makes * sure that the hypercalls are done in the order the Guest - * places them in the ring. */ + * places them in the ring. + */ unsigned int n = cpu->next_hcall; /* 0xFF means there's no call here (yet). */ if (st[n] == 0xFF) break; - /* OK, we have hypercall. Increment the "next_hcall" cursor, - * and wrap back to 0 if we reach the end. */ + /* + * OK, we have hypercall. Increment the "next_hcall" cursor, + * and wrap back to 0 if we reach the end. + */ if (++cpu->next_hcall == LHCALL_RING_SIZE) cpu->next_hcall = 0; - /* Copy the hypercall arguments into a local copy of - * the hcall_args struct. */ + /* + * Copy the hypercall arguments into a local copy of the + * hcall_args struct. + */ if (copy_from_user(&args, &cpu->lg->lguest_data->hcalls[n], sizeof(struct hcall_args))) { kill_guest(cpu, "Fetching async hypercalls"); @@ -164,19 +185,25 @@ static void do_async_hcalls(struct lg_cpu *cpu) break; } - /* Stop doing hypercalls if they want to notify the Launcher: - * it needs to service this first. */ + /* + * Stop doing hypercalls if they want to notify the Launcher: + * it needs to service this first. + */ if (cpu->pending_notify) break; } } -/* Last of all, we look at what happens first of all. The very first time the - * Guest makes a hypercall, we end up here to set things up: */ +/* + * Last of all, we look at what happens first of all. The very first time the + * Guest makes a hypercall, we end up here to set things up: + */ static void initialize(struct lg_cpu *cpu) { - /* You can't do anything until you're initialized. The Guest knows the - * rules, so we're unforgiving here. */ + /* + * You can't do anything until you're initialized. The Guest knows the + * rules, so we're unforgiving here. + */ if (cpu->hcall->arg0 != LHCALL_LGUEST_INIT) { kill_guest(cpu, "hypercall %li before INIT", cpu->hcall->arg0); return; @@ -185,32 +212,44 @@ static void initialize(struct lg_cpu *cpu) if (lguest_arch_init_hypercalls(cpu)) kill_guest(cpu, "bad guest page %p", cpu->lg->lguest_data); - /* The Guest tells us where we're not to deliver interrupts by putting - * the range of addresses into "struct lguest_data". */ + /* + * The Guest tells us where we're not to deliver interrupts by putting + * the range of addresses into "struct lguest_data". + */ if (get_user(cpu->lg->noirq_start, &cpu->lg->lguest_data->noirq_start) || get_user(cpu->lg->noirq_end, &cpu->lg->lguest_data->noirq_end)) kill_guest(cpu, "bad guest page %p", cpu->lg->lguest_data); - /* We write the current time into the Guest's data page once so it can - * set its clock. */ + /* + * We write the current time into the Guest's data page once so it can + * set its clock. + */ write_timestamp(cpu); /* page_tables.c will also do some setup. */ page_table_guest_data_init(cpu); - /* This is the one case where the above accesses might have been the + /* + * This is the one case where the above accesses might have been the * first write to a Guest page. This may have caused a copy-on-write * fault, but the old page might be (read-only) in the Guest - * pagetable. */ + * pagetable. + */ guest_pagetable_clear_all(cpu); } /*:*/ -/*M:013 If a Guest reads from a page (so creates a mapping) that it has never +/*M:013 + * If a Guest reads from a page (so creates a mapping) that it has never * written to, and then the Launcher writes to it (ie. the output of a virtual * device), the Guest will still see the old page. In practice, this never * happens: why would the Guest read a page which it has never written to? But - * a similar scenario might one day bite us, so it's worth mentioning. :*/ + * a similar scenario might one day bite us, so it's worth mentioning. + * + * Note that if we used a shared anonymous mapping in the Launcher instead of + * mapping /dev/zero private, we wouldn't worry about cop-on-write. And we + * need that to switch the Launcher to processes (away from threads) anyway. +:*/ /*H:100 * Hypercalls @@ -229,17 +268,22 @@ void do_hypercalls(struct lg_cpu *cpu) return; } - /* The Guest has initialized. + /* + * The Guest has initialized. * - * Look in the hypercall ring for the async hypercalls: */ + * Look in the hypercall ring for the async hypercalls: + */ do_async_hcalls(cpu); - /* If we stopped reading the hypercall ring because the Guest did a + /* + * If we stopped reading the hypercall ring because the Guest did a * NOTIFY to the Launcher, we want to return now. Otherwise we do - * the hypercall. */ + * the hypercall. + */ if (!cpu->pending_notify) { do_hcall(cpu, cpu->hcall); - /* Tricky point: we reset the hcall pointer to mark the + /* + * Tricky point: we reset the hcall pointer to mark the * hypercall as "done". We use the hcall pointer rather than * the trap number to indicate a hypercall is pending. * Normally it doesn't matter: the Guest will run again and @@ -248,13 +292,16 @@ void do_hypercalls(struct lg_cpu *cpu) * However, if we are signalled or the Guest sends I/O to the * Launcher, the run_guest() loop will exit without running the * Guest. When it comes back it would try to re-run the - * hypercall. Finding that bug sucked. */ + * hypercall. Finding that bug sucked. + */ cpu->hcall = NULL; } } -/* This routine supplies the Guest with time: it's used for wallclock time at - * initial boot and as a rough time source if the TSC isn't available. */ +/* + * This routine supplies the Guest with time: it's used for wallclock time at + * initial boot and as a rough time source if the TSC isn't available. + */ void write_timestamp(struct lg_cpu *cpu) { struct timespec now; diff --git a/drivers/lguest/interrupts_and_traps.c b/drivers/lguest/interrupts_and_traps.c index 0e9067b0d50..18648180db0 100644 --- a/drivers/lguest/interrupts_and_traps.c +++ b/drivers/lguest/interrupts_and_traps.c @@ -1,4 +1,5 @@ -/*P:800 Interrupts (traps) are complicated enough to earn their own file. +/*P:800 + * Interrupts (traps) are complicated enough to earn their own file. * There are three classes of interrupts: * * 1) Real hardware interrupts which occur while we're running the Guest, @@ -10,7 +11,8 @@ * just like real hardware would deliver them. Traps from the Guest can be set * up to go directly back into the Guest, but sometimes the Host wants to see * them first, so we also have a way of "reflecting" them into the Guest as if - * they had been delivered to it directly. :*/ + * they had been delivered to it directly. +:*/ #include <linux/uaccess.h> #include <linux/interrupt.h> #include <linux/module.h> @@ -26,8 +28,10 @@ static unsigned long idt_address(u32 lo, u32 hi) return (lo & 0x0000FFFF) | (hi & 0xFFFF0000); } -/* The "type" of the interrupt handler is a 4 bit field: we only support a - * couple of types. */ +/* + * The "type" of the interrupt handler is a 4 bit field: we only support a + * couple of types. + */ static int idt_type(u32 lo, u32 hi) { return (hi >> 8) & 0xF; @@ -39,8 +43,10 @@ static bool idt_present(u32 lo, u32 hi) return (hi & 0x8000); } -/* We need a helper to "push" a value onto the Guest's stack, since that's a - * big part of what delivering an interrupt does. */ +/* + * We need a helper to "push" a value onto the Guest's stack, since that's a + * big part of what delivering an interrupt does. + */ static void push_guest_stack(struct lg_cpu *cpu, unsigned long *gstack, u32 val) { /* Stack grows upwards: move stack then write value. */ @@ -48,7 +54,8 @@ static void push_guest_stack(struct lg_cpu *cpu, unsigned long *gstack, u32 val) lgwrite(cpu, *gstack, u32, val); } -/*H:210 The set_guest_interrupt() routine actually delivers the interrupt or +/*H:210 + * The set_guest_interrupt() routine actually delivers the interrupt or * trap. The mechanics of delivering traps and interrupts to the Guest are the * same, except some traps have an "error code" which gets pushed onto the * stack as well: the caller tells us if this is one. @@ -59,7 +66,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). */ + * it). + */ static void set_guest_interrupt(struct lg_cpu *cpu, u32 lo, u32 hi, bool has_err) { @@ -67,20 +75,26 @@ static void set_guest_interrupt(struct lg_cpu *cpu, u32 lo, u32 hi, u32 eflags, ss, irq_enable; unsigned long virtstack; - /* There are two cases for interrupts: one where the Guest is already + /* + * There are two cases for interrupts: one where the Guest is already * in the kernel, and a more complex one where the Guest is in - * userspace. We check the privilege level to find out. */ + * userspace. We check the privilege level to find out. + */ if ((cpu->regs->ss&0x3) != GUEST_PL) { - /* The Guest told us their kernel stack with the SET_STACK - * hypercall: both the virtual address and the segment */ + /* + * The Guest told us their kernel stack with the SET_STACK + * hypercall: both the virtual address and the segment. + */ virtstack = cpu->esp1; ss = cpu->ss1; origstack = gstack = guest_pa(cpu, virtstack); - /* We push the old stack segment and pointer onto the new + /* + * We push the old stack segment and pointer onto the new * stack: when the Guest does an "iret" back from the interrupt * handler the CPU will notice they're dropping privilege - * levels and expect these here. */ + * levels and expect these here. + */ push_guest_stack(cpu, &gstack, cpu->regs->ss); push_guest_stack(cpu, &gstack, cpu->regs->esp); } else { @@ -91,18 +105,22 @@ static void set_guest_interrupt(struct lg_cpu *cpu, u32 lo, u32 hi, origstack = gstack = guest_pa(cpu, virtstack); } - /* Remember that we never let the Guest actually disable interrupts, so + /* + * Remember that we never let the Guest actually disable interrupts, so * the "Interrupt Flag" bit is always set. We copy that bit from the * Guest's "irq_enabled" field into the eflags word: we saw the Guest - * copy it back in "lguest_iret". */ + * copy it back in "lguest_iret". + */ eflags = cpu->regs->eflags; if (get_user(irq_enable, &cpu->lg->lguest_data->irq_enabled) == 0 && !(irq_enable & X86_EFLAGS_IF)) eflags &= ~X86_EFLAGS_IF; - /* An interrupt is expected to push three things on the stack: the old + /* + * An interrupt is expected to push three things on the stack: the old * "eflags" word, the old code segment, and the old instruction - * pointer. */ + * pointer. + */ push_guest_stack(cpu, &gstack, eflags); push_guest_stack(cpu, &gstack, cpu->regs->cs); push_guest_stack(cpu, &gstack, cpu->regs->eip); @@ -111,15 +129,19 @@ static void set_guest_interrupt(struct lg_cpu *cpu, u32 lo, u32 hi, if (has_err) push_guest_stack(cpu, &gstack, cpu->regs->errcode); - /* Now we've pushed all the old state, we change the stack, the code - * segment and the address to execute. */ + /* + * Now we've pushed all the old state, we change the stack, the code + * segment and the address to execute. + */ cpu->regs->ss = ss; cpu->regs->esp = virtstack + (gstack - origstack); cpu->regs->cs = (__KERNEL_CS|GUEST_PL); cpu->regs->eip = idt_address(lo, hi); - /* There are two kinds of interrupt handlers: 0xE is an "interrupt - * gate" which expects interrupts to be disabled on entry. */ + /* + * There are two kinds of interrupt handlers: 0xE is an "interrupt + * gate" which expects interrupts to be disabled on entry. + */ if (idt_type(lo, hi) == 0xE) if (put_user(0, &cpu->lg->lguest_data->irq_enabled)) kill_guest(cpu, "Disabling interrupts"); @@ -130,7 +152,8 @@ static void set_guest_interrupt(struct lg_cpu *cpu, u32 lo, u32 hi, * * interrupt_pending() returns the first pending interrupt which isn't blocked * by the Guest. It is called before every entry to the Guest, and just before - * we go to sleep when the Guest has halted itself. */ + * we go to sleep when the Guest has halted itself. + */ unsigned int interrupt_pending(struct lg_cpu *cpu, bool *more) { unsigned int irq; @@ -140,8 +163,10 @@ unsigned int interrupt_pending(struct lg_cpu *cpu, bool *more) if (!cpu->lg->lguest_data) return LGUEST_IRQS; - /* Take our "irqs_pending" array and remove any interrupts the Guest - * wants blocked: the result ends up in "blk". */ + /* + * Take our "irqs_pending" array and remove any interrupts the Guest + * wants blocked: the result ends up in "blk". + */ if (copy_from_user(&blk, cpu->lg->lguest_data->blocked_interrupts, sizeof(blk))) return LGUEST_IRQS; @@ -154,16 +179,20 @@ unsigned int interrupt_pending(struct lg_cpu *cpu, bool *more) return irq; } -/* This actually diverts the Guest to running an interrupt handler, once an - * interrupt has been identified by interrupt_pending(). */ +/* + * This actually diverts the Guest to running an interrupt handler, once an + * interrupt has been identified by interrupt_pending(). + */ void try_deliver_interrupt(struct lg_cpu *cpu, unsigned int irq, bool more) { struct desc_struct *idt; BUG_ON(irq >= LGUEST_IRQS); - /* They may be in the middle of an iret, where they asked us never to - * deliver interrupts. */ + /* + * They may be in the middle of an iret, where they asked us never to + * deliver interrupts. + */ if (cpu->regs->eip >= cpu->lg->noirq_start && (cpu->regs->eip < cpu->lg->noirq_end)) return; @@ -187,29 +216,37 @@ void try_deliver_interrupt(struct lg_cpu *cpu, unsigned int irq, bool more) } } - /* Look at the IDT entry the Guest gave us for this interrupt. The + /* + * Look at the IDT entry the Guest gave us for this interrupt. The * first 32 (FIRST_EXTERNAL_VECTOR) entries are for traps, so we skip - * over them. */ + * over them. + */ idt = &cpu->arch.idt[FIRST_EXTERNAL_VECTOR+irq]; /* If they don't have a handler (yet?), we just ignore it */ if (idt_present(idt->a, idt->b)) { /* OK, mark it no longer pending and deliver it. */ clear_bit(irq, cpu->irqs_pending); - /* set_guest_interrupt() takes the interrupt descriptor and a + /* + * 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. */ + * the stack as well: virtual interrupts never do. + */ set_guest_interrupt(cpu, idt->a, idt->b, false); } - /* Every time we deliver an interrupt, we update the timestamp in the + /* + * Every time we deliver an interrupt, we update the timestamp in the * Guest's lguest_data struct. It would be better for the Guest if we * did this more often, but it can actually be quite slow: doing it * here is a compromise which means at least it gets updated every - * timer interrupt. */ + * timer interrupt. + */ write_timestamp(cpu); - /* If there are no other interrupts we want to deliver, clear - * the pending flag. */ + /* + * If there are no other interrupts we want to deliver, clear + * the pending flag. + */ if (!more) put_user(0, &cpu->lg->lguest_data->irq_pending); } @@ -217,24 +254,29 @@ void try_deliver_interrupt(struct lg_cpu *cpu, unsigned int irq, bool more) /* And this is the routine when we want to set an interrupt for the Guest. */ void set_interrupt(struct lg_cpu *cpu, unsigned int irq) { - /* Next time the Guest runs, the core code will see if it can deliver - * this interrupt. */ + /* + * Next time the Guest runs, the core code will see if it can deliver + * this interrupt. + */ set_bit(irq, cpu->irqs_pending); - /* Make sure it sees it; it might be asleep (eg. halted), or - * running the Guest right now, in which case kick_process() - * will knock it out. */ + /* + * Make sure it sees it; it might be asleep (eg. halted), or running + * the Guest right now, in which case kick_process() will knock it out. + */ if (!wake_up_process(cpu->tsk)) kick_process(cpu->tsk); } /*:*/ -/* Linux uses trap 128 for system calls. Plan9 uses 64, and Ron Minnich sent +/* + * Linux uses trap 128 for system calls. Plan9 uses 64, and Ron Minnich sent * me a patch, so we support that too. It'd be a big step for lguest if half * the Plan 9 user base were to start using it. * * Actually now I think of it, it's possible that Ron *is* half the Plan 9 - * userbase. Oh well. */ + * userbase. Oh well. + */ static bool could_be_syscall(unsigned int num) { /* Normal Linux SYSCALL_VECTOR or reserved vector? */ @@ -274,9 +316,11 @@ void free_interrupts(void) clear_bit(syscall_vector, used_vectors); } -/*H:220 Now we've got the routines to deliver interrupts, delivering traps like +/*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: */ + * should have error codes: + */ static bool has_err(unsigned int trap) { return (trap == 8 || (trap >= 10 && trap <= 14) || trap == 17); @@ -285,13 +329,17 @@ static bool has_err(unsigned int trap) /* deliver_trap() returns true if it could deliver the trap. */ 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. */ + /* + * 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 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. */ + /* + * 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 false; set_guest_interrupt(cpu, cpu->arch.idt[num].a, @@ -299,7 +347,8 @@ bool deliver_trap(struct lg_cpu *cpu, unsigned int num) return true; } -/*H:250 Here's the hard part: returning to the Host every time a trap happens +/*H:250 + * Here's the hard part: returning to the Host every time a trap happens * and then calling deliver_trap() and re-entering the Guest is slow. * Particularly because Guest userspace system calls are traps (usually trap * 128). @@ -311,69 +360,87 @@ bool deliver_trap(struct lg_cpu *cpu, unsigned int num) * the other hypervisors would beat it up at lunchtime. * * This routine indicates if a particular trap number could be delivered - * directly. */ + * directly. + */ static bool direct_trap(unsigned int num) { - /* Hardware interrupts don't go to the Guest at all (except system - * call). */ + /* + * Hardware interrupts don't go to the Guest at all (except system + * call). + */ if (num >= FIRST_EXTERNAL_VECTOR && !could_be_syscall(num)) return false; - /* The Host needs to see page faults (for shadow paging and to save the + /* + * 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), invalid opcode fault (kvm hcall), - * and of course, the hypercall trap. */ + * and of course, the hypercall trap. + */ return num != 14 && num != 13 && num != 7 && num != 6 && num != LGUEST_TRAP_ENTRY; } /*:*/ -/*M:005 The Guest has the ability to turn its interrupt gates into trap gates, +/*M:005 + * The Guest has the ability to turn its interrupt gates into trap gates, * if it is careful. The Host will let trap gates can go directly to the * Guest, but the Guest needs the interrupts atomically disabled for an * interrupt gate. It can do this by pointing the trap gate at instructions - * within noirq_start and noirq_end, where it can safely disable interrupts. */ + * within noirq_start and noirq_end, where it can safely disable interrupts. + */ -/*M:006 The Guests do not use the sysenter (fast system call) instruction, +/*M:006 + * The Guests do not use the sysenter (fast system call) instruction, * because it's hardcoded to enter privilege level 0 and so can't go direct. * It's about twice as fast as the older "int 0x80" system call, so it might * still be worthwhile to handle it in the Switcher and lcall down to the * Guest. The sysenter semantics are hairy tho: search for that keyword in - * entry.S :*/ + * entry.S +:*/ -/*H:260 When we make traps go directly into the Guest, we need to make sure +/*H:260 + * When we make traps go directly into the Guest, we need to make sure * the kernel stack is valid (ie. mapped in the page tables). Otherwise, the * CPU trying to deliver the trap will fault while trying to push the interrupt * words on the stack: this is called a double fault, and it forces us to kill * the Guest. * - * Which is deeply unfair, because (literally!) it wasn't the Guests' fault. */ + * Which is deeply unfair, because (literally!) it wasn't the Guests' fault. + */ void pin_stack_pages(struct lg_cpu *cpu) { unsigned int i; - /* Depending on the CONFIG_4KSTACKS option, the Guest can have one or - * two pages of stack space. */ + /* + * Depending on the CONFIG_4KSTACKS option, the Guest can have one or + * two pages of stack space. + */ for (i = 0; i < cpu->lg->stack_pages; i++) - /* The stack grows *upwards*, so the address we're given is the + /* + * The stack grows *upwards*, so the address we're given is the * start of the page after the kernel stack. Subtract one to * get back onto the first stack page, and keep subtracting to - * get to the rest of the stack pages. */ + * get to the rest of the stack pages. + */ pin_page(cpu, cpu->esp1 - 1 - i * PAGE_SIZE); } -/* Direct traps also mean that we need to know whenever the Guest wants to use +/* + * Direct traps also mean that we need to know whenever the Guest wants to use * a different kernel stack, so we can change the IDT entries to use that * stack. The IDT entries expect a virtual address, so unlike most addresses * the Guest gives us, the "esp" (stack pointer) value here is virtual, not * physical. * * In Linux each process has its own kernel stack, so this happens a lot: we - * change stacks on each context switch. */ + * change stacks on each context switch. + */ void guest_set_stack(struct lg_cpu *cpu, u32 seg, u32 esp, unsigned int pages) { - /* You are not allowed have a stack segment with privilege level 0: bad - * Guest! */ + /* + * You're not allowed a stack segment with privilege level 0: bad Guest! + */ if ((seg & 0x3) != GUEST_PL) kill_guest(cpu, "bad stack segment %i", seg); /* We only expect one or two stack pages. */ @@ -387,11 +454,15 @@ void guest_set_stack(struct lg_cpu *cpu, u32 seg, u32 esp, unsigned int pages) pin_stack_pages(cpu); } -/* All this reference to mapping stacks leads us neatly into the other complex - * part of the Host: page table handling. */ +/* + * All this reference to mapping stacks leads us neatly into the other complex + * part of the Host: page table handling. + */ -/*H:235 This is the routine which actually checks the Guest's IDT entry and - * transfers it into the entry in "struct lguest": */ +/*H:235 + * This is the routine which actually checks the Guest's IDT entry and + * transfers it into the entry in "struct lguest": + */ static void set_trap(struct lg_cpu *cpu, struct desc_struct *trap, unsigned int num, u32 lo, u32 hi) { @@ -407,30 +478,38 @@ static void set_trap(struct lg_cpu *cpu, struct desc_struct *trap, if (type != 0xE && type != 0xF) kill_guest(cpu, "bad IDT type %i", type); - /* We only copy the handler address, present bit, privilege level and + /* + * We only copy the handler address, present bit, privilege level and * type. The privilege level controls where the trap can be triggered * manually with an "int" instruction. This is usually GUEST_PL, - * except for system calls which userspace can use. */ + * except for system calls which userspace can use. + */ trap->a = ((__KERNEL_CS|GUEST_PL)<<16) | (lo&0x0000FFFF); trap->b = (hi&0xFFFFEF00); } -/*H:230 While we're here, dealing with delivering traps and interrupts to the +/*H:230 + * While we're here, dealing with delivering traps and interrupts to the * Guest, we might as well complete the picture: how the Guest tells us where * it wants them to go. This would be simple, except making traps fast * requires some tricks. * * We saw the Guest setting Interrupt Descriptor Table (IDT) entries with the - * LHCALL_LOAD_IDT_ENTRY hypercall before: that comes here. */ + * LHCALL_LOAD_IDT_ENTRY hypercall before: that comes here. + */ void load_guest_idt_entry(struct lg_cpu *cpu, unsigned int num, u32 lo, u32 hi) { - /* Guest never handles: NMI, doublefault, spurious interrupt or - * hypercall. We ignore when it tries to set them. */ + /* + * Guest never handles: NMI, doublefault, spurious interrupt or + * hypercall. We ignore when it tries to set them. + */ if (num == 2 || num == 8 || num == 15 || num == LGUEST_TRAP_ENTRY) return; - /* Mark the IDT as changed: next time the Guest runs we'll know we have - * to copy this again. */ + /* + * Mark the IDT as changed: next time the Guest runs we'll know we have + * to copy this again. + */ cpu->changed |= CHANGED_IDT; /* Check that the Guest doesn't try to step outside the bounds. */ @@ -440,9 +519,11 @@ void load_guest_idt_entry(struct lg_cpu *cpu, unsigned int num, u32 lo, u32 hi) set_trap(cpu, &cpu->arch.idt[num], num, lo, hi); } -/* The default entry for each interrupt points into the Switcher routines which +/* + * The default entry for each interrupt points into the Switcher routines which * simply return to the Host. The run_guest() loop will then call - * deliver_trap() to bounce it back into the Guest. */ + * deliver_trap() to bounce it back into the Guest. + */ static void default_idt_entry(struct desc_struct *idt, int trap, const unsigned long handler, @@ -451,13 +532,17 @@ static void default_idt_entry(struct desc_struct *idt, /* A present interrupt gate. */ u32 flags = 0x8e00; - /* Set the privilege level on the entry for the hypercall: this allows - * the Guest to use the "int" instruction to trigger it. */ + /* + * Set the privilege level on the entry for the hypercall: this allows + * the Guest to use the "int" instruction to trigger it. + */ if (trap == LGUEST_TRAP_ENTRY) flags |= (GUEST_PL << 13); else if (base) - /* Copy priv. level from what Guest asked for. This allows - * debug (int 3) traps from Guest userspace, for example. */ + /* + * Copy privilege level from what Guest asked for. This allows + * debug (int 3) traps from Guest userspace, for example. + */ flags |= (base->b & 0x6000); /* Now pack it into the IDT entry in its weird format. */ @@ -475,16 +560,20 @@ void setup_default_idt_entries(struct lguest_ro_state *state, default_idt_entry(&state->guest_idt[i], i, def[i], NULL); } -/*H:240 We don't use the IDT entries in the "struct lguest" directly, instead +/*H:240 + * We don't use the IDT entries in the "struct lguest" directly, instead * we copy them into the IDT which we've set up for Guests on this CPU, just - * before we run the Guest. This routine does that copy. */ + * before we run the Guest. This routine does that copy. + */ void copy_traps(const struct lg_cpu *cpu, struct desc_struct *idt, const unsigned long *def) { unsigned int i; - /* We can simply copy the direct traps, otherwise we use the default - * ones in the Switcher: they will return to the Host. */ + /* + * We can simply copy the direct traps, otherwise we use the default + * ones in the Switcher: they will return to the Host. + */ for (i = 0; i < ARRAY_SIZE(cpu->arch.idt); i++) { const struct desc_struct *gidt = &cpu->arch.idt[i]; @@ -492,14 +581,16 @@ void copy_traps(const struct lg_cpu *cpu, struct desc_struct *idt, if (!direct_trap(i)) continue; - /* Only trap gates (type 15) can go direct to the Guest. + /* + * Only trap gates (type 15) can go direct to the Guest. * Interrupt gates (type 14) disable interrupts as they are * entered, which we never let the Guest do. Not present * entries (type 0x0) also can't go direct, of course. * * If it can't go direct, we still need to copy the priv. level: * they might want to give userspace access to a software - * interrupt. */ + * interrupt. + */ if (idt_type(gidt->a, gidt->b) == 0xF) idt[i] = *gidt; else @@ -518,7 +609,8 @@ void copy_traps(const struct lg_cpu *cpu, struct desc_struct *idt, * the next timer interrupt (in nanoseconds). We use the high-resolution timer * infrastructure to set a callback at that time. * - * 0 means "turn off the clock". */ + * 0 means "turn off the clock". + */ void guest_set_clockevent(struct lg_cpu *cpu, unsigned long delta) { ktime_t expires; @@ -529,9 +621,11 @@ void guest_set_clockevent(struct lg_cpu *cpu, unsigned long delta) return; } - /* We use wallclock time here, so the Guest might not be running for + /* + * We use wallclock time here, so the Guest might not be running for * all the time between now and the timer interrupt it asked for. This - * is almost always the right thing to do. */ + * is almost always the right thing to do. + */ expires = ktime_add_ns(ktime_get_real(), delta); hrtimer_start(&cpu->hrt, expires, HRTIMER_MODE_ABS); } diff --git a/drivers/lguest/lg.h b/drivers/lguest/lg.h index 9c3138265f8..bc28745d05a 100644 --- a/drivers/lguest/lg.h +++ b/drivers/lguest/lg.h @@ -16,15 +16,13 @@ void free_pagetables(void); int init_pagetables(struct page **switcher_page, unsigned int pages); -struct pgdir -{ +struct pgdir { unsigned long gpgdir; pgd_t *pgdir; }; /* We have two pages shared with guests, per cpu. */ -struct lguest_pages -{ +struct lguest_pages { /* This is the stack page mapped rw in guest */ char spare[PAGE_SIZE - sizeof(struct lguest_regs)]; struct lguest_regs regs; @@ -38,8 +36,6 @@ struct lguest_pages #define CHANGED_GDT_TLS 4 /* Actually a subset of CHANGED_GDT */ #define CHANGED_ALL 3 -struct lguest; - struct lg_cpu { unsigned int id; struct lguest *lg; @@ -56,13 +52,13 @@ struct lg_cpu { unsigned long pending_notify; /* pfn from LHCALL_NOTIFY */ - /* At end of a page shared mapped over lguest_pages in guest. */ + /* At end of a page shared mapped over lguest_pages in guest. */ unsigned long regs_page; struct lguest_regs *regs; struct lguest_pages *last_pages; - int cpu_pgd; /* which pgd this cpu is currently using */ + int cpu_pgd; /* Which pgd this cpu is currently using */ /* If a hypercall was asked for, this points to the arguments. */ struct hcall_args *hcall; @@ -91,15 +87,17 @@ struct lg_eventfd_map { }; /* The private info the thread maintains about the guest. */ -struct lguest -{ +struct lguest { struct lguest_data __user *lguest_data; struct lg_cpu cpus[NR_CPUS]; unsigned int nr_cpus; u32 pfn_limit; - /* This provides the offset to the base of guest-physical - * memory in the Launcher. */ + + /* + * This provides the offset to the base of guest-physical memory in the + * Launcher. + */ void __user *mem_base; unsigned long kernel_address; @@ -124,11 +122,13 @@ bool lguest_address_ok(const struct lguest *lg, void __lgread(struct lg_cpu *, void *, unsigned long, unsigned); void __lgwrite(struct lg_cpu *, unsigned long, const void *, unsigned); -/*H:035 Using memory-copy operations like that is usually inconvient, so we +/*H:035 + * Using memory-copy operations like that is usually inconvient, so we * have the following helper macros which read and write a specific type (often * an unsigned long). * - * This reads into a variable of the given type then returns that. */ + * This reads into a variable of the given type then returns that. + */ #define lgread(cpu, addr, type) \ ({ type _v; __lgread((cpu), &_v, (addr), sizeof(_v)); _v; }) @@ -142,9 +142,11 @@ void __lgwrite(struct lg_cpu *, unsigned long, const void *, unsigned); int run_guest(struct lg_cpu *cpu, unsigned long __user *user); -/* Helper macros to obtain the first 12 or the last 20 bits, this is only the +/* + * Helper macros to obtain the first 12 or the last 20 bits, this is only the * first step in the migration to the kernel types. pte_pfn is already defined - * in the kernel. */ + * in the kernel. + */ #define pgd_flags(x) (pgd_val(x) & ~PAGE_MASK) #define pgd_pfn(x) (pgd_val(x) >> PAGE_SHIFT) #define pmd_flags(x) (pmd_val(x) & ~PAGE_MASK) diff --git a/drivers/lguest/lguest_device.c b/drivers/lguest/lguest_device.c index e082cdac88b..b6200bc39b5 100644 --- a/drivers/lguest/lguest_device.c +++ b/drivers/lguest/lguest_device.c @@ -1,10 +1,12 @@ -/*P:050 Lguest guests use a very simple method to describe devices. It's a +/*P:050 + * Lguest guests use a very simple method to describe devices. It's a * series of device descriptors contained just above the top of normal Guest * memory. * * We use the standard "virtio" device infrastructure, which provides us with a * console, a network and a block driver. Each one expects some configuration - * information and a "virtqueue" or two to send and receive data. :*/ + * information and a "virtqueue" or two to send and receive data. +:*/ #include <linux/init.h> #include <linux/bootmem.h> #include <linux/lguest_launcher.h> @@ -20,8 +22,10 @@ /* The pointer to our (page) of device descriptions. */ static void *lguest_devices; -/* For Guests, device memory can be used as normal memory, so we cast away the - * __iomem to quieten sparse. */ +/* + * For Guests, device memory can be used as normal memory, so we cast away the + * __iomem to quieten sparse. + */ static inline void *lguest_map(unsigned long phys_addr, unsigned long pages) { return (__force void *)ioremap_cache(phys_addr, PAGE_SIZE*pages); @@ -32,8 +36,10 @@ static inline void lguest_unmap(void *addr) iounmap((__force void __iomem *)addr); } -/*D:100 Each lguest device is just a virtio device plus a pointer to its entry - * in the lguest_devices page. */ +/*D:100 + * Each lguest device is just a virtio device plus a pointer to its entry + * in the lguest_devices page. + */ struct lguest_device { struct virtio_device vdev; @@ -41,9 +47,11 @@ struct lguest_device { struct lguest_device_desc *desc; }; -/* Since the virtio infrastructure hands us a pointer to the virtio_device all +/* + * Since the virtio infrastructure hands us a pointer to the virtio_device all * the time, it helps to have a curt macro to get a pointer to the struct - * lguest_device it's enclosed in. */ + * lguest_device it's enclosed in. + */ #define to_lgdev(vd) container_of(vd, struct lguest_device, vdev) /*D:130 @@ -55,7 +63,8 @@ struct lguest_device { * the driver will look at them during setup. * * A convenient routine to return the device's virtqueue config array: - * immediately after the descriptor. */ + * immediately after the descriptor. + */ static struct lguest_vqconfig *lg_vq(const struct lguest_device_desc *desc) { return (void *)(desc + 1); @@ -98,10 +107,12 @@ static u32 lg_get_features(struct virtio_device *vdev) return features; } -/* The virtio core takes the features the Host offers, and copies the - * ones supported by the driver into the vdev->features array. Once - * that's all sorted out, this routine is called so we can tell the - * Host which features we understand and accept. */ +/* + * The virtio core takes the features the Host offers, and copies the ones + * supported by the driver into the vdev->features array. Once that's all + * sorted out, this routine is called so we can tell the Host which features we + * understand and accept. + */ static void lg_finalize_features(struct virtio_device *vdev) { unsigned int i, bits; @@ -112,10 +123,11 @@ static void lg_finalize_features(struct virtio_device *vdev) /* Give virtio_ring a chance to accept features. */ vring_transport_features(vdev); - /* The vdev->feature array is a Linux bitmask: this isn't the - * same as a the simple array of bits used by lguest devices - * for features. So we do this slow, manual conversion which is - * completely general. */ + /* + * The vdev->feature array is a Linux bitmask: this isn't the same as a + * the simple array of bits used by lguest devices for features. So we + * do this slow, manual conversion which is completely general. + */ memset(out_features, 0, desc->feature_len); bits = min_t(unsigned, desc->feature_len, sizeof(vdev->features)) * 8; for (i = 0; i < bits; i++) { @@ -146,15 +158,19 @@ static void lg_set(struct virtio_device *vdev, unsigned int offset, memcpy(lg_config(desc) + offset, buf, len); } -/* The operations to get and set the status word just access the status field - * of the device descriptor. */ +/* + * The operations to get and set the status word just access the status field + * of the device descriptor. + */ static u8 lg_get_status(struct virtio_device *vdev) { return to_lgdev(vdev)->desc->status; } -/* To notify on status updates, we (ab)use the NOTIFY hypercall, with the - * descriptor address of the device. A zero status means "reset". */ +/* + * To notify on status updates, we (ab)use the NOTIFY hypercall, with the + * descriptor address of the device. A zero status means "reset". + */ static void set_status(struct virtio_device *vdev, u8 status) { unsigned long offset = (void *)to_lgdev(vdev)->desc - lguest_devices; @@ -191,8 +207,7 @@ static void lg_reset(struct virtio_device *vdev) */ /*D:140 This is the information we remember about each virtqueue. */ -struct lguest_vq_info -{ +struct lguest_vq_info { /* A copy of the information contained in the device config. */ struct lguest_vqconfig config; @@ -200,13 +215,17 @@ struct lguest_vq_info void *pages; }; -/* When the virtio_ring code wants to prod the Host, it calls us here and we +/* + * When the virtio_ring code wants to prod the Host, it calls us here and we * make a hypercall. We hand the physical address of the virtqueue so the Host - * knows which virtqueue we're talking about. */ + * knows which virtqueue we're talking about. + */ static void lg_notify(struct virtqueue *vq) { - /* We store our virtqueue information in the "priv" pointer of the - * virtqueue structure. */ + /* + * We store our virtqueue information in the "priv" pointer of the + * virtqueue structure. + */ struct lguest_vq_info *lvq = vq->priv; kvm_hypercall1(LHCALL_NOTIFY, lvq->config.pfn << PAGE_SHIFT); @@ -215,7 +234,8 @@ static void lg_notify(struct virtqueue *vq) /* An extern declaration inside a C file is bad form. Don't do it. */ extern void lguest_setup_irq(unsigned int irq); -/* This routine finds the first virtqueue described in the configuration of +/* + * This routine finds the Nth virtqueue described in the configuration of * this device and sets it up. * * This is kind of an ugly duckling. It'd be nicer to have a standard @@ -223,9 +243,7 @@ extern void lguest_setup_irq(unsigned int irq); * everyone wants to do it differently. The KVM coders want the Guest to * allocate its own pages and tell the Host where they are, but for lguest it's * simpler for the Host to simply tell us where the pages are. - * - * So we provide drivers with a "find the Nth virtqueue and set it up" - * function. */ + */ static struct virtqueue *lg_find_vq(struct virtio_device *vdev, unsigned index, void (*callback)(struct virtqueue *vq), @@ -244,9 +262,11 @@ static struct virtqueue *lg_find_vq(struct virtio_device *vdev, if (!lvq) return ERR_PTR(-ENOMEM); - /* Make a copy of the "struct lguest_vqconfig" entry, which sits after + /* + * Make a copy of the "struct lguest_vqconfig" entry, which sits after * the descriptor. We need a copy because the config space might not - * be aligned correctly. */ + * be aligned correctly. + */ memcpy(&lvq->config, lg_vq(ldev->desc)+index, sizeof(lvq->config)); printk("Mapping virtqueue %i addr %lx\n", index, @@ -261,8 +281,10 @@ static struct virtqueue *lg_find_vq(struct virtio_device *vdev, goto free_lvq; } - /* OK, tell virtio_ring.c to set up a virtqueue now we know its size - * and we've got a pointer to its pages. */ + /* + * OK, tell virtio_ring.c to set up a virtqueue now we know its size + * and we've got a pointer to its pages. + */ vq = vring_new_virtqueue(lvq->config.num, LGUEST_VRING_ALIGN, vdev, lvq->pages, lg_notify, callback, name); if (!vq) { @@ -273,18 +295,23 @@ static struct virtqueue *lg_find_vq(struct virtio_device *vdev, /* Make sure the interrupt is allocated. */ lguest_setup_irq(lvq->config.irq); - /* Tell the interrupt for this virtqueue to go to the virtio_ring - * interrupt handler. */ - /* FIXME: We used to have a flag for the Host to tell us we could use + /* + * Tell the interrupt for this virtqueue to go to the virtio_ring + * interrupt handler. + * + * FIXME: We used to have a flag for the Host to tell us we could use * the interrupt as a source of randomness: it'd be nice to have that - * back.. */ + * back. + */ err = request_irq(lvq->config.irq, vring_interrupt, IRQF_SHARED, dev_name(&vdev->dev), vq); if (err) goto destroy_vring; - /* Last of all we hook up our 'struct lguest_vq_info" to the - * virtqueue's priv pointer. */ + /* + * Last of all we hook up our 'struct lguest_vq_info" to the + * virtqueue's priv pointer. + */ vq->priv = lvq; return vq; @@ -358,11 +385,14 @@ static struct virtio_config_ops lguest_config_ops = { .del_vqs = lg_del_vqs, }; -/* The root device for the lguest virtio devices. This makes them appear as - * /sys/devices/lguest/0,1,2 not /sys/devices/0,1,2. */ +/* + * The root device for the lguest virtio devices. This makes them appear as + * /sys/devices/lguest/0,1,2 not /sys/devices/0,1,2. + */ static struct device *lguest_root; -/*D:120 This is the core of the lguest bus: actually adding a new device. +/*D:120 + * This is the core of the lguest bus: actually adding a new device. * It's a separate function because it's neater that way, and because an * earlier version of the code supported hotplug and unplug. They were removed * early on because they were never used. @@ -371,14 +401,14 @@ static struct device *lguest_root; * * It's worth reading this carefully: we start with a pointer to the new device * descriptor in the "lguest_devices" page, and the offset into the device - * descriptor page so we can uniquely identify it if things go badly wrong. */ + * descriptor page so we can uniquely identify it if things go badly wrong. + */ static void add_lguest_device(struct lguest_device_desc *d, unsigned int offset) { struct lguest_device *ldev; - /* Start with zeroed memory; Linux's device layer seems to count on - * it. */ + /* Start with zeroed memory; Linux's device layer counts on it. */ ldev = kzalloc(sizeof(*ldev), GFP_KERNEL); if (!ldev) { printk(KERN_EMERG "Cannot allocate lguest dev %u type %u\n", @@ -388,17 +418,25 @@ static void add_lguest_device(struct lguest_device_desc *d, /* This devices' parent is the lguest/ dir. */ ldev->vdev.dev.parent = lguest_root; - /* We have a unique device index thanks to the dev_index counter. */ + /* + * The device type comes straight from the descriptor. There's also a + * device vendor field in the virtio_device struct, which we leave as + * 0. + */ ldev->vdev.id.device = d->type; - /* We have a simple set of routines for querying the device's - * configuration information and setting its status. */ + /* + * We have a simple set of routines for querying the device's + * configuration information and setting its status. + */ ldev->vdev.config = &lguest_config_ops; /* And we remember the device's descriptor for lguest_config_ops. */ ldev->desc = d; - /* register_virtio_device() sets up the generic fields for the struct + /* + * register_virtio_device() sets up the generic fields for the struct * virtio_device and calls device_register(). This makes the bus - * infrastructure look for a matching driver. */ + * infrastructure look for a matching driver. + */ if (register_virtio_device(&ldev->vdev) != 0) { printk(KERN_ERR "Failed to register lguest dev %u type %u\n", offset, d->type); @@ -406,8 +444,10 @@ static void add_lguest_device(struct lguest_device_desc *d, } } -/*D:110 scan_devices() simply iterates through the device page. The type 0 is - * reserved to mean "end of devices". */ +/*D:110 + * scan_devices() simply iterates through the device page. The type 0 is + * reserved to mean "end of devices". + */ static void scan_devices(void) { unsigned int i; @@ -426,7 +466,8 @@ static void scan_devices(void) } } -/*D:105 Fairly early in boot, lguest_devices_init() is called to set up the +/*D:105 + * Fairly early in boot, lguest_devices_init() is called to set up the * lguest device infrastructure. We check that we are a Guest by checking * pv_info.name: there are other ways of checking, but this seems most * obvious to me. @@ -437,7 +478,8 @@ static void scan_devices(void) * correct sysfs incantation). * * Finally we call scan_devices() which adds all the devices found in the - * lguest_devices page. */ + * lguest_devices page. + */ static int __init lguest_devices_init(void) { if (strcmp(pv_info.name, "lguest") != 0) @@ -456,11 +498,13 @@ static int __init lguest_devices_init(void) /* We do this after core stuff, but before the drivers. */ postcore_initcall(lguest_devices_init); -/*D:150 At this point in the journey we used to now wade through the lguest +/*D:150 + * At this point in the journey we used to now wade through the lguest * devices themselves: net, block and console. Since they're all now virtio * devices rather than lguest-specific, I've decided to ignore them. Mostly, * they're kind of boring. But this does mean you'll never experience the * thrill of reading the forbidden love scene buried deep in the block driver. * * "make Launcher" beckons, where we answer questions like "Where do Guests - * come from?", and "What do you do when someone asks for optimization?". */ + * come from?", and "What do you do when someone asks for optimization?". + */ diff --git a/drivers/lguest/lguest_user.c b/drivers/lguest/lguest_user.c index 9f9a2953b38..b4d3f7ca554 100644 --- a/drivers/lguest/lguest_user.c +++ b/drivers/lguest/lguest_user.c @@ -1,8 +1,9 @@ /*P:200 This contains all the /dev/lguest code, whereby the userspace launcher * controls and communicates with the Guest. For example, the first write will - * tell us the Guest's memory layout, pagetable, entry point and kernel address - * offset. A read will run the Guest until something happens, such as a signal - * or the Guest doing a NOTIFY out to the Launcher. :*/ + * tell us the Guest's memory layout and entry point. A read will run the + * Guest until something happens, such as a signal or the Guest doing a NOTIFY + * out to the Launcher. +:*/ #include <linux/uaccess.h> #include <linux/miscdevice.h> #include <linux/fs.h> @@ -11,14 +12,41 @@ #include <linux/file.h> #include "lg.h" +/*L:056 + * Before we move on, let's jump ahead and look at what the kernel does when + * it needs to look up the eventfds. That will complete our picture of how we + * use RCU. + * + * The notification value is in cpu->pending_notify: we return true if it went + * to an eventfd. + */ bool send_notify_to_eventfd(struct lg_cpu *cpu) { unsigned int i; struct lg_eventfd_map *map; - /* lg->eventfds is RCU-protected */ + /* + * This "rcu_read_lock()" helps track when someone is still looking at + * the (RCU-using) eventfds array. It's not actually a lock at all; + * indeed it's a noop in many configurations. (You didn't expect me to + * explain all the RCU secrets here, did you?) + */ rcu_read_lock(); + /* + * rcu_dereference is the counter-side of rcu_assign_pointer(); it + * makes sure we don't access the memory pointed to by + * cpu->lg->eventfds before cpu->lg->eventfds is set. Sounds crazy, + * but Alpha allows this! Paul McKenney points out that a really + * aggressive compiler could have the same effect: + * http://lists.ozlabs.org/pipermail/lguest/2009-July/001560.html + * + * So play safe, use rcu_dereference to get the rcu-protected pointer: + */ map = rcu_dereference(cpu->lg->eventfds); + /* + * Simple array search: even if they add an eventfd while we do this, + * we'll continue to use the old array and just won't see the new one. + */ for (i = 0; i < map->num; i++) { if (map->map[i].addr == cpu->pending_notify) { eventfd_signal(map->map[i].event, 1); @@ -26,19 +54,50 @@ bool send_notify_to_eventfd(struct lg_cpu *cpu) break; } } + /* We're done with the rcu-protected variable cpu->lg->eventfds. */ rcu_read_unlock(); + + /* If we cleared the notification, it's because we found a match. */ return cpu->pending_notify == 0; } +/*L:055 + * One of the more tricksy tricks in the Linux Kernel is a technique called + * Read Copy Update. Since one point of lguest is to teach lguest journeyers + * about kernel coding, I use it here. (In case you're curious, other purposes + * include learning about virtualization and instilling a deep appreciation for + * simplicity and puppies). + * + * We keep a simple array which maps LHCALL_NOTIFY values to eventfds, but we + * add new eventfds without ever blocking readers from accessing the array. + * The current Launcher only does this during boot, so that never happens. But + * Read Copy Update is cool, and adding a lock risks damaging even more puppies + * than this code does. + * + * We allocate a brand new one-larger array, copy the old one and add our new + * element. Then we make the lg eventfd pointer point to the new array. + * That's the easy part: now we need to free the old one, but we need to make + * sure no slow CPU somewhere is still looking at it. That's what + * synchronize_rcu does for us: waits until every CPU has indicated that it has + * moved on to know it's no longer using the old one. + * + * If that's unclear, see http://en.wikipedia.org/wiki/Read-copy-update. + */ static int add_eventfd(struct lguest *lg, unsigned long addr, int fd) { struct lg_eventfd_map *new, *old = lg->eventfds; + /* + * We don't allow notifications on value 0 anyway (pending_notify of + * 0 means "nothing pending"). + */ if (!addr) return -EINVAL; - /* Replace the old array with the new one, carefully: others can - * be accessing it at the same time */ + /* + * Replace the old array with the new one, carefully: others can + * be accessing it at the same time. + */ new = kmalloc(sizeof(*new) + sizeof(new->map[0]) * (old->num + 1), GFP_KERNEL); if (!new) @@ -52,22 +111,41 @@ static int add_eventfd(struct lguest *lg, unsigned long addr, int fd) new->map[new->num].addr = addr; new->map[new->num].event = eventfd_ctx_fdget(fd); if (IS_ERR(new->map[new->num].event)) { + int err = PTR_ERR(new->map[new->num].event); kfree(new); - return PTR_ERR(new->map[new->num].event); + return err; } new->num++; - /* Now put new one in place. */ + /* + * Now put new one in place: rcu_assign_pointer() is a fancy way of + * doing "lg->eventfds = new", but it uses memory barriers to make + * absolutely sure that the contents of "new" written above is nailed + * down before we actually do the assignment. + * + * We have to think about these kinds of things when we're operating on + * live data without locks. + */ rcu_assign_pointer(lg->eventfds, new); - /* We're not in a big hurry. Wait until noone's looking at old - * version, then delete it. */ + /* + * We're not in a big hurry. Wait until noone's looking at old + * version, then free it. + */ synchronize_rcu(); kfree(old); return 0; } +/*L:052 + * Receiving notifications from the Guest is usually done by attaching a + * particular LHCALL_NOTIFY value to an event filedescriptor. The eventfd will + * become readable when the Guest does an LHCALL_NOTIFY with that value. + * + * This is really convenient for processing each virtqueue in a separate + * thread. + */ static int attach_eventfd(struct lguest *lg, const unsigned long __user *input) { unsigned long addr, fd; @@ -79,15 +157,22 @@ static int attach_eventfd(struct lguest *lg, const unsigned long __user *input) if (get_user(fd, input) != 0) return -EFAULT; + /* + * Just make sure two callers don't add eventfds at once. We really + * only need to lock against callers adding to the same Guest, so using + * the Big Lguest Lock is overkill. But this is setup, not a fast path. + */ mutex_lock(&lguest_lock); err = add_eventfd(lg, addr, fd); mutex_unlock(&lguest_lock); - return 0; + return err; } -/*L:050 Sending an interrupt is done by writing LHREQ_IRQ and an interrupt - * number to /dev/lguest. */ +/*L:050 + * Sending an interrupt is done by writing LHREQ_IRQ and an interrupt + * number to /dev/lguest. + */ static int user_send_irq(struct lg_cpu *cpu, const unsigned long __user *input) { unsigned long irq; @@ -97,12 +182,18 @@ static int user_send_irq(struct lg_cpu *cpu, const unsigned long __user *input) if (irq >= LGUEST_IRQS) return -EINVAL; + /* + * Next time the Guest runs, the core code will see if it can deliver + * this interrupt. + */ set_interrupt(cpu, irq); return 0; } -/*L:040 Once our Guest is initialized, the Launcher makes it run by reading - * from /dev/lguest. */ +/*L:040 + * Once our Guest is initialized, the Launcher makes it run by reading + * from /dev/lguest. + */ static ssize_t read(struct file *file, char __user *user, size_t size,loff_t*o) { struct lguest *lg = file->private_data; @@ -138,8 +229,10 @@ static ssize_t read(struct file *file, char __user *user, size_t size,loff_t*o) return len; } - /* If we returned from read() last time because the Guest sent I/O, - * clear the flag. */ + /* + * If we returned from read() last time because the Guest sent I/O, + * clear the flag. + */ if (cpu->pending_notify) cpu->pending_notify = 0; @@ -147,8 +240,10 @@ static ssize_t read(struct file *file, char __user *user, size_t size,loff_t*o) return run_guest(cpu, (unsigned long __user *)user); } -/*L:025 This actually initializes a CPU. For the moment, a Guest is only - * uniprocessor, so "id" is always 0. */ +/*L:025 + * This actually initializes a CPU. For the moment, a Guest is only + * uniprocessor, so "id" is always 0. + */ static int lg_cpu_start(struct lg_cpu *cpu, unsigned id, unsigned long start_ip) { /* We have a limited number the number of CPUs in the lguest struct. */ @@ -163,8 +258,10 @@ static int lg_cpu_start(struct lg_cpu *cpu, unsigned id, unsigned long start_ip) /* Each CPU has a timer it can set. */ init_clockdev(cpu); - /* We need a complete page for the Guest registers: they are accessible - * to the Guest and we can only grant it access to whole pages. */ + /* + * We need a complete page for the Guest registers: they are accessible + * to the Guest and we can only grant it access to whole pages. + */ cpu->regs_page = get_zeroed_page(GFP_KERNEL); if (!cpu->regs_page) return -ENOMEM; @@ -172,29 +269,38 @@ static int lg_cpu_start(struct lg_cpu *cpu, unsigned id, unsigned long start_ip) /* We actually put the registers at the bottom of the page. */ cpu->regs = (void *)cpu->regs_page + PAGE_SIZE - sizeof(*cpu->regs); - /* Now we initialize the Guest's registers, handing it the start - * address. */ + /* + * Now we initialize the Guest's registers, handing it the start + * address. + */ lguest_arch_setup_regs(cpu, start_ip); - /* We keep a pointer to the Launcher task (ie. current task) for when - * other Guests want to wake this one (eg. console input). */ + /* + * We keep a pointer to the Launcher task (ie. current task) for when + * other Guests want to wake this one (eg. console input). + */ cpu->tsk = current; - /* We need to keep a pointer to the Launcher's memory map, because if + /* + * We need to keep a pointer to the Launcher's memory map, because if * the Launcher dies we need to clean it up. If we don't keep a - * reference, it is destroyed before close() is called. */ + * reference, it is destroyed before close() is called. + */ cpu->mm = get_task_mm(cpu->tsk); - /* We remember which CPU's pages this Guest used last, for optimization - * when the same Guest runs on the same CPU twice. */ + /* + * We remember which CPU's pages this Guest used last, for optimization + * when the same Guest runs on the same CPU twice. + */ cpu->last_pages = NULL; /* No error == success. */ return 0; } -/*L:020 The initialization write supplies 3 pointer sized (32 or 64 bit) - * values (in addition to the LHREQ_INITIALIZE value). These are: +/*L:020 + * The initialization write supplies 3 pointer sized (32 or 64 bit) values (in + * addition to the LHREQ_INITIALIZE value). These are: * * base: The start of the Guest-physical memory inside the Launcher memory. * @@ -206,14 +312,15 @@ static int lg_cpu_start(struct lg_cpu *cpu, unsigned id, unsigned long start_ip) */ static int initialize(struct file *file, const unsigned long __user *input) { - /* "struct lguest" contains everything we (the Host) know about a - * Guest. */ + /* "struct lguest" contains all we (the Host) know about a Guest. */ struct lguest *lg; int err; unsigned long args[3]; - /* We grab the Big Lguest lock, which protects against multiple - * simultaneous initializations. */ + /* + * We grab the Big Lguest lock, which protects against multiple + * simultaneous initializations. + */ mutex_lock(&lguest_lock); /* You can't initialize twice! Close the device and start again... */ if (file->private_data) { @@ -248,8 +355,10 @@ static int initialize(struct file *file, const unsigned long __user *input) if (err) goto free_eventfds; - /* Initialize the Guest's shadow page tables, using the toplevel - * address the Launcher gave us. This allocates memory, so can fail. */ + /* + * Initialize the Guest's shadow page tables, using the toplevel + * address the Launcher gave us. This allocates memory, so can fail. + */ err = init_guest_pagetable(lg); if (err) goto free_regs; @@ -274,20 +383,24 @@ unlock: return err; } -/*L:010 The first operation the Launcher does must be a write. All writes +/*L:010 + * The first operation the Launcher does must be a write. All writes * start with an unsigned long number: for the first write this must be * LHREQ_INITIALIZE to set up the Guest. After that the Launcher can use - * writes of other values to send interrupts. + * writes of other values to send interrupts or set up receipt of notifications. * * Note that we overload the "offset" in the /dev/lguest file to indicate what - * CPU number we're dealing with. Currently this is always 0, since we only + * CPU number we're dealing with. Currently this is always 0 since we only * support uniprocessor Guests, but you can see the beginnings of SMP support - * here. */ + * here. + */ static ssize_t write(struct file *file, const char __user *in, size_t size, loff_t *off) { - /* Once the Guest is initialized, we hold the "struct lguest" in the - * file private data. */ + /* + * Once the Guest is initialized, we hold the "struct lguest" in the + * file private data. + */ struct lguest *lg = file->private_data; const unsigned long __user *input = (const unsigned long __user *)in; unsigned long req; @@ -322,13 +435,15 @@ static ssize_t write(struct file *file, const char __user *in, } } -/*L:060 The final piece of interface code is the close() routine. It reverses +/*L:060 + * The final piece of interface code is the close() routine. It reverses * everything done in initialize(). This is usually called because the * Launcher exited. * * Note that the close routine returns 0 or a negative error number: it can't * really fail, but it can whine. I blame Sun for this wart, and K&R C for - * letting them do it. :*/ + * letting them do it. +:*/ static int close(struct inode *inode, struct file *file) { struct lguest *lg = file->private_data; @@ -338,8 +453,10 @@ static int close(struct inode *inode, struct file *file) if (!lg) return 0; - /* We need the big lock, to protect from inter-guest I/O and other - * Launchers initializing guests. */ + /* + * We need the big lock, to protect from inter-guest I/O and other + * Launchers initializing guests. + */ mutex_lock(&lguest_lock); /* Free up the shadow page tables for the Guest. */ @@ -350,8 +467,10 @@ static int close(struct inode *inode, struct file *file) hrtimer_cancel(&lg->cpus[i].hrt); /* We can free up the register page we allocated. */ free_page(lg->cpus[i].regs_page); - /* Now all the memory cleanups are done, it's safe to release - * the Launcher's memory management structure. */ + /* + * Now all the memory cleanups are done, it's safe to release + * the Launcher's memory management structure. + */ mmput(lg->cpus[i].mm); } @@ -360,8 +479,10 @@ static int close(struct inode *inode, struct file *file) eventfd_ctx_put(lg->eventfds->map[i].event); kfree(lg->eventfds); - /* If lg->dead doesn't contain an error code it will be NULL or a - * kmalloc()ed string, either of which is ok to hand to kfree(). */ + /* + * If lg->dead doesn't contain an error code it will be NULL or a + * kmalloc()ed string, either of which is ok to hand to kfree(). + */ if (!IS_ERR(lg->dead)) kfree(lg->dead); /* Free the memory allocated to the lguest_struct */ @@ -385,7 +506,8 @@ static int close(struct inode *inode, struct file *file) * * We begin our understanding with the Host kernel interface which the Launcher * uses: reading and writing a character device called /dev/lguest. All the - * work happens in the read(), write() and close() routines: */ + * work happens in the read(), write() and close() routines: + */ static struct file_operations lguest_fops = { .owner = THIS_MODULE, .release = close, @@ -393,8 +515,10 @@ static struct file_operations lguest_fops = { .read = read, }; -/* This is a textbook example of a "misc" character device. Populate a "struct - * miscdevice" and register it with misc_register(). */ +/* + * This is a textbook example of a "misc" character device. Populate a "struct + * miscdevice" and register it with misc_register(). + */ static struct miscdevice lguest_dev = { .minor = MISC_DYNAMIC_MINOR, .name = "lguest", diff --git a/drivers/lguest/page_tables.c b/drivers/lguest/page_tables.c index a6fe1abda24..a8d0aee3bc0 100644 --- a/drivers/lguest/page_tables.c +++ b/drivers/lguest/page_tables.c @@ -1,9 +1,11 @@ -/*P:700 The pagetable code, on the other hand, still shows the scars of +/*P:700 + * The pagetable code, on the other hand, still shows the scars of * previous encounters. It's functional, and as neat as it can be in the * circumstances, but be wary, for these things are subtle and break easily. * The Guest provides a virtual to physical mapping, but we can neither trust * it nor use it: we verify and convert it here then point the CPU to the - * converted Guest pages when running the Guest. :*/ + * converted Guest pages when running the Guest. +:*/ /* Copyright (C) Rusty Russell IBM Corporation 2006. * GPL v2 and any later version */ @@ -17,18 +19,20 @@ #include <asm/bootparam.h> #include "lg.h" -/*M:008 We hold reference to pages, which prevents them from being swapped. +/*M:008 + * We hold reference to pages, which prevents them from being swapped. * It'd be nice to have a callback in the "struct mm_struct" when Linux wants * to swap out. If we had this, and a shrinker callback to trim PTE pages, we - * could probably consider launching Guests as non-root. :*/ + * could probably consider launching Guests as non-root. +:*/ /*H:300 * The Page Table Code * - * We use two-level page tables for the Guest. If you're not entirely - * comfortable with virtual addresses, physical addresses and page tables then - * I recommend you review arch/x86/lguest/boot.c's "Page Table Handling" (with - * diagrams!). + * We use two-level page tables for the Guest, or three-level with PAE. If + * you're not entirely comfortable with virtual addresses, physical addresses + * and page tables then I recommend you review arch/x86/lguest/boot.c's "Page + * Table Handling" (with diagrams!). * * The Guest keeps page tables, but we maintain the actual ones here: these are * called "shadow" page tables. Which is a very Guest-centric name: these are @@ -45,16 +49,18 @@ * (v) Flushing (throwing away) page tables, * (vi) Mapping the Switcher when the Guest is about to run, * (vii) Setting up the page tables initially. - :*/ +:*/ - -/* 1024 entries in a page table page maps 1024 pages: 4MB. The Switcher is - * conveniently placed at the top 4MB, so it uses a separate, complete PTE - * page. */ +/* + * The Switcher uses the complete top PTE page. That's 1024 PTE entries (4MB) + * or 512 PTE entries with PAE (2MB). + */ #define SWITCHER_PGD_INDEX (PTRS_PER_PGD - 1) -/* For PAE we need the PMD index as well. We use the last 2MB, so we - * will need the last pmd entry of the last pmd page. */ +/* + * For PAE we need the PMD index as well. We use the last 2MB, so we + * will need the last pmd entry of the last pmd page. + */ #ifdef CONFIG_X86_PAE #define SWITCHER_PMD_INDEX (PTRS_PER_PMD - 1) #define RESERVE_MEM 2U @@ -64,14 +70,18 @@ #define CHECK_GPGD_MASK _PAGE_TABLE #endif -/* We actually need a separate PTE page for each CPU. Remember that after the +/* + * We actually need a separate PTE page for each CPU. Remember that after the * Switcher code itself comes two pages for each CPU, and we don't want this - * CPU's guest to see the pages of any other CPU. */ + * CPU's guest to see the pages of any other CPU. + */ static DEFINE_PER_CPU(pte_t *, switcher_pte_pages); #define switcher_pte_page(cpu) per_cpu(switcher_pte_pages, cpu) -/*H:320 The page table code is curly enough to need helper functions to keep it - * clear and clean. +/*H:320 + * The page table code is curly enough to need helper functions to keep it + * clear and clean. The kernel itself provides many of them; one advantage + * of insisting that the Guest and Host use the same CONFIG_PAE setting. * * There are two functions which return pointers to the shadow (aka "real") * page tables. @@ -79,7 +89,8 @@ static DEFINE_PER_CPU(pte_t *, switcher_pte_pages); * spgd_addr() takes the virtual address and returns a pointer to the top-level * page directory entry (PGD) for that address. Since we keep track of several * page tables, the "i" argument tells us which one we're interested in (it's - * usually the current one). */ + * usually the current one). + */ static pgd_t *spgd_addr(struct lg_cpu *cpu, u32 i, unsigned long vaddr) { unsigned int index = pgd_index(vaddr); @@ -96,9 +107,11 @@ static pgd_t *spgd_addr(struct lg_cpu *cpu, u32 i, unsigned long vaddr) } #ifdef CONFIG_X86_PAE -/* This routine then takes the PGD entry given above, which contains the +/* + * This routine then takes the PGD entry given above, which contains the * address of the PMD page. It then returns a pointer to the PMD entry for the - * given address. */ + * given address. + */ static pmd_t *spmd_addr(struct lg_cpu *cpu, pgd_t spgd, unsigned long vaddr) { unsigned int index = pmd_index(vaddr); @@ -119,9 +132,11 @@ static pmd_t *spmd_addr(struct lg_cpu *cpu, pgd_t spgd, unsigned long vaddr) } #endif -/* This routine then takes the page directory entry returned above, which +/* + * This routine then takes the page directory entry returned above, which * contains the address of the page table entry (PTE) page. It then returns a - * pointer to the PTE entry for the given address. */ + * pointer to the PTE entry for the given address. + */ static pte_t *spte_addr(struct lg_cpu *cpu, pgd_t spgd, unsigned long vaddr) { #ifdef CONFIG_X86_PAE @@ -139,8 +154,10 @@ static pte_t *spte_addr(struct lg_cpu *cpu, pgd_t spgd, unsigned long vaddr) return &page[pte_index(vaddr)]; } -/* These two functions just like the above two, except they access the Guest - * page tables. Hence they return a Guest address. */ +/* + * These functions are just like the above two, except they access the Guest + * page tables. Hence they return a Guest address. + */ static unsigned long gpgd_addr(struct lg_cpu *cpu, unsigned long vaddr) { unsigned int index = vaddr >> (PGDIR_SHIFT); @@ -148,6 +165,7 @@ static unsigned long gpgd_addr(struct lg_cpu *cpu, unsigned long vaddr) } #ifdef CONFIG_X86_PAE +/* Follow the PGD to the PMD. */ static unsigned long gpmd_addr(pgd_t gpgd, unsigned long vaddr) { unsigned long gpage = pgd_pfn(gpgd) << PAGE_SHIFT; @@ -155,6 +173,7 @@ static unsigned long gpmd_addr(pgd_t gpgd, unsigned long vaddr) return gpage + pmd_index(vaddr) * sizeof(pmd_t); } +/* Follow the PMD to the PTE. */ static unsigned long gpte_addr(struct lg_cpu *cpu, pmd_t gpmd, unsigned long vaddr) { @@ -164,6 +183,7 @@ static unsigned long gpte_addr(struct lg_cpu *cpu, return gpage + pte_index(vaddr) * sizeof(pte_t); } #else +/* Follow the PGD to the PTE (no mid-level for !PAE). */ static unsigned long gpte_addr(struct lg_cpu *cpu, pgd_t gpgd, unsigned long vaddr) { @@ -175,17 +195,21 @@ static unsigned long gpte_addr(struct lg_cpu *cpu, #endif /*:*/ -/*M:014 get_pfn is slow: we could probably try to grab batches of pages here as - * an optimization (ie. pre-faulting). :*/ +/*M:014 + * get_pfn is slow: we could probably try to grab batches of pages here as + * an optimization (ie. pre-faulting). +:*/ -/*H:350 This routine takes a page number given by the Guest and converts it to +/*H:350 + * This routine takes a page number given by the Guest and converts it to * an actual, physical page number. It can fail for several reasons: the * virtual address might not be mapped by the Launcher, the write flag is set * and the page is read-only, or the write flag was set and the page was * shared so had to be copied, but we ran out of memory. * * This holds a reference to the page, so release_pte() is careful to put that - * back. */ + * back. + */ static unsigned long get_pfn(unsigned long virtpfn, int write) { struct page *page; @@ -198,33 +222,41 @@ static unsigned long get_pfn(unsigned long virtpfn, int write) return -1UL; } -/*H:340 Converting a Guest page table entry to a shadow (ie. real) page table +/*H:340 + * Converting a Guest page table entry to a shadow (ie. real) page table * entry can be a little tricky. The flags are (almost) the same, but the * Guest PTE contains a virtual page number: the CPU needs the real page - * number. */ + * number. + */ static pte_t gpte_to_spte(struct lg_cpu *cpu, pte_t gpte, int write) { unsigned long pfn, base, flags; - /* The Guest sets the global flag, because it thinks that it is using + /* + * The Guest sets the global flag, because it thinks that it is using * PGE. We only told it to use PGE so it would tell us whether it was * flushing a kernel mapping or a userspace mapping. We don't actually - * use the global bit, so throw it away. */ + * use the global bit, so throw it away. + */ flags = (pte_flags(gpte) & ~_PAGE_GLOBAL); /* The Guest's pages are offset inside the Launcher. */ base = (unsigned long)cpu->lg->mem_base / PAGE_SIZE; - /* We need a temporary "unsigned long" variable to hold the answer from + /* + * We need a temporary "unsigned long" variable to hold the answer from * get_pfn(), because it returns 0xFFFFFFFF on failure, which wouldn't * fit in spte.pfn. get_pfn() finds the real physical number of the - * page, given the virtual number. */ + * page, given the virtual number. + */ pfn = get_pfn(base + pte_pfn(gpte), write); if (pfn == -1UL) { kill_guest(cpu, "failed to get page %lu", pte_pfn(gpte)); - /* When we destroy the Guest, we'll go through the shadow page + /* + * When we destroy the Guest, we'll go through the shadow page * tables and release_pte() them. Make sure we don't think - * this one is valid! */ + * this one is valid! + */ flags = 0; } /* Now we assemble our shadow PTE from the page number and flags. */ @@ -234,8 +266,10 @@ static pte_t gpte_to_spte(struct lg_cpu *cpu, pte_t gpte, int write) /*H:460 And to complete the chain, release_pte() looks like this: */ static void release_pte(pte_t pte) { - /* Remember that get_user_pages_fast() took a reference to the page, in - * get_pfn()? We have to put it back now. */ + /* + * Remember that get_user_pages_fast() took a reference to the page, in + * get_pfn()? We have to put it back now. + */ if (pte_flags(pte) & _PAGE_PRESENT) put_page(pte_page(pte)); } @@ -273,7 +307,8 @@ static void check_gpmd(struct lg_cpu *cpu, pmd_t gpmd) * and return to the Guest without it knowing. * * 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. */ + * true. Otherwise, it was a real fault and we need to tell the Guest. + */ bool demand_page(struct lg_cpu *cpu, unsigned long vaddr, int errcode) { pgd_t gpgd; @@ -282,6 +317,7 @@ bool demand_page(struct lg_cpu *cpu, unsigned long vaddr, int errcode) pte_t gpte; pte_t *spte; + /* Mid level for PAE. */ #ifdef CONFIG_X86_PAE pmd_t *spmd; pmd_t gpmd; @@ -298,22 +334,26 @@ bool demand_page(struct lg_cpu *cpu, unsigned long vaddr, int errcode) if (!(pgd_flags(*spgd) & _PAGE_PRESENT)) { /* No shadow entry: allocate a new shadow PTE page. */ unsigned long ptepage = get_zeroed_page(GFP_KERNEL); - /* This is not really the Guest's fault, but killing it is - * simple for this corner case. */ + /* + * This is not really the Guest's fault, but killing it is + * simple for this corner case. + */ if (!ptepage) { kill_guest(cpu, "out of memory allocating pte page"); return false; } /* We check that the Guest pgd is OK. */ check_gpgd(cpu, gpgd); - /* And we copy the flags to the shadow PGD entry. The page - * number in the shadow PGD is the page we just allocated. */ + /* + * And we copy the flags to the shadow PGD entry. The page + * number in the shadow PGD is the page we just allocated. + */ set_pgd(spgd, __pgd(__pa(ptepage) | pgd_flags(gpgd))); } #ifdef CONFIG_X86_PAE gpmd = lgread(cpu, gpmd_addr(gpgd, vaddr), pmd_t); - /* middle level not present? We can't map it in. */ + /* Middle level not present? We can't map it in. */ if (!(pmd_flags(gpmd) & _PAGE_PRESENT)) return false; @@ -324,8 +364,10 @@ bool demand_page(struct lg_cpu *cpu, unsigned long vaddr, int errcode) /* No shadow entry: allocate a new shadow PTE page. */ unsigned long ptepage = get_zeroed_page(GFP_KERNEL); - /* This is not really the Guest's fault, but killing it is - * simple for this corner case. */ + /* + * This is not really the Guest's fault, but killing it is + * simple for this corner case. + */ if (!ptepage) { kill_guest(cpu, "out of memory allocating pte page"); return false; @@ -334,27 +376,37 @@ bool demand_page(struct lg_cpu *cpu, unsigned long vaddr, int errcode) /* We check that the Guest pmd is OK. */ check_gpmd(cpu, gpmd); - /* And we copy the flags to the shadow PMD entry. The page - * number in the shadow PMD is the page we just allocated. */ + /* + * And we copy the flags to the shadow PMD entry. The page + * number in the shadow PMD is the page we just allocated. + */ native_set_pmd(spmd, __pmd(__pa(ptepage) | pmd_flags(gpmd))); } - /* OK, now we look at the lower level in the Guest page table: keep its - * address, because we might update it later. */ + /* + * OK, now we look at the lower level in the Guest page table: keep its + * address, because we might update it later. + */ gpte_ptr = gpte_addr(cpu, gpmd, vaddr); #else - /* OK, now we look at the lower level in the Guest page table: keep its - * address, because we might update it later. */ + /* + * OK, now we look at the lower level in the Guest page table: keep its + * address, because we might update it later. + */ gpte_ptr = gpte_addr(cpu, gpgd, vaddr); #endif + + /* Read the actual PTE value. */ gpte = lgread(cpu, gpte_ptr, pte_t); /* If this page isn't in the Guest page tables, we can't page it in. */ if (!(pte_flags(gpte) & _PAGE_PRESENT)) return false; - /* Check they're not trying to write to a page the Guest wants - * read-only (bit 2 of errcode == write). */ + /* + * 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 false; @@ -362,8 +414,10 @@ bool demand_page(struct lg_cpu *cpu, unsigned long vaddr, int errcode) if ((errcode & 4) && !(pte_flags(gpte) & _PAGE_USER)) 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). */ + /* + * Check that the Guest PTE flags are OK, and the page number is below + * the pfn_limit (ie. not mapping the Launcher binary). + */ check_gpte(cpu, gpte); /* Add the _PAGE_ACCESSED and (for a write) _PAGE_DIRTY flag */ @@ -373,29 +427,40 @@ bool demand_page(struct lg_cpu *cpu, unsigned long vaddr, int errcode) /* Get the pointer to the shadow PTE entry we're going to set. */ spte = spte_addr(cpu, *spgd, vaddr); - /* If there was a valid shadow PTE entry here before, we release it. - * This can happen with a write to a previously read-only entry. */ + + /* + * If there was a valid shadow PTE entry here before, we release it. + * This can happen with a write to a previously read-only entry. + */ release_pte(*spte); - /* If this is a write, we insist that the Guest page is writable (the - * final arg to gpte_to_spte()). */ + /* + * If this is a write, we insist that the Guest page is writable (the + * final arg to gpte_to_spte()). + */ if (pte_dirty(gpte)) *spte = gpte_to_spte(cpu, gpte, 1); else - /* If this is a read, don't set the "writable" bit in the page + /* + * If this is a read, don't set the "writable" bit in the page * table entry, even if the Guest says it's writable. That way * we will come back here when a write does actually occur, so - * we can update the Guest's _PAGE_DIRTY flag. */ + * we can update the Guest's _PAGE_DIRTY flag. + */ native_set_pte(spte, gpte_to_spte(cpu, pte_wrprotect(gpte), 0)); - /* Finally, we write the Guest PTE entry back: we've set the - * _PAGE_ACCESSED and maybe the _PAGE_DIRTY flags. */ + /* + * Finally, we write the Guest PTE entry back: we've set the + * _PAGE_ACCESSED and maybe the _PAGE_DIRTY flags. + */ lgwrite(cpu, gpte_ptr, pte_t, gpte); - /* The fault is fixed, the page table is populated, the mapping + /* + * The fault is fixed, the page table is populated, the mapping * 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. */ + * has that a page fault occurred at all. + */ return true; } @@ -408,7 +473,8 @@ bool demand_page(struct lg_cpu *cpu, unsigned long vaddr, int errcode) * mapped, so it's overkill. * * This is a quick version which answers the question: is this virtual address - * mapped by the shadow page tables, and is it writable? */ + * mapped by the shadow page tables, and is it writable? + */ static bool page_writable(struct lg_cpu *cpu, unsigned long vaddr) { pgd_t *spgd; @@ -428,21 +494,26 @@ static bool page_writable(struct lg_cpu *cpu, unsigned long vaddr) return false; #endif - /* Check the flags on the pte entry itself: it must be present and - * writable. */ + /* + * Check the flags on the pte entry itself: it must be present and + * writable. + */ flags = pte_flags(*(spte_addr(cpu, *spgd, vaddr))); return (flags & (_PAGE_PRESENT|_PAGE_RW)) == (_PAGE_PRESENT|_PAGE_RW); } -/* So, when pin_stack_pages() asks us to pin a page, we check if it's already +/* + * So, when pin_stack_pages() asks us to pin a page, we check if it's already * in the page tables, and if not, we call demand_page() with error code 2 - * (meaning "write"). */ + * (meaning "write"). + */ void pin_page(struct lg_cpu *cpu, unsigned long vaddr) { if (!page_writable(cpu, vaddr) && !demand_page(cpu, vaddr, 2)) kill_guest(cpu, "bad stack page %#lx", vaddr); } +/*:*/ #ifdef CONFIG_X86_PAE static void release_pmd(pmd_t *spmd) @@ -479,15 +550,21 @@ static void release_pgd(pgd_t *spgd) } #else /* !CONFIG_X86_PAE */ -/*H:450 If we chase down the release_pgd() code, it looks like this: */ +/*H:450 + * If we chase down the release_pgd() code, the non-PAE version looks like + * this. The PAE version is almost identical, but instead of calling + * release_pte it calls release_pmd(), which looks much like this. + */ static void release_pgd(pgd_t *spgd) { /* If the entry's not present, there's nothing to release. */ if (pgd_flags(*spgd) & _PAGE_PRESENT) { unsigned int i; - /* Converting the pfn to find the actual PTE page is easy: turn + /* + * Converting the pfn to find the actual PTE page is easy: turn * the page number into a physical address, then convert to a - * virtual address (easy for kernel pages like this one). */ + * virtual address (easy for kernel pages like this one). + */ pte_t *ptepage = __va(pgd_pfn(*spgd) << PAGE_SHIFT); /* For each entry in the page, we might need to release it. */ for (i = 0; i < PTRS_PER_PTE; i++) @@ -499,9 +576,12 @@ static void release_pgd(pgd_t *spgd) } } #endif -/*H:445 We saw flush_user_mappings() twice: once from the flush_user_mappings() + +/*H:445 + * We saw flush_user_mappings() twice: once from the flush_user_mappings() * hypercall and once in new_pgdir() when we re-used a top-level pgdir page. - * It simply releases every PTE page from 0 up to the Guest's kernel address. */ + * It simply releases every PTE page from 0 up to the Guest's kernel address. + */ static void flush_user_mappings(struct lguest *lg, int idx) { unsigned int i; @@ -510,10 +590,12 @@ static void flush_user_mappings(struct lguest *lg, int idx) release_pgd(lg->pgdirs[idx].pgdir + i); } -/*H:440 (v) Flushing (throwing away) page tables, +/*H:440 + * (v) Flushing (throwing away) page tables, * * The Guest has a hypercall to throw away the page tables: it's used when a - * large number of mappings have been changed. */ + * large number of mappings have been changed. + */ void guest_pagetable_flush_user(struct lg_cpu *cpu) { /* Drop the userspace part of the current page table. */ @@ -551,9 +633,11 @@ unsigned long guest_pa(struct lg_cpu *cpu, unsigned long vaddr) return pte_pfn(gpte) * PAGE_SIZE | (vaddr & ~PAGE_MASK); } -/* We keep several page tables. This is a simple routine to find the page +/* + * We keep several page tables. This is a simple routine to find the page * table (if any) corresponding to this top-level address the Guest has given - * us. */ + * us. + */ static unsigned int find_pgdir(struct lguest *lg, unsigned long pgtable) { unsigned int i; @@ -563,9 +647,11 @@ static unsigned int find_pgdir(struct lguest *lg, unsigned long pgtable) return i; } -/*H:435 And this is us, creating the new page directory. If we really do +/*H:435 + * And this is us, creating the new page directory. If we really do * allocate a new one (and so the kernel parts are not there), we set - * blank_pgdir. */ + * blank_pgdir. + */ static unsigned int new_pgdir(struct lg_cpu *cpu, unsigned long gpgdir, int *blank_pgdir) @@ -575,8 +661,10 @@ static unsigned int new_pgdir(struct lg_cpu *cpu, pmd_t *pmd_table; #endif - /* We pick one entry at random to throw out. Choosing the Least - * Recently Used might be better, but this is easy. */ + /* + * We pick one entry at random to throw out. Choosing the Least + * Recently Used might be better, but this is easy. + */ next = random32() % ARRAY_SIZE(cpu->lg->pgdirs); /* If it's never been allocated at all before, try now. */ if (!cpu->lg->pgdirs[next].pgdir) { @@ -587,8 +675,10 @@ static unsigned int new_pgdir(struct lg_cpu *cpu, next = cpu->cpu_pgd; else { #ifdef CONFIG_X86_PAE - /* In PAE mode, allocate a pmd page and populate the - * last pgd entry. */ + /* + * In PAE mode, allocate a pmd page and populate the + * last pgd entry. + */ pmd_table = (pmd_t *)get_zeroed_page(GFP_KERNEL); if (!pmd_table) { free_page((long)cpu->lg->pgdirs[next].pgdir); @@ -598,8 +688,10 @@ static unsigned int new_pgdir(struct lg_cpu *cpu, set_pgd(cpu->lg->pgdirs[next].pgdir + SWITCHER_PGD_INDEX, __pgd(__pa(pmd_table) | _PAGE_PRESENT)); - /* This is a blank page, so there are no kernel - * mappings: caller must map the stack! */ + /* + * This is a blank page, so there are no kernel + * mappings: caller must map the stack! + */ *blank_pgdir = 1; } #else @@ -615,19 +707,23 @@ static unsigned int new_pgdir(struct lg_cpu *cpu, return next; } -/*H:430 (iv) Switching page tables +/*H:430 + * (iv) Switching page tables * * Now we've seen all the page table setting and manipulation, let's see * what happens when the Guest changes page tables (ie. changes the top-level - * pgdir). This occurs on almost every context switch. */ + * pgdir). This occurs on almost every context switch. + */ void guest_new_pagetable(struct lg_cpu *cpu, unsigned long pgtable) { int newpgdir, repin = 0; /* Look to see if we have this one already. */ newpgdir = find_pgdir(cpu->lg, pgtable); - /* If not, we allocate or mug an existing one: if it's a fresh one, - * repin gets set to 1. */ + /* + * If not, we allocate or mug an existing one: if it's a fresh one, + * repin gets set to 1. + */ if (newpgdir == ARRAY_SIZE(cpu->lg->pgdirs)) newpgdir = new_pgdir(cpu, pgtable, &repin); /* Change the current pgd index to the new one. */ @@ -637,9 +733,11 @@ void guest_new_pagetable(struct lg_cpu *cpu, unsigned long pgtable) pin_stack_pages(cpu); } -/*H:470 Finally, a routine which throws away everything: all PGD entries in all +/*H:470 + * Finally, a routine which throws away everything: all PGD entries in all * the shadow page tables, including the Guest's kernel mappings. This is used - * when we destroy the Guest. */ + * when we destroy the Guest. + */ static void release_all_pagetables(struct lguest *lg) { unsigned int i, j; @@ -656,8 +754,10 @@ static void release_all_pagetables(struct lguest *lg) spgd = lg->pgdirs[i].pgdir + SWITCHER_PGD_INDEX; pmdpage = __va(pgd_pfn(*spgd) << PAGE_SHIFT); - /* And release the pmd entries of that pmd page, - * except for the switcher pmd. */ + /* + * And release the pmd entries of that pmd page, + * except for the switcher pmd. + */ for (k = 0; k < SWITCHER_PMD_INDEX; k++) release_pmd(&pmdpage[k]); #endif @@ -667,10 +767,12 @@ static void release_all_pagetables(struct lguest *lg) } } -/* We also throw away everything when a Guest tells us it's changed a kernel +/* + * We also throw away everything when a Guest tells us it's changed a kernel * mapping. Since kernel mappings are in every page table, it's easiest to * throw them all away. This traps the Guest in amber for a while as - * everything faults back in, but it's rare. */ + * everything faults back in, but it's rare. + */ void guest_pagetable_clear_all(struct lg_cpu *cpu) { release_all_pagetables(cpu->lg); @@ -678,15 +780,19 @@ void guest_pagetable_clear_all(struct lg_cpu *cpu) pin_stack_pages(cpu); } /*:*/ -/*M:009 Since we throw away all mappings when a kernel mapping changes, our + +/*M:009 + * Since we throw away all mappings when a kernel mapping changes, our * performance sucks for guests using highmem. In fact, a guest with * PAGE_OFFSET 0xc0000000 (the default) and more than about 700MB of RAM is * usually slower than a Guest with less memory. * * This, of course, cannot be fixed. It would take some kind of... well, I - * don't know, but the term "puissant code-fu" comes to mind. :*/ + * don't know, but the term "puissant code-fu" comes to mind. +:*/ -/*H:420 This is the routine which actually sets the page table entry for then +/*H:420 + * This is the routine which actually sets the page table entry for then * "idx"'th shadow page table. * * Normally, we can just throw out the old entry and replace it with 0: if they @@ -715,31 +821,36 @@ static void do_set_pte(struct lg_cpu *cpu, int idx, spmd = spmd_addr(cpu, *spgd, vaddr); if (pmd_flags(*spmd) & _PAGE_PRESENT) { #endif - /* Otherwise, we start by releasing - * the existing entry. */ + /* Otherwise, start by releasing the existing entry. */ pte_t *spte = spte_addr(cpu, *spgd, vaddr); release_pte(*spte); - /* If they're setting this entry as dirty or accessed, - * we might as well put that entry they've given us - * in now. This shaves 10% off a - * copy-on-write micro-benchmark. */ + /* + * If they're setting this entry as dirty or accessed, + * we might as well put that entry they've given us in + * now. This shaves 10% off a copy-on-write + * micro-benchmark. + */ if (pte_flags(gpte) & (_PAGE_DIRTY | _PAGE_ACCESSED)) { check_gpte(cpu, gpte); native_set_pte(spte, gpte_to_spte(cpu, gpte, pte_flags(gpte) & _PAGE_DIRTY)); - } else - /* Otherwise kill it and we can demand_page() - * it in later. */ + } else { + /* + * Otherwise kill it and we can demand_page() + * it in later. + */ native_set_pte(spte, __pte(0)); + } #ifdef CONFIG_X86_PAE } #endif } } -/*H:410 Updating a PTE entry is a little trickier. +/*H:410 + * Updating a PTE entry is a little trickier. * * We keep track of several different page tables (the Guest uses one for each * process, so it makes sense to cache at least a few). Each of these have @@ -748,12 +859,15 @@ static void do_set_pte(struct lg_cpu *cpu, int idx, * all the page tables, not just the current one. This is rare. * * The benefit is that when we have to track a new page table, we can keep all - * the kernel mappings. This speeds up context switch immensely. */ + * the kernel mappings. This speeds up context switch immensely. + */ void guest_set_pte(struct lg_cpu *cpu, unsigned long gpgdir, unsigned long vaddr, pte_t gpte) { - /* Kernel mappings must be changed on all top levels. Slow, but doesn't - * happen often. */ + /* + * Kernel mappings must be changed on all top levels. Slow, but doesn't + * happen often. + */ if (vaddr >= cpu->lg->kernel_address) { unsigned int i; for (i = 0; i < ARRAY_SIZE(cpu->lg->pgdirs); i++) @@ -795,19 +909,25 @@ void guest_set_pgd(struct lguest *lg, unsigned long gpgdir, u32 idx) /* ... throw it away. */ release_pgd(lg->pgdirs[pgdir].pgdir + idx); } + #ifdef CONFIG_X86_PAE +/* For setting a mid-level, we just throw everything away. It's easy. */ void guest_set_pmd(struct lguest *lg, unsigned long pmdp, u32 idx) { guest_pagetable_clear_all(&lg->cpus[0]); } #endif -/* Once we know how much memory we have we can construct simple identity - * (which set virtual == physical) and linear mappings - * which will get the Guest far enough into the boot to create its own. +/*H:505 + * To get through boot, we construct simple identity page mappings (which + * set virtual == physical) and linear mappings which will get the Guest far + * enough into the boot to create its own. The linear mapping means we + * simplify the Guest boot, but it makes assumptions about their PAGE_OFFSET, + * as you'll see. * * We lay them out of the way, just below the initrd (which is why we need to - * know its size here). */ + * know its size here). + */ static unsigned long setup_pagetables(struct lguest *lg, unsigned long mem, unsigned long initrd_size) @@ -825,8 +945,10 @@ static unsigned long setup_pagetables(struct lguest *lg, unsigned int phys_linear; #endif - /* We have mapped_pages frames to map, so we need - * linear_pages page tables to map them. */ + /* + * We have mapped_pages frames to map, so we need linear_pages page + * tables to map them. + */ mapped_pages = mem / PAGE_SIZE; linear_pages = (mapped_pages + PTRS_PER_PTE - 1) / PTRS_PER_PTE; @@ -837,10 +959,16 @@ static unsigned long setup_pagetables(struct lguest *lg, linear = (void *)pgdir - linear_pages * PAGE_SIZE; #ifdef CONFIG_X86_PAE + /* + * And the single mid page goes below that. We only use one, but + * that's enough to map 1G, which definitely gets us through boot. + */ pmds = (void *)linear - PAGE_SIZE; #endif - /* Linear mapping is easy: put every page's address into the - * mapping in order. */ + /* + * Linear mapping is easy: put every page's address into the + * mapping in order. + */ for (i = 0; i < mapped_pages; i++) { pte_t pte; pte = pfn_pte(i, __pgprot(_PAGE_PRESENT|_PAGE_RW|_PAGE_USER)); @@ -848,11 +976,14 @@ static unsigned long setup_pagetables(struct lguest *lg, return -EFAULT; } - /* The top level points to the linear page table pages above. - * We setup the identity and linear mappings here. */ #ifdef CONFIG_X86_PAE + /* + * Make the Guest PMD entries point to the corresponding place in the + * linear mapping (up to one page worth of PMD). + */ for (i = j = 0; i < mapped_pages && j < PTRS_PER_PMD; i += PTRS_PER_PTE, j++) { + /* FIXME: native_set_pmd is overkill here. */ native_set_pmd(&pmd, __pmd(((unsigned long)(linear + i) - mem_base) | _PAGE_PRESENT | _PAGE_RW | _PAGE_USER)); @@ -860,18 +991,36 @@ static unsigned long setup_pagetables(struct lguest *lg, return -EFAULT; } + /* One PGD entry, pointing to that PMD page. */ set_pgd(&pgd, __pgd(((u32)pmds - mem_base) | _PAGE_PRESENT)); + /* Copy it in as the first PGD entry (ie. addresses 0-1G). */ if (copy_to_user(&pgdir[0], &pgd, sizeof(pgd)) != 0) return -EFAULT; + /* + * And the third PGD entry (ie. addresses 3G-4G). + * + * FIXME: This assumes that PAGE_OFFSET for the Guest is 0xC0000000. + */ if (copy_to_user(&pgdir[3], &pgd, sizeof(pgd)) != 0) return -EFAULT; #else + /* + * The top level points to the linear page table pages above. + * We setup the identity and linear mappings here. + */ phys_linear = (unsigned long)linear - mem_base; for (i = 0; i < mapped_pages; i += PTRS_PER_PTE) { pgd_t pgd; + /* + * Create a PGD entry which points to the right part of the + * linear PTE pages. + */ pgd = __pgd((phys_linear + i * sizeof(pte_t)) | (_PAGE_PRESENT | _PAGE_RW | _PAGE_USER)); + /* + * Copy it into the PGD page at 0 and PAGE_OFFSET. + */ if (copy_to_user(&pgdir[i / PTRS_PER_PTE], &pgd, sizeof(pgd)) || copy_to_user(&pgdir[pgd_index(PAGE_OFFSET) + i / PTRS_PER_PTE], @@ -880,15 +1029,19 @@ static unsigned long setup_pagetables(struct lguest *lg, } #endif - /* We return the top level (guest-physical) address: remember where - * this is. */ + /* + * We return the top level (guest-physical) address: we remember where + * this is to write it into lguest_data when the Guest initializes. + */ return (unsigned long)pgdir - mem_base; } -/*H:500 (vii) Setting up the page tables initially. +/*H:500 + * (vii) Setting up the page tables initially. * * When a Guest is first created, the Launcher tells us where the toplevel of - * its first page table is. We set some things up here: */ + * its first page table is. We set some things up here: + */ int init_guest_pagetable(struct lguest *lg) { u64 mem; @@ -898,21 +1051,27 @@ int init_guest_pagetable(struct lguest *lg) pgd_t *pgd; pmd_t *pmd_table; #endif - /* Get the Guest memory size and the ramdisk size from the boot header - * located at lg->mem_base (Guest address 0). */ + /* + * Get the Guest memory size and the ramdisk size from the boot header + * located at lg->mem_base (Guest address 0). + */ if (copy_from_user(&mem, &boot->e820_map[0].size, sizeof(mem)) || get_user(initrd_size, &boot->hdr.ramdisk_size)) return -EFAULT; - /* We start on the first shadow page table, and give it a blank PGD - * page. */ + /* + * We start on the first shadow page table, and give it a blank PGD + * page. + */ lg->pgdirs[0].gpgdir = setup_pagetables(lg, mem, initrd_size); if (IS_ERR_VALUE(lg->pgdirs[0].gpgdir)) return lg->pgdirs[0].gpgdir; lg->pgdirs[0].pgdir = (pgd_t *)get_zeroed_page(GFP_KERNEL); if (!lg->pgdirs[0].pgdir) return -ENOMEM; + #ifdef CONFIG_X86_PAE + /* For PAE, we also create the initial mid-level. */ pgd = lg->pgdirs[0].pgdir; pmd_table = (pmd_t *) get_zeroed_page(GFP_KERNEL); if (!pmd_table) @@ -921,27 +1080,33 @@ int init_guest_pagetable(struct lguest *lg) set_pgd(pgd + SWITCHER_PGD_INDEX, __pgd(__pa(pmd_table) | _PAGE_PRESENT)); #endif + + /* This is the current page table. */ lg->cpus[0].cpu_pgd = 0; return 0; } -/* When the Guest calls LHCALL_LGUEST_INIT we do more setup. */ +/*H:508 When the Guest calls LHCALL_LGUEST_INIT we do more setup. */ void page_table_guest_data_init(struct lg_cpu *cpu) { /* We get the kernel address: above this is all kernel memory. */ if (get_user(cpu->lg->kernel_address, &cpu->lg->lguest_data->kernel_address) - /* We tell the Guest that it can't use the top 2 or 4 MB - * of virtual addresses used by the Switcher. */ + /* + * We tell the Guest that it can't use the top 2 or 4 MB + * of virtual addresses used by the Switcher. + */ || put_user(RESERVE_MEM * 1024 * 1024, &cpu->lg->lguest_data->reserve_mem) || put_user(cpu->lg->pgdirs[0].gpgdir, &cpu->lg->lguest_data->pgdir)) kill_guest(cpu, "bad guest page %p", cpu->lg->lguest_data); - /* In flush_user_mappings() we loop from 0 to + /* + * In flush_user_mappings() we loop from 0 to * "pgd_index(lg->kernel_address)". This assumes it won't hit the - * Switcher mappings, so check that now. */ + * Switcher mappings, so check that now. + */ #ifdef CONFIG_X86_PAE if (pgd_index(cpu->lg->kernel_address) == SWITCHER_PGD_INDEX && pmd_index(cpu->lg->kernel_address) == SWITCHER_PMD_INDEX) @@ -964,12 +1129,14 @@ void free_guest_pagetable(struct lguest *lg) free_page((long)lg->pgdirs[i].pgdir); } -/*H:480 (vi) Mapping the Switcher when the Guest is about to run. +/*H:480 + * (vi) Mapping the Switcher when the Guest is about to run. * * The Switcher and the two pages for this CPU need to be visible in the * Guest (and not the pages for other CPUs). We have the appropriate PTE pages * for each CPU already set up, we just need to hook them in now we know which - * Guest is about to run on this CPU. */ + * Guest is about to run on this CPU. + */ void map_switcher_in_guest(struct lg_cpu *cpu, struct lguest_pages *pages) { pte_t *switcher_pte_page = __get_cpu_var(switcher_pte_pages); @@ -980,30 +1147,38 @@ void map_switcher_in_guest(struct lg_cpu *cpu, struct lguest_pages *pages) pmd_t switcher_pmd; pmd_t *pmd_table; + /* FIXME: native_set_pmd is overkill here. */ native_set_pmd(&switcher_pmd, pfn_pmd(__pa(switcher_pte_page) >> PAGE_SHIFT, PAGE_KERNEL_EXEC)); + /* Figure out where the pmd page is, by reading the PGD, and converting + * it to a virtual address. */ pmd_table = __va(pgd_pfn(cpu->lg-> pgdirs[cpu->cpu_pgd].pgdir[SWITCHER_PGD_INDEX]) << PAGE_SHIFT); + /* Now write it into the shadow page table. */ native_set_pmd(&pmd_table[SWITCHER_PMD_INDEX], switcher_pmd); #else pgd_t switcher_pgd; - /* Make the last PGD entry for this Guest point to the Switcher's PTE - * page for this CPU (with appropriate flags). */ + /* + * Make the last PGD entry for this Guest point to the Switcher's PTE + * page for this CPU (with appropriate flags). + */ switcher_pgd = __pgd(__pa(switcher_pte_page) | __PAGE_KERNEL_EXEC); cpu->lg->pgdirs[cpu->cpu_pgd].pgdir[SWITCHER_PGD_INDEX] = switcher_pgd; #endif - /* We also change the Switcher PTE page. When we're running the Guest, + /* + * We also change the Switcher PTE page. When we're running the Guest, * we want the Guest's "regs" page to appear where the first Switcher * page for this CPU is. This is an optimization: when the Switcher * saves the Guest registers, it saves them into the first page of this * CPU's "struct lguest_pages": if we make sure the Guest's register * page is already mapped there, we don't have to copy them out - * again. */ + * again. + */ pfn = __pa(cpu->regs_page) >> PAGE_SHIFT; native_set_pte(®s_pte, pfn_pte(pfn, PAGE_KERNEL)); native_set_pte(&switcher_pte_page[pte_index((unsigned long)pages)], @@ -1019,10 +1194,12 @@ static void free_switcher_pte_pages(void) free_page((long)switcher_pte_page(i)); } -/*H:520 Setting up the Switcher PTE page for given CPU is fairly easy, given +/*H:520 + * Setting up the Switcher PTE page for given CPU is fairly easy, given * the CPU number and the "struct page"s for the Switcher code itself. * - * Currently the Switcher is less than a page long, so "pages" is always 1. */ + * Currently the Switcher is less than a page long, so "pages" is always 1. + */ static __init void populate_switcher_pte_page(unsigned int cpu, struct page *switcher_page[], unsigned int pages) @@ -1043,13 +1220,16 @@ static __init void populate_switcher_pte_page(unsigned int cpu, native_set_pte(&pte[i], pfn_pte(page_to_pfn(switcher_page[i]), __pgprot(_PAGE_PRESENT|_PAGE_ACCESSED|_PAGE_RW))); - /* The second page contains the "struct lguest_ro_state", and is - * read-only. */ + /* + * The second page contains the "struct lguest_ro_state", and is + * read-only. + */ native_set_pte(&pte[i+1], pfn_pte(page_to_pfn(switcher_page[i+1]), __pgprot(_PAGE_PRESENT|_PAGE_ACCESSED))); } -/* We've made it through the page table code. Perhaps our tired brains are +/* + * We've made it through the page table code. Perhaps our tired brains are * still processing the details, or perhaps we're simply glad it's over. * * If nothing else, note that all this complexity in juggling shadow page tables @@ -1058,10 +1238,13 @@ static __init void populate_switcher_pte_page(unsigned int cpu, * uses exotic direct Guest pagetable manipulation, and why both Intel and AMD * have implemented shadow page table support directly into hardware. * - * There is just one file remaining in the Host. */ + * There is just one file remaining in the Host. + */ -/*H:510 At boot or module load time, init_pagetables() allocates and populates - * the Switcher PTE page for each CPU. */ +/*H:510 + * At boot or module load time, init_pagetables() allocates and populates + * the Switcher PTE page for each CPU. + */ __init int init_pagetables(struct page **switcher_page, unsigned int pages) { unsigned int i; diff --git a/drivers/lguest/segments.c b/drivers/lguest/segments.c index 482ed5a1875..951c57b0a7e 100644 --- a/drivers/lguest/segments.c +++ b/drivers/lguest/segments.c @@ -1,4 +1,5 @@ -/*P:600 The x86 architecture has segments, which involve a table of descriptors +/*P:600 + * The x86 architecture has segments, which involve a table of descriptors * which can be used to do funky things with virtual address interpretation. * We originally used to use segments so the Guest couldn't alter the * Guest<->Host Switcher, and then we had to trim Guest segments, and restore @@ -8,7 +9,8 @@ * * In these modern times, the segment handling code consists of simple sanity * checks, and the worst you'll experience reading this code is butterfly-rash - * from frolicking through its parklike serenity. :*/ + * from frolicking through its parklike serenity. +:*/ #include "lg.h" /*H:600 @@ -41,10 +43,12 @@ * begin. */ -/* There are several entries we don't let the Guest set. The TSS entry is the +/* + * There are several entries we don't let the Guest set. The TSS entry is the * "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. */ + * the Guest can't be trusted to deal with double faults. + */ static bool ignored_gdt(unsigned int num) { return (num == GDT_ENTRY_TSS @@ -53,42 +57,52 @@ static bool ignored_gdt(unsigned int num) || num == GDT_ENTRY_DOUBLEFAULT_TSS); } -/*H:630 Once the Guest gave us new GDT entries, we fix them up a little. We +/*H:630 + * Once the Guest gave us new GDT entries, we fix them up a little. We * don't care if they're invalid: the worst that can happen is a General * Protection Fault in the Switcher when it restores a Guest segment register * which tries to use that entry. Then we kill the Guest for causing such a - * mess: the message will be "unhandled trap 256". */ + * mess: the message will be "unhandled trap 256". + */ static void fixup_gdt_table(struct lg_cpu *cpu, unsigned start, unsigned end) { unsigned int i; for (i = start; i < end; i++) { - /* We never copy these ones to real GDT, so we don't care what - * they say */ + /* + * We never copy these ones to real GDT, so we don't care what + * they say + */ if (ignored_gdt(i)) continue; - /* Segment descriptors contain a privilege level: the Guest is + /* + * Segment descriptors contain a privilege level: the Guest is * sometimes careless and leaves this as 0, even though it's - * running at privilege level 1. If so, we fix it here. */ + * running at privilege level 1. If so, we fix it here. + */ if ((cpu->arch.gdt[i].b & 0x00006000) == 0) cpu->arch.gdt[i].b |= (GUEST_PL << 13); - /* Each descriptor has an "accessed" bit. If we don't set it + /* + * Each descriptor has an "accessed" bit. If we don't set it * now, the CPU will try to set it when the Guest first loads * that entry into a segment register. But the GDT isn't - * writable by the Guest, so bad things can happen. */ + * writable by the Guest, so bad things can happen. + */ cpu->arch.gdt[i].b |= 0x00000100; } } -/*H:610 Like the IDT, we never simply use the GDT the Guest gives us. We keep +/*H:610 + * Like the IDT, we never simply use the GDT the Guest gives us. We keep * a GDT for each CPU, and copy across the Guest's entries each time we want to * run the Guest on that CPU. * * This routine is called at boot or modprobe time for each CPU to set up the * constant GDT entries: the ones which are the same no matter what Guest we're - * running. */ + * running. + */ void setup_default_gdt_entries(struct lguest_ro_state *state) { struct desc_struct *gdt = state->guest_gdt; @@ -98,30 +112,37 @@ void setup_default_gdt_entries(struct lguest_ro_state *state) gdt[GDT_ENTRY_LGUEST_CS] = FULL_EXEC_SEGMENT; gdt[GDT_ENTRY_LGUEST_DS] = FULL_SEGMENT; - /* The TSS segment refers to the TSS entry for this particular CPU. + /* + * The TSS segment refers to the TSS entry for this particular CPU. * Forgive the magic flags: the 0x8900 means the entry is Present, it's * privilege level 0 Available 386 TSS system segment, and the 0x67 - * means Saturn is eclipsed by Mercury in the twelfth house. */ + * means Saturn is eclipsed by Mercury in the twelfth house. + */ gdt[GDT_ENTRY_TSS].a = 0x00000067 | (tss << 16); gdt[GDT_ENTRY_TSS].b = 0x00008900 | (tss & 0xFF000000) | ((tss >> 16) & 0x000000FF); } -/* This routine sets up the initial Guest GDT for booting. All entries start - * as 0 (unusable). */ +/* + * This routine sets up the initial Guest GDT for booting. All entries start + * as 0 (unusable). + */ void setup_guest_gdt(struct lg_cpu *cpu) { - /* Start with full 0-4G segments... */ + /* + * Start with full 0-4G segments...except the Guest is allowed to use + * them, so set the privilege level appropriately in the flags. + */ cpu->arch.gdt[GDT_ENTRY_KERNEL_CS] = FULL_EXEC_SEGMENT; cpu->arch.gdt[GDT_ENTRY_KERNEL_DS] = FULL_SEGMENT; - /* ...except the Guest is allowed to use them, so set the privilege - * level appropriately in the flags. */ cpu->arch.gdt[GDT_ENTRY_KERNEL_CS].b |= (GUEST_PL << 13); cpu->arch.gdt[GDT_ENTRY_KERNEL_DS].b |= (GUEST_PL << 13); } -/*H:650 An optimization of copy_gdt(), for just the three "thead-local storage" - * entries. */ +/*H:650 + * An optimization of copy_gdt(), for just the three "thead-local storage" + * entries. + */ void copy_gdt_tls(const struct lg_cpu *cpu, struct desc_struct *gdt) { unsigned int i; @@ -130,26 +151,34 @@ void copy_gdt_tls(const struct lg_cpu *cpu, struct desc_struct *gdt) gdt[i] = cpu->arch.gdt[i]; } -/*H:640 When the Guest is run on a different CPU, or the GDT entries have - * changed, copy_gdt() is called to copy the Guest's GDT entries across to this - * CPU's GDT. */ +/*H:640 + * When the Guest is run on a different CPU, or the GDT entries have changed, + * copy_gdt() is called to copy the Guest's GDT entries across to this CPU's + * GDT. + */ void copy_gdt(const struct lg_cpu *cpu, struct desc_struct *gdt) { unsigned int i; - /* The default entries from setup_default_gdt_entries() are not - * replaced. See ignored_gdt() above. */ + /* + * The default entries from setup_default_gdt_entries() are not + * replaced. See ignored_gdt() above. + */ for (i = 0; i < GDT_ENTRIES; i++) if (!ignored_gdt(i)) gdt[i] = cpu->arch.gdt[i]; } -/*H:620 This is where the Guest asks us to load a new GDT entry - * (LHCALL_LOAD_GDT_ENTRY). We tweak the entry and copy it in. */ +/*H:620 + * This is where the Guest asks us to load a new GDT entry + * (LHCALL_LOAD_GDT_ENTRY). We tweak the entry and copy it in. + */ void load_guest_gdt_entry(struct lg_cpu *cpu, u32 num, u32 lo, u32 hi) { - /* We assume the Guest has the same number of GDT entries as the - * Host, otherwise we'd have to dynamically allocate the Guest GDT. */ + /* + * We assume the Guest has the same number of GDT entries as the + * Host, otherwise we'd have to dynamically allocate the Guest GDT. + */ if (num >= ARRAY_SIZE(cpu->arch.gdt)) kill_guest(cpu, "too many gdt entries %i", num); @@ -157,15 +186,19 @@ void load_guest_gdt_entry(struct lg_cpu *cpu, u32 num, u32 lo, u32 hi) cpu->arch.gdt[num].a = lo; cpu->arch.gdt[num].b = hi; fixup_gdt_table(cpu, num, num+1); - /* Mark that the GDT changed so the core knows it has to copy it again, - * even if the Guest is run on the same CPU. */ + /* + * Mark that the GDT changed so the core knows it has to copy it again, + * even if the Guest is run on the same CPU. + */ cpu->changed |= CHANGED_GDT; } -/* This is the fast-track version for just changing the three TLS entries. +/* + * This is the fast-track version for just changing the three TLS entries. * Remember that this happens on every context switch, so it's worth * optimizing. But wouldn't it be neater to have a single hypercall to cover - * both cases? */ + * both cases? + */ void guest_load_tls(struct lg_cpu *cpu, unsigned long gtls) { struct desc_struct *tls = &cpu->arch.gdt[GDT_ENTRY_TLS_MIN]; @@ -175,7 +208,6 @@ void guest_load_tls(struct lg_cpu *cpu, unsigned long gtls) /* Note that just the TLS entries have changed. */ cpu->changed |= CHANGED_GDT_TLS; } -/*:*/ /*H:660 * With this, we have finished the Host. diff --git a/drivers/lguest/x86/core.c b/drivers/lguest/x86/core.c index eaf722fe309..6ae388849a3 100644 --- a/drivers/lguest/x86/core.c +++ b/drivers/lguest/x86/core.c @@ -17,13 +17,15 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -/*P:450 This file contains the x86-specific lguest code. It used to be all +/*P:450 + * This file contains the x86-specific lguest code. It used to be all * mixed in with drivers/lguest/core.c but several foolhardy code slashers * wrestled most of the dependencies out to here in preparation for porting * lguest to other architectures (see what I mean by foolhardy?). * * This also contains a couple of non-obvious setup and teardown pieces which - * were implemented after days of debugging pain. :*/ + * were implemented after days of debugging pain. +:*/ #include <linux/kernel.h> #include <linux/start_kernel.h> #include <linux/string.h> @@ -82,25 +84,33 @@ static DEFINE_PER_CPU(struct lg_cpu *, last_cpu); */ static void copy_in_guest_info(struct lg_cpu *cpu, struct lguest_pages *pages) { - /* Copying all this data can be quite expensive. We usually run the + /* + * Copying all this data can be quite expensive. We usually run the * same Guest we ran last time (and that Guest hasn't run anywhere else * meanwhile). If that's not the case, we pretend everything in the - * Guest has changed. */ + * Guest has changed. + */ if (__get_cpu_var(last_cpu) != cpu || cpu->last_pages != pages) { __get_cpu_var(last_cpu) = cpu; cpu->last_pages = pages; cpu->changed = CHANGED_ALL; } - /* These copies are pretty cheap, so we do them unconditionally: */ - /* Save the current Host top-level page directory. */ + /* + * These copies are pretty cheap, so we do them unconditionally: */ + /* Save the current Host top-level page directory. + */ pages->state.host_cr3 = __pa(current->mm->pgd); - /* Set up the Guest's page tables to see this CPU's pages (and no - * other CPU's pages). */ + /* + * Set up the Guest's page tables to see this CPU's pages (and no + * other CPU's pages). + */ map_switcher_in_guest(cpu, pages); - /* Set up the two "TSS" members which tell the CPU what stack to use + /* + * Set up the two "TSS" members which tell the CPU what stack to use * for traps which do directly into the Guest (ie. traps at privilege - * level 1). */ + * level 1). + */ pages->state.guest_tss.sp1 = cpu->esp1; pages->state.guest_tss.ss1 = cpu->ss1; @@ -125,97 +135,126 @@ static void run_guest_once(struct lg_cpu *cpu, struct lguest_pages *pages) /* This is a dummy value we need for GCC's sake. */ unsigned int clobber; - /* Copy the guest-specific information into this CPU's "struct - * lguest_pages". */ + /* + * Copy the guest-specific information into this CPU's "struct + * lguest_pages". + */ copy_in_guest_info(cpu, pages); - /* Set the trap number to 256 (impossible value). If we fault while + /* + * Set the trap number to 256 (impossible value). If we fault while * switching to the Guest (bad segment registers or bug), this will - * cause us to abort the Guest. */ + * cause us to abort the Guest. + */ cpu->regs->trapnum = 256; - /* Now: we push the "eflags" register on the stack, then do an "lcall". + /* + * Now: we push the "eflags" register on the stack, then do an "lcall". * This is how we change from using the kernel code segment to using * the dedicated lguest code segment, as well as jumping into the * Switcher. * * The lcall also pushes the old code segment (KERNEL_CS) onto the * stack, then the address of this call. This stack layout happens to - * exactly match the stack layout created by an interrupt... */ + * exactly match the stack layout created by an interrupt... + */ asm volatile("pushf; lcall *lguest_entry" - /* This is how we tell GCC that %eax ("a") and %ebx ("b") - * are changed by this routine. The "=" means output. */ + /* + * This is how we tell GCC that %eax ("a") and %ebx ("b") + * are changed by this routine. The "=" means output. + */ : "=a"(clobber), "=b"(clobber) - /* %eax contains the pages pointer. ("0" refers to the + /* + * %eax contains the pages pointer. ("0" refers to the * 0-th argument above, ie "a"). %ebx contains the * physical address of the Guest's top-level page - * directory. */ + * directory. + */ : "0"(pages), "1"(__pa(cpu->lg->pgdirs[cpu->cpu_pgd].pgdir)) - /* We tell gcc that all these registers could change, + /* + * We tell gcc that all these registers could change, * which means we don't have to save and restore them in - * the Switcher. */ + * the Switcher. + */ : "memory", "%edx", "%ecx", "%edi", "%esi"); } /*:*/ -/*M:002 There are hooks in the scheduler which we can register to tell when we +/*M:002 + * There are hooks in the scheduler which we can register to tell when we * get kicked off the CPU (preempt_notifier_register()). This would allow us * to lazily disable SYSENTER which would regain some performance, and should * also simplify copy_in_guest_info(). Note that we'd still need to restore * things when we exit to Launcher userspace, but that's fairly easy. * - * We could also try using this hooks for PGE, but that might be too expensive. + * We could also try using these hooks for PGE, but that might be too expensive. * - * The hooks were designed for KVM, but we can also put them to good use. :*/ + * The hooks were designed for KVM, but we can also put them to good use. +:*/ -/*H:040 This is the i386-specific code to setup and run the Guest. Interrupts - * are disabled: we own the CPU. */ +/*H:040 + * This is the i386-specific code to setup and run the Guest. Interrupts + * are disabled: we own the CPU. + */ void lguest_arch_run_guest(struct lg_cpu *cpu) { - /* Remember the awfully-named TS bit? If the Guest has asked to set it + /* + * Remember the awfully-named TS bit? If the Guest has asked to set it * we set it now, so we can trap and pass that trap to the Guest if it - * uses the FPU. */ + * uses the FPU. + */ if (cpu->ts) unlazy_fpu(current); - /* SYSENTER is an optimized way of doing system calls. We can't allow + /* + * SYSENTER is an optimized way of doing system calls. We can't allow * it because it always jumps to privilege level 0. A normal Guest * won't try it because we don't advertise it in CPUID, but a malicious * Guest (or malicious Guest userspace program) could, so we tell the - * CPU to disable it before running the Guest. */ + * CPU to disable it before running the Guest. + */ if (boot_cpu_has(X86_FEATURE_SEP)) wrmsr(MSR_IA32_SYSENTER_CS, 0, 0); - /* Now we actually run the Guest. It will return when something + /* + * Now we actually run the Guest. It will return when something * interesting happens, and we can examine its registers to see what it - * was doing. */ + * was doing. + */ run_guest_once(cpu, lguest_pages(raw_smp_processor_id())); - /* Note that the "regs" structure contains two extra entries which are + /* + * Note that the "regs" structure contains two extra entries which are * not really registers: a trap number which says what interrupt or * trap made the switcher code come back, and an error code which some - * traps set. */ + * traps set. + */ /* Restore SYSENTER if it's supposed to be on. */ if (boot_cpu_has(X86_FEATURE_SEP)) wrmsr(MSR_IA32_SYSENTER_CS, __KERNEL_CS, 0); - /* If the Guest page faulted, then the cr2 register will tell us the + /* + * If the Guest page faulted, then the cr2 register will tell us the * bad virtual address. We have to grab this now, because once we * re-enable interrupts an interrupt could fault and thus overwrite - * cr2, or we could even move off to a different CPU. */ + * cr2, or we could even move off to a different CPU. + */ if (cpu->regs->trapnum == 14) cpu->arch.last_pagefault = read_cr2(); - /* Similarly, if we took a trap because the Guest used the FPU, + /* + * Similarly, if we took a trap because the Guest used the FPU, * we have to restore the FPU it expects to see. * math_state_restore() may sleep and we may even move off to * a different CPU. So all the critical stuff should be done - * before this. */ + * before this. + */ else if (cpu->regs->trapnum == 7) math_state_restore(); } -/*H:130 Now we've examined the hypercall code; our Guest can make requests. +/*H:130 + * Now we've examined the hypercall code; our Guest can make requests. * Our Guest is usually so well behaved; it never tries to do things it isn't * allowed to, and uses hypercalls instead. Unfortunately, Linux's paravirtual * infrastructure isn't quite complete, because it doesn't contain replacements @@ -225,26 +264,33 @@ void lguest_arch_run_guest(struct lg_cpu *cpu) * * When the Guest uses one of these instructions, we get a trap (General * Protection Fault) and come here. We see if it's one of those troublesome - * instructions and skip over it. We return true if we did. */ + * instructions and skip over it. We return true if we did. + */ static int emulate_insn(struct lg_cpu *cpu) { u8 insn; unsigned int insnlen = 0, in = 0, shift = 0; - /* The eip contains the *virtual* address of the Guest's instruction: - * guest_pa just subtracts the Guest's page_offset. */ + /* + * The eip contains the *virtual* address of the Guest's instruction: + * guest_pa just subtracts the Guest's page_offset. + */ unsigned long physaddr = guest_pa(cpu, cpu->regs->eip); - /* This must be the Guest kernel trying to do something, not userspace! + /* + * This must be the Guest kernel trying to do something, not userspace! * The bottom two bits of the CS segment register are the privilege - * level. */ + * level. + */ if ((cpu->regs->cs & 3) != GUEST_PL) return 0; /* Decoding x86 instructions is icky. */ insn = lgread(cpu, physaddr, u8); - /* 0x66 is an "operand prefix". It means it's using the upper 16 bits - of the eax register. */ + /* + * 0x66 is an "operand prefix". It means it's using the upper 16 bits + * of the eax register. + */ if (insn == 0x66) { shift = 16; /* The instruction is 1 byte so far, read the next byte. */ @@ -252,8 +298,10 @@ static int emulate_insn(struct lg_cpu *cpu) insn = lgread(cpu, physaddr + insnlen, u8); } - /* We can ignore the lower bit for the moment and decode the 4 opcodes - * we need to emulate. */ + /* + * We can ignore the lower bit for the moment and decode the 4 opcodes + * we need to emulate. + */ switch (insn & 0xFE) { case 0xE4: /* in <next byte>,%al */ insnlen += 2; @@ -274,9 +322,11 @@ static int emulate_insn(struct lg_cpu *cpu) return 0; } - /* If it was an "IN" instruction, they expect the result to be read + /* + * If it was an "IN" instruction, they expect the result to be read * into %eax, so we change %eax. We always return all-ones, which - * traditionally means "there's nothing there". */ + * traditionally means "there's nothing there". + */ if (in) { /* Lower bit tells is whether it's a 16 or 32 bit access */ if (insn & 0x1) @@ -290,7 +340,8 @@ static int emulate_insn(struct lg_cpu *cpu) return 1; } -/* Our hypercalls mechanism used to be based on direct software interrupts. +/* + * 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. * @@ -318,16 +369,20 @@ static int emulate_insn(struct lg_cpu *cpu) */ static void rewrite_hypercall(struct lg_cpu *cpu) { - /* This are the opcodes we use to patch the Guest. The opcode for "int + /* + * 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). */ + * complete the sequence with a NOP (0x90). + */ u8 insn[3] = {0xcd, 0x1f, 0x90}; __lgwrite(cpu, guest_pa(cpu, cpu->regs->eip), insn, sizeof(insn)); - /* The above write might have caused a copy of that page to be made + /* + * The above write might have caused a copy of that page to be made * (if it was read-only). We need to make sure the Guest has * up-to-date pagetables. As this doesn't happen often, we can just - * drop them all. */ + * drop them all. + */ guest_pagetable_clear_all(cpu); } @@ -335,9 +390,11 @@ static bool is_hypercall(struct lg_cpu *cpu) { u8 insn[3]; - /* This must be the Guest kernel trying to do something. + /* + * This must be the Guest kernel trying to do something. * The bottom two bits of the CS segment register are the privilege - * level. */ + * level. + */ if ((cpu->regs->cs & 3) != GUEST_PL) return false; @@ -351,86 +408,105 @@ void lguest_arch_handle_trap(struct lg_cpu *cpu) { switch (cpu->regs->trapnum) { case 13: /* We've intercepted a General Protection Fault. */ - /* Check if this was one of those annoying IN or OUT + /* + * Check if this was one of those annoying IN or OUT * instructions which we need to emulate. If so, we just go - * back into the Guest after we've done it. */ + * back into the Guest after we've done it. + */ if (cpu->regs->errcode == 0) { if (emulate_insn(cpu)) return; } - /* If KVM is active, the vmcall instruction triggers a - * General Protection Fault. Normally it triggers an - * invalid opcode fault (6): */ + /* + * If KVM is active, the vmcall instruction triggers a General + * Protection Fault. Normally it triggers an invalid opcode + * fault (6): + */ case 6: - /* We need to check if ring == GUEST_PL and - * faulting instruction == vmcall. */ + /* + * We need to check if ring == GUEST_PL and faulting + * instruction == vmcall. + */ if (is_hypercall(cpu)) { rewrite_hypercall(cpu); return; } break; case 14: /* We've intercepted a Page Fault. */ - /* The Guest accessed a virtual address that wasn't mapped. + /* + * The Guest accessed a virtual address that wasn't mapped. * This happens a lot: we don't actually set up most of the page * tables for the Guest at all when we start: as it runs it asks * for more and more, and we set them up as required. In this * case, we don't even tell the Guest that the fault happened. * * The errcode tells whether this was a read or a write, and - * whether kernel or userspace code. */ + * whether kernel or userspace code. + */ if (demand_page(cpu, cpu->arch.last_pagefault, cpu->regs->errcode)) return; - /* OK, it's really not there (or not OK): the Guest needs to + /* + * OK, it's really not there (or not OK): the Guest needs to * know. We write out the cr2 value so it knows where the * fault occurred. * * Note that if the Guest were really messed up, this could * happen before it's done the LHCALL_LGUEST_INIT hypercall, so - * lg->lguest_data could be NULL */ + * lg->lguest_data could be NULL + */ if (cpu->lg->lguest_data && put_user(cpu->arch.last_pagefault, &cpu->lg->lguest_data->cr2)) kill_guest(cpu, "Writing cr2"); break; case 7: /* We've intercepted a Device Not Available fault. */ - /* If the Guest doesn't want to know, we already restored the - * Floating Point Unit, so we just continue without telling - * it. */ + /* + * If the Guest doesn't want to know, we already restored the + * Floating Point Unit, so we just continue without telling it. + */ if (!cpu->ts) return; break; case 32 ... 255: - /* These values mean a real interrupt occurred, in which case + /* + * These values mean a real interrupt occurred, in which case * 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 */ + * return to run the Guest again + */ cond_resched(); return; case LGUEST_TRAP_ENTRY: - /* Our 'struct hcall_args' maps directly over our regs: we set - * up the pointer now to indicate a hypercall is pending. */ + /* + * Our 'struct hcall_args' maps directly over our regs: we set + * up the pointer now to indicate a hypercall is pending. + */ cpu->hcall = (struct hcall_args *)cpu->regs; return; } /* We didn't handle the trap, so it needs to go to the Guest. */ if (!deliver_trap(cpu, cpu->regs->trapnum)) - /* If the Guest doesn't have a handler (either it hasn't + /* + * If the Guest doesn't have a handler (either it hasn't * registered any yet, or it's one of the faults we don't let - * it handle), it dies with this cryptic error message. */ + * it handle), it dies with this cryptic error message. + */ kill_guest(cpu, "unhandled trap %li at %#lx (%#lx)", cpu->regs->trapnum, cpu->regs->eip, cpu->regs->trapnum == 14 ? cpu->arch.last_pagefault : cpu->regs->errcode); } -/* Now we can look at each of the routines this calls, in increasing order of +/* + * Now we can look at each of the routines this calls, in increasing order of * complexity: do_hypercalls(), emulate_insn(), maybe_do_interrupt(), * deliver_trap() and demand_page(). After all those, we'll be ready to * examine the Switcher, and our philosophical understanding of the Host/Guest - * duality will be complete. :*/ + * duality will be complete. +:*/ static void adjust_pge(void *on) { if (on) @@ -439,13 +515,16 @@ static void adjust_pge(void *on) write_cr4(read_cr4() & ~X86_CR4_PGE); } -/*H:020 Now the Switcher is mapped and every thing else is ready, we need to do - * some more i386-specific initialization. */ +/*H:020 + * Now the Switcher is mapped and every thing else is ready, we need to do + * some more i386-specific initialization. + */ void __init lguest_arch_host_init(void) { int i; - /* Most of the i386/switcher.S doesn't care that it's been moved; on + /* + * Most of the i386/switcher.S doesn't care that it's been moved; on * Intel, jumps are relative, and it doesn't access any references to * external code or data. * @@ -453,7 +532,8 @@ void __init lguest_arch_host_init(void) * addresses are placed in a table (default_idt_entries), so we need to * update the table with the new addresses. switcher_offset() is a * convenience function which returns the distance between the - * compiled-in switcher code and the high-mapped copy we just made. */ + * compiled-in switcher code and the high-mapped copy we just made. + */ for (i = 0; i < IDT_ENTRIES; i++) default_idt_entries[i] += switcher_offset(); @@ -468,63 +548,81 @@ void __init lguest_arch_host_init(void) for_each_possible_cpu(i) { /* lguest_pages() returns this CPU's two pages. */ struct lguest_pages *pages = lguest_pages(i); - /* This is a convenience pointer to make the code fit one - * statement to a line. */ + /* This is a convenience pointer to make the code neater. */ struct lguest_ro_state *state = &pages->state; - /* The Global Descriptor Table: the Host has a different one + /* + * The Global Descriptor Table: the Host has a different one * for each CPU. We keep a descriptor for the GDT which says * where it is and how big it is (the size is actually the last - * byte, not the size, hence the "-1"). */ + * byte, not the size, hence the "-1"). + */ state->host_gdt_desc.size = GDT_SIZE-1; state->host_gdt_desc.address = (long)get_cpu_gdt_table(i); - /* All CPUs on the Host use the same Interrupt Descriptor + /* + * All CPUs on the Host use the same Interrupt Descriptor * Table, so we just use store_idt(), which gets this CPU's IDT - * descriptor. */ + * descriptor. + */ store_idt(&state->host_idt_desc); - /* The descriptors for the Guest's GDT and IDT can be filled + /* + * The descriptors for the Guest's GDT and IDT can be filled * out now, too. We copy the GDT & IDT into ->guest_gdt and - * ->guest_idt before actually running the Guest. */ + * ->guest_idt before actually running the Guest. + */ state->guest_idt_desc.size = sizeof(state->guest_idt)-1; state->guest_idt_desc.address = (long)&state->guest_idt; state->guest_gdt_desc.size = sizeof(state->guest_gdt)-1; state->guest_gdt_desc.address = (long)&state->guest_gdt; - /* We know where we want the stack to be when the Guest enters + /* + * We know where we want the stack to be when the Guest enters * the Switcher: in pages->regs. The stack grows upwards, so - * we start it at the end of that structure. */ + * we start it at the end of that structure. + */ state->guest_tss.sp0 = (long)(&pages->regs + 1); - /* And this is the GDT entry to use for the stack: we keep a - * couple of special LGUEST entries. */ + /* + * And this is the GDT entry to use for the stack: we keep a + * couple of special LGUEST entries. + */ state->guest_tss.ss0 = LGUEST_DS; - /* x86 can have a finegrained bitmap which indicates what I/O + /* + * x86 can have a finegrained bitmap which indicates what I/O * ports the process can use. We set it to the end of our - * structure, meaning "none". */ + * structure, meaning "none". + */ state->guest_tss.io_bitmap_base = sizeof(state->guest_tss); - /* Some GDT entries are the same across all Guests, so we can - * set them up now. */ + /* + * Some GDT entries are the same across all Guests, so we can + * set them up now. + */ setup_default_gdt_entries(state); /* Most IDT entries are the same for all Guests, too.*/ setup_default_idt_entries(state, default_idt_entries); - /* The Host needs to be able to use the LGUEST segments on this - * CPU, too, so put them in the Host GDT. */ + /* + * The Host needs to be able to use the LGUEST segments on this + * CPU, too, so put them in the Host GDT. + */ get_cpu_gdt_table(i)[GDT_ENTRY_LGUEST_CS] = FULL_EXEC_SEGMENT; get_cpu_gdt_table(i)[GDT_ENTRY_LGUEST_DS] = FULL_SEGMENT; } - /* In the Switcher, we want the %cs segment register to use the + /* + * In the Switcher, we want the %cs segment register to use the * LGUEST_CS GDT entry: we've put that in the Host and Guest GDTs, so * it will be undisturbed when we switch. To change %cs and jump we - * need this structure to feed to Intel's "lcall" instruction. */ + * need this structure to feed to Intel's "lcall" instruction. + */ lguest_entry.offset = (long)switch_to_guest + switcher_offset(); lguest_entry.segment = LGUEST_CS; - /* Finally, we need to turn off "Page Global Enable". PGE is an + /* + * Finally, we need to turn off "Page Global Enable". PGE is an * optimization where page table entries are specially marked to show * they never change. The Host kernel marks all the kernel pages this * way because it's always present, even when userspace is running. @@ -534,16 +632,21 @@ void __init lguest_arch_host_init(void) * you'll get really weird bugs that you'll chase for two days. * * I used to turn PGE off every time we switched to the Guest and back - * on when we return, but that slowed the Switcher down noticibly. */ + * on when we return, but that slowed the Switcher down noticibly. + */ - /* We don't need the complexity of CPUs coming and going while we're - * doing this. */ + /* + * We don't need the complexity of CPUs coming and going while we're + * doing this. + */ get_online_cpus(); if (cpu_has_pge) { /* We have a broader idea of "global". */ /* Remember that this was originally set (for cleanup). */ cpu_had_pge = 1; - /* adjust_pge is a helper function which sets or unsets the PGE - * bit on its CPU, depending on the argument (0 == unset). */ + /* + * adjust_pge is a helper function which sets or unsets the PGE + * bit on its CPU, depending on the argument (0 == unset). + */ on_each_cpu(adjust_pge, (void *)0, 1); /* Turn off the feature in the global feature set. */ clear_cpu_cap(&boot_cpu_data, X86_FEATURE_PGE); @@ -590,26 +693,32 @@ int lguest_arch_init_hypercalls(struct lg_cpu *cpu) { u32 tsc_speed; - /* The pointer to the Guest's "struct lguest_data" is the only argument. - * We check that address now. */ + /* + * The pointer to the Guest's "struct lguest_data" is the only argument. + * We check that address now. + */ if (!lguest_address_ok(cpu->lg, cpu->hcall->arg1, sizeof(*cpu->lg->lguest_data))) return -EFAULT; - /* Having checked it, we simply set lg->lguest_data to point straight + /* + * Having checked it, we simply set lg->lguest_data to point straight * into the Launcher's memory at the right place and then use * copy_to_user/from_user from now on, instead of lgread/write. I put * this in to show that I'm not immune to writing stupid - * optimizations. */ + * optimizations. + */ cpu->lg->lguest_data = cpu->lg->mem_base + cpu->hcall->arg1; - /* We insist that the Time Stamp Counter exist and doesn't change with + /* + * We insist that the Time Stamp Counter exist and doesn't change with * cpu frequency. Some devious chip manufacturers decided that TSC * changes could be handled in software. I decided that time going * backwards might be good for benchmarks, but it's bad for users. * * We also insist that the TSC be stable: the kernel detects unreliable - * TSCs for its own purposes, and we use that here. */ + * TSCs for its own purposes, and we use that here. + */ if (boot_cpu_has(X86_FEATURE_CONSTANT_TSC) && !check_tsc_unstable()) tsc_speed = tsc_khz; else @@ -625,38 +734,47 @@ int lguest_arch_init_hypercalls(struct lg_cpu *cpu) } /*:*/ -/*L:030 lguest_arch_setup_regs() +/*L:030 + * lguest_arch_setup_regs() * * Most of the Guest's registers are left alone: we used get_zeroed_page() to - * allocate the structure, so they will be 0. */ + * allocate the structure, so they will be 0. + */ void lguest_arch_setup_regs(struct lg_cpu *cpu, unsigned long start) { struct lguest_regs *regs = cpu->regs; - /* There are four "segment" registers which the Guest needs to boot: + /* + * There are four "segment" registers which the Guest needs to boot: * The "code segment" register (cs) refers to the kernel code segment * __KERNEL_CS, and the "data", "extra" and "stack" segment registers * refer to the kernel data segment __KERNEL_DS. * * The privilege level is packed into the lower bits. The Guest runs - * at privilege level 1 (GUEST_PL).*/ + * at privilege level 1 (GUEST_PL). + */ regs->ds = regs->es = regs->ss = __KERNEL_DS|GUEST_PL; regs->cs = __KERNEL_CS|GUEST_PL; - /* The "eflags" register contains miscellaneous flags. Bit 1 (0x002) + /* + * The "eflags" register contains miscellaneous flags. Bit 1 (0x002) * is supposed to always be "1". Bit 9 (0x200) controls whether * interrupts are enabled. We always leave interrupts enabled while - * running the Guest. */ + * running the Guest. + */ regs->eflags = X86_EFLAGS_IF | 0x2; - /* The "Extended Instruction Pointer" register says where the Guest is - * running. */ + /* + * The "Extended Instruction Pointer" register says where the Guest is + * running. + */ regs->eip = start; - /* %esi points to our boot information, at physical address 0, so don't - * touch it. */ + /* + * %esi points to our boot information, at physical address 0, so don't + * touch it. + */ - /* There are a couple of GDT entries the Guest expects when first - * booting. */ + /* There are a couple of GDT entries the Guest expects at boot. */ setup_guest_gdt(cpu); } diff --git a/drivers/lguest/x86/switcher_32.S b/drivers/lguest/x86/switcher_32.S index 3fc15318a80..40634b0db9f 100644 --- a/drivers/lguest/x86/switcher_32.S +++ b/drivers/lguest/x86/switcher_32.S @@ -1,12 +1,15 @@ -/*P:900 This is the Switcher: code which sits at 0xFFC00000 astride both the - * Host and Guest to do the low-level Guest<->Host switch. It is as simple as - * it can be made, but it's naturally very specific to x86. +/*P:900 + * This is the Switcher: code which sits at 0xFFC00000 (or 0xFFE00000) astride + * both the Host and Guest to do the low-level Guest<->Host switch. It is as + * simple as it can be made, but it's naturally very specific to x86. * * You have now completed Preparation. If this has whet your appetite; if you * are feeling invigorated and refreshed then the next, more challenging stage - * can be found in "make Guest". :*/ + * can be found in "make Guest". + :*/ -/*M:012 Lguest is meant to be simple: my rule of thumb is that 1% more LOC must +/*M:012 + * Lguest is meant to be simple: my rule of thumb is that 1% more LOC must * gain at least 1% more performance. Since neither LOC nor performance can be * measured beforehand, it generally means implementing a feature then deciding * if it's worth it. And once it's implemented, who can say no? @@ -31,11 +34,14 @@ * Host (which is actually really easy). * * Two questions remain. Would the performance gain outweigh the complexity? - * And who would write the verse documenting it? :*/ + * And who would write the verse documenting it? +:*/ -/*M:011 Lguest64 handles NMI. This gave me NMI envy (until I looked at their +/*M:011 + * Lguest64 handles NMI. This gave me NMI envy (until I looked at their * code). It's worth doing though, since it would let us use oprofile in the - * Host when a Guest is running. :*/ + * Host when a Guest is running. +:*/ /*S:100 * Welcome to the Switcher itself! diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index 529e2ba505c..ed103816401 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -1318,7 +1318,7 @@ static int crypt_iterate_devices(struct dm_target *ti, { struct crypt_config *cc = ti->private; - return fn(ti, cc->dev, cc->start, data); + return fn(ti, cc->dev, cc->start, ti->len, data); } static struct target_type crypt_target = { diff --git a/drivers/md/dm-delay.c b/drivers/md/dm-delay.c index 4e5b843cd4d..ebe7381f47c 100644 --- a/drivers/md/dm-delay.c +++ b/drivers/md/dm-delay.c @@ -324,12 +324,12 @@ static int delay_iterate_devices(struct dm_target *ti, struct delay_c *dc = ti->private; int ret = 0; - ret = fn(ti, dc->dev_read, dc->start_read, data); + ret = fn(ti, dc->dev_read, dc->start_read, ti->len, data); if (ret) goto out; if (dc->dev_write) - ret = fn(ti, dc->dev_write, dc->start_write, data); + ret = fn(ti, dc->dev_write, dc->start_write, ti->len, data); out: return ret; diff --git a/drivers/md/dm-linear.c b/drivers/md/dm-linear.c index 9184b6deb86..82f7d6e6b1e 100644 --- a/drivers/md/dm-linear.c +++ b/drivers/md/dm-linear.c @@ -139,7 +139,7 @@ static int linear_iterate_devices(struct dm_target *ti, { struct linear_c *lc = ti->private; - return fn(ti, lc->dev, lc->start, data); + return fn(ti, lc->dev, lc->start, ti->len, data); } static struct target_type linear_target = { diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index c70604a2089..6f0d90d4a54 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c @@ -1453,7 +1453,7 @@ static int multipath_iterate_devices(struct dm_target *ti, list_for_each_entry(pg, &m->priority_groups, list) { list_for_each_entry(p, &pg->pgpaths, list) { - ret = fn(ti, p->path.dev, ti->begin, data); + ret = fn(ti, p->path.dev, ti->begin, ti->len, data); if (ret) goto out; } diff --git a/drivers/md/dm-raid1.c b/drivers/md/dm-raid1.c index ce8868c768c..9726577cde4 100644 --- a/drivers/md/dm-raid1.c +++ b/drivers/md/dm-raid1.c @@ -638,6 +638,7 @@ static void do_writes(struct mirror_set *ms, struct bio_list *writes) spin_lock_irq(&ms->lock); bio_list_merge(&ms->writes, &requeue); spin_unlock_irq(&ms->lock); + delayed_wake(ms); } /* @@ -1292,7 +1293,7 @@ static int mirror_iterate_devices(struct dm_target *ti, for (i = 0; !ret && i < ms->nr_mirrors; i++) ret = fn(ti, ms->mirror[i].dev, - ms->mirror[i].offset, data); + ms->mirror[i].offset, ti->len, data); return ret; } diff --git a/drivers/md/dm-stripe.c b/drivers/md/dm-stripe.c index b240e85ae39..4e0e5937e42 100644 --- a/drivers/md/dm-stripe.c +++ b/drivers/md/dm-stripe.c @@ -320,10 +320,11 @@ static int stripe_iterate_devices(struct dm_target *ti, int ret = 0; unsigned i = 0; - do + do { ret = fn(ti, sc->stripe[i].dev, - sc->stripe[i].physical_start, data); - while (!ret && ++i < sc->stripes); + sc->stripe[i].physical_start, + sc->stripe_width, data); + } while (!ret && ++i < sc->stripes); return ret; } diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index 2cba557d9e6..d952b344191 100644 --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c @@ -346,7 +346,7 @@ static void close_dev(struct dm_dev_internal *d, struct mapped_device *md) * If possible, this checks an area of a destination device is valid. */ static int device_area_is_valid(struct dm_target *ti, struct dm_dev *dev, - sector_t start, void *data) + sector_t start, sector_t len, void *data) { struct queue_limits *limits = data; struct block_device *bdev = dev->bdev; @@ -359,7 +359,7 @@ static int device_area_is_valid(struct dm_target *ti, struct dm_dev *dev, if (!dev_size) return 1; - if ((start >= dev_size) || (start + ti->len > dev_size)) { + if ((start >= dev_size) || (start + len > dev_size)) { DMWARN("%s: %s too small for target", dm_device_name(ti->table->md), bdevname(bdev, b)); return 0; @@ -377,11 +377,11 @@ static int device_area_is_valid(struct dm_target *ti, struct dm_dev *dev, return 0; } - if (ti->len & (logical_block_size_sectors - 1)) { + if (len & (logical_block_size_sectors - 1)) { DMWARN("%s: len=%llu not aligned to h/w " "logical block size %hu of %s", dm_device_name(ti->table->md), - (unsigned long long)ti->len, + (unsigned long long)len, limits->logical_block_size, bdevname(bdev, b)); return 0; } @@ -482,7 +482,7 @@ static int __table_get_device(struct dm_table *t, struct dm_target *ti, #define min_not_zero(l, r) (l == 0) ? r : ((r == 0) ? l : min(l, r)) int dm_set_device_limits(struct dm_target *ti, struct dm_dev *dev, - sector_t start, void *data) + sector_t start, sector_t len, void *data) { struct queue_limits *limits = data; struct block_device *bdev = dev->bdev; @@ -830,11 +830,6 @@ unsigned dm_table_get_type(struct dm_table *t) return t->type; } -bool dm_table_bio_based(struct dm_table *t) -{ - return dm_table_get_type(t) == DM_TYPE_BIO_BASED; -} - bool dm_table_request_based(struct dm_table *t) { return dm_table_get_type(t) == DM_TYPE_REQUEST_BASED; diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 9acd54a5cff..8a311ea0d44 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -2203,16 +2203,6 @@ int dm_swap_table(struct mapped_device *md, struct dm_table *table) goto out; } - /* - * It is enought that blk_queue_ordered() is called only once when - * the first bio-based table is bound. - * - * This setting should be moved to alloc_dev() when request-based dm - * supports barrier. - */ - if (!md->map && dm_table_bio_based(table)) - blk_queue_ordered(md->queue, QUEUE_ORDERED_DRAIN, NULL); - __unbind(md); r = __bind(md, table, &limits); diff --git a/drivers/md/dm.h b/drivers/md/dm.h index 23278ae80f0..a7663eba17e 100644 --- a/drivers/md/dm.h +++ b/drivers/md/dm.h @@ -61,7 +61,6 @@ int dm_table_any_congested(struct dm_table *t, int bdi_bits); int dm_table_any_busy_target(struct dm_table *t); int dm_table_set_type(struct dm_table *t); unsigned dm_table_get_type(struct dm_table *t); -bool dm_table_bio_based(struct dm_table *t); bool dm_table_request_based(struct dm_table *t); int dm_table_alloc_md_mempools(struct dm_table *t); void dm_table_free_md_mempools(struct dm_table *t); diff --git a/drivers/md/linear.c b/drivers/md/linear.c index 5810fa906af..5fe39c2a3d2 100644 --- a/drivers/md/linear.c +++ b/drivers/md/linear.c @@ -220,6 +220,7 @@ static int linear_run (mddev_t *mddev) mddev->queue->unplug_fn = linear_unplug; mddev->queue->backing_dev_info.congested_fn = linear_congested; mddev->queue->backing_dev_info.congested_data = mddev; + md_integrity_register(mddev); return 0; } @@ -256,6 +257,7 @@ static int linear_add(mddev_t *mddev, mdk_rdev_t *rdev) rcu_assign_pointer(mddev->private, newconf); md_set_array_sectors(mddev, linear_size(mddev, 0, 0)); set_capacity(mddev->gendisk, mddev->array_sectors); + revalidate_disk(mddev->gendisk); call_rcu(&oldconf->rcu, free_conf); return 0; } diff --git a/drivers/md/md.c b/drivers/md/md.c index d4351ff0849..5b98bea4ff9 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -1308,7 +1308,12 @@ static int super_1_validate(mddev_t *mddev, mdk_rdev_t *rdev) } if (mddev->level != LEVEL_MULTIPATH) { int role; - role = le16_to_cpu(sb->dev_roles[rdev->desc_nr]); + if (rdev->desc_nr < 0 || + rdev->desc_nr >= le32_to_cpu(sb->max_dev)) { + role = 0xffff; + rdev->desc_nr = -1; + } else + role = le16_to_cpu(sb->dev_roles[rdev->desc_nr]); switch(role) { case 0xffff: /* spare */ break; @@ -1394,8 +1399,14 @@ static void super_1_sync(mddev_t *mddev, mdk_rdev_t *rdev) if (rdev2->desc_nr+1 > max_dev) max_dev = rdev2->desc_nr+1; - if (max_dev > le32_to_cpu(sb->max_dev)) + if (max_dev > le32_to_cpu(sb->max_dev)) { + int bmask; sb->max_dev = cpu_to_le32(max_dev); + rdev->sb_size = max_dev * 2 + 256; + bmask = queue_logical_block_size(rdev->bdev->bd_disk->queue)-1; + if (rdev->sb_size & bmask) + rdev->sb_size = (rdev->sb_size | bmask) + 1; + } for (i=0; i<max_dev;i++) sb->dev_roles[i] = cpu_to_le16(0xfffe); @@ -1487,37 +1498,76 @@ static int match_mddev_units(mddev_t *mddev1, mddev_t *mddev2) static LIST_HEAD(pending_raid_disks); -static void md_integrity_check(mdk_rdev_t *rdev, mddev_t *mddev) +/* + * Try to register data integrity profile for an mddev + * + * This is called when an array is started and after a disk has been kicked + * from the array. It only succeeds if all working and active component devices + * are integrity capable with matching profiles. + */ +int md_integrity_register(mddev_t *mddev) +{ + mdk_rdev_t *rdev, *reference = NULL; + + if (list_empty(&mddev->disks)) + return 0; /* nothing to do */ + if (blk_get_integrity(mddev->gendisk)) + return 0; /* already registered */ + list_for_each_entry(rdev, &mddev->disks, same_set) { + /* skip spares and non-functional disks */ + if (test_bit(Faulty, &rdev->flags)) + continue; + if (rdev->raid_disk < 0) + continue; + /* + * If at least one rdev is not integrity capable, we can not + * enable data integrity for the md device. + */ + if (!bdev_get_integrity(rdev->bdev)) + return -EINVAL; + if (!reference) { + /* Use the first rdev as the reference */ + reference = rdev; + continue; + } + /* does this rdev's profile match the reference profile? */ + if (blk_integrity_compare(reference->bdev->bd_disk, + rdev->bdev->bd_disk) < 0) + return -EINVAL; + } + /* + * All component devices are integrity capable and have matching + * profiles, register the common profile for the md device. + */ + if (blk_integrity_register(mddev->gendisk, + bdev_get_integrity(reference->bdev)) != 0) { + printk(KERN_ERR "md: failed to register integrity for %s\n", + mdname(mddev)); + return -EINVAL; + } + printk(KERN_NOTICE "md: data integrity on %s enabled\n", + mdname(mddev)); + return 0; +} +EXPORT_SYMBOL(md_integrity_register); + +/* Disable data integrity if non-capable/non-matching disk is being added */ +void md_integrity_add_rdev(mdk_rdev_t *rdev, mddev_t *mddev) { - struct mdk_personality *pers = mddev->pers; - struct gendisk *disk = mddev->gendisk; struct blk_integrity *bi_rdev = bdev_get_integrity(rdev->bdev); - struct blk_integrity *bi_mddev = blk_get_integrity(disk); + struct blk_integrity *bi_mddev = blk_get_integrity(mddev->gendisk); - /* Data integrity passthrough not supported on RAID 4, 5 and 6 */ - if (pers && pers->level >= 4 && pers->level <= 6) + if (!bi_mddev) /* nothing to do */ return; - - /* If rdev is integrity capable, register profile for mddev */ - if (!bi_mddev && bi_rdev) { - if (blk_integrity_register(disk, bi_rdev)) - printk(KERN_ERR "%s: %s Could not register integrity!\n", - __func__, disk->disk_name); - else - printk(KERN_NOTICE "Enabling data integrity on %s\n", - disk->disk_name); + if (rdev->raid_disk < 0) /* skip spares */ return; - } - - /* Check that mddev and rdev have matching profiles */ - if (blk_integrity_compare(disk, rdev->bdev->bd_disk) < 0) { - printk(KERN_ERR "%s: %s/%s integrity mismatch!\n", __func__, - disk->disk_name, rdev->bdev->bd_disk->disk_name); - printk(KERN_NOTICE "Disabling data integrity on %s\n", - disk->disk_name); - blk_integrity_unregister(disk); - } + if (bi_rdev && blk_integrity_compare(mddev->gendisk, + rdev->bdev->bd_disk) >= 0) + return; + printk(KERN_NOTICE "disabling data integrity on %s\n", mdname(mddev)); + blk_integrity_unregister(mddev->gendisk); } +EXPORT_SYMBOL(md_integrity_add_rdev); static int bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev) { @@ -1591,7 +1641,6 @@ static int bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev) /* May as well allow recovery to be retried once */ mddev->recovery_disabled = 0; - md_integrity_check(rdev, mddev); return 0; fail: @@ -2657,6 +2706,7 @@ level_store(mddev_t *mddev, const char *buf, size_t len) ssize_t rv = len; struct mdk_personality *pers; void *priv; + mdk_rdev_t *rdev; if (mddev->pers == NULL) { if (len == 0) @@ -2736,6 +2786,12 @@ level_store(mddev_t *mddev, const char *buf, size_t len) mddev_suspend(mddev); mddev->pers->stop(mddev); module_put(mddev->pers->owner); + /* Invalidate devices that are now superfluous */ + list_for_each_entry(rdev, &mddev->disks, same_set) + if (rdev->raid_disk >= mddev->raid_disks) { + rdev->raid_disk = -1; + clear_bit(In_sync, &rdev->flags); + } mddev->pers = pers; mddev->private = priv; strlcpy(mddev->clevel, pers->name, sizeof(mddev->clevel)); @@ -3685,17 +3741,8 @@ array_size_store(mddev_t *mddev, const char *buf, size_t len) mddev->array_sectors = sectors; set_capacity(mddev->gendisk, mddev->array_sectors); - if (mddev->pers) { - struct block_device *bdev = bdget_disk(mddev->gendisk, 0); - - if (bdev) { - mutex_lock(&bdev->bd_inode->i_mutex); - i_size_write(bdev->bd_inode, - (loff_t)mddev->array_sectors << 9); - mutex_unlock(&bdev->bd_inode->i_mutex); - bdput(bdev); - } - } + if (mddev->pers) + revalidate_disk(mddev->gendisk); return len; } @@ -4048,10 +4095,6 @@ static int do_md_run(mddev_t * mddev) } strlcpy(mddev->clevel, pers->name, sizeof(mddev->clevel)); - if (pers->level >= 4 && pers->level <= 6) - /* Cannot support integrity (yet) */ - blk_integrity_unregister(mddev->gendisk); - if (mddev->reshape_position != MaxSector && pers->start_reshape == NULL) { /* This personality cannot handle reshaping... */ @@ -4189,6 +4232,7 @@ static int do_md_run(mddev_t * mddev) md_wakeup_thread(mddev->thread); md_wakeup_thread(mddev->sync_thread); /* possibly kick off a reshape */ + revalidate_disk(mddev->gendisk); mddev->changed = 1; md_new_event(mddev); sysfs_notify_dirent(mddev->sysfs_state); @@ -5087,18 +5131,8 @@ static int update_size(mddev_t *mddev, sector_t num_sectors) return -ENOSPC; } rv = mddev->pers->resize(mddev, num_sectors); - if (!rv) { - struct block_device *bdev; - - bdev = bdget_disk(mddev->gendisk, 0); - if (bdev) { - mutex_lock(&bdev->bd_inode->i_mutex); - i_size_write(bdev->bd_inode, - (loff_t)mddev->array_sectors << 9); - mutex_unlock(&bdev->bd_inode->i_mutex); - bdput(bdev); - } - } + if (!rv) + revalidate_disk(mddev->gendisk); return rv; } diff --git a/drivers/md/md.h b/drivers/md/md.h index 9430a110db9..78f03168baf 100644 --- a/drivers/md/md.h +++ b/drivers/md/md.h @@ -431,5 +431,7 @@ extern int md_allow_write(mddev_t *mddev); extern void md_wait_for_blocked_rdev(mdk_rdev_t *rdev, mddev_t *mddev); extern void md_set_array_sectors(mddev_t *mddev, sector_t array_sectors); extern int md_check_no_bitmap(mddev_t *mddev); +extern int md_integrity_register(mddev_t *mddev); +void md_integrity_add_rdev(mdk_rdev_t *rdev, mddev_t *mddev); #endif /* _MD_MD_H */ diff --git a/drivers/md/multipath.c b/drivers/md/multipath.c index 237fe3fd235..7140909f666 100644 --- a/drivers/md/multipath.c +++ b/drivers/md/multipath.c @@ -313,6 +313,7 @@ static int multipath_add_disk(mddev_t *mddev, mdk_rdev_t *rdev) set_bit(In_sync, &rdev->flags); rcu_assign_pointer(p->rdev, rdev); err = 0; + md_integrity_add_rdev(rdev, mddev); break; } @@ -345,7 +346,9 @@ static int multipath_remove_disk(mddev_t *mddev, int number) /* lost the race, try later */ err = -EBUSY; p->rdev = rdev; + goto abort; } + md_integrity_register(mddev); } abort: @@ -519,7 +522,7 @@ static int multipath_run (mddev_t *mddev) mddev->queue->unplug_fn = multipath_unplug; mddev->queue->backing_dev_info.congested_fn = multipath_congested; mddev->queue->backing_dev_info.congested_data = mddev; - + md_integrity_register(mddev); return 0; out_free_conf: diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c index 335f490dcad..898e2bdfee4 100644 --- a/drivers/md/raid0.c +++ b/drivers/md/raid0.c @@ -351,6 +351,7 @@ static int raid0_run(mddev_t *mddev) blk_queue_merge_bvec(mddev->queue, raid0_mergeable_bvec); dump_zones(mddev); + md_integrity_register(mddev); return 0; } diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 0569efba0c0..8726fd7ebce 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -1144,7 +1144,7 @@ static int raid1_add_disk(mddev_t *mddev, mdk_rdev_t *rdev) rcu_assign_pointer(p->rdev, rdev); break; } - + md_integrity_add_rdev(rdev, mddev); print_conf(conf); return err; } @@ -1178,7 +1178,9 @@ static int raid1_remove_disk(mddev_t *mddev, int number) /* lost the race, try later */ err = -EBUSY; p->rdev = rdev; + goto abort; } + md_integrity_register(mddev); } abort: @@ -2067,7 +2069,7 @@ static int run(mddev_t *mddev) mddev->queue->unplug_fn = raid1_unplug; mddev->queue->backing_dev_info.congested_fn = raid1_congested; mddev->queue->backing_dev_info.congested_data = mddev; - + md_integrity_register(mddev); return 0; out_no_mem: @@ -2132,6 +2134,7 @@ static int raid1_resize(mddev_t *mddev, sector_t sectors) return -EINVAL; set_capacity(mddev->gendisk, mddev->array_sectors); mddev->changed = 1; + revalidate_disk(mddev->gendisk); if (sectors > mddev->dev_sectors && mddev->recovery_cp == MaxSector) { mddev->recovery_cp = mddev->dev_sectors; diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 7298a5e5a18..3d9020cf6f6 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -1170,6 +1170,7 @@ static int raid10_add_disk(mddev_t *mddev, mdk_rdev_t *rdev) break; } + md_integrity_add_rdev(rdev, mddev); print_conf(conf); return err; } @@ -1203,7 +1204,9 @@ static int raid10_remove_disk(mddev_t *mddev, int number) /* lost the race, try later */ err = -EBUSY; p->rdev = rdev; + goto abort; } + md_integrity_register(mddev); } abort: @@ -2225,6 +2228,7 @@ static int run(mddev_t *mddev) if (conf->near_copies < mddev->raid_disks) blk_queue_merge_bvec(mddev->queue, raid10_mergeable_bvec); + md_integrity_register(mddev); return 0; out_free_conf: diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 37835538b58..2b521ee67df 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -3999,6 +3999,9 @@ static inline sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *ski return 0; } + /* Allow raid5_quiesce to complete */ + wait_event(conf->wait_for_overlap, conf->quiesce != 2); + if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery)) return reshape_request(mddev, sector_nr, skipped); @@ -4316,6 +4319,15 @@ raid5_size(mddev_t *mddev, sector_t sectors, int raid_disks) return sectors * (raid_disks - conf->max_degraded); } +static void free_conf(raid5_conf_t *conf) +{ + shrink_stripes(conf); + safe_put_page(conf->spare_page); + kfree(conf->disks); + kfree(conf->stripe_hashtbl); + kfree(conf); +} + static raid5_conf_t *setup_conf(mddev_t *mddev) { raid5_conf_t *conf; @@ -4447,11 +4459,7 @@ static raid5_conf_t *setup_conf(mddev_t *mddev) abort: if (conf) { - shrink_stripes(conf); - safe_put_page(conf->spare_page); - kfree(conf->disks); - kfree(conf->stripe_hashtbl); - kfree(conf); + free_conf(conf); return ERR_PTR(-EIO); } else return ERR_PTR(-ENOMEM); @@ -4629,12 +4637,8 @@ abort: md_unregister_thread(mddev->thread); mddev->thread = NULL; if (conf) { - shrink_stripes(conf); print_raid5_conf(conf); - safe_put_page(conf->spare_page); - kfree(conf->disks); - kfree(conf->stripe_hashtbl); - kfree(conf); + free_conf(conf); } mddev->private = NULL; printk(KERN_ALERT "raid5: failed to run raid set %s\n", mdname(mddev)); @@ -4649,13 +4653,10 @@ static int stop(mddev_t *mddev) md_unregister_thread(mddev->thread); mddev->thread = NULL; - shrink_stripes(conf); - kfree(conf->stripe_hashtbl); mddev->queue->backing_dev_info.congested_fn = NULL; blk_sync_queue(mddev->queue); /* the unplug fn references 'conf'*/ sysfs_remove_group(&mddev->kobj, &raid5_attrs_group); - kfree(conf->disks); - kfree(conf); + free_conf(conf); mddev->private = NULL; return 0; } @@ -4857,6 +4858,7 @@ static int raid5_resize(mddev_t *mddev, sector_t sectors) return -EINVAL; set_capacity(mddev->gendisk, mddev->array_sectors); mddev->changed = 1; + revalidate_disk(mddev->gendisk); if (sectors > mddev->dev_sectors && mddev->recovery_cp == MaxSector) { mddev->recovery_cp = mddev->dev_sectors; set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); @@ -5002,7 +5004,7 @@ static int raid5_start_reshape(mddev_t *mddev) spin_unlock_irqrestore(&conf->device_lock, flags); } mddev->raid_disks = conf->raid_disks; - mddev->reshape_position = 0; + mddev->reshape_position = conf->reshape_progress; set_bit(MD_CHANGE_DEVS, &mddev->flags); clear_bit(MD_RECOVERY_SYNC, &mddev->recovery); @@ -5057,7 +5059,6 @@ static void end_reshape(raid5_conf_t *conf) */ static void raid5_finish_reshape(mddev_t *mddev) { - struct block_device *bdev; raid5_conf_t *conf = mddev->private; if (!test_bit(MD_RECOVERY_INTR, &mddev->recovery)) { @@ -5066,15 +5067,7 @@ static void raid5_finish_reshape(mddev_t *mddev) md_set_array_sectors(mddev, raid5_size(mddev, 0, 0)); set_capacity(mddev->gendisk, mddev->array_sectors); mddev->changed = 1; - - bdev = bdget_disk(mddev->gendisk, 0); - if (bdev) { - mutex_lock(&bdev->bd_inode->i_mutex); - i_size_write(bdev->bd_inode, - (loff_t)mddev->array_sectors << 9); - mutex_unlock(&bdev->bd_inode->i_mutex); - bdput(bdev); - } + revalidate_disk(mddev->gendisk); } else { int d; mddev->degraded = conf->raid_disks; @@ -5106,12 +5099,18 @@ static void raid5_quiesce(mddev_t *mddev, int state) case 1: /* stop all writes */ spin_lock_irq(&conf->device_lock); - conf->quiesce = 1; + /* '2' tells resync/reshape to pause so that all + * active stripes can drain + */ + conf->quiesce = 2; wait_event_lock_irq(conf->wait_for_stripe, atomic_read(&conf->active_stripes) == 0 && atomic_read(&conf->active_aligned_reads) == 0, conf->device_lock, /* nothing */); + conf->quiesce = 1; spin_unlock_irq(&conf->device_lock); + /* allow reshape to continue */ + wake_up(&conf->wait_for_overlap); break; case 0: /* re-enable writes */ diff --git a/drivers/media/dvb/b2c2/flexcop-fe-tuner.c b/drivers/media/dvb/b2c2/flexcop-fe-tuner.c index efb4a6c2b57..9a6307a347b 100644 --- a/drivers/media/dvb/b2c2/flexcop-fe-tuner.c +++ b/drivers/media/dvb/b2c2/flexcop-fe-tuner.c @@ -20,8 +20,14 @@ #include "tuner-simple.h" #include "stv0297.h" + +/* Can we use the specified front-end? Remember that if we are compiled + * into the kernel we can't call code that's in modules. */ +#define FE_SUPPORTED(fe) (defined(CONFIG_DVB_##fe) || \ + (defined(CONFIG_DVB_##fe##_MODULE) && defined(MODULE))) + /* lnb control */ -#if defined(CONFIG_DVB_MT312_MODULE) || defined(CONFIG_DVB_STV0299_MODULE) +#if FE_SUPPORTED(MT312) || FE_SUPPORTED(STV0299) static int flexcop_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) { struct flexcop_device *fc = fe->dvb->priv; @@ -49,8 +55,7 @@ static int flexcop_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage } #endif -#if defined(CONFIG_DVB_S5H1420_MODULE) || defined(CONFIG_DVB_STV0299_MODULE) \ - || defined(CONFIG_DVB_MT312_MODULE) +#if FE_SUPPORTED(S5H1420) || FE_SUPPORTED(STV0299) || FE_SUPPORTED(MT312) static int flexcop_sleep(struct dvb_frontend* fe) { struct flexcop_device *fc = fe->dvb->priv; @@ -61,7 +66,7 @@ static int flexcop_sleep(struct dvb_frontend* fe) #endif /* SkyStar2 DVB-S rev 2.3 */ -#if defined(CONFIG_DVB_MT312_MODULE) +#if FE_SUPPORTED(MT312) static int flexcop_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) { /* u16 wz_half_period_for_45_mhz[] = { 0x01ff, 0x0154, 0x00ff, 0x00cc }; */ @@ -193,10 +198,12 @@ static int skystar2_rev23_attach(struct flexcop_device *fc, } return 0; } +#else +#define skystar2_rev23_attach NULL #endif /* SkyStar2 DVB-S rev 2.6 */ -#if defined(CONFIG_DVB_STV0299_MODULE) +#if FE_SUPPORTED(STV0299) static int samsung_tbmu24112_set_symbol_rate(struct dvb_frontend *fe, u32 srate, u32 ratio) { @@ -321,10 +328,12 @@ static int skystar2_rev26_attach(struct flexcop_device *fc, } return 0; } +#else +#define skystar2_rev26_attach NULL #endif /* SkyStar2 DVB-S rev 2.7 */ -#if defined(CONFIG_DVB_S5H1420_MODULE) +#if FE_SUPPORTED(S5H1420) && FE_SUPPORTED(ISL6421) && FE_SUPPORTED(TUNER_ITD1000) static struct s5h1420_config skystar2_rev2_7_s5h1420_config = { .demod_address = 0x53, .invert = 1, @@ -385,10 +394,12 @@ fail: fc->fc_i2c_adap[0].no_base_addr = 0; return 0; } +#else +#define skystar2_rev27_attach NULL #endif /* SkyStar2 rev 2.8 */ -#if defined(CONFIG_DVB_CX24123_MODULE) +#if FE_SUPPORTED(CX24123) && FE_SUPPORTED(ISL6421) && FE_SUPPORTED(TUNER_CX24113) static struct cx24123_config skystar2_rev2_8_cx24123_config = { .demod_address = 0x55, .dont_use_pll = 1, @@ -433,10 +444,12 @@ static int skystar2_rev28_attach(struct flexcop_device *fc, * IR-receiver (PIC16F818) - but the card has no input for that ??? */ return 1; } +#else +#define skystar2_rev28_attach NULL #endif /* AirStar DVB-T */ -#if defined(CONFIG_DVB_MT352_MODULE) +#if FE_SUPPORTED(MT352) static int samsung_tdtc9251dh0_demod_init(struct dvb_frontend *fe) { static u8 mt352_clock_config[] = { 0x89, 0x18, 0x2d }; @@ -495,10 +508,12 @@ static int airstar_dvbt_attach(struct flexcop_device *fc, } return 0; } +#else +#define airstar_dvbt_attach NULL #endif /* AirStar ATSC 1st generation */ -#if defined(CONFIG_DVB_BCM3510_MODULE) +#if FE_SUPPORTED(BCM3510) static int flexcop_fe_request_firmware(struct dvb_frontend *fe, const struct firmware **fw, char* name) { @@ -517,10 +532,12 @@ static int airstar_atsc1_attach(struct flexcop_device *fc, fc->fe = dvb_attach(bcm3510_attach, &air2pc_atsc_first_gen_config, i2c); return fc->fe != NULL; } +#else +#define airstar_atsc1_attach NULL #endif /* AirStar ATSC 2nd generation */ -#if defined(CONFIG_DVB_NXT200X_MODULE) +#if FE_SUPPORTED(NXT200X) && FE_SUPPORTED(PLL) static struct nxt200x_config samsung_tbmv_config = { .demod_address = 0x0a, }; @@ -535,10 +552,12 @@ static int airstar_atsc2_attach(struct flexcop_device *fc, return !!dvb_attach(dvb_pll_attach, fc->fe, 0x61, NULL, DVB_PLL_SAMSUNG_TBMV); } +#else +#define airstar_atsc2_attach NULL #endif /* AirStar ATSC 3rd generation */ -#if defined(CONFIG_DVB_LGDT330X_MODULE) +#if FE_SUPPORTED(LGDT330X) static struct lgdt330x_config air2pc_atsc_hd5000_config = { .demod_address = 0x59, .demod_chip = LGDT3303, @@ -556,10 +575,12 @@ static int airstar_atsc3_attach(struct flexcop_device *fc, return !!dvb_attach(simple_tuner_attach, fc->fe, i2c, 0x61, TUNER_LG_TDVS_H06XF); } +#else +#define airstar_atsc3_attach NULL #endif /* CableStar2 DVB-C */ -#if defined(CONFIG_DVB_STV0297_MODULE) +#if FE_SUPPORTED(STV0297) static int alps_tdee4_stv0297_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters *fep) { @@ -698,39 +719,23 @@ static int cablestar2_attach(struct flexcop_device *fc, fc->fe->ops.tuner_ops.set_params = alps_tdee4_stv0297_tuner_set_params; return 1; } +#else +#define cablestar2_attach NULL #endif static struct { flexcop_device_type_t type; int (*attach)(struct flexcop_device *, struct i2c_adapter *); } flexcop_frontends[] = { -#if defined(CONFIG_DVB_S5H1420_MODULE) { FC_SKY_REV27, skystar2_rev27_attach }, -#endif -#if defined(CONFIG_DVB_CX24123_MODULE) { FC_SKY_REV28, skystar2_rev28_attach }, -#endif -#if defined(CONFIG_DVB_STV0299_MODULE) { FC_SKY_REV26, skystar2_rev26_attach }, -#endif -#if defined(CONFIG_DVB_MT352_MODULE) { FC_AIR_DVBT, airstar_dvbt_attach }, -#endif -#if defined(CONFIG_DVB_NXT200X_MODULE) { FC_AIR_ATSC2, airstar_atsc2_attach }, -#endif -#if defined(CONFIG_DVB_LGDT330X_MODULE) { FC_AIR_ATSC3, airstar_atsc3_attach }, -#endif -#if defined(CONFIG_DVB_BCM3510_MODULE) { FC_AIR_ATSC1, airstar_atsc1_attach }, -#endif -#if defined(CONFIG_DVB_STV0297_MODULE) { FC_CABLE, cablestar2_attach }, -#endif -#if defined(CONFIG_DVB_MT312_MODULE) { FC_SKY_REV23, skystar2_rev23_attach }, -#endif }; /* try to figure out the frontend */ @@ -738,6 +743,8 @@ int flexcop_frontend_init(struct flexcop_device *fc) { int i; for (i = 0; i < ARRAY_SIZE(flexcop_frontends); i++) { + if (!flexcop_frontends[i].attach) + continue; /* type needs to be set before, because of some workarounds * done based on the probed card type */ fc->dev_type = flexcop_frontends[i].type; diff --git a/drivers/media/dvb/frontends/af9013.c b/drivers/media/dvb/frontends/af9013.c index 136c5863d81..12e018b4107 100644 --- a/drivers/media/dvb/frontends/af9013.c +++ b/drivers/media/dvb/frontends/af9013.c @@ -527,6 +527,10 @@ static int af9013_set_ofdm_params(struct af9013_state *state, u8 i, buf[3] = {0, 0, 0}; *auto_mode = 0; /* set if parameters are requested to auto set */ + /* Try auto-detect transmission parameters in case of AUTO requested or + garbage parameters given by application for compatibility. + MPlayer seems to provide garbage parameters currently. */ + switch (params->transmission_mode) { case TRANSMISSION_MODE_AUTO: *auto_mode = 1; @@ -536,7 +540,8 @@ static int af9013_set_ofdm_params(struct af9013_state *state, buf[0] |= (1 << 0); break; default: - return -EINVAL; + deb_info("%s: invalid transmission_mode\n", __func__); + *auto_mode = 1; } switch (params->guard_interval) { @@ -554,7 +559,8 @@ static int af9013_set_ofdm_params(struct af9013_state *state, buf[0] |= (3 << 2); break; default: - return -EINVAL; + deb_info("%s: invalid guard_interval\n", __func__); + *auto_mode = 1; } switch (params->hierarchy_information) { @@ -572,7 +578,8 @@ static int af9013_set_ofdm_params(struct af9013_state *state, buf[0] |= (3 << 4); break; default: - return -EINVAL; + deb_info("%s: invalid hierarchy_information\n", __func__); + *auto_mode = 1; }; switch (params->constellation) { @@ -587,7 +594,8 @@ static int af9013_set_ofdm_params(struct af9013_state *state, buf[1] |= (2 << 6); break; default: - return -EINVAL; + deb_info("%s: invalid constellation\n", __func__); + *auto_mode = 1; } /* Use HP. How and which case we can switch to LP? */ @@ -611,7 +619,8 @@ static int af9013_set_ofdm_params(struct af9013_state *state, buf[2] |= (4 << 0); break; default: - return -EINVAL; + deb_info("%s: invalid code_rate_HP\n", __func__); + *auto_mode = 1; } switch (params->code_rate_LP) { @@ -638,7 +647,8 @@ static int af9013_set_ofdm_params(struct af9013_state *state, if (params->hierarchy_information == HIERARCHY_AUTO) break; default: - return -EINVAL; + deb_info("%s: invalid code_rate_LP\n", __func__); + *auto_mode = 1; } switch (params->bandwidth) { @@ -651,7 +661,8 @@ static int af9013_set_ofdm_params(struct af9013_state *state, buf[1] |= (2 << 2); break; default: - return -EINVAL; + deb_info("%s: invalid bandwidth\n", __func__); + buf[1] |= (2 << 2); /* cannot auto-detect BW, try 8 MHz */ } /* program */ diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/video/bt8xx/bttv-cards.c index fdb4adff3d2..ca6558c394b 100644 --- a/drivers/media/video/bt8xx/bttv-cards.c +++ b/drivers/media/video/bt8xx/bttv-cards.c @@ -3324,8 +3324,6 @@ void __devinit bttv_init_card1(struct bttv *btv) /* initialization part two -- after registering i2c bus */ void __devinit bttv_init_card2(struct bttv *btv) { - int addr=ADDR_UNSET; - btv->tuner_type = UNSET; if (BTTV_BOARD_UNKNOWN == btv->c.type) { @@ -3470,9 +3468,6 @@ void __devinit bttv_init_card2(struct bttv *btv) btv->pll.pll_current = -1; /* tuner configuration (from card list / autodetect / insmod option) */ - if (ADDR_UNSET != bttv_tvcards[btv->c.type].tuner_addr) - addr = bttv_tvcards[btv->c.type].tuner_addr; - if (UNSET != bttv_tvcards[btv->c.type].tuner_type) if (UNSET == btv->tuner_type) btv->tuner_type = bttv_tvcards[btv->c.type].tuner_type; @@ -3496,40 +3491,6 @@ void __devinit bttv_init_card2(struct bttv *btv) if (UNSET == btv->tuner_type) btv->tuner_type = TUNER_ABSENT; - if (btv->tuner_type != TUNER_ABSENT) { - struct tuner_setup tun_setup; - - /* Load tuner module before issuing tuner config call! */ - if (bttv_tvcards[btv->c.type].has_radio) - v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev, - &btv->c.i2c_adap, "tuner", "tuner", - v4l2_i2c_tuner_addrs(ADDRS_RADIO)); - v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev, - &btv->c.i2c_adap, "tuner", "tuner", - v4l2_i2c_tuner_addrs(ADDRS_DEMOD)); - v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev, - &btv->c.i2c_adap, "tuner", "tuner", - v4l2_i2c_tuner_addrs(ADDRS_TV_WITH_DEMOD)); - - tun_setup.mode_mask = T_ANALOG_TV | T_DIGITAL_TV; - tun_setup.type = btv->tuner_type; - tun_setup.addr = addr; - - if (bttv_tvcards[btv->c.type].has_radio) - tun_setup.mode_mask |= T_RADIO; - - bttv_call_all(btv, tuner, s_type_addr, &tun_setup); - } - - if (btv->tda9887_conf) { - struct v4l2_priv_tun_config tda9887_cfg; - - tda9887_cfg.tuner = TUNER_TDA9887; - tda9887_cfg.priv = &btv->tda9887_conf; - - bttv_call_all(btv, tuner, s_config, &tda9887_cfg); - } - btv->dig = bttv_tvcards[btv->c.type].has_dig_in ? bttv_tvcards[btv->c.type].video_inputs - 1 : UNSET; btv->svhs = bttv_tvcards[btv->c.type].svhs == NO_SVHS ? @@ -3540,15 +3501,15 @@ void __devinit bttv_init_card2(struct bttv *btv) btv->has_remote = remote[btv->c.nr]; if (bttv_tvcards[btv->c.type].has_radio) - btv->has_radio=1; + btv->has_radio = 1; if (bttv_tvcards[btv->c.type].has_remote) - btv->has_remote=1; + btv->has_remote = 1; if (!bttv_tvcards[btv->c.type].no_gpioirq) - btv->gpioirq=1; + btv->gpioirq = 1; if (bttv_tvcards[btv->c.type].volume_gpio) - btv->volume_gpio=bttv_tvcards[btv->c.type].volume_gpio; + btv->volume_gpio = bttv_tvcards[btv->c.type].volume_gpio; if (bttv_tvcards[btv->c.type].audio_mode_gpio) - btv->audio_mode_gpio=bttv_tvcards[btv->c.type].audio_mode_gpio; + btv->audio_mode_gpio = bttv_tvcards[btv->c.type].audio_mode_gpio; if (btv->tuner_type == TUNER_ABSENT) return; /* no tuner or related drivers to load */ @@ -3666,6 +3627,49 @@ no_audio: } +/* initialize the tuner */ +void __devinit bttv_init_tuner(struct bttv *btv) +{ + int addr = ADDR_UNSET; + + if (ADDR_UNSET != bttv_tvcards[btv->c.type].tuner_addr) + addr = bttv_tvcards[btv->c.type].tuner_addr; + + if (btv->tuner_type != TUNER_ABSENT) { + struct tuner_setup tun_setup; + + /* Load tuner module before issuing tuner config call! */ + if (bttv_tvcards[btv->c.type].has_radio) + v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev, + &btv->c.i2c_adap, "tuner", "tuner", + v4l2_i2c_tuner_addrs(ADDRS_RADIO)); + v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev, + &btv->c.i2c_adap, "tuner", "tuner", + v4l2_i2c_tuner_addrs(ADDRS_DEMOD)); + v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev, + &btv->c.i2c_adap, "tuner", "tuner", + v4l2_i2c_tuner_addrs(ADDRS_TV_WITH_DEMOD)); + + tun_setup.mode_mask = T_ANALOG_TV | T_DIGITAL_TV; + tun_setup.type = btv->tuner_type; + tun_setup.addr = addr; + + if (bttv_tvcards[btv->c.type].has_radio) + tun_setup.mode_mask |= T_RADIO; + + bttv_call_all(btv, tuner, s_type_addr, &tun_setup); + } + + if (btv->tda9887_conf) { + struct v4l2_priv_tun_config tda9887_cfg; + + tda9887_cfg.tuner = TUNER_TDA9887; + tda9887_cfg.priv = &btv->tda9887_conf; + + bttv_call_all(btv, tuner, s_config, &tda9887_cfg); + } +} + /* ----------------------------------------------------------------------- */ static void modtec_eeprom(struct bttv *btv) diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c index d147d29bb0d..8cc6dd28d6a 100644 --- a/drivers/media/video/bt8xx/bttv-driver.c +++ b/drivers/media/video/bt8xx/bttv-driver.c @@ -4419,6 +4419,7 @@ static int __devinit bttv_probe(struct pci_dev *dev, /* some card-specific stuff (needs working i2c) */ bttv_init_card2(btv); + bttv_init_tuner(btv); init_irqreg(btv); /* register video4linux + input */ diff --git a/drivers/media/video/bt8xx/bttv.h b/drivers/media/video/bt8xx/bttv.h index 3d36daf206f..3ec2402c6b4 100644 --- a/drivers/media/video/bt8xx/bttv.h +++ b/drivers/media/video/bt8xx/bttv.h @@ -283,6 +283,7 @@ extern struct tvcard bttv_tvcards[]; extern void bttv_idcard(struct bttv *btv); extern void bttv_init_card1(struct bttv *btv); extern void bttv_init_card2(struct bttv *btv); +extern void bttv_init_tuner(struct bttv *btv); /* card-specific funtions */ extern void tea5757_set_freq(struct bttv *btv, unsigned short freq); diff --git a/drivers/media/video/cx23885/cx23885-417.c b/drivers/media/video/cx23885/cx23885-417.c index 428f0c45e6b..e0cf21e0b1b 100644 --- a/drivers/media/video/cx23885/cx23885-417.c +++ b/drivers/media/video/cx23885/cx23885-417.c @@ -58,7 +58,8 @@ MODULE_PARM_DESC(v4l_debug, "enable V4L debug messages"); #define dprintk(level, fmt, arg...)\ do { if (v4l_debug >= level) \ - printk(KERN_DEBUG "%s: " fmt, dev->name , ## arg);\ + printk(KERN_DEBUG "%s: " fmt, \ + (dev) ? dev->name : "cx23885[?]", ## arg); \ } while (0) static struct cx23885_tvnorm cx23885_tvnorms[] = { @@ -1677,6 +1678,7 @@ static struct v4l2_file_operations mpeg_fops = { .read = mpeg_read, .poll = mpeg_poll, .mmap = mpeg_mmap, + .ioctl = video_ioctl2, }; static const struct v4l2_ioctl_ops mpeg_ioctl_ops = { diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index ebd24a25fb8..320f1f60276 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -58,8 +58,6 @@ static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; module_param_array(card, int, NULL, 0444); MODULE_PARM_DESC(card, "card type"); -#define MT9V011_VERSION 0x8243 - /* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS */ static unsigned long em28xx_devused; @@ -159,6 +157,20 @@ static struct em28xx_reg_seq evga_indtube_digital[] = { { -1, -1, -1, -1}, }; +/* Pinnacle Hybrid Pro eb1a:2881 */ +static struct em28xx_reg_seq pinnacle_hybrid_pro_analog[] = { + {EM28XX_R08_GPIO, 0xfd, ~EM_GPIO_4, 10}, + { -1, -1, -1, -1}, +}; + +static struct em28xx_reg_seq pinnacle_hybrid_pro_digital[] = { + {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10}, + {EM2880_R04_GPO, 0x04, 0xff, 100},/* zl10353 reset */ + {EM2880_R04_GPO, 0x0c, 0xff, 1}, + { -1, -1, -1, -1}, +}; + + /* Callback for the most boards */ static struct em28xx_reg_seq default_tuner_gpio[] = { {EM28XX_R08_GPIO, EM_GPIO_4, EM_GPIO_4, 10}, @@ -205,13 +217,15 @@ static struct em28xx_reg_seq silvercrest_reg_seq[] = { */ struct em28xx_board em28xx_boards[] = { [EM2750_BOARD_UNKNOWN] = { - .name = "Unknown EM2750/EM2751 webcam grabber", + .name = "EM2710/EM2750/EM2751 webcam grabber", .xclk = EM28XX_XCLK_FREQUENCY_48MHZ, - .tuner_type = TUNER_ABSENT, /* This is a webcam */ + .tuner_type = TUNER_ABSENT, + .is_webcam = 1, .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = 0, .amux = EM28XX_AMUX_VIDEO, + .gpio = silvercrest_reg_seq, } }, }, [EM2800_BOARD_UNKNOWN] = { @@ -233,13 +247,15 @@ struct em28xx_board em28xx_boards[] = { [EM2820_BOARD_UNKNOWN] = { .name = "Unknown EM2750/28xx video grabber", .tuner_type = TUNER_ABSENT, + .is_webcam = 1, /* To enable sensor probe */ }, [EM2750_BOARD_DLCW_130] = { /* Beijing Huaqi Information Digital Technology Co., Ltd */ .name = "Huaqi DLCW-130", .valid = EM28XX_BOARD_NOT_VALIDATED, .xclk = EM28XX_XCLK_FREQUENCY_48MHZ, - .tuner_type = TUNER_ABSENT, /* This is a webcam */ + .tuner_type = TUNER_ABSENT, + .is_webcam = 1, .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = 0, @@ -440,7 +456,8 @@ struct em28xx_board em28xx_boards[] = { [EM2820_BOARD_VIDEOLOGY_20K14XUSB] = { .name = "Videology 20K14XUSB USB2.0", .valid = EM28XX_BOARD_NOT_VALIDATED, - .tuner_type = TUNER_ABSENT, /* This is a webcam */ + .tuner_type = TUNER_ABSENT, + .is_webcam = 1, .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = 0, @@ -450,8 +467,7 @@ struct em28xx_board em28xx_boards[] = { [EM2820_BOARD_SILVERCREST_WEBCAM] = { .name = "Silvercrest Webcam 1.3mpix", .tuner_type = TUNER_ABSENT, - .is_27xx = 1, - .decoder = EM28XX_MT9V011, + .is_webcam = 1, .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = 0, @@ -500,7 +516,8 @@ struct em28xx_board em28xx_boards[] = { /* Beijing Huaqi Information Digital Technology Co., Ltd */ .name = "NetGMBH Cam", .valid = EM28XX_BOARD_NOT_VALIDATED, - .tuner_type = TUNER_ABSENT, /* This is a webcam */ + .tuner_type = TUNER_ABSENT, + .is_webcam = 1, .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = 0, @@ -1250,25 +1267,26 @@ struct em28xx_board em28xx_boards[] = { }, [EM2881_BOARD_PINNACLE_HYBRID_PRO] = { .name = "Pinnacle Hybrid Pro", - .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_XC2028, .tuner_gpio = default_tuner_gpio, .decoder = EM28XX_TVP5150, + .has_dvb = 1, + .dvb_gpio = pinnacle_hybrid_pro_digital, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_VIDEO, - .gpio = default_analog, + .gpio = pinnacle_hybrid_pro_analog, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, .amux = EM28XX_AMUX_LINE_IN, - .gpio = default_analog, + .gpio = pinnacle_hybrid_pro_analog, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, .amux = EM28XX_AMUX_LINE_IN, - .gpio = default_analog, + .gpio = pinnacle_hybrid_pro_analog, } }, }, [EM2882_BOARD_PINNACLE_HYBRID_PRO] = { @@ -1638,6 +1656,7 @@ static struct em28xx_hash_table em28xx_eeprom_hash[] = { {0x966a0441, EM2880_BOARD_KWORLD_DVB_310U, TUNER_XC2028}, {0x9567eb1a, EM2880_BOARD_EMPIRE_DUAL_TV, TUNER_XC2028}, {0xcee44a99, EM2882_BOARD_EVGA_INDTUBE, TUNER_XC2028}, + {0xb8846b20, EM2881_BOARD_PINNACLE_HYBRID_PRO, TUNER_XC2028}, }; /* I2C devicelist hash table for devices with generic USB IDs */ @@ -1704,6 +1723,32 @@ static inline void em28xx_set_model(struct em28xx *dev) EM28XX_I2C_FREQ_100_KHZ; } +/* FIXME: Should be replaced by a proper mt9m001 driver */ +static int em28xx_initialize_mt9m001(struct em28xx *dev) +{ + int i; + unsigned char regs[][3] = { + { 0x0d, 0x00, 0x01, }, + { 0x0d, 0x00, 0x00, }, + { 0x04, 0x05, 0x00, }, /* hres = 1280 */ + { 0x03, 0x04, 0x00, }, /* vres = 1024 */ + { 0x20, 0x11, 0x00, }, + { 0x06, 0x00, 0x10, }, + { 0x2b, 0x00, 0x24, }, + { 0x2e, 0x00, 0x24, }, + { 0x35, 0x00, 0x24, }, + { 0x2d, 0x00, 0x20, }, + { 0x2c, 0x00, 0x20, }, + { 0x09, 0x0a, 0xd4, }, + { 0x35, 0x00, 0x57, }, + }; + + for (i = 0; i < ARRAY_SIZE(regs); i++) + i2c_master_send(&dev->i2c_client, ®s[i][0], 3); + + return 0; +} + /* HINT method: webcam I2C chips * * This method work for webcams with Micron sensors @@ -1716,9 +1761,6 @@ static int em28xx_hint_sensor(struct em28xx *dev) __be16 version_be; u16 version; - if (dev->model != EM2820_BOARD_UNKNOWN) - return 0; - dev->i2c_client.addr = 0xba >> 1; cmd = 0; i2c_master_send(&dev->i2c_client, &cmd, 1); @@ -1729,16 +1771,38 @@ static int em28xx_hint_sensor(struct em28xx *dev) version = be16_to_cpu(version_be); switch (version) { - case MT9V011_VERSION: + case 0x8243: /* mt9v011 640x480 1.3 Mpix sensor */ dev->model = EM2820_BOARD_SILVERCREST_WEBCAM; sensor_name = "mt9v011"; + dev->em28xx_sensor = EM28XX_MT9V011; + dev->sensor_xres = 640; + dev->sensor_yres = 480; + dev->sensor_xtal = 6300000; + + /* probably means GRGB 16 bit bayer */ + dev->vinmode = 0x0d; + dev->vinctl = 0x00; + + break; + case 0x8431: + dev->model = EM2750_BOARD_UNKNOWN; + sensor_name = "mt9m001"; + dev->em28xx_sensor = EM28XX_MT9M001; + em28xx_initialize_mt9m001(dev); + dev->sensor_xres = 1280; + dev->sensor_yres = 1024; + + /* probably means BGGR 16 bit bayer */ + dev->vinmode = 0x0c; + dev->vinctl = 0x00; + break; default: - printk("Unknown Sensor 0x%04x\n", be16_to_cpu(version)); + printk("Unknown Micron Sensor 0x%04x\n", be16_to_cpu(version)); return -EINVAL; } - em28xx_errdev("Sensor is %s, assuming that webcam is %s\n", + em28xx_errdev("Sensor is %s, using model %s entry.\n", sensor_name, em28xx_boards[dev->model].name); return 0; @@ -1772,10 +1836,7 @@ void em28xx_pre_card_setup(struct em28xx *dev) em28xx_info("chip ID is em2750\n"); break; case CHIP_ID_EM2820: - if (dev->board.is_27xx) - em28xx_info("chip is em2710\n"); - else - em28xx_info("chip ID is em2820\n"); + em28xx_info("chip ID is em2710 or em2820\n"); break; case CHIP_ID_EM2840: em28xx_info("chip ID is em2840\n"); @@ -1929,6 +1990,7 @@ static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl) ctl->demod = XC3028_FE_ZARLINK456; break; case EM2880_BOARD_TERRATEC_HYBRID_XS: + case EM2881_BOARD_PINNACLE_HYBRID_PRO: ctl->demod = XC3028_FE_ZARLINK456; break; case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2: @@ -2225,6 +2287,7 @@ void em28xx_card_setup(struct em28xx *dev) em28xx_set_mode() in em28xx_pre_card_setup() was a no-op, so make the call now so the analog GPIOs are set properly before probing the i2c bus. */ + em28xx_gpio_set(dev, dev->board.tuner_gpio); em28xx_set_mode(dev, EM28XX_ANALOG_MODE); break; case EM2820_BOARD_SILVERCREST_WEBCAM: @@ -2262,9 +2325,14 @@ void em28xx_card_setup(struct em28xx *dev) v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, &dev->i2c_adap, "tvp5150", "tvp5150", tvp5150_addrs); - if (dev->board.decoder == EM28XX_MT9V011) - v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, &dev->i2c_adap, - "mt9v011", "mt9v011", mt9v011_addrs); + if (dev->em28xx_sensor == EM28XX_MT9V011) { + struct v4l2_subdev *sd; + + sd = v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, + &dev->i2c_adap, "mt9v011", "mt9v011", mt9v011_addrs); + v4l2_subdev_call(sd, core, s_config, 0, &dev->sensor_xtal); + } + if (dev->board.adecoder == EM28XX_TVAUDIO) v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, @@ -2410,7 +2478,19 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, return errCode; } - em28xx_hint_sensor(dev); + /* + * Default format, used for tvp5150 or saa711x output formats + */ + dev->vinmode = 0x10; + dev->vinctl = 0x11; + + /* + * If the device can be a webcam, seek for a sensor. + * If sensor is not found, then it isn't a webcam. + */ + if (dev->board.is_webcam) + if (em28xx_hint_sensor(dev) < 0) + dev->board.is_webcam = 0; /* Do board specific init and eeprom reading */ em28xx_card_setup(dev); diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c index 079ab4d563a..5b78e199abd 100644 --- a/drivers/media/video/em28xx/em28xx-core.c +++ b/drivers/media/video/em28xx/em28xx-core.c @@ -648,28 +648,17 @@ int em28xx_capture_start(struct em28xx *dev, int start) int em28xx_set_outfmt(struct em28xx *dev) { int ret; - int vinmode, vinctl, outfmt; - - outfmt = dev->format->reg; - - if (dev->board.is_27xx) { - vinmode = 0x0d; - vinctl = 0x00; - } else { - vinmode = 0x10; - vinctl = 0x11; - } ret = em28xx_write_reg_bits(dev, EM28XX_R27_OUTFMT, - outfmt | 0x20, 0xff); + dev->format->reg | 0x20, 0xff); if (ret < 0) return ret; - ret = em28xx_write_reg(dev, EM28XX_R10_VINMODE, vinmode); + ret = em28xx_write_reg(dev, EM28XX_R10_VINMODE, dev->vinmode); if (ret < 0) return ret; - return em28xx_write_reg(dev, EM28XX_R11_VINCTRL, vinctl); + return em28xx_write_reg(dev, EM28XX_R11_VINCTRL, dev->vinctl); } static int em28xx_accumulator_set(struct em28xx *dev, u8 xmin, u8 xmax, @@ -707,10 +696,7 @@ static int em28xx_scaler_set(struct em28xx *dev, u16 h, u16 v) u8 mode; /* the em2800 scaler only supports scaling down to 50% */ - if (dev->board.is_27xx) { - /* FIXME: Don't use the scaler yet */ - mode = 0; - } else if (dev->board.is_em2800) { + if (dev->board.is_em2800) { mode = (v ? 0x20 : 0x00) | (h ? 0x10 : 0x00); } else { u8 buf[2]; diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c index 3da97c32b8f..cf0ac7f2a30 100644 --- a/drivers/media/video/em28xx/em28xx-dvb.c +++ b/drivers/media/video/em28xx/em28xx-dvb.c @@ -31,6 +31,8 @@ #include "lgdt330x.h" #include "zl10353.h" #include "s5h1409.h" +#include "mt352.h" +#include "mt352_priv.h" /* FIXME */ MODULE_DESCRIPTION("driver for em28xx based DVB cards"); MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>"); @@ -243,7 +245,7 @@ static struct s5h1409_config em28xx_s5h1409_with_xc3028 = { .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK }; -static struct zl10353_config em28xx_terratec_xs_zl10353_xc3028 = { +static struct zl10353_config em28xx_zl10353_xc3028_no_i2c_gate = { .demod_address = (0x1e >> 1), .no_tuner = 1, .disable_i2c_gate_ctrl = 1, @@ -258,6 +260,41 @@ static struct drx397xD_config em28xx_drx397xD_with_xc3028 = { }; #endif +static int mt352_terratec_xs_init(struct dvb_frontend *fe) +{ + /* Values extracted from a USB trace of the Terratec Windows driver */ + static u8 clock_config[] = { CLOCK_CTL, 0x38, 0x2c }; + static u8 reset[] = { RESET, 0x80 }; + static u8 adc_ctl_1_cfg[] = { ADC_CTL_1, 0x40 }; + static u8 agc_cfg[] = { AGC_TARGET, 0x28, 0xa0 }; + static u8 input_freq_cfg[] = { INPUT_FREQ_1, 0x31, 0xb8 }; + static u8 rs_err_cfg[] = { RS_ERR_PER_1, 0x00, 0x4d }; + static u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 }; + static u8 trl_nom_cfg[] = { TRL_NOMINAL_RATE_1, 0x64, 0x00 }; + static u8 tps_given_cfg[] = { TPS_GIVEN_1, 0x40, 0x80, 0x50 }; + static u8 tuner_go[] = { TUNER_GO, 0x01}; + + mt352_write(fe, clock_config, sizeof(clock_config)); + udelay(200); + mt352_write(fe, reset, sizeof(reset)); + mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg)); + mt352_write(fe, agc_cfg, sizeof(agc_cfg)); + mt352_write(fe, input_freq_cfg, sizeof(input_freq_cfg)); + mt352_write(fe, rs_err_cfg, sizeof(rs_err_cfg)); + mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg)); + mt352_write(fe, trl_nom_cfg, sizeof(trl_nom_cfg)); + mt352_write(fe, tps_given_cfg, sizeof(tps_given_cfg)); + mt352_write(fe, tuner_go, sizeof(tuner_go)); + return 0; +} + +static struct mt352_config terratec_xs_mt352_cfg = { + .demod_address = (0x1e >> 1), + .no_tuner = 1, + .if2 = 45600, + .demod_init = mt352_terratec_xs_init, +}; + /* ------------------------------------------------------------------ */ static int attach_xc3028(u8 addr, struct em28xx *dev) @@ -440,7 +477,6 @@ static int dvb_init(struct em28xx *dev) goto out_free; } break; - case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: case EM2880_BOARD_KWORLD_DVB_310U: case EM2880_BOARD_EMPIRE_DUAL_TV: dvb->frontend = dvb_attach(zl10353_attach, @@ -451,20 +487,28 @@ static int dvb_init(struct em28xx *dev) goto out_free; } break; + case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: + dvb->frontend = dvb_attach(zl10353_attach, + &em28xx_zl10353_xc3028_no_i2c_gate, + &dev->i2c_adap); + if (attach_xc3028(0x61, dev) < 0) { + result = -EINVAL; + goto out_free; + } + break; case EM2880_BOARD_TERRATEC_HYBRID_XS: + case EM2881_BOARD_PINNACLE_HYBRID_PRO: dvb->frontend = dvb_attach(zl10353_attach, - &em28xx_terratec_xs_zl10353_xc3028, + &em28xx_zl10353_xc3028_no_i2c_gate, &dev->i2c_adap); if (dvb->frontend == NULL) { /* This board could have either a zl10353 or a mt352. If the chip id isn't for zl10353, try mt352 */ - - /* FIXME: make support for mt352 work */ - printk(KERN_ERR "version of this board with mt352 not " - "currently supported\n"); - result = -EINVAL; - goto out_free; + dvb->frontend = dvb_attach(mt352_attach, + &terratec_xs_mt352_cfg, + &dev->i2c_adap); } + if (attach_xc3028(0x61, dev) < 0) { result = -EINVAL; goto out_free; diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index 14316c91217..ff37b4c15f4 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -657,8 +657,8 @@ static void get_scale(struct em28xx *dev, unsigned int width, unsigned int height, unsigned int *hscale, unsigned int *vscale) { - unsigned int maxw = norm_maxw(dev); - unsigned int maxh = norm_maxh(dev); + unsigned int maxw = norm_maxw(dev); + unsigned int maxh = norm_maxh(dev); *hscale = (((unsigned long)maxw) << 12) / width - 4096L; if (*hscale >= 0x4000) @@ -726,11 +726,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, return -EINVAL; } - if (dev->board.is_27xx) { - /* FIXME: This is the only supported fmt */ - width = 640; - height = 480; - } else if (dev->board.is_em2800) { + if (dev->board.is_em2800) { /* the em2800 can only scale down to 50% */ height = height > (3 * maxh / 4) ? maxh : maxh / 2; width = width > (3 * maxw / 4) ? maxw : maxw / 2; @@ -767,12 +763,6 @@ static int em28xx_set_video_format(struct em28xx *dev, unsigned int fourcc, { struct em28xx_fmt *fmt; - /* FIXME: This is the only supported fmt */ - if (dev->board.is_27xx) { - width = 640; - height = 480; - } - fmt = format_by_fourcc(fourcc); if (!fmt) return -EINVAL; diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index d90fef46376..45bd513f62d 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -358,10 +358,15 @@ struct em28xx_input { #define INPUT(nr) (&em28xx_boards[dev->model].input[nr]) enum em28xx_decoder { - EM28XX_NODECODER, + EM28XX_NODECODER = 0, EM28XX_TVP5150, EM28XX_SAA711X, +}; + +enum em28xx_sensor { + EM28XX_NOSENSOR = 0, EM28XX_MT9V011, + EM28XX_MT9M001, }; enum em28xx_adecoder { @@ -390,7 +395,7 @@ struct em28xx_board { unsigned int max_range_640_480:1; unsigned int has_dvb:1; unsigned int has_snapshot_button:1; - unsigned int is_27xx:1; + unsigned int is_webcam:1; unsigned int valid:1; unsigned char xclk, i2c_speed; @@ -474,6 +479,14 @@ struct em28xx { struct v4l2_device v4l2_dev; struct em28xx_board board; + /* Webcam specific fields */ + enum em28xx_sensor em28xx_sensor; + int sensor_xres, sensor_yres; + int sensor_xtal; + + /* Vinmode/Vinctl used at the driver */ + int vinmode, vinctl; + unsigned int stream_on:1; /* Locks streams */ unsigned int has_audio_class:1; unsigned int has_alsa_audio:1; @@ -754,17 +767,23 @@ static inline int em28xx_gamma_set(struct em28xx *dev, s32 val) /*FIXME: maxw should be dependent of alt mode */ static inline unsigned int norm_maxw(struct em28xx *dev) { + if (dev->board.is_webcam) + return dev->sensor_xres; + if (dev->board.max_range_640_480) return 640; - else - return 720; + + return 720; } static inline unsigned int norm_maxh(struct em28xx *dev) { + if (dev->board.is_webcam) + return dev->sensor_yres; + if (dev->board.max_range_640_480) return 480; - else - return (dev->norm & V4L2_STD_625_50) ? 576 : 480; + + return (dev->norm & V4L2_STD_625_50) ? 576 : 480; } #endif diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig index 578dc4ffc96..34f46f2bc04 100644 --- a/drivers/media/video/gspca/Kconfig +++ b/drivers/media/video/gspca/Kconfig @@ -102,6 +102,22 @@ config USB_GSPCA_PAC7311 To compile this driver as a module, choose M here: the module will be called gspca_pac7311. +config USB_GSPCA_SN9C20X + tristate "SN9C20X USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the + sn9c20x chips (SN9C201 and SN9C202). + + To compile this driver as a module, choose M here: the + module will be called gspca_sn9c20x. + +config USB_GSPCA_SN9C20X_EVDEV + bool "Enable evdev support" + depends on USB_GSPCA_SN9C20X + ---help--- + Say Y here in order to enable evdev support for sn9c20x webcam button. + config USB_GSPCA_SONIXB tristate "SONIX Bayer USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/video/gspca/Makefile index 8a6643e8eb9..f6d3b86e9ad 100644 --- a/drivers/media/video/gspca/Makefile +++ b/drivers/media/video/gspca/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_USB_GSPCA_OV519) += gspca_ov519.o obj-$(CONFIG_USB_GSPCA_OV534) += gspca_ov534.o obj-$(CONFIG_USB_GSPCA_PAC207) += gspca_pac207.o obj-$(CONFIG_USB_GSPCA_PAC7311) += gspca_pac7311.o +obj-$(CONFIG_USB_GSPCA_SN9C20X) += gspca_sn9c20x.o obj-$(CONFIG_USB_GSPCA_SONIXB) += gspca_sonixb.o obj-$(CONFIG_USB_GSPCA_SONIXJ) += gspca_sonixj.o obj-$(CONFIG_USB_GSPCA_SPCA500) += gspca_spca500.o @@ -35,6 +36,7 @@ gspca_ov519-objs := ov519.o gspca_ov534-objs := ov534.o gspca_pac207-objs := pac207.o gspca_pac7311-objs := pac7311.o +gspca_sn9c20x-objs := sn9c20x.o gspca_sonixb-objs := sonixb.o gspca_sonixj-objs := sonixj.o gspca_spca500-objs := spca500.o diff --git a/drivers/media/video/gspca/conex.c b/drivers/media/video/gspca/conex.c index 219cfa6fb87..8d48ea1742c 100644 --- a/drivers/media/video/gspca/conex.c +++ b/drivers/media/video/gspca/conex.c @@ -846,6 +846,8 @@ static int sd_start(struct gspca_dev *gspca_dev) /* create the JPEG header */ sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + if (!sd->jpeg_hdr) + return -ENOMEM; jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, 0x22); /* JPEG 411 */ jpeg_set_qual(sd->jpeg_hdr, sd->quality); diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 1e89600986c..b8561dfb6c8 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -727,6 +727,74 @@ static int gspca_get_mode(struct gspca_dev *gspca_dev, return -EINVAL; } +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int vidioc_g_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg) +{ + int ret; + struct gspca_dev *gspca_dev = priv; + + if (!gspca_dev->sd_desc->get_chip_ident) + return -EINVAL; + + if (!gspca_dev->sd_desc->get_register) + return -EINVAL; + + if (mutex_lock_interruptible(&gspca_dev->usb_lock)) + return -ERESTARTSYS; + if (gspca_dev->present) + ret = gspca_dev->sd_desc->get_register(gspca_dev, reg); + else + ret = -ENODEV; + mutex_unlock(&gspca_dev->usb_lock); + + return ret; +} + +static int vidioc_s_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg) +{ + int ret; + struct gspca_dev *gspca_dev = priv; + + if (!gspca_dev->sd_desc->get_chip_ident) + return -EINVAL; + + if (!gspca_dev->sd_desc->set_register) + return -EINVAL; + + if (mutex_lock_interruptible(&gspca_dev->usb_lock)) + return -ERESTARTSYS; + if (gspca_dev->present) + ret = gspca_dev->sd_desc->set_register(gspca_dev, reg); + else + ret = -ENODEV; + mutex_unlock(&gspca_dev->usb_lock); + + return ret; +} +#endif + +static int vidioc_g_chip_ident(struct file *file, void *priv, + struct v4l2_dbg_chip_ident *chip) +{ + int ret; + struct gspca_dev *gspca_dev = priv; + + if (!gspca_dev->sd_desc->get_chip_ident) + return -EINVAL; + + if (mutex_lock_interruptible(&gspca_dev->usb_lock)) + return -ERESTARTSYS; + if (gspca_dev->present) + ret = gspca_dev->sd_desc->get_chip_ident(gspca_dev, chip); + else + ret = -ENODEV; + mutex_unlock(&gspca_dev->usb_lock); + + return ret; +} + static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *fmtdesc) { @@ -1883,6 +1951,11 @@ static const struct v4l2_ioctl_ops dev_ioctl_ops = { .vidioc_s_parm = vidioc_s_parm, .vidioc_s_std = vidioc_s_std, .vidioc_enum_framesizes = vidioc_enum_framesizes, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = vidioc_g_register, + .vidioc_s_register = vidioc_s_register, +#endif + .vidioc_g_chip_ident = vidioc_g_chip_ident, #ifdef CONFIG_VIDEO_V4L1_COMPAT .vidiocgmbuf = vidiocgmbuf, #endif diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h index bd1faff8864..46c4effdfcd 100644 --- a/drivers/media/video/gspca/gspca.h +++ b/drivers/media/video/gspca/gspca.h @@ -69,6 +69,10 @@ typedef void (*cam_v_op) (struct gspca_dev *); typedef int (*cam_cf_op) (struct gspca_dev *, const struct usb_device_id *); typedef int (*cam_jpg_op) (struct gspca_dev *, struct v4l2_jpegcompression *); +typedef int (*cam_reg_op) (struct gspca_dev *, + struct v4l2_dbg_register *); +typedef int (*cam_ident_op) (struct gspca_dev *, + struct v4l2_dbg_chip_ident *); typedef int (*cam_streamparm_op) (struct gspca_dev *, struct v4l2_streamparm *); typedef int (*cam_qmnu_op) (struct gspca_dev *, @@ -105,6 +109,11 @@ struct sd_desc { cam_qmnu_op querymenu; cam_streamparm_op get_streamparm; cam_streamparm_op set_streamparm; +#ifdef CONFIG_VIDEO_ADV_DEBUG + cam_reg_op set_register; + cam_reg_op get_register; +#endif + cam_ident_op get_chip_ident; }; /* packet types when moving from iso buf to frame buf */ diff --git a/drivers/media/video/gspca/m5602/m5602_s5k4aa.c b/drivers/media/video/gspca/m5602/m5602_s5k4aa.c index 191bcd71897..0163903d1c0 100644 --- a/drivers/media/video/gspca/m5602/m5602_s5k4aa.c +++ b/drivers/media/video/gspca/m5602/m5602_s5k4aa.c @@ -476,9 +476,6 @@ static int s5k4aa_set_vflip(struct gspca_dev *gspca_dev, __s32 val) err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); if (err < 0) return err; - err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1); - if (err < 0) - return err; err = m5602_read_sensor(sd, S5K4AA_READ_MODE, &data, 1); if (err < 0) @@ -524,9 +521,6 @@ static int s5k4aa_set_hflip(struct gspca_dev *gspca_dev, __s32 val) err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); if (err < 0) return err; - err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1); - if (err < 0) - return err; err = m5602_read_sensor(sd, S5K4AA_READ_MODE, &data, 1); if (err < 0) diff --git a/drivers/media/video/gspca/mars.c b/drivers/media/video/gspca/mars.c index 75e8d14e4ac..de769caf013 100644 --- a/drivers/media/video/gspca/mars.c +++ b/drivers/media/video/gspca/mars.c @@ -201,6 +201,8 @@ static int sd_start(struct gspca_dev *gspca_dev) /* create the JPEG header */ sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + if (!sd->jpeg_hdr) + return -ENOMEM; jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, 0x21); /* JPEG 422 */ jpeg_set_qual(sd->jpeg_hdr, sd->quality); diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c new file mode 100644 index 00000000000..fcfbbd329b4 --- /dev/null +++ b/drivers/media/video/gspca/sn9c20x.c @@ -0,0 +1,2434 @@ +/* + * Sonix sn9c201 sn9c202 library + * Copyright (C) 2008-2009 microdia project <microdia@googlegroups.com> + * Copyright (C) 2009 Brian Johnson <brijohn@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef CONFIG_USB_GSPCA_SN9C20X_EVDEV +#include <linux/kthread.h> +#include <linux/freezer.h> +#include <linux/usb/input.h> +#include <linux/input.h> +#endif + +#include "gspca.h" +#include "jpeg.h" + +#include <media/v4l2-chip-ident.h> + +MODULE_AUTHOR("Brian Johnson <brijohn@gmail.com>, " + "microdia project <microdia@googlegroups.com>"); +MODULE_DESCRIPTION("GSPCA/SN9C20X USB Camera Driver"); +MODULE_LICENSE("GPL"); + +#define MODULE_NAME "sn9c20x" + +#define MODE_RAW 0x10 +#define MODE_JPEG 0x20 +#define MODE_SXGA 0x80 + +#define SENSOR_OV9650 0 +#define SENSOR_OV9655 1 +#define SENSOR_SOI968 2 +#define SENSOR_OV7660 3 +#define SENSOR_OV7670 4 +#define SENSOR_MT9V011 5 +#define SENSOR_MT9V111 6 +#define SENSOR_MT9V112 7 +#define SENSOR_MT9M001 8 +#define SENSOR_MT9M111 9 +#define SENSOR_HV7131R 10 +#define SENSOR_MT9VPRB 20 + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; + +#define MIN_AVG_LUM 80 +#define MAX_AVG_LUM 130 + atomic_t avg_lum; + u8 old_step; + u8 older_step; + u8 exposure_step; + + u8 brightness; + u8 contrast; + u8 saturation; + s16 hue; + u8 gamma; + u8 red; + u8 blue; + + u8 hflip; + u8 vflip; + u8 gain; + u16 exposure; + u8 auto_exposure; + + u8 i2c_addr; + u8 sensor; + u8 hstart; + u8 vstart; + + u8 *jpeg_hdr; + u8 quality; + +#ifdef CONFIG_USB_GSPCA_SN9C20X_EVDEV + struct input_dev *input_dev; + u8 input_gpio; + struct task_struct *input_task; +#endif +}; + +static int sd_setbrightness(struct gspca_dev *gspca_dev, s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, s32 *val); +static int sd_setsaturation(struct gspca_dev *gspca_dev, s32 val); +static int sd_getsaturation(struct gspca_dev *gspca_dev, s32 *val); +static int sd_sethue(struct gspca_dev *gspca_dev, s32 val); +static int sd_gethue(struct gspca_dev *gspca_dev, s32 *val); +static int sd_setgamma(struct gspca_dev *gspca_dev, s32 val); +static int sd_getgamma(struct gspca_dev *gspca_dev, s32 *val); +static int sd_setredbalance(struct gspca_dev *gspca_dev, s32 val); +static int sd_getredbalance(struct gspca_dev *gspca_dev, s32 *val); +static int sd_setbluebalance(struct gspca_dev *gspca_dev, s32 val); +static int sd_getbluebalance(struct gspca_dev *gspca_dev, s32 *val); +static int sd_setvflip(struct gspca_dev *gspca_dev, s32 val); +static int sd_getvflip(struct gspca_dev *gspca_dev, s32 *val); +static int sd_sethflip(struct gspca_dev *gspca_dev, s32 val); +static int sd_gethflip(struct gspca_dev *gspca_dev, s32 *val); +static int sd_setgain(struct gspca_dev *gspca_dev, s32 val); +static int sd_getgain(struct gspca_dev *gspca_dev, s32 *val); +static int sd_setexposure(struct gspca_dev *gspca_dev, s32 val); +static int sd_getexposure(struct gspca_dev *gspca_dev, s32 *val); +static int sd_setautoexposure(struct gspca_dev *gspca_dev, s32 val); +static int sd_getautoexposure(struct gspca_dev *gspca_dev, s32 *val); + +static struct ctrl sd_ctrls[] = { + { +#define BRIGHTNESS_IDX 0 + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 0xff, + .step = 1, +#define BRIGHTNESS_DEFAULT 0x7f + .default_value = BRIGHTNESS_DEFAULT, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, + { +#define CONTRAST_IDX 1 + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 0xff, + .step = 1, +#define CONTRAST_DEFAULT 0x7f + .default_value = CONTRAST_DEFAULT, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, + { +#define SATURATION_IDX 2 + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 0, + .maximum = 0xff, + .step = 1, +#define SATURATION_DEFAULT 0x7f + .default_value = SATURATION_DEFAULT, + }, + .set = sd_setsaturation, + .get = sd_getsaturation, + }, + { +#define HUE_IDX 3 + { + .id = V4L2_CID_HUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Hue", + .minimum = -180, + .maximum = 180, + .step = 1, +#define HUE_DEFAULT 0 + .default_value = HUE_DEFAULT, + }, + .set = sd_sethue, + .get = sd_gethue, + }, + { +#define GAMMA_IDX 4 + { + .id = V4L2_CID_GAMMA, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gamma", + .minimum = 0, + .maximum = 0xff, + .step = 1, +#define GAMMA_DEFAULT 0x10 + .default_value = GAMMA_DEFAULT, + }, + .set = sd_setgamma, + .get = sd_getgamma, + }, + { +#define BLUE_IDX 5 + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Blue Balance", + .minimum = 0, + .maximum = 0x7f, + .step = 1, +#define BLUE_DEFAULT 0x28 + .default_value = BLUE_DEFAULT, + }, + .set = sd_setbluebalance, + .get = sd_getbluebalance, + }, + { +#define RED_IDX 6 + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Red Balance", + .minimum = 0, + .maximum = 0x7f, + .step = 1, +#define RED_DEFAULT 0x28 + .default_value = RED_DEFAULT, + }, + .set = sd_setredbalance, + .get = sd_getredbalance, + }, + { +#define HFLIP_IDX 7 + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Horizontal Flip", + .minimum = 0, + .maximum = 1, + .step = 1, +#define HFLIP_DEFAULT 0 + .default_value = HFLIP_DEFAULT, + }, + .set = sd_sethflip, + .get = sd_gethflip, + }, + { +#define VFLIP_IDX 8 + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Vertical Flip", + .minimum = 0, + .maximum = 1, + .step = 1, +#define VFLIP_DEFAULT 0 + .default_value = VFLIP_DEFAULT, + }, + .set = sd_setvflip, + .get = sd_getvflip, + }, + { +#define EXPOSURE_IDX 9 + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 0, + .maximum = 0x1780, + .step = 1, +#define EXPOSURE_DEFAULT 0x33 + .default_value = EXPOSURE_DEFAULT, + }, + .set = sd_setexposure, + .get = sd_getexposure, + }, + { +#define GAIN_IDX 10 + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 0, + .maximum = 28, + .step = 1, +#define GAIN_DEFAULT 0x00 + .default_value = GAIN_DEFAULT, + }, + .set = sd_setgain, + .get = sd_getgain, + }, + { +#define AUTOGAIN_IDX 11 + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Exposure", + .minimum = 0, + .maximum = 1, + .step = 1, +#define AUTO_EXPOSURE_DEFAULT 1 + .default_value = AUTO_EXPOSURE_DEFAULT, + }, + .set = sd_setautoexposure, + .get = sd_getautoexposure, + }, +}; + +static const struct v4l2_pix_format vga_mode[] = { + {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 240, + .sizeimage = 240 * 120, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0 | MODE_JPEG}, + {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0 | MODE_RAW}, + {160, 120, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE, + .bytesperline = 240, + .sizeimage = 240 * 120, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 480, + .sizeimage = 480 * 240 , + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1 | MODE_JPEG}, + {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 , + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1 | MODE_RAW}, + {320, 240, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE, + .bytesperline = 480, + .sizeimage = 480 * 240 , + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 960, + .sizeimage = 960 * 480, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 2 | MODE_JPEG}, + {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2 | MODE_RAW}, + {640, 480, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE, + .bytesperline = 960, + .sizeimage = 960 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2}, +}; + +static const struct v4l2_pix_format sxga_mode[] = { + {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 240, + .sizeimage = 240 * 120, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0 | MODE_JPEG}, + {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0 | MODE_RAW}, + {160, 120, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE, + .bytesperline = 240, + .sizeimage = 240 * 120, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 480, + .sizeimage = 480 * 240 , + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1 | MODE_JPEG}, + {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 , + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1 | MODE_RAW}, + {320, 240, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE, + .bytesperline = 480, + .sizeimage = 480 * 240 , + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 960, + .sizeimage = 960 * 480, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 2 | MODE_JPEG}, + {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2 | MODE_RAW}, + {640, 480, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE, + .bytesperline = 960, + .sizeimage = 960 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2}, + {1280, 1024, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 1280, + .sizeimage = (1280 * 1024) + 64, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 3 | MODE_RAW | MODE_SXGA}, +}; + +static const int hsv_red_x[] = { + 41, 44, 46, 48, 50, 52, 54, 56, + 58, 60, 62, 64, 66, 68, 70, 72, + 74, 76, 78, 80, 81, 83, 85, 87, + 88, 90, 92, 93, 95, 97, 98, 100, + 101, 102, 104, 105, 107, 108, 109, 110, + 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 123, 124, 125, 125, + 126, 127, 127, 128, 128, 129, 129, 129, + 130, 130, 130, 130, 131, 131, 131, 131, + 131, 131, 131, 131, 130, 130, 130, 130, + 129, 129, 129, 128, 128, 127, 127, 126, + 125, 125, 124, 123, 122, 122, 121, 120, + 119, 118, 117, 116, 115, 114, 112, 111, + 110, 109, 107, 106, 105, 103, 102, 101, + 99, 98, 96, 94, 93, 91, 90, 88, + 86, 84, 83, 81, 79, 77, 75, 74, + 72, 70, 68, 66, 64, 62, 60, 58, + 56, 54, 52, 49, 47, 45, 43, 41, + 39, 36, 34, 32, 30, 28, 25, 23, + 21, 19, 16, 14, 12, 9, 7, 5, + 3, 0, -1, -3, -6, -8, -10, -12, + -15, -17, -19, -22, -24, -26, -28, -30, + -33, -35, -37, -39, -41, -44, -46, -48, + -50, -52, -54, -56, -58, -60, -62, -64, + -66, -68, -70, -72, -74, -76, -78, -80, + -81, -83, -85, -87, -88, -90, -92, -93, + -95, -97, -98, -100, -101, -102, -104, -105, + -107, -108, -109, -110, -112, -113, -114, -115, + -116, -117, -118, -119, -120, -121, -122, -123, + -123, -124, -125, -125, -126, -127, -127, -128, + -128, -128, -128, -128, -128, -128, -128, -128, + -128, -128, -128, -128, -128, -128, -128, -128, + -128, -128, -128, -128, -128, -128, -128, -128, + -128, -127, -127, -126, -125, -125, -124, -123, + -122, -122, -121, -120, -119, -118, -117, -116, + -115, -114, -112, -111, -110, -109, -107, -106, + -105, -103, -102, -101, -99, -98, -96, -94, + -93, -91, -90, -88, -86, -84, -83, -81, + -79, -77, -75, -74, -72, -70, -68, -66, + -64, -62, -60, -58, -56, -54, -52, -49, + -47, -45, -43, -41, -39, -36, -34, -32, + -30, -28, -25, -23, -21, -19, -16, -14, + -12, -9, -7, -5, -3, 0, 1, 3, + 6, 8, 10, 12, 15, 17, 19, 22, + 24, 26, 28, 30, 33, 35, 37, 39, 41 +}; + +static const int hsv_red_y[] = { + 82, 80, 78, 76, 74, 73, 71, 69, + 67, 65, 63, 61, 58, 56, 54, 52, + 50, 48, 46, 44, 41, 39, 37, 35, + 32, 30, 28, 26, 23, 21, 19, 16, + 14, 12, 10, 7, 5, 3, 0, -1, + -3, -6, -8, -10, -13, -15, -17, -19, + -22, -24, -26, -29, -31, -33, -35, -38, + -40, -42, -44, -46, -48, -51, -53, -55, + -57, -59, -61, -63, -65, -67, -69, -71, + -73, -75, -77, -79, -81, -82, -84, -86, + -88, -89, -91, -93, -94, -96, -98, -99, + -101, -102, -104, -105, -106, -108, -109, -110, + -112, -113, -114, -115, -116, -117, -119, -120, + -120, -121, -122, -123, -124, -125, -126, -126, + -127, -128, -128, -128, -128, -128, -128, -128, + -128, -128, -128, -128, -128, -128, -128, -128, + -128, -128, -128, -128, -128, -128, -128, -128, + -128, -128, -128, -128, -128, -128, -128, -128, + -127, -127, -126, -125, -125, -124, -123, -122, + -121, -120, -119, -118, -117, -116, -115, -114, + -113, -111, -110, -109, -107, -106, -105, -103, + -102, -100, -99, -97, -96, -94, -92, -91, + -89, -87, -85, -84, -82, -80, -78, -76, + -74, -73, -71, -69, -67, -65, -63, -61, + -58, -56, -54, -52, -50, -48, -46, -44, + -41, -39, -37, -35, -32, -30, -28, -26, + -23, -21, -19, -16, -14, -12, -10, -7, + -5, -3, 0, 1, 3, 6, 8, 10, + 13, 15, 17, 19, 22, 24, 26, 29, + 31, 33, 35, 38, 40, 42, 44, 46, + 48, 51, 53, 55, 57, 59, 61, 63, + 65, 67, 69, 71, 73, 75, 77, 79, + 81, 82, 84, 86, 88, 89, 91, 93, + 94, 96, 98, 99, 101, 102, 104, 105, + 106, 108, 109, 110, 112, 113, 114, 115, + 116, 117, 119, 120, 120, 121, 122, 123, + 124, 125, 126, 126, 127, 128, 128, 129, + 129, 130, 130, 131, 131, 131, 131, 132, + 132, 132, 132, 132, 132, 132, 132, 132, + 132, 132, 132, 131, 131, 131, 130, 130, + 130, 129, 129, 128, 127, 127, 126, 125, + 125, 124, 123, 122, 121, 120, 119, 118, + 117, 116, 115, 114, 113, 111, 110, 109, + 107, 106, 105, 103, 102, 100, 99, 97, + 96, 94, 92, 91, 89, 87, 85, 84, 82 +}; + +static const int hsv_green_x[] = { + -124, -124, -125, -125, -125, -125, -125, -125, + -125, -126, -126, -125, -125, -125, -125, -125, + -125, -124, -124, -124, -123, -123, -122, -122, + -121, -121, -120, -120, -119, -118, -117, -117, + -116, -115, -114, -113, -112, -111, -110, -109, + -108, -107, -105, -104, -103, -102, -100, -99, + -98, -96, -95, -93, -92, -91, -89, -87, + -86, -84, -83, -81, -79, -77, -76, -74, + -72, -70, -69, -67, -65, -63, -61, -59, + -57, -55, -53, -51, -49, -47, -45, -43, + -41, -39, -37, -35, -33, -30, -28, -26, + -24, -22, -20, -18, -15, -13, -11, -9, + -7, -4, -2, 0, 1, 3, 6, 8, + 10, 12, 14, 17, 19, 21, 23, 25, + 27, 29, 32, 34, 36, 38, 40, 42, + 44, 46, 48, 50, 52, 54, 56, 58, + 60, 62, 64, 66, 68, 70, 71, 73, + 75, 77, 78, 80, 82, 83, 85, 87, + 88, 90, 91, 93, 94, 96, 97, 98, + 100, 101, 102, 104, 105, 106, 107, 108, + 109, 111, 112, 113, 113, 114, 115, 116, + 117, 118, 118, 119, 120, 120, 121, 122, + 122, 123, 123, 124, 124, 124, 125, 125, + 125, 125, 125, 125, 125, 126, 126, 125, + 125, 125, 125, 125, 125, 124, 124, 124, + 123, 123, 122, 122, 121, 121, 120, 120, + 119, 118, 117, 117, 116, 115, 114, 113, + 112, 111, 110, 109, 108, 107, 105, 104, + 103, 102, 100, 99, 98, 96, 95, 93, + 92, 91, 89, 87, 86, 84, 83, 81, + 79, 77, 76, 74, 72, 70, 69, 67, + 65, 63, 61, 59, 57, 55, 53, 51, + 49, 47, 45, 43, 41, 39, 37, 35, + 33, 30, 28, 26, 24, 22, 20, 18, + 15, 13, 11, 9, 7, 4, 2, 0, + -1, -3, -6, -8, -10, -12, -14, -17, + -19, -21, -23, -25, -27, -29, -32, -34, + -36, -38, -40, -42, -44, -46, -48, -50, + -52, -54, -56, -58, -60, -62, -64, -66, + -68, -70, -71, -73, -75, -77, -78, -80, + -82, -83, -85, -87, -88, -90, -91, -93, + -94, -96, -97, -98, -100, -101, -102, -104, + -105, -106, -107, -108, -109, -111, -112, -113, + -113, -114, -115, -116, -117, -118, -118, -119, + -120, -120, -121, -122, -122, -123, -123, -124, -124 +}; + +static const int hsv_green_y[] = { + -100, -99, -98, -97, -95, -94, -93, -91, + -90, -89, -87, -86, -84, -83, -81, -80, + -78, -76, -75, -73, -71, -70, -68, -66, + -64, -63, -61, -59, -57, -55, -53, -51, + -49, -48, -46, -44, -42, -40, -38, -36, + -34, -32, -30, -27, -25, -23, -21, -19, + -17, -15, -13, -11, -9, -7, -4, -2, + 0, 1, 3, 5, 7, 9, 11, 14, + 16, 18, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, + 48, 50, 52, 54, 56, 58, 59, 61, + 63, 65, 67, 68, 70, 72, 74, 75, + 77, 78, 80, 82, 83, 85, 86, 88, + 89, 90, 92, 93, 95, 96, 97, 98, + 100, 101, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 111, 112, 112, 113, 114, + 115, 115, 116, 116, 117, 117, 118, 118, + 119, 119, 119, 120, 120, 120, 120, 120, + 121, 121, 121, 121, 121, 121, 120, 120, + 120, 120, 120, 119, 119, 119, 118, 118, + 117, 117, 116, 116, 115, 114, 114, 113, + 112, 111, 111, 110, 109, 108, 107, 106, + 105, 104, 103, 102, 100, 99, 98, 97, + 95, 94, 93, 91, 90, 89, 87, 86, + 84, 83, 81, 80, 78, 76, 75, 73, + 71, 70, 68, 66, 64, 63, 61, 59, + 57, 55, 53, 51, 49, 48, 46, 44, + 42, 40, 38, 36, 34, 32, 30, 27, + 25, 23, 21, 19, 17, 15, 13, 11, + 9, 7, 4, 2, 0, -1, -3, -5, + -7, -9, -11, -14, -16, -18, -20, -22, + -24, -26, -28, -30, -32, -34, -36, -38, + -40, -42, -44, -46, -48, -50, -52, -54, + -56, -58, -59, -61, -63, -65, -67, -68, + -70, -72, -74, -75, -77, -78, -80, -82, + -83, -85, -86, -88, -89, -90, -92, -93, + -95, -96, -97, -98, -100, -101, -102, -103, + -104, -105, -106, -107, -108, -109, -110, -111, + -112, -112, -113, -114, -115, -115, -116, -116, + -117, -117, -118, -118, -119, -119, -119, -120, + -120, -120, -120, -120, -121, -121, -121, -121, + -121, -121, -120, -120, -120, -120, -120, -119, + -119, -119, -118, -118, -117, -117, -116, -116, + -115, -114, -114, -113, -112, -111, -111, -110, + -109, -108, -107, -106, -105, -104, -103, -102, -100 +}; + +static const int hsv_blue_x[] = { + 112, 113, 114, 114, 115, 116, 117, 117, + 118, 118, 119, 119, 120, 120, 120, 121, + 121, 121, 122, 122, 122, 122, 122, 122, + 122, 122, 122, 122, 122, 122, 121, 121, + 121, 120, 120, 120, 119, 119, 118, 118, + 117, 116, 116, 115, 114, 113, 113, 112, + 111, 110, 109, 108, 107, 106, 105, 104, + 103, 102, 100, 99, 98, 97, 95, 94, + 93, 91, 90, 88, 87, 85, 84, 82, + 80, 79, 77, 76, 74, 72, 70, 69, + 67, 65, 63, 61, 60, 58, 56, 54, + 52, 50, 48, 46, 44, 42, 40, 38, + 36, 34, 32, 30, 28, 26, 24, 22, + 19, 17, 15, 13, 11, 9, 7, 5, + 2, 0, -1, -3, -5, -7, -9, -12, + -14, -16, -18, -20, -22, -24, -26, -28, + -31, -33, -35, -37, -39, -41, -43, -45, + -47, -49, -51, -53, -54, -56, -58, -60, + -62, -64, -66, -67, -69, -71, -73, -74, + -76, -78, -79, -81, -83, -84, -86, -87, + -89, -90, -92, -93, -94, -96, -97, -98, + -99, -101, -102, -103, -104, -105, -106, -107, + -108, -109, -110, -111, -112, -113, -114, -114, + -115, -116, -117, -117, -118, -118, -119, -119, + -120, -120, -120, -121, -121, -121, -122, -122, + -122, -122, -122, -122, -122, -122, -122, -122, + -122, -122, -121, -121, -121, -120, -120, -120, + -119, -119, -118, -118, -117, -116, -116, -115, + -114, -113, -113, -112, -111, -110, -109, -108, + -107, -106, -105, -104, -103, -102, -100, -99, + -98, -97, -95, -94, -93, -91, -90, -88, + -87, -85, -84, -82, -80, -79, -77, -76, + -74, -72, -70, -69, -67, -65, -63, -61, + -60, -58, -56, -54, -52, -50, -48, -46, + -44, -42, -40, -38, -36, -34, -32, -30, + -28, -26, -24, -22, -19, -17, -15, -13, + -11, -9, -7, -5, -2, 0, 1, 3, + 5, 7, 9, 12, 14, 16, 18, 20, + 22, 24, 26, 28, 31, 33, 35, 37, + 39, 41, 43, 45, 47, 49, 51, 53, + 54, 56, 58, 60, 62, 64, 66, 67, + 69, 71, 73, 74, 76, 78, 79, 81, + 83, 84, 86, 87, 89, 90, 92, 93, + 94, 96, 97, 98, 99, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112 +}; + +static const int hsv_blue_y[] = { + -11, -13, -15, -17, -19, -21, -23, -25, + -27, -29, -31, -33, -35, -37, -39, -41, + -43, -45, -46, -48, -50, -52, -54, -55, + -57, -59, -61, -62, -64, -66, -67, -69, + -71, -72, -74, -75, -77, -78, -80, -81, + -83, -84, -86, -87, -88, -90, -91, -92, + -93, -95, -96, -97, -98, -99, -100, -101, + -102, -103, -104, -105, -106, -106, -107, -108, + -109, -109, -110, -111, -111, -112, -112, -113, + -113, -114, -114, -114, -115, -115, -115, -115, + -116, -116, -116, -116, -116, -116, -116, -116, + -116, -115, -115, -115, -115, -114, -114, -114, + -113, -113, -112, -112, -111, -111, -110, -110, + -109, -108, -108, -107, -106, -105, -104, -103, + -102, -101, -100, -99, -98, -97, -96, -95, + -94, -93, -91, -90, -89, -88, -86, -85, + -84, -82, -81, -79, -78, -76, -75, -73, + -71, -70, -68, -67, -65, -63, -62, -60, + -58, -56, -55, -53, -51, -49, -47, -45, + -44, -42, -40, -38, -36, -34, -32, -30, + -28, -26, -24, -22, -20, -18, -16, -14, + -12, -10, -8, -6, -4, -2, 0, 1, + 3, 5, 7, 9, 11, 13, 15, 17, + 19, 21, 23, 25, 27, 29, 31, 33, + 35, 37, 39, 41, 43, 45, 46, 48, + 50, 52, 54, 55, 57, 59, 61, 62, + 64, 66, 67, 69, 71, 72, 74, 75, + 77, 78, 80, 81, 83, 84, 86, 87, + 88, 90, 91, 92, 93, 95, 96, 97, + 98, 99, 100, 101, 102, 103, 104, 105, + 106, 106, 107, 108, 109, 109, 110, 111, + 111, 112, 112, 113, 113, 114, 114, 114, + 115, 115, 115, 115, 116, 116, 116, 116, + 116, 116, 116, 116, 116, 115, 115, 115, + 115, 114, 114, 114, 113, 113, 112, 112, + 111, 111, 110, 110, 109, 108, 108, 107, + 106, 105, 104, 103, 102, 101, 100, 99, + 98, 97, 96, 95, 94, 93, 91, 90, + 89, 88, 86, 85, 84, 82, 81, 79, + 78, 76, 75, 73, 71, 70, 68, 67, + 65, 63, 62, 60, 58, 56, 55, 53, + 51, 49, 47, 45, 44, 42, 40, 38, + 36, 34, 32, 30, 28, 26, 24, 22, + 20, 18, 16, 14, 12, 10, 8, 6, + 4, 2, 0, -1, -3, -5, -7, -9, -11 +}; + +static u16 i2c_ident[] = { + V4L2_IDENT_OV9650, + V4L2_IDENT_OV9655, + V4L2_IDENT_SOI968, + V4L2_IDENT_OV7660, + V4L2_IDENT_OV7670, + V4L2_IDENT_MT9V011, + V4L2_IDENT_MT9V111, + V4L2_IDENT_MT9V112, + V4L2_IDENT_MT9M001C12ST, + V4L2_IDENT_MT9M111, + V4L2_IDENT_HV7131R, +}; + +static u16 bridge_init[][2] = { + {0x1000, 0x78}, {0x1001, 0x40}, {0x1002, 0x1c}, + {0x1020, 0x80}, {0x1061, 0x01}, {0x1067, 0x40}, + {0x1068, 0x30}, {0x1069, 0x20}, {0x106a, 0x10}, + {0x106b, 0x08}, {0x1188, 0x87}, {0x11a1, 0x00}, + {0x11a2, 0x00}, {0x11a3, 0x6a}, {0x11a4, 0x50}, + {0x11ab, 0x00}, {0x11ac, 0x00}, {0x11ad, 0x50}, + {0x11ae, 0x3c}, {0x118a, 0x04}, {0x0395, 0x04}, + {0x11b8, 0x3a}, {0x118b, 0x0e}, {0x10f7, 0x05}, + {0x10f8, 0x14}, {0x10fa, 0xff}, {0x10f9, 0x00}, + {0x11ba, 0x0a}, {0x11a5, 0x2d}, {0x11a6, 0x2d}, + {0x11a7, 0x3a}, {0x11a8, 0x05}, {0x11a9, 0x04}, + {0x11aa, 0x3f}, {0x11af, 0x28}, {0x11b0, 0xd8}, + {0x11b1, 0x14}, {0x11b2, 0xec}, {0x11b3, 0x32}, + {0x11b4, 0xdd}, {0x11b5, 0x32}, {0x11b6, 0xdd}, + {0x10e0, 0x2c}, {0x11bc, 0x40}, {0x11bd, 0x01}, + {0x11be, 0xf0}, {0x11bf, 0x00}, {0x118c, 0x1f}, + {0x118d, 0x1f}, {0x118e, 0x1f}, {0x118f, 0x1f}, + {0x1180, 0x01}, {0x1181, 0x00}, {0x1182, 0x01}, + {0x1183, 0x00}, {0x1184, 0x50}, {0x1185, 0x80} +}; + +/* Gain = (bit[3:0] / 16 + 1) * (bit[4] + 1) * (bit[5] + 1) * (bit[6] + 1) */ +static u8 ov_gain[] = { + 0x00 /* 1x */, 0x04 /* 1.25x */, 0x08 /* 1.5x */, 0x0c /* 1.75x */, + 0x10 /* 2x */, 0x12 /* 2.25x */, 0x14 /* 2.5x */, 0x16 /* 2.75x */, + 0x18 /* 3x */, 0x1a /* 3.25x */, 0x1c /* 3.5x */, 0x1e /* 3.75x */, + 0x30 /* 4x */, 0x31 /* 4.25x */, 0x32 /* 4.5x */, 0x33 /* 4.75x */, + 0x34 /* 5x */, 0x35 /* 5.25x */, 0x36 /* 5.5x */, 0x37 /* 5.75x */, + 0x38 /* 6x */, 0x39 /* 6.25x */, 0x3a /* 6.5x */, 0x3b /* 6.75x */, + 0x3c /* 7x */, 0x3d /* 7.25x */, 0x3e /* 7.5x */, 0x3f /* 7.75x */, + 0x70 /* 8x */ +}; + +/* Gain = (bit[8] + 1) * (bit[7] + 1) * (bit[6:0] * 0.03125) */ +static u16 micron1_gain[] = { + /* 1x 1.25x 1.5x 1.75x */ + 0x0020, 0x0028, 0x0030, 0x0038, + /* 2x 2.25x 2.5x 2.75x */ + 0x00a0, 0x00a4, 0x00a8, 0x00ac, + /* 3x 3.25x 3.5x 3.75x */ + 0x00b0, 0x00b4, 0x00b8, 0x00bc, + /* 4x 4.25x 4.5x 4.75x */ + 0x00c0, 0x00c4, 0x00c8, 0x00cc, + /* 5x 5.25x 5.5x 5.75x */ + 0x00d0, 0x00d4, 0x00d8, 0x00dc, + /* 6x 6.25x 6.5x 6.75x */ + 0x00e0, 0x00e4, 0x00e8, 0x00ec, + /* 7x 7.25x 7.5x 7.75x */ + 0x00f0, 0x00f4, 0x00f8, 0x00fc, + /* 8x */ + 0x01c0 +}; + +/* mt9m001 sensor uses a different gain formula then other micron sensors */ +/* Gain = (bit[6] + 1) * (bit[5-0] * 0.125) */ +static u16 micron2_gain[] = { + /* 1x 1.25x 1.5x 1.75x */ + 0x0008, 0x000a, 0x000c, 0x000e, + /* 2x 2.25x 2.5x 2.75x */ + 0x0010, 0x0012, 0x0014, 0x0016, + /* 3x 3.25x 3.5x 3.75x */ + 0x0018, 0x001a, 0x001c, 0x001e, + /* 4x 4.25x 4.5x 4.75x */ + 0x0020, 0x0051, 0x0052, 0x0053, + /* 5x 5.25x 5.5x 5.75x */ + 0x0054, 0x0055, 0x0056, 0x0057, + /* 6x 6.25x 6.5x 6.75x */ + 0x0058, 0x0059, 0x005a, 0x005b, + /* 7x 7.25x 7.5x 7.75x */ + 0x005c, 0x005d, 0x005e, 0x005f, + /* 8x */ + 0x0060 +}; + +/* Gain = .5 + bit[7:0] / 16 */ +static u8 hv7131r_gain[] = { + 0x08 /* 1x */, 0x0c /* 1.25x */, 0x10 /* 1.5x */, 0x14 /* 1.75x */, + 0x18 /* 2x */, 0x1c /* 2.25x */, 0x20 /* 2.5x */, 0x24 /* 2.75x */, + 0x28 /* 3x */, 0x2c /* 3.25x */, 0x30 /* 3.5x */, 0x34 /* 3.75x */, + 0x38 /* 4x */, 0x3c /* 4.25x */, 0x40 /* 4.5x */, 0x44 /* 4.75x */, + 0x48 /* 5x */, 0x4c /* 5.25x */, 0x50 /* 5.5x */, 0x54 /* 5.75x */, + 0x58 /* 6x */, 0x5c /* 6.25x */, 0x60 /* 6.5x */, 0x64 /* 6.75x */, + 0x68 /* 7x */, 0x6c /* 7.25x */, 0x70 /* 7.5x */, 0x74 /* 7.75x */, + 0x78 /* 8x */ +}; + +static u8 soi968_init[][2] = { + {0x12, 0x80}, {0x0c, 0x00}, {0x0f, 0x1f}, + {0x11, 0x80}, {0x38, 0x52}, {0x1e, 0x00}, + {0x33, 0x08}, {0x35, 0x8c}, {0x36, 0x0c}, + {0x37, 0x04}, {0x45, 0x04}, {0x47, 0xff}, + {0x3e, 0x00}, {0x3f, 0x00}, {0x3b, 0x20}, + {0x3a, 0x96}, {0x3d, 0x0a}, {0x14, 0x8e}, + {0x13, 0x8a}, {0x12, 0x40}, {0x17, 0x13}, + {0x18, 0x63}, {0x19, 0x01}, {0x1a, 0x79}, + {0x32, 0x24}, {0x03, 0x00}, {0x11, 0x40}, + {0x2a, 0x10}, {0x2b, 0xe0}, {0x10, 0x32}, + {0x00, 0x00}, {0x01, 0x80}, {0x02, 0x80}, +}; + +static u8 ov7660_init[][2] = { + {0x0e, 0x80}, {0x0d, 0x08}, {0x0f, 0xc3}, + {0x04, 0xc3}, {0x10, 0x40}, {0x11, 0x40}, + {0x12, 0x05}, {0x13, 0xba}, {0x14, 0x2a}, + {0x37, 0x0f}, {0x38, 0x02}, {0x39, 0x43}, + {0x3a, 0x00}, {0x69, 0x90}, {0x2d, 0xf6}, + {0x2e, 0x0b}, {0x01, 0x78}, {0x02, 0x50}, +}; + +static u8 ov7670_init[][2] = { + {0x12, 0x80}, {0x11, 0x80}, {0x3a, 0x04}, {0x12, 0x01}, + {0x32, 0xb6}, {0x03, 0x0a}, {0x0c, 0x00}, {0x3e, 0x00}, + {0x70, 0x3a}, {0x71, 0x35}, {0x72, 0x11}, {0x73, 0xf0}, + {0xa2, 0x02}, {0x13, 0xe0}, {0x00, 0x00}, {0x10, 0x00}, + {0x0d, 0x40}, {0x14, 0x28}, {0xa5, 0x05}, {0xab, 0x07}, + {0x24, 0x95}, {0x25, 0x33}, {0x26, 0xe3}, {0x9f, 0x75}, + {0xa0, 0x65}, {0xa1, 0x0b}, {0xa6, 0xd8}, {0xa7, 0xd8}, + {0xa8, 0xf0}, {0xa9, 0x90}, {0xaa, 0x94}, {0x13, 0xe5}, + {0x0e, 0x61}, {0x0f, 0x4b}, {0x16, 0x02}, {0x1e, 0x27}, + {0x21, 0x02}, {0x22, 0x91}, {0x29, 0x07}, {0x33, 0x0b}, + {0x35, 0x0b}, {0x37, 0x1d}, {0x38, 0x71}, {0x39, 0x2a}, + {0x3c, 0x78}, {0x4d, 0x40}, {0x4e, 0x20}, {0x69, 0x00}, + {0x74, 0x19}, {0x8d, 0x4f}, {0x8e, 0x00}, {0x8f, 0x00}, + {0x90, 0x00}, {0x91, 0x00}, {0x96, 0x00}, {0x9a, 0x80}, + {0xb0, 0x84}, {0xb1, 0x0c}, {0xb2, 0x0e}, {0xb3, 0x82}, + {0xb8, 0x0a}, {0x43, 0x0a}, {0x44, 0xf0}, {0x45, 0x20}, + {0x46, 0x7d}, {0x47, 0x29}, {0x48, 0x4a}, {0x59, 0x8c}, + {0x5a, 0xa5}, {0x5b, 0xde}, {0x5c, 0x96}, {0x5d, 0x66}, + {0x5e, 0x10}, {0x6c, 0x0a}, {0x6d, 0x55}, {0x6e, 0x11}, + {0x6f, 0x9e}, {0x6a, 0x40}, {0x01, 0x40}, {0x02, 0x40}, + {0x13, 0xe7}, {0x4f, 0x6e}, {0x50, 0x70}, {0x51, 0x02}, + {0x52, 0x1d}, {0x53, 0x56}, {0x54, 0x73}, {0x55, 0x0a}, + {0x56, 0x55}, {0x57, 0x80}, {0x58, 0x9e}, {0x41, 0x08}, + {0x3f, 0x02}, {0x75, 0x03}, {0x76, 0x63}, {0x4c, 0x04}, + {0x77, 0x06}, {0x3d, 0x02}, {0x4b, 0x09}, {0xc9, 0x30}, + {0x41, 0x08}, {0x56, 0x48}, {0x34, 0x11}, {0xa4, 0x88}, + {0x96, 0x00}, {0x97, 0x30}, {0x98, 0x20}, {0x99, 0x30}, + {0x9a, 0x84}, {0x9b, 0x29}, {0x9c, 0x03}, {0x9d, 0x99}, + {0x9e, 0x7f}, {0x78, 0x04}, {0x79, 0x01}, {0xc8, 0xf0}, + {0x79, 0x0f}, {0xc8, 0x00}, {0x79, 0x10}, {0xc8, 0x7e}, + {0x79, 0x0a}, {0xc8, 0x80}, {0x79, 0x0b}, {0xc8, 0x01}, + {0x79, 0x0c}, {0xc8, 0x0f}, {0x79, 0x0d}, {0xc8, 0x20}, + {0x79, 0x09}, {0xc8, 0x80}, {0x79, 0x02}, {0xc8, 0xc0}, + {0x79, 0x03}, {0xc8, 0x40}, {0x79, 0x05}, {0xc8, 0x30}, + {0x79, 0x26}, {0x62, 0x20}, {0x63, 0x00}, {0x64, 0x06}, + {0x65, 0x00}, {0x66, 0x05}, {0x94, 0x05}, {0x95, 0x0a}, + {0x17, 0x13}, {0x18, 0x01}, {0x19, 0x02}, {0x1a, 0x7a}, + {0x46, 0x59}, {0x47, 0x30}, {0x58, 0x9a}, {0x59, 0x84}, + {0x5a, 0x91}, {0x5b, 0x57}, {0x5c, 0x75}, {0x5d, 0x6d}, + {0x5e, 0x13}, {0x64, 0x07}, {0x94, 0x07}, {0x95, 0x0d}, + {0xa6, 0xdf}, {0xa7, 0xdf}, {0x48, 0x4d}, {0x51, 0x00}, + {0x6b, 0x0a}, {0x11, 0x80}, {0x2a, 0x00}, {0x2b, 0x00}, + {0x92, 0x00}, {0x93, 0x00}, {0x55, 0x0a}, {0x56, 0x60}, + {0x4f, 0x6e}, {0x50, 0x70}, {0x51, 0x00}, {0x52, 0x1d}, + {0x53, 0x56}, {0x54, 0x73}, {0x58, 0x9a}, {0x4f, 0x6e}, + {0x50, 0x70}, {0x51, 0x00}, {0x52, 0x1d}, {0x53, 0x56}, + {0x54, 0x73}, {0x58, 0x9a}, {0x3f, 0x01}, {0x7b, 0x03}, + {0x7c, 0x09}, {0x7d, 0x16}, {0x7e, 0x38}, {0x7f, 0x47}, + {0x80, 0x53}, {0x81, 0x5e}, {0x82, 0x6a}, {0x83, 0x74}, + {0x84, 0x80}, {0x85, 0x8c}, {0x86, 0x9b}, {0x87, 0xb2}, + {0x88, 0xcc}, {0x89, 0xe5}, {0x7a, 0x24}, {0x3b, 0x00}, + {0x9f, 0x76}, {0xa0, 0x65}, {0x13, 0xe2}, {0x6b, 0x0a}, + {0x11, 0x80}, {0x2a, 0x00}, {0x2b, 0x00}, {0x92, 0x00}, + {0x93, 0x00}, +}; + +static u8 ov9650_init[][2] = { + {0x12, 0x80}, {0x00, 0x00}, {0x01, 0x78}, + {0x02, 0x78}, {0x03, 0x36}, {0x04, 0x03}, + {0x05, 0x00}, {0x06, 0x00}, {0x08, 0x00}, + {0x09, 0x01}, {0x0c, 0x00}, {0x0d, 0x00}, + {0x0e, 0xa0}, {0x0f, 0x52}, {0x10, 0x7c}, + {0x11, 0x80}, {0x12, 0x45}, {0x13, 0xc2}, + {0x14, 0x2e}, {0x15, 0x00}, {0x16, 0x07}, + {0x17, 0x24}, {0x18, 0xc5}, {0x19, 0x00}, + {0x1a, 0x3c}, {0x1b, 0x00}, {0x1e, 0x04}, + {0x1f, 0x00}, {0x24, 0x78}, {0x25, 0x68}, + {0x26, 0xd4}, {0x27, 0x80}, {0x28, 0x80}, + {0x29, 0x30}, {0x2a, 0x00}, {0x2b, 0x00}, + {0x2c, 0x80}, {0x2d, 0x00}, {0x2e, 0x00}, + {0x2f, 0x00}, {0x30, 0x08}, {0x31, 0x30}, + {0x32, 0x84}, {0x33, 0xe2}, {0x34, 0xbf}, + {0x35, 0x81}, {0x36, 0xf9}, {0x37, 0x00}, + {0x38, 0x93}, {0x39, 0x50}, {0x3a, 0x01}, + {0x3b, 0x01}, {0x3c, 0x73}, {0x3d, 0x19}, + {0x3e, 0x0b}, {0x3f, 0x80}, {0x40, 0xc1}, + {0x41, 0x00}, {0x42, 0x08}, {0x67, 0x80}, + {0x68, 0x80}, {0x69, 0x40}, {0x6a, 0x00}, + {0x6b, 0x0a}, {0x8b, 0x06}, {0x8c, 0x20}, + {0x8d, 0x00}, {0x8e, 0x00}, {0x8f, 0xdf}, + {0x92, 0x00}, {0x93, 0x00}, {0x94, 0x88}, + {0x95, 0x88}, {0x96, 0x04}, {0xa1, 0x00}, + {0xa5, 0x80}, {0xa8, 0x80}, {0xa9, 0xb8}, + {0xaa, 0x92}, {0xab, 0x0a}, +}; + +static u8 ov9655_init[][2] = { + {0x12, 0x80}, {0x12, 0x01}, {0x0d, 0x00}, {0x0e, 0x61}, + {0x11, 0x80}, {0x13, 0xba}, {0x14, 0x2e}, {0x16, 0x24}, + {0x1e, 0x04}, {0x1e, 0x04}, {0x1e, 0x04}, {0x27, 0x08}, + {0x28, 0x08}, {0x29, 0x15}, {0x2c, 0x08}, {0x32, 0xbf}, + {0x34, 0x3d}, {0x35, 0x00}, {0x36, 0xf8}, {0x38, 0x12}, + {0x39, 0x57}, {0x3a, 0x00}, {0x3b, 0xcc}, {0x3c, 0x0c}, + {0x3d, 0x19}, {0x3e, 0x0c}, {0x3f, 0x01}, {0x41, 0x40}, + {0x42, 0x80}, {0x45, 0x46}, {0x46, 0x62}, {0x47, 0x2a}, + {0x48, 0x3c}, {0x4a, 0xf0}, {0x4b, 0xdc}, {0x4c, 0xdc}, + {0x4d, 0xdc}, {0x4e, 0xdc}, {0x69, 0x02}, {0x6c, 0x04}, + {0x6f, 0x9e}, {0x70, 0x05}, {0x71, 0x78}, {0x77, 0x02}, + {0x8a, 0x23}, {0x8c, 0x0d}, {0x90, 0x7e}, {0x91, 0x7c}, + {0x9f, 0x6e}, {0xa0, 0x6e}, {0xa5, 0x68}, {0xa6, 0x60}, + {0xa8, 0xc1}, {0xa9, 0xfa}, {0xaa, 0x92}, {0xab, 0x04}, + {0xac, 0x80}, {0xad, 0x80}, {0xae, 0x80}, {0xaf, 0x80}, + {0xb2, 0xf2}, {0xb3, 0x20}, {0xb5, 0x00}, {0xb6, 0xaf}, + {0xbb, 0xae}, {0xbc, 0x44}, {0xbd, 0x44}, {0xbe, 0x3b}, + {0xbf, 0x3a}, {0xc0, 0xe2}, {0xc1, 0xc8}, {0xc2, 0x01}, + {0xc4, 0x00}, {0xc6, 0x85}, {0xc7, 0x81}, {0xc9, 0xe0}, + {0xca, 0xe8}, {0xcc, 0xd8}, {0xcd, 0x93}, {0x12, 0x61}, + {0x36, 0xfa}, {0x8c, 0x8d}, {0xc0, 0xaa}, {0x69, 0x0a}, + {0x03, 0x12}, {0x17, 0x14}, {0x18, 0x00}, {0x19, 0x01}, + {0x1a, 0x3d}, {0x32, 0xbf}, {0x11, 0x80}, {0x2a, 0x10}, + {0x2b, 0x0a}, {0x92, 0x00}, {0x93, 0x00}, {0x1e, 0x04}, + {0x1e, 0x04}, {0x10, 0x7c}, {0x04, 0x03}, {0xa1, 0x00}, + {0x2d, 0x00}, {0x2e, 0x00}, {0x00, 0x00}, {0x01, 0x80}, + {0x02, 0x80}, {0x12, 0x61}, {0x36, 0xfa}, {0x8c, 0x8d}, + {0xc0, 0xaa}, {0x69, 0x0a}, {0x03, 0x12}, {0x17, 0x14}, + {0x18, 0x00}, {0x19, 0x01}, {0x1a, 0x3d}, {0x32, 0xbf}, + {0x11, 0x80}, {0x2a, 0x10}, {0x2b, 0x0a}, {0x92, 0x00}, + {0x93, 0x00}, {0x04, 0x01}, {0x10, 0x1f}, {0xa1, 0x00}, + {0x00, 0x0a}, {0xa1, 0x00}, {0x10, 0x5d}, {0x04, 0x03}, + {0x00, 0x01}, {0xa1, 0x00}, {0x10, 0x7c}, {0x04, 0x03}, + {0x00, 0x03}, {0x00, 0x0a}, {0x00, 0x10}, {0x00, 0x13}, +}; + +static u16 mt9v112_init[][2] = { + {0xf0, 0x0000}, {0x0d, 0x0021}, {0x0d, 0x0020}, + {0x34, 0xc019}, {0x0a, 0x0011}, {0x0b, 0x000b}, + {0x20, 0x0703}, {0x35, 0x2022}, {0xf0, 0x0001}, + {0x05, 0x0000}, {0x06, 0x340c}, {0x3b, 0x042a}, + {0x3c, 0x0400}, {0xf0, 0x0002}, {0x2e, 0x0c58}, + {0x5b, 0x0001}, {0xc8, 0x9f0b}, {0xf0, 0x0001}, + {0x9b, 0x5300}, {0xf0, 0x0000}, {0x2b, 0x0020}, + {0x2c, 0x002a}, {0x2d, 0x0032}, {0x2e, 0x0020}, + {0x09, 0x01dc}, {0x01, 0x000c}, {0x02, 0x0020}, + {0x03, 0x01e0}, {0x04, 0x0280}, {0x06, 0x000c}, + {0x05, 0x0098}, {0x20, 0x0703}, {0x09, 0x01f2}, + {0x2b, 0x00a0}, {0x2c, 0x00a0}, {0x2d, 0x00a0}, + {0x2e, 0x00a0}, {0x01, 0x000c}, {0x02, 0x0020}, + {0x03, 0x01e0}, {0x04, 0x0280}, {0x06, 0x000c}, + {0x05, 0x0098}, {0x09, 0x01c1}, {0x2b, 0x00ae}, + {0x2c, 0x00ae}, {0x2d, 0x00ae}, {0x2e, 0x00ae}, +}; + +static u16 mt9v111_init[][2] = { + {0x01, 0x0004}, {0x0d, 0x0001}, {0x0d, 0x0000}, + {0x01, 0x0001}, {0x02, 0x0016}, {0x03, 0x01e1}, + {0x04, 0x0281}, {0x05, 0x0004}, {0x07, 0x3002}, + {0x21, 0x0000}, {0x25, 0x4024}, {0x26, 0xff03}, + {0x27, 0xff10}, {0x2b, 0x7828}, {0x2c, 0xb43c}, + {0x2d, 0xf0a0}, {0x2e, 0x0c64}, {0x2f, 0x0064}, + {0x67, 0x4010}, {0x06, 0x301e}, {0x08, 0x0480}, + {0x01, 0x0004}, {0x02, 0x0016}, {0x03, 0x01e6}, + {0x04, 0x0286}, {0x05, 0x0004}, {0x06, 0x0000}, + {0x07, 0x3002}, {0x08, 0x0008}, {0x0c, 0x0000}, + {0x0d, 0x0000}, {0x0e, 0x0000}, {0x0f, 0x0000}, + {0x10, 0x0000}, {0x11, 0x0000}, {0x12, 0x00b0}, + {0x13, 0x007c}, {0x14, 0x0000}, {0x15, 0x0000}, + {0x16, 0x0000}, {0x17, 0x0000}, {0x18, 0x0000}, + {0x19, 0x0000}, {0x1a, 0x0000}, {0x1b, 0x0000}, + {0x1c, 0x0000}, {0x1d, 0x0000}, {0x30, 0x0000}, + {0x30, 0x0005}, {0x31, 0x0000}, {0x02, 0x0016}, + {0x03, 0x01e1}, {0x04, 0x0281}, {0x05, 0x0004}, + {0x06, 0x0000}, {0x07, 0x3002}, {0x06, 0x002d}, + {0x05, 0x0004}, {0x09, 0x0064}, {0x2b, 0x00a0}, + {0x2c, 0x00a0}, {0x2d, 0x00a0}, {0x2e, 0x00a0}, + {0x02, 0x0016}, {0x03, 0x01e1}, {0x04, 0x0281}, + {0x05, 0x0004}, {0x06, 0x002d}, {0x07, 0x3002}, + {0x0e, 0x0008}, {0x06, 0x002d}, {0x05, 0x0004}, +}; + +static u16 mt9v011_init[][2] = { + {0x07, 0x0002}, {0x0d, 0x0001}, {0x0d, 0x0000}, + {0x01, 0x0008}, {0x02, 0x0016}, {0x03, 0x01e1}, + {0x04, 0x0281}, {0x05, 0x0083}, {0x06, 0x0006}, + {0x0d, 0x0002}, {0x0a, 0x0000}, {0x0b, 0x0000}, + {0x0c, 0x0000}, {0x0d, 0x0000}, {0x0e, 0x0000}, + {0x0f, 0x0000}, {0x10, 0x0000}, {0x11, 0x0000}, + {0x12, 0x0000}, {0x13, 0x0000}, {0x14, 0x0000}, + {0x15, 0x0000}, {0x16, 0x0000}, {0x17, 0x0000}, + {0x18, 0x0000}, {0x19, 0x0000}, {0x1a, 0x0000}, + {0x1b, 0x0000}, {0x1c, 0x0000}, {0x1d, 0x0000}, + {0x32, 0x0000}, {0x20, 0x1101}, {0x21, 0x0000}, + {0x22, 0x0000}, {0x23, 0x0000}, {0x24, 0x0000}, + {0x25, 0x0000}, {0x26, 0x0000}, {0x27, 0x0024}, + {0x2f, 0xf7b0}, {0x30, 0x0005}, {0x31, 0x0000}, + {0x32, 0x0000}, {0x33, 0x0000}, {0x34, 0x0100}, + {0x3d, 0x068f}, {0x40, 0x01e0}, {0x41, 0x00d1}, + {0x44, 0x0082}, {0x5a, 0x0000}, {0x5b, 0x0000}, + {0x5c, 0x0000}, {0x5d, 0x0000}, {0x5e, 0x0000}, + {0x5f, 0xa31d}, {0x62, 0x0611}, {0x0a, 0x0000}, + {0x06, 0x0029}, {0x05, 0x0009}, {0x20, 0x1101}, + {0x20, 0x1101}, {0x09, 0x0064}, {0x07, 0x0003}, + {0x2b, 0x0033}, {0x2c, 0x00a0}, {0x2d, 0x00a0}, + {0x2e, 0x0033}, {0x07, 0x0002}, {0x06, 0x0000}, + {0x06, 0x0029}, {0x05, 0x0009}, +}; + +static u16 mt9m001_init[][2] = { + {0x0d, 0x0001}, {0x0d, 0x0000}, {0x01, 0x000e}, + {0x02, 0x0014}, {0x03, 0x03c1}, {0x04, 0x0501}, + {0x05, 0x0083}, {0x06, 0x0006}, {0x0d, 0x0002}, + {0x0a, 0x0000}, {0x0c, 0x0000}, {0x11, 0x0000}, + {0x1e, 0x8000}, {0x5f, 0x8904}, {0x60, 0x0000}, + {0x61, 0x0000}, {0x62, 0x0498}, {0x63, 0x0000}, + {0x64, 0x0000}, {0x20, 0x111d}, {0x06, 0x00f2}, + {0x05, 0x0013}, {0x09, 0x10f2}, {0x07, 0x0003}, + {0x2b, 0x002a}, {0x2d, 0x002a}, {0x2c, 0x002a}, + {0x2e, 0x0029}, {0x07, 0x0002}, +}; + +static u16 mt9m111_init[][2] = { + {0xf0, 0x0000}, {0x0d, 0x0008}, {0x0d, 0x0009}, + {0x0d, 0x0008}, {0xf0, 0x0001}, {0x3a, 0x4300}, + {0x9b, 0x4300}, {0xa1, 0x0280}, {0xa4, 0x0200}, + {0x06, 0x308e}, {0xf0, 0x0000}, +}; + +static u8 hv7131r_init[][2] = { + {0x02, 0x08}, {0x02, 0x00}, {0x01, 0x08}, + {0x02, 0x00}, {0x20, 0x00}, {0x21, 0xd0}, + {0x22, 0x00}, {0x23, 0x09}, {0x01, 0x08}, + {0x01, 0x08}, {0x01, 0x08}, {0x25, 0x07}, + {0x26, 0xc3}, {0x27, 0x50}, {0x30, 0x62}, + {0x31, 0x10}, {0x32, 0x06}, {0x33, 0x10}, + {0x20, 0x00}, {0x21, 0xd0}, {0x22, 0x00}, + {0x23, 0x09}, {0x01, 0x08}, +}; + +int reg_r(struct gspca_dev *gspca_dev, u16 reg, u16 length) +{ + struct usb_device *dev = gspca_dev->dev; + int result; + result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x00, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + reg, + 0x00, + gspca_dev->usb_buf, + length, + 500); + if (unlikely(result < 0 || result != length)) { + err("Read register failed 0x%02X", reg); + return -EIO; + } + return 0; +} + +int reg_w(struct gspca_dev *gspca_dev, u16 reg, const u8 *buffer, int length) +{ + struct usb_device *dev = gspca_dev->dev; + int result; + memcpy(gspca_dev->usb_buf, buffer, length); + result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0x08, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + reg, + 0x00, + gspca_dev->usb_buf, + length, + 500); + if (unlikely(result < 0 || result != length)) { + err("Write register failed index 0x%02X", reg); + return -EIO; + } + return 0; +} + +int reg_w1(struct gspca_dev *gspca_dev, u16 reg, const u8 value) +{ + u8 data[1] = {value}; + return reg_w(gspca_dev, reg, data, 1); +} + +int i2c_w(struct gspca_dev *gspca_dev, const u8 *buffer) +{ + int i; + reg_w(gspca_dev, 0x10c0, buffer, 8); + for (i = 0; i < 5; i++) { + reg_r(gspca_dev, 0x10c0, 1); + if (gspca_dev->usb_buf[0] & 0x04) { + if (gspca_dev->usb_buf[0] & 0x08) + return -1; + return 0; + } + msleep(1); + } + return -1; +} + +int i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + u8 row[8]; + + /* + * from the point of view of the bridge, the length + * includes the address + */ + row[0] = 0x81 | (2 << 4); + row[1] = sd->i2c_addr; + row[2] = reg; + row[3] = val; + row[4] = 0x00; + row[5] = 0x00; + row[6] = 0x00; + row[7] = 0x10; + + return i2c_w(gspca_dev, row); +} + +int i2c_w2(struct gspca_dev *gspca_dev, u8 reg, u16 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 row[8]; + + /* + * from the point of view of the bridge, the length + * includes the address + */ + row[0] = 0x81 | (3 << 4); + row[1] = sd->i2c_addr; + row[2] = reg; + row[3] = (val >> 8) & 0xff; + row[4] = val & 0xff; + row[5] = 0x00; + row[6] = 0x00; + row[7] = 0x10; + + return i2c_w(gspca_dev, row); +} + +int i2c_r1(struct gspca_dev *gspca_dev, u8 reg, u8 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 row[8]; + + row[0] = 0x81 | 0x10; + row[1] = sd->i2c_addr; + row[2] = reg; + row[3] = 0; + row[4] = 0; + row[5] = 0; + row[6] = 0; + row[7] = 0x10; + reg_w(gspca_dev, 0x10c0, row, 8); + msleep(1); + row[0] = 0x81 | (2 << 4) | 0x02; + row[2] = 0; + reg_w(gspca_dev, 0x10c0, row, 8); + msleep(1); + reg_r(gspca_dev, 0x10c2, 5); + *val = gspca_dev->usb_buf[3]; + return 0; +} + +int i2c_r2(struct gspca_dev *gspca_dev, u8 reg, u16 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 row[8]; + + row[0] = 0x81 | 0x10; + row[1] = sd->i2c_addr; + row[2] = reg; + row[3] = 0; + row[4] = 0; + row[5] = 0; + row[6] = 0; + row[7] = 0x10; + reg_w(gspca_dev, 0x10c0, row, 8); + msleep(1); + row[0] = 0x81 | (3 << 4) | 0x02; + row[2] = 0; + reg_w(gspca_dev, 0x10c0, row, 8); + msleep(1); + reg_r(gspca_dev, 0x10c2, 5); + *val = (gspca_dev->usb_buf[2] << 8) | gspca_dev->usb_buf[3]; + return 0; +} + +static int ov9650_init_sensor(struct gspca_dev *gspca_dev) +{ + int i; + struct sd *sd = (struct sd *) gspca_dev; + + for (i = 0; i < ARRAY_SIZE(ov9650_init); i++) { + if (i2c_w1(gspca_dev, ov9650_init[i][0], + ov9650_init[i][1]) < 0) { + err("OV9650 sensor initialization failed"); + return -ENODEV; + } + } + sd->hstart = 1; + sd->vstart = 7; + return 0; +} + +static int ov9655_init_sensor(struct gspca_dev *gspca_dev) +{ + int i; + struct sd *sd = (struct sd *) gspca_dev; + + for (i = 0; i < ARRAY_SIZE(ov9655_init); i++) { + if (i2c_w1(gspca_dev, ov9655_init[i][0], + ov9655_init[i][1]) < 0) { + err("OV9655 sensor initialization failed"); + return -ENODEV; + } + } + /* disable hflip and vflip */ + gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX); + sd->hstart = 0; + sd->vstart = 7; + return 0; +} + +static int soi968_init_sensor(struct gspca_dev *gspca_dev) +{ + int i; + struct sd *sd = (struct sd *) gspca_dev; + + for (i = 0; i < ARRAY_SIZE(soi968_init); i++) { + if (i2c_w1(gspca_dev, soi968_init[i][0], + soi968_init[i][1]) < 0) { + err("SOI968 sensor initialization failed"); + return -ENODEV; + } + } + /* disable hflip and vflip */ + gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX); + sd->hstart = 60; + sd->vstart = 11; + return 0; +} + +static int ov7660_init_sensor(struct gspca_dev *gspca_dev) +{ + int i; + struct sd *sd = (struct sd *) gspca_dev; + + for (i = 0; i < ARRAY_SIZE(ov7660_init); i++) { + if (i2c_w1(gspca_dev, ov7660_init[i][0], + ov7660_init[i][1]) < 0) { + err("OV7660 sensor initialization failed"); + return -ENODEV; + } + } + /* disable hflip and vflip */ + gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX); + sd->hstart = 1; + sd->vstart = 1; + return 0; +} + +static int ov7670_init_sensor(struct gspca_dev *gspca_dev) +{ + int i; + struct sd *sd = (struct sd *) gspca_dev; + + for (i = 0; i < ARRAY_SIZE(ov7670_init); i++) { + if (i2c_w1(gspca_dev, ov7670_init[i][0], + ov7670_init[i][1]) < 0) { + err("OV7670 sensor initialization failed"); + return -ENODEV; + } + } + /* disable hflip and vflip */ + gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX); + sd->hstart = 0; + sd->vstart = 1; + return 0; +} + +static int mt9v_init_sensor(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + u16 value; + int ret; + + sd->i2c_addr = 0x5d; + ret = i2c_r2(gspca_dev, 0xff, &value); + if ((ret == 0) && (value == 0x8243)) { + for (i = 0; i < ARRAY_SIZE(mt9v011_init); i++) { + if (i2c_w2(gspca_dev, mt9v011_init[i][0], + mt9v011_init[i][1]) < 0) { + err("MT9V011 sensor initialization failed"); + return -ENODEV; + } + } + sd->hstart = 2; + sd->vstart = 2; + sd->sensor = SENSOR_MT9V011; + info("MT9V011 sensor detected"); + return 0; + } + + sd->i2c_addr = 0x5c; + i2c_w2(gspca_dev, 0x01, 0x0004); + ret = i2c_r2(gspca_dev, 0xff, &value); + if ((ret == 0) && (value == 0x823a)) { + for (i = 0; i < ARRAY_SIZE(mt9v111_init); i++) { + if (i2c_w2(gspca_dev, mt9v111_init[i][0], + mt9v111_init[i][1]) < 0) { + err("MT9V111 sensor initialization failed"); + return -ENODEV; + } + } + sd->hstart = 2; + sd->vstart = 2; + sd->sensor = SENSOR_MT9V111; + info("MT9V111 sensor detected"); + return 0; + } + + sd->i2c_addr = 0x5d; + ret = i2c_w2(gspca_dev, 0xf0, 0x0000); + if (ret < 0) { + sd->i2c_addr = 0x48; + i2c_w2(gspca_dev, 0xf0, 0x0000); + } + ret = i2c_r2(gspca_dev, 0x00, &value); + if ((ret == 0) && (value == 0x1229)) { + for (i = 0; i < ARRAY_SIZE(mt9v112_init); i++) { + if (i2c_w2(gspca_dev, mt9v112_init[i][0], + mt9v112_init[i][1]) < 0) { + err("MT9V112 sensor initialization failed"); + return -ENODEV; + } + } + sd->hstart = 6; + sd->vstart = 2; + sd->sensor = SENSOR_MT9V112; + info("MT9V112 sensor detected"); + return 0; + } + + return -ENODEV; +} + +static int mt9m111_init_sensor(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + for (i = 0; i < ARRAY_SIZE(mt9m111_init); i++) { + if (i2c_w2(gspca_dev, mt9m111_init[i][0], + mt9m111_init[i][1]) < 0) { + err("MT9M111 sensor initialization failed"); + return -ENODEV; + } + } + sd->hstart = 0; + sd->vstart = 2; + return 0; +} + +static int mt9m001_init_sensor(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + for (i = 0; i < ARRAY_SIZE(mt9m001_init); i++) { + if (i2c_w2(gspca_dev, mt9m001_init[i][0], + mt9m001_init[i][1]) < 0) { + err("MT9M001 sensor initialization failed"); + return -ENODEV; + } + } + /* disable hflip and vflip */ + gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX); + sd->hstart = 2; + sd->vstart = 2; + return 0; +} + +static int hv7131r_init_sensor(struct gspca_dev *gspca_dev) +{ + int i; + struct sd *sd = (struct sd *) gspca_dev; + + for (i = 0; i < ARRAY_SIZE(hv7131r_init); i++) { + if (i2c_w1(gspca_dev, hv7131r_init[i][0], + hv7131r_init[i][1]) < 0) { + err("HV7131R Sensor initialization failed"); + return -ENODEV; + } + } + sd->hstart = 0; + sd->vstart = 1; + return 0; +} + +#ifdef CONFIG_USB_GSPCA_SN9C20X_EVDEV +static int input_kthread(void *data) +{ + struct gspca_dev *gspca_dev = (struct gspca_dev *)data; + struct sd *sd = (struct sd *) gspca_dev; + + DECLARE_WAIT_QUEUE_HEAD(wait); + set_freezable(); + for (;;) { + if (kthread_should_stop()) + break; + + if (reg_r(gspca_dev, 0x1005, 1) < 0) + continue; + + input_report_key(sd->input_dev, + KEY_CAMERA, + gspca_dev->usb_buf[0] & sd->input_gpio); + input_sync(sd->input_dev); + + wait_event_freezable_timeout(wait, + kthread_should_stop(), + msecs_to_jiffies(100)); + } + return 0; +} + + +static int sn9c20x_input_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + if (sd->input_gpio == 0) + return 0; + + sd->input_dev = input_allocate_device(); + if (!sd->input_dev) + return -ENOMEM; + + sd->input_dev->name = "SN9C20X Webcam"; + + sd->input_dev->phys = kasprintf(GFP_KERNEL, "usb-%s-%s", + gspca_dev->dev->bus->bus_name, + gspca_dev->dev->devpath); + + if (!sd->input_dev->phys) + return -ENOMEM; + + usb_to_input_id(gspca_dev->dev, &sd->input_dev->id); + sd->input_dev->dev.parent = &gspca_dev->dev->dev; + + set_bit(EV_KEY, sd->input_dev->evbit); + set_bit(KEY_CAMERA, sd->input_dev->keybit); + + if (input_register_device(sd->input_dev)) + return -EINVAL; + + sd->input_task = kthread_run(input_kthread, gspca_dev, "sn9c20x/%d", + gspca_dev->vdev.minor); + + if (IS_ERR(sd->input_task)) + return -EINVAL; + + return 0; +} + +static void sn9c20x_input_cleanup(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + if (sd->input_task != NULL && !IS_ERR(sd->input_task)) + kthread_stop(sd->input_task); + + if (sd->input_dev != NULL) { + input_unregister_device(sd->input_dev); + kfree(sd->input_dev->phys); + input_free_device(sd->input_dev); + sd->input_dev = NULL; + } +} +#endif + +static int set_cmatrix(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 hue_coord, hue_index = 180 + sd->hue; + u8 cmatrix[21]; + memset(cmatrix, 0, 21); + + cmatrix[2] = (sd->contrast * 0x25 / 0x100) + 0x26; + cmatrix[0] = 0x13 + (cmatrix[2] - 0x26) * 0x13 / 0x25; + cmatrix[4] = 0x07 + (cmatrix[2] - 0x26) * 0x07 / 0x25; + cmatrix[18] = sd->brightness - 0x80; + + hue_coord = (hsv_red_x[hue_index] * sd->saturation) >> 8; + cmatrix[6] = (unsigned char)(hue_coord & 0xff); + cmatrix[7] = (unsigned char)((hue_coord >> 8) & 0x0f); + + hue_coord = (hsv_red_y[hue_index] * sd->saturation) >> 8; + cmatrix[8] = (unsigned char)(hue_coord & 0xff); + cmatrix[9] = (unsigned char)((hue_coord >> 8) & 0x0f); + + hue_coord = (hsv_green_x[hue_index] * sd->saturation) >> 8; + cmatrix[10] = (unsigned char)(hue_coord & 0xff); + cmatrix[11] = (unsigned char)((hue_coord >> 8) & 0x0f); + + hue_coord = (hsv_green_y[hue_index] * sd->saturation) >> 8; + cmatrix[12] = (unsigned char)(hue_coord & 0xff); + cmatrix[13] = (unsigned char)((hue_coord >> 8) & 0x0f); + + hue_coord = (hsv_blue_x[hue_index] * sd->saturation) >> 8; + cmatrix[14] = (unsigned char)(hue_coord & 0xff); + cmatrix[15] = (unsigned char)((hue_coord >> 8) & 0x0f); + + hue_coord = (hsv_blue_y[hue_index] * sd->saturation) >> 8; + cmatrix[16] = (unsigned char)(hue_coord & 0xff); + cmatrix[17] = (unsigned char)((hue_coord >> 8) & 0x0f); + + return reg_w(gspca_dev, 0x10e1, cmatrix, 21); +} + +static int set_gamma(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 gamma[17]; + u8 gval = sd->gamma * 0xb8 / 0x100; + + + gamma[0] = 0x0a; + gamma[1] = 0x13 + (gval * (0xcb - 0x13) / 0xb8); + gamma[2] = 0x25 + (gval * (0xee - 0x25) / 0xb8); + gamma[3] = 0x37 + (gval * (0xfa - 0x37) / 0xb8); + gamma[4] = 0x45 + (gval * (0xfc - 0x45) / 0xb8); + gamma[5] = 0x55 + (gval * (0xfb - 0x55) / 0xb8); + gamma[6] = 0x65 + (gval * (0xfc - 0x65) / 0xb8); + gamma[7] = 0x74 + (gval * (0xfd - 0x74) / 0xb8); + gamma[8] = 0x83 + (gval * (0xfe - 0x83) / 0xb8); + gamma[9] = 0x92 + (gval * (0xfc - 0x92) / 0xb8); + gamma[10] = 0xa1 + (gval * (0xfc - 0xa1) / 0xb8); + gamma[11] = 0xb0 + (gval * (0xfc - 0xb0) / 0xb8); + gamma[12] = 0xbf + (gval * (0xfb - 0xbf) / 0xb8); + gamma[13] = 0xce + (gval * (0xfb - 0xce) / 0xb8); + gamma[14] = 0xdf + (gval * (0xfd - 0xdf) / 0xb8); + gamma[15] = 0xea + (gval * (0xf9 - 0xea) / 0xb8); + gamma[16] = 0xf5; + + return reg_w(gspca_dev, 0x1190, gamma, 17); +} + +static int set_redblue(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + reg_w1(gspca_dev, 0x118c, sd->red); + reg_w1(gspca_dev, 0x118f, sd->blue); + return 0; +} + +static int set_hvflip(struct gspca_dev *gspca_dev) +{ + u8 value, tslb; + u16 value2; + struct sd *sd = (struct sd *) gspca_dev; + switch (sd->sensor) { + case SENSOR_OV9650: + i2c_r1(gspca_dev, 0x1e, &value); + value &= ~0x30; + tslb = 0x01; + if (sd->hflip) + value |= 0x20; + if (sd->vflip) { + value |= 0x10; + tslb = 0x49; + } + i2c_w1(gspca_dev, 0x1e, value); + i2c_w1(gspca_dev, 0x3a, tslb); + break; + case SENSOR_MT9V111: + case SENSOR_MT9V011: + i2c_r2(gspca_dev, 0x20, &value2); + value2 &= ~0xc0a0; + if (sd->hflip) + value2 |= 0x8080; + if (sd->vflip) + value2 |= 0x4020; + i2c_w2(gspca_dev, 0x20, value2); + break; + case SENSOR_MT9M111: + case SENSOR_MT9V112: + i2c_r2(gspca_dev, 0x20, &value2); + value2 &= ~0x0003; + if (sd->hflip) + value2 |= 0x0002; + if (sd->vflip) + value2 |= 0x0001; + i2c_w2(gspca_dev, 0x20, value2); + break; + case SENSOR_HV7131R: + i2c_r1(gspca_dev, 0x01, &value); + value &= ~0x03; + if (sd->vflip) + value |= 0x01; + if (sd->hflip) + value |= 0x02; + i2c_w1(gspca_dev, 0x01, value); + break; + } + return 0; +} + +static int set_exposure(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 exp[8] = {0x81, sd->i2c_addr, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e}; + switch (sd->sensor) { + case SENSOR_OV7660: + case SENSOR_OV7670: + case SENSOR_SOI968: + case SENSOR_OV9655: + case SENSOR_OV9650: + exp[0] |= (3 << 4); + exp[2] = 0x2d; + exp[3] = sd->exposure & 0xff; + exp[4] = sd->exposure >> 8; + break; + case SENSOR_MT9M001: + case SENSOR_MT9M111: + case SENSOR_MT9V112: + case SENSOR_MT9V111: + case SENSOR_MT9V011: + exp[0] |= (3 << 4); + exp[2] = 0x09; + exp[3] = sd->exposure >> 8; + exp[4] = sd->exposure & 0xff; + break; + case SENSOR_HV7131R: + exp[0] |= (4 << 4); + exp[2] = 0x25; + exp[3] = ((sd->exposure * 0xffffff) / 0xffff) >> 16; + exp[4] = ((sd->exposure * 0xffffff) / 0xffff) >> 8; + exp[5] = ((sd->exposure * 0xffffff) / 0xffff) & 0xff; + break; + } + i2c_w(gspca_dev, exp); + return 0; +} + +static int set_gain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 gain[8] = {0x81, sd->i2c_addr, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d}; + switch (sd->sensor) { + case SENSOR_OV7660: + case SENSOR_OV7670: + case SENSOR_SOI968: + case SENSOR_OV9655: + case SENSOR_OV9650: + gain[0] |= (2 << 4); + gain[3] = ov_gain[sd->gain]; + break; + case SENSOR_MT9V011: + case SENSOR_MT9V111: + gain[0] |= (3 << 4); + gain[2] = 0x35; + gain[3] = micron1_gain[sd->gain] >> 8; + gain[4] = micron1_gain[sd->gain] & 0xff; + break; + case SENSOR_MT9V112: + case SENSOR_MT9M111: + gain[0] |= (3 << 4); + gain[2] = 0x2f; + gain[3] = micron1_gain[sd->gain] >> 8; + gain[4] = micron1_gain[sd->gain] & 0xff; + break; + case SENSOR_MT9M001: + gain[0] |= (3 << 4); + gain[2] = 0x2f; + gain[3] = micron2_gain[sd->gain] >> 8; + gain[4] = micron2_gain[sd->gain] & 0xff; + break; + case SENSOR_HV7131R: + gain[0] |= (2 << 4); + gain[2] = 0x30; + gain[3] = hv7131r_gain[sd->gain]; + break; + } + i2c_w(gspca_dev, gain); + return 0; +} + +static int sd_setbrightness(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + return set_cmatrix(gspca_dev); + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + *val = sd->brightness; + return 0; +} + + +static int sd_setcontrast(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) + return set_cmatrix(gspca_dev); + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + *val = sd->contrast; + return 0; +} + +static int sd_setsaturation(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->saturation = val; + if (gspca_dev->streaming) + return set_cmatrix(gspca_dev); + return 0; +} + +static int sd_getsaturation(struct gspca_dev *gspca_dev, s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + *val = sd->saturation; + return 0; +} + +static int sd_sethue(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->hue = val; + if (gspca_dev->streaming) + return set_cmatrix(gspca_dev); + return 0; +} + +static int sd_gethue(struct gspca_dev *gspca_dev, s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + *val = sd->hue; + return 0; +} + +static int sd_setgamma(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->gamma = val; + if (gspca_dev->streaming) + return set_gamma(gspca_dev); + return 0; +} + +static int sd_getgamma(struct gspca_dev *gspca_dev, s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + *val = sd->gamma; + return 0; +} + +static int sd_setredbalance(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->red = val; + if (gspca_dev->streaming) + return set_redblue(gspca_dev); + return 0; +} + +static int sd_getredbalance(struct gspca_dev *gspca_dev, s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + *val = sd->red; + return 0; +} + +static int sd_setbluebalance(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->blue = val; + if (gspca_dev->streaming) + return set_redblue(gspca_dev); + return 0; +} + +static int sd_getbluebalance(struct gspca_dev *gspca_dev, s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + *val = sd->blue; + return 0; +} + +static int sd_sethflip(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->hflip = val; + if (gspca_dev->streaming) + return set_hvflip(gspca_dev); + return 0; +} + +static int sd_gethflip(struct gspca_dev *gspca_dev, s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + *val = sd->hflip; + return 0; +} + +static int sd_setvflip(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->vflip = val; + if (gspca_dev->streaming) + return set_hvflip(gspca_dev); + return 0; +} + +static int sd_getvflip(struct gspca_dev *gspca_dev, s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + *val = sd->vflip; + return 0; +} + +static int sd_setexposure(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->exposure = val; + if (gspca_dev->streaming) + return set_exposure(gspca_dev); + return 0; +} + +static int sd_getexposure(struct gspca_dev *gspca_dev, s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + *val = sd->exposure; + return 0; +} + +static int sd_setgain(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->gain = val; + if (gspca_dev->streaming) + return set_gain(gspca_dev); + return 0; +} + +static int sd_getgain(struct gspca_dev *gspca_dev, s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + *val = sd->gain; + return 0; +} + +static int sd_setautoexposure(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + sd->auto_exposure = val; + return 0; +} + +static int sd_getautoexposure(struct gspca_dev *gspca_dev, s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + *val = sd->auto_exposure; + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int sd_dbg_g_register(struct gspca_dev *gspca_dev, + struct v4l2_dbg_register *reg) +{ + struct sd *sd = (struct sd *) gspca_dev; + switch (reg->match.type) { + case V4L2_CHIP_MATCH_HOST: + if (reg->match.addr != 0) + return -EINVAL; + if (reg->reg < 0x1000 || reg->reg > 0x11ff) + return -EINVAL; + if (reg_r(gspca_dev, reg->reg, 1) < 0) + return -EINVAL; + reg->val = gspca_dev->usb_buf[0]; + return 0; + case V4L2_CHIP_MATCH_I2C_ADDR: + if (reg->match.addr != sd->i2c_addr) + return -EINVAL; + if (sd->sensor >= SENSOR_MT9V011 && + sd->sensor <= SENSOR_MT9M111) { + if (i2c_r2(gspca_dev, reg->reg, (u16 *)®->val) < 0) + return -EINVAL; + } else { + if (i2c_r1(gspca_dev, reg->reg, (u8 *)®->val) < 0) + return -EINVAL; + } + return 0; + } + return -EINVAL; +} + +static int sd_dbg_s_register(struct gspca_dev *gspca_dev, + struct v4l2_dbg_register *reg) +{ + struct sd *sd = (struct sd *) gspca_dev; + switch (reg->match.type) { + case V4L2_CHIP_MATCH_HOST: + if (reg->match.addr != 0) + return -EINVAL; + if (reg->reg < 0x1000 || reg->reg > 0x11ff) + return -EINVAL; + if (reg_w1(gspca_dev, reg->reg, reg->val) < 0) + return -EINVAL; + return 0; + case V4L2_CHIP_MATCH_I2C_ADDR: + if (reg->match.addr != sd->i2c_addr) + return -EINVAL; + if (sd->sensor >= SENSOR_MT9V011 && + sd->sensor <= SENSOR_MT9M111) { + if (i2c_w2(gspca_dev, reg->reg, reg->val) < 0) + return -EINVAL; + } else { + if (i2c_w1(gspca_dev, reg->reg, reg->val) < 0) + return -EINVAL; + } + return 0; + } + return -EINVAL; +} +#endif + +static int sd_chip_ident(struct gspca_dev *gspca_dev, + struct v4l2_dbg_chip_ident *chip) +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (chip->match.type) { + case V4L2_CHIP_MATCH_HOST: + if (chip->match.addr != 0) + return -EINVAL; + chip->revision = 0; + chip->ident = V4L2_IDENT_SN9C20X; + return 0; + case V4L2_CHIP_MATCH_I2C_ADDR: + if (chip->match.addr != sd->i2c_addr) + return -EINVAL; + chip->revision = 0; + chip->ident = i2c_ident[sd->sensor]; + return 0; + } + return -EINVAL; +} + +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + + cam = &gspca_dev->cam; + + sd->sensor = (id->driver_info >> 8) & 0xff; + sd->i2c_addr = id->driver_info & 0xff; + + switch (sd->sensor) { + case SENSOR_OV9650: + cam->cam_mode = sxga_mode; + cam->nmodes = ARRAY_SIZE(sxga_mode); + break; + default: + cam->cam_mode = vga_mode; + cam->nmodes = ARRAY_SIZE(vga_mode); + } + + sd->old_step = 0; + sd->older_step = 0; + sd->exposure_step = 16; + + sd->brightness = BRIGHTNESS_DEFAULT; + sd->contrast = CONTRAST_DEFAULT; + sd->saturation = SATURATION_DEFAULT; + sd->hue = HUE_DEFAULT; + sd->gamma = GAMMA_DEFAULT; + sd->red = RED_DEFAULT; + sd->blue = BLUE_DEFAULT; + + sd->hflip = HFLIP_DEFAULT; + sd->vflip = VFLIP_DEFAULT; + sd->exposure = EXPOSURE_DEFAULT; + sd->gain = GAIN_DEFAULT; + sd->auto_exposure = AUTO_EXPOSURE_DEFAULT; + + sd->quality = 95; + +#ifdef CONFIG_USB_GSPCA_SN9C20X_EVDEV + sd->input_gpio = (id->driver_info >> 16) & 0xff; + if (sn9c20x_input_init(gspca_dev) < 0) + return -ENODEV; +#endif + return 0; +} + +static int sd_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + u8 value; + u8 i2c_init[9] = + {0x80, sd->i2c_addr, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03}; + + for (i = 0; i < ARRAY_SIZE(bridge_init); i++) { + value = bridge_init[i][1]; + if (reg_w(gspca_dev, bridge_init[i][0], &value, 1) < 0) { + err("Device initialization failed"); + return -ENODEV; + } + } + + if (reg_w(gspca_dev, 0x10c0, i2c_init, 9) < 0) { + err("Device initialization failed"); + return -ENODEV; + } + + switch (sd->sensor) { + case SENSOR_OV9650: + if (ov9650_init_sensor(gspca_dev) < 0) + return -ENODEV; + info("OV9650 sensor detected"); + break; + case SENSOR_OV9655: + if (ov9655_init_sensor(gspca_dev) < 0) + return -ENODEV; + info("OV9655 sensor detected"); + break; + case SENSOR_SOI968: + if (soi968_init_sensor(gspca_dev) < 0) + return -ENODEV; + info("SOI968 sensor detected"); + break; + case SENSOR_OV7660: + if (ov7660_init_sensor(gspca_dev) < 0) + return -ENODEV; + info("OV7660 sensor detected"); + break; + case SENSOR_OV7670: + if (ov7670_init_sensor(gspca_dev) < 0) + return -ENODEV; + info("OV7670 sensor detected"); + break; + case SENSOR_MT9VPRB: + if (mt9v_init_sensor(gspca_dev) < 0) + return -ENODEV; + break; + case SENSOR_MT9M111: + if (mt9m111_init_sensor(gspca_dev) < 0) + return -ENODEV; + info("MT9M111 sensor detected"); + break; + case SENSOR_MT9M001: + if (mt9m001_init_sensor(gspca_dev) < 0) + return -ENODEV; + info("MT9M001 sensor detected"); + break; + case SENSOR_HV7131R: + if (hv7131r_init_sensor(gspca_dev) < 0) + return -ENODEV; + info("HV7131R sensor detected"); + break; + default: + info("Unsupported Sensor"); + return -ENODEV; + } + + return 0; +} + +static void configure_sensor_output(struct gspca_dev *gspca_dev, int mode) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 value; + switch (sd->sensor) { + case SENSOR_OV9650: + if (mode & MODE_SXGA) { + i2c_w1(gspca_dev, 0x17, 0x1b); + i2c_w1(gspca_dev, 0x18, 0xbc); + i2c_w1(gspca_dev, 0x19, 0x01); + i2c_w1(gspca_dev, 0x1a, 0x82); + i2c_r1(gspca_dev, 0x12, &value); + i2c_w1(gspca_dev, 0x12, value & 0x07); + } else { + i2c_w1(gspca_dev, 0x17, 0x24); + i2c_w1(gspca_dev, 0x18, 0xc5); + i2c_w1(gspca_dev, 0x19, 0x00); + i2c_w1(gspca_dev, 0x1a, 0x3c); + i2c_r1(gspca_dev, 0x12, &value); + i2c_w1(gspca_dev, 0x12, (value & 0x7) | 0x40); + } + break; + } +} + +#define HW_WIN(mode, hstart, vstart) \ +((const u8 []){hstart & 0xff, hstart >> 8, \ +vstart & 0xff, vstart >> 8, \ +(mode & MODE_SXGA ? 1280 >> 4 : 640 >> 4), \ +(mode & MODE_SXGA ? 1024 >> 3 : 480 >> 3)}) + +#define CLR_WIN(width, height) \ +((const u8 [])\ +{0, width >> 2, 0, height >> 1,\ +((width >> 10) & 0x01) | ((height >> 8) & 0x6)}) + +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; + int width = gspca_dev->width; + int height = gspca_dev->height; + u8 fmt, scale = 0; + + sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + if (sd->jpeg_hdr == NULL) + return -ENOMEM; + + jpeg_define(sd->jpeg_hdr, height, width, + 0x21); + jpeg_set_qual(sd->jpeg_hdr, sd->quality); + + if (mode & MODE_RAW) + fmt = 0x2d; + else if (mode & MODE_JPEG) + fmt = 0x2c; + else + fmt = 0x2f; + + switch (mode & 0x0f) { + case 3: + scale = 0xc0; + info("Set 1280x1024"); + break; + case 2: + scale = 0x80; + info("Set 640x480"); + break; + case 1: + scale = 0x90; + info("Set 320x240"); + break; + case 0: + scale = 0xa0; + info("Set 160x120"); + break; + } + + configure_sensor_output(gspca_dev, mode); + reg_w(gspca_dev, 0x1100, sd->jpeg_hdr + JPEG_QT0_OFFSET, 64); + reg_w(gspca_dev, 0x1140, sd->jpeg_hdr + JPEG_QT1_OFFSET, 64); + reg_w(gspca_dev, 0x10fb, CLR_WIN(width, height), 5); + reg_w(gspca_dev, 0x1180, HW_WIN(mode, sd->hstart, sd->vstart), 6); + reg_w1(gspca_dev, 0x1189, scale); + reg_w1(gspca_dev, 0x10e0, fmt); + + set_cmatrix(gspca_dev); + set_gamma(gspca_dev); + set_redblue(gspca_dev); + set_gain(gspca_dev); + set_exposure(gspca_dev); + set_hvflip(gspca_dev); + + reg_r(gspca_dev, 0x1061, 1); + reg_w1(gspca_dev, 0x1061, gspca_dev->usb_buf[0] | 0x02); + return 0; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + reg_r(gspca_dev, 0x1061, 1); + reg_w1(gspca_dev, 0x1061, gspca_dev->usb_buf[0] & ~0x02); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + kfree(sd->jpeg_hdr); +} + +static void do_autoexposure(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int avg_lum, new_exp; + + if (!sd->auto_exposure) + return; + + avg_lum = atomic_read(&sd->avg_lum); + + /* + * some hardcoded values are present + * like those for maximal/minimal exposure + * and exposure steps + */ + if (avg_lum < MIN_AVG_LUM) { + if (sd->exposure > 0x1770) + return; + + new_exp = sd->exposure + sd->exposure_step; + if (new_exp > 0x1770) + new_exp = 0x1770; + if (new_exp < 0x10) + new_exp = 0x10; + sd->exposure = new_exp; + set_exposure(gspca_dev); + + sd->older_step = sd->old_step; + sd->old_step = 1; + + if (sd->old_step ^ sd->older_step) + sd->exposure_step /= 2; + else + sd->exposure_step += 2; + } + if (avg_lum > MAX_AVG_LUM) { + if (sd->exposure < 0x10) + return; + new_exp = sd->exposure - sd->exposure_step; + if (new_exp > 0x1700) + new_exp = 0x1770; + if (new_exp < 0x10) + new_exp = 0x10; + sd->exposure = new_exp; + set_exposure(gspca_dev); + sd->older_step = sd->old_step; + sd->old_step = 0; + + if (sd->old_step ^ sd->older_step) + sd->exposure_step /= 2; + else + sd->exposure_step += 2; + } +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + int avg_lum; + static unsigned char frame_header[] = + {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96}; + if (len == 64 && memcmp(data, frame_header, 6) == 0) { + avg_lum = ((data[35] >> 2) & 3) | + (data[20] << 2) | + (data[19] << 10); + avg_lum += ((data[35] >> 4) & 3) | + (data[22] << 2) | + (data[21] << 10); + avg_lum += ((data[35] >> 6) & 3) | + (data[24] << 2) | + (data[23] << 10); + avg_lum += (data[36] & 3) | + (data[26] << 2) | + (data[25] << 10); + avg_lum += ((data[36] >> 2) & 3) | + (data[28] << 2) | + (data[27] << 10); + avg_lum += ((data[36] >> 4) & 3) | + (data[30] << 2) | + (data[29] << 10); + avg_lum += ((data[36] >> 6) & 3) | + (data[32] << 2) | + (data[31] << 10); + avg_lum += ((data[44] >> 4) & 3) | + (data[34] << 2) | + (data[33] << 10); + avg_lum >>= 9; + atomic_set(&sd->avg_lum, avg_lum); + gspca_frame_add(gspca_dev, LAST_PACKET, + frame, data, len); + return; + } + if (gspca_dev->last_packet_type == LAST_PACKET) { + if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv + & MODE_JPEG) { + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + sd->jpeg_hdr, JPEG_HDR_SZ); + gspca_frame_add(gspca_dev, INTER_PACKET, frame, + data, len); + } else { + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + data, len); + } + } else { + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + } +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .init = sd_init, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .pkt_scan = sd_pkt_scan, + .dq_callback = do_autoexposure, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .set_register = sd_dbg_s_register, + .get_register = sd_dbg_g_register, +#endif + .get_chip_ident = sd_chip_ident, +}; + +#define SN9C20X(sensor, i2c_addr, button_mask) \ + .driver_info = (button_mask << 16) \ + | (SENSOR_ ## sensor << 8) \ + | (i2c_addr) + +static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x0c45, 0x6240), SN9C20X(MT9M001, 0x5d, 0)}, + {USB_DEVICE(0x0c45, 0x6242), SN9C20X(MT9M111, 0x5d, 0)}, + {USB_DEVICE(0x0c45, 0x6248), SN9C20X(OV9655, 0x30, 0)}, + {USB_DEVICE(0x0c45, 0x624e), SN9C20X(SOI968, 0x30, 0x10)}, + {USB_DEVICE(0x0c45, 0x624f), SN9C20X(OV9650, 0x30, 0)}, + {USB_DEVICE(0x0c45, 0x6251), SN9C20X(OV9650, 0x30, 0)}, + {USB_DEVICE(0x0c45, 0x6253), SN9C20X(OV9650, 0x30, 0)}, + {USB_DEVICE(0x0c45, 0x6260), SN9C20X(OV7670, 0x21, 0)}, + {USB_DEVICE(0x0c45, 0x6270), SN9C20X(MT9VPRB, 0x00, 0)}, + {USB_DEVICE(0x0c45, 0x627b), SN9C20X(OV7660, 0x21, 0)}, + {USB_DEVICE(0x0c45, 0x627c), SN9C20X(HV7131R, 0x11, 0)}, + {USB_DEVICE(0x0c45, 0x627f), SN9C20X(OV9650, 0x30, 0)}, + {USB_DEVICE(0x0c45, 0x6280), SN9C20X(MT9M001, 0x5d, 0)}, + {USB_DEVICE(0x0c45, 0x6282), SN9C20X(MT9M111, 0x5d, 0)}, + {USB_DEVICE(0x0c45, 0x6288), SN9C20X(OV9655, 0x30, 0)}, + {USB_DEVICE(0x0c45, 0x628e), SN9C20X(SOI968, 0x30, 0)}, + {USB_DEVICE(0x0c45, 0x628f), SN9C20X(OV9650, 0x30, 0)}, + {USB_DEVICE(0x0c45, 0x62a0), SN9C20X(OV7670, 0x21, 0)}, + {USB_DEVICE(0x0c45, 0x62b0), SN9C20X(MT9VPRB, 0x00, 0)}, + {USB_DEVICE(0x0c45, 0x62b3), SN9C20X(OV9655, 0x30, 0)}, + {USB_DEVICE(0x0c45, 0x62bb), SN9C20X(OV7660, 0x21, 0)}, + {USB_DEVICE(0x0c45, 0x62bc), SN9C20X(HV7131R, 0x11, 0)}, + {USB_DEVICE(0x045e, 0x00f4), SN9C20X(OV9650, 0x30, 0)}, + {USB_DEVICE(0x145f, 0x013d), SN9C20X(OV7660, 0x21, 0)}, + {USB_DEVICE(0x0458, 0x7029), SN9C20X(HV7131R, 0x11, 0)}, + {USB_DEVICE(0xa168, 0x0610), SN9C20X(HV7131R, 0x11, 0)}, + {USB_DEVICE(0xa168, 0x0611), SN9C20X(HV7131R, 0x11, 0)}, + {USB_DEVICE(0xa168, 0x0613), SN9C20X(HV7131R, 0x11, 0)}, + {USB_DEVICE(0xa168, 0x0618), SN9C20X(HV7131R, 0x11, 0)}, + {USB_DEVICE(0xa168, 0x0614), SN9C20X(MT9M111, 0x5d, 0)}, + {USB_DEVICE(0xa168, 0x0615), SN9C20X(MT9M111, 0x5d, 0)}, + {USB_DEVICE(0xa168, 0x0617), SN9C20X(MT9M111, 0x5d, 0)}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static void sd_disconnect(struct usb_interface *intf) +{ +#ifdef CONFIG_USB_GSPCA_SN9C20X_EVDEV + struct gspca_dev *gspca_dev = usb_get_intfdata(intf); + + sn9c20x_input_cleanup(gspca_dev); +#endif + + gspca_disconnect(intf); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = sd_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + int ret; + ret = usb_register(&sd_driver); + if (ret < 0) + return ret; + info("registered"); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + info("deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index 0d02f41fa7d..d6332ab8066 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -1634,6 +1634,8 @@ static void setfreq(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + if (gspca_dev->ctrl_dis & (1 << FREQ_IDX)) + return; if (sd->sensor == SENSOR_OV7660) { switch (sd->freq) { case 0: /* Banding filter disabled */ @@ -1735,6 +1737,8 @@ static int sd_start(struct gspca_dev *gspca_dev) /* create the JPEG header */ sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + if (!sd->jpeg_hdr) + return -ENOMEM; jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, 0x21); /* JPEG 422 */ jpeg_set_qual(sd->jpeg_hdr, sd->quality); diff --git a/drivers/media/video/gspca/spca500.c b/drivers/media/video/gspca/spca500.c index 8806b2ff82b..fab7ef85a6c 100644 --- a/drivers/media/video/gspca/spca500.c +++ b/drivers/media/video/gspca/spca500.c @@ -670,6 +670,8 @@ static int sd_start(struct gspca_dev *gspca_dev) /* create the JPEG header */ sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + if (!sd->jpeg_hdr) + return -ENOMEM; jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, 0x22); /* JPEG 411 */ jpeg_set_qual(sd->jpeg_hdr, sd->quality); diff --git a/drivers/media/video/gspca/stk014.c b/drivers/media/video/gspca/stk014.c index f25be20cf1a..47628964801 100644 --- a/drivers/media/video/gspca/stk014.c +++ b/drivers/media/video/gspca/stk014.c @@ -333,6 +333,8 @@ static int sd_start(struct gspca_dev *gspca_dev) /* create the JPEG header */ sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + if (!sd->jpeg_hdr) + return -ENOMEM; jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, 0x22); /* JPEG 411 */ jpeg_set_qual(sd->jpeg_hdr, sd->quality); diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c index 3039ec208f3..e5024c8496e 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c @@ -64,7 +64,7 @@ static struct v4l2_pix_format hdcs1x00_mode[] = { { HDCS_1X00_DEF_WIDTH, HDCS_1X00_DEF_HEIGHT, - V4L2_PIX_FMT_SBGGR8, + V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, .sizeimage = HDCS_1X00_DEF_WIDTH * HDCS_1X00_DEF_HEIGHT, @@ -80,7 +80,7 @@ static struct v4l2_pix_format hdcs1020_mode[] = { { HDCS_1020_DEF_WIDTH, HDCS_1020_DEF_HEIGHT, - V4L2_PIX_FMT_SBGGR8, + V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, .sizeimage = HDCS_1020_DEF_WIDTH * HDCS_1020_DEF_HEIGHT, @@ -131,9 +131,11 @@ static int hdcs_reg_write_seq(struct sd *sd, u8 reg, u8 *vals, u8 len) (reg + len > 0xff))) return -EINVAL; - for (i = 0; i < len; i++, reg++) { - regs[2*i] = reg; - regs[2*i+1] = vals[i]; + for (i = 0; i < len; i++) { + regs[2 * i] = reg; + regs[2 * i + 1] = vals[i]; + /* All addresses are shifted left one bit as bit 0 toggles r/w */ + reg += 2; } return stv06xx_write_sensor_bytes(sd, regs, len); @@ -174,7 +176,9 @@ static int hdcs_set_state(struct sd *sd, enum hdcs_power_state state) } ret = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), val); - if (ret < 0) + + /* Update the state if the write succeeded */ + if (!ret) hdcs->state = state; return ret; diff --git a/drivers/media/video/gspca/sunplus.c b/drivers/media/video/gspca/sunplus.c index 9623f294bda..5127bbf9dd2 100644 --- a/drivers/media/video/gspca/sunplus.c +++ b/drivers/media/video/gspca/sunplus.c @@ -973,6 +973,8 @@ static int sd_start(struct gspca_dev *gspca_dev) /* create the JPEG header */ sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + if (!sd->jpeg_hdr) + return -ENOMEM; jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, 0x22); /* JPEG 411 */ jpeg_set_qual(sd->jpeg_hdr, sd->quality); diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index 08422d315e6..3d2756f7874 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -7243,6 +7243,8 @@ static int sd_start(struct gspca_dev *gspca_dev) /* create the JPEG header */ sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + if (!sd->jpeg_hdr) + return -ENOMEM; jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, 0x21); /* JPEG 422 */ jpeg_set_qual(sd->jpeg_hdr, sd->quality); diff --git a/drivers/media/video/mt9v011.c b/drivers/media/video/mt9v011.c index 1fe8fc9183a..b2260de645f 100644 --- a/drivers/media/video/mt9v011.c +++ b/drivers/media/video/mt9v011.c @@ -8,6 +8,7 @@ #include <linux/i2c.h> #include <linux/videodev2.h> #include <linux/delay.h> +#include <asm/div64.h> #include <media/v4l2-device.h> #include "mt9v011.h" #include <media/v4l2-i2c-drv.h> @@ -57,6 +58,7 @@ static struct v4l2_queryctrl mt9v011_qctrl[] = { struct mt9v011 { struct v4l2_subdev sd; unsigned width, height; + unsigned xtal; u16 global_gain, red_bal, blue_bal; }; @@ -131,7 +133,7 @@ static const struct i2c_reg_value mt9v011_init_default[] = { { R1E_MT9V011_DIGITAL_ZOOM, 0x0000 }, { R20_MT9V011_READ_MODE, 0x1000 }, - { R07_MT9V011_OUT_CTRL, 0x000a }, /* chip enable */ + { R07_MT9V011_OUT_CTRL, 0x0002 }, /* chip enable */ }; static void set_balance(struct v4l2_subdev *sd) @@ -154,6 +156,31 @@ static void set_balance(struct v4l2_subdev *sd) mt9v011_write(sd, R2D_MT9V011_RED_GAIN, red_gain); } +static void calc_fps(struct v4l2_subdev *sd) +{ + struct mt9v011 *core = to_mt9v011(sd); + unsigned height, width, hblank, vblank, speed; + unsigned row_time, t_time; + u64 frames_per_ms; + unsigned tmp; + + height = mt9v011_read(sd, R03_MT9V011_HEIGHT); + width = mt9v011_read(sd, R04_MT9V011_WIDTH); + hblank = mt9v011_read(sd, R05_MT9V011_HBLANK); + vblank = mt9v011_read(sd, R06_MT9V011_VBLANK); + speed = mt9v011_read(sd, R0A_MT9V011_CLK_SPEED); + + row_time = (width + 113 + hblank) * (speed + 2); + t_time = row_time * (height + vblank + 1); + + frames_per_ms = core->xtal * 1000l; + do_div(frames_per_ms, t_time); + tmp = frames_per_ms; + + v4l2_dbg(1, debug, sd, "Programmed to %u.%03u fps (%d pixel clcks)\n", + tmp / 1000, tmp % 1000, t_time); +} + static void set_res(struct v4l2_subdev *sd) { struct mt9v011 *core = to_mt9v011(sd); @@ -175,10 +202,12 @@ static void set_res(struct v4l2_subdev *sd) mt9v011_write(sd, R04_MT9V011_WIDTH, core->width); mt9v011_write(sd, R05_MT9V011_HBLANK, 771 - core->width); - vstart = 8 + (640 - core->height) / 2; + vstart = 8 + (480 - core->height) / 2; mt9v011_write(sd, R01_MT9V011_ROWSTART, vstart); mt9v011_write(sd, R03_MT9V011_HEIGHT, core->height); mt9v011_write(sd, R06_MT9V011_VBLANK, 508 - core->height); + + calc_fps(sd); }; static int mt9v011_reset(struct v4l2_subdev *sd, u32 val) @@ -215,6 +244,23 @@ static int mt9v011_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) return -EINVAL; } +static int mt9v011_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) +{ + int i; + + v4l2_dbg(1, debug, sd, "queryctrl called\n"); + + for (i = 0; i < ARRAY_SIZE(mt9v011_qctrl); i++) + if (qc->id && qc->id == mt9v011_qctrl[i].id) { + memcpy(qc, &(mt9v011_qctrl[i]), + sizeof(*qc)); + return 0; + } + + return -EINVAL; +} + + static int mt9v011_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { struct mt9v011 *core = to_mt9v011(sd); @@ -294,6 +340,22 @@ static int mt9v011_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) return 0; } +static int mt9v011_s_config(struct v4l2_subdev *sd, int dumb, void *data) +{ + struct mt9v011 *core = to_mt9v011(sd); + unsigned *xtal = data; + + v4l2_dbg(1, debug, sd, "s_config called\n"); + + if (xtal) { + core->xtal = *xtal; + v4l2_dbg(1, debug, sd, "xtal set to %d.%03d MHz\n", + *xtal / 1000000, (*xtal / 1000) % 1000); + } + + return 0; +} + #ifdef CONFIG_VIDEO_ADV_DEBUG static int mt9v011_g_register(struct v4l2_subdev *sd, @@ -338,9 +400,11 @@ static int mt9v011_g_chip_ident(struct v4l2_subdev *sd, } static const struct v4l2_subdev_core_ops mt9v011_core_ops = { + .queryctrl = mt9v011_queryctrl, .g_ctrl = mt9v011_g_ctrl, .s_ctrl = mt9v011_s_ctrl, .reset = mt9v011_reset, + .s_config = mt9v011_s_config, .g_chip_ident = mt9v011_g_chip_ident, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = mt9v011_g_register, @@ -395,6 +459,7 @@ static int mt9v011_probe(struct i2c_client *c, core->global_gain = 0x0024; core->width = 640; core->height = 480; + core->xtal = 27000000; /* Hz */ v4l_info(c, "chip found @ 0x%02x (%s)\n", c->addr << 1, c->adapter->name); diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c index bae61b22501..7d430835655 100644 --- a/drivers/mfd/twl4030-irq.c +++ b/drivers/mfd/twl4030-irq.c @@ -180,14 +180,9 @@ static struct completion irq_event; static int twl4030_irq_thread(void *data) { long irq = (long)data; - struct irq_desc *desc = irq_to_desc(irq); static unsigned i2c_errors; static const unsigned max_i2c_errors = 100; - if (!desc) { - pr_err("twl4030: Invalid IRQ: %ld\n", irq); - return -EINVAL; - } current->flags |= PF_NOFREEZE; @@ -240,7 +235,7 @@ static int twl4030_irq_thread(void *data) } local_irq_enable(); - desc->chip->unmask(irq); + enable_irq(irq); } return 0; @@ -255,25 +250,13 @@ static int twl4030_irq_thread(void *data) * thread. All we do here is acknowledge and mask the interrupt and wakeup * the kernel thread. */ -static void handle_twl4030_pih(unsigned int irq, struct irq_desc *desc) +static irqreturn_t handle_twl4030_pih(int irq, void *devid) { /* Acknowledge, clear *AND* mask the interrupt... */ - desc->chip->ack(irq); - complete(&irq_event); -} - -static struct task_struct *start_twl4030_irq_thread(long irq) -{ - struct task_struct *thread; - - init_completion(&irq_event); - thread = kthread_run(twl4030_irq_thread, (void *)irq, "twl4030-irq"); - if (!thread) - pr_err("twl4030: could not create irq %ld thread!\n", irq); - - return thread; + disable_irq_nosync(irq); + complete(devid); + return IRQ_HANDLED; } - /*----------------------------------------------------------------------*/ /* @@ -734,18 +717,28 @@ int twl_init_irq(int irq_num, unsigned irq_base, unsigned irq_end) } /* install an irq handler to demultiplex the TWL4030 interrupt */ - task = start_twl4030_irq_thread(irq_num); - if (!task) { - pr_err("twl4030: irq thread FAIL\n"); - status = -ESRCH; - goto fail; - } - set_irq_data(irq_num, task); - set_irq_chained_handler(irq_num, handle_twl4030_pih); - return status; + init_completion(&irq_event); + status = request_irq(irq_num, handle_twl4030_pih, IRQF_DISABLED, + "TWL4030-PIH", &irq_event); + if (status < 0) { + pr_err("twl4030: could not claim irq%d: %d\n", irq_num, status); + goto fail_rqirq; + } + + task = kthread_run(twl4030_irq_thread, (void *)irq_num, "twl4030-irq"); + if (IS_ERR(task)) { + pr_err("twl4030: could not create irq %d thread!\n", irq_num); + status = PTR_ERR(task); + goto fail_kthread; + } + return status; +fail_kthread: + free_irq(irq_num, &irq_event); +fail_rqirq: + /* clean up twl4030_sih_setup */ fail: for (i = irq_base; i < irq_end; i++) set_irq_chip_and_handler(i, NULL, NULL); diff --git a/drivers/misc/cb710/sgbuf2.c b/drivers/misc/cb710/sgbuf2.c index d38a7acdb6e..d019746551f 100644 --- a/drivers/misc/cb710/sgbuf2.c +++ b/drivers/misc/cb710/sgbuf2.c @@ -114,7 +114,6 @@ static void sg_dwiter_write_slow(struct sg_mapping_iter *miter, uint32_t data) if (!left) return; addr += len; - flush_kernel_dcache_page(miter->page); } while (sg_dwiter_next(miter)); } @@ -142,9 +141,6 @@ void cb710_sg_dwiter_write_next_block(struct sg_mapping_iter *miter, uint32_t da return; } else sg_dwiter_write_slow(miter, data); - - if (miter->length == miter->consumed) - flush_kernel_dcache_page(miter->page); } EXPORT_SYMBOL_GPL(cb710_sg_dwiter_write_next_block); diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c index b34cb5f79ee..2e535a0ccd5 100644 --- a/drivers/misc/eeprom/at25.c +++ b/drivers/misc/eeprom/at25.c @@ -173,6 +173,7 @@ at25_ee_write(struct at25_data *at25, const char *buf, loff_t off, unsigned segment; unsigned offset = (unsigned) off; u8 *cp = bounce + 1; + int sr; *cp = AT25_WREN; status = spi_write(at25->spi, cp, 1); @@ -214,7 +215,6 @@ at25_ee_write(struct at25_data *at25, const char *buf, loff_t off, timeout = jiffies + msecs_to_jiffies(EE_TIMEOUT); retries = 0; do { - int sr; sr = spi_w8r8(at25->spi, AT25_RDSR); if (sr < 0 || (sr & AT25_SR_nRDY)) { @@ -228,7 +228,7 @@ at25_ee_write(struct at25_data *at25, const char *buf, loff_t off, break; } while (retries++ < 3 || time_before_eq(jiffies, timeout)); - if (time_after(jiffies, timeout)) { + if ((sr < 0) || (sr & AT25_SR_nRDY)) { dev_err(&at25->spi->dev, "write %d bytes offset %d, " "timeout after %u msecs\n", diff --git a/drivers/mmc/host/cb710-mmc.c b/drivers/mmc/host/cb710-mmc.c index 11efefb1af5..4e72964a7b4 100644 --- a/drivers/mmc/host/cb710-mmc.c +++ b/drivers/mmc/host/cb710-mmc.c @@ -278,7 +278,7 @@ static int cb710_mmc_receive(struct cb710_slot *slot, struct mmc_data *data) if (unlikely(data->blksz & 15 && (data->blocks != 1 || data->blksz != 8))) return -EINVAL; - sg_miter_start(&miter, data->sg, data->sg_len, 0); + sg_miter_start(&miter, data->sg, data->sg_len, SG_MITER_TO_SG); cb710_modify_port_8(slot, CB710_MMC_CONFIG2_PORT, 15, CB710_MMC_C2_READ_PIO_SIZE_MASK); @@ -307,7 +307,7 @@ static int cb710_mmc_receive(struct cb710_slot *slot, struct mmc_data *data) goto out; } out: - cb710_sg_miter_stop_writing(&miter); + sg_miter_stop(&miter); return err; } @@ -322,7 +322,7 @@ static int cb710_mmc_send(struct cb710_slot *slot, struct mmc_data *data) if (unlikely(data->blocks > 1 && data->blksz & 15)) return -EINVAL; - sg_miter_start(&miter, data->sg, data->sg_len, 0); + sg_miter_start(&miter, data->sg, data->sg_len, SG_MITER_FROM_SG); cb710_modify_port_8(slot, CB710_MMC_CONFIG2_PORT, 0, CB710_MMC_C2_READ_PIO_SIZE_MASK); diff --git a/drivers/mmc/host/imxmmc.c b/drivers/mmc/host/imxmmc.c index e0be21a4a69..bf98d7cc928 100644 --- a/drivers/mmc/host/imxmmc.c +++ b/drivers/mmc/host/imxmmc.c @@ -652,7 +652,7 @@ static irqreturn_t imxmci_irq(int irq, void *devid) set_bit(IMXMCI_PEND_STARTED_b, &host->pending_events); tasklet_schedule(&host->tasklet); - return IRQ_RETVAL(handled);; + return IRQ_RETVAL(handled); } static void imxmci_tasklet_fnc(unsigned long data) diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c index b56d72ff06e..34e23489811 100644 --- a/drivers/mmc/host/mvsdio.c +++ b/drivers/mmc/host/mvsdio.c @@ -384,7 +384,7 @@ static irqreturn_t mvsd_irq(int irq, void *dev) u16 val[2] = {0, 0}; val[0] = mvsd_read(MVSD_FIFO); val[1] = mvsd_read(MVSD_FIFO); - memcpy(p, &val, s); + memcpy(p, ((void *)&val) + 4 - s, s); s = 0; intr_status = mvsd_read(MVSD_NOR_INTR_STATUS); } @@ -423,7 +423,7 @@ static irqreturn_t mvsd_irq(int irq, void *dev) if (s < 4) { if (s && (intr_status & MVSD_NOR_TX_AVAIL)) { u16 val[2] = {0, 0}; - memcpy(&val, p, s); + memcpy(((void *)&val) + 4 - s, p, s); mvsd_write(MVSD_FIFO, val[0]); mvsd_write(MVSD_FIFO, val[1]); s = 0; diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index d7d7109ef47..e55ac792d68 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -168,12 +168,12 @@ static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data) if (data->flags & MMC_DATA_READ) { host->dma_dir = DMA_FROM_DEVICE; - dcmd = DCMD_INCTRGADDR | DCMD_FLOWTRG; + dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC; DRCMR(host->dma_drcmrtx) = 0; DRCMR(host->dma_drcmrrx) = host->dma | DRCMR_MAPVLD; } else { host->dma_dir = DMA_TO_DEVICE; - dcmd = DCMD_INCSRCADDR | DCMD_FLOWSRC; + dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG; DRCMR(host->dma_drcmrrx) = 0; DRCMR(host->dma_drcmrtx) = host->dma | DRCMR_MAPVLD; } diff --git a/drivers/mmc/host/sdhci-of.c b/drivers/mmc/host/sdhci-of.c index d79fa55c3b8..908844327db 100644 --- a/drivers/mmc/host/sdhci-of.c +++ b/drivers/mmc/host/sdhci-of.c @@ -158,6 +158,13 @@ static unsigned int esdhc_get_max_clock(struct sdhci_host *host) return of_host->clock; } +static unsigned int esdhc_get_min_clock(struct sdhci_host *host) +{ + struct sdhci_of_host *of_host = sdhci_priv(host); + + return of_host->clock / 256 / 16; +} + static unsigned int esdhc_get_timeout_clock(struct sdhci_host *host) { struct sdhci_of_host *of_host = sdhci_priv(host); @@ -184,6 +191,7 @@ static struct sdhci_of_data sdhci_esdhc = { .set_clock = esdhc_set_clock, .enable_dma = esdhc_enable_dma, .get_max_clock = esdhc_get_max_clock, + .get_min_clock = esdhc_get_min_clock, .get_timeout_clock = esdhc_get_timeout_clock, }, }; diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 6779b4ecab1..fc96f8cb9c0 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -773,8 +773,14 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) } if (!(host->flags & SDHCI_REQ_USE_DMA)) { - sg_miter_start(&host->sg_miter, - data->sg, data->sg_len, SG_MITER_ATOMIC); + int flags; + + flags = SG_MITER_ATOMIC; + if (host->data->flags & MMC_DATA_READ) + flags |= SG_MITER_TO_SG; + else + flags |= SG_MITER_FROM_SG; + sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags); host->blocks = data->blocks; } @@ -1766,7 +1772,10 @@ int sdhci_add_host(struct sdhci_host *host) * Set host parameters. */ mmc->ops = &sdhci_ops; - mmc->f_min = host->max_clk / 256; + if (host->ops->get_min_clock) + mmc->f_min = host->ops->get_min_clock(host); + else + mmc->f_min = host->max_clk / 256; mmc->f_max = host->max_clk; mmc->caps = MMC_CAP_SDIO_IRQ; diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 831ddf7dcb4..c77e9ff3022 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -302,6 +302,7 @@ struct sdhci_ops { int (*enable_dma)(struct sdhci_host *host); unsigned int (*get_max_clock)(struct sdhci_host *host); + unsigned int (*get_min_clock)(struct sdhci_host *host); unsigned int (*get_timeout_clock)(struct sdhci_host *host); }; diff --git a/drivers/mtd/ubi/gluebi.c b/drivers/mtd/ubi/gluebi.c index 95aaac03f93..b5e478fa266 100644 --- a/drivers/mtd/ubi/gluebi.c +++ b/drivers/mtd/ubi/gluebi.c @@ -332,6 +332,7 @@ static int gluebi_create(struct ubi_device_info *di, } gluebi->vol_id = vi->vol_id; + gluebi->ubi_num = vi->ubi_num; mtd->type = MTD_UBIVOLUME; if (!di->ro_mode) mtd->flags = MTD_WRITEABLE; diff --git a/drivers/mtd/ubi/scan.c b/drivers/mtd/ubi/scan.c index f60895ee0ae..a423131b617 100644 --- a/drivers/mtd/ubi/scan.c +++ b/drivers/mtd/ubi/scan.c @@ -781,7 +781,7 @@ static int process_eb(struct ubi_device *ubi, struct ubi_scan_info *si, return -EINVAL; } - image_seq = be32_to_cpu(ech->ec); + image_seq = be32_to_cpu(ech->image_seq); if (!si->image_seq_set) { ubi->image_seq = image_seq; si->image_seq_set = 1; diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index c155bd3ec9f..5f6509a5f64 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -1729,6 +1729,13 @@ config KS8842 help This platform driver is for Micrel KSZ8842 chip. +config KS8851 + tristate "Micrel KS8851 SPI" + depends on SPI + select MII + help + SPI driver for Micrel KS8851 SPI attached network chip. + config VIA_RHINE tristate "VIA Rhine support" depends on NET_PCI && PCI diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 4b58a59f211..ead8cab3cfe 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -88,6 +88,7 @@ obj-$(CONFIG_SKGE) += skge.o obj-$(CONFIG_SKY2) += sky2.o obj-$(CONFIG_SKFP) += skfp/ obj-$(CONFIG_KS8842) += ks8842.o +obj-$(CONFIG_KS8851) += ks8851.o obj-$(CONFIG_VIA_RHINE) += via-rhine.o obj-$(CONFIG_VIA_VELOCITY) += via-velocity.o obj-$(CONFIG_ADAPTEC_STARFIRE) += starfire.o diff --git a/drivers/net/arm/Kconfig b/drivers/net/arm/Kconfig index 2895db13bfa..c37ee9e6b67 100644 --- a/drivers/net/arm/Kconfig +++ b/drivers/net/arm/Kconfig @@ -63,3 +63,11 @@ config IXP4XX_ETH help Say Y here if you want to use built-in Ethernet ports on IXP4xx processor. + +config W90P910_ETH + tristate "Nuvoton w90p910 Ethernet support" + depends on ARM && ARCH_W90X900 + select PHYLIB + help + Say Y here if you want to use built-in Ethernet ports + on w90p910 processor. diff --git a/drivers/net/arm/Makefile b/drivers/net/arm/Makefile index 811a3ccd14c..303171f589e 100644 --- a/drivers/net/arm/Makefile +++ b/drivers/net/arm/Makefile @@ -11,3 +11,4 @@ obj-$(CONFIG_ARM_AT91_ETHER) += at91_ether.o obj-$(CONFIG_ARM_KS8695_ETHER) += ks8695net.o obj-$(CONFIG_EP93XX_ETH) += ep93xx_eth.o obj-$(CONFIG_IXP4XX_ETH) += ixp4xx_eth.o +obj-$(CONFIG_W90P910_ETH) += w90p910_ether.o diff --git a/drivers/net/arm/at91_ether.c b/drivers/net/arm/at91_ether.c index 2e7419a6119..5041d10bae9 100644 --- a/drivers/net/arm/at91_ether.c +++ b/drivers/net/arm/at91_ether.c @@ -1228,7 +1228,6 @@ static int at91ether_resume(struct platform_device *pdev) #endif static struct platform_driver at91ether_driver = { - .probe = at91ether_probe, .remove = __devexit_p(at91ether_remove), .suspend = at91ether_suspend, .resume = at91ether_resume, @@ -1240,7 +1239,7 @@ static struct platform_driver at91ether_driver = { static int __init at91ether_init(void) { - return platform_driver_register(&at91ether_driver); + return platform_driver_probe(&at91ether_driver, at91ether_probe); } static void __exit at91ether_exit(void) diff --git a/drivers/net/arm/w90p910_ether.c b/drivers/net/arm/w90p910_ether.c new file mode 100644 index 00000000000..616fb7985a3 --- /dev/null +++ b/drivers/net/arm/w90p910_ether.c @@ -0,0 +1,1105 @@ +/* + * Copyright (c) 2008-2009 Nuvoton technology corporation. + * + * Wan ZongShun <mcuos.com@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation;version 2 of the License. + * + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/mii.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/ethtool.h> +#include <linux/platform_device.h> +#include <linux/clk.h> + +#define DRV_MODULE_NAME "w90p910-emc" +#define DRV_MODULE_VERSION "0.1" + +/* Ethernet MAC Registers */ +#define REG_CAMCMR 0x00 +#define REG_CAMEN 0x04 +#define REG_CAMM_BASE 0x08 +#define REG_CAML_BASE 0x0c +#define REG_TXDLSA 0x88 +#define REG_RXDLSA 0x8C +#define REG_MCMDR 0x90 +#define REG_MIID 0x94 +#define REG_MIIDA 0x98 +#define REG_FFTCR 0x9C +#define REG_TSDR 0xa0 +#define REG_RSDR 0xa4 +#define REG_DMARFC 0xa8 +#define REG_MIEN 0xac +#define REG_MISTA 0xb0 +#define REG_CTXDSA 0xcc +#define REG_CTXBSA 0xd0 +#define REG_CRXDSA 0xd4 +#define REG_CRXBSA 0xd8 + +/* mac controller bit */ +#define MCMDR_RXON 0x01 +#define MCMDR_ACP (0x01 << 3) +#define MCMDR_SPCRC (0x01 << 5) +#define MCMDR_TXON (0x01 << 8) +#define MCMDR_FDUP (0x01 << 18) +#define MCMDR_ENMDC (0x01 << 19) +#define MCMDR_OPMOD (0x01 << 20) +#define SWR (0x01 << 24) + +/* cam command regiser */ +#define CAMCMR_AUP 0x01 +#define CAMCMR_AMP (0x01 << 1) +#define CAMCMR_ABP (0x01 << 2) +#define CAMCMR_CCAM (0x01 << 3) +#define CAMCMR_ECMP (0x01 << 4) +#define CAM0EN 0x01 + +/* mac mii controller bit */ +#define MDCCR (0x0a << 20) +#define PHYAD (0x01 << 8) +#define PHYWR (0x01 << 16) +#define PHYBUSY (0x01 << 17) +#define PHYPRESP (0x01 << 18) +#define CAM_ENTRY_SIZE 0x08 + +/* rx and tx status */ +#define TXDS_TXCP (0x01 << 19) +#define RXDS_CRCE (0x01 << 17) +#define RXDS_PTLE (0x01 << 19) +#define RXDS_RXGD (0x01 << 20) +#define RXDS_ALIE (0x01 << 21) +#define RXDS_RP (0x01 << 22) + +/* mac interrupt status*/ +#define MISTA_EXDEF (0x01 << 19) +#define MISTA_TXBERR (0x01 << 24) +#define MISTA_TDU (0x01 << 23) +#define MISTA_RDU (0x01 << 10) +#define MISTA_RXBERR (0x01 << 11) + +#define ENSTART 0x01 +#define ENRXINTR 0x01 +#define ENRXGD (0x01 << 4) +#define ENRXBERR (0x01 << 11) +#define ENTXINTR (0x01 << 16) +#define ENTXCP (0x01 << 18) +#define ENTXABT (0x01 << 21) +#define ENTXBERR (0x01 << 24) +#define ENMDC (0x01 << 19) +#define PHYBUSY (0x01 << 17) +#define MDCCR_VAL 0xa00000 + +/* rx and tx owner bit */ +#define RX_OWEN_DMA (0x01 << 31) +#define RX_OWEN_CPU (~(0x03 << 30)) +#define TX_OWEN_DMA (0x01 << 31) +#define TX_OWEN_CPU (~(0x01 << 31)) + +/* tx frame desc controller bit */ +#define MACTXINTEN 0x04 +#define CRCMODE 0x02 +#define PADDINGMODE 0x01 + +/* fftcr controller bit */ +#define TXTHD (0x03 << 8) +#define BLENGTH (0x01 << 20) + +/* global setting for driver */ +#define RX_DESC_SIZE 50 +#define TX_DESC_SIZE 10 +#define MAX_RBUFF_SZ 0x600 +#define MAX_TBUFF_SZ 0x600 +#define TX_TIMEOUT 50 +#define DELAY 1000 +#define CAM0 0x0 + +static int w90p910_mdio_read(struct net_device *dev, int phy_id, int reg); + +struct w90p910_rxbd { + unsigned int sl; + unsigned int buffer; + unsigned int reserved; + unsigned int next; +}; + +struct w90p910_txbd { + unsigned int mode; + unsigned int buffer; + unsigned int sl; + unsigned int next; +}; + +struct recv_pdesc { + struct w90p910_rxbd desclist[RX_DESC_SIZE]; + char recv_buf[RX_DESC_SIZE][MAX_RBUFF_SZ]; +}; + +struct tran_pdesc { + struct w90p910_txbd desclist[TX_DESC_SIZE]; + char tran_buf[RX_DESC_SIZE][MAX_TBUFF_SZ]; +}; + +struct w90p910_ether { + struct recv_pdesc *rdesc; + struct recv_pdesc *rdesc_phys; + struct tran_pdesc *tdesc; + struct tran_pdesc *tdesc_phys; + struct net_device_stats stats; + struct platform_device *pdev; + struct sk_buff *skb; + struct clk *clk; + struct clk *rmiiclk; + struct mii_if_info mii; + struct timer_list check_timer; + void __iomem *reg; + unsigned int rxirq; + unsigned int txirq; + unsigned int cur_tx; + unsigned int cur_rx; + unsigned int finish_tx; + unsigned int rx_packets; + unsigned int rx_bytes; + unsigned int start_tx_ptr; + unsigned int start_rx_ptr; + unsigned int linkflag; + spinlock_t lock; +}; + +static void update_linkspeed_register(struct net_device *dev, + unsigned int speed, unsigned int duplex) +{ + struct w90p910_ether *ether = netdev_priv(dev); + unsigned int val; + + val = __raw_readl(ether->reg + REG_MCMDR); + + if (speed == SPEED_100) { + /* 100 full/half duplex */ + if (duplex == DUPLEX_FULL) { + val |= (MCMDR_OPMOD | MCMDR_FDUP); + } else { + val |= MCMDR_OPMOD; + val &= ~MCMDR_FDUP; + } + } else { + /* 10 full/half duplex */ + if (duplex == DUPLEX_FULL) { + val |= MCMDR_FDUP; + val &= ~MCMDR_OPMOD; + } else { + val &= ~(MCMDR_FDUP | MCMDR_OPMOD); + } + } + + __raw_writel(val, ether->reg + REG_MCMDR); +} + +static void update_linkspeed(struct net_device *dev) +{ + struct w90p910_ether *ether = netdev_priv(dev); + struct platform_device *pdev; + unsigned int bmsr, bmcr, lpa, speed, duplex; + + pdev = ether->pdev; + + if (!mii_link_ok(ðer->mii)) { + ether->linkflag = 0x0; + netif_carrier_off(dev); + dev_warn(&pdev->dev, "%s: Link down.\n", dev->name); + return; + } + + if (ether->linkflag == 1) + return; + + bmsr = w90p910_mdio_read(dev, ether->mii.phy_id, MII_BMSR); + bmcr = w90p910_mdio_read(dev, ether->mii.phy_id, MII_BMCR); + + if (bmcr & BMCR_ANENABLE) { + if (!(bmsr & BMSR_ANEGCOMPLETE)) + return; + + lpa = w90p910_mdio_read(dev, ether->mii.phy_id, MII_LPA); + + if ((lpa & LPA_100FULL) || (lpa & LPA_100HALF)) + speed = SPEED_100; + else + speed = SPEED_10; + + if ((lpa & LPA_100FULL) || (lpa & LPA_10FULL)) + duplex = DUPLEX_FULL; + else + duplex = DUPLEX_HALF; + + } else { + speed = (bmcr & BMCR_SPEED100) ? SPEED_100 : SPEED_10; + duplex = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF; + } + + update_linkspeed_register(dev, speed, duplex); + + dev_info(&pdev->dev, "%s: Link now %i-%s\n", dev->name, speed, + (duplex == DUPLEX_FULL) ? "FullDuplex" : "HalfDuplex"); + ether->linkflag = 0x01; + + netif_carrier_on(dev); +} + +static void w90p910_check_link(unsigned long dev_id) +{ + struct net_device *dev = (struct net_device *) dev_id; + struct w90p910_ether *ether = netdev_priv(dev); + + update_linkspeed(dev); + mod_timer(ðer->check_timer, jiffies + msecs_to_jiffies(1000)); +} + +static void w90p910_write_cam(struct net_device *dev, + unsigned int x, unsigned char *pval) +{ + struct w90p910_ether *ether = netdev_priv(dev); + unsigned int msw, lsw; + + msw = (pval[0] << 24) | (pval[1] << 16) | (pval[2] << 8) | pval[3]; + + lsw = (pval[4] << 24) | (pval[5] << 16); + + __raw_writel(lsw, ether->reg + REG_CAML_BASE + x * CAM_ENTRY_SIZE); + __raw_writel(msw, ether->reg + REG_CAMM_BASE + x * CAM_ENTRY_SIZE); +} + +static void w90p910_init_desc(struct net_device *dev) +{ + struct w90p910_ether *ether; + struct w90p910_txbd *tdesc, *tdesc_phys; + struct w90p910_rxbd *rdesc, *rdesc_phys; + unsigned int i, j; + + ether = netdev_priv(dev); + + ether->tdesc = (struct tran_pdesc *) + dma_alloc_coherent(NULL, sizeof(struct tran_pdesc), + (dma_addr_t *) ðer->tdesc_phys, GFP_KERNEL); + + ether->rdesc = (struct recv_pdesc *) + dma_alloc_coherent(NULL, sizeof(struct recv_pdesc), + (dma_addr_t *) ðer->rdesc_phys, GFP_KERNEL); + + for (i = 0; i < TX_DESC_SIZE; i++) { + tdesc = &(ether->tdesc->desclist[i]); + + j = ((i + 1) / TX_DESC_SIZE); + + if (j != 0) { + tdesc_phys = &(ether->tdesc_phys->desclist[0]); + ether->start_tx_ptr = (unsigned int)tdesc_phys; + tdesc->next = (unsigned int)ether->start_tx_ptr; + } else { + tdesc_phys = &(ether->tdesc_phys->desclist[i+1]); + tdesc->next = (unsigned int)tdesc_phys; + } + + tdesc->buffer = (unsigned int)ether->tdesc_phys->tran_buf[i]; + tdesc->sl = 0; + tdesc->mode = 0; + } + + for (i = 0; i < RX_DESC_SIZE; i++) { + rdesc = &(ether->rdesc->desclist[i]); + + j = ((i + 1) / RX_DESC_SIZE); + + if (j != 0) { + rdesc_phys = &(ether->rdesc_phys->desclist[0]); + ether->start_rx_ptr = (unsigned int)rdesc_phys; + rdesc->next = (unsigned int)ether->start_rx_ptr; + } else { + rdesc_phys = &(ether->rdesc_phys->desclist[i+1]); + rdesc->next = (unsigned int)rdesc_phys; + } + + rdesc->sl = RX_OWEN_DMA; + rdesc->buffer = (unsigned int)ether->rdesc_phys->recv_buf[i]; + } +} + +static void w90p910_set_fifo_threshold(struct net_device *dev) +{ + struct w90p910_ether *ether = netdev_priv(dev); + unsigned int val; + + val = TXTHD | BLENGTH; + __raw_writel(val, ether->reg + REG_FFTCR); +} + +static void w90p910_return_default_idle(struct net_device *dev) +{ + struct w90p910_ether *ether = netdev_priv(dev); + unsigned int val; + + val = __raw_readl(ether->reg + REG_MCMDR); + val |= SWR; + __raw_writel(val, ether->reg + REG_MCMDR); +} + +static void w90p910_trigger_rx(struct net_device *dev) +{ + struct w90p910_ether *ether = netdev_priv(dev); + + __raw_writel(ENSTART, ether->reg + REG_RSDR); +} + +static void w90p910_trigger_tx(struct net_device *dev) +{ + struct w90p910_ether *ether = netdev_priv(dev); + + __raw_writel(ENSTART, ether->reg + REG_TSDR); +} + +static void w90p910_enable_mac_interrupt(struct net_device *dev) +{ + struct w90p910_ether *ether = netdev_priv(dev); + unsigned int val; + + val = ENTXINTR | ENRXINTR | ENRXGD | ENTXCP; + val |= ENTXBERR | ENRXBERR | ENTXABT; + + __raw_writel(val, ether->reg + REG_MIEN); +} + +static void w90p910_get_and_clear_int(struct net_device *dev, + unsigned int *val) +{ + struct w90p910_ether *ether = netdev_priv(dev); + + *val = __raw_readl(ether->reg + REG_MISTA); + __raw_writel(*val, ether->reg + REG_MISTA); +} + +static void w90p910_set_global_maccmd(struct net_device *dev) +{ + struct w90p910_ether *ether = netdev_priv(dev); + unsigned int val; + + val = __raw_readl(ether->reg + REG_MCMDR); + val |= MCMDR_SPCRC | MCMDR_ENMDC | MCMDR_ACP | ENMDC; + __raw_writel(val, ether->reg + REG_MCMDR); +} + +static void w90p910_enable_cam(struct net_device *dev) +{ + struct w90p910_ether *ether = netdev_priv(dev); + unsigned int val; + + w90p910_write_cam(dev, CAM0, dev->dev_addr); + + val = __raw_readl(ether->reg + REG_CAMEN); + val |= CAM0EN; + __raw_writel(val, ether->reg + REG_CAMEN); +} + +static void w90p910_enable_cam_command(struct net_device *dev) +{ + struct w90p910_ether *ether = netdev_priv(dev); + unsigned int val; + + val = CAMCMR_ECMP | CAMCMR_ABP | CAMCMR_AMP; + __raw_writel(val, ether->reg + REG_CAMCMR); +} + +static void w90p910_enable_tx(struct net_device *dev, unsigned int enable) +{ + struct w90p910_ether *ether = netdev_priv(dev); + unsigned int val; + + val = __raw_readl(ether->reg + REG_MCMDR); + + if (enable) + val |= MCMDR_TXON; + else + val &= ~MCMDR_TXON; + + __raw_writel(val, ether->reg + REG_MCMDR); +} + +static void w90p910_enable_rx(struct net_device *dev, unsigned int enable) +{ + struct w90p910_ether *ether = netdev_priv(dev); + unsigned int val; + + val = __raw_readl(ether->reg + REG_MCMDR); + + if (enable) + val |= MCMDR_RXON; + else + val &= ~MCMDR_RXON; + + __raw_writel(val, ether->reg + REG_MCMDR); +} + +static void w90p910_set_curdest(struct net_device *dev) +{ + struct w90p910_ether *ether = netdev_priv(dev); + + __raw_writel(ether->start_rx_ptr, ether->reg + REG_RXDLSA); + __raw_writel(ether->start_tx_ptr, ether->reg + REG_TXDLSA); +} + +static void w90p910_reset_mac(struct net_device *dev) +{ + struct w90p910_ether *ether = netdev_priv(dev); + + spin_lock(ðer->lock); + + w90p910_enable_tx(dev, 0); + w90p910_enable_rx(dev, 0); + w90p910_set_fifo_threshold(dev); + w90p910_return_default_idle(dev); + + if (!netif_queue_stopped(dev)) + netif_stop_queue(dev); + + w90p910_init_desc(dev); + + dev->trans_start = jiffies; + ether->cur_tx = 0x0; + ether->finish_tx = 0x0; + ether->cur_rx = 0x0; + + w90p910_set_curdest(dev); + w90p910_enable_cam(dev); + w90p910_enable_cam_command(dev); + w90p910_enable_mac_interrupt(dev); + w90p910_enable_tx(dev, 1); + w90p910_enable_rx(dev, 1); + w90p910_trigger_tx(dev); + w90p910_trigger_rx(dev); + + dev->trans_start = jiffies; + + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); + + spin_unlock(ðer->lock); +} + +static void w90p910_mdio_write(struct net_device *dev, + int phy_id, int reg, int data) +{ + struct w90p910_ether *ether = netdev_priv(dev); + struct platform_device *pdev; + unsigned int val, i; + + pdev = ether->pdev; + + __raw_writel(data, ether->reg + REG_MIID); + + val = (phy_id << 0x08) | reg; + val |= PHYBUSY | PHYWR | MDCCR_VAL; + __raw_writel(val, ether->reg + REG_MIIDA); + + for (i = 0; i < DELAY; i++) { + if ((__raw_readl(ether->reg + REG_MIIDA) & PHYBUSY) == 0) + break; + } + + if (i == DELAY) + dev_warn(&pdev->dev, "mdio write timed out\n"); +} + +static int w90p910_mdio_read(struct net_device *dev, int phy_id, int reg) +{ + struct w90p910_ether *ether = netdev_priv(dev); + struct platform_device *pdev; + unsigned int val, i, data; + + pdev = ether->pdev; + + val = (phy_id << 0x08) | reg; + val |= PHYBUSY | MDCCR_VAL; + __raw_writel(val, ether->reg + REG_MIIDA); + + for (i = 0; i < DELAY; i++) { + if ((__raw_readl(ether->reg + REG_MIIDA) & PHYBUSY) == 0) + break; + } + + if (i == DELAY) { + dev_warn(&pdev->dev, "mdio read timed out\n"); + data = 0xffff; + } else { + data = __raw_readl(ether->reg + REG_MIID); + } + + return data; +} + +static int set_mac_address(struct net_device *dev, void *addr) +{ + struct sockaddr *address = addr; + + if (!is_valid_ether_addr(address->sa_data)) + return -EADDRNOTAVAIL; + + memcpy(dev->dev_addr, address->sa_data, dev->addr_len); + w90p910_write_cam(dev, CAM0, dev->dev_addr); + + return 0; +} + +static int w90p910_ether_close(struct net_device *dev) +{ + struct w90p910_ether *ether = netdev_priv(dev); + + dma_free_writecombine(NULL, sizeof(struct w90p910_rxbd), + ether->rdesc, (dma_addr_t)ether->rdesc_phys); + dma_free_writecombine(NULL, sizeof(struct w90p910_txbd), + ether->tdesc, (dma_addr_t)ether->tdesc_phys); + + netif_stop_queue(dev); + + del_timer_sync(ðer->check_timer); + clk_disable(ether->rmiiclk); + clk_disable(ether->clk); + + free_irq(ether->txirq, dev); + free_irq(ether->rxirq, dev); + + return 0; +} + +static struct net_device_stats *w90p910_ether_stats(struct net_device *dev) +{ + struct w90p910_ether *ether; + + ether = netdev_priv(dev); + + return ðer->stats; +} + +static int w90p910_send_frame(struct net_device *dev, + unsigned char *data, int length) +{ + struct w90p910_ether *ether; + struct w90p910_txbd *txbd; + struct platform_device *pdev; + unsigned char *buffer; + + ether = netdev_priv(dev); + pdev = ether->pdev; + + txbd = ðer->tdesc->desclist[ether->cur_tx]; + buffer = ether->tdesc->tran_buf[ether->cur_tx]; + if (length > 1514) { + dev_err(&pdev->dev, "send data %d bytes, check it\n", length); + length = 1514; + } + + txbd->sl = length & 0xFFFF; + + memcpy(buffer, data, length); + + txbd->mode = TX_OWEN_DMA | PADDINGMODE | CRCMODE | MACTXINTEN; + + w90p910_enable_tx(dev, 1); + + w90p910_trigger_tx(dev); + + ether->cur_tx = (ether->cur_tx+1) % TX_DESC_SIZE; + txbd = ðer->tdesc->desclist[ether->cur_tx]; + + dev->trans_start = jiffies; + + if (txbd->mode & TX_OWEN_DMA) + netif_stop_queue(dev); + + return 0; +} + +static int w90p910_ether_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct w90p910_ether *ether = netdev_priv(dev); + + if (!(w90p910_send_frame(dev, skb->data, skb->len))) { + ether->skb = skb; + dev_kfree_skb_irq(skb); + return 0; + } + return -1; +} + +static irqreturn_t w90p910_tx_interrupt(int irq, void *dev_id) +{ + struct w90p910_ether *ether; + struct w90p910_txbd *txbd; + struct platform_device *pdev; + struct tran_pdesc *tran_pdesc; + struct net_device *dev; + unsigned int cur_entry, entry, status; + + dev = (struct net_device *)dev_id; + ether = netdev_priv(dev); + pdev = ether->pdev; + + spin_lock(ðer->lock); + + w90p910_get_and_clear_int(dev, &status); + + cur_entry = __raw_readl(ether->reg + REG_CTXDSA); + + tran_pdesc = ether->tdesc_phys; + entry = (unsigned int)(&tran_pdesc->desclist[ether->finish_tx]); + + while (entry != cur_entry) { + txbd = ðer->tdesc->desclist[ether->finish_tx]; + + ether->finish_tx = (ether->finish_tx + 1) % TX_DESC_SIZE; + + if (txbd->sl & TXDS_TXCP) { + ether->stats.tx_packets++; + ether->stats.tx_bytes += txbd->sl & 0xFFFF; + } else { + ether->stats.tx_errors++; + } + + txbd->sl = 0x0; + txbd->mode = 0x0; + + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); + + entry = (unsigned int)(&tran_pdesc->desclist[ether->finish_tx]); + } + + if (status & MISTA_EXDEF) { + dev_err(&pdev->dev, "emc defer exceed interrupt\n"); + } else if (status & MISTA_TXBERR) { + dev_err(&pdev->dev, "emc bus error interrupt\n"); + w90p910_reset_mac(dev); + } else if (status & MISTA_TDU) { + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); + } + + spin_unlock(ðer->lock); + + return IRQ_HANDLED; +} + +static void netdev_rx(struct net_device *dev) +{ + struct w90p910_ether *ether; + struct w90p910_rxbd *rxbd; + struct platform_device *pdev; + struct recv_pdesc *rdesc_phys; + struct sk_buff *skb; + unsigned char *data; + unsigned int length, status, val, entry; + + ether = netdev_priv(dev); + pdev = ether->pdev; + rdesc_phys = ether->rdesc_phys; + + rxbd = ðer->rdesc->desclist[ether->cur_rx]; + + do { + val = __raw_readl(ether->reg + REG_CRXDSA); + entry = (unsigned int)&rdesc_phys->desclist[ether->cur_rx]; + + if (val == entry) + break; + + status = rxbd->sl; + length = status & 0xFFFF; + + if (status & RXDS_RXGD) { + data = ether->rdesc->recv_buf[ether->cur_rx]; + skb = dev_alloc_skb(length+2); + if (!skb) { + dev_err(&pdev->dev, "get skb buffer error\n"); + ether->stats.rx_dropped++; + return; + } + + skb->dev = dev; + skb_reserve(skb, 2); + skb_put(skb, length); + skb_copy_to_linear_data(skb, data, length); + skb->protocol = eth_type_trans(skb, dev); + ether->stats.rx_packets++; + ether->stats.rx_bytes += length; + netif_rx(skb); + } else { + ether->stats.rx_errors++; + + if (status & RXDS_RP) { + dev_err(&pdev->dev, "rx runt err\n"); + ether->stats.rx_length_errors++; + } else if (status & RXDS_CRCE) { + dev_err(&pdev->dev, "rx crc err\n"); + ether->stats.rx_crc_errors++; + } + + if (status & RXDS_ALIE) { + dev_err(&pdev->dev, "rx aligment err\n"); + ether->stats.rx_frame_errors++; + } else if (status & RXDS_PTLE) { + dev_err(&pdev->dev, "rx longer err\n"); + ether->stats.rx_over_errors++; + } + } + + rxbd->sl = RX_OWEN_DMA; + rxbd->reserved = 0x0; + ether->cur_rx = (ether->cur_rx+1) % RX_DESC_SIZE; + rxbd = ðer->rdesc->desclist[ether->cur_rx]; + + dev->last_rx = jiffies; + } while (1); +} + +static irqreturn_t w90p910_rx_interrupt(int irq, void *dev_id) +{ + struct net_device *dev; + struct w90p910_ether *ether; + struct platform_device *pdev; + unsigned int status; + + dev = (struct net_device *)dev_id; + ether = netdev_priv(dev); + pdev = ether->pdev; + + spin_lock(ðer->lock); + + w90p910_get_and_clear_int(dev, &status); + + if (status & MISTA_RDU) { + netdev_rx(dev); + + w90p910_trigger_rx(dev); + + spin_unlock(ðer->lock); + return IRQ_HANDLED; + } else if (status & MISTA_RXBERR) { + dev_err(&pdev->dev, "emc rx bus error\n"); + w90p910_reset_mac(dev); + } + + netdev_rx(dev); + spin_unlock(ðer->lock); + return IRQ_HANDLED; +} + +static int w90p910_ether_open(struct net_device *dev) +{ + struct w90p910_ether *ether; + struct platform_device *pdev; + + ether = netdev_priv(dev); + pdev = ether->pdev; + + w90p910_reset_mac(dev); + w90p910_set_fifo_threshold(dev); + w90p910_set_curdest(dev); + w90p910_enable_cam(dev); + w90p910_enable_cam_command(dev); + w90p910_enable_mac_interrupt(dev); + w90p910_set_global_maccmd(dev); + w90p910_enable_rx(dev, 1); + + ether->rx_packets = 0x0; + ether->rx_bytes = 0x0; + + if (request_irq(ether->txirq, w90p910_tx_interrupt, + 0x0, pdev->name, dev)) { + dev_err(&pdev->dev, "register irq tx failed\n"); + return -EAGAIN; + } + + if (request_irq(ether->rxirq, w90p910_rx_interrupt, + 0x0, pdev->name, dev)) { + dev_err(&pdev->dev, "register irq rx failed\n"); + return -EAGAIN; + } + + mod_timer(ðer->check_timer, jiffies + msecs_to_jiffies(1000)); + netif_start_queue(dev); + w90p910_trigger_rx(dev); + + dev_info(&pdev->dev, "%s is OPENED\n", dev->name); + + return 0; +} + +static void w90p910_ether_set_multicast_list(struct net_device *dev) +{ + struct w90p910_ether *ether; + unsigned int rx_mode; + + ether = netdev_priv(dev); + + if (dev->flags & IFF_PROMISC) + rx_mode = CAMCMR_AUP | CAMCMR_AMP | CAMCMR_ABP | CAMCMR_ECMP; + else if ((dev->flags & IFF_ALLMULTI) || dev->mc_list) + rx_mode = CAMCMR_AMP | CAMCMR_ABP | CAMCMR_ECMP; + else + rx_mode = CAMCMR_ECMP | CAMCMR_ABP; + __raw_writel(rx_mode, ether->reg + REG_CAMCMR); +} + +static int w90p910_ether_ioctl(struct net_device *dev, + struct ifreq *ifr, int cmd) +{ + struct w90p910_ether *ether = netdev_priv(dev); + struct mii_ioctl_data *data = if_mii(ifr); + + return generic_mii_ioctl(ðer->mii, data, cmd, NULL); +} + +static void w90p910_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + strcpy(info->driver, DRV_MODULE_NAME); + strcpy(info->version, DRV_MODULE_VERSION); +} + +static int w90p910_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct w90p910_ether *ether = netdev_priv(dev); + return mii_ethtool_gset(ðer->mii, cmd); +} + +static int w90p910_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct w90p910_ether *ether = netdev_priv(dev); + return mii_ethtool_sset(ðer->mii, cmd); +} + +static int w90p910_nway_reset(struct net_device *dev) +{ + struct w90p910_ether *ether = netdev_priv(dev); + return mii_nway_restart(ðer->mii); +} + +static u32 w90p910_get_link(struct net_device *dev) +{ + struct w90p910_ether *ether = netdev_priv(dev); + return mii_link_ok(ðer->mii); +} + +static const struct ethtool_ops w90p910_ether_ethtool_ops = { + .get_settings = w90p910_get_settings, + .set_settings = w90p910_set_settings, + .get_drvinfo = w90p910_get_drvinfo, + .nway_reset = w90p910_nway_reset, + .get_link = w90p910_get_link, +}; + +static const struct net_device_ops w90p910_ether_netdev_ops = { + .ndo_open = w90p910_ether_open, + .ndo_stop = w90p910_ether_close, + .ndo_start_xmit = w90p910_ether_start_xmit, + .ndo_get_stats = w90p910_ether_stats, + .ndo_set_multicast_list = w90p910_ether_set_multicast_list, + .ndo_set_mac_address = set_mac_address, + .ndo_do_ioctl = w90p910_ether_ioctl, + .ndo_validate_addr = eth_validate_addr, + .ndo_change_mtu = eth_change_mtu, +}; + +static void __init get_mac_address(struct net_device *dev) +{ + struct w90p910_ether *ether = netdev_priv(dev); + struct platform_device *pdev; + char addr[6]; + + pdev = ether->pdev; + + addr[0] = 0x00; + addr[1] = 0x02; + addr[2] = 0xac; + addr[3] = 0x55; + addr[4] = 0x88; + addr[5] = 0xa8; + + if (is_valid_ether_addr(addr)) + memcpy(dev->dev_addr, &addr, 0x06); + else + dev_err(&pdev->dev, "invalid mac address\n"); +} + +static int w90p910_ether_setup(struct net_device *dev) +{ + struct w90p910_ether *ether = netdev_priv(dev); + + ether_setup(dev); + dev->netdev_ops = &w90p910_ether_netdev_ops; + dev->ethtool_ops = &w90p910_ether_ethtool_ops; + + dev->tx_queue_len = 16; + dev->dma = 0x0; + dev->watchdog_timeo = TX_TIMEOUT; + + get_mac_address(dev); + + spin_lock_init(ðer->lock); + + ether->cur_tx = 0x0; + ether->cur_rx = 0x0; + ether->finish_tx = 0x0; + ether->linkflag = 0x0; + ether->mii.phy_id = 0x01; + ether->mii.phy_id_mask = 0x1f; + ether->mii.reg_num_mask = 0x1f; + ether->mii.dev = dev; + ether->mii.mdio_read = w90p910_mdio_read; + ether->mii.mdio_write = w90p910_mdio_write; + + setup_timer(ðer->check_timer, w90p910_check_link, + (unsigned long)dev); + + return 0; +} + +static int __devinit w90p910_ether_probe(struct platform_device *pdev) +{ + struct w90p910_ether *ether; + struct net_device *dev; + struct resource *res; + int error; + + dev = alloc_etherdev(sizeof(struct w90p910_ether)); + if (!dev) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "failed to get I/O memory\n"); + error = -ENXIO; + goto failed_free; + } + + res = request_mem_region(res->start, resource_size(res), pdev->name); + if (res == NULL) { + dev_err(&pdev->dev, "failed to request I/O memory\n"); + error = -EBUSY; + goto failed_free; + } + + ether = netdev_priv(dev); + + ether->reg = ioremap(res->start, resource_size(res)); + if (ether->reg == NULL) { + dev_err(&pdev->dev, "failed to remap I/O memory\n"); + error = -ENXIO; + goto failed_free_mem; + } + + ether->txirq = platform_get_irq(pdev, 0); + if (ether->txirq < 0) { + dev_err(&pdev->dev, "failed to get ether tx irq\n"); + error = -ENXIO; + goto failed_free_io; + } + + ether->rxirq = platform_get_irq(pdev, 1); + if (ether->rxirq < 0) { + dev_err(&pdev->dev, "failed to get ether rx irq\n"); + error = -ENXIO; + goto failed_free_txirq; + } + + platform_set_drvdata(pdev, dev); + + ether->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(ether->clk)) { + dev_err(&pdev->dev, "failed to get ether clock\n"); + error = PTR_ERR(ether->clk); + goto failed_free_rxirq; + } + + ether->rmiiclk = clk_get(&pdev->dev, "RMII"); + if (IS_ERR(ether->rmiiclk)) { + dev_err(&pdev->dev, "failed to get ether clock\n"); + error = PTR_ERR(ether->rmiiclk); + goto failed_put_clk; + } + + ether->pdev = pdev; + + w90p910_ether_setup(dev); + + error = register_netdev(dev); + if (error != 0) { + dev_err(&pdev->dev, "Regiter EMC w90p910 FAILED\n"); + error = -ENODEV; + goto failed_put_rmiiclk; + } + + return 0; +failed_put_rmiiclk: + clk_put(ether->rmiiclk); +failed_put_clk: + clk_put(ether->clk); +failed_free_rxirq: + free_irq(ether->rxirq, pdev); + platform_set_drvdata(pdev, NULL); +failed_free_txirq: + free_irq(ether->txirq, pdev); +failed_free_io: + iounmap(ether->reg); +failed_free_mem: + release_mem_region(res->start, resource_size(res)); +failed_free: + free_netdev(dev); + return error; +} + +static int __devexit w90p910_ether_remove(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct w90p910_ether *ether = netdev_priv(dev); + + unregister_netdev(dev); + clk_put(ether->rmiiclk); + clk_put(ether->clk); + del_timer_sync(ðer->check_timer); + platform_set_drvdata(pdev, NULL); + free_netdev(dev); + return 0; +} + +static struct platform_driver w90p910_ether_driver = { + .probe = w90p910_ether_probe, + .remove = __devexit_p(w90p910_ether_remove), + .driver = { + .name = "w90p910-emc", + .owner = THIS_MODULE, + }, +}; + +static int __init w90p910_ether_init(void) +{ + return platform_driver_register(&w90p910_ether_driver); +} + +static void __exit w90p910_ether_exit(void) +{ + platform_driver_unregister(&w90p910_ether_driver); +} + +module_init(w90p910_ether_init); +module_exit(w90p910_ether_exit); + +MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>"); +MODULE_DESCRIPTION("w90p910 MAC driver!"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:w90p910-emc"); + diff --git a/drivers/net/at1700.c b/drivers/net/at1700.c index 18b566ad4fd..cf30e278f18 100644 --- a/drivers/net/at1700.c +++ b/drivers/net/at1700.c @@ -318,7 +318,7 @@ static int __init at1700_probe1(struct net_device *dev, int ioaddr) pos3 = mca_read_stored_pos( slot, 3 ); pos4 = mca_read_stored_pos( slot, 4 ); - for (l_i = 0; l_i < 0x09; l_i++) + for (l_i = 0; l_i < 8; l_i++) if (( pos3 & 0x07) == at1700_ioaddr_pattern[l_i]) break; ioaddr = at1700_mca_probe_list[l_i]; diff --git a/drivers/net/atl1c/atl1c.h b/drivers/net/atl1c/atl1c.h index e1658ef3fcd..2a1120ad2e7 100644 --- a/drivers/net/atl1c/atl1c.h +++ b/drivers/net/atl1c/atl1c.h @@ -188,14 +188,14 @@ struct atl1c_tpd_ext_desc { #define RRS_HDS_TYPE_DATA 2 #define RRS_IS_NO_HDS_TYPE(flag) \ - (((flag) >> (RRS_HDS_TYPE_SHIFT)) & RRS_HDS_TYPE_MASK == 0) + ((((flag) >> (RRS_HDS_TYPE_SHIFT)) & RRS_HDS_TYPE_MASK) == 0) #define RRS_IS_HDS_HEAD(flag) \ - (((flag) >> (RRS_HDS_TYPE_SHIFT)) & RRS_HDS_TYPE_MASK == \ + ((((flag) >> (RRS_HDS_TYPE_SHIFT)) & RRS_HDS_TYPE_MASK) == \ RRS_HDS_TYPE_HEAD) #define RRS_IS_HDS_DATA(flag) \ - (((flag) >> (RRS_HDS_TYPE_SHIFT)) & RRS_HDS_TYPE_MASK == \ + ((((flag) >> (RRS_HDS_TYPE_SHIFT)) & RRS_HDS_TYPE_MASK) == \ RRS_HDS_TYPE_DATA) /* rrs word 3 bit 0:31 */ @@ -245,7 +245,7 @@ struct atl1c_tpd_ext_desc { #define RRS_PACKET_TYPE_802_3 1 #define RRS_PACKET_TYPE_ETH 0 #define RRS_PACKET_IS_ETH(word) \ - (((word) >> RRS_PACKET_TYPE_SHIFT) & RRS_PACKET_TYPE_MASK == \ + ((((word) >> RRS_PACKET_TYPE_SHIFT) & RRS_PACKET_TYPE_MASK) == \ RRS_PACKET_TYPE_ETH) #define RRS_RXD_IS_VALID(word) \ ((((word) >> RRS_RXD_UPDATED_SHIFT) & RRS_RXD_UPDATED_MASK) == 1) diff --git a/drivers/net/atl1c/atl1c_main.c b/drivers/net/atl1c/atl1c_main.c index cd547a205fb..a383122679d 100644 --- a/drivers/net/atl1c/atl1c_main.c +++ b/drivers/net/atl1c/atl1c_main.c @@ -1689,7 +1689,7 @@ static void atl1c_clean_rx_irq(struct atl1c_adapter *adapter, u8 que, if (likely(RRS_RXD_IS_VALID(rrs->word3))) { rfd_num = (rrs->word0 >> RRS_RX_RFD_CNT_SHIFT) & RRS_RX_RFD_CNT_MASK; - if (unlikely(rfd_num) != 1) + if (unlikely(rfd_num != 1)) /* TODO support mul rfd*/ if (netif_msg_rx_err(adapter)) dev_warn(&pdev->dev, diff --git a/drivers/net/benet/be_main.c b/drivers/net/benet/be_main.c index c43f6a11929..dea3155688b 100644 --- a/drivers/net/benet/be_main.c +++ b/drivers/net/benet/be_main.c @@ -667,7 +667,7 @@ static void skb_fill_rx_data(struct be_adapter *adapter, struct be_queue_info *rxq = &adapter->rx_obj.q; struct be_rx_page_info *page_info; u16 rxq_idx, i, num_rcvd, j; - u32 pktsize, hdr_len, curr_frag_len; + u32 pktsize, hdr_len, curr_frag_len, size; u8 *start; rxq_idx = AMAP_GET_BITS(struct amap_eth_rx_compl, fragndx, rxcp); @@ -708,12 +708,13 @@ static void skb_fill_rx_data(struct be_adapter *adapter, } /* More frags present for this completion */ - pktsize -= curr_frag_len; /* account for above copied frag */ + size = pktsize; for (i = 1, j = 0; i < num_rcvd; i++) { + size -= curr_frag_len; index_inc(&rxq_idx, rxq->len); page_info = get_rx_page_info(adapter, rxq_idx); - curr_frag_len = min(pktsize, rx_frag_size); + curr_frag_len = min(size, rx_frag_size); /* Coalesce all frags from the same physical page in one slot */ if (page_info->page_offset == 0) { @@ -731,7 +732,6 @@ static void skb_fill_rx_data(struct be_adapter *adapter, skb_shinfo(skb)->frags[j].size += curr_frag_len; skb->len += curr_frag_len; skb->data_len += curr_frag_len; - pktsize -= curr_frag_len; memset(page_info, 0, sizeof(*page_info)); } diff --git a/drivers/net/bnx2x_link.c b/drivers/net/bnx2x_link.c index ed648acef7c..2ee581a2cde 100644 --- a/drivers/net/bnx2x_link.c +++ b/drivers/net/bnx2x_link.c @@ -4212,13 +4212,14 @@ static void bnx2x_turn_off_sf(struct bnx2x *bp, u8 port) u8 bnx2x_get_ext_phy_fw_version(struct link_params *params, u8 driver_loaded, u8 *version, u16 len) { - struct bnx2x *bp = params->bp; + struct bnx2x *bp; u32 ext_phy_type = 0; u32 spirom_ver = 0; u8 status = 0 ; if (version == NULL || params == NULL) return -EINVAL; + bp = params->bp; spirom_ver = REG_RD(bp, params->shmem_base + offsetof(struct shmem_region, diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index d927f71af8a..aa1be1fecee 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1459,8 +1459,16 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) * ether type (eg ARPHRD_ETHER and ARPHRD_INFINIBAND) share the same bond */ if (bond->slave_cnt == 0) { - if (slave_dev->type != ARPHRD_ETHER) - bond_setup_by_slave(bond_dev, slave_dev); + if (bond_dev->type != slave_dev->type) { + dev_close(bond_dev); + pr_debug("%s: change device type from %d to %d\n", + bond_dev->name, bond_dev->type, slave_dev->type); + if (slave_dev->type != ARPHRD_ETHER) + bond_setup_by_slave(bond_dev, slave_dev); + else + ether_setup(bond_dev); + dev_open(bond_dev); + } } else if (bond_dev->type != slave_dev->type) { pr_err(DRV_NAME ": %s ether type (%d) is different " "from other slaves (%d), can not enslave it.\n", diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c index 574daddc21b..9e4283aff82 100644 --- a/drivers/net/can/dev.c +++ b/drivers/net/can/dev.c @@ -346,7 +346,7 @@ void can_restart(unsigned long data) skb = dev_alloc_skb(sizeof(struct can_frame)); if (skb == NULL) { err = -ENOMEM; - goto out; + goto restart; } skb->dev = dev; skb->protocol = htons(ETH_P_CAN); @@ -361,13 +361,13 @@ void can_restart(unsigned long data) stats->rx_packets++; stats->rx_bytes += cf->can_dlc; +restart: dev_dbg(dev->dev.parent, "restarted\n"); priv->can_stats.restarts++; /* Now restart the device */ err = priv->do_set_mode(dev, CAN_MODE_START); -out: netif_carrier_on(dev); if (err) dev_err(dev->dev.parent, "Error %d during restart", err); @@ -473,6 +473,10 @@ int open_candev(struct net_device *dev) return -EINVAL; } + /* Switch carrier on if device was stopped while in bus-off state */ + if (!netif_carrier_ok(dev)) + netif_carrier_on(dev); + setup_timer(&priv->restart_timer, can_restart, (unsigned long)dev); return 0; diff --git a/drivers/net/can/sja1000/sja1000.c b/drivers/net/can/sja1000/sja1000.c index 571f133a8fe..08ebee79d8a 100644 --- a/drivers/net/can/sja1000/sja1000.c +++ b/drivers/net/can/sja1000/sja1000.c @@ -63,7 +63,6 @@ #include <linux/can.h> #include <linux/can/dev.h> #include <linux/can/error.h> -#include <linux/can/dev.h> #include "sja1000.h" diff --git a/drivers/net/cnic.c b/drivers/net/cnic.c index 4d1515f45ba..4869d77cbe9 100644 --- a/drivers/net/cnic.c +++ b/drivers/net/cnic.c @@ -227,7 +227,7 @@ static int cnic_send_nlmsg(struct cnic_local *cp, u32 type, } rcu_read_lock(); - ulp_ops = rcu_dereference(cp->ulp_ops[CNIC_ULP_ISCSI]); + ulp_ops = rcu_dereference(cnic_ulp_tbl[CNIC_ULP_ISCSI]); if (ulp_ops) ulp_ops->iscsi_nl_send_msg(cp->dev, msg_type, buf, len); rcu_read_unlock(); @@ -319,6 +319,20 @@ static int cnic_abort_prep(struct cnic_sock *csk) return 0; } +static void cnic_uio_stop(void) +{ + struct cnic_dev *dev; + + read_lock(&cnic_dev_lock); + list_for_each_entry(dev, &cnic_dev_list, list) { + struct cnic_local *cp = dev->cnic_priv; + + if (cp->cnic_uinfo) + cnic_send_nlmsg(cp, ISCSI_KEVENT_IF_DOWN, NULL); + } + read_unlock(&cnic_dev_lock); +} + int cnic_register_driver(int ulp_type, struct cnic_ulp_ops *ulp_ops) { struct cnic_dev *dev; @@ -390,6 +404,9 @@ int cnic_unregister_driver(int ulp_type) } read_unlock(&cnic_dev_lock); + if (ulp_type == CNIC_ULP_ISCSI) + cnic_uio_stop(); + rcu_assign_pointer(cnic_ulp_tbl[ulp_type], NULL); mutex_unlock(&cnic_lock); @@ -632,7 +649,6 @@ static void cnic_free_resc(struct cnic_dev *dev) int i = 0; if (cp->cnic_uinfo) { - cnic_send_nlmsg(cp, ISCSI_KEVENT_IF_DOWN, NULL); while (cp->uio_dev != -1 && i < 15) { msleep(100); i++; @@ -1057,6 +1073,9 @@ static void cnic_ulp_stop(struct cnic_dev *dev) struct cnic_local *cp = dev->cnic_priv; int if_type; + if (cp->cnic_uinfo) + cnic_send_nlmsg(cp, ISCSI_KEVENT_IF_DOWN, NULL); + rcu_read_lock(); for (if_type = 0; if_type < MAX_CNIC_ULP_TYPE; if_type++) { struct cnic_ulp_ops *ulp_ops; diff --git a/drivers/net/e100.c b/drivers/net/e100.c index efa680f4b8d..41b648a67fe 100644 --- a/drivers/net/e100.c +++ b/drivers/net/e100.c @@ -1897,6 +1897,9 @@ static int e100_rx_indicate(struct nic *nic, struct rx *rx, if (ioread8(&nic->csr->scb.status) & rus_no_res) nic->ru_running = RU_SUSPENDED; + pci_dma_sync_single_for_device(nic->pdev, rx->dma_addr, + sizeof(struct rfd), + PCI_DMA_BIDIRECTIONAL); return -ENODATA; } diff --git a/drivers/net/eepro.c b/drivers/net/eepro.c index cc2ab6412c7..4f700348534 100644 --- a/drivers/net/eepro.c +++ b/drivers/net/eepro.c @@ -1784,7 +1784,7 @@ int __init init_module(void) printk(KERN_INFO "eepro_init_module: Auto-detecting boards (May God protect us...)\n"); } - for (i = 0; io[i] != -1 && i < MAX_EEPRO; i++) { + for (i = 0; i < MAX_EEPRO && io[i] != -1; i++) { dev = alloc_etherdev(sizeof(struct eepro_local)); if (!dev) break; diff --git a/drivers/net/fealnx.c b/drivers/net/fealnx.c index 48385c42ab5..160655d2458 100644 --- a/drivers/net/fealnx.c +++ b/drivers/net/fealnx.c @@ -584,7 +584,8 @@ static int __devinit fealnx_init_one(struct pci_dev *pdev, if (np->flags == HAS_MII_XCVR) { int phy, phy_idx = 0; - for (phy = 1; phy < 32 && phy_idx < 4; phy++) { + for (phy = 1; phy < 32 && phy_idx < ARRAY_SIZE(np->phys); + phy++) { int mii_status = mdio_read(dev, phy, 1); if (mii_status != 0xffff && mii_status != 0x0000) { diff --git a/drivers/net/fs_enet/fs_enet-main.c b/drivers/net/fs_enet/fs_enet-main.c index b892c3ad9a7..2bc2d2b2064 100644 --- a/drivers/net/fs_enet/fs_enet-main.c +++ b/drivers/net/fs_enet/fs_enet-main.c @@ -754,17 +754,16 @@ static int fs_init_phy(struct net_device *dev) fep->oldlink = 0; fep->oldspeed = 0; fep->oldduplex = -1; - if(fep->fpi->phy_node) - phydev = of_phy_connect(dev, fep->fpi->phy_node, - &fs_adjust_link, 0, - PHY_INTERFACE_MODE_MII); - else { - printk("No phy bus ID specified in BSP code\n"); - return -EINVAL; + + phydev = of_phy_connect(dev, fep->fpi->phy_node, &fs_adjust_link, 0, + PHY_INTERFACE_MODE_MII); + if (!phydev) { + phydev = of_phy_connect_fixed_link(dev, &fs_adjust_link, + PHY_INTERFACE_MODE_MII); } - if (IS_ERR(phydev)) { - printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name); - return PTR_ERR(phydev); + if (!phydev) { + dev_err(&dev->dev, "Could not attach to PHY\n"); + return -ENODEV; } fep->phydev = phydev; @@ -1005,6 +1004,7 @@ static int __devinit fs_enet_probe(struct of_device *ofdev, goto out_free_fpi; } + SET_NETDEV_DEV(ndev, &ofdev->dev); dev_set_drvdata(&ofdev->dev, ndev); fep = netdev_priv(ndev); diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c index 43d813ed9f4..f8ffcbf0bc3 100644 --- a/drivers/net/gianfar.c +++ b/drivers/net/gianfar.c @@ -264,15 +264,6 @@ static int gfar_of_init(struct net_device *dev) priv->device_flags |= FSL_GIANFAR_DEV_HAS_MAGIC_PACKET; priv->phy_node = of_parse_phandle(np, "phy-handle", 0); - if (!priv->phy_node) { - u32 *fixed_link; - - fixed_link = (u32 *)of_get_property(np, "fixed-link", NULL); - if (!fixed_link) { - err = -ENODEV; - goto err_out; - } - } /* Find the TBI PHY. If it's not there, we don't support SGMII */ priv->tbi_node = of_parse_phandle(np, "tbi-handle", 0); @@ -659,13 +650,14 @@ static int init_phy(struct net_device *dev) interface = gfar_get_interface(dev); - if (priv->phy_node) { - priv->phydev = of_phy_connect(dev, priv->phy_node, &adjust_link, - 0, interface); - if (!priv->phydev) { - dev_err(&dev->dev, "error: Could not attach to PHY\n"); - return -ENODEV; - } + priv->phydev = of_phy_connect(dev, priv->phy_node, &adjust_link, 0, + interface); + if (!priv->phydev) + priv->phydev = of_phy_connect_fixed_link(dev, &adjust_link, + interface); + if (!priv->phydev) { + dev_err(&dev->dev, "could not attach to PHY\n"); + return -ENODEV; } if (interface == PHY_INTERFACE_MODE_SGMII) diff --git a/drivers/net/hamradio/6pack.c b/drivers/net/hamradio/6pack.c index 155160052c8..981ab530e9a 100644 --- a/drivers/net/hamradio/6pack.c +++ b/drivers/net/hamradio/6pack.c @@ -3,7 +3,7 @@ * devices like TTY. It interfaces between a raw TTY and the * kernel's AX.25 protocol layers. * - * Authors: Andreas Könsgen <ajk@iehk.rwth-aachen.de> + * Authors: Andreas Könsgen <ajk@comnets.uni-bremen.de> * Ralf Baechle DL5RB <ralf@linux-mips.org> * * Quite a lot of stuff "stolen" by Joerg Reuter from slip.c, written by diff --git a/drivers/net/ibm_newemac/rgmii.c b/drivers/net/ibm_newemac/rgmii.c index 1d5379de690..8d76cb89dbd 100644 --- a/drivers/net/ibm_newemac/rgmii.c +++ b/drivers/net/ibm_newemac/rgmii.c @@ -188,11 +188,12 @@ void rgmii_put_mdio(struct of_device *ofdev, int input) void rgmii_detach(struct of_device *ofdev, int input) { struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev); - struct rgmii_regs __iomem *p = dev->base; - - mutex_lock(&dev->lock); + struct rgmii_regs __iomem *p; BUG_ON(!dev || dev->users == 0); + p = dev->base; + + mutex_lock(&dev->lock); RGMII_DBG(dev, "detach(%d)" NL, input); diff --git a/drivers/net/ixgbe/ixgbe.h b/drivers/net/ixgbe/ixgbe.h index cd22323cfd2..1b12c7ba275 100644 --- a/drivers/net/ixgbe/ixgbe.h +++ b/drivers/net/ixgbe/ixgbe.h @@ -327,6 +327,7 @@ struct ixgbe_adapter { #define IXGBE_FLAG_IN_SFP_MOD_TASK (u32)(1 << 25) #define IXGBE_FLAG_FDIR_HASH_CAPABLE (u32)(1 << 26) #define IXGBE_FLAG_FDIR_PERFECT_CAPABLE (u32)(1 << 27) +#define IXGBE_FLAG_FCOE_CAPABLE (u32)(1 << 28) #define IXGBE_FLAG_FCOE_ENABLED (u32)(1 << 29) u32 flags2; diff --git a/drivers/net/ixgbe/ixgbe_dcb_nl.c b/drivers/net/ixgbe/ixgbe_dcb_nl.c index 7c5978ad929..1c726573290 100644 --- a/drivers/net/ixgbe/ixgbe_dcb_nl.c +++ b/drivers/net/ixgbe/ixgbe_dcb_nl.c @@ -106,8 +106,6 @@ static u8 ixgbe_dcbnl_get_state(struct net_device *netdev) { struct ixgbe_adapter *adapter = netdev_priv(netdev); - DPRINTK(DRV, INFO, "Get DCB Admin Mode.\n"); - return !!(adapter->flags & IXGBE_FLAG_DCB_ENABLED); } @@ -116,8 +114,6 @@ static u8 ixgbe_dcbnl_set_state(struct net_device *netdev, u8 state) u8 err = 0; struct ixgbe_adapter *adapter = netdev_priv(netdev); - DPRINTK(DRV, INFO, "Set DCB Admin Mode.\n"); - if (state > 0) { /* Turn on DCB */ if (adapter->flags & IXGBE_FLAG_DCB_ENABLED) @@ -143,6 +139,18 @@ static u8 ixgbe_dcbnl_set_state(struct net_device *netdev, u8 state) adapter->flags &= ~IXGBE_FLAG_FDIR_PERFECT_CAPABLE; } adapter->flags |= IXGBE_FLAG_DCB_ENABLED; +#ifdef IXGBE_FCOE + /* Turn on FCoE offload */ + if ((adapter->flags & IXGBE_FLAG_FCOE_CAPABLE) && + (!(adapter->flags & IXGBE_FLAG_FCOE_ENABLED))) { + adapter->flags |= IXGBE_FLAG_FCOE_ENABLED; + adapter->ring_feature[RING_F_FCOE].indices = + IXGBE_FCRETA_SIZE; + netdev->features |= NETIF_F_FCOE_CRC; + netdev->features |= NETIF_F_FSO; + netdev->fcoe_ddp_xid = IXGBE_FCOE_DDP_MAX - 1; + } +#endif /* IXGBE_FCOE */ ixgbe_init_interrupt_scheme(adapter); if (netif_running(netdev)) netdev->netdev_ops->ndo_open(netdev); @@ -160,6 +168,18 @@ static u8 ixgbe_dcbnl_set_state(struct net_device *netdev, u8 state) adapter->flags |= IXGBE_FLAG_RSS_ENABLED; if (adapter->hw.mac.type == ixgbe_mac_82599EB) adapter->flags |= IXGBE_FLAG_FDIR_HASH_CAPABLE; + +#ifdef IXGBE_FCOE + /* Turn off FCoE offload */ + if (adapter->flags & (IXGBE_FLAG_FCOE_CAPABLE | + IXGBE_FLAG_FCOE_ENABLED)) { + adapter->flags &= ~IXGBE_FLAG_FCOE_ENABLED; + adapter->ring_feature[RING_F_FCOE].indices = 0; + netdev->features &= ~NETIF_F_FCOE_CRC; + netdev->features &= ~NETIF_F_FSO; + netdev->fcoe_ddp_xid = 0; + } +#endif /* IXGBE_FCOE */ ixgbe_init_interrupt_scheme(adapter); if (netif_running(netdev)) netdev->netdev_ops->ndo_open(netdev); @@ -175,6 +195,8 @@ static void ixgbe_dcbnl_get_perm_hw_addr(struct net_device *netdev, struct ixgbe_adapter *adapter = netdev_priv(netdev); int i, j; + memset(perm_addr, 0xff, MAX_ADDR_LEN); + for (i = 0; i < netdev->addr_len; i++) perm_addr[i] = adapter->hw.mac.perm_addr[i]; diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c index e3442f47f93..200454f30f6 100644 --- a/drivers/net/ixgbe/ixgbe_main.c +++ b/drivers/net/ixgbe/ixgbe_main.c @@ -34,6 +34,7 @@ #include <linux/in.h> #include <linux/ip.h> #include <linux/tcp.h> +#include <linux/pkt_sched.h> #include <linux/ipv6.h> #include <net/checksum.h> #include <net/ip6_checksum.h> @@ -510,8 +511,11 @@ static void ixgbe_receive_skb(struct ixgbe_q_vector *q_vector, * @skb: skb currently being received and modified **/ static inline void ixgbe_rx_checksum(struct ixgbe_adapter *adapter, - u32 status_err, struct sk_buff *skb) + union ixgbe_adv_rx_desc *rx_desc, + struct sk_buff *skb) { + u32 status_err = le32_to_cpu(rx_desc->wb.upper.status_error); + skb->ip_summed = CHECKSUM_NONE; /* Rx csum disabled */ @@ -529,6 +533,16 @@ static inline void ixgbe_rx_checksum(struct ixgbe_adapter *adapter, return; if (status_err & IXGBE_RXDADV_ERR_TCPE) { + u16 pkt_info = rx_desc->wb.lower.lo_dword.hs_rss.pkt_info; + + /* + * 82599 errata, UDP frames with a 0 checksum can be marked as + * checksum errors. + */ + if ((pkt_info & IXGBE_RXDADV_PKTTYPE_UDP) && + (adapter->hw.mac.type == ixgbe_mac_82599EB)) + return; + adapter->hw_csum_rx_error++; return; } @@ -802,7 +816,7 @@ static bool ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, goto next_desc; } - ixgbe_rx_checksum(adapter, staterr, skb); + ixgbe_rx_checksum(adapter, rx_desc, skb); /* probably a little skewed due to removing CRC */ total_rx_bytes += skb->len; @@ -3806,8 +3820,9 @@ static int __devinit ixgbe_sw_init(struct ixgbe_adapter *adapter) adapter->atr_sample_rate = 20; adapter->fdir_pballoc = 0; #ifdef IXGBE_FCOE - adapter->flags |= IXGBE_FLAG_FCOE_ENABLED; - adapter->ring_feature[RING_F_FCOE].indices = IXGBE_FCRETA_SIZE; + adapter->flags |= IXGBE_FLAG_FCOE_CAPABLE; + adapter->flags &= ~IXGBE_FLAG_FCOE_ENABLED; + adapter->ring_feature[RING_F_FCOE].indices = 0; #endif /* IXGBE_FCOE */ } @@ -5125,9 +5140,6 @@ static int ixgbe_xmit_frame(struct sk_buff *skb, struct net_device *netdev) int count = 0; unsigned int f; - r_idx = skb->queue_mapping; - tx_ring = &adapter->tx_ring[r_idx]; - if (adapter->vlgrp && vlan_tx_tag_present(skb)) { tx_flags |= vlan_tx_tag_get(skb); if (adapter->flags & IXGBE_FLAG_DCB_ENABLED) { @@ -5137,11 +5149,19 @@ static int ixgbe_xmit_frame(struct sk_buff *skb, struct net_device *netdev) tx_flags <<= IXGBE_TX_FLAGS_VLAN_SHIFT; tx_flags |= IXGBE_TX_FLAGS_VLAN; } else if (adapter->flags & IXGBE_FLAG_DCB_ENABLED) { - tx_flags |= (skb->queue_mapping << 13); - tx_flags <<= IXGBE_TX_FLAGS_VLAN_SHIFT; - tx_flags |= IXGBE_TX_FLAGS_VLAN; + if (skb->priority != TC_PRIO_CONTROL) { + tx_flags |= (skb->queue_mapping << 13); + tx_flags <<= IXGBE_TX_FLAGS_VLAN_SHIFT; + tx_flags |= IXGBE_TX_FLAGS_VLAN; + } else { + skb->queue_mapping = + adapter->ring_feature[RING_F_DCB].indices-1; + } } + r_idx = skb->queue_mapping; + tx_ring = &adapter->tx_ring[r_idx]; + if ((adapter->flags & IXGBE_FLAG_FCOE_ENABLED) && (skb->protocol == htons(ETH_P_FCOE))) tx_flags |= IXGBE_TX_FLAGS_FCOE; @@ -5580,16 +5600,11 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev, #endif #ifdef IXGBE_FCOE - if (adapter->flags & IXGBE_FLAG_FCOE_ENABLED) { + if (adapter->flags & IXGBE_FLAG_FCOE_CAPABLE) { if (hw->mac.ops.get_device_caps) { hw->mac.ops.get_device_caps(hw, &device_caps); - if (!(device_caps & IXGBE_DEVICE_CAPS_FCOE_OFFLOADS)) { - netdev->features |= NETIF_F_FCOE_CRC; - netdev->features |= NETIF_F_FSO; - netdev->fcoe_ddp_xid = IXGBE_FCOE_DDP_MAX - 1; - } else { - adapter->flags &= ~IXGBE_FLAG_FCOE_ENABLED; - } + if (device_caps & IXGBE_DEVICE_CAPS_FCOE_OFFLOADS) + adapter->flags &= ~IXGBE_FLAG_FCOE_CAPABLE; } } #endif /* IXGBE_FCOE */ @@ -5638,7 +5653,6 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev, adapter->wol = 0; break; } - device_init_wakeup(&adapter->pdev->dev, true); device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol); /* pick up the PCI bus settings for reporting later */ diff --git a/drivers/net/jazzsonic.c b/drivers/net/jazzsonic.c index d12106b47bf..2f286091394 100644 --- a/drivers/net/jazzsonic.c +++ b/drivers/net/jazzsonic.c @@ -229,6 +229,7 @@ static int __init jazz_sonic_probe(struct platform_device *pdev) lp = netdev_priv(dev); lp->device = &pdev->dev; SET_NETDEV_DEV(dev, &pdev->dev); + platform_set_drvdata(pdev, dev); netdev_boot_setup_check(dev); diff --git a/drivers/net/ks8851.c b/drivers/net/ks8851.c new file mode 100644 index 00000000000..9a1dea60c1c --- /dev/null +++ b/drivers/net/ks8851.c @@ -0,0 +1,1322 @@ +/* drivers/net/ks8651.c + * + * Copyright 2009 Simtec Electronics + * http://www.simtec.co.uk/ + * Ben Dooks <ben@simtec.co.uk> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#define DEBUG + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/ethtool.h> +#include <linux/cache.h> +#include <linux/crc32.h> +#include <linux/mii.h> + +#include <linux/spi/spi.h> + +#include "ks8851.h" + +/** + * struct ks8851_rxctrl - KS8851 driver rx control + * @mchash: Multicast hash-table data. + * @rxcr1: KS_RXCR1 register setting + * @rxcr2: KS_RXCR2 register setting + * + * Representation of the settings needs to control the receive filtering + * such as the multicast hash-filter and the receive register settings. This + * is used to make the job of working out if the receive settings change and + * then issuing the new settings to the worker that will send the necessary + * commands. + */ +struct ks8851_rxctrl { + u16 mchash[4]; + u16 rxcr1; + u16 rxcr2; +}; + +/** + * union ks8851_tx_hdr - tx header data + * @txb: The header as bytes + * @txw: The header as 16bit, little-endian words + * + * A dual representation of the tx header data to allow + * access to individual bytes, and to allow 16bit accesses + * with 16bit alignment. + */ +union ks8851_tx_hdr { + u8 txb[6]; + __le16 txw[3]; +}; + +/** + * struct ks8851_net - KS8851 driver private data + * @netdev: The network device we're bound to + * @spidev: The spi device we're bound to. + * @lock: Lock to ensure that the device is not accessed when busy. + * @statelock: Lock on this structure for tx list. + * @mii: The MII state information for the mii calls. + * @rxctrl: RX settings for @rxctrl_work. + * @tx_work: Work queue for tx packets + * @irq_work: Work queue for servicing interrupts + * @rxctrl_work: Work queue for updating RX mode and multicast lists + * @txq: Queue of packets for transmission. + * @spi_msg1: pre-setup SPI transfer with one message, @spi_xfer1. + * @spi_msg2: pre-setup SPI transfer with two messages, @spi_xfer2. + * @txh: Space for generating packet TX header in DMA-able data + * @rxd: Space for receiving SPI data, in DMA-able space. + * @txd: Space for transmitting SPI data, in DMA-able space. + * @msg_enable: The message flags controlling driver output (see ethtool). + * @fid: Incrementing frame id tag. + * @rc_ier: Cached copy of KS_IER. + * @rc_rxqcr: Cached copy of KS_RXQCR. + * + * The @lock ensures that the chip is protected when certain operations are + * in progress. When the read or write packet transfer is in progress, most + * of the chip registers are not ccessible until the transfer is finished and + * the DMA has been de-asserted. + * + * The @statelock is used to protect information in the structure which may + * need to be accessed via several sources, such as the network driver layer + * or one of the work queues. + * + * We align the buffers we may use for rx/tx to ensure that if the SPI driver + * wants to DMA map them, it will not have any problems with data the driver + * modifies. + */ +struct ks8851_net { + struct net_device *netdev; + struct spi_device *spidev; + struct mutex lock; + spinlock_t statelock; + + union ks8851_tx_hdr txh ____cacheline_aligned; + u8 rxd[8]; + u8 txd[8]; + + u32 msg_enable ____cacheline_aligned; + u16 tx_space; + u8 fid; + + u16 rc_ier; + u16 rc_rxqcr; + + struct mii_if_info mii; + struct ks8851_rxctrl rxctrl; + + struct work_struct tx_work; + struct work_struct irq_work; + struct work_struct rxctrl_work; + + struct sk_buff_head txq; + + struct spi_message spi_msg1; + struct spi_message spi_msg2; + struct spi_transfer spi_xfer1; + struct spi_transfer spi_xfer2[2]; +}; + +static int msg_enable; + +#define ks_info(_ks, _msg...) dev_info(&(_ks)->spidev->dev, _msg) +#define ks_warn(_ks, _msg...) dev_warn(&(_ks)->spidev->dev, _msg) +#define ks_dbg(_ks, _msg...) dev_dbg(&(_ks)->spidev->dev, _msg) +#define ks_err(_ks, _msg...) dev_err(&(_ks)->spidev->dev, _msg) + +/* shift for byte-enable data */ +#define BYTE_EN(_x) ((_x) << 2) + +/* turn register number and byte-enable mask into data for start of packet */ +#define MK_OP(_byteen, _reg) (BYTE_EN(_byteen) | (_reg) << (8+2) | (_reg) >> 6) + +/* SPI register read/write calls. + * + * All these calls issue SPI transactions to access the chip's registers. They + * all require that the necessary lock is held to prevent accesses when the + * chip is busy transfering packet data (RX/TX FIFO accesses). + */ + +/** + * ks8851_wrreg16 - write 16bit register value to chip + * @ks: The chip state + * @reg: The register address + * @val: The value to write + * + * Issue a write to put the value @val into the register specified in @reg. + */ +static void ks8851_wrreg16(struct ks8851_net *ks, unsigned reg, unsigned val) +{ + struct spi_transfer *xfer = &ks->spi_xfer1; + struct spi_message *msg = &ks->spi_msg1; + __le16 txb[2]; + int ret; + + txb[0] = cpu_to_le16(MK_OP(reg & 2 ? 0xC : 0x03, reg) | KS_SPIOP_WR); + txb[1] = cpu_to_le16(val); + + xfer->tx_buf = txb; + xfer->rx_buf = NULL; + xfer->len = 4; + + ret = spi_sync(ks->spidev, msg); + if (ret < 0) + ks_err(ks, "spi_sync() failed\n"); +} + +/** + * ks8851_rx_1msg - select whether to use one or two messages for spi read + * @ks: The device structure + * + * Return whether to generate a single message with a tx and rx buffer + * supplied to spi_sync(), or alternatively send the tx and rx buffers + * as separate messages. + * + * Depending on the hardware in use, a single message may be more efficient + * on interrupts or work done by the driver. + * + * This currently always returns true until we add some per-device data passed + * from the platform code to specify which mode is better. + */ +static inline bool ks8851_rx_1msg(struct ks8851_net *ks) +{ + return true; +} + +/** + * ks8851_rdreg - issue read register command and return the data + * @ks: The device state + * @op: The register address and byte enables in message format. + * @rxb: The RX buffer to return the result into + * @rxl: The length of data expected. + * + * This is the low level read call that issues the necessary spi message(s) + * to read data from the register specified in @op. + */ +static void ks8851_rdreg(struct ks8851_net *ks, unsigned op, + u8 *rxb, unsigned rxl) +{ + struct spi_transfer *xfer; + struct spi_message *msg; + __le16 *txb = (__le16 *)ks->txd; + u8 *trx = ks->rxd; + int ret; + + txb[0] = cpu_to_le16(op | KS_SPIOP_RD); + + if (ks8851_rx_1msg(ks)) { + msg = &ks->spi_msg1; + xfer = &ks->spi_xfer1; + + xfer->tx_buf = txb; + xfer->rx_buf = trx; + xfer->len = rxl + 2; + } else { + msg = &ks->spi_msg2; + xfer = ks->spi_xfer2; + + xfer->tx_buf = txb; + xfer->rx_buf = NULL; + xfer->len = 2; + + xfer++; + xfer->tx_buf = NULL; + xfer->rx_buf = trx; + xfer->len = rxl; + } + + ret = spi_sync(ks->spidev, msg); + if (ret < 0) + ks_err(ks, "read: spi_sync() failed\n"); + else if (ks8851_rx_1msg(ks)) + memcpy(rxb, trx + 2, rxl); + else + memcpy(rxb, trx, rxl); +} + +/** + * ks8851_rdreg8 - read 8 bit register from device + * @ks: The chip information + * @reg: The register address + * + * Read a 8bit register from the chip, returning the result +*/ +static unsigned ks8851_rdreg8(struct ks8851_net *ks, unsigned reg) +{ + u8 rxb[1]; + + ks8851_rdreg(ks, MK_OP(1 << (reg & 3), reg), rxb, 1); + return rxb[0]; +} + +/** + * ks8851_rdreg16 - read 16 bit register from device + * @ks: The chip information + * @reg: The register address + * + * Read a 16bit register from the chip, returning the result +*/ +static unsigned ks8851_rdreg16(struct ks8851_net *ks, unsigned reg) +{ + __le16 rx = 0; + + ks8851_rdreg(ks, MK_OP(reg & 2 ? 0xC : 0x3, reg), (u8 *)&rx, 2); + return le16_to_cpu(rx); +} + +/** + * ks8851_rdreg32 - read 32 bit register from device + * @ks: The chip information + * @reg: The register address + * + * Read a 32bit register from the chip. + * + * Note, this read requires the address be aligned to 4 bytes. +*/ +static unsigned ks8851_rdreg32(struct ks8851_net *ks, unsigned reg) +{ + __le32 rx = 0; + + WARN_ON(reg & 3); + + ks8851_rdreg(ks, MK_OP(0xf, reg), (u8 *)&rx, 4); + return le32_to_cpu(rx); +} + +/** + * ks8851_soft_reset - issue one of the soft reset to the device + * @ks: The device state. + * @op: The bit(s) to set in the GRR + * + * Issue the relevant soft-reset command to the device's GRR register + * specified by @op. + * + * Note, the delays are in there as a caution to ensure that the reset + * has time to take effect and then complete. Since the datasheet does + * not currently specify the exact sequence, we have chosen something + * that seems to work with our device. + */ +static void ks8851_soft_reset(struct ks8851_net *ks, unsigned op) +{ + ks8851_wrreg16(ks, KS_GRR, op); + mdelay(1); /* wait a short time to effect reset */ + ks8851_wrreg16(ks, KS_GRR, 0); + mdelay(1); /* wait for condition to clear */ +} + +/** + * ks8851_write_mac_addr - write mac address to device registers + * @dev: The network device + * + * Update the KS8851 MAC address registers from the address in @dev. + * + * This call assumes that the chip is not running, so there is no need to + * shutdown the RXQ process whilst setting this. +*/ +static int ks8851_write_mac_addr(struct net_device *dev) +{ + struct ks8851_net *ks = netdev_priv(dev); + u16 *mcp = (u16 *)dev->dev_addr; + + mutex_lock(&ks->lock); + + ks8851_wrreg16(ks, KS_MARL, mcp[0]); + ks8851_wrreg16(ks, KS_MARM, mcp[1]); + ks8851_wrreg16(ks, KS_MARH, mcp[2]); + + mutex_unlock(&ks->lock); + + return 0; +} + +/** + * ks8851_init_mac - initialise the mac address + * @ks: The device structure + * + * Get or create the initial mac address for the device and then set that + * into the station address register. Currently we assume that the device + * does not have a valid mac address in it, and so we use random_ether_addr() + * to create a new one. + * + * In future, the driver should check to see if the device has an EEPROM + * attached and whether that has a valid ethernet address in it. + */ +static void ks8851_init_mac(struct ks8851_net *ks) +{ + struct net_device *dev = ks->netdev; + + random_ether_addr(dev->dev_addr); + ks8851_write_mac_addr(dev); +} + +/** + * ks8851_irq - device interrupt handler + * @irq: Interrupt number passed from the IRQ hnalder. + * @pw: The private word passed to register_irq(), our struct ks8851_net. + * + * Disable the interrupt from happening again until we've processed the + * current status by scheduling ks8851_irq_work(). + */ +static irqreturn_t ks8851_irq(int irq, void *pw) +{ + struct ks8851_net *ks = pw; + + disable_irq_nosync(irq); + schedule_work(&ks->irq_work); + return IRQ_HANDLED; +} + +/** + * ks8851_rdfifo - read data from the receive fifo + * @ks: The device state. + * @buff: The buffer address + * @len: The length of the data to read + * + * Issue an RXQ FIFO read command and read the @len ammount of data from + * the FIFO into the buffer specified by @buff. + */ +static void ks8851_rdfifo(struct ks8851_net *ks, u8 *buff, unsigned len) +{ + struct spi_transfer *xfer = ks->spi_xfer2; + struct spi_message *msg = &ks->spi_msg2; + u8 txb[1]; + int ret; + + if (netif_msg_rx_status(ks)) + ks_dbg(ks, "%s: %d@%p\n", __func__, len, buff); + + /* set the operation we're issuing */ + txb[0] = KS_SPIOP_RXFIFO; + + xfer->tx_buf = txb; + xfer->rx_buf = NULL; + xfer->len = 1; + + xfer++; + xfer->rx_buf = buff; + xfer->tx_buf = NULL; + xfer->len = len; + + ret = spi_sync(ks->spidev, msg); + if (ret < 0) + ks_err(ks, "%s: spi_sync() failed\n", __func__); +} + +/** + * ks8851_dbg_dumpkkt - dump initial packet contents to debug + * @ks: The device state + * @rxpkt: The data for the received packet + * + * Dump the initial data from the packet to dev_dbg(). +*/ +static void ks8851_dbg_dumpkkt(struct ks8851_net *ks, u8 *rxpkt) +{ + ks_dbg(ks, "pkt %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x\n", + rxpkt[4], rxpkt[5], rxpkt[6], rxpkt[7], + rxpkt[8], rxpkt[9], rxpkt[10], rxpkt[11], + rxpkt[12], rxpkt[13], rxpkt[14], rxpkt[15]); +} + +/** + * ks8851_rx_pkts - receive packets from the host + * @ks: The device information. + * + * This is called from the IRQ work queue when the system detects that there + * are packets in the receive queue. Find out how many packets there are and + * read them from the FIFO. + */ +static void ks8851_rx_pkts(struct ks8851_net *ks) +{ + struct sk_buff *skb; + unsigned rxfc; + unsigned rxlen; + unsigned rxstat; + u32 rxh; + u8 *rxpkt; + + rxfc = ks8851_rdreg8(ks, KS_RXFC); + + if (netif_msg_rx_status(ks)) + ks_dbg(ks, "%s: %d packets\n", __func__, rxfc); + + /* Currently we're issuing a read per packet, but we could possibly + * improve the code by issuing a single read, getting the receive + * header, allocating the packet and then reading the packet data + * out in one go. + * + * This form of operation would require us to hold the SPI bus' + * chipselect low during the entie transaction to avoid any + * reset to the data stream comming from the chip. + */ + + for (; rxfc != 0; rxfc--) { + rxh = ks8851_rdreg32(ks, KS_RXFHSR); + rxstat = rxh & 0xffff; + rxlen = rxh >> 16; + + if (netif_msg_rx_status(ks)) + ks_dbg(ks, "rx: stat 0x%04x, len 0x%04x\n", + rxstat, rxlen); + + /* the length of the packet includes the 32bit CRC */ + + /* set dma read address */ + ks8851_wrreg16(ks, KS_RXFDPR, RXFDPR_RXFPAI | 0x00); + + /* start the packet dma process, and set auto-dequeue rx */ + ks8851_wrreg16(ks, KS_RXQCR, + ks->rc_rxqcr | RXQCR_SDA | RXQCR_ADRFE); + + if (rxlen > 0) { + skb = netdev_alloc_skb(ks->netdev, rxlen + 2 + 8); + if (!skb) { + /* todo - dump frame and move on */ + } + + /* two bytes to ensure ip is aligned, and four bytes + * for the status header and 4 bytes of garbage */ + skb_reserve(skb, 2 + 4 + 4); + + rxpkt = skb_put(skb, rxlen - 4) - 8; + + /* align the packet length to 4 bytes, and add 4 bytes + * as we're getting the rx status header as well */ + ks8851_rdfifo(ks, rxpkt, ALIGN(rxlen, 4) + 8); + + if (netif_msg_pktdata(ks)) + ks8851_dbg_dumpkkt(ks, rxpkt); + + skb->protocol = eth_type_trans(skb, ks->netdev); + netif_rx(skb); + + ks->netdev->stats.rx_packets++; + ks->netdev->stats.rx_bytes += rxlen - 4; + } + + ks8851_wrreg16(ks, KS_RXQCR, ks->rc_rxqcr); + } +} + +/** + * ks8851_irq_work - work queue handler for dealing with interrupt requests + * @work: The work structure that was scheduled by schedule_work() + * + * This is the handler invoked when the ks8851_irq() is called to find out + * what happened, as we cannot allow ourselves to sleep whilst waiting for + * anything other process has the chip's lock. + * + * Read the interrupt status, work out what needs to be done and then clear + * any of the interrupts that are not needed. + */ +static void ks8851_irq_work(struct work_struct *work) +{ + struct ks8851_net *ks = container_of(work, struct ks8851_net, irq_work); + unsigned status; + unsigned handled = 0; + + mutex_lock(&ks->lock); + + status = ks8851_rdreg16(ks, KS_ISR); + + if (netif_msg_intr(ks)) + dev_dbg(&ks->spidev->dev, "%s: status 0x%04x\n", + __func__, status); + + if (status & IRQ_LCI) { + /* should do something about checking link status */ + handled |= IRQ_LCI; + } + + if (status & IRQ_LDI) { + u16 pmecr = ks8851_rdreg16(ks, KS_PMECR); + pmecr &= ~PMECR_WKEVT_MASK; + ks8851_wrreg16(ks, KS_PMECR, pmecr | PMECR_WKEVT_LINK); + + handled |= IRQ_LDI; + } + + if (status & IRQ_RXPSI) + handled |= IRQ_RXPSI; + + if (status & IRQ_TXI) { + handled |= IRQ_TXI; + + /* no lock here, tx queue should have been stopped */ + + /* update our idea of how much tx space is available to the + * system */ + ks->tx_space = ks8851_rdreg16(ks, KS_TXMIR); + + if (netif_msg_intr(ks)) + ks_dbg(ks, "%s: txspace %d\n", __func__, ks->tx_space); + } + + if (status & IRQ_RXI) + handled |= IRQ_RXI; + + if (status & IRQ_SPIBEI) { + dev_err(&ks->spidev->dev, "%s: spi bus error\n", __func__); + handled |= IRQ_SPIBEI; + } + + ks8851_wrreg16(ks, KS_ISR, handled); + + if (status & IRQ_RXI) { + /* the datasheet says to disable the rx interrupt during + * packet read-out, however we're masking the interrupt + * from the device so do not bother masking just the RX + * from the device. */ + + ks8851_rx_pkts(ks); + } + + /* if something stopped the rx process, probably due to wanting + * to change the rx settings, then do something about restarting + * it. */ + if (status & IRQ_RXPSI) { + struct ks8851_rxctrl *rxc = &ks->rxctrl; + + /* update the multicast hash table */ + ks8851_wrreg16(ks, KS_MAHTR0, rxc->mchash[0]); + ks8851_wrreg16(ks, KS_MAHTR1, rxc->mchash[1]); + ks8851_wrreg16(ks, KS_MAHTR2, rxc->mchash[2]); + ks8851_wrreg16(ks, KS_MAHTR3, rxc->mchash[3]); + + ks8851_wrreg16(ks, KS_RXCR2, rxc->rxcr2); + ks8851_wrreg16(ks, KS_RXCR1, rxc->rxcr1); + } + + mutex_unlock(&ks->lock); + + if (status & IRQ_TXI) + netif_wake_queue(ks->netdev); + + enable_irq(ks->netdev->irq); +} + +/** + * calc_txlen - calculate size of message to send packet + * @len: Lenght of data + * + * Returns the size of the TXFIFO message needed to send + * this packet. + */ +static inline unsigned calc_txlen(unsigned len) +{ + return ALIGN(len + 4, 4); +} + +/** + * ks8851_wrpkt - write packet to TX FIFO + * @ks: The device state. + * @txp: The sk_buff to transmit. + * @irq: IRQ on completion of the packet. + * + * Send the @txp to the chip. This means creating the relevant packet header + * specifying the length of the packet and the other information the chip + * needs, such as IRQ on completion. Send the header and the packet data to + * the device. + */ +static void ks8851_wrpkt(struct ks8851_net *ks, struct sk_buff *txp, bool irq) +{ + struct spi_transfer *xfer = ks->spi_xfer2; + struct spi_message *msg = &ks->spi_msg2; + unsigned fid = 0; + int ret; + + if (netif_msg_tx_queued(ks)) + dev_dbg(&ks->spidev->dev, "%s: skb %p, %d@%p, irq %d\n", + __func__, txp, txp->len, txp->data, irq); + + fid = ks->fid++; + fid &= TXFR_TXFID_MASK; + + if (irq) + fid |= TXFR_TXIC; /* irq on completion */ + + /* start header at txb[1] to align txw entries */ + ks->txh.txb[1] = KS_SPIOP_TXFIFO; + ks->txh.txw[1] = cpu_to_le16(fid); + ks->txh.txw[2] = cpu_to_le16(txp->len); + + xfer->tx_buf = &ks->txh.txb[1]; + xfer->rx_buf = NULL; + xfer->len = 5; + + xfer++; + xfer->tx_buf = txp->data; + xfer->rx_buf = NULL; + xfer->len = ALIGN(txp->len, 4); + + ret = spi_sync(ks->spidev, msg); + if (ret < 0) + ks_err(ks, "%s: spi_sync() failed\n", __func__); +} + +/** + * ks8851_done_tx - update and then free skbuff after transmitting + * @ks: The device state + * @txb: The buffer transmitted + */ +static void ks8851_done_tx(struct ks8851_net *ks, struct sk_buff *txb) +{ + struct net_device *dev = ks->netdev; + + dev->stats.tx_bytes += txb->len; + dev->stats.tx_packets++; + + dev_kfree_skb(txb); +} + +/** + * ks8851_tx_work - process tx packet(s) + * @work: The work strucutre what was scheduled. + * + * This is called when a number of packets have been scheduled for + * transmission and need to be sent to the device. + */ +static void ks8851_tx_work(struct work_struct *work) +{ + struct ks8851_net *ks = container_of(work, struct ks8851_net, tx_work); + struct sk_buff *txb; + bool last = false; + + mutex_lock(&ks->lock); + + while (!last) { + txb = skb_dequeue(&ks->txq); + last = skb_queue_empty(&ks->txq); + + ks8851_wrreg16(ks, KS_RXQCR, ks->rc_rxqcr | RXQCR_SDA); + ks8851_wrpkt(ks, txb, last); + ks8851_wrreg16(ks, KS_RXQCR, ks->rc_rxqcr); + ks8851_wrreg16(ks, KS_TXQCR, TXQCR_METFE); + + ks8851_done_tx(ks, txb); + } + + mutex_unlock(&ks->lock); +} + +/** + * ks8851_set_powermode - set power mode of the device + * @ks: The device state + * @pwrmode: The power mode value to write to KS_PMECR. + * + * Change the power mode of the chip. + */ +static void ks8851_set_powermode(struct ks8851_net *ks, unsigned pwrmode) +{ + unsigned pmecr; + + if (netif_msg_hw(ks)) + ks_dbg(ks, "setting power mode %d\n", pwrmode); + + pmecr = ks8851_rdreg16(ks, KS_PMECR); + pmecr &= ~PMECR_PM_MASK; + pmecr |= pwrmode; + + ks8851_wrreg16(ks, KS_PMECR, pmecr); +} + +/** + * ks8851_net_open - open network device + * @dev: The network device being opened. + * + * Called when the network device is marked active, such as a user executing + * 'ifconfig up' on the device. + */ +static int ks8851_net_open(struct net_device *dev) +{ + struct ks8851_net *ks = netdev_priv(dev); + + /* lock the card, even if we may not actually be doing anything + * else at the moment */ + mutex_lock(&ks->lock); + + if (netif_msg_ifup(ks)) + ks_dbg(ks, "opening %s\n", dev->name); + + /* bring chip out of any power saving mode it was in */ + ks8851_set_powermode(ks, PMECR_PM_NORMAL); + + /* issue a soft reset to the RX/TX QMU to put it into a known + * state. */ + ks8851_soft_reset(ks, GRR_QMU); + + /* setup transmission parameters */ + + ks8851_wrreg16(ks, KS_TXCR, (TXCR_TXE | /* enable transmit process */ + TXCR_TXPE | /* pad to min length */ + TXCR_TXCRC | /* add CRC */ + TXCR_TXFCE)); /* enable flow control */ + + /* auto-increment tx data, reset tx pointer */ + ks8851_wrreg16(ks, KS_TXFDPR, TXFDPR_TXFPAI); + + /* setup receiver control */ + + ks8851_wrreg16(ks, KS_RXCR1, (RXCR1_RXPAFMA | /* from mac filter */ + RXCR1_RXFCE | /* enable flow control */ + RXCR1_RXBE | /* broadcast enable */ + RXCR1_RXUE | /* unicast enable */ + RXCR1_RXE)); /* enable rx block */ + + /* transfer entire frames out in one go */ + ks8851_wrreg16(ks, KS_RXCR2, RXCR2_SRDBL_FRAME); + + /* set receive counter timeouts */ + ks8851_wrreg16(ks, KS_RXDTTR, 1000); /* 1ms after first frame to IRQ */ + ks8851_wrreg16(ks, KS_RXDBCTR, 4096); /* >4Kbytes in buffer to IRQ */ + ks8851_wrreg16(ks, KS_RXFCTR, 10); /* 10 frames to IRQ */ + + ks->rc_rxqcr = (RXQCR_RXFCTE | /* IRQ on frame count exceeded */ + RXQCR_RXDBCTE | /* IRQ on byte count exceeded */ + RXQCR_RXDTTE); /* IRQ on time exceeded */ + + ks8851_wrreg16(ks, KS_RXQCR, ks->rc_rxqcr); + + /* clear then enable interrupts */ + +#define STD_IRQ (IRQ_LCI | /* Link Change */ \ + IRQ_TXI | /* TX done */ \ + IRQ_RXI | /* RX done */ \ + IRQ_SPIBEI | /* SPI bus error */ \ + IRQ_TXPSI | /* TX process stop */ \ + IRQ_RXPSI) /* RX process stop */ + + ks->rc_ier = STD_IRQ; + ks8851_wrreg16(ks, KS_ISR, STD_IRQ); + ks8851_wrreg16(ks, KS_IER, STD_IRQ); + + netif_start_queue(ks->netdev); + + if (netif_msg_ifup(ks)) + ks_dbg(ks, "network device %s up\n", dev->name); + + mutex_unlock(&ks->lock); + return 0; +} + +/** + * ks8851_net_stop - close network device + * @dev: The device being closed. + * + * Called to close down a network device which has been active. Cancell any + * work, shutdown the RX and TX process and then place the chip into a low + * power state whilst it is not being used. + */ +static int ks8851_net_stop(struct net_device *dev) +{ + struct ks8851_net *ks = netdev_priv(dev); + + if (netif_msg_ifdown(ks)) + ks_info(ks, "%s: shutting down\n", dev->name); + + netif_stop_queue(dev); + + mutex_lock(&ks->lock); + + /* stop any outstanding work */ + flush_work(&ks->irq_work); + flush_work(&ks->tx_work); + flush_work(&ks->rxctrl_work); + + /* turn off the IRQs and ack any outstanding */ + ks8851_wrreg16(ks, KS_IER, 0x0000); + ks8851_wrreg16(ks, KS_ISR, 0xffff); + + /* shutdown RX process */ + ks8851_wrreg16(ks, KS_RXCR1, 0x0000); + + /* shutdown TX process */ + ks8851_wrreg16(ks, KS_TXCR, 0x0000); + + /* set powermode to soft power down to save power */ + ks8851_set_powermode(ks, PMECR_PM_SOFTDOWN); + + /* ensure any queued tx buffers are dumped */ + while (!skb_queue_empty(&ks->txq)) { + struct sk_buff *txb = skb_dequeue(&ks->txq); + + if (netif_msg_ifdown(ks)) + ks_dbg(ks, "%s: freeing txb %p\n", __func__, txb); + + dev_kfree_skb(txb); + } + + mutex_unlock(&ks->lock); + return 0; +} + +/** + * ks8851_start_xmit - transmit packet + * @skb: The buffer to transmit + * @dev: The device used to transmit the packet. + * + * Called by the network layer to transmit the @skb. Queue the packet for + * the device and schedule the necessary work to transmit the packet when + * it is free. + * + * We do this to firstly avoid sleeping with the network device locked, + * and secondly so we can round up more than one packet to transmit which + * means we can try and avoid generating too many transmit done interrupts. + */ +static int ks8851_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct ks8851_net *ks = netdev_priv(dev); + unsigned needed = calc_txlen(skb->len); + int ret = NETDEV_TX_OK; + + if (netif_msg_tx_queued(ks)) + ks_dbg(ks, "%s: skb %p, %d@%p\n", __func__, + skb, skb->len, skb->data); + + spin_lock(&ks->statelock); + + if (needed > ks->tx_space) { + netif_stop_queue(dev); + ret = NETDEV_TX_BUSY; + } else { + ks->tx_space -= needed; + skb_queue_tail(&ks->txq, skb); + } + + spin_unlock(&ks->statelock); + schedule_work(&ks->tx_work); + + return ret; +} + +/** + * ks8851_rxctrl_work - work handler to change rx mode + * @work: The work structure this belongs to. + * + * Lock the device and issue the necessary changes to the receive mode from + * the network device layer. This is done so that we can do this without + * having to sleep whilst holding the network device lock. + * + * Since the recommendation from Micrel is that the RXQ is shutdown whilst the + * receive parameters are programmed, we issue a write to disable the RXQ and + * then wait for the interrupt handler to be triggered once the RXQ shutdown is + * complete. The interrupt handler then writes the new values into the chip. + */ +static void ks8851_rxctrl_work(struct work_struct *work) +{ + struct ks8851_net *ks = container_of(work, struct ks8851_net, rxctrl_work); + + mutex_lock(&ks->lock); + + /* need to shutdown RXQ before modifying filter parameters */ + ks8851_wrreg16(ks, KS_RXCR1, 0x00); + + mutex_unlock(&ks->lock); +} + +static void ks8851_set_rx_mode(struct net_device *dev) +{ + struct ks8851_net *ks = netdev_priv(dev); + struct ks8851_rxctrl rxctrl; + + memset(&rxctrl, 0, sizeof(rxctrl)); + + if (dev->flags & IFF_PROMISC) { + /* interface to receive everything */ + + rxctrl.rxcr1 = RXCR1_RXAE | RXCR1_RXINVF; + } else if (dev->flags & IFF_ALLMULTI) { + /* accept all multicast packets */ + + rxctrl.rxcr1 = (RXCR1_RXME | RXCR1_RXAE | + RXCR1_RXPAFMA | RXCR1_RXMAFMA); + } else if (dev->flags & IFF_MULTICAST && dev->mc_count > 0) { + struct dev_mc_list *mcptr = dev->mc_list; + u32 crc; + int i; + + /* accept some multicast */ + + for (i = dev->mc_count; i > 0; i--) { + crc = ether_crc(ETH_ALEN, mcptr->dmi_addr); + crc >>= (32 - 6); /* get top six bits */ + + rxctrl.mchash[crc >> 4] |= (1 << (crc & 0xf)); + mcptr = mcptr->next; + } + + rxctrl.rxcr1 = RXCR1_RXME | RXCR1_RXAE | RXCR1_RXPAFMA; + } else { + /* just accept broadcast / unicast */ + rxctrl.rxcr1 = RXCR1_RXPAFMA; + } + + rxctrl.rxcr1 |= (RXCR1_RXUE | /* unicast enable */ + RXCR1_RXBE | /* broadcast enable */ + RXCR1_RXE | /* RX process enable */ + RXCR1_RXFCE); /* enable flow control */ + + rxctrl.rxcr2 |= RXCR2_SRDBL_FRAME; + + /* schedule work to do the actual set of the data if needed */ + + spin_lock(&ks->statelock); + + if (memcmp(&rxctrl, &ks->rxctrl, sizeof(rxctrl)) != 0) { + memcpy(&ks->rxctrl, &rxctrl, sizeof(ks->rxctrl)); + schedule_work(&ks->rxctrl_work); + } + + spin_unlock(&ks->statelock); +} + +static int ks8851_set_mac_address(struct net_device *dev, void *addr) +{ + struct sockaddr *sa = addr; + + if (netif_running(dev)) + return -EBUSY; + + if (!is_valid_ether_addr(sa->sa_data)) + return -EADDRNOTAVAIL; + + memcpy(dev->dev_addr, sa->sa_data, ETH_ALEN); + return ks8851_write_mac_addr(dev); +} + +static int ks8851_net_ioctl(struct net_device *dev, struct ifreq *req, int cmd) +{ + struct ks8851_net *ks = netdev_priv(dev); + + if (!netif_running(dev)) + return -EINVAL; + + return generic_mii_ioctl(&ks->mii, if_mii(req), cmd, NULL); +} + +static const struct net_device_ops ks8851_netdev_ops = { + .ndo_open = ks8851_net_open, + .ndo_stop = ks8851_net_stop, + .ndo_do_ioctl = ks8851_net_ioctl, + .ndo_start_xmit = ks8851_start_xmit, + .ndo_set_mac_address = ks8851_set_mac_address, + .ndo_set_rx_mode = ks8851_set_rx_mode, + .ndo_change_mtu = eth_change_mtu, + .ndo_validate_addr = eth_validate_addr, +}; + +/* ethtool support */ + +static void ks8851_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *di) +{ + strlcpy(di->driver, "KS8851", sizeof(di->driver)); + strlcpy(di->version, "1.00", sizeof(di->version)); + strlcpy(di->bus_info, dev_name(dev->dev.parent), sizeof(di->bus_info)); +} + +static u32 ks8851_get_msglevel(struct net_device *dev) +{ + struct ks8851_net *ks = netdev_priv(dev); + return ks->msg_enable; +} + +static void ks8851_set_msglevel(struct net_device *dev, u32 to) +{ + struct ks8851_net *ks = netdev_priv(dev); + ks->msg_enable = to; +} + +static int ks8851_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct ks8851_net *ks = netdev_priv(dev); + return mii_ethtool_gset(&ks->mii, cmd); +} + +static int ks8851_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct ks8851_net *ks = netdev_priv(dev); + return mii_ethtool_sset(&ks->mii, cmd); +} + +static u32 ks8851_get_link(struct net_device *dev) +{ + struct ks8851_net *ks = netdev_priv(dev); + return mii_link_ok(&ks->mii); +} + +static int ks8851_nway_reset(struct net_device *dev) +{ + struct ks8851_net *ks = netdev_priv(dev); + return mii_nway_restart(&ks->mii); +} + +static const struct ethtool_ops ks8851_ethtool_ops = { + .get_drvinfo = ks8851_get_drvinfo, + .get_msglevel = ks8851_get_msglevel, + .set_msglevel = ks8851_set_msglevel, + .get_settings = ks8851_get_settings, + .set_settings = ks8851_set_settings, + .get_link = ks8851_get_link, + .nway_reset = ks8851_nway_reset, +}; + +/* MII interface controls */ + +/** + * ks8851_phy_reg - convert MII register into a KS8851 register + * @reg: MII register number. + * + * Return the KS8851 register number for the corresponding MII PHY register + * if possible. Return zero if the MII register has no direct mapping to the + * KS8851 register set. + */ +static int ks8851_phy_reg(int reg) +{ + switch (reg) { + case MII_BMCR: + return KS_P1MBCR; + case MII_BMSR: + return KS_P1MBSR; + case MII_PHYSID1: + return KS_PHY1ILR; + case MII_PHYSID2: + return KS_PHY1IHR; + case MII_ADVERTISE: + return KS_P1ANAR; + case MII_LPA: + return KS_P1ANLPR; + } + + return 0x0; +} + +/** + * ks8851_phy_read - MII interface PHY register read. + * @dev: The network device the PHY is on. + * @phy_addr: Address of PHY (ignored as we only have one) + * @reg: The register to read. + * + * This call reads data from the PHY register specified in @reg. Since the + * device does not support all the MII registers, the non-existant values + * are always returned as zero. + * + * We return zero for unsupported registers as the MII code does not check + * the value returned for any error status, and simply returns it to the + * caller. The mii-tool that the driver was tested with takes any -ve error + * as real PHY capabilities, thus displaying incorrect data to the user. + */ +static int ks8851_phy_read(struct net_device *dev, int phy_addr, int reg) +{ + struct ks8851_net *ks = netdev_priv(dev); + int ksreg; + int result; + + ksreg = ks8851_phy_reg(reg); + if (!ksreg) + return 0x0; /* no error return allowed, so use zero */ + + mutex_lock(&ks->lock); + result = ks8851_rdreg16(ks, ksreg); + mutex_unlock(&ks->lock); + + return result; +} + +static void ks8851_phy_write(struct net_device *dev, + int phy, int reg, int value) +{ + struct ks8851_net *ks = netdev_priv(dev); + int ksreg; + + ksreg = ks8851_phy_reg(reg); + if (ksreg) { + mutex_lock(&ks->lock); + ks8851_wrreg16(ks, ksreg, value); + mutex_unlock(&ks->lock); + } +} + +/** + * ks8851_read_selftest - read the selftest memory info. + * @ks: The device state + * + * Read and check the TX/RX memory selftest information. + */ +static int ks8851_read_selftest(struct ks8851_net *ks) +{ + unsigned both_done = MBIR_TXMBF | MBIR_RXMBF; + int ret = 0; + unsigned rd; + + rd = ks8851_rdreg16(ks, KS_MBIR); + + if ((rd & both_done) != both_done) { + ks_warn(ks, "Memory selftest not finished\n"); + return 0; + } + + if (rd & MBIR_TXMBFA) { + ks_err(ks, "TX memory selftest fail\n"); + ret |= 1; + } + + if (rd & MBIR_RXMBFA) { + ks_err(ks, "RX memory selftest fail\n"); + ret |= 2; + } + + return 0; +} + +/* driver bus management functions */ + +static int __devinit ks8851_probe(struct spi_device *spi) +{ + struct net_device *ndev; + struct ks8851_net *ks; + int ret; + + ndev = alloc_etherdev(sizeof(struct ks8851_net)); + if (!ndev) { + dev_err(&spi->dev, "failed to alloc ethernet device\n"); + return -ENOMEM; + } + + spi->bits_per_word = 8; + + ks = netdev_priv(ndev); + + ks->netdev = ndev; + ks->spidev = spi; + ks->tx_space = 6144; + + mutex_init(&ks->lock); + spin_lock_init(&ks->statelock); + + INIT_WORK(&ks->tx_work, ks8851_tx_work); + INIT_WORK(&ks->irq_work, ks8851_irq_work); + INIT_WORK(&ks->rxctrl_work, ks8851_rxctrl_work); + + /* initialise pre-made spi transfer messages */ + + spi_message_init(&ks->spi_msg1); + spi_message_add_tail(&ks->spi_xfer1, &ks->spi_msg1); + + spi_message_init(&ks->spi_msg2); + spi_message_add_tail(&ks->spi_xfer2[0], &ks->spi_msg2); + spi_message_add_tail(&ks->spi_xfer2[1], &ks->spi_msg2); + + /* setup mii state */ + ks->mii.dev = ndev; + ks->mii.phy_id = 1, + ks->mii.phy_id_mask = 1; + ks->mii.reg_num_mask = 0xf; + ks->mii.mdio_read = ks8851_phy_read; + ks->mii.mdio_write = ks8851_phy_write; + + dev_info(&spi->dev, "message enable is %d\n", msg_enable); + + /* set the default message enable */ + ks->msg_enable = netif_msg_init(msg_enable, (NETIF_MSG_DRV | + NETIF_MSG_PROBE | + NETIF_MSG_LINK)); + + skb_queue_head_init(&ks->txq); + + SET_ETHTOOL_OPS(ndev, &ks8851_ethtool_ops); + SET_NETDEV_DEV(ndev, &spi->dev); + + dev_set_drvdata(&spi->dev, ks); + + ndev->if_port = IF_PORT_100BASET; + ndev->netdev_ops = &ks8851_netdev_ops; + ndev->irq = spi->irq; + + /* simple check for a valid chip being connected to the bus */ + + if ((ks8851_rdreg16(ks, KS_CIDER) & ~CIDER_REV_MASK) != CIDER_ID) { + dev_err(&spi->dev, "failed to read device ID\n"); + ret = -ENODEV; + goto err_id; + } + + ks8851_read_selftest(ks); + ks8851_init_mac(ks); + + ret = request_irq(spi->irq, ks8851_irq, IRQF_TRIGGER_LOW, + ndev->name, ks); + if (ret < 0) { + dev_err(&spi->dev, "failed to get irq\n"); + goto err_irq; + } + + ret = register_netdev(ndev); + if (ret) { + dev_err(&spi->dev, "failed to register network device\n"); + goto err_netdev; + } + + dev_info(&spi->dev, "revision %d, MAC %pM, IRQ %d\n", + CIDER_REV_GET(ks8851_rdreg16(ks, KS_CIDER)), + ndev->dev_addr, ndev->irq); + + return 0; + + +err_netdev: + free_irq(ndev->irq, ndev); + +err_id: +err_irq: + free_netdev(ndev); + return ret; +} + +static int __devexit ks8851_remove(struct spi_device *spi) +{ + struct ks8851_net *priv = dev_get_drvdata(&spi->dev); + + if (netif_msg_drv(priv)) + dev_info(&spi->dev, "remove"); + + unregister_netdev(priv->netdev); + free_irq(spi->irq, priv); + free_netdev(priv->netdev); + + return 0; +} + +static struct spi_driver ks8851_driver = { + .driver = { + .name = "ks8851", + .owner = THIS_MODULE, + }, + .probe = ks8851_probe, + .remove = __devexit_p(ks8851_remove), +}; + +static int __init ks8851_init(void) +{ + return spi_register_driver(&ks8851_driver); +} + +static void __exit ks8851_exit(void) +{ + spi_unregister_driver(&ks8851_driver); +} + +module_init(ks8851_init); +module_exit(ks8851_exit); + +MODULE_DESCRIPTION("KS8851 Network driver"); +MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); +MODULE_LICENSE("GPL"); + +module_param_named(message, msg_enable, int, 0); +MODULE_PARM_DESC(message, "Message verbosity level (0=none, 31=all)"); diff --git a/drivers/net/ks8851.h b/drivers/net/ks8851.h new file mode 100644 index 00000000000..85abe147afb --- /dev/null +++ b/drivers/net/ks8851.h @@ -0,0 +1,296 @@ +/* drivers/net/ks8851.h + * + * Copyright 2009 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * KS8851 register definitions + * + * 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. +*/ + +#define KS_CCR 0x08 +#define CCR_EEPROM (1 << 9) +#define CCR_SPI (1 << 8) +#define CCR_32PIN (1 << 0) + +/* MAC address registers */ +#define KS_MARL 0x10 +#define KS_MARM 0x12 +#define KS_MARH 0x14 + +#define KS_OBCR 0x20 +#define OBCR_ODS_16mA (1 << 6) + +#define KS_EEPCR 0x22 +#define EEPCR_EESA (1 << 4) +#define EEPCR_EESB (1 << 3) +#define EEPCR_EEDO (1 << 2) +#define EEPCR_EESCK (1 << 1) +#define EEPCR_EECS (1 << 0) + +#define KS_MBIR 0x24 +#define MBIR_TXMBF (1 << 12) +#define MBIR_TXMBFA (1 << 11) +#define MBIR_RXMBF (1 << 4) +#define MBIR_RXMBFA (1 << 3) + +#define KS_GRR 0x26 +#define GRR_QMU (1 << 1) +#define GRR_GSR (1 << 0) + +#define KS_WFCR 0x2A +#define WFCR_MPRXE (1 << 7) +#define WFCR_WF3E (1 << 3) +#define WFCR_WF2E (1 << 2) +#define WFCR_WF1E (1 << 1) +#define WFCR_WF0E (1 << 0) + +#define KS_WF0CRC0 0x30 +#define KS_WF0CRC1 0x32 +#define KS_WF0BM0 0x34 +#define KS_WF0BM1 0x36 +#define KS_WF0BM2 0x38 +#define KS_WF0BM3 0x3A + +#define KS_WF1CRC0 0x40 +#define KS_WF1CRC1 0x42 +#define KS_WF1BM0 0x44 +#define KS_WF1BM1 0x46 +#define KS_WF1BM2 0x48 +#define KS_WF1BM3 0x4A + +#define KS_WF2CRC0 0x50 +#define KS_WF2CRC1 0x52 +#define KS_WF2BM0 0x54 +#define KS_WF2BM1 0x56 +#define KS_WF2BM2 0x58 +#define KS_WF2BM3 0x5A + +#define KS_WF3CRC0 0x60 +#define KS_WF3CRC1 0x62 +#define KS_WF3BM0 0x64 +#define KS_WF3BM1 0x66 +#define KS_WF3BM2 0x68 +#define KS_WF3BM3 0x6A + +#define KS_TXCR 0x70 +#define TXCR_TCGICMP (1 << 8) +#define TXCR_TCGUDP (1 << 7) +#define TXCR_TCGTCP (1 << 6) +#define TXCR_TCGIP (1 << 5) +#define TXCR_FTXQ (1 << 4) +#define TXCR_TXFCE (1 << 3) +#define TXCR_TXPE (1 << 2) +#define TXCR_TXCRC (1 << 1) +#define TXCR_TXE (1 << 0) + +#define KS_TXSR 0x72 +#define TXSR_TXLC (1 << 13) +#define TXSR_TXMC (1 << 12) +#define TXSR_TXFID_MASK (0x3f << 0) +#define TXSR_TXFID_SHIFT (0) +#define TXSR_TXFID_GET(_v) (((_v) >> 0) & 0x3f) + +#define KS_RXCR1 0x74 +#define RXCR1_FRXQ (1 << 15) +#define RXCR1_RXUDPFCC (1 << 14) +#define RXCR1_RXTCPFCC (1 << 13) +#define RXCR1_RXIPFCC (1 << 12) +#define RXCR1_RXPAFMA (1 << 11) +#define RXCR1_RXFCE (1 << 10) +#define RXCR1_RXEFE (1 << 9) +#define RXCR1_RXMAFMA (1 << 8) +#define RXCR1_RXBE (1 << 7) +#define RXCR1_RXME (1 << 6) +#define RXCR1_RXUE (1 << 5) +#define RXCR1_RXAE (1 << 4) +#define RXCR1_RXINVF (1 << 1) +#define RXCR1_RXE (1 << 0) + +#define KS_RXCR2 0x76 +#define RXCR2_SRDBL_MASK (0x7 << 5) +#define RXCR2_SRDBL_SHIFT (5) +#define RXCR2_SRDBL_4B (0x0 << 5) +#define RXCR2_SRDBL_8B (0x1 << 5) +#define RXCR2_SRDBL_16B (0x2 << 5) +#define RXCR2_SRDBL_32B (0x3 << 5) +#define RXCR2_SRDBL_FRAME (0x4 << 5) +#define RXCR2_IUFFP (1 << 4) +#define RXCR2_RXIUFCEZ (1 << 3) +#define RXCR2_UDPLFE (1 << 2) +#define RXCR2_RXICMPFCC (1 << 1) +#define RXCR2_RXSAF (1 << 0) + +#define KS_TXMIR 0x78 + +#define KS_RXFHSR 0x7C +#define RXFSHR_RXFV (1 << 15) +#define RXFSHR_RXICMPFCS (1 << 13) +#define RXFSHR_RXIPFCS (1 << 12) +#define RXFSHR_RXTCPFCS (1 << 11) +#define RXFSHR_RXUDPFCS (1 << 10) +#define RXFSHR_RXBF (1 << 7) +#define RXFSHR_RXMF (1 << 6) +#define RXFSHR_RXUF (1 << 5) +#define RXFSHR_RXMR (1 << 4) +#define RXFSHR_RXFT (1 << 3) +#define RXFSHR_RXFTL (1 << 2) +#define RXFSHR_RXRF (1 << 1) +#define RXFSHR_RXCE (1 << 0) + +#define KS_RXFHBCR 0x7E +#define KS_TXQCR 0x80 +#define TXQCR_AETFE (1 << 2) +#define TXQCR_TXQMAM (1 << 1) +#define TXQCR_METFE (1 << 0) + +#define KS_RXQCR 0x82 +#define RXQCR_RXDTTS (1 << 12) +#define RXQCR_RXDBCTS (1 << 11) +#define RXQCR_RXFCTS (1 << 10) +#define RXQCR_RXIPHTOE (1 << 9) +#define RXQCR_RXDTTE (1 << 7) +#define RXQCR_RXDBCTE (1 << 6) +#define RXQCR_RXFCTE (1 << 5) +#define RXQCR_ADRFE (1 << 4) +#define RXQCR_SDA (1 << 3) +#define RXQCR_RRXEF (1 << 0) + +#define KS_TXFDPR 0x84 +#define TXFDPR_TXFPAI (1 << 14) +#define TXFDPR_TXFP_MASK (0x7ff << 0) +#define TXFDPR_TXFP_SHIFT (0) + +#define KS_RXFDPR 0x86 +#define RXFDPR_RXFPAI (1 << 14) + +#define KS_RXDTTR 0x8C +#define KS_RXDBCTR 0x8E + +#define KS_IER 0x90 +#define KS_ISR 0x92 +#define IRQ_LCI (1 << 15) +#define IRQ_TXI (1 << 14) +#define IRQ_RXI (1 << 13) +#define IRQ_RXOI (1 << 11) +#define IRQ_TXPSI (1 << 9) +#define IRQ_RXPSI (1 << 8) +#define IRQ_TXSAI (1 << 6) +#define IRQ_RXWFDI (1 << 5) +#define IRQ_RXMPDI (1 << 4) +#define IRQ_LDI (1 << 3) +#define IRQ_EDI (1 << 2) +#define IRQ_SPIBEI (1 << 1) +#define IRQ_DEDI (1 << 0) + +#define KS_RXFCTR 0x9C +#define KS_RXFC 0x9D +#define RXFCTR_RXFC_MASK (0xff << 8) +#define RXFCTR_RXFC_SHIFT (8) +#define RXFCTR_RXFC_GET(_v) (((_v) >> 8) & 0xff) +#define RXFCTR_RXFCT_MASK (0xff << 0) +#define RXFCTR_RXFCT_SHIFT (0) + +#define KS_TXNTFSR 0x9E + +#define KS_MAHTR0 0xA0 +#define KS_MAHTR1 0xA2 +#define KS_MAHTR2 0xA4 +#define KS_MAHTR3 0xA6 + +#define KS_FCLWR 0xB0 +#define KS_FCHWR 0xB2 +#define KS_FCOWR 0xB4 + +#define KS_CIDER 0xC0 +#define CIDER_ID 0x8870 +#define CIDER_REV_MASK (0x7 << 1) +#define CIDER_REV_SHIFT (1) +#define CIDER_REV_GET(_v) (((_v) >> 1) & 0x7) + +#define KS_CGCR 0xC6 + +#define KS_IACR 0xC8 +#define IACR_RDEN (1 << 12) +#define IACR_TSEL_MASK (0x3 << 10) +#define IACR_TSEL_SHIFT (10) +#define IACR_TSEL_MIB (0x3 << 10) +#define IACR_ADDR_MASK (0x1f << 0) +#define IACR_ADDR_SHIFT (0) + +#define KS_IADLR 0xD0 +#define KS_IAHDR 0xD2 + +#define KS_PMECR 0xD4 +#define PMECR_PME_DELAY (1 << 14) +#define PMECR_PME_POL (1 << 12) +#define PMECR_WOL_WAKEUP (1 << 11) +#define PMECR_WOL_MAGICPKT (1 << 10) +#define PMECR_WOL_LINKUP (1 << 9) +#define PMECR_WOL_ENERGY (1 << 8) +#define PMECR_AUTO_WAKE_EN (1 << 7) +#define PMECR_WAKEUP_NORMAL (1 << 6) +#define PMECR_WKEVT_MASK (0xf << 2) +#define PMECR_WKEVT_SHIFT (2) +#define PMECR_WKEVT_GET(_v) (((_v) >> 2) & 0xf) +#define PMECR_WKEVT_ENERGY (0x1 << 2) +#define PMECR_WKEVT_LINK (0x2 << 2) +#define PMECR_WKEVT_MAGICPKT (0x4 << 2) +#define PMECR_WKEVT_FRAME (0x8 << 2) +#define PMECR_PM_MASK (0x3 << 0) +#define PMECR_PM_SHIFT (0) +#define PMECR_PM_NORMAL (0x0 << 0) +#define PMECR_PM_ENERGY (0x1 << 0) +#define PMECR_PM_SOFTDOWN (0x2 << 0) +#define PMECR_PM_POWERSAVE (0x3 << 0) + +/* Standard MII PHY data */ +#define KS_P1MBCR 0xE4 +#define KS_P1MBSR 0xE6 +#define KS_PHY1ILR 0xE8 +#define KS_PHY1IHR 0xEA +#define KS_P1ANAR 0xEC +#define KS_P1ANLPR 0xEE + +#define KS_P1SCLMD 0xF4 +#define P1SCLMD_LEDOFF (1 << 15) +#define P1SCLMD_TXIDS (1 << 14) +#define P1SCLMD_RESTARTAN (1 << 13) +#define P1SCLMD_DISAUTOMDIX (1 << 10) +#define P1SCLMD_FORCEMDIX (1 << 9) +#define P1SCLMD_AUTONEGEN (1 << 7) +#define P1SCLMD_FORCE100 (1 << 6) +#define P1SCLMD_FORCEFDX (1 << 5) +#define P1SCLMD_ADV_FLOW (1 << 4) +#define P1SCLMD_ADV_100BT_FDX (1 << 3) +#define P1SCLMD_ADV_100BT_HDX (1 << 2) +#define P1SCLMD_ADV_10BT_FDX (1 << 1) +#define P1SCLMD_ADV_10BT_HDX (1 << 0) + +#define KS_P1CR 0xF6 +#define P1CR_HP_MDIX (1 << 15) +#define P1CR_REV_POL (1 << 13) +#define P1CR_OP_100M (1 << 10) +#define P1CR_OP_FDX (1 << 9) +#define P1CR_OP_MDI (1 << 7) +#define P1CR_AN_DONE (1 << 6) +#define P1CR_LINK_GOOD (1 << 5) +#define P1CR_PNTR_FLOW (1 << 4) +#define P1CR_PNTR_100BT_FDX (1 << 3) +#define P1CR_PNTR_100BT_HDX (1 << 2) +#define P1CR_PNTR_10BT_FDX (1 << 1) +#define P1CR_PNTR_10BT_HDX (1 << 0) + +/* TX Frame control */ + +#define TXFR_TXIC (1 << 15) +#define TXFR_TXFID_MASK (0x3f << 0) +#define TXFR_TXFID_SHIFT (0) + +/* SPI frame opcodes */ +#define KS_SPIOP_RD (0x00) +#define KS_SPIOP_WR (0x40) +#define KS_SPIOP_RXFIFO (0x80) +#define KS_SPIOP_TXFIFO (0xC0) diff --git a/drivers/net/macsonic.c b/drivers/net/macsonic.c index acd143da161..61eabcac734 100644 --- a/drivers/net/macsonic.c +++ b/drivers/net/macsonic.c @@ -179,7 +179,7 @@ static const struct net_device_ops macsonic_netdev_ops = { .ndo_set_mac_address = eth_mac_addr, }; -static int __init macsonic_init(struct net_device *dev) +static int __devinit macsonic_init(struct net_device *dev) { struct sonic_local* lp = netdev_priv(dev); @@ -223,7 +223,7 @@ static int __init macsonic_init(struct net_device *dev) return 0; } -static int __init mac_onboard_sonic_ethernet_addr(struct net_device *dev) +static int __devinit mac_onboard_sonic_ethernet_addr(struct net_device *dev) { struct sonic_local *lp = netdev_priv(dev); const int prom_addr = ONBOARD_SONIC_PROM_BASE; @@ -288,7 +288,7 @@ static int __init mac_onboard_sonic_ethernet_addr(struct net_device *dev) } else return 0; } -static int __init mac_onboard_sonic_probe(struct net_device *dev) +static int __devinit mac_onboard_sonic_probe(struct net_device *dev) { /* Bwahahaha */ static int once_is_more_than_enough; @@ -409,7 +409,7 @@ static int __init mac_onboard_sonic_probe(struct net_device *dev) return macsonic_init(dev); } -static int __init mac_nubus_sonic_ethernet_addr(struct net_device *dev, +static int __devinit mac_nubus_sonic_ethernet_addr(struct net_device *dev, unsigned long prom_addr, int id) { @@ -424,7 +424,7 @@ static int __init mac_nubus_sonic_ethernet_addr(struct net_device *dev, return 0; } -static int __init macsonic_ident(struct nubus_dev *ndev) +static int __devinit macsonic_ident(struct nubus_dev *ndev) { if (ndev->dr_hw == NUBUS_DRHW_ASANTE_LC && ndev->dr_sw == NUBUS_DRSW_SONIC_LC) @@ -449,7 +449,7 @@ static int __init macsonic_ident(struct nubus_dev *ndev) return -1; } -static int __init mac_nubus_sonic_probe(struct net_device *dev) +static int __devinit mac_nubus_sonic_probe(struct net_device *dev) { static int slots; struct nubus_dev* ndev = NULL; @@ -562,7 +562,7 @@ static int __init mac_nubus_sonic_probe(struct net_device *dev) return macsonic_init(dev); } -static int __init mac_sonic_probe(struct platform_device *pdev) +static int __devinit mac_sonic_probe(struct platform_device *pdev) { struct net_device *dev; struct sonic_local *lp; @@ -575,6 +575,7 @@ static int __init mac_sonic_probe(struct platform_device *pdev) lp = netdev_priv(dev); lp->device = &pdev->dev; SET_NETDEV_DEV(dev, &pdev->dev); + platform_set_drvdata(pdev, dev); /* This will catch fatal stuff like -ENOMEM as well as success */ err = mac_onboard_sonic_probe(dev); diff --git a/drivers/net/mlx4/en_ethtool.c b/drivers/net/mlx4/en_ethtool.c index 091f99052c9..86467b444ac 100644 --- a/drivers/net/mlx4/en_ethtool.c +++ b/drivers/net/mlx4/en_ethtool.c @@ -220,7 +220,7 @@ static int mlx4_en_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) { cmd->autoneg = AUTONEG_DISABLE; cmd->supported = SUPPORTED_10000baseT_Full; - cmd->advertising = SUPPORTED_10000baseT_Full; + cmd->advertising = ADVERTISED_1000baseT_Full; if (netif_carrier_ok(dev)) { cmd->speed = SPEED_10000; cmd->duplex = DUPLEX_FULL; diff --git a/drivers/net/netxen/netxen_nic.h b/drivers/net/netxen/netxen_nic.h index e1cdba752e0..f86e05047d1 100644 --- a/drivers/net/netxen/netxen_nic.h +++ b/drivers/net/netxen/netxen_nic.h @@ -210,6 +210,7 @@ #define NETXEN_CTX_SIGNATURE 0xdee0 #define NETXEN_CTX_SIGNATURE_V2 0x0002dee0 #define NETXEN_CTX_RESET 0xbad0 +#define NETXEN_CTX_D3_RESET 0xacc0 #define NETXEN_RCV_PRODUCER(ringid) (ringid) #define PHAN_PEG_RCV_INITIALIZED 0xff01 @@ -773,6 +774,8 @@ struct nx_host_tx_ring { u32 crb_cmd_consumer; u32 num_desc; + struct netdev_queue *txq; + struct netxen_cmd_buffer *cmd_buf_arr; struct cmd_desc_type0 *desc_head; dma_addr_t phys_addr; diff --git a/drivers/net/netxen/netxen_nic_ctx.c b/drivers/net/netxen/netxen_nic_ctx.c index 4754f5cffad..9f8ae4719e2 100644 --- a/drivers/net/netxen/netxen_nic_ctx.c +++ b/drivers/net/netxen/netxen_nic_ctx.c @@ -684,10 +684,8 @@ int netxen_alloc_hw_resources(struct netxen_adapter *adapter) goto err_out_free; } else { err = netxen_init_old_ctx(adapter); - if (err) { - netxen_free_hw_resources(adapter); - return err; - } + if (err) + goto err_out_free; } return 0; @@ -708,15 +706,18 @@ void netxen_free_hw_resources(struct netxen_adapter *adapter) int port = adapter->portnum; if (adapter->fw_major >= 4) { - nx_fw_cmd_destroy_tx_ctx(adapter); nx_fw_cmd_destroy_rx_ctx(adapter); + nx_fw_cmd_destroy_tx_ctx(adapter); } else { netxen_api_lock(adapter); NXWR32(adapter, CRB_CTX_SIGNATURE_REG(port), - NETXEN_CTX_RESET | port); + NETXEN_CTX_D3_RESET | port); netxen_api_unlock(adapter); } + /* Allow dma queues to drain after context reset */ + msleep(20); + recv_ctx = &adapter->recv_ctx; if (recv_ctx->hwctx != NULL) { diff --git a/drivers/net/netxen/netxen_nic_hw.c b/drivers/net/netxen/netxen_nic_hw.c index ce3b89d2cbb..b9123d445c9 100644 --- a/drivers/net/netxen/netxen_nic_hw.c +++ b/drivers/net/netxen/netxen_nic_hw.c @@ -461,13 +461,14 @@ netxen_send_cmd_descs(struct netxen_adapter *adapter, i = 0; tx_ring = adapter->tx_ring; - netif_tx_lock_bh(adapter->netdev); + __netif_tx_lock_bh(tx_ring->txq); producer = tx_ring->producer; consumer = tx_ring->sw_consumer; - if (nr_desc >= find_diff_among(producer, consumer, tx_ring->num_desc)) { - netif_tx_unlock_bh(adapter->netdev); + if (nr_desc >= netxen_tx_avail(tx_ring)) { + netif_tx_stop_queue(tx_ring->txq); + __netif_tx_unlock_bh(tx_ring->txq); return -EBUSY; } @@ -490,7 +491,7 @@ netxen_send_cmd_descs(struct netxen_adapter *adapter, netxen_nic_update_cmd_producer(adapter, tx_ring); - netif_tx_unlock_bh(adapter->netdev); + __netif_tx_unlock_bh(tx_ring->txq); return 0; } diff --git a/drivers/net/netxen/netxen_nic_init.c b/drivers/net/netxen/netxen_nic_init.c index b899bd51fcd..7acf204e38c 100644 --- a/drivers/net/netxen/netxen_nic_init.c +++ b/drivers/net/netxen/netxen_nic_init.c @@ -184,6 +184,13 @@ void netxen_free_sw_resources(struct netxen_adapter *adapter) kfree(recv_ctx->rds_rings); skip_rds: + if (recv_ctx->sds_rings == NULL) + goto skip_sds; + + for(ring = 0; ring < adapter->max_sds_rings; ring++) + recv_ctx->sds_rings[ring].consumer = 0; + +skip_sds: if (adapter->tx_ring == NULL) return; @@ -214,6 +221,7 @@ int netxen_alloc_sw_resources(struct netxen_adapter *adapter) adapter->tx_ring = tx_ring; tx_ring->num_desc = adapter->num_txd; + tx_ring->txq = netdev_get_tx_queue(netdev, 0); cmd_buf_arr = vmalloc(TX_BUFF_RINGSIZE(tx_ring)); if (cmd_buf_arr == NULL) { @@ -1400,10 +1408,10 @@ int netxen_process_cmd_ring(struct netxen_adapter *adapter) smp_mb(); if (netif_queue_stopped(netdev) && netif_carrier_ok(netdev)) { - netif_tx_lock(netdev); + __netif_tx_lock(tx_ring->txq, smp_processor_id()); if (netxen_tx_avail(tx_ring) > TX_STOP_THRESH) netif_wake_queue(netdev); - netif_tx_unlock(netdev); + __netif_tx_unlock(tx_ring->txq); } } /* diff --git a/drivers/net/netxen/netxen_nic_main.c b/drivers/net/netxen/netxen_nic_main.c index 27539ddf94c..637ac8b89ba 100644 --- a/drivers/net/netxen/netxen_nic_main.c +++ b/drivers/net/netxen/netxen_nic_main.c @@ -215,9 +215,9 @@ netxen_napi_disable(struct netxen_adapter *adapter) for (ring = 0; ring < adapter->max_sds_rings; ring++) { sds_ring = &recv_ctx->sds_rings[ring]; - napi_disable(&sds_ring->napi); netxen_nic_disable_int(sds_ring); - synchronize_irq(sds_ring->irq); + napi_synchronize(&sds_ring->napi); + napi_disable(&sds_ring->napi); } } @@ -833,11 +833,11 @@ netxen_nic_up(struct netxen_adapter *adapter, struct net_device *netdev) adapter->ahw.linkup = 0; - netxen_napi_enable(adapter); - if (adapter->max_sds_rings > 1) netxen_config_rss(adapter, 1); + netxen_napi_enable(adapter); + if (adapter->capabilities & NX_FW_CAPABILITY_LINK_NOTIFICATION) netxen_linkevent_request(adapter, 1); else @@ -851,8 +851,9 @@ netxen_nic_up(struct netxen_adapter *adapter, struct net_device *netdev) static void netxen_nic_down(struct netxen_adapter *adapter, struct net_device *netdev) { + spin_lock(&adapter->tx_clean_lock); netif_carrier_off(netdev); - netif_stop_queue(netdev); + netif_tx_disable(netdev); if (adapter->stop_port) adapter->stop_port(adapter); @@ -863,9 +864,10 @@ netxen_nic_down(struct netxen_adapter *adapter, struct net_device *netdev) netxen_napi_disable(adapter); netxen_release_tx_buffers(adapter); + spin_unlock(&adapter->tx_clean_lock); - FLUSH_SCHEDULED_WORK(); del_timer_sync(&adapter->watchdog_timer); + FLUSH_SCHEDULED_WORK(); } @@ -943,8 +945,8 @@ err_out_free_sw: static void netxen_nic_detach(struct netxen_adapter *adapter) { - netxen_release_rx_buffers(adapter); netxen_free_hw_resources(adapter); + netxen_release_rx_buffers(adapter); netxen_nic_free_irq(adapter); netxen_free_sw_resources(adapter); @@ -1533,10 +1535,12 @@ static int netxen_nic_check_temp(struct netxen_adapter *adapter) printk(KERN_ALERT "%s: Device temperature %d degrees C exceeds" " maximum allowed. Hardware has been shut down.\n", - netxen_nic_driver_name, temp_val); + netdev->name, temp_val); + + netif_device_detach(netdev); + netxen_nic_down(adapter, netdev); + netxen_nic_detach(adapter); - netif_carrier_off(netdev); - netif_stop_queue(netdev); rv = 1; } else if (temp_state == NX_TEMP_WARN) { if (adapter->temp == NX_TEMP_NORMAL) { @@ -1544,13 +1548,13 @@ static int netxen_nic_check_temp(struct netxen_adapter *adapter) "%s: Device temperature %d degrees C " "exceeds operating range." " Immediate action needed.\n", - netxen_nic_driver_name, temp_val); + netdev->name, temp_val); } } else { if (adapter->temp == NX_TEMP_WARN) { printk(KERN_INFO "%s: Device temperature is now %d degrees C" - " in normal range.\n", netxen_nic_driver_name, + " in normal range.\n", netdev->name, temp_val); } } @@ -1623,7 +1627,7 @@ void netxen_watchdog_task(struct work_struct *work) struct netxen_adapter *adapter = container_of(work, struct netxen_adapter, watchdog_task); - if ((adapter->portnum == 0) && netxen_nic_check_temp(adapter)) + if (netxen_nic_check_temp(adapter)) return; if (!adapter->has_link_events) @@ -1645,6 +1649,9 @@ static void netxen_tx_timeout_task(struct work_struct *work) struct netxen_adapter *adapter = container_of(work, struct netxen_adapter, tx_timeout_task); + if (!netif_running(adapter->netdev)) + return; + printk(KERN_ERR "%s %s: transmit timeout, resetting.\n", netxen_nic_driver_name, adapter->netdev->name); @@ -1757,7 +1764,8 @@ static int netxen_nic_poll(struct napi_struct *napi, int budget) if ((work_done < budget) && tx_complete) { napi_complete(&sds_ring->napi); - netxen_nic_enable_int(sds_ring); + if (netif_running(adapter->netdev)) + netxen_nic_enable_int(sds_ring); } return work_done; diff --git a/drivers/net/pcmcia/3c589_cs.c b/drivers/net/pcmcia/3c589_cs.c index ec7cf5ac4f0..690b9c76d34 100644 --- a/drivers/net/pcmcia/3c589_cs.c +++ b/drivers/net/pcmcia/3c589_cs.c @@ -156,6 +156,7 @@ static struct net_device_stats *el3_get_stats(struct net_device *dev); static int el3_rx(struct net_device *dev); static int el3_close(struct net_device *dev); static void el3_tx_timeout(struct net_device *dev); +static void set_rx_mode(struct net_device *dev); static void set_multicast_list(struct net_device *dev); static const struct ethtool_ops netdev_ethtool_ops; @@ -488,8 +489,7 @@ static void tc589_reset(struct net_device *dev) /* Switch to register set 1 for normal use. */ EL3WINDOW(1); - /* Accept b-cast and phys addr only. */ - outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD); + set_rx_mode(dev); outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */ outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */ outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */ @@ -700,7 +700,7 @@ static irqreturn_t el3_interrupt(int irq, void *dev_id) if (fifo_diag & 0x2000) { /* Rx underrun */ tc589_wait_for_completion(dev, RxReset); - set_multicast_list(dev); + set_rx_mode(dev); outw(RxEnable, ioaddr + EL3_CMD); } outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD); @@ -905,14 +905,11 @@ static int el3_rx(struct net_device *dev) return 0; } -static void set_multicast_list(struct net_device *dev) +static void set_rx_mode(struct net_device *dev) { - struct el3_private *lp = netdev_priv(dev); - struct pcmcia_device *link = lp->p_dev; unsigned int ioaddr = dev->base_addr; u16 opts = SetRxFilter | RxStation | RxBroadcast; - if (!pcmcia_dev_present(link)) return; if (dev->flags & IFF_PROMISC) opts |= RxMulticast | RxProm; else if (dev->mc_count || (dev->flags & IFF_ALLMULTI)) @@ -920,6 +917,16 @@ static void set_multicast_list(struct net_device *dev) outw(opts, ioaddr + EL3_CMD); } +static void set_multicast_list(struct net_device *dev) +{ + struct el3_private *priv = netdev_priv(dev); + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + set_rx_mode(dev); + spin_unlock_irqrestore(&priv->lock, flags); +} + static int el3_close(struct net_device *dev) { struct el3_private *lp = netdev_priv(dev); diff --git a/drivers/net/phy/mdio-gpio.c b/drivers/net/phy/mdio-gpio.c index 33984b73723..22cdd451fb8 100644 --- a/drivers/net/phy/mdio-gpio.c +++ b/drivers/net/phy/mdio-gpio.c @@ -30,6 +30,7 @@ #ifdef CONFIG_OF_GPIO #include <linux/of_gpio.h> +#include <linux/of_mdio.h> #include <linux/of_platform.h> #endif @@ -81,13 +82,12 @@ static struct mdiobb_ops mdio_gpio_ops = { .get_mdio_data = mdio_get, }; -static int __devinit mdio_gpio_bus_init(struct device *dev, +static struct mii_bus * __devinit mdio_gpio_bus_init(struct device *dev, struct mdio_gpio_platform_data *pdata, int bus_id) { struct mii_bus *new_bus; struct mdio_gpio_info *bitbang; - int ret = -ENOMEM; int i; bitbang = kzalloc(sizeof(*bitbang), GFP_KERNEL); @@ -104,8 +104,6 @@ static int __devinit mdio_gpio_bus_init(struct device *dev, new_bus->name = "GPIO Bitbanged MDIO", - ret = -ENODEV; - new_bus->phy_mask = pdata->phy_mask; new_bus->irq = pdata->irqs; new_bus->parent = dev; @@ -129,15 +127,8 @@ static int __devinit mdio_gpio_bus_init(struct device *dev, dev_set_drvdata(dev, new_bus); - ret = mdiobus_register(new_bus); - if (ret) - goto out_free_all; - - return 0; + return new_bus; -out_free_all: - dev_set_drvdata(dev, NULL); - gpio_free(bitbang->mdio); out_free_mdc: gpio_free(bitbang->mdc); out_free_bus: @@ -145,30 +136,47 @@ out_free_bus: out_free_bitbang: kfree(bitbang); out: - return ret; + return NULL; } -static void __devexit mdio_gpio_bus_destroy(struct device *dev) +static void __devinit mdio_gpio_bus_deinit(struct device *dev) { struct mii_bus *bus = dev_get_drvdata(dev); struct mdio_gpio_info *bitbang = bus->priv; - mdiobus_unregister(bus); - free_mdio_bitbang(bus); dev_set_drvdata(dev, NULL); - gpio_free(bitbang->mdc); gpio_free(bitbang->mdio); + gpio_free(bitbang->mdc); + free_mdio_bitbang(bus); kfree(bitbang); } +static void __devexit mdio_gpio_bus_destroy(struct device *dev) +{ + struct mii_bus *bus = dev_get_drvdata(dev); + + mdiobus_unregister(bus); + mdio_gpio_bus_deinit(dev); +} + static int __devinit mdio_gpio_probe(struct platform_device *pdev) { struct mdio_gpio_platform_data *pdata = pdev->dev.platform_data; + struct mii_bus *new_bus; + int ret; if (!pdata) return -ENODEV; - return mdio_gpio_bus_init(&pdev->dev, pdata, pdev->id); + new_bus = mdio_gpio_bus_init(&pdev->dev, pdata, pdev->id); + if (!new_bus) + return -ENODEV; + + ret = mdiobus_register(new_bus); + if (ret) + mdio_gpio_bus_deinit(&pdev->dev); + + return ret; } static int __devexit mdio_gpio_remove(struct platform_device *pdev) @@ -179,29 +187,12 @@ static int __devexit mdio_gpio_remove(struct platform_device *pdev) } #ifdef CONFIG_OF_GPIO -static void __devinit add_phy(struct mdio_gpio_platform_data *pdata, - struct device_node *np) -{ - const u32 *data; - int len, id, irq; - - data = of_get_property(np, "reg", &len); - if (!data || len != 4) - return; - - id = *data; - pdata->phy_mask &= ~(1 << id); - - irq = of_irq_to_resource(np, 0, NULL); - if (irq) - pdata->irqs[id] = irq; -} static int __devinit mdio_ofgpio_probe(struct of_device *ofdev, const struct of_device_id *match) { - struct device_node *np = NULL; struct mdio_gpio_platform_data *pdata; + struct mii_bus *new_bus; int ret; pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); @@ -215,14 +206,18 @@ static int __devinit mdio_ofgpio_probe(struct of_device *ofdev, ret = of_get_gpio(ofdev->node, 1); if (ret < 0) - goto out_free; + goto out_free; pdata->mdio = ret; - while ((np = of_get_next_child(ofdev->node, np))) - if (!strcmp(np->type, "ethernet-phy")) - add_phy(pdata, np); + new_bus = mdio_gpio_bus_init(&ofdev->dev, pdata, pdata->mdc); + if (!new_bus) + return -ENODEV; - return mdio_gpio_bus_init(&ofdev->dev, pdata, pdata->mdc); + ret = of_mdiobus_register(new_bus, ofdev->node); + if (ret) + mdio_gpio_bus_deinit(&ofdev->dev); + + return ret; out_free: kfree(pdata); diff --git a/drivers/net/ppp_async.c b/drivers/net/ppp_async.c index 17c116bb332..6de8399d6dd 100644 --- a/drivers/net/ppp_async.c +++ b/drivers/net/ppp_async.c @@ -356,6 +356,7 @@ ppp_asynctty_receive(struct tty_struct *tty, const unsigned char *buf, if (!skb_queue_empty(&ap->rqueue)) tasklet_schedule(&ap->tsk); ap_put(ap); + tty_unthrottle(tty); } static void diff --git a/drivers/net/ppp_synctty.c b/drivers/net/ppp_synctty.c index aa3d39f38e2..d2fa2db1358 100644 --- a/drivers/net/ppp_synctty.c +++ b/drivers/net/ppp_synctty.c @@ -397,6 +397,7 @@ ppp_sync_receive(struct tty_struct *tty, const unsigned char *buf, if (!skb_queue_empty(&ap->rqueue)) tasklet_schedule(&ap->tsk); sp_put(ap); + tty_unthrottle(tty); } static void diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c index 4b53b58d75f..b82780d805f 100644 --- a/drivers/net/r8169.c +++ b/drivers/net/r8169.c @@ -2060,8 +2060,6 @@ rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) } } - pci_set_master(pdev); - /* ioremap MMIO region */ ioaddr = ioremap(pci_resource_start(pdev, region), R8169_REGS_SIZE); if (!ioaddr) { @@ -2089,6 +2087,8 @@ rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) RTL_W16(IntrStatus, 0xffff); + pci_set_master(pdev); + /* Identify chip attached to board */ rtl8169_get_mac_version(tp, ioaddr); @@ -3874,6 +3874,15 @@ static void rtl_shutdown(struct pci_dev *pdev) spin_unlock_irq(&tp->lock); if (system_state == SYSTEM_POWER_OFF) { + /* WoL fails with some 8168 when the receiver is disabled. */ + if (tp->features & RTL_FEATURE_WOL) { + pci_clear_master(pdev); + + RTL_W8(ChipCmd, CmdRxEnb); + /* PCI commit */ + RTL_R8(ChipCmd); + } + pci_wake_from_d3(pdev, true); pci_set_power_state(pdev, PCI_D3hot); } diff --git a/drivers/net/sc92031.c b/drivers/net/sc92031.c index 18821f217e1..e3156c97bb5 100644 --- a/drivers/net/sc92031.c +++ b/drivers/net/sc92031.c @@ -1593,6 +1593,7 @@ out: static struct pci_device_id sc92031_pci_device_id_table[] __devinitdata = { { PCI_DEVICE(PCI_VENDOR_ID_SILAN, 0x2031) }, { PCI_DEVICE(PCI_VENDOR_ID_SILAN, 0x8139) }, + { PCI_DEVICE(0x1088, 0x2031) }, { 0, } }; MODULE_DEVICE_TABLE(pci, sc92031_pci_device_id_table); diff --git a/drivers/net/skge.c b/drivers/net/skge.c index 60d502eef4f..543af2044f4 100644 --- a/drivers/net/skge.c +++ b/drivers/net/skge.c @@ -3854,8 +3854,10 @@ static struct net_device *skge_devinit(struct skge_hw *hw, int port, skge->speed = -1; skge->advertising = skge_supported_modes(hw); - if (device_may_wakeup(&hw->pdev->dev)) + if (device_can_wakeup(&hw->pdev->dev)) { skge->wol = wol_supported(hw) & WAKE_MAGIC; + device_set_wakeup_enable(&hw->pdev->dev, skge->wol); + } hw->dev[port] = dev; diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index daf961ab68b..3550c5dcd93 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -1151,14 +1151,7 @@ stopped: /* reset the Rx prefetch unit */ sky2_write32(hw, Y2_QADDR(rxq, PREF_UNIT_CTRL), PREF_UNIT_RST_SET); - - /* Reset the RAM Buffer receive queue */ - sky2_write8(hw, RB_ADDR(rxq, RB_CTRL), RB_RST_SET); - - /* Reset Rx MAC FIFO */ - sky2_write8(hw, SK_REG(sky2->port, RX_GMF_CTRL_T), GMF_RST_SET); - - sky2_read8(hw, B0_CTST); + mmiowb(); } /* Clean out receive buffer area, assumes receiver hardware stopped */ @@ -1825,12 +1818,6 @@ static int sky2_down(struct net_device *dev) if (netif_msg_ifdown(sky2)) printk(KERN_INFO PFX "%s: disabling interface\n", dev->name); - /* Disable port IRQ */ - imask = sky2_read32(hw, B0_IMSK); - imask &= ~portirq_msk[port]; - sky2_write32(hw, B0_IMSK, imask); - sky2_read32(hw, B0_IMSK); - /* Force flow control off */ sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_OFF); @@ -1870,8 +1857,6 @@ static int sky2_down(struct net_device *dev) sky2_write32(hw, RB_ADDR(txqaddr[port], RB_CTRL), RB_RST_SET); - sky2_rx_stop(sky2); - sky2_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_RST_SET); sky2_write8(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_RST_SET); @@ -1881,6 +1866,14 @@ static int sky2_down(struct net_device *dev) sky2_write32(hw, STAT_ISR_TIMER_CNT, 0); sky2_read8(hw, STAT_ISR_TIMER_CTRL); + sky2_rx_stop(sky2); + + /* Disable port IRQ */ + imask = sky2_read32(hw, B0_IMSK); + imask &= ~portirq_msk[port]; + sky2_write32(hw, B0_IMSK, imask); + sky2_read32(hw, B0_IMSK); + synchronize_irq(hw->pdev->irq); napi_synchronize(&hw->napi); diff --git a/drivers/net/smc91x.h b/drivers/net/smc91x.h index f1f773b17fe..57a159fac99 100644 --- a/drivers/net/smc91x.h +++ b/drivers/net/smc91x.h @@ -186,7 +186,8 @@ static inline void SMC_outw(u16 val, void __iomem *ioaddr, int reg) #define SMC_outsb(a, r, p, l) writesb((a) + (r), p, (l)) #define SMC_IRQ_FLAGS (-1) /* from resource */ -#elif defined(CONFIG_MACH_LOGICPD_PXA270) +#elif defined(CONFIG_MACH_LOGICPD_PXA270) \ + || defined(CONFIG_MACH_NOMADIK_8815NHK) #define SMC_CAN_USE_8BIT 0 #define SMC_CAN_USE_16BIT 1 diff --git a/drivers/net/tokenring/ibmtr.c b/drivers/net/tokenring/ibmtr.c index 9d896116cf7..08a6c41c159 100644 --- a/drivers/net/tokenring/ibmtr.c +++ b/drivers/net/tokenring/ibmtr.c @@ -1912,7 +1912,7 @@ static int __init ibmtr_init(void) find_turbo_adapters(io); - for (i = 0; io[i] && (i < IBMTR_MAX_ADAPTERS); i++) { + for (i = 0; i < IBMTR_MAX_ADAPTERS && io[i]; i++) { struct net_device *dev; irq[i] = 0; mem[i] = 0; diff --git a/drivers/net/ucc_geth.c b/drivers/net/ucc_geth.c index 40c6eba775c..3b957e6412e 100644 --- a/drivers/net/ucc_geth.c +++ b/drivers/net/ucc_geth.c @@ -1590,13 +1590,13 @@ static int init_phy(struct net_device *dev) priv->oldspeed = 0; priv->oldduplex = -1; - if (!ug_info->phy_node) - return 0; - phydev = of_phy_connect(dev, ug_info->phy_node, &adjust_link, 0, priv->phy_interface); + if (!phydev) + phydev = of_phy_connect_fixed_link(dev, &adjust_link, + priv->phy_interface); if (!phydev) { - printk("%s: Could not attach to PHY\n", dev->name); + dev_err(&dev->dev, "Could not attach to PHY\n"); return -ENODEV; } @@ -3608,9 +3608,7 @@ static int ucc_geth_probe(struct of_device* ofdev, const struct of_device_id *ma struct ucc_geth_private *ugeth = NULL; struct ucc_geth_info *ug_info; struct resource res; - struct device_node *phy; int err, ucc_num, max_speed = 0; - const u32 *fixed_link; const unsigned int *prop; const char *sprop; const void *mac_addr; @@ -3708,15 +3706,8 @@ static int ucc_geth_probe(struct of_device* ofdev, const struct of_device_id *ma ug_info->uf_info.regs = res.start; ug_info->uf_info.irq = irq_of_parse_and_map(np, 0); - fixed_link = of_get_property(np, "fixed-link", NULL); - if (fixed_link) { - phy = NULL; - } else { - phy = of_parse_phandle(np, "phy-handle", 0); - if (phy == NULL) - return -ENODEV; - } - ug_info->phy_node = phy; + + ug_info->phy_node = of_parse_phandle(np, "phy-handle", 0); /* Find the TBI PHY node. If it's not there, we don't support SGMII */ ug_info->tbi_node = of_parse_phandle(np, "tbi-handle", 0); @@ -3725,7 +3716,7 @@ static int ucc_geth_probe(struct of_device* ofdev, const struct of_device_id *ma prop = of_get_property(np, "phy-connection-type", NULL); if (!prop) { /* handle interface property present in old trees */ - prop = of_get_property(phy, "interface", NULL); + prop = of_get_property(ug_info->phy_node, "interface", NULL); if (prop != NULL) { phy_interface = enet_to_phy_interface[*prop]; max_speed = enet_to_speed[*prop]; diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig index a906d399813..c47237c2d63 100644 --- a/drivers/net/usb/Kconfig +++ b/drivers/net/usb/Kconfig @@ -369,4 +369,12 @@ config USB_NET_INT51X1 (Powerline Communications) solution with an Intellon INT51x1/INT5200 chip, like the "devolo dLan duo". +config USB_CDC_PHONET + tristate "CDC Phonet support" + depends on PHONET + help + Choose this option to support the Phonet interface to a Nokia + cellular modem, as found on most Nokia handsets with the + "PC suite" USB profile. + endmenu diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile index b870b0b1cbe..e17afb78f37 100644 --- a/drivers/net/usb/Makefile +++ b/drivers/net/usb/Makefile @@ -21,4 +21,5 @@ obj-$(CONFIG_USB_NET_ZAURUS) += zaurus.o obj-$(CONFIG_USB_NET_MCS7830) += mcs7830.o obj-$(CONFIG_USB_USBNET) += usbnet.o obj-$(CONFIG_USB_NET_INT51X1) += int51x1.o +obj-$(CONFIG_USB_CDC_PHONET) += cdc-phonet.o diff --git a/drivers/net/usb/cdc-phonet.c b/drivers/net/usb/cdc-phonet.c new file mode 100644 index 00000000000..792af72da8a --- /dev/null +++ b/drivers/net/usb/cdc-phonet.c @@ -0,0 +1,461 @@ +/* + * phonet.c -- USB CDC Phonet host driver + * + * Copyright (C) 2008-2009 Nokia Corporation. All rights reserved. + * + * Author: Rémi Denis-Courmont + * + * 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. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/usb.h> +#include <linux/usb/cdc.h> +#include <linux/netdevice.h> +#include <linux/if_arp.h> +#include <linux/if_phonet.h> + +#define PN_MEDIA_USB 0x1B + +static const unsigned rxq_size = 17; + +struct usbpn_dev { + struct net_device *dev; + + struct usb_interface *intf, *data_intf; + struct usb_device *usb; + unsigned int tx_pipe, rx_pipe; + u8 active_setting; + u8 disconnected; + + unsigned tx_queue; + spinlock_t tx_lock; + + spinlock_t rx_lock; + struct sk_buff *rx_skb; + struct urb *urbs[0]; +}; + +static void tx_complete(struct urb *req); +static void rx_complete(struct urb *req); + +/* + * Network device callbacks + */ +static int usbpn_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct usbpn_dev *pnd = netdev_priv(dev); + struct urb *req = NULL; + unsigned long flags; + int err; + + if (skb->protocol != htons(ETH_P_PHONET)) + goto drop; + + req = usb_alloc_urb(0, GFP_ATOMIC); + if (!req) + goto drop; + usb_fill_bulk_urb(req, pnd->usb, pnd->tx_pipe, skb->data, skb->len, + tx_complete, skb); + req->transfer_flags = URB_ZERO_PACKET; + err = usb_submit_urb(req, GFP_ATOMIC); + if (err) { + usb_free_urb(req); + goto drop; + } + + spin_lock_irqsave(&pnd->tx_lock, flags); + pnd->tx_queue++; + if (pnd->tx_queue >= dev->tx_queue_len) + netif_stop_queue(dev); + spin_unlock_irqrestore(&pnd->tx_lock, flags); + return 0; + +drop: + dev_kfree_skb(skb); + dev->stats.tx_dropped++; + return 0; +} + +static void tx_complete(struct urb *req) +{ + struct sk_buff *skb = req->context; + struct net_device *dev = skb->dev; + struct usbpn_dev *pnd = netdev_priv(dev); + + switch (req->status) { + case 0: + dev->stats.tx_bytes += skb->len; + break; + + case -ENOENT: + case -ECONNRESET: + case -ESHUTDOWN: + dev->stats.tx_aborted_errors++; + default: + dev->stats.tx_errors++; + dev_dbg(&dev->dev, "TX error (%d)\n", req->status); + } + dev->stats.tx_packets++; + + spin_lock(&pnd->tx_lock); + pnd->tx_queue--; + netif_wake_queue(dev); + spin_unlock(&pnd->tx_lock); + + dev_kfree_skb_any(skb); + usb_free_urb(req); +} + +static int rx_submit(struct usbpn_dev *pnd, struct urb *req, gfp_t gfp_flags) +{ + struct net_device *dev = pnd->dev; + struct page *page; + int err; + + page = __netdev_alloc_page(dev, gfp_flags); + if (!page) + return -ENOMEM; + + usb_fill_bulk_urb(req, pnd->usb, pnd->rx_pipe, page_address(page), + PAGE_SIZE, rx_complete, dev); + req->transfer_flags = 0; + err = usb_submit_urb(req, gfp_flags); + if (unlikely(err)) { + dev_dbg(&dev->dev, "RX submit error (%d)\n", err); + netdev_free_page(dev, page); + } + return err; +} + +static void rx_complete(struct urb *req) +{ + struct net_device *dev = req->context; + struct usbpn_dev *pnd = netdev_priv(dev); + struct page *page = virt_to_page(req->transfer_buffer); + struct sk_buff *skb; + unsigned long flags; + + switch (req->status) { + case 0: + spin_lock_irqsave(&pnd->rx_lock, flags); + skb = pnd->rx_skb; + if (!skb) { + skb = pnd->rx_skb = netdev_alloc_skb(dev, 12); + if (likely(skb)) { + /* Can't use pskb_pull() on page in IRQ */ + memcpy(skb_put(skb, 1), page_address(page), 1); + skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, + page, 1, req->actual_length); + page = NULL; + } + } else { + skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, + page, 0, req->actual_length); + page = NULL; + } + if (req->actual_length < PAGE_SIZE) + pnd->rx_skb = NULL; /* Last fragment */ + else + skb = NULL; + spin_unlock_irqrestore(&pnd->rx_lock, flags); + if (skb) { + skb->protocol = htons(ETH_P_PHONET); + skb_reset_mac_header(skb); + __skb_pull(skb, 1); + skb->dev = dev; + dev->stats.rx_packets++; + dev->stats.rx_bytes += skb->len; + + netif_rx(skb); + } + goto resubmit; + + case -ENOENT: + case -ECONNRESET: + case -ESHUTDOWN: + req = NULL; + break; + + case -EOVERFLOW: + dev->stats.rx_over_errors++; + dev_dbg(&dev->dev, "RX overflow\n"); + break; + + case -EILSEQ: + dev->stats.rx_crc_errors++; + break; + } + + dev->stats.rx_errors++; +resubmit: + if (page) + netdev_free_page(dev, page); + if (req) + rx_submit(pnd, req, GFP_ATOMIC); +} + +static int usbpn_close(struct net_device *dev); + +static int usbpn_open(struct net_device *dev) +{ + struct usbpn_dev *pnd = netdev_priv(dev); + int err; + unsigned i; + unsigned num = pnd->data_intf->cur_altsetting->desc.bInterfaceNumber; + + err = usb_set_interface(pnd->usb, num, pnd->active_setting); + if (err) + return err; + + for (i = 0; i < rxq_size; i++) { + struct urb *req = usb_alloc_urb(0, GFP_KERNEL); + + if (!req || rx_submit(pnd, req, GFP_KERNEL)) { + usbpn_close(dev); + return -ENOMEM; + } + pnd->urbs[i] = req; + } + + netif_wake_queue(dev); + return 0; +} + +static int usbpn_close(struct net_device *dev) +{ + struct usbpn_dev *pnd = netdev_priv(dev); + unsigned i; + unsigned num = pnd->data_intf->cur_altsetting->desc.bInterfaceNumber; + + netif_stop_queue(dev); + + for (i = 0; i < rxq_size; i++) { + struct urb *req = pnd->urbs[i]; + + if (!req) + continue; + usb_kill_urb(req); + usb_free_urb(req); + pnd->urbs[i] = NULL; + } + + return usb_set_interface(pnd->usb, num, !pnd->active_setting); +} + +static int usbpn_set_mtu(struct net_device *dev, int new_mtu) +{ + if ((new_mtu < PHONET_MIN_MTU) || (new_mtu > PHONET_MAX_MTU)) + return -EINVAL; + + dev->mtu = new_mtu; + return 0; +} + +static const struct net_device_ops usbpn_ops = { + .ndo_open = usbpn_open, + .ndo_stop = usbpn_close, + .ndo_start_xmit = usbpn_xmit, + .ndo_change_mtu = usbpn_set_mtu, +}; + +static void usbpn_setup(struct net_device *dev) +{ + dev->features = 0; + dev->netdev_ops = &usbpn_ops, + dev->header_ops = &phonet_header_ops; + dev->type = ARPHRD_PHONET; + dev->flags = IFF_POINTOPOINT | IFF_NOARP; + dev->mtu = PHONET_MAX_MTU; + dev->hard_header_len = 1; + dev->dev_addr[0] = PN_MEDIA_USB; + dev->addr_len = 1; + dev->tx_queue_len = 3; + + dev->destructor = free_netdev; +} + +/* + * USB driver callbacks + */ +static struct usb_device_id usbpn_ids[] = { + { + .match_flags = USB_DEVICE_ID_MATCH_VENDOR + | USB_DEVICE_ID_MATCH_INT_CLASS + | USB_DEVICE_ID_MATCH_INT_SUBCLASS, + .idVendor = 0x0421, /* Nokia */ + .bInterfaceClass = USB_CLASS_COMM, + .bInterfaceSubClass = 0xFE, + }, + { }, +}; + +MODULE_DEVICE_TABLE(usb, usbpn_ids); + +static struct usb_driver usbpn_driver; + +int usbpn_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + static const char ifname[] = "usbpn%d"; + const struct usb_cdc_union_desc *union_header = NULL; + const struct usb_cdc_header_desc *phonet_header = NULL; + const struct usb_host_interface *data_desc; + struct usb_interface *data_intf; + struct usb_device *usbdev = interface_to_usbdev(intf); + struct net_device *dev; + struct usbpn_dev *pnd; + u8 *data; + int len, err; + + data = intf->altsetting->extra; + len = intf->altsetting->extralen; + while (len >= 3) { + u8 dlen = data[0]; + if (dlen < 3) + return -EINVAL; + + /* bDescriptorType */ + if (data[1] == USB_DT_CS_INTERFACE) { + /* bDescriptorSubType */ + switch (data[2]) { + case USB_CDC_UNION_TYPE: + if (union_header || dlen < 5) + break; + union_header = + (struct usb_cdc_union_desc *)data; + break; + case 0xAB: + if (phonet_header || dlen < 5) + break; + phonet_header = + (struct usb_cdc_header_desc *)data; + break; + } + } + data += dlen; + len -= dlen; + } + + if (!union_header || !phonet_header) + return -EINVAL; + + data_intf = usb_ifnum_to_if(usbdev, union_header->bSlaveInterface0); + if (data_intf == NULL) + return -ENODEV; + /* Data interface has one inactive and one active setting */ + if (data_intf->num_altsetting != 2) + return -EINVAL; + if (data_intf->altsetting[0].desc.bNumEndpoints == 0 + && data_intf->altsetting[1].desc.bNumEndpoints == 2) + data_desc = data_intf->altsetting + 1; + else + if (data_intf->altsetting[0].desc.bNumEndpoints == 2 + && data_intf->altsetting[1].desc.bNumEndpoints == 0) + data_desc = data_intf->altsetting; + else + return -EINVAL; + + dev = alloc_netdev(sizeof(*pnd) + sizeof(pnd->urbs[0]) * rxq_size, + ifname, usbpn_setup); + if (!dev) + return -ENOMEM; + + pnd = netdev_priv(dev); + SET_NETDEV_DEV(dev, &intf->dev); + netif_stop_queue(dev); + + pnd->dev = dev; + pnd->usb = usb_get_dev(usbdev); + pnd->intf = intf; + pnd->data_intf = data_intf; + spin_lock_init(&pnd->tx_lock); + spin_lock_init(&pnd->rx_lock); + /* Endpoints */ + if (usb_pipein(data_desc->endpoint[0].desc.bEndpointAddress)) { + pnd->rx_pipe = usb_rcvbulkpipe(usbdev, + data_desc->endpoint[0].desc.bEndpointAddress); + pnd->tx_pipe = usb_sndbulkpipe(usbdev, + data_desc->endpoint[1].desc.bEndpointAddress); + } else { + pnd->rx_pipe = usb_rcvbulkpipe(usbdev, + data_desc->endpoint[1].desc.bEndpointAddress); + pnd->tx_pipe = usb_sndbulkpipe(usbdev, + data_desc->endpoint[0].desc.bEndpointAddress); + } + pnd->active_setting = data_desc - data_intf->altsetting; + + err = usb_driver_claim_interface(&usbpn_driver, data_intf, pnd); + if (err) + goto out; + + /* Force inactive mode until the network device is brought UP */ + usb_set_interface(usbdev, union_header->bSlaveInterface0, + !pnd->active_setting); + usb_set_intfdata(intf, pnd); + + err = register_netdev(dev); + if (err) { + usb_driver_release_interface(&usbpn_driver, data_intf); + goto out; + } + + dev_dbg(&dev->dev, "USB CDC Phonet device found\n"); + return 0; + +out: + usb_set_intfdata(intf, NULL); + free_netdev(dev); + return err; +} + +static void usbpn_disconnect(struct usb_interface *intf) +{ + struct usbpn_dev *pnd = usb_get_intfdata(intf); + struct usb_device *usb = pnd->usb; + + if (pnd->disconnected) + return; + + pnd->disconnected = 1; + usb_driver_release_interface(&usbpn_driver, + (pnd->intf == intf) ? pnd->data_intf : pnd->intf); + unregister_netdev(pnd->dev); + usb_put_dev(usb); +} + +static struct usb_driver usbpn_driver = { + .name = "cdc_phonet", + .probe = usbpn_probe, + .disconnect = usbpn_disconnect, + .id_table = usbpn_ids, +}; + +static int __init usbpn_init(void) +{ + return usb_register(&usbpn_driver); +} + +static void __exit usbpn_exit(void) +{ + usb_deregister(&usbpn_driver); +} + +module_init(usbpn_init); +module_exit(usbpn_exit); + +MODULE_AUTHOR("Remi Denis-Courmont"); +MODULE_DESCRIPTION("USB CDC Phonet host interface"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/usb/cdc_eem.c b/drivers/net/usb/cdc_eem.c index cd35d50e46d..45cebfb302c 100644 --- a/drivers/net/usb/cdc_eem.c +++ b/drivers/net/usb/cdc_eem.c @@ -311,7 +311,7 @@ static int eem_rx_fixup(struct usbnet *dev, struct sk_buff *skb) * bmCRC = 0 : CRC = 0xDEADBEEF */ if (header & BIT(14)) - crc2 = ~crc32_le(~0, skb2->data, len); + crc2 = ~crc32_le(~0, skb2->data, skb2->len); else crc2 = 0xdeadbeef; diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index ea045151f95..029c1bc7468 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -2970,6 +2970,9 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, if (modparam_nohwcrypt) return -EOPNOTSUPP; + if (sc->opmode == NL80211_IFTYPE_AP) + return -EOPNOTSUPP; + switch (key->alg) { case ALG_WEP: case ALG_TKIP: diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index 1aeafb511dd..aad259b4c19 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -478,6 +478,18 @@ void ath9k_ani_reset(struct ath_hw *ah) "Reset ANI state opmode %u\n", ah->opmode); ah->stats.ast_ani_reset++; + if (ah->opmode == NL80211_IFTYPE_AP) { + /* + * ath9k_hw_ani_control() will only process items set on + * ah->ani_function + */ + if (IS_CHAN_2GHZ(chan)) + ah->ani_function = (ATH9K_ANI_SPUR_IMMUNITY_LEVEL | + ATH9K_ANI_FIRSTEP_LEVEL); + else + ah->ani_function = 0; + } + ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL, 0); ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL, 0); ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, 0); diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c index eef370bd121..bf3d25ba7be 100644 --- a/drivers/net/wireless/ath/regd.c +++ b/drivers/net/wireless/ath/regd.c @@ -474,6 +474,21 @@ ath_regd_init_wiphy(struct ath_regulatory *reg, return 0; } +/* + * Some users have reported their EEPROM programmed with + * 0x8000 set, this is not a supported regulatory domain + * but since we have more than one user with it we need + * a solution for them. We default to 0x64, which is the + * default Atheros world regulatory domain. + */ +static void ath_regd_sanitize(struct ath_regulatory *reg) +{ + if (reg->current_rd != COUNTRY_ERD_FLAG) + return; + printk(KERN_DEBUG "ath: EEPROM regdomain sanitized\n"); + reg->current_rd = 0x64; +} + int ath_regd_init(struct ath_regulatory *reg, struct wiphy *wiphy, @@ -486,6 +501,8 @@ ath_regd_init(struct ath_regulatory *reg, if (!reg) return -EINVAL; + ath_regd_sanitize(reg); + printk(KERN_DEBUG "ath: EEPROM regdomain: 0x%0x\n", reg->current_rd); if (!ath_regd_is_eeprom_valid(reg)) { diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 6d1519e1f01..355f50ea7fe 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -2675,12 +2675,10 @@ static ssize_t show_power_level(struct device *d, struct device_attribute *attr, char *buf) { struct iwl_priv *priv = dev_get_drvdata(d); - int mode = priv->power_data.user_power_setting; int level = priv->power_data.power_mode; char *p = buf; - p += sprintf(p, "INDEX:%d\t", level); - p += sprintf(p, "USER:%d\n", mode); + p += sprintf(p, "%d\n", level); return p - buf + 1; } diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c index 85ae7a62109..9bbeec9427f 100644 --- a/drivers/net/wireless/iwlwifi/iwl-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-tx.c @@ -872,7 +872,8 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) iwl_print_hex_dump(priv, IWL_DL_TX, (u8 *)tx_cmd->hdr, hdr_len); /* Set up entry for this TFD in Tx byte-count array */ - priv->cfg->ops->lib->txq_update_byte_cnt_tbl(priv, txq, + if (info->flags & IEEE80211_TX_CTL_AMPDU) + priv->cfg->ops->lib->txq_update_byte_cnt_tbl(priv, txq, le16_to_cpu(tx_cmd->len)); pci_dma_sync_single_for_device(priv->pci_dev, txcmd_phys, diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index cb9bd4c8f25..956798f2c80 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -3643,12 +3643,10 @@ static ssize_t show_power_level(struct device *d, struct device_attribute *attr, char *buf) { struct iwl_priv *priv = dev_get_drvdata(d); - int mode = priv->power_data.user_power_setting; int level = priv->power_data.power_mode; char *p = buf; - p += sprintf(p, "INDEX:%d\t", level); - p += sprintf(p, "USER:%d\n", mode); + p += sprintf(p, "%d\n", level); return p - buf + 1; } diff --git a/drivers/net/wireless/iwmc3200wifi/netdev.c b/drivers/net/wireless/iwmc3200wifi/netdev.c index aaa20c6885c..aea5ccf24cc 100644 --- a/drivers/net/wireless/iwmc3200wifi/netdev.c +++ b/drivers/net/wireless/iwmc3200wifi/netdev.c @@ -151,8 +151,8 @@ void iwm_if_free(struct iwm_priv *iwm) return; free_netdev(iwm_to_ndev(iwm)); - iwm_wdev_free(iwm); iwm_priv_deinit(iwm); + iwm_wdev_free(iwm); } int iwm_if_add(struct iwm_priv *iwm) diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c index 01db705a38e..685098148e1 100644 --- a/drivers/net/wireless/libertas/cmd.c +++ b/drivers/net/wireless/libertas/cmd.c @@ -135,8 +135,14 @@ int lbs_update_hw_spec(struct lbs_private *priv) /* Clamp region code to 8-bit since FW spec indicates that it should * only ever be 8-bit, even though the field size is 16-bit. Some firmware * returns non-zero high 8 bits here. + * + * Firmware version 4.0.102 used in CF8381 has region code shifted. We + * need to check for this problem and handle it properly. */ - priv->regioncode = le16_to_cpu(cmd.regioncode) & 0xFF; + if (MRVL_FW_MAJOR_REV(priv->fwrelease) == MRVL_FW_V4) + priv->regioncode = (le16_to_cpu(cmd.regioncode) >> 8) & 0xFF; + else + priv->regioncode = le16_to_cpu(cmd.regioncode) & 0xFF; for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) { /* use the region code to search for the index */ diff --git a/drivers/net/wireless/libertas/defs.h b/drivers/net/wireless/libertas/defs.h index 48da157d6cd..72f3479a4d7 100644 --- a/drivers/net/wireless/libertas/defs.h +++ b/drivers/net/wireless/libertas/defs.h @@ -234,6 +234,8 @@ static inline void lbs_deb_hex(unsigned int grp, const char *prompt, u8 *buf, in /** Mesh enable bit in FW capability */ #define MESH_CAPINFO_ENABLE_MASK (1<<16) +/** FW definition from Marvell v4 */ +#define MRVL_FW_V4 (0x04) /** FW definition from Marvell v5 */ #define MRVL_FW_V5 (0x05) /** FW definition from Marvell v10 */ diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index a111bda392e..7916ca3f84c 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -709,7 +709,7 @@ static const struct ieee80211_ops mac80211_hwsim_ops = static void mac80211_hwsim_free(void) { struct list_head tmplist, *i, *tmp; - struct mac80211_hwsim_data *data; + struct mac80211_hwsim_data *data, *tmpdata; INIT_LIST_HEAD(&tmplist); @@ -718,7 +718,7 @@ static void mac80211_hwsim_free(void) list_move(i, &tmplist); spin_unlock_bh(&hwsim_radio_lock); - list_for_each_entry(data, &tmplist, list) { + list_for_each_entry_safe(data, tmpdata, &tmplist, list) { debugfs_remove(data->debugfs_group); debugfs_remove(data->debugfs_ps); debugfs_remove(data->debugfs); @@ -1167,8 +1167,8 @@ static void __exit exit_mac80211_hwsim(void) { printk(KERN_DEBUG "mac80211_hwsim: unregister radios\n"); - unregister_netdev(hwsim_mon); mac80211_hwsim_free(); + unregister_netdev(hwsim_mon); } diff --git a/drivers/net/wireless/p54/p54spi.c b/drivers/net/wireless/p54/p54spi.c index 83116baeb11..72c7dbd39d0 100644 --- a/drivers/net/wireless/p54/p54spi.c +++ b/drivers/net/wireless/p54/p54spi.c @@ -635,7 +635,7 @@ static int __devinit p54spi_probe(struct spi_device *spi) hw = p54_init_common(sizeof(*priv)); if (!hw) { - dev_err(&priv->spi->dev, "could not alloc ieee80211_hw"); + dev_err(&spi->dev, "could not alloc ieee80211_hw"); return -ENOMEM; } diff --git a/drivers/net/wireless/rt2x00/rt2500usb.c b/drivers/net/wireless/rt2x00/rt2500usb.c index 66daf68ff0e..ce75426764a 100644 --- a/drivers/net/wireless/rt2x00/rt2500usb.c +++ b/drivers/net/wireless/rt2x00/rt2500usb.c @@ -1550,7 +1550,9 @@ static int rt2500usb_init_eeprom(struct rt2x00_dev *rt2x00dev) rt2500usb_register_read(rt2x00dev, MAC_CSR0, ®); rt2x00_set_chip(rt2x00dev, RT2570, value, reg); - if (!rt2x00_check_rev(&rt2x00dev->chip, 0x000ffff0, 0)) { + if (!rt2x00_check_rev(&rt2x00dev->chip, 0x000ffff0, 0) || + rt2x00_check_rev(&rt2x00dev->chip, 0x0000000f, 0)) { + ERROR(rt2x00dev, "Invalid RT chipset detected.\n"); return -ENODEV; } diff --git a/drivers/net/wireless/rtl818x/rtl8187_leds.c b/drivers/net/wireless/rtl818x/rtl8187_leds.c index b4425359224..cf9f899fe0e 100644 --- a/drivers/net/wireless/rtl818x/rtl8187_leds.c +++ b/drivers/net/wireless/rtl818x/rtl8187_leds.c @@ -208,11 +208,12 @@ void rtl8187_leds_exit(struct ieee80211_hw *dev) { struct rtl8187_priv *priv = dev->priv; - rtl8187_unregister_led(&priv->led_tx); /* turn the LED off before exiting */ queue_delayed_work(dev->workqueue, &priv->led_off, 0); cancel_delayed_work_sync(&priv->led_off); + cancel_delayed_work_sync(&priv->led_on); rtl8187_unregister_led(&priv->led_rx); + rtl8187_unregister_led(&priv->led_tx); } #endif /* def CONFIG_RTL8187_LED */ diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c index aee967d7f76..bacaa536fd5 100644 --- a/drivers/of/of_mdio.c +++ b/drivers/of/of_mdio.c @@ -9,6 +9,10 @@ * out of the OpenFirmware device tree and using it to populate an mii_bus. */ +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/netdevice.h> +#include <linux/err.h> #include <linux/phy.h> #include <linux/of.h> #include <linux/of_mdio.h> @@ -137,3 +141,41 @@ struct phy_device *of_phy_connect(struct net_device *dev, return phy_connect_direct(dev, phy, hndlr, flags, iface) ? NULL : phy; } EXPORT_SYMBOL(of_phy_connect); + +/** + * of_phy_connect_fixed_link - Parse fixed-link property and return a dummy phy + * @dev: pointer to net_device claiming the phy + * @hndlr: Link state callback for the network device + * @iface: PHY data interface type + * + * This function is a temporary stop-gap and will be removed soon. It is + * only to support the fs_enet, ucc_geth and gianfar Ethernet drivers. Do + * not call this function from new drivers. + */ +struct phy_device *of_phy_connect_fixed_link(struct net_device *dev, + void (*hndlr)(struct net_device *), + phy_interface_t iface) +{ + struct device_node *net_np; + char bus_id[MII_BUS_ID_SIZE + 3]; + struct phy_device *phy; + const u32 *phy_id; + int sz; + + if (!dev->dev.parent) + return NULL; + + net_np = dev_archdata_get_node(&dev->dev.parent->archdata); + if (!net_np) + return NULL; + + phy_id = of_get_property(net_np, "fixed-link", &sz); + if (!phy_id || sz < sizeof(*phy_id)) + return NULL; + + sprintf(bus_id, PHY_ID_FMT, "0", phy_id[0]); + + phy = phy_connect(dev, bus_id, hndlr, 0, iface); + return IS_ERR(phy) ? NULL : phy; +} +EXPORT_SYMBOL(of_phy_connect_fixed_link); diff --git a/drivers/parisc/ccio-dma.c b/drivers/parisc/ccio-dma.c index 0f0e0b919ef..a45b0c0d574 100644 --- a/drivers/parisc/ccio-dma.c +++ b/drivers/parisc/ccio-dma.c @@ -70,7 +70,6 @@ #undef CCIO_COLLECT_STATS #endif -#include <linux/proc_fs.h> #include <asm/runway.h> /* for proc_runway_root */ #ifdef DEBUG_CCIO_INIT diff --git a/drivers/parisc/dino.c b/drivers/parisc/dino.c index c590974e981..d69bde6a234 100644 --- a/drivers/parisc/dino.c +++ b/drivers/parisc/dino.c @@ -614,7 +614,7 @@ dino_fixup_bus(struct pci_bus *bus) dev_name(&bus->self->dev), i, bus->self->resource[i].start, bus->self->resource[i].end); - pci_assign_resource(bus->self, i); + WARN_ON(pci_assign_resource(bus->self, i)); DBG("DEBUG %s after assign %d [0x%lx,0x%lx]\n", dev_name(&bus->self->dev), i, bus->self->resource[i].start, diff --git a/drivers/parisc/eisa_eeprom.c b/drivers/parisc/eisa_eeprom.c index 685d94e69d4..8c0b26e9b98 100644 --- a/drivers/parisc/eisa_eeprom.c +++ b/drivers/parisc/eisa_eeprom.c @@ -55,7 +55,7 @@ static ssize_t eisa_eeprom_read(struct file * file, ssize_t ret; int i; - if (*ppos >= HPEE_MAX_LENGTH) + if (*ppos < 0 || *ppos >= HPEE_MAX_LENGTH) return 0; count = *ppos + count < HPEE_MAX_LENGTH ? count : HPEE_MAX_LENGTH - *ppos; diff --git a/drivers/parisc/hppb.c b/drivers/parisc/hppb.c index 13856415b43..815db175d42 100644 --- a/drivers/parisc/hppb.c +++ b/drivers/parisc/hppb.c @@ -62,7 +62,8 @@ static int hppb_probe(struct parisc_device *dev) } card = card->next; } - printk(KERN_INFO "Found GeckoBoa at 0x%x\n", dev->hpa.start); + printk(KERN_INFO "Found GeckoBoa at 0x%llx\n", + (unsigned long long) dev->hpa.start); card->hpa = dev->hpa.start; card->mmio_region.name = "HP-PB Bus"; @@ -73,8 +74,10 @@ static int hppb_probe(struct parisc_device *dev) status = ccio_request_resource(dev, &card->mmio_region); if(status < 0) { - printk(KERN_ERR "%s: failed to claim HP-PB bus space (%08x, %08x)\n", - __FILE__, card->mmio_region.start, card->mmio_region.end); + printk(KERN_ERR "%s: failed to claim HP-PB " + "bus space (0x%08llx, 0x%08llx)\n", + __FILE__, (unsigned long long) card->mmio_region.start, + (unsigned long long) card->mmio_region.end); } return 0; diff --git a/drivers/parisc/lba_pci.c b/drivers/parisc/lba_pci.c index ede614616f8..3aeb3279c92 100644 --- a/drivers/parisc/lba_pci.c +++ b/drivers/parisc/lba_pci.c @@ -992,7 +992,7 @@ lba_pat_resources(struct parisc_device *pa_dev, struct lba_device *lba_dev) return; io_pdc_cell = kzalloc(sizeof(pdc_pat_cell_mod_maddr_block_t), GFP_KERNEL); - if (!pa_pdc_cell) { + if (!io_pdc_cell) { kfree(pa_pdc_cell); return; } diff --git a/drivers/parisc/pdc_stable.c b/drivers/parisc/pdc_stable.c index f9f9a5f1bbd..13a64bc081b 100644 --- a/drivers/parisc/pdc_stable.c +++ b/drivers/parisc/pdc_stable.c @@ -370,7 +370,7 @@ pdcspath_layer_read(struct pdcspath_entry *entry, char *buf) if (!i) /* entry is not ready */ return -ENODATA; - for (i = 0; devpath->layers[i] && (likely(i < 6)); i++) + for (i = 0; i < 6 && devpath->layers[i]; i++) out += sprintf(out, "%u ", devpath->layers[i]); out += sprintf(out, "\n"); diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index b711fb7181e..1898c7b4790 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -100,16 +100,16 @@ int pci_claim_resource(struct pci_dev *dev, int resource) { struct resource *res = &dev->resource[resource]; struct resource *root; - char *dtype = resource < PCI_BRIDGE_RESOURCES ? "device" : "bridge"; int err; root = pci_find_parent_resource(dev, res); err = -EINVAL; if (root != NULL) - err = insert_resource(root, res); + err = request_resource(root, res); if (err) { + const char *dtype = resource < PCI_BRIDGE_RESOURCES ? "device" : "bridge"; dev_err(&dev->dev, "BAR %d: %s of %s %pR\n", resource, root ? "address space collision on" : diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 46dad12f952..77c6097ced8 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -277,31 +277,6 @@ config THINKPAD_ACPI_UNSAFE_LEDS Say N here, unless you are building a kernel for your own use, and need to control the important firmware LEDs. -config THINKPAD_ACPI_DOCK - bool "Legacy Docking Station Support" - depends on THINKPAD_ACPI - depends on ACPI_DOCK=n - default n - ---help--- - Allows the thinkpad_acpi driver to handle docking station events. - This support was made obsolete by the generic ACPI docking station - support (CONFIG_ACPI_DOCK). It will allow locking and removing the - laptop from the docking station, but will not properly connect PCI - devices. - - If you are not sure, say N here. - -config THINKPAD_ACPI_BAY - bool "Legacy Removable Bay Support" - depends on THINKPAD_ACPI - default y - ---help--- - Allows the thinkpad_acpi driver to handle removable bays. It will - electrically disable the device in the bay, and also generate - notifications when the bay lever is ejected or inserted. - - If you are not sure, say Y here. - config THINKPAD_ACPI_VIDEO bool "Video output control support" depends on THINKPAD_ACPI diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index be2fd6f9163..fb45f5ee8df 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -973,7 +973,7 @@ static int acer_rfkill_set(void *data, bool blocked) { acpi_status status; u32 cap = (unsigned long)data; - status = set_u32(!!blocked, cap); + status = set_u32(!blocked, cap); if (ACPI_FAILURE(status)) return -ENODEV; return 0; diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index ec560f16d72..222ffb892f2 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -143,6 +143,7 @@ struct eeepc_hotk { struct rfkill *bluetooth_rfkill; struct rfkill *wwan3g_rfkill; struct hotplug_slot *hotplug_slot; + struct work_struct hotplug_work; }; /* The actual device the driver binds to */ @@ -660,7 +661,7 @@ static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot, return 0; } -static void eeepc_rfkill_hotplug(void) +static void eeepc_hotplug_work(struct work_struct *work) { struct pci_dev *dev; struct pci_bus *bus = pci_find_bus(0, 1); @@ -701,7 +702,7 @@ static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data) if (event != ACPI_NOTIFY_BUS_CHECK) return; - eeepc_rfkill_hotplug(); + schedule_work(&ehotk->hotplug_work); } static void eeepc_hotk_notify(struct acpi_device *device, u32 event) @@ -892,7 +893,7 @@ static int eeepc_hotk_resume(struct acpi_device *device) rfkill_set_sw_state(ehotk->wlan_rfkill, wlan != 1); - eeepc_rfkill_hotplug(); + schedule_work(&ehotk->hotplug_work); } if (ehotk->bluetooth_rfkill) @@ -1093,6 +1094,8 @@ static int eeepc_rfkill_init(struct device *dev) { int result = 0; + INIT_WORK(&ehotk->hotplug_work, eeepc_hotplug_work); + eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P6"); eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7"); diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index ca508564a18..a2ad53e1587 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -520,11 +520,13 @@ static int hp_wmi_resume_handler(struct platform_device *device) * the input layer will only actually pass it on if the state * changed. */ - - input_report_switch(hp_wmi_input_dev, SW_DOCK, hp_wmi_dock_state()); - input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE, - hp_wmi_tablet_state()); - input_sync(hp_wmi_input_dev); + if (hp_wmi_input_dev) { + input_report_switch(hp_wmi_input_dev, SW_DOCK, + hp_wmi_dock_state()); + input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE, + hp_wmi_tablet_state()); + input_sync(hp_wmi_input_dev); + } return 0; } diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index a463fd72c49..e8560085250 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -239,12 +239,6 @@ struct ibm_init_struct { }; static struct { -#ifdef CONFIG_THINKPAD_ACPI_BAY - u32 bay_status:1; - u32 bay_eject:1; - u32 bay_status2:1; - u32 bay_eject2:1; -#endif u32 bluetooth:1; u32 hotkey:1; u32 hotkey_mask:1; @@ -589,18 +583,6 @@ static int acpi_ec_write(int i, u8 v) return 1; } -#if defined(CONFIG_THINKPAD_ACPI_DOCK) || defined(CONFIG_THINKPAD_ACPI_BAY) -static int _sta(acpi_handle handle) -{ - int status; - - if (!handle || !acpi_evalf(handle, &status, "_STA", "d")) - status = 0; - - return status; -} -#endif - static int issue_thinkpad_cmos_command(int cmos_cmd) { if (!cmos_handle) @@ -784,6 +766,8 @@ static int dispatch_procfs_write(struct file *file, if (!ibm || !ibm->write) return -EINVAL; + if (count > PAGE_SIZE - 2) + return -EINVAL; kernbuf = kmalloc(count + 2, GFP_KERNEL); if (!kernbuf) @@ -4442,293 +4426,6 @@ static struct ibm_struct light_driver_data = { }; /************************************************************************* - * Dock subdriver - */ - -#ifdef CONFIG_THINKPAD_ACPI_DOCK - -static void dock_notify(struct ibm_struct *ibm, u32 event); -static int dock_read(char *p); -static int dock_write(char *buf); - -TPACPI_HANDLE(dock, root, "\\_SB.GDCK", /* X30, X31, X40 */ - "\\_SB.PCI0.DOCK", /* 600e/x,770e,770x,A2xm/p,T20-22,X20-21 */ - "\\_SB.PCI0.PCI1.DOCK", /* all others */ - "\\_SB.PCI.ISA.SLCE", /* 570 */ - ); /* A21e,G4x,R30,R31,R32,R40,R40e,R50e */ - -/* don't list other alternatives as we install a notify handler on the 570 */ -TPACPI_HANDLE(pci, root, "\\_SB.PCI"); /* 570 */ - -static const struct acpi_device_id ibm_pci_device_ids[] = { - {PCI_ROOT_HID_STRING, 0}, - {"", 0}, -}; - -static struct tp_acpi_drv_struct ibm_dock_acpidriver[2] = { - { - .notify = dock_notify, - .handle = &dock_handle, - .type = ACPI_SYSTEM_NOTIFY, - }, - { - /* THIS ONE MUST NEVER BE USED FOR DRIVER AUTOLOADING. - * We just use it to get notifications of dock hotplug - * in very old thinkpads */ - .hid = ibm_pci_device_ids, - .notify = dock_notify, - .handle = &pci_handle, - .type = ACPI_SYSTEM_NOTIFY, - }, -}; - -static struct ibm_struct dock_driver_data[2] = { - { - .name = "dock", - .read = dock_read, - .write = dock_write, - .acpi = &ibm_dock_acpidriver[0], - }, - { - .name = "dock", - .acpi = &ibm_dock_acpidriver[1], - }, -}; - -#define dock_docked() (_sta(dock_handle) & 1) - -static int __init dock_init(struct ibm_init_struct *iibm) -{ - vdbg_printk(TPACPI_DBG_INIT, "initializing dock subdriver\n"); - - TPACPI_ACPIHANDLE_INIT(dock); - - vdbg_printk(TPACPI_DBG_INIT, "dock is %s\n", - str_supported(dock_handle != NULL)); - - return (dock_handle)? 0 : 1; -} - -static int __init dock_init2(struct ibm_init_struct *iibm) -{ - int dock2_needed; - - vdbg_printk(TPACPI_DBG_INIT, "initializing dock subdriver part 2\n"); - - if (dock_driver_data[0].flags.acpi_driver_registered && - dock_driver_data[0].flags.acpi_notify_installed) { - TPACPI_ACPIHANDLE_INIT(pci); - dock2_needed = (pci_handle != NULL); - vdbg_printk(TPACPI_DBG_INIT, - "dock PCI handler for the TP 570 is %s\n", - str_supported(dock2_needed)); - } else { - vdbg_printk(TPACPI_DBG_INIT, - "dock subdriver part 2 not required\n"); - dock2_needed = 0; - } - - return (dock2_needed)? 0 : 1; -} - -static void dock_notify(struct ibm_struct *ibm, u32 event) -{ - int docked = dock_docked(); - int pci = ibm->acpi->hid && ibm->acpi->device && - acpi_match_device_ids(ibm->acpi->device, ibm_pci_device_ids); - int data; - - if (event == 1 && !pci) /* 570 */ - data = 1; /* button */ - else if (event == 1 && pci) /* 570 */ - data = 3; /* dock */ - else if (event == 3 && docked) - data = 1; /* button */ - else if (event == 3 && !docked) - data = 2; /* undock */ - else if (event == 0 && docked) - data = 3; /* dock */ - else { - printk(TPACPI_ERR "unknown dock event %d, status %d\n", - event, _sta(dock_handle)); - data = 0; /* unknown */ - } - acpi_bus_generate_proc_event(ibm->acpi->device, event, data); - acpi_bus_generate_netlink_event(ibm->acpi->device->pnp.device_class, - dev_name(&ibm->acpi->device->dev), - event, data); -} - -static int dock_read(char *p) -{ - int len = 0; - int docked = dock_docked(); - - if (!dock_handle) - len += sprintf(p + len, "status:\t\tnot supported\n"); - else if (!docked) - len += sprintf(p + len, "status:\t\tundocked\n"); - else { - len += sprintf(p + len, "status:\t\tdocked\n"); - len += sprintf(p + len, "commands:\tdock, undock\n"); - } - - return len; -} - -static int dock_write(char *buf) -{ - char *cmd; - - if (!dock_docked()) - return -ENODEV; - - while ((cmd = next_cmd(&buf))) { - if (strlencmp(cmd, "undock") == 0) { - if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 0) || - !acpi_evalf(dock_handle, NULL, "_EJ0", "vd", 1)) - return -EIO; - } else if (strlencmp(cmd, "dock") == 0) { - if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 1)) - return -EIO; - } else - return -EINVAL; - } - - return 0; -} - -#endif /* CONFIG_THINKPAD_ACPI_DOCK */ - -/************************************************************************* - * Bay subdriver - */ - -#ifdef CONFIG_THINKPAD_ACPI_BAY - -TPACPI_HANDLE(bay, root, "\\_SB.PCI.IDE.SECN.MAST", /* 570 */ - "\\_SB.PCI0.IDE0.IDES.IDSM", /* 600e/x, 770e, 770x */ - "\\_SB.PCI0.SATA.SCND.MSTR", /* T60, X60, Z60 */ - "\\_SB.PCI0.IDE0.SCND.MSTR", /* all others */ - ); /* A21e, R30, R31 */ -TPACPI_HANDLE(bay_ej, bay, "_EJ3", /* 600e/x, A2xm/p, A3x */ - "_EJ0", /* all others */ - ); /* 570,A21e,G4x,R30,R31,R32,R40e,R50e */ -TPACPI_HANDLE(bay2, root, "\\_SB.PCI0.IDE0.PRIM.SLAV", /* A3x, R32 */ - "\\_SB.PCI0.IDE0.IDEP.IDPS", /* 600e/x, 770e, 770x */ - ); /* all others */ -TPACPI_HANDLE(bay2_ej, bay2, "_EJ3", /* 600e/x, 770e, A3x */ - "_EJ0", /* 770x */ - ); /* all others */ - -static int __init bay_init(struct ibm_init_struct *iibm) -{ - vdbg_printk(TPACPI_DBG_INIT, "initializing bay subdriver\n"); - - TPACPI_ACPIHANDLE_INIT(bay); - if (bay_handle) - TPACPI_ACPIHANDLE_INIT(bay_ej); - TPACPI_ACPIHANDLE_INIT(bay2); - if (bay2_handle) - TPACPI_ACPIHANDLE_INIT(bay2_ej); - - tp_features.bay_status = bay_handle && - acpi_evalf(bay_handle, NULL, "_STA", "qv"); - tp_features.bay_status2 = bay2_handle && - acpi_evalf(bay2_handle, NULL, "_STA", "qv"); - - tp_features.bay_eject = bay_handle && bay_ej_handle && - (strlencmp(bay_ej_path, "_EJ0") == 0 || experimental); - tp_features.bay_eject2 = bay2_handle && bay2_ej_handle && - (strlencmp(bay2_ej_path, "_EJ0") == 0 || experimental); - - vdbg_printk(TPACPI_DBG_INIT, - "bay 1: status %s, eject %s; bay 2: status %s, eject %s\n", - str_supported(tp_features.bay_status), - str_supported(tp_features.bay_eject), - str_supported(tp_features.bay_status2), - str_supported(tp_features.bay_eject2)); - - return (tp_features.bay_status || tp_features.bay_eject || - tp_features.bay_status2 || tp_features.bay_eject2)? 0 : 1; -} - -static void bay_notify(struct ibm_struct *ibm, u32 event) -{ - acpi_bus_generate_proc_event(ibm->acpi->device, event, 0); - acpi_bus_generate_netlink_event(ibm->acpi->device->pnp.device_class, - dev_name(&ibm->acpi->device->dev), - event, 0); -} - -#define bay_occupied(b) (_sta(b##_handle) & 1) - -static int bay_read(char *p) -{ - int len = 0; - int occupied = bay_occupied(bay); - int occupied2 = bay_occupied(bay2); - int eject, eject2; - - len += sprintf(p + len, "status:\t\t%s\n", - tp_features.bay_status ? - (occupied ? "occupied" : "unoccupied") : - "not supported"); - if (tp_features.bay_status2) - len += sprintf(p + len, "status2:\t%s\n", occupied2 ? - "occupied" : "unoccupied"); - - eject = tp_features.bay_eject && occupied; - eject2 = tp_features.bay_eject2 && occupied2; - - if (eject && eject2) - len += sprintf(p + len, "commands:\teject, eject2\n"); - else if (eject) - len += sprintf(p + len, "commands:\teject\n"); - else if (eject2) - len += sprintf(p + len, "commands:\teject2\n"); - - return len; -} - -static int bay_write(char *buf) -{ - char *cmd; - - if (!tp_features.bay_eject && !tp_features.bay_eject2) - return -ENODEV; - - while ((cmd = next_cmd(&buf))) { - if (tp_features.bay_eject && strlencmp(cmd, "eject") == 0) { - if (!acpi_evalf(bay_ej_handle, NULL, NULL, "vd", 1)) - return -EIO; - } else if (tp_features.bay_eject2 && - strlencmp(cmd, "eject2") == 0) { - if (!acpi_evalf(bay2_ej_handle, NULL, NULL, "vd", 1)) - return -EIO; - } else - return -EINVAL; - } - - return 0; -} - -static struct tp_acpi_drv_struct ibm_bay_acpidriver = { - .notify = bay_notify, - .handle = &bay_handle, - .type = ACPI_SYSTEM_NOTIFY, -}; - -static struct ibm_struct bay_driver_data = { - .name = "bay", - .read = bay_read, - .write = bay_write, - .acpi = &ibm_bay_acpidriver, -}; - -#endif /* CONFIG_THINKPAD_ACPI_BAY */ - -/************************************************************************* * CMOS subdriver */ @@ -5945,14 +5642,48 @@ static struct backlight_ops ibm_backlight_data = { /* --------------------------------------------------------------------- */ +/* + * These are only useful for models that have only one possibility + * of GPU. If the BIOS model handles both ATI and Intel, don't use + * these quirks. + */ +#define TPACPI_BRGHT_Q_NOEC 0x0001 /* Must NOT use EC HBRV */ +#define TPACPI_BRGHT_Q_EC 0x0002 /* Should or must use EC HBRV */ +#define TPACPI_BRGHT_Q_ASK 0x8000 /* Ask for user report */ + +static const struct tpacpi_quirk brightness_quirk_table[] __initconst = { + /* Models with ATI GPUs known to require ECNVRAM mode */ + TPACPI_Q_IBM('1', 'Y', TPACPI_BRGHT_Q_EC), /* T43/p ATI */ + + /* Models with ATI GPUs (waiting confirmation) */ + TPACPI_Q_IBM('1', 'R', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC), + TPACPI_Q_IBM('1', 'Q', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC), + TPACPI_Q_IBM('7', '6', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC), + TPACPI_Q_IBM('7', '8', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC), + + /* Models with Intel Extreme Graphics 2 (waiting confirmation) */ + TPACPI_Q_IBM('1', 'V', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_NOEC), + TPACPI_Q_IBM('1', 'W', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_NOEC), + TPACPI_Q_IBM('1', 'U', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_NOEC), + + /* Models with Intel GMA900 */ + TPACPI_Q_IBM('7', '0', TPACPI_BRGHT_Q_NOEC), /* T43, R52 */ + TPACPI_Q_IBM('7', '4', TPACPI_BRGHT_Q_NOEC), /* X41 */ + TPACPI_Q_IBM('7', '5', TPACPI_BRGHT_Q_NOEC), /* X41 Tablet */ +}; + static int __init brightness_init(struct ibm_init_struct *iibm) { int b; + unsigned long quirks; vdbg_printk(TPACPI_DBG_INIT, "initializing brightness subdriver\n"); mutex_init(&brightness_mutex); + quirks = tpacpi_check_quirks(brightness_quirk_table, + ARRAY_SIZE(brightness_quirk_table)); + /* * We always attempt to detect acpi support, so as to switch * Lenovo Vista BIOS to ACPI brightness mode even if we are not @@ -6009,23 +5740,13 @@ static int __init brightness_init(struct ibm_init_struct *iibm) /* TPACPI_BRGHT_MODE_AUTO not implemented yet, just use default */ if (brightness_mode == TPACPI_BRGHT_MODE_AUTO || brightness_mode == TPACPI_BRGHT_MODE_MAX) { - if (thinkpad_id.vendor == PCI_VENDOR_ID_IBM) { - /* - * IBM models that define HBRV probably have - * EC-based backlight level control - */ - if (acpi_evalf(ec_handle, NULL, "HBRV", "qd")) - /* T40-T43, R50-R52, R50e, R51e, X31-X41 */ - brightness_mode = TPACPI_BRGHT_MODE_ECNVRAM; - else - /* all other IBM ThinkPads */ - brightness_mode = TPACPI_BRGHT_MODE_UCMS_STEP; - } else - /* All Lenovo ThinkPads */ + if (quirks & TPACPI_BRGHT_Q_EC) + brightness_mode = TPACPI_BRGHT_MODE_ECNVRAM; + else brightness_mode = TPACPI_BRGHT_MODE_UCMS_STEP; dbg_printk(TPACPI_DBG_BRGHT, - "selected brightness_mode=%d\n", + "driver auto-selected brightness_mode=%d\n", brightness_mode); } @@ -6052,6 +5773,15 @@ static int __init brightness_init(struct ibm_init_struct *iibm) vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_BRGHT, "brightness is supported\n"); + if (quirks & TPACPI_BRGHT_Q_ASK) { + printk(TPACPI_NOTICE + "brightness: will use unverified default: " + "brightness_mode=%d\n", brightness_mode); + printk(TPACPI_NOTICE + "brightness: please report to %s whether it works well " + "or not on your ThinkPad\n", TPACPI_MAIL); + } + ibm_backlight_device->props.max_brightness = (tp_features.bright_16levels)? 15 : 7; ibm_backlight_device->props.brightness = b & TP_EC_BACKLIGHT_LVLMSK; @@ -7854,22 +7584,6 @@ static struct ibm_init_struct ibms_init[] __initdata = { .init = light_init, .data = &light_driver_data, }, -#ifdef CONFIG_THINKPAD_ACPI_DOCK - { - .init = dock_init, - .data = &dock_driver_data[0], - }, - { - .init = dock_init2, - .data = &dock_driver_data[1], - }, -#endif -#ifdef CONFIG_THINKPAD_ACPI_BAY - { - .init = bay_init, - .data = &bay_driver_data, - }, -#endif { .init = cmos_init, .data = &cmos_driver_data, @@ -7968,12 +7682,6 @@ TPACPI_PARAM(hotkey); TPACPI_PARAM(bluetooth); TPACPI_PARAM(video); TPACPI_PARAM(light); -#ifdef CONFIG_THINKPAD_ACPI_DOCK -TPACPI_PARAM(dock); -#endif -#ifdef CONFIG_THINKPAD_ACPI_BAY -TPACPI_PARAM(bay); -#endif /* CONFIG_THINKPAD_ACPI_BAY */ TPACPI_PARAM(cmos); TPACPI_PARAM(led); TPACPI_PARAM(beep); diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 7eda34838bf..bdbc4f73fcd 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -43,6 +43,13 @@ config BATTERY_DS2760 help Say Y here to enable support for batteries with ds2760 chip. +config BATTERY_DS2782 + tristate "DS2782 standalone gas-gauge" + depends on I2C + help + Say Y here to enable support for the DS2782 standalone battery + gas-gauge. + config BATTERY_PMU tristate "Apple PMU battery" depends on PPC32 && ADB_PMU diff --git a/drivers/power/Makefile b/drivers/power/Makefile index daf3179689a..380d17c9ae2 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_APM_POWER) += apm_power.o obj-$(CONFIG_WM8350_POWER) += wm8350_power.o obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o +obj-$(CONFIG_BATTERY_DS2782) += ds2782_battery.o obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o obj-$(CONFIG_BATTERY_TOSA) += tosa_battery.o diff --git a/drivers/power/ds2782_battery.c b/drivers/power/ds2782_battery.c new file mode 100644 index 00000000000..da14f374cb6 --- /dev/null +++ b/drivers/power/ds2782_battery.c @@ -0,0 +1,330 @@ +/* + * I2C client/driver for the Maxim/Dallas DS2782 Stand-Alone Fuel Gauge IC + * + * Copyright (C) 2009 Bluewater Systems Ltd + * + * Author: Ryan Mallon <ryan@bluewatersys.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/swab.h> +#include <linux/i2c.h> +#include <linux/idr.h> +#include <linux/power_supply.h> + +#define DS2782_REG_RARC 0x06 /* Remaining active relative capacity */ + +#define DS2782_REG_VOLT_MSB 0x0c +#define DS2782_REG_TEMP_MSB 0x0a +#define DS2782_REG_CURRENT_MSB 0x0e + +/* EEPROM Block */ +#define DS2782_REG_RSNSP 0x69 /* Sense resistor value */ + +/* Current unit measurement in uA for a 1 milli-ohm sense resistor */ +#define DS2782_CURRENT_UNITS 1563 + +#define to_ds2782_info(x) container_of(x, struct ds2782_info, battery) + +struct ds2782_info { + struct i2c_client *client; + struct power_supply battery; + int id; +}; + +static DEFINE_IDR(battery_id); +static DEFINE_MUTEX(battery_lock); + +static inline int ds2782_read_reg(struct ds2782_info *info, int reg, u8 *val) +{ + int ret; + + ret = i2c_smbus_read_byte_data(info->client, reg); + if (ret < 0) { + dev_err(&info->client->dev, "register read failed\n"); + return ret; + } + + *val = ret; + return 0; +} + +static inline int ds2782_read_reg16(struct ds2782_info *info, int reg_msb, + s16 *val) +{ + int ret; + + ret = swab16(i2c_smbus_read_word_data(info->client, reg_msb)); + if (ret < 0) { + dev_err(&info->client->dev, "register read failed\n"); + return ret; + } + + *val = ret; + return 0; +} + +static int ds2782_get_temp(struct ds2782_info *info, int *temp) +{ + s16 raw; + int err; + + /* + * Temperature is measured in units of 0.125 degrees celcius, the + * power_supply class measures temperature in tenths of degrees + * celsius. The temperature value is stored as a 10 bit number, plus + * sign in the upper bits of a 16 bit register. + */ + err = ds2782_read_reg16(info, DS2782_REG_TEMP_MSB, &raw); + if (err) + return err; + *temp = ((raw / 32) * 125) / 100; + return 0; +} + +static int ds2782_get_current(struct ds2782_info *info, int *current_uA) +{ + int sense_res; + int err; + u8 sense_res_raw; + s16 raw; + + /* + * The units of measurement for current are dependent on the value of + * the sense resistor. + */ + err = ds2782_read_reg(info, DS2782_REG_RSNSP, &sense_res_raw); + if (err) + return err; + if (sense_res_raw == 0) { + dev_err(&info->client->dev, "sense resistor value is 0\n"); + return -ENXIO; + } + sense_res = 1000 / sense_res_raw; + + dev_dbg(&info->client->dev, "sense resistor = %d milli-ohms\n", + sense_res); + err = ds2782_read_reg16(info, DS2782_REG_CURRENT_MSB, &raw); + if (err) + return err; + *current_uA = raw * (DS2782_CURRENT_UNITS / sense_res); + return 0; +} + +static int ds2782_get_voltage(struct ds2782_info *info, int *voltage_uA) +{ + s16 raw; + int err; + + /* + * Voltage is measured in units of 4.88mV. The voltage is stored as + * a 10-bit number plus sign, in the upper bits of a 16-bit register + */ + err = ds2782_read_reg16(info, DS2782_REG_VOLT_MSB, &raw); + if (err) + return err; + *voltage_uA = (raw / 32) * 4800; + return 0; +} + +static int ds2782_get_capacity(struct ds2782_info *info, int *capacity) +{ + int err; + u8 raw; + + err = ds2782_read_reg(info, DS2782_REG_RARC, &raw); + if (err) + return err; + *capacity = raw; + return raw; +} + +static int ds2782_get_status(struct ds2782_info *info, int *status) +{ + int err; + int current_uA; + int capacity; + + err = ds2782_get_current(info, ¤t_uA); + if (err) + return err; + + err = ds2782_get_capacity(info, &capacity); + if (err) + return err; + + if (capacity == 100) + *status = POWER_SUPPLY_STATUS_FULL; + else if (current_uA == 0) + *status = POWER_SUPPLY_STATUS_NOT_CHARGING; + else if (current_uA < 0) + *status = POWER_SUPPLY_STATUS_DISCHARGING; + else + *status = POWER_SUPPLY_STATUS_CHARGING; + + return 0; +} + +static int ds2782_battery_get_property(struct power_supply *psy, + enum power_supply_property prop, + union power_supply_propval *val) +{ + struct ds2782_info *info = to_ds2782_info(psy); + int ret; + + switch (prop) { + case POWER_SUPPLY_PROP_STATUS: + ret = ds2782_get_status(info, &val->intval); + break; + + case POWER_SUPPLY_PROP_CAPACITY: + ret = ds2782_get_capacity(info, &val->intval); + break; + + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + ret = ds2782_get_voltage(info, &val->intval); + break; + + case POWER_SUPPLY_PROP_CURRENT_NOW: + ret = ds2782_get_current(info, &val->intval); + break; + + case POWER_SUPPLY_PROP_TEMP: + ret = ds2782_get_temp(info, &val->intval); + break; + + default: + ret = -EINVAL; + } + + return ret; +} + +static enum power_supply_property ds2782_battery_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_TEMP, +}; + +static void ds2782_power_supply_init(struct power_supply *battery) +{ + battery->type = POWER_SUPPLY_TYPE_BATTERY; + battery->properties = ds2782_battery_props; + battery->num_properties = ARRAY_SIZE(ds2782_battery_props); + battery->get_property = ds2782_battery_get_property; + battery->external_power_changed = NULL; +} + +static int ds2782_battery_remove(struct i2c_client *client) +{ + struct ds2782_info *info = i2c_get_clientdata(client); + + power_supply_unregister(&info->battery); + kfree(info->battery.name); + + mutex_lock(&battery_lock); + idr_remove(&battery_id, info->id); + mutex_unlock(&battery_lock); + + i2c_set_clientdata(client, info); + + kfree(info); + return 0; +} + +static int ds2782_battery_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ds2782_info *info; + int ret; + int num; + + /* Get an ID for this battery */ + ret = idr_pre_get(&battery_id, GFP_KERNEL); + if (ret == 0) { + ret = -ENOMEM; + goto fail_id; + } + + mutex_lock(&battery_lock); + ret = idr_get_new(&battery_id, client, &num); + mutex_unlock(&battery_lock); + if (ret < 0) + goto fail_id; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + ret = -ENOMEM; + goto fail_info; + } + + info->battery.name = kasprintf(GFP_KERNEL, "ds2782-%d", num); + if (!info->battery.name) { + ret = -ENOMEM; + goto fail_name; + } + + i2c_set_clientdata(client, info); + info->client = client; + ds2782_power_supply_init(&info->battery); + + ret = power_supply_register(&client->dev, &info->battery); + if (ret) { + dev_err(&client->dev, "failed to register battery\n"); + goto fail_register; + } + + return 0; + +fail_register: + kfree(info->battery.name); +fail_name: + i2c_set_clientdata(client, info); + kfree(info); +fail_info: + mutex_lock(&battery_lock); + idr_remove(&battery_id, num); + mutex_unlock(&battery_lock); +fail_id: + return ret; +} + +static const struct i2c_device_id ds2782_id[] = { + {"ds2782", 0}, + {}, +}; + +static struct i2c_driver ds2782_battery_driver = { + .driver = { + .name = "ds2782-battery", + }, + .probe = ds2782_battery_probe, + .remove = ds2782_battery_remove, + .id_table = ds2782_id, +}; + +static int __init ds2782_init(void) +{ + return i2c_add_driver(&ds2782_battery_driver); +} +module_init(ds2782_init); + +static void __exit ds2782_exit(void) +{ + i2c_del_driver(&ds2782_battery_driver); +} +module_exit(ds2782_exit); + +MODULE_AUTHOR("Ryan Mallon <ryan@bluewatersys.com>"); +MODULE_DESCRIPTION("Maxim/Dallas DS2782 Stand-Alone Fuel Gauage IC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/olpc_battery.c b/drivers/power/olpc_battery.c index 5fbca2681ba..58e419299cd 100644 --- a/drivers/power/olpc_battery.c +++ b/drivers/power/olpc_battery.c @@ -8,6 +8,7 @@ * published by the Free Software Foundation. */ +#include <linux/kernel.h> #include <linux/module.h> #include <linux/err.h> #include <linux/platform_device.h> @@ -35,6 +36,7 @@ #define BAT_STAT_AC 0x10 #define BAT_STAT_CHARGING 0x20 #define BAT_STAT_DISCHARGING 0x40 +#define BAT_STAT_TRICKLE 0x80 #define BAT_ERR_INFOFAIL 0x02 #define BAT_ERR_OVERVOLTAGE 0x04 @@ -89,7 +91,7 @@ static char bat_serial[17]; /* Ick */ static int olpc_bat_get_status(union power_supply_propval *val, uint8_t ec_byte) { if (olpc_platform_info.ecver > 0x44) { - if (ec_byte & BAT_STAT_CHARGING) + if (ec_byte & (BAT_STAT_CHARGING | BAT_STAT_TRICKLE)) val->intval = POWER_SUPPLY_STATUS_CHARGING; else if (ec_byte & BAT_STAT_DISCHARGING) val->intval = POWER_SUPPLY_STATUS_DISCHARGING; @@ -219,7 +221,8 @@ static int olpc_bat_get_property(struct power_supply *psy, It doesn't matter though -- the EC will return the last-known information, and it's as if we just ran that _little_ bit faster and managed to read it out before the battery went away. */ - if (!(ec_byte & BAT_STAT_PRESENT) && psp != POWER_SUPPLY_PROP_PRESENT) + if (!(ec_byte & (BAT_STAT_PRESENT | BAT_STAT_TRICKLE)) && + psp != POWER_SUPPLY_PROP_PRESENT) return -ENODEV; switch (psp) { @@ -229,7 +232,8 @@ static int olpc_bat_get_property(struct power_supply *psy, return ret; break; case POWER_SUPPLY_PROP_PRESENT: - val->intval = !!(ec_byte & BAT_STAT_PRESENT); + val->intval = !!(ec_byte & (BAT_STAT_PRESENT | + BAT_STAT_TRICKLE)); break; case POWER_SUPPLY_PROP_HEALTH: @@ -334,21 +338,21 @@ static ssize_t olpc_bat_eeprom_read(struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t off, size_t count) { uint8_t ec_byte; - int ret, end; + int ret; + int i; if (off >= EEPROM_SIZE) return 0; if (off + count > EEPROM_SIZE) count = EEPROM_SIZE - off; - end = EEPROM_START + off + count; - for (ec_byte = EEPROM_START + off; ec_byte < end; ec_byte++) { - ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, - &buf[ec_byte - EEPROM_START], 1); + for (i = 0; i < count; i++) { + ec_byte = EEPROM_START + off + i; + ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &buf[i], 1); if (ret) { - printk(KERN_ERR "olpc-battery: EC command " - "EC_BAT_EEPROM @ 0x%x failed -" - " %d!\n", ec_byte, ret); + pr_err("olpc-battery: " + "EC_BAT_EEPROM cmd @ 0x%x failed - %d!\n", + ec_byte, ret); return -EIO; } } diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index 23e10b6263d..f7a4701bf86 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -1174,23 +1174,34 @@ static struct platform_driver cmos_platform_driver = { } }; +#ifdef CONFIG_PNP +static bool pnp_driver_registered; +#endif +static bool platform_driver_registered; + static int __init cmos_init(void) { int retval = 0; #ifdef CONFIG_PNP - pnp_register_driver(&cmos_pnp_driver); + retval = pnp_register_driver(&cmos_pnp_driver); + if (retval == 0) + pnp_driver_registered = true; #endif - if (!cmos_rtc.dev) + if (!cmos_rtc.dev) { retval = platform_driver_probe(&cmos_platform_driver, cmos_platform_probe); + if (retval == 0) + platform_driver_registered = true; + } if (retval == 0) return 0; #ifdef CONFIG_PNP - pnp_unregister_driver(&cmos_pnp_driver); + if (pnp_driver_registered) + pnp_unregister_driver(&cmos_pnp_driver); #endif return retval; } @@ -1199,9 +1210,11 @@ module_init(cmos_init); static void __exit cmos_exit(void) { #ifdef CONFIG_PNP - pnp_unregister_driver(&cmos_pnp_driver); + if (pnp_driver_registered) + pnp_unregister_driver(&cmos_pnp_driver); #endif - platform_driver_unregister(&cmos_platform_driver); + if (platform_driver_registered) + platform_driver_unregister(&cmos_platform_driver); } module_exit(cmos_exit); diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 727a809636d..ed3dcdea7fe 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -1145,12 +1145,17 @@ ap_config_timeout(unsigned long ptr) */ static inline void ap_schedule_poll_timer(void) { + ktime_t hr_time; if (ap_using_interrupts() || ap_suspend_flag) return; if (hrtimer_is_queued(&ap_poll_timer)) return; - hrtimer_start(&ap_poll_timer, ktime_set(0, poll_timeout), - HRTIMER_MODE_ABS); + if (ktime_to_ns(hrtimer_expires_remaining(&ap_poll_timer)) <= 0) { + hr_time = ktime_set(0, poll_timeout); + hrtimer_forward_now(&ap_poll_timer, hr_time); + hrtimer_restart(&ap_poll_timer); + } + return; } /** diff --git a/drivers/serial/atmel_serial.c b/drivers/serial/atmel_serial.c index 338b15c0a54..607d43a3104 100644 --- a/drivers/serial/atmel_serial.c +++ b/drivers/serial/atmel_serial.c @@ -1551,6 +1551,7 @@ static int __devinit atmel_serial_probe(struct platform_device *pdev) if (ret) goto err_add_port; +#ifdef CONFIG_SERIAL_ATMEL_CONSOLE if (atmel_is_console_port(&port->uart) && ATMEL_CONSOLE_DEVICE->flags & CON_ENABLED) { /* @@ -1559,6 +1560,7 @@ static int __devinit atmel_serial_probe(struct platform_device *pdev) */ clk_disable(port->clk); } +#endif device_init_wakeup(&pdev->dev, 1); platform_set_drvdata(pdev, port); diff --git a/drivers/serial/bfin_sport_uart.c b/drivers/serial/bfin_sport_uart.c index 34b4ae0fe76..c108b1a0ce9 100644 --- a/drivers/serial/bfin_sport_uart.c +++ b/drivers/serial/bfin_sport_uart.c @@ -236,7 +236,6 @@ static int sport_startup(struct uart_port *port) int retval; pr_debug("%s enter\n", __func__); - memset(buffer, 20, '\0'); snprintf(buffer, 20, "%s rx", up->name); retval = request_irq(up->rx_irq, sport_uart_rx_irq, IRQF_SAMPLE_RANDOM, buffer, up); if (retval) { diff --git a/drivers/serial/cpm_uart/cpm_uart_cpm2.c b/drivers/serial/cpm_uart/cpm_uart_cpm2.c index 141c0a3333a..a9802e76b5f 100644 --- a/drivers/serial/cpm_uart/cpm_uart_cpm2.c +++ b/drivers/serial/cpm_uart/cpm_uart_cpm2.c @@ -132,7 +132,7 @@ int cpm_uart_allocbuf(struct uart_cpm_port *pinfo, unsigned int is_con) memsz = L1_CACHE_ALIGN(pinfo->rx_nrfifos * pinfo->rx_fifosize) + L1_CACHE_ALIGN(pinfo->tx_nrfifos * pinfo->tx_fifosize); if (is_con) { - mem_addr = alloc_bootmem(memsz); + mem_addr = kzalloc(memsz, GFP_NOWAIT); dma_addr = virt_to_bus(mem_addr); } else diff --git a/drivers/serial/msm_serial.c b/drivers/serial/msm_serial.c index 698048f64f5..f7c24baa141 100644 --- a/drivers/serial/msm_serial.c +++ b/drivers/serial/msm_serial.c @@ -730,7 +730,6 @@ static int __devexit msm_serial_remove(struct platform_device *pdev) } static struct platform_driver msm_platform_driver = { - .probe = msm_serial_probe, .remove = msm_serial_remove, .driver = { .name = "msm_serial", diff --git a/drivers/spi/omap2_mcspi.c b/drivers/spi/omap2_mcspi.c index eee4b6e0af2..9b80ad36dbb 100644 --- a/drivers/spi/omap2_mcspi.c +++ b/drivers/spi/omap2_mcspi.c @@ -59,6 +59,8 @@ /* per-register bitmasks: */ +#define OMAP2_MCSPI_SYSCONFIG_SMARTIDLE (2 << 3) +#define OMAP2_MCSPI_SYSCONFIG_ENAWAKEUP (1 << 2) #define OMAP2_MCSPI_SYSCONFIG_AUTOIDLE (1 << 0) #define OMAP2_MCSPI_SYSCONFIG_SOFTRESET (1 << 1) @@ -90,6 +92,7 @@ #define OMAP2_MCSPI_CHCTRL_EN (1 << 0) +#define OMAP2_MCSPI_WAKEUPENABLE_WKEN (1 << 0) /* We have 2 DMA channels per CS, one for RX and one for TX */ struct omap2_mcspi_dma { @@ -269,7 +272,7 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) if (rx != NULL) { omap_set_dma_transfer_params(mcspi_dma->dma_rx_channel, - data_type, element_count, 1, + data_type, element_count - 1, 1, OMAP_DMA_SYNC_ELEMENT, mcspi_dma->dma_rx_sync_dev, 1); @@ -300,6 +303,25 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) if (rx != NULL) { wait_for_completion(&mcspi_dma->dma_rx_completion); dma_unmap_single(NULL, xfer->rx_dma, count, DMA_FROM_DEVICE); + omap2_mcspi_set_enable(spi, 0); + if (likely(mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHSTAT0) + & OMAP2_MCSPI_CHSTAT_RXS)) { + u32 w; + + w = mcspi_read_cs_reg(spi, OMAP2_MCSPI_RX0); + if (word_len <= 8) + ((u8 *)xfer->rx_buf)[element_count - 1] = w; + else if (word_len <= 16) + ((u16 *)xfer->rx_buf)[element_count - 1] = w; + else /* word_len <= 32 */ + ((u32 *)xfer->rx_buf)[element_count - 1] = w; + } else { + dev_err(&spi->dev, "DMA RX last word empty"); + count -= (word_len <= 8) ? 1 : + (word_len <= 16) ? 2 : + /* word_len <= 32 */ 4; + } + omap2_mcspi_set_enable(spi, 1); } return count; } @@ -873,8 +895,12 @@ static int __init omap2_mcspi_reset(struct omap2_mcspi *mcspi) } while (!(tmp & OMAP2_MCSPI_SYSSTATUS_RESETDONE)); mcspi_write_reg(master, OMAP2_MCSPI_SYSCONFIG, - /* (3 << 8) | (2 << 3) | */ - OMAP2_MCSPI_SYSCONFIG_AUTOIDLE); + OMAP2_MCSPI_SYSCONFIG_AUTOIDLE | + OMAP2_MCSPI_SYSCONFIG_ENAWAKEUP | + OMAP2_MCSPI_SYSCONFIG_SMARTIDLE); + + mcspi_write_reg(master, OMAP2_MCSPI_WAKEUPENABLE, + OMAP2_MCSPI_WAKEUPENABLE_WKEN); omap2_mcspi_set_master_mode(master); diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 348bf61a8fe..975ecddbce3 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -103,8 +103,6 @@ source "drivers/staging/pohmelfs/Kconfig" source "drivers/staging/stlc45xx/Kconfig" -source "drivers/staging/uc2322/Kconfig" - source "drivers/staging/b3dfg/Kconfig" source "drivers/staging/phison/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 8d61d7b4deb..2241ae1b21e 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -34,7 +34,6 @@ obj-$(CONFIG_ANDROID) += android/ obj-$(CONFIG_DST) += dst/ obj-$(CONFIG_POHMELFS) += pohmelfs/ obj-$(CONFIG_STLC45XX) += stlc45xx/ -obj-$(CONFIG_USB_SERIAL_ATEN2011) += uc2322/ obj-$(CONFIG_B3DFG) += b3dfg/ obj-$(CONFIG_IDE_PHISON) += phison/ obj-$(CONFIG_PLAN9AUTH) += p9auth/ diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index fe72240f5a9..f934393f395 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -96,19 +96,21 @@ static int lowmem_shrink(int nr_to_scan, gfp_t gfp_mask) read_lock(&tasklist_lock); for_each_process(p) { + struct mm_struct *mm; int oom_adj; task_lock(p); - if (!p->mm) { + mm = p->mm; + if (!mm) { task_unlock(p); continue; } - oom_adj = p->oomkilladj; + oom_adj = mm->oom_adj; if (oom_adj < min_adj) { task_unlock(p); continue; } - tasksize = get_mm_rss(p->mm); + tasksize = get_mm_rss(mm); task_unlock(p); if (tasksize <= 0) continue; diff --git a/drivers/staging/serqt_usb2/serqt_usb2.c b/drivers/staging/serqt_usb2/serqt_usb2.c index a9bd4106beb..0fdf8c6dc64 100644 --- a/drivers/staging/serqt_usb2/serqt_usb2.c +++ b/drivers/staging/serqt_usb2/serqt_usb2.c @@ -360,18 +360,18 @@ static void qt_read_bulk_callback(struct urb *urb) if (port_paranoia_check(port, __func__) != 0) { dbg("%s - port_paranoia_check, exiting\n", __func__); qt_port->ReadBulkStopped = 1; - return; + goto exit; } if (!serial) { dbg("%s - bad serial pointer, exiting\n", __func__); - return; + goto exit; } if (qt_port->closePending == 1) { /* Were closing , stop reading */ dbg("%s - (qt_port->closepending == 1\n", __func__); qt_port->ReadBulkStopped = 1; - return; + goto exit; } /* @@ -381,7 +381,7 @@ static void qt_read_bulk_callback(struct urb *urb) */ if (qt_port->RxHolding == 1) { qt_port->ReadBulkStopped = 1; - return; + goto exit; } if (urb->status) { @@ -389,7 +389,7 @@ static void qt_read_bulk_callback(struct urb *urb) dbg("%s - nonzero read bulk status received: %d\n", __func__, urb->status); - return; + goto exit; } if (tty && RxCount) { @@ -463,6 +463,8 @@ static void qt_read_bulk_callback(struct urb *urb) } schedule_work(&port->work); +exit: + tty_kref_put(tty); } /* @@ -736,6 +738,11 @@ static int qt_startup(struct usb_serial *serial) if (!qt_port) { dbg("%s: kmalloc for quatech_port (%d) failed!.", __func__, i); + for(--i; i >= 0; i--) { + port = serial->port[i]; + kfree(usb_get_serial_port_data(port)); + usb_set_serial_port_data(port, NULL); + } return -ENOMEM; } spin_lock_init(&qt_port->lock); @@ -1041,7 +1048,7 @@ static void qt_block_until_empty(struct tty_struct *tty, } } -static void qt_close( struct usb_serial_port *port) +static void qt_close(struct usb_serial_port *port) { struct usb_serial *serial = port->serial; struct quatech_port *qt_port; @@ -1068,6 +1075,7 @@ static void qt_close( struct usb_serial_port *port) /* wait up to for transmitter to empty */ if (serial->dev) qt_block_until_empty(tty, qt_port); + tty_kref_put(tty); /* Close uart channel */ status = qt_close_channel(serial, index); diff --git a/drivers/staging/uc2322/Kconfig b/drivers/staging/uc2322/Kconfig deleted file mode 100644 index 2e0c6e79df2..00000000000 --- a/drivers/staging/uc2322/Kconfig +++ /dev/null @@ -1,10 +0,0 @@ -config USB_SERIAL_ATEN2011 - tristate "ATEN 2011 USB to serial device support" - depends on USB_SERIAL - default N - ---help--- - Say Y here if you want to use a ATEN 2011 dual port USB to serial - adapter. - - To compile this driver as a module, choose M here: the module will be - called aten2011. diff --git a/drivers/staging/uc2322/Makefile b/drivers/staging/uc2322/Makefile deleted file mode 100644 index 49c18d6e579..00000000000 --- a/drivers/staging/uc2322/Makefile +++ /dev/null @@ -1 +0,0 @@ -obj-$(CONFIG_USB_SERIAL_ATEN2011) += aten2011.o diff --git a/drivers/staging/uc2322/TODO b/drivers/staging/uc2322/TODO deleted file mode 100644 index c189a64c418..00000000000 --- a/drivers/staging/uc2322/TODO +++ /dev/null @@ -1,7 +0,0 @@ -TODO: - - checkpatch.pl cleanups - - remove dead and useless code (auditing the tty ioctls to - verify that they really are correct and needed.) - -Please send any patches to Greg Kroah-Hartman <greg@kroah.com> and -Russell Lang <gsview@ghostgum.com.au>. diff --git a/drivers/staging/uc2322/aten2011.c b/drivers/staging/uc2322/aten2011.c deleted file mode 100644 index 39d0926d1a9..00000000000 --- a/drivers/staging/uc2322/aten2011.c +++ /dev/null @@ -1,2430 +0,0 @@ -/* - * Aten 2011 USB serial driver for 4 port devices - * - * Copyright (C) 2000 Inside Out Networks - * Copyright (C) 2001-2002, 2009 Greg Kroah-Hartman <greg@kroah.com> - * Copyright (C) 2009 Novell Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - */ - -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/tty.h> -#include <linux/tty_driver.h> -#include <linux/tty_flip.h> -#include <linux/module.h> -#include <linux/serial.h> -#include <linux/uaccess.h> -#include <linux/usb.h> -#include <linux/usb/serial.h> - - -#define ZLP_REG1 0x3A /* Zero_Flag_Reg1 58 */ -#define ZLP_REG2 0x3B /* Zero_Flag_Reg2 59 */ -#define ZLP_REG3 0x3C /* Zero_Flag_Reg3 60 */ -#define ZLP_REG4 0x3D /* Zero_Flag_Reg4 61 */ -#define ZLP_REG5 0x3E /* Zero_Flag_Reg5 62 */ - -/* Interrupt Rotinue Defines */ -#define SERIAL_IIR_RLS 0x06 -#define SERIAL_IIR_RDA 0x04 -#define SERIAL_IIR_CTI 0x0c -#define SERIAL_IIR_THR 0x02 -#define SERIAL_IIR_MS 0x00 - -/* Emulation of the bit mask on the LINE STATUS REGISTER. */ -#define SERIAL_LSR_DR 0x0001 -#define SERIAL_LSR_OE 0x0002 -#define SERIAL_LSR_PE 0x0004 -#define SERIAL_LSR_FE 0x0008 -#define SERIAL_LSR_BI 0x0010 -#define SERIAL_LSR_THRE 0x0020 -#define SERIAL_LSR_TEMT 0x0040 -#define SERIAL_LSR_FIFOERR 0x0080 - -/* MSR bit defines(place holders) */ -#define ATEN_MSR_DELTA_CTS 0x10 -#define ATEN_MSR_DELTA_DSR 0x20 -#define ATEN_MSR_DELTA_RI 0x40 -#define ATEN_MSR_DELTA_CD 0x80 - -/* Serial Port register Address */ -#define RECEIVE_BUFFER_REGISTER ((__u16)(0x00)) -#define TRANSMIT_HOLDING_REGISTER ((__u16)(0x00)) -#define INTERRUPT_ENABLE_REGISTER ((__u16)(0x01)) -#define INTERRUPT_IDENT_REGISTER ((__u16)(0x02)) -#define FIFO_CONTROL_REGISTER ((__u16)(0x02)) -#define LINE_CONTROL_REGISTER ((__u16)(0x03)) -#define MODEM_CONTROL_REGISTER ((__u16)(0x04)) -#define LINE_STATUS_REGISTER ((__u16)(0x05)) -#define MODEM_STATUS_REGISTER ((__u16)(0x06)) -#define SCRATCH_PAD_REGISTER ((__u16)(0x07)) -#define DIVISOR_LATCH_LSB ((__u16)(0x00)) -#define DIVISOR_LATCH_MSB ((__u16)(0x01)) - -#define SP1_REGISTER ((__u16)(0x00)) -#define CONTROL1_REGISTER ((__u16)(0x01)) -#define CLK_MULTI_REGISTER ((__u16)(0x02)) -#define CLK_START_VALUE_REGISTER ((__u16)(0x03)) -#define DCR1_REGISTER ((__u16)(0x04)) -#define GPIO_REGISTER ((__u16)(0x07)) - -#define SERIAL_LCR_DLAB ((__u16)(0x0080)) - -/* - * URB POOL related defines - */ -#define NUM_URBS 16 /* URB Count */ -#define URB_TRANSFER_BUFFER_SIZE 32 /* URB Size */ - -#define USB_VENDOR_ID_ATENINTL 0x0557 -#define ATENINTL_DEVICE_ID_2011 0x2011 -#define ATENINTL_DEVICE_ID_7820 0x7820 - -static struct usb_device_id id_table[] = { - { USB_DEVICE(USB_VENDOR_ID_ATENINTL, ATENINTL_DEVICE_ID_2011) }, - { USB_DEVICE(USB_VENDOR_ID_ATENINTL, ATENINTL_DEVICE_ID_7820) }, - { } /* terminating entry */ -}; -MODULE_DEVICE_TABLE(usb, id_table); - -/* This structure holds all of the local port information */ -struct ATENINTL_port { - int port_num; /*Actual port number in the device(1,2,etc)*/ - __u8 bulk_out_endpoint; /* the bulk out endpoint handle */ - unsigned char *bulk_out_buffer; /* buffer used for the bulk out endpoint */ - struct urb *write_urb; /* write URB for this port */ - __u8 bulk_in_endpoint; /* the bulk in endpoint handle */ - unsigned char *bulk_in_buffer; /* the buffer we use for the bulk in endpoint */ - struct urb *read_urb; /* read URB for this port */ - __u8 shadowLCR; /* last LCR value received */ - __u8 shadowMCR; /* last MCR value received */ - char open; - char chaseResponsePending; - wait_queue_head_t wait_chase; /* for handling sleeping while waiting for chase to finish */ - wait_queue_head_t wait_command; /* for handling sleeping while waiting for command to finish */ - struct async_icount icount; - struct usb_serial_port *port; /* loop back to the owner of this object */ - /*Offsets*/ - __u8 SpRegOffset; - __u8 ControlRegOffset; - __u8 DcrRegOffset; - /* for processing control URBS in interrupt context */ - struct urb *control_urb; - char *ctrl_buf; - int MsrLsr; - - struct urb *write_urb_pool[NUM_URBS]; - /* we pass a pointer to this as the arguement sent to cypress_set_termios old_termios */ - struct ktermios tmp_termios; /* stores the old termios settings */ - spinlock_t lock; /* private lock */ -}; - -/* This structure holds all of the individual serial device information */ -struct ATENINTL_serial { - __u8 interrupt_in_endpoint; /* the interrupt endpoint handle */ - unsigned char *interrupt_in_buffer; /* the buffer we use for the interrupt endpoint */ - struct urb *interrupt_read_urb; /* our interrupt urb */ - __u8 bulk_in_endpoint; /* the bulk in endpoint handle */ - unsigned char *bulk_in_buffer; /* the buffer we use for the bulk in endpoint */ - struct urb *read_urb; /* our bulk read urb */ - __u8 bulk_out_endpoint; /* the bulk out endpoint handle */ - struct usb_serial *serial; /* loop back to the owner of this object */ - int ATEN2011_spectrum_2or4ports; /* this says the number of ports in the device */ - /* Indicates about the no.of opened ports of an individual USB-serial adapater. */ - unsigned int NoOfOpenPorts; - /* a flag for Status endpoint polling */ - unsigned char status_polling_started; -}; - -static void ATEN2011_set_termios(struct tty_struct *tty, - struct usb_serial_port *port, - struct ktermios *old_termios); -static void ATEN2011_change_port_settings(struct tty_struct *tty, - struct ATENINTL_port *ATEN2011_port, - struct ktermios *old_termios); - -/************************************* - * Bit definitions for each register * - *************************************/ -#define LCR_BITS_5 0x00 /* 5 bits/char */ -#define LCR_BITS_6 0x01 /* 6 bits/char */ -#define LCR_BITS_7 0x02 /* 7 bits/char */ -#define LCR_BITS_8 0x03 /* 8 bits/char */ -#define LCR_BITS_MASK 0x03 /* Mask for bits/char field */ - -#define LCR_STOP_1 0x00 /* 1 stop bit */ -#define LCR_STOP_1_5 0x04 /* 1.5 stop bits (if 5 bits/char) */ -#define LCR_STOP_2 0x04 /* 2 stop bits (if 6-8 bits/char) */ -#define LCR_STOP_MASK 0x04 /* Mask for stop bits field */ - -#define LCR_PAR_NONE 0x00 /* No parity */ -#define LCR_PAR_ODD 0x08 /* Odd parity */ -#define LCR_PAR_EVEN 0x18 /* Even parity */ -#define LCR_PAR_MARK 0x28 /* Force parity bit to 1 */ -#define LCR_PAR_SPACE 0x38 /* Force parity bit to 0 */ -#define LCR_PAR_MASK 0x38 /* Mask for parity field */ - -#define LCR_SET_BREAK 0x40 /* Set Break condition */ -#define LCR_DL_ENABLE 0x80 /* Enable access to divisor latch */ - -#define MCR_DTR 0x01 /* Assert DTR */ -#define MCR_RTS 0x02 /* Assert RTS */ -#define MCR_OUT1 0x04 /* Loopback only: Sets state of RI */ -#define MCR_MASTER_IE 0x08 /* Enable interrupt outputs */ -#define MCR_LOOPBACK 0x10 /* Set internal (digital) loopback mode */ -#define MCR_XON_ANY 0x20 /* Enable any char to exit XOFF mode */ - -#define ATEN2011_MSR_CTS 0x10 /* Current state of CTS */ -#define ATEN2011_MSR_DSR 0x20 /* Current state of DSR */ -#define ATEN2011_MSR_RI 0x40 /* Current state of RI */ -#define ATEN2011_MSR_CD 0x80 /* Current state of CD */ - - -static int debug; - -/* - * Version Information - */ -#define DRIVER_VERSION "2.0" -#define DRIVER_DESC "ATENINTL 2011 USB Serial Adapter" - -/* - * Defines used for sending commands to port - */ - -#define ATEN_WDR_TIMEOUT (50) /* default urb timeout */ - -/* Requests */ -#define ATEN_RD_RTYPE 0xC0 -#define ATEN_WR_RTYPE 0x40 -#define ATEN_RDREQ 0x0D -#define ATEN_WRREQ 0x0E -#define ATEN_CTRL_TIMEOUT 500 -#define VENDOR_READ_LENGTH (0x01) - -/* set to 1 for RS485 mode and 0 for RS232 mode */ -/* FIXME make this somehow dynamic and not build time specific */ -static int RS485mode; - -static int set_reg_sync(struct usb_serial_port *port, __u16 reg, __u16 val) -{ - struct usb_device *dev = port->serial->dev; - val = val & 0x00ff; - - dbg("%s: is %x, value %x", __func__, reg, val); - - return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), ATEN_WRREQ, - ATEN_WR_RTYPE, val, reg, NULL, 0, - ATEN_WDR_TIMEOUT); -} - -static int get_reg_sync(struct usb_serial_port *port, __u16 reg, __u16 *val) -{ - struct usb_device *dev = port->serial->dev; - int ret; - - ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), ATEN_RDREQ, - ATEN_RD_RTYPE, 0, reg, val, VENDOR_READ_LENGTH, - ATEN_WDR_TIMEOUT); - dbg("%s: offset is %x, return val %x", __func__, reg, *val); - *val = (*val) & 0x00ff; - return ret; -} - -static int set_uart_reg(struct usb_serial_port *port, __u16 reg, __u16 val) -{ - struct usb_device *dev = port->serial->dev; - struct ATENINTL_serial *a_serial; - __u16 minor; - - a_serial = usb_get_serial_data(port->serial); - minor = port->serial->minor; - if (minor == SERIAL_TTY_NO_MINOR) - minor = 0; - val = val & 0x00ff; - - /* - * For the UART control registers, - * the application number need to be Or'ed - */ - if (a_serial->ATEN2011_spectrum_2or4ports == 4) - val |= (((__u16)port->number - minor) + 1) << 8; - else { - if (((__u16) port->number - minor) == 0) - val |= (((__u16)port->number - minor) + 1) << 8; - else - val |= (((__u16)port->number - minor) + 2) << 8; - } - dbg("%s: application number is %x", __func__, val); - - return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), ATEN_WRREQ, - ATEN_WR_RTYPE, val, reg, NULL, 0, - ATEN_WDR_TIMEOUT); -} - -static int get_uart_reg(struct usb_serial_port *port, __u16 reg, __u16 *val) -{ - struct usb_device *dev = port->serial->dev; - int ret = 0; - __u16 wval; - struct ATENINTL_serial *a_serial; - __u16 minor = port->serial->minor; - - a_serial = usb_get_serial_data(port->serial); - if (minor == SERIAL_TTY_NO_MINOR) - minor = 0; - - /* wval is same as application number */ - if (a_serial->ATEN2011_spectrum_2or4ports == 4) - wval = (((__u16)port->number - minor) + 1) << 8; - else { - if (((__u16) port->number - minor) == 0) - wval = (((__u16) port->number - minor) + 1) << 8; - else - wval = (((__u16) port->number - minor) + 2) << 8; - } - dbg("%s: application number is %x", __func__, wval); - ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), ATEN_RDREQ, - ATEN_RD_RTYPE, wval, reg, val, VENDOR_READ_LENGTH, - ATEN_WDR_TIMEOUT); - *val = (*val) & 0x00ff; - return ret; -} - -static int handle_newMsr(struct ATENINTL_port *port, __u8 newMsr) -{ - struct ATENINTL_port *ATEN2011_port; - struct async_icount *icount; - ATEN2011_port = port; - icount = &ATEN2011_port->icount; - if (newMsr & - (ATEN_MSR_DELTA_CTS | ATEN_MSR_DELTA_DSR | ATEN_MSR_DELTA_RI | - ATEN_MSR_DELTA_CD)) { - icount = &ATEN2011_port->icount; - - /* update input line counters */ - if (newMsr & ATEN_MSR_DELTA_CTS) - icount->cts++; - if (newMsr & ATEN_MSR_DELTA_DSR) - icount->dsr++; - if (newMsr & ATEN_MSR_DELTA_CD) - icount->dcd++; - if (newMsr & ATEN_MSR_DELTA_RI) - icount->rng++; - } - - return 0; -} - -static int handle_newLsr(struct ATENINTL_port *port, __u8 newLsr) -{ - struct async_icount *icount; - - dbg("%s - %02x", __func__, newLsr); - - if (newLsr & SERIAL_LSR_BI) { - /* - * Parity and Framing errors only count if they occur exclusive - * of a break being received. - */ - newLsr &= (__u8) (SERIAL_LSR_OE | SERIAL_LSR_BI); - } - - /* update input line counters */ - icount = &port->icount; - if (newLsr & SERIAL_LSR_BI) - icount->brk++; - if (newLsr & SERIAL_LSR_OE) - icount->overrun++; - if (newLsr & SERIAL_LSR_PE) - icount->parity++; - if (newLsr & SERIAL_LSR_FE) - icount->frame++; - - return 0; -} - -static void ATEN2011_control_callback(struct urb *urb) -{ - unsigned char *data; - struct ATENINTL_port *ATEN2011_port; - __u8 regval = 0x0; - - switch (urb->status) { - case 0: - /* success */ - break; - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - /* this urb is terminated, clean up */ - dbg("%s - urb shutting down with status: %d", __func__, - urb->status); - return; - default: - dbg("%s - nonzero urb status received: %d", __func__, - urb->status); - goto exit; - } - - ATEN2011_port = (struct ATENINTL_port *)urb->context; - - dbg("%s urb buffer size is %d", __func__, urb->actual_length); - dbg("%s ATEN2011_port->MsrLsr is %d port %d", __func__, - ATEN2011_port->MsrLsr, ATEN2011_port->port_num); - data = urb->transfer_buffer; - regval = (__u8) data[0]; - dbg("%s data is %x", __func__, regval); - if (ATEN2011_port->MsrLsr == 0) - handle_newMsr(ATEN2011_port, regval); - else if (ATEN2011_port->MsrLsr == 1) - handle_newLsr(ATEN2011_port, regval); - -exit: - return; -} - -static int ATEN2011_get_reg(struct ATENINTL_port *ATEN, __u16 Wval, __u16 reg, - __u16 *val) -{ - struct usb_device *dev = ATEN->port->serial->dev; - struct usb_ctrlrequest *dr = NULL; - unsigned char *buffer = NULL; - int ret = 0; - buffer = (__u8 *) ATEN->ctrl_buf; - - dr = (void *)(buffer + 2); - dr->bRequestType = ATEN_RD_RTYPE; - dr->bRequest = ATEN_RDREQ; - dr->wValue = cpu_to_le16(Wval); - dr->wIndex = cpu_to_le16(reg); - dr->wLength = cpu_to_le16(2); - - usb_fill_control_urb(ATEN->control_urb, dev, usb_rcvctrlpipe(dev, 0), - (unsigned char *)dr, buffer, 2, - ATEN2011_control_callback, ATEN); - ATEN->control_urb->transfer_buffer_length = 2; - ret = usb_submit_urb(ATEN->control_urb, GFP_ATOMIC); - return ret; -} - -static void ATEN2011_interrupt_callback(struct urb *urb) -{ - int result; - int length; - struct ATENINTL_port *ATEN2011_port; - struct ATENINTL_serial *ATEN2011_serial; - struct usb_serial *serial; - __u16 Data; - unsigned char *data; - __u8 sp[5], st; - int i; - __u16 wval; - int minor; - - dbg("%s", " : Entering"); - - ATEN2011_serial = (struct ATENINTL_serial *)urb->context; - - switch (urb->status) { - case 0: - /* success */ - break; - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - /* this urb is terminated, clean up */ - dbg("%s - urb shutting down with status: %d", __func__, - urb->status); - return; - default: - dbg("%s - nonzero urb status received: %d", __func__, - urb->status); - goto exit; - } - length = urb->actual_length; - data = urb->transfer_buffer; - - serial = ATEN2011_serial->serial; - - /* ATENINTL get 5 bytes - * Byte 1 IIR Port 1 (port.number is 0) - * Byte 2 IIR Port 2 (port.number is 1) - * Byte 3 IIR Port 3 (port.number is 2) - * Byte 4 IIR Port 4 (port.number is 3) - * Byte 5 FIFO status for both */ - - if (length && length > 5) { - dbg("%s", "Wrong data !!!"); - return; - } - - /* MATRIX */ - if (ATEN2011_serial->ATEN2011_spectrum_2or4ports == 4) { - sp[0] = (__u8) data[0]; - sp[1] = (__u8) data[1]; - sp[2] = (__u8) data[2]; - sp[3] = (__u8) data[3]; - st = (__u8) data[4]; - } else { - sp[0] = (__u8) data[0]; - sp[1] = (__u8) data[2]; - /* sp[2]=(__u8)data[2]; */ - /* sp[3]=(__u8)data[3]; */ - st = (__u8) data[4]; - - } - for (i = 0; i < serial->num_ports; i++) { - ATEN2011_port = usb_get_serial_port_data(serial->port[i]); - minor = serial->minor; - if (minor == SERIAL_TTY_NO_MINOR) - minor = 0; - if ((ATEN2011_serial->ATEN2011_spectrum_2or4ports == 2) - && (i != 0)) - wval = - (((__u16) serial->port[i]->number - - (__u16) (minor)) + 2) << 8; - else - wval = - (((__u16) serial->port[i]->number - - (__u16) (minor)) + 1) << 8; - if (ATEN2011_port->open != 0) { - if (sp[i] & 0x01) { - dbg("SP%d No Interrupt !!!", i); - } else { - switch (sp[i] & 0x0f) { - case SERIAL_IIR_RLS: - dbg("Serial Port %d: Receiver status error or address bit detected in 9-bit mode", i); - ATEN2011_port->MsrLsr = 1; - ATEN2011_get_reg(ATEN2011_port, wval, - LINE_STATUS_REGISTER, - &Data); - break; - case SERIAL_IIR_MS: - dbg("Serial Port %d: Modem status change", i); - ATEN2011_port->MsrLsr = 0; - ATEN2011_get_reg(ATEN2011_port, wval, - MODEM_STATUS_REGISTER, - &Data); - break; - } - } - } - - } -exit: - if (ATEN2011_serial->status_polling_started == 0) - return; - - result = usb_submit_urb(urb, GFP_ATOMIC); - if (result) { - dev_err(&urb->dev->dev, - "%s - Error %d submitting interrupt urb\n", - __func__, result); - } - - return; -} - -static void ATEN2011_bulk_in_callback(struct urb *urb) -{ - int status; - unsigned char *data; - struct usb_serial *serial; - struct usb_serial_port *port; - struct ATENINTL_serial *ATEN2011_serial; - struct ATENINTL_port *ATEN2011_port; - struct tty_struct *tty; - - if (urb->status) { - dbg("nonzero read bulk status received: %d", urb->status); - return; - } - - ATEN2011_port = (struct ATENINTL_port *)urb->context; - - port = (struct usb_serial_port *)ATEN2011_port->port; - serial = port->serial; - - dbg("%s", "Entering..."); - - data = urb->transfer_buffer; - ATEN2011_serial = usb_get_serial_data(serial); - - if (urb->actual_length) { - tty = tty_port_tty_get(&ATEN2011_port->port->port); - if (tty) { - tty_buffer_request_room(tty, urb->actual_length); - tty_insert_flip_string(tty, data, urb->actual_length); - tty_flip_buffer_push(tty); - tty_kref_put(tty); - } - - ATEN2011_port->icount.rx += urb->actual_length; - dbg("ATEN2011_port->icount.rx is %d:", - ATEN2011_port->icount.rx); - } - - if (!ATEN2011_port->read_urb) { - dbg("%s", "URB KILLED !!!"); - return; - } - - if (ATEN2011_port->read_urb->status != -EINPROGRESS) { - ATEN2011_port->read_urb->dev = serial->dev; - - status = usb_submit_urb(ATEN2011_port->read_urb, GFP_ATOMIC); - if (status) - dbg("usb_submit_urb(read bulk) failed, status = %d", status); - } -} - -static void ATEN2011_bulk_out_data_callback(struct urb *urb) -{ - struct ATENINTL_port *ATEN2011_port; - struct tty_struct *tty; - - if (urb->status) { - dbg("nonzero write bulk status received:%d", urb->status); - return; - } - - ATEN2011_port = (struct ATENINTL_port *)urb->context; - - dbg("%s", "Entering ........."); - - tty = tty_port_tty_get(&ATEN2011_port->port->port); - - if (tty && ATEN2011_port->open) - /* tell the tty driver that something has changed */ - tty_wakeup(tty); - - /* schedule_work(&ATEN2011_port->port->work); */ - tty_kref_put(tty); - -} - -#ifdef ATENSerialProbe -static int ATEN2011_serial_probe(struct usb_serial *serial, - const struct usb_device_id *id) -{ - - /*need to implement the mode_reg reading and updating\ - structures usb_serial_ device_type\ - (i.e num_ports, num_bulkin,bulkout etc) */ - /* Also we can update the changes attach */ - return 1; -} -#endif - -static int ATEN2011_open(struct tty_struct *tty, struct usb_serial_port *port, - struct file *filp) -{ - int response; - int j; - struct usb_serial *serial; - struct urb *urb; - __u16 Data; - int status; - struct ATENINTL_serial *ATEN2011_serial; - struct ATENINTL_port *ATEN2011_port; - struct ktermios tmp_termios; - int minor; - - serial = port->serial; - - ATEN2011_port = usb_get_serial_port_data(port); - - if (ATEN2011_port == NULL) - return -ENODEV; - - ATEN2011_serial = usb_get_serial_data(serial); - if (ATEN2011_serial == NULL) - return -ENODEV; - - /* increment the number of opened ports counter here */ - ATEN2011_serial->NoOfOpenPorts++; - - usb_clear_halt(serial->dev, port->write_urb->pipe); - usb_clear_halt(serial->dev, port->read_urb->pipe); - - /* Initialising the write urb pool */ - for (j = 0; j < NUM_URBS; ++j) { - urb = usb_alloc_urb(0, GFP_ATOMIC); - ATEN2011_port->write_urb_pool[j] = urb; - - if (urb == NULL) { - err("No more urbs???"); - continue; - } - - urb->transfer_buffer = NULL; - urb->transfer_buffer = - kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL); - if (!urb->transfer_buffer) { - err("%s-out of memory for urb buffers.", __func__); - continue; - } - } - -/***************************************************************************** - * Initialize ATEN2011 -- Write Init values to corresponding Registers - * - * Register Index - * 1 : IER - * 2 : FCR - * 3 : LCR - * 4 : MCR - * - * 0x08 : SP1/2 Control Reg - *****************************************************************************/ - -/* NEED to check the fallowing Block */ - - Data = 0x0; - status = get_reg_sync(port, ATEN2011_port->SpRegOffset, &Data); - if (status < 0) { - dbg("Reading Spreg failed"); - return -1; - } - Data |= 0x80; - status = set_reg_sync(port, ATEN2011_port->SpRegOffset, Data); - if (status < 0) { - dbg("writing Spreg failed"); - return -1; - } - - Data &= ~0x80; - status = set_reg_sync(port, ATEN2011_port->SpRegOffset, Data); - if (status < 0) { - dbg("writing Spreg failed"); - return -1; - } - -/* End of block to be checked */ -/**************************CHECK***************************/ - - if (RS485mode == 0) - Data = 0xC0; - else - Data = 0x00; - status = set_uart_reg(port, SCRATCH_PAD_REGISTER, Data); - if (status < 0) { - dbg("Writing SCRATCH_PAD_REGISTER failed status-0x%x", status); - return -1; - } else - dbg("SCRATCH_PAD_REGISTER Writing success status%d", status); - -/**************************CHECK***************************/ - - Data = 0x0; - status = get_reg_sync(port, ATEN2011_port->ControlRegOffset, &Data); - if (status < 0) { - dbg("Reading Controlreg failed"); - return -1; - } - Data |= 0x08; /* Driver done bit */ - Data |= 0x20; /* rx_disable */ - status = 0; - status = - set_reg_sync(port, ATEN2011_port->ControlRegOffset, Data); - if (status < 0) { - dbg("writing Controlreg failed"); - return -1; - } - /* - * do register settings here - * Set all regs to the device default values. - * First Disable all interrupts. - */ - - Data = 0x00; - status = set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data); - if (status < 0) { - dbg("disableing interrupts failed"); - return -1; - } - /* Set FIFO_CONTROL_REGISTER to the default value */ - Data = 0x00; - status = set_uart_reg(port, FIFO_CONTROL_REGISTER, Data); - if (status < 0) { - dbg("Writing FIFO_CONTROL_REGISTER failed"); - return -1; - } - - Data = 0xcf; /* chk */ - status = set_uart_reg(port, FIFO_CONTROL_REGISTER, Data); - if (status < 0) { - dbg("Writing FIFO_CONTROL_REGISTER failed"); - return -1; - } - - Data = 0x03; /* LCR_BITS_8 */ - status = set_uart_reg(port, LINE_CONTROL_REGISTER, Data); - ATEN2011_port->shadowLCR = Data; - - Data = 0x0b; /* MCR_DTR|MCR_RTS|MCR_MASTER_IE */ - status = set_uart_reg(port, MODEM_CONTROL_REGISTER, Data); - ATEN2011_port->shadowMCR = Data; - -#ifdef Check - Data = 0x00; - status = get_uart_reg(port, LINE_CONTROL_REGISTER, &Data); - ATEN2011_port->shadowLCR = Data; - - Data |= SERIAL_LCR_DLAB; /* data latch enable in LCR 0x80 */ - status = set_uart_reg(port, LINE_CONTROL_REGISTER, Data); - - Data = 0x0c; - status = set_uart_reg(port, DIVISOR_LATCH_LSB, Data); - - Data = 0x0; - status = set_uart_reg(port, DIVISOR_LATCH_MSB, Data); - - Data = 0x00; - status = get_uart_reg(port, LINE_CONTROL_REGISTER, &Data); - -/* Data = ATEN2011_port->shadowLCR; */ /* data latch disable */ - Data = Data & ~SERIAL_LCR_DLAB; - status = set_uart_reg(port, LINE_CONTROL_REGISTER, Data); - ATEN2011_port->shadowLCR = Data; -#endif - /* clearing Bulkin and Bulkout Fifo */ - Data = 0x0; - status = get_reg_sync(port, ATEN2011_port->SpRegOffset, &Data); - - Data = Data | 0x0c; - status = set_reg_sync(port, ATEN2011_port->SpRegOffset, Data); - - Data = Data & ~0x0c; - status = set_reg_sync(port, ATEN2011_port->SpRegOffset, Data); - /* Finally enable all interrupts */ - Data = 0x0; - Data = 0x0c; - status = set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data); - - /* clearing rx_disable */ - Data = 0x0; - status = get_reg_sync(port, ATEN2011_port->ControlRegOffset, &Data); - Data = Data & ~0x20; - status = set_reg_sync(port, ATEN2011_port->ControlRegOffset, Data); - - /* rx_negate */ - Data = 0x0; - status = get_reg_sync(port, ATEN2011_port->ControlRegOffset, &Data); - Data = Data | 0x10; - status = 0; - status = set_reg_sync(port, ATEN2011_port->ControlRegOffset, Data); - - /* - * Check to see if we've set up our endpoint info yet - * (can't set it up in ATEN2011_startup as the structures - * were not set up at that time.) - */ - if (ATEN2011_serial->NoOfOpenPorts == 1) { - /* start the status polling here */ - ATEN2011_serial->status_polling_started = 1; - /* If not yet set, Set here */ - ATEN2011_serial->interrupt_in_buffer = - serial->port[0]->interrupt_in_buffer; - ATEN2011_serial->interrupt_in_endpoint = - serial->port[0]->interrupt_in_endpointAddress; - ATEN2011_serial->interrupt_read_urb = - serial->port[0]->interrupt_in_urb; - - /* set up interrupt urb */ - usb_fill_int_urb(ATEN2011_serial->interrupt_read_urb, - serial->dev, - usb_rcvintpipe(serial->dev, - ATEN2011_serial-> - interrupt_in_endpoint), - ATEN2011_serial->interrupt_in_buffer, - ATEN2011_serial->interrupt_read_urb-> - transfer_buffer_length, - ATEN2011_interrupt_callback, ATEN2011_serial, - ATEN2011_serial->interrupt_read_urb->interval); - - /* start interrupt read for ATEN2011 * - * will continue as long as ATEN2011 is connected */ - - response = - usb_submit_urb(ATEN2011_serial->interrupt_read_urb, - GFP_KERNEL); - if (response) { - dbg("%s - Error %d submitting interrupt urb", - __func__, response); - } - - } - - /* - * See if we've set up our endpoint info yet - * (can't set it up in ATEN2011_startup as the - * structures were not set up at that time.) - */ - - dbg("port number is %d", port->number); - dbg("serial number is %d", port->serial->minor); - dbg("Bulkin endpoint is %d", port->bulk_in_endpointAddress); - dbg("BulkOut endpoint is %d", port->bulk_out_endpointAddress); - dbg("Interrupt endpoint is %d", - port->interrupt_in_endpointAddress); - dbg("port's number in the device is %d", ATEN2011_port->port_num); - ATEN2011_port->bulk_in_buffer = port->bulk_in_buffer; - ATEN2011_port->bulk_in_endpoint = port->bulk_in_endpointAddress; - ATEN2011_port->read_urb = port->read_urb; - ATEN2011_port->bulk_out_endpoint = port->bulk_out_endpointAddress; - - minor = port->serial->minor; - if (minor == SERIAL_TTY_NO_MINOR) - minor = 0; - - /* set up our bulk in urb */ - if ((ATEN2011_serial->ATEN2011_spectrum_2or4ports == 2) - && (((__u16) port->number - (__u16) (minor)) != 0)) { - usb_fill_bulk_urb(ATEN2011_port->read_urb, serial->dev, - usb_rcvbulkpipe(serial->dev, - (port-> - bulk_in_endpointAddress + - 2)), port->bulk_in_buffer, - ATEN2011_port->read_urb-> - transfer_buffer_length, - ATEN2011_bulk_in_callback, ATEN2011_port); - } else - usb_fill_bulk_urb(ATEN2011_port->read_urb, - serial->dev, - usb_rcvbulkpipe(serial->dev, - port-> - bulk_in_endpointAddress), - port->bulk_in_buffer, - ATEN2011_port->read_urb-> - transfer_buffer_length, - ATEN2011_bulk_in_callback, ATEN2011_port); - - dbg("ATEN2011_open: bulkin endpoint is %d", - port->bulk_in_endpointAddress); - response = usb_submit_urb(ATEN2011_port->read_urb, GFP_KERNEL); - if (response) { - err("%s - Error %d submitting control urb", __func__, - response); - } - - /* initialize our wait queues */ - init_waitqueue_head(&ATEN2011_port->wait_chase); - init_waitqueue_head(&ATEN2011_port->wait_command); - - /* initialize our icount structure */ - memset(&(ATEN2011_port->icount), 0x00, sizeof(ATEN2011_port->icount)); - - /* initialize our port settings */ - ATEN2011_port->shadowMCR = MCR_MASTER_IE; /* Must set to enable ints! */ - ATEN2011_port->chaseResponsePending = 0; - /* send a open port command */ - ATEN2011_port->open = 1; - /* ATEN2011_change_port_settings(ATEN2011_port,old_termios); */ - /* Setup termios */ - ATEN2011_set_termios(tty, port, &tmp_termios); - ATEN2011_port->icount.tx = 0; - ATEN2011_port->icount.rx = 0; - - dbg("usb_serial serial:%x ATEN2011_port:%x\nATEN2011_serial:%x usb_serial_port port:%x", - (unsigned int)serial, (unsigned int)ATEN2011_port, - (unsigned int)ATEN2011_serial, (unsigned int)port); - - return 0; - -} - -static int ATEN2011_chars_in_buffer(struct tty_struct *tty) -{ - struct usb_serial_port *port = tty->driver_data; - int i; - int chars = 0; - struct ATENINTL_port *ATEN2011_port; - - /* dbg("%s"," ATEN2011_chars_in_buffer:entering ..........."); */ - - ATEN2011_port = usb_get_serial_port_data(port); - if (ATEN2011_port == NULL) { - dbg("%s", "ATEN2011_break:leaving ..........."); - return -1; - } - - for (i = 0; i < NUM_URBS; ++i) - if (ATEN2011_port->write_urb_pool[i]->status == -EINPROGRESS) - chars += URB_TRANSFER_BUFFER_SIZE; - - dbg("%s - returns %d", __func__, chars); - return chars; - -} - -static void ATEN2011_block_until_tx_empty(struct tty_struct *tty, - struct ATENINTL_port *ATEN2011_port) -{ - int timeout = HZ / 10; - int wait = 30; - int count; - - while (1) { - count = ATEN2011_chars_in_buffer(tty); - - /* Check for Buffer status */ - if (count <= 0) - return; - - /* Block the thread for a while */ - interruptible_sleep_on_timeout(&ATEN2011_port->wait_chase, - timeout); - - /* No activity.. count down section */ - wait--; - if (wait == 0) { - dbg("%s - TIMEOUT", __func__); - return; - } else { - /* Reset timout value back to seconds */ - wait = 30; - } - } -} - -static void ATEN2011_close(struct tty_struct *tty, struct usb_serial_port *port, - struct file *filp) -{ - struct usb_serial *serial; - struct ATENINTL_serial *ATEN2011_serial; - struct ATENINTL_port *ATEN2011_port; - int no_urbs; - __u16 Data; - - dbg("%s", "ATEN2011_close:entering..."); - serial = port->serial; - - /* take the Adpater and port's private data */ - ATEN2011_serial = usb_get_serial_data(serial); - ATEN2011_port = usb_get_serial_port_data(port); - if ((ATEN2011_serial == NULL) || (ATEN2011_port == NULL)) - return; - - if (serial->dev) { - /* flush and block(wait) until tx is empty */ - ATEN2011_block_until_tx_empty(tty, ATEN2011_port); - } - /* kill the ports URB's */ - for (no_urbs = 0; no_urbs < NUM_URBS; no_urbs++) - usb_kill_urb(ATEN2011_port->write_urb_pool[no_urbs]); - /* Freeing Write URBs */ - for (no_urbs = 0; no_urbs < NUM_URBS; ++no_urbs) { - kfree(ATEN2011_port->write_urb_pool[no_urbs]->transfer_buffer); - usb_free_urb(ATEN2011_port->write_urb_pool[no_urbs]); - } - /* While closing port, shutdown all bulk read, write * - * and interrupt read if they exists */ - if (serial->dev) { - if (ATEN2011_port->write_urb) { - dbg("%s", "Shutdown bulk write"); - usb_kill_urb(ATEN2011_port->write_urb); - } - if (ATEN2011_port->read_urb) { - dbg("%s", "Shutdown bulk read"); - usb_kill_urb(ATEN2011_port->read_urb); - } - if ((&ATEN2011_port->control_urb)) { - dbg("%s", "Shutdown control read"); - /* usb_kill_urb (ATEN2011_port->control_urb); */ - - } - } - /* if(ATEN2011_port->ctrl_buf != NULL) */ - /* kfree(ATEN2011_port->ctrl_buf); */ - /* decrement the no.of open ports counter of an individual USB-serial adapter. */ - ATEN2011_serial->NoOfOpenPorts--; - dbg("NoOfOpenPorts in close%d:in port%d", - ATEN2011_serial->NoOfOpenPorts, port->number); - if (ATEN2011_serial->NoOfOpenPorts == 0) { - /* stop the stus polling here */ - ATEN2011_serial->status_polling_started = 0; - if (ATEN2011_serial->interrupt_read_urb) { - dbg("%s", "Shutdown interrupt_read_urb"); - /* ATEN2011_serial->interrupt_in_buffer=NULL; */ - /* usb_kill_urb (ATEN2011_serial->interrupt_read_urb); */ - } - } - if (ATEN2011_port->write_urb) { - /* if this urb had a transfer buffer already (old tx) free it */ - kfree(ATEN2011_port->write_urb->transfer_buffer); - usb_free_urb(ATEN2011_port->write_urb); - } - - /* clear the MCR & IER */ - Data = 0x00; - set_uart_reg(port, MODEM_CONTROL_REGISTER, Data); - Data = 0x00; - set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data); - - ATEN2011_port->open = 0; - dbg("%s", "Leaving ............"); - -} - -static void ATEN2011_block_until_chase_response(struct tty_struct *tty, - struct ATENINTL_port - *ATEN2011_port) -{ - int timeout = 1 * HZ; - int wait = 10; - int count; - - while (1) { - count = ATEN2011_chars_in_buffer(tty); - - /* Check for Buffer status */ - if (count <= 0) { - ATEN2011_port->chaseResponsePending = 0; - return; - } - - /* Block the thread for a while */ - interruptible_sleep_on_timeout(&ATEN2011_port->wait_chase, - timeout); - /* No activity.. count down section */ - wait--; - if (wait == 0) { - dbg("%s - TIMEOUT", __func__); - return; - } else { - /* Reset timout value back to seconds */ - wait = 10; - } - } - -} - -static void ATEN2011_break(struct tty_struct *tty, int break_state) -{ - struct usb_serial_port *port = tty->driver_data; - unsigned char data; - struct usb_serial *serial; - struct ATENINTL_serial *ATEN2011_serial; - struct ATENINTL_port *ATEN2011_port; - - dbg("%s", "Entering ..........."); - dbg("ATEN2011_break: Start"); - - serial = port->serial; - - ATEN2011_serial = usb_get_serial_data(serial); - ATEN2011_port = usb_get_serial_port_data(port); - - if ((ATEN2011_serial == NULL) || (ATEN2011_port == NULL)) - return; - - /* flush and chase */ - ATEN2011_port->chaseResponsePending = 1; - - if (serial->dev) { - /* flush and block until tx is empty */ - ATEN2011_block_until_chase_response(tty, ATEN2011_port); - } - - if (break_state == -1) - data = ATEN2011_port->shadowLCR | LCR_SET_BREAK; - else - data = ATEN2011_port->shadowLCR & ~LCR_SET_BREAK; - - ATEN2011_port->shadowLCR = data; - dbg("ATEN2011_break ATEN2011_port->shadowLCR is %x", - ATEN2011_port->shadowLCR); - set_uart_reg(port, LINE_CONTROL_REGISTER, ATEN2011_port->shadowLCR); - - return; -} - -static int ATEN2011_write_room(struct tty_struct *tty) -{ - struct usb_serial_port *port = tty->driver_data; - int i; - int room = 0; - struct ATENINTL_port *ATEN2011_port; - - ATEN2011_port = usb_get_serial_port_data(port); - if (ATEN2011_port == NULL) { - dbg("%s", "ATEN2011_break:leaving ..........."); - return -1; - } - - for (i = 0; i < NUM_URBS; ++i) - if (ATEN2011_port->write_urb_pool[i]->status != -EINPROGRESS) - room += URB_TRANSFER_BUFFER_SIZE; - - dbg("%s - returns %d", __func__, room); - return room; - -} - -static int ATEN2011_write(struct tty_struct *tty, struct usb_serial_port *port, - const unsigned char *data, int count) -{ - int status; - int i; - int bytes_sent = 0; - int transfer_size; - int minor; - - struct ATENINTL_port *ATEN2011_port; - struct usb_serial *serial; - struct ATENINTL_serial *ATEN2011_serial; - struct urb *urb; - const unsigned char *current_position = data; - unsigned char *data1; - dbg("%s", "entering ..........."); - - serial = port->serial; - - ATEN2011_port = usb_get_serial_port_data(port); - if (ATEN2011_port == NULL) { - dbg("%s", "ATEN2011_port is NULL"); - return -1; - } - - ATEN2011_serial = usb_get_serial_data(serial); - if (ATEN2011_serial == NULL) { - dbg("%s", "ATEN2011_serial is NULL"); - return -1; - } - - /* try to find a free urb in the list */ - urb = NULL; - - for (i = 0; i < NUM_URBS; ++i) { - if (ATEN2011_port->write_urb_pool[i]->status != -EINPROGRESS) { - urb = ATEN2011_port->write_urb_pool[i]; - dbg("URB:%d", i); - break; - } - } - - if (urb == NULL) { - dbg("%s - no more free urbs", __func__); - goto exit; - } - - if (urb->transfer_buffer == NULL) { - urb->transfer_buffer = - kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL); - - if (urb->transfer_buffer == NULL) { - err("%s no more kernel memory...", __func__); - goto exit; - } - } - transfer_size = min(count, URB_TRANSFER_BUFFER_SIZE); - - memcpy(urb->transfer_buffer, current_position, transfer_size); - /* usb_serial_debug_data (__FILE__, __func__, transfer_size, urb->transfer_buffer); */ - - /* fill urb with data and submit */ - minor = port->serial->minor; - if (minor == SERIAL_TTY_NO_MINOR) - minor = 0; - if ((ATEN2011_serial->ATEN2011_spectrum_2or4ports == 2) - && (((__u16) port->number - (__u16) (minor)) != 0)) { - usb_fill_bulk_urb(urb, ATEN2011_serial->serial->dev, - usb_sndbulkpipe(ATEN2011_serial->serial->dev, - (port-> - bulk_out_endpointAddress) + - 2), urb->transfer_buffer, - transfer_size, - ATEN2011_bulk_out_data_callback, - ATEN2011_port); - } else - - usb_fill_bulk_urb(urb, - ATEN2011_serial->serial->dev, - usb_sndbulkpipe(ATEN2011_serial->serial->dev, - port-> - bulk_out_endpointAddress), - urb->transfer_buffer, transfer_size, - ATEN2011_bulk_out_data_callback, - ATEN2011_port); - - data1 = urb->transfer_buffer; - dbg("bulkout endpoint is %d", port->bulk_out_endpointAddress); - /* for(i=0;i < urb->actual_length;i++) */ - /* dbg("Data is %c ",data1[i]); */ - - /* send it down the pipe */ - status = usb_submit_urb(urb, GFP_ATOMIC); - - if (status) { - err("%s - usb_submit_urb(write bulk) failed with status = %d", - __func__, status); - bytes_sent = status; - goto exit; - } - bytes_sent = transfer_size; - ATEN2011_port->icount.tx += transfer_size; - dbg("ATEN2011_port->icount.tx is %d:", ATEN2011_port->icount.tx); - -exit: - return bytes_sent; -} - -static void ATEN2011_throttle(struct tty_struct *tty) -{ - struct usb_serial_port *port = tty->driver_data; - struct ATENINTL_port *ATEN2011_port; - int status; - - dbg("- port %d", port->number); - - ATEN2011_port = usb_get_serial_port_data(port); - - if (ATEN2011_port == NULL) - return; - - if (!ATEN2011_port->open) { - dbg("%s", "port not opened"); - return; - } - - dbg("%s", "Entering .......... "); - - if (!tty) { - dbg("%s - no tty available", __func__); - return; - } - - /* if we are implementing XON/XOFF, send the stop character */ - if (I_IXOFF(tty)) { - unsigned char stop_char = STOP_CHAR(tty); - status = ATEN2011_write(tty, port, &stop_char, 1); - if (status <= 0) - return; - } - - /* if we are implementing RTS/CTS, toggle that line */ - if (tty->termios->c_cflag & CRTSCTS) { - ATEN2011_port->shadowMCR &= ~MCR_RTS; - status = set_uart_reg(port, MODEM_CONTROL_REGISTER, - ATEN2011_port->shadowMCR); - if (status < 0) - return; - } - - return; -} - -static void ATEN2011_unthrottle(struct tty_struct *tty) -{ - struct usb_serial_port *port = tty->driver_data; - int status; - struct ATENINTL_port *ATEN2011_port = usb_get_serial_port_data(port); - - if (ATEN2011_port == NULL) - return; - - if (!ATEN2011_port->open) { - dbg("%s - port not opened", __func__); - return; - } - - dbg("%s", "Entering .......... "); - - if (!tty) { - dbg("%s - no tty available", __func__); - return; - } - - /* if we are implementing XON/XOFF, send the start character */ - if (I_IXOFF(tty)) { - unsigned char start_char = START_CHAR(tty); - status = ATEN2011_write(tty, port, &start_char, 1); - if (status <= 0) - return; - } - - /* if we are implementing RTS/CTS, toggle that line */ - if (tty->termios->c_cflag & CRTSCTS) { - ATEN2011_port->shadowMCR |= MCR_RTS; - status = set_uart_reg(port, MODEM_CONTROL_REGISTER, - ATEN2011_port->shadowMCR); - if (status < 0) - return; - } - - return; -} - -static int ATEN2011_tiocmget(struct tty_struct *tty, struct file *file) -{ - struct usb_serial_port *port = tty->driver_data; - struct ATENINTL_port *ATEN2011_port; - unsigned int result; - __u16 msr; - __u16 mcr; - /* unsigned int mcr; */ - int status = 0; - ATEN2011_port = usb_get_serial_port_data(port); - - dbg("%s - port %d", __func__, port->number); - - if (ATEN2011_port == NULL) - return -ENODEV; - - status = get_uart_reg(port, MODEM_STATUS_REGISTER, &msr); - status = get_uart_reg(port, MODEM_CONTROL_REGISTER, &mcr); - /* mcr = ATEN2011_port->shadowMCR; */ - /* COMMENT2: the Fallowing three line are commented for updating only MSR values */ - result = ((mcr & MCR_DTR) ? TIOCM_DTR : 0) - | ((mcr & MCR_RTS) ? TIOCM_RTS : 0) - | ((mcr & MCR_LOOPBACK) ? TIOCM_LOOP : 0) - | ((msr & ATEN2011_MSR_CTS) ? TIOCM_CTS : 0) - | ((msr & ATEN2011_MSR_CD) ? TIOCM_CAR : 0) - | ((msr & ATEN2011_MSR_RI) ? TIOCM_RI : 0) - | ((msr & ATEN2011_MSR_DSR) ? TIOCM_DSR : 0); - - dbg("%s - 0x%04X", __func__, result); - - return result; -} - -static int ATEN2011_tiocmset(struct tty_struct *tty, struct file *file, - unsigned int set, unsigned int clear) -{ - struct usb_serial_port *port = tty->driver_data; - struct ATENINTL_port *ATEN2011_port; - unsigned int mcr; - unsigned int status; - - dbg("%s - port %d", __func__, port->number); - - ATEN2011_port = usb_get_serial_port_data(port); - - if (ATEN2011_port == NULL) - return -ENODEV; - - mcr = ATEN2011_port->shadowMCR; - if (clear & TIOCM_RTS) - mcr &= ~MCR_RTS; - if (clear & TIOCM_DTR) - mcr &= ~MCR_DTR; - if (clear & TIOCM_LOOP) - mcr &= ~MCR_LOOPBACK; - - if (set & TIOCM_RTS) - mcr |= MCR_RTS; - if (set & TIOCM_DTR) - mcr |= MCR_DTR; - if (set & TIOCM_LOOP) - mcr |= MCR_LOOPBACK; - - ATEN2011_port->shadowMCR = mcr; - - status = set_uart_reg(port, MODEM_CONTROL_REGISTER, mcr); - if (status < 0) { - dbg("setting MODEM_CONTROL_REGISTER Failed"); - return -1; - } - - return 0; -} - -static void ATEN2011_set_termios(struct tty_struct *tty, - struct usb_serial_port *port, - struct ktermios *old_termios) -{ - int status; - unsigned int cflag; - struct usb_serial *serial; - struct ATENINTL_port *ATEN2011_port; - - dbg("ATEN2011_set_termios: START"); - - serial = port->serial; - - ATEN2011_port = usb_get_serial_port_data(port); - - if (ATEN2011_port == NULL) - return; - - if (!ATEN2011_port->open) { - dbg("%s - port not opened", __func__); - return; - } - - dbg("%s", "setting termios - "); - - cflag = tty->termios->c_cflag; - - dbg("%s - cflag %08x iflag %08x", __func__, - tty->termios->c_cflag, RELEVANT_IFLAG(tty->termios->c_iflag)); - - if (old_termios) { - dbg("%s - old clfag %08x old iflag %08x", __func__, - old_termios->c_cflag, RELEVANT_IFLAG(old_termios->c_iflag)); - } - - dbg("%s - port %d", __func__, port->number); - - /* change the port settings to the new ones specified */ - - ATEN2011_change_port_settings(tty, ATEN2011_port, old_termios); - - if (!ATEN2011_port->read_urb) { - dbg("%s", "URB KILLED !!!!!"); - return; - } - - if (ATEN2011_port->read_urb->status != -EINPROGRESS) { - ATEN2011_port->read_urb->dev = serial->dev; - status = usb_submit_urb(ATEN2011_port->read_urb, GFP_ATOMIC); - if (status) { - dbg - (" usb_submit_urb(read bulk) failed, status = %d", - status); - } - } - return; -} - -static int get_lsr_info(struct tty_struct *tty, - struct ATENINTL_port *ATEN2011_port, - unsigned int __user *value) -{ - int count; - unsigned int result = 0; - - count = ATEN2011_chars_in_buffer(tty); - if (count == 0) { - dbg("%s -- Empty", __func__); - result = TIOCSER_TEMT; - } - - if (copy_to_user(value, &result, sizeof(int))) - return -EFAULT; - return 0; -} - -static int get_number_bytes_avail(struct tty_struct *tty, - struct ATENINTL_port *ATEN2011_port, - unsigned int __user *value) -{ - unsigned int result = 0; - - if (!tty) - return -ENOIOCTLCMD; - - result = tty->read_cnt; - - dbg("%s(%d) = %d", __func__, ATEN2011_port->port->number, result); - if (copy_to_user(value, &result, sizeof(int))) - return -EFAULT; - - return -ENOIOCTLCMD; -} - -static int set_modem_info(struct ATENINTL_port *ATEN2011_port, unsigned int cmd, - unsigned int __user *value) -{ - unsigned int mcr; - unsigned int arg; - __u16 Data; - int status; - struct usb_serial_port *port; - - if (ATEN2011_port == NULL) - return -1; - - port = (struct usb_serial_port *)ATEN2011_port->port; - - mcr = ATEN2011_port->shadowMCR; - - if (copy_from_user(&arg, value, sizeof(int))) - return -EFAULT; - - switch (cmd) { - case TIOCMBIS: - if (arg & TIOCM_RTS) - mcr |= MCR_RTS; - if (arg & TIOCM_DTR) - mcr |= MCR_RTS; - if (arg & TIOCM_LOOP) - mcr |= MCR_LOOPBACK; - break; - - case TIOCMBIC: - if (arg & TIOCM_RTS) - mcr &= ~MCR_RTS; - if (arg & TIOCM_DTR) - mcr &= ~MCR_RTS; - if (arg & TIOCM_LOOP) - mcr &= ~MCR_LOOPBACK; - break; - - case TIOCMSET: - /* turn off the RTS and DTR and LOOPBACK - * and then only turn on what was asked to */ - mcr &= ~(MCR_RTS | MCR_DTR | MCR_LOOPBACK); - mcr |= ((arg & TIOCM_RTS) ? MCR_RTS : 0); - mcr |= ((arg & TIOCM_DTR) ? MCR_DTR : 0); - mcr |= ((arg & TIOCM_LOOP) ? MCR_LOOPBACK : 0); - break; - } - - ATEN2011_port->shadowMCR = mcr; - - Data = ATEN2011_port->shadowMCR; - status = set_uart_reg(port, MODEM_CONTROL_REGISTER, Data); - if (status < 0) { - dbg("setting MODEM_CONTROL_REGISTER Failed"); - return -1; - } - - return 0; -} - -static int get_modem_info(struct ATENINTL_port *ATEN2011_port, - unsigned int __user *value) -{ - unsigned int result = 0; - __u16 msr; - unsigned int mcr = ATEN2011_port->shadowMCR; - int status; - - status = get_uart_reg(ATEN2011_port->port, MODEM_STATUS_REGISTER, &msr); - result = ((mcr & MCR_DTR) ? TIOCM_DTR : 0) /* 0x002 */ - |((mcr & MCR_RTS) ? TIOCM_RTS : 0) /* 0x004 */ - |((msr & ATEN2011_MSR_CTS) ? TIOCM_CTS : 0) /* 0x020 */ - |((msr & ATEN2011_MSR_CD) ? TIOCM_CAR : 0) /* 0x040 */ - |((msr & ATEN2011_MSR_RI) ? TIOCM_RI : 0) /* 0x080 */ - |((msr & ATEN2011_MSR_DSR) ? TIOCM_DSR : 0); /* 0x100 */ - - dbg("%s -- %x", __func__, result); - - if (copy_to_user(value, &result, sizeof(int))) - return -EFAULT; - return 0; -} - -static int get_serial_info(struct ATENINTL_port *ATEN2011_port, - struct serial_struct __user *retinfo) -{ - struct serial_struct tmp; - - if (ATEN2011_port == NULL) - return -1; - - if (!retinfo) - return -EFAULT; - - memset(&tmp, 0, sizeof(tmp)); - - tmp.type = PORT_16550A; - tmp.line = ATEN2011_port->port->serial->minor; - if (tmp.line == SERIAL_TTY_NO_MINOR) - tmp.line = 0; - tmp.port = ATEN2011_port->port->number; - tmp.irq = 0; - tmp.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ; - tmp.xmit_fifo_size = NUM_URBS * URB_TRANSFER_BUFFER_SIZE; - tmp.baud_base = 9600; - tmp.close_delay = 5 * HZ; - tmp.closing_wait = 30 * HZ; - - if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) - return -EFAULT; - return 0; -} - -static int ATEN2011_ioctl(struct tty_struct *tty, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct usb_serial_port *port = tty->driver_data; - struct ATENINTL_port *ATEN2011_port; - struct async_icount cnow; - struct async_icount cprev; - struct serial_icounter_struct icount; - int ATENret = 0; - unsigned int __user *user_arg = (unsigned int __user *)arg; - - ATEN2011_port = usb_get_serial_port_data(port); - - if (ATEN2011_port == NULL) - return -1; - - dbg("%s - port %d, cmd = 0x%x", __func__, port->number, cmd); - - switch (cmd) { - /* return number of bytes available */ - - case TIOCINQ: - dbg("%s (%d) TIOCINQ", __func__, port->number); - return get_number_bytes_avail(tty, ATEN2011_port, user_arg); - break; - - case TIOCOUTQ: - dbg("%s (%d) TIOCOUTQ", __func__, port->number); - return put_user(ATEN2011_chars_in_buffer(tty), user_arg); - break; - - case TIOCSERGETLSR: - dbg("%s (%d) TIOCSERGETLSR", __func__, port->number); - return get_lsr_info(tty, ATEN2011_port, user_arg); - return 0; - - case TIOCMBIS: - case TIOCMBIC: - case TIOCMSET: - dbg("%s (%d) TIOCMSET/TIOCMBIC/TIOCMSET", __func__, - port->number); - ATENret = set_modem_info(ATEN2011_port, cmd, user_arg); - return ATENret; - - case TIOCMGET: - dbg("%s (%d) TIOCMGET", __func__, port->number); - return get_modem_info(ATEN2011_port, user_arg); - - case TIOCGSERIAL: - dbg("%s (%d) TIOCGSERIAL", __func__, port->number); - return get_serial_info(ATEN2011_port, - (struct serial_struct __user *)arg); - - case TIOCSSERIAL: - dbg("%s (%d) TIOCSSERIAL", __func__, port->number); - break; - - case TIOCMIWAIT: - dbg("%s (%d) TIOCMIWAIT", __func__, port->number); - cprev = ATEN2011_port->icount; - while (1) { - /* see if a signal did it */ - if (signal_pending(current)) - return -ERESTARTSYS; - cnow = ATEN2011_port->icount; - if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && - cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) - return -EIO; /* no change => error */ - if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || - ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || - ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || - ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) { - return 0; - } - cprev = cnow; - } - /* NOTREACHED */ - break; - - case TIOCGICOUNT: - cnow = ATEN2011_port->icount; - icount.cts = cnow.cts; - icount.dsr = cnow.dsr; - icount.rng = cnow.rng; - icount.dcd = cnow.dcd; - icount.rx = cnow.rx; - icount.tx = cnow.tx; - icount.frame = cnow.frame; - icount.overrun = cnow.overrun; - icount.parity = cnow.parity; - icount.brk = cnow.brk; - icount.buf_overrun = cnow.buf_overrun; - - dbg("%s (%d) TIOCGICOUNT RX=%d, TX=%d", __func__, - port->number, icount.rx, icount.tx); - if (copy_to_user((void __user *)arg, &icount, sizeof(icount))) - return -EFAULT; - return 0; - - default: - break; - } - - return -ENOIOCTLCMD; -} - -static int ATEN2011_calc_baud_rate_divisor(int baudRate, int *divisor, - __u16 *clk_sel_val) -{ - dbg("%s - %d", __func__, baudRate); - - if (baudRate <= 115200) { - *divisor = 115200 / baudRate; - *clk_sel_val = 0x0; - } - if ((baudRate > 115200) && (baudRate <= 230400)) { - *divisor = 230400 / baudRate; - *clk_sel_val = 0x10; - } else if ((baudRate > 230400) && (baudRate <= 403200)) { - *divisor = 403200 / baudRate; - *clk_sel_val = 0x20; - } else if ((baudRate > 403200) && (baudRate <= 460800)) { - *divisor = 460800 / baudRate; - *clk_sel_val = 0x30; - } else if ((baudRate > 460800) && (baudRate <= 806400)) { - *divisor = 806400 / baudRate; - *clk_sel_val = 0x40; - } else if ((baudRate > 806400) && (baudRate <= 921600)) { - *divisor = 921600 / baudRate; - *clk_sel_val = 0x50; - } else if ((baudRate > 921600) && (baudRate <= 1572864)) { - *divisor = 1572864 / baudRate; - *clk_sel_val = 0x60; - } else if ((baudRate > 1572864) && (baudRate <= 3145728)) { - *divisor = 3145728 / baudRate; - *clk_sel_val = 0x70; - } - return 0; -} - -static int ATEN2011_send_cmd_write_baud_rate(struct ATENINTL_port - *ATEN2011_port, int baudRate) -{ - int divisor = 0; - int status; - __u16 Data; - unsigned char number; - __u16 clk_sel_val; - struct usb_serial_port *port; - int minor; - - if (ATEN2011_port == NULL) - return -1; - - port = (struct usb_serial_port *)ATEN2011_port->port; - - dbg("%s", "Entering .......... "); - - minor = ATEN2011_port->port->serial->minor; - if (minor == SERIAL_TTY_NO_MINOR) - minor = 0; - number = ATEN2011_port->port->number - minor; - - dbg("%s - port = %d, baud = %d", __func__, - ATEN2011_port->port->number, baudRate); - /* reset clk_uart_sel in spregOffset */ - if (baudRate > 115200) { -#ifdef HW_flow_control - /* - * NOTE: need to see the pther register to modify - * setting h/w flow control bit to 1; - */ - /* Data = ATEN2011_port->shadowMCR; */ - Data = 0x2b; - ATEN2011_port->shadowMCR = Data; - status = set_uart_reg(port, MODEM_CONTROL_REGISTER, Data); - if (status < 0) { - dbg("Writing spreg failed in set_serial_baud"); - return -1; - } -#endif - - } else { -#ifdef HW_flow_control - /* setting h/w flow control bit to 0; */ - /* Data = ATEN2011_port->shadowMCR; */ - Data = 0xb; - ATEN2011_port->shadowMCR = Data; - status = set_uart_reg(port, MODEM_CONTROL_REGISTER, Data); - if (status < 0) { - dbg("Writing spreg failed in set_serial_baud"); - return -1; - } -#endif - - } - - if (1) /* baudRate <= 115200) */ { - clk_sel_val = 0x0; - Data = 0x0; - status = - ATEN2011_calc_baud_rate_divisor(baudRate, &divisor, - &clk_sel_val); - status = get_reg_sync(port, ATEN2011_port->SpRegOffset, &Data); - if (status < 0) { - dbg("reading spreg failed in set_serial_baud"); - return -1; - } - Data = (Data & 0x8f) | clk_sel_val; - status = set_reg_sync(port, ATEN2011_port->SpRegOffset, Data); - if (status < 0) { - dbg("Writing spreg failed in set_serial_baud"); - return -1; - } - /* Calculate the Divisor */ - - if (status) { - err("%s - bad baud rate", __func__); - dbg("%s", "bad baud rate"); - return status; - } - /* Enable access to divisor latch */ - Data = ATEN2011_port->shadowLCR | SERIAL_LCR_DLAB; - ATEN2011_port->shadowLCR = Data; - set_uart_reg(port, LINE_CONTROL_REGISTER, Data); - - /* Write the divisor */ - Data = (unsigned char)(divisor & 0xff); - dbg("set_serial_baud Value to write DLL is %x", Data); - set_uart_reg(port, DIVISOR_LATCH_LSB, Data); - - Data = (unsigned char)((divisor & 0xff00) >> 8); - dbg("set_serial_baud Value to write DLM is %x", Data); - set_uart_reg(port, DIVISOR_LATCH_MSB, Data); - - /* Disable access to divisor latch */ - Data = ATEN2011_port->shadowLCR & ~SERIAL_LCR_DLAB; - ATEN2011_port->shadowLCR = Data; - set_uart_reg(port, LINE_CONTROL_REGISTER, Data); - - } - - return status; -} - -static void ATEN2011_change_port_settings(struct tty_struct *tty, - struct ATENINTL_port *ATEN2011_port, - struct ktermios *old_termios) -{ - int baud; - unsigned cflag; - unsigned iflag; - __u8 lData; - __u8 lParity; - __u8 lStop; - int status; - __u16 Data; - struct usb_serial_port *port; - struct usb_serial *serial; - - if (ATEN2011_port == NULL) - return; - - port = (struct usb_serial_port *)ATEN2011_port->port; - - serial = port->serial; - - dbg("%s - port %d", __func__, ATEN2011_port->port->number); - - if (!ATEN2011_port->open) { - dbg("%s - port not opened", __func__); - return; - } - - if ((!tty) || (!tty->termios)) { - dbg("%s - no tty structures", __func__); - return; - } - - dbg("%s", "Entering .......... "); - - lData = LCR_BITS_8; - lStop = LCR_STOP_1; - lParity = LCR_PAR_NONE; - - cflag = tty->termios->c_cflag; - iflag = tty->termios->c_iflag; - - /* Change the number of bits */ - - /* COMMENT1: the below Line"if(cflag & CSIZE)" is added for the errors we get for serial loop data test i.e serial_loopback.pl -v */ - /* if(cflag & CSIZE) */ - { - switch (cflag & CSIZE) { - case CS5: - lData = LCR_BITS_5; - break; - - case CS6: - lData = LCR_BITS_6; - break; - - case CS7: - lData = LCR_BITS_7; - break; - default: - case CS8: - lData = LCR_BITS_8; - break; - } - } - /* Change the Parity bit */ - if (cflag & PARENB) { - if (cflag & PARODD) { - lParity = LCR_PAR_ODD; - dbg("%s - parity = odd", __func__); - } else { - lParity = LCR_PAR_EVEN; - dbg("%s - parity = even", __func__); - } - - } else { - dbg("%s - parity = none", __func__); - } - - if (cflag & CMSPAR) - lParity = lParity | 0x20; - - /* Change the Stop bit */ - if (cflag & CSTOPB) { - lStop = LCR_STOP_2; - dbg("%s - stop bits = 2", __func__); - } else { - lStop = LCR_STOP_1; - dbg("%s - stop bits = 1", __func__); - } - - /* Update the LCR with the correct value */ - ATEN2011_port->shadowLCR &= - ~(LCR_BITS_MASK | LCR_STOP_MASK | LCR_PAR_MASK); - ATEN2011_port->shadowLCR |= (lData | lParity | lStop); - - dbg - ("ATEN2011_change_port_settings ATEN2011_port->shadowLCR is %x", - ATEN2011_port->shadowLCR); - /* Disable Interrupts */ - Data = 0x00; - set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data); - - Data = 0x00; - set_uart_reg(port, FIFO_CONTROL_REGISTER, Data); - - Data = 0xcf; - set_uart_reg(port, FIFO_CONTROL_REGISTER, Data); - - /* Send the updated LCR value to the ATEN2011 */ - Data = ATEN2011_port->shadowLCR; - - set_uart_reg(port, LINE_CONTROL_REGISTER, Data); - - Data = 0x00b; - ATEN2011_port->shadowMCR = Data; - set_uart_reg(port, MODEM_CONTROL_REGISTER, Data); - Data = 0x00b; - set_uart_reg(port, MODEM_CONTROL_REGISTER, Data); - - /* set up the MCR register and send it to the ATEN2011 */ - - ATEN2011_port->shadowMCR = MCR_MASTER_IE; - if (cflag & CBAUD) - ATEN2011_port->shadowMCR |= (MCR_DTR | MCR_RTS); - - if (cflag & CRTSCTS) - ATEN2011_port->shadowMCR |= (MCR_XON_ANY); - else - ATEN2011_port->shadowMCR &= ~(MCR_XON_ANY); - - Data = ATEN2011_port->shadowMCR; - set_uart_reg(port, MODEM_CONTROL_REGISTER, Data); - - /* Determine divisor based on baud rate */ - baud = tty_get_baud_rate(tty); - - if (!baud) { - /* pick a default, any default... */ - dbg("%s", "Picked default baud..."); - baud = 9600; - } - - dbg("%s - baud rate = %d", __func__, baud); - status = ATEN2011_send_cmd_write_baud_rate(ATEN2011_port, baud); - - /* Enable Interrupts */ - Data = 0x0c; - set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data); - - if (ATEN2011_port->read_urb->status != -EINPROGRESS) { - ATEN2011_port->read_urb->dev = serial->dev; - - status = usb_submit_urb(ATEN2011_port->read_urb, GFP_ATOMIC); - - if (status) { - dbg - (" usb_submit_urb(read bulk) failed, status = %d", - status); - } - } - dbg - ("ATEN2011_change_port_settings ATEN2011_port->shadowLCR is End %x", - ATEN2011_port->shadowLCR); - - return; -} - -static int ATEN2011_calc_num_ports(struct usb_serial *serial) -{ - - __u16 Data = 0x00; - int ret = 0; - int ATEN2011_2or4ports; - ret = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), - ATEN_RDREQ, ATEN_RD_RTYPE, 0, GPIO_REGISTER, - &Data, VENDOR_READ_LENGTH, ATEN_WDR_TIMEOUT); - -/* ghostgum: here is where the problem appears to bet */ -/* Which of the following are needed? */ -/* Greg used the serial->type->num_ports=2 */ -/* But the code in the ATEN2011_open relies on serial->num_ports=2 */ - if ((Data & 0x01) == 0) { - ATEN2011_2or4ports = 2; - serial->type->num_ports = 2; - serial->num_ports = 2; - } - /* else if(serial->interface->cur_altsetting->desc.bNumEndpoints == 9) */ - else { - ATEN2011_2or4ports = 4; - serial->type->num_ports = 4; - serial->num_ports = 4; - - } - - return ATEN2011_2or4ports; -} - -static int ATEN2011_startup(struct usb_serial *serial) -{ - struct ATENINTL_serial *ATEN2011_serial; - struct ATENINTL_port *ATEN2011_port; - struct usb_device *dev; - int i, status; - int minor; - - __u16 Data; - dbg("%s", " ATEN2011_startup :entering.........."); - - if (!serial) { - dbg("%s", "Invalid Handler"); - return -1; - } - - dev = serial->dev; - - dbg("%s", "Entering..."); - - /* create our private serial structure */ - ATEN2011_serial = kzalloc(sizeof(struct ATENINTL_serial), GFP_KERNEL); - if (ATEN2011_serial == NULL) { - err("%s - Out of memory", __func__); - return -ENOMEM; - } - - /* resetting the private structure field values to zero */ - memset(ATEN2011_serial, 0, sizeof(struct ATENINTL_serial)); - - ATEN2011_serial->serial = serial; - /* initilize status polling flag to 0 */ - ATEN2011_serial->status_polling_started = 0; - - usb_set_serial_data(serial, ATEN2011_serial); - ATEN2011_serial->ATEN2011_spectrum_2or4ports = - ATEN2011_calc_num_ports(serial); - /* we set up the pointers to the endpoints in the ATEN2011_open * - * function, as the structures aren't created yet. */ - - /* set up port private structures */ - for (i = 0; i < serial->num_ports; ++i) { - ATEN2011_port = - kmalloc(sizeof(struct ATENINTL_port), GFP_KERNEL); - if (ATEN2011_port == NULL) { - err("%s - Out of memory", __func__); - usb_set_serial_data(serial, NULL); - kfree(ATEN2011_serial); - return -ENOMEM; - } - memset(ATEN2011_port, 0, sizeof(struct ATENINTL_port)); - - /* - * Initialize all port interrupt end point to port 0 - * int endpoint. Our device has only one interrupt end point - * comman to all port - */ - /* serial->port[i]->interrupt_in_endpointAddress = serial->port[0]->interrupt_in_endpointAddress; */ - - ATEN2011_port->port = serial->port[i]; - usb_set_serial_port_data(serial->port[i], ATEN2011_port); - - minor = serial->port[i]->serial->minor; - if (minor == SERIAL_TTY_NO_MINOR) - minor = 0; - ATEN2011_port->port_num = - ((serial->port[i]->number - minor) + 1); - - if (ATEN2011_port->port_num == 1) { - ATEN2011_port->SpRegOffset = 0x0; - ATEN2011_port->ControlRegOffset = 0x1; - ATEN2011_port->DcrRegOffset = 0x4; - } else if ((ATEN2011_port->port_num == 2) - && (ATEN2011_serial->ATEN2011_spectrum_2or4ports == - 4)) { - ATEN2011_port->SpRegOffset = 0x8; - ATEN2011_port->ControlRegOffset = 0x9; - ATEN2011_port->DcrRegOffset = 0x16; - } else if ((ATEN2011_port->port_num == 2) - && (ATEN2011_serial->ATEN2011_spectrum_2or4ports == - 2)) { - ATEN2011_port->SpRegOffset = 0xa; - ATEN2011_port->ControlRegOffset = 0xb; - ATEN2011_port->DcrRegOffset = 0x19; - } else if ((ATEN2011_port->port_num == 3) - && (ATEN2011_serial->ATEN2011_spectrum_2or4ports == - 4)) { - ATEN2011_port->SpRegOffset = 0xa; - ATEN2011_port->ControlRegOffset = 0xb; - ATEN2011_port->DcrRegOffset = 0x19; - } else if ((ATEN2011_port->port_num == 4) - && (ATEN2011_serial->ATEN2011_spectrum_2or4ports == - 4)) { - ATEN2011_port->SpRegOffset = 0xc; - ATEN2011_port->ControlRegOffset = 0xd; - ATEN2011_port->DcrRegOffset = 0x1c; - } - - usb_set_serial_port_data(serial->port[i], ATEN2011_port); - - /* enable rx_disable bit in control register */ - - status = get_reg_sync(serial->port[i], - ATEN2011_port->ControlRegOffset, &Data); - if (status < 0) { - dbg("Reading ControlReg failed status-0x%x", - status); - break; - } else - dbg - ("ControlReg Reading success val is %x, status%d", - Data, status); - Data |= 0x08; /* setting driver done bit */ - Data |= 0x04; /* sp1_bit to have cts change reflect in modem status reg */ - - /* Data |= 0x20; */ /* rx_disable bit */ - status = set_reg_sync(serial->port[i], - ATEN2011_port->ControlRegOffset, Data); - if (status < 0) { - dbg - ("Writing ControlReg failed(rx_disable) status-0x%x", - status); - break; - } else - dbg - ("ControlReg Writing success(rx_disable) status%d", - status); - - /* - * Write default values in DCR (i.e 0x01 in DCR0, 0x05 in DCR2 - * and 0x24 in DCR3 - */ - Data = 0x01; - status = set_reg_sync(serial->port[i], - (__u16)(ATEN2011_port->DcrRegOffset + 0), - Data); - if (status < 0) { - dbg("Writing DCR0 failed status-0x%x", status); - break; - } else - dbg("DCR0 Writing success status%d", status); - - Data = 0x05; - status = set_reg_sync(serial->port[i], - (__u16)(ATEN2011_port->DcrRegOffset + 1), - Data); - if (status < 0) { - dbg("Writing DCR1 failed status-0x%x", status); - break; - } else - dbg("DCR1 Writing success status%d", status); - - Data = 0x24; - status = set_reg_sync(serial->port[i], - (__u16)(ATEN2011_port->DcrRegOffset + 2), - Data); - if (status < 0) { - dbg("Writing DCR2 failed status-0x%x", status); - break; - } else - dbg("DCR2 Writing success status%d", status); - - /* write values in clkstart0x0 and clkmulti 0x20 */ - Data = 0x0; - status = set_reg_sync(serial->port[i], CLK_START_VALUE_REGISTER, - Data); - if (status < 0) { - dbg - ("Writing CLK_START_VALUE_REGISTER failed status-0x%x", - status); - break; - } else - dbg - ("CLK_START_VALUE_REGISTER Writing success status%d", - status); - - Data = 0x20; - status = set_reg_sync(serial->port[i], CLK_MULTI_REGISTER, - Data); - if (status < 0) { - dbg - ("Writing CLK_MULTI_REGISTER failed status-0x%x", - status); - break; - } else - dbg("CLK_MULTI_REGISTER Writing success status%d", - status); - - /* Zero Length flag register */ - if ((ATEN2011_port->port_num != 1) - && (ATEN2011_serial->ATEN2011_spectrum_2or4ports == 2)) { - - Data = 0xff; - status = set_reg_sync(serial->port[i], - (__u16)(ZLP_REG1 + ((__u16)ATEN2011_port->port_num)), - Data); - dbg("ZLIP offset%x", - (__u16) (ZLP_REG1 + - ((__u16) ATEN2011_port->port_num))); - if (status < 0) { - dbg - ("Writing ZLP_REG%d failed status-0x%x", - i + 2, status); - break; - } else - dbg("ZLP_REG%d Writing success status%d", - i + 2, status); - } else { - Data = 0xff; - status = set_reg_sync(serial->port[i], - (__u16)(ZLP_REG1 + ((__u16)ATEN2011_port->port_num) - 0x1), - Data); - dbg("ZLIP offset%x", - (__u16) (ZLP_REG1 + - ((__u16) ATEN2011_port->port_num) - - 0x1)); - if (status < 0) { - dbg - ("Writing ZLP_REG%d failed status-0x%x", - i + 1, status); - break; - } else - dbg("ZLP_REG%d Writing success status%d", - i + 1, status); - - } - ATEN2011_port->control_urb = usb_alloc_urb(0, GFP_ATOMIC); - ATEN2011_port->ctrl_buf = kmalloc(16, GFP_KERNEL); - - } - - /* Zero Length flag enable */ - Data = 0x0f; - status = set_reg_sync(serial->port[0], ZLP_REG5, Data); - if (status < 0) { - dbg("Writing ZLP_REG5 failed status-0x%x", status); - return -1; - } else - dbg("ZLP_REG5 Writing success status%d", status); - - /* setting configuration feature to one */ - usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), - (__u8) 0x03, 0x00, 0x01, 0x00, NULL, 0x00, 5 * HZ); - return 0; -} - -static void ATEN2011_release(struct usb_serial *serial) -{ - int i; - struct ATENINTL_port *ATEN2011_port; - - /* check for the ports to be closed,close the ports and disconnect */ - - /* free private structure allocated for serial port * - * stop reads and writes on all ports */ - - for (i = 0; i < serial->num_ports; ++i) { - ATEN2011_port = usb_get_serial_port_data(serial->port[i]); - kfree(ATEN2011_port->ctrl_buf); - usb_kill_urb(ATEN2011_port->control_urb); - kfree(ATEN2011_port); - usb_set_serial_port_data(serial->port[i], NULL); - } - - /* free private structure allocated for serial device */ - - kfree(usb_get_serial_data(serial)); - usb_set_serial_data(serial, NULL); -} - -static struct usb_serial_driver aten_serial_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "aten2011", - }, - .description = DRIVER_DESC, - .id_table = id_table, - .open = ATEN2011_open, - .close = ATEN2011_close, - .write = ATEN2011_write, - .write_room = ATEN2011_write_room, - .chars_in_buffer = ATEN2011_chars_in_buffer, - .throttle = ATEN2011_throttle, - .unthrottle = ATEN2011_unthrottle, - .calc_num_ports = ATEN2011_calc_num_ports, - - .ioctl = ATEN2011_ioctl, - .set_termios = ATEN2011_set_termios, - .break_ctl = ATEN2011_break, - .tiocmget = ATEN2011_tiocmget, - .tiocmset = ATEN2011_tiocmset, - .attach = ATEN2011_startup, - .release = ATEN2011_release, - .read_bulk_callback = ATEN2011_bulk_in_callback, - .read_int_callback = ATEN2011_interrupt_callback, -}; - -static struct usb_driver aten_driver = { - .name = "aten2011", - .probe = usb_serial_probe, - .disconnect = usb_serial_disconnect, - .id_table = id_table, -}; - -static int __init aten_init(void) -{ - int retval; - - /* Register with the usb serial */ - retval = usb_serial_register(&aten_serial_driver); - if (retval) - return retval; - - printk(KERN_INFO KBUILD_MODNAME ":" - DRIVER_DESC " " DRIVER_VERSION "\n"); - - /* Register with the usb */ - retval = usb_register(&aten_driver); - if (retval) - usb_serial_deregister(&aten_serial_driver); - - return retval; -} - -static void __exit aten_exit(void) -{ - usb_deregister(&aten_driver); - usb_serial_deregister(&aten_serial_driver); -} - -module_init(aten_init); -module_exit(aten_exit); - -/* Module information */ -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); - -MODULE_PARM_DESC(debug, "Debug enabled or not"); diff --git a/drivers/staging/udlfb/udlfb.c b/drivers/staging/udlfb/udlfb.c index 0ab9d15f343..f5416af1e90 100644 --- a/drivers/staging/udlfb/udlfb.c +++ b/drivers/staging/udlfb/udlfb.c @@ -21,6 +21,7 @@ #include <linux/mm.h> #include <linux/fb.h> #include <linux/mutex.h> +#include <linux/vmalloc.h> #include "udlfb.h" diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 5b15d9d8896..e1f89416ef8 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -750,7 +750,7 @@ static int acm_tty_chars_in_buffer(struct tty_struct *tty) { struct acm *acm = tty->driver_data; if (!ACM_READY(acm)) - return -EINVAL; + return 0; /* * This is inaccurate (overcounts), but it works. */ diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 24dfb33f90c..a16c538d013 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -80,38 +80,18 @@ static int usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno, int max_tx; int i; - /* Allocate space for the SS endpoint companion descriptor */ - ep->ss_ep_comp = kzalloc(sizeof(struct usb_host_ss_ep_comp), - GFP_KERNEL); - if (!ep->ss_ep_comp) - return -ENOMEM; desc = (struct usb_ss_ep_comp_descriptor *) buffer; if (desc->bDescriptorType != USB_DT_SS_ENDPOINT_COMP) { dev_warn(ddev, "No SuperSpeed endpoint companion for config %d " " interface %d altsetting %d ep %d: " "using minimum values\n", cfgno, inum, asnum, ep->desc.bEndpointAddress); - ep->ss_ep_comp->desc.bLength = USB_DT_SS_EP_COMP_SIZE; - ep->ss_ep_comp->desc.bDescriptorType = USB_DT_SS_ENDPOINT_COMP; - ep->ss_ep_comp->desc.bMaxBurst = 0; - /* - * Leave bmAttributes as zero, which will mean no streams for - * bulk, and isoc won't support multiple bursts of packets. - * With bursts of only one packet, and a Mult of 1, the max - * amount of data moved per endpoint service interval is one - * packet. - */ - if (usb_endpoint_xfer_isoc(&ep->desc) || - usb_endpoint_xfer_int(&ep->desc)) - ep->ss_ep_comp->desc.wBytesPerInterval = - ep->desc.wMaxPacketSize; /* * The next descriptor is for an Endpoint or Interface, * no extra descriptors to copy into the companion structure, * and we didn't eat up any of the buffer. */ - retval = 0; - goto valid; + return 0; } memcpy(&ep->ss_ep_comp->desc, desc, USB_DT_SS_EP_COMP_SIZE); desc = &ep->ss_ep_comp->desc; @@ -320,6 +300,28 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, buffer += i; size -= i; + /* Allocate space for the SS endpoint companion descriptor */ + endpoint->ss_ep_comp = kzalloc(sizeof(struct usb_host_ss_ep_comp), + GFP_KERNEL); + if (!endpoint->ss_ep_comp) + return -ENOMEM; + + /* Fill in some default values (may be overwritten later) */ + endpoint->ss_ep_comp->desc.bLength = USB_DT_SS_EP_COMP_SIZE; + endpoint->ss_ep_comp->desc.bDescriptorType = USB_DT_SS_ENDPOINT_COMP; + endpoint->ss_ep_comp->desc.bMaxBurst = 0; + /* + * Leave bmAttributes as zero, which will mean no streams for + * bulk, and isoc won't support multiple bursts of packets. + * With bursts of only one packet, and a Mult of 1, the max + * amount of data moved per endpoint service interval is one + * packet. + */ + if (usb_endpoint_xfer_isoc(&endpoint->desc) || + usb_endpoint_xfer_int(&endpoint->desc)) + endpoint->ss_ep_comp->desc.wBytesPerInterval = + endpoint->desc.wMaxPacketSize; + if (size > 0) { retval = usb_parse_ss_endpoint_companion(ddev, cfgno, inum, asnum, endpoint, num_ep, buffer, @@ -329,6 +331,10 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, retval = buffer - buffer0; } } else { + dev_warn(ddev, "config %d interface %d altsetting %d " + "endpoint 0x%X has no " + "SuperSpeed companion descriptor\n", + cfgno, inum, asnum, d->bEndpointAddress); retval = buffer - buffer0; } } else { diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c index dc2ac613a9d..1d283e1b2b8 100644 --- a/drivers/usb/host/ehci-orion.c +++ b/drivers/usb/host/ehci-orion.c @@ -105,6 +105,7 @@ static int ehci_orion_setup(struct usb_hcd *hcd) struct ehci_hcd *ehci = hcd_to_ehci(hcd); int retval; + ehci_reset(ehci); retval = ehci_halt(ehci); if (retval) return retval; @@ -118,7 +119,6 @@ static int ehci_orion_setup(struct usb_hcd *hcd) hcd->has_tt = 1; - ehci_reset(ehci); ehci_port_power(ehci, 0); return retval; diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index f3aaba35e91..83cbecd2a1e 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -282,6 +282,7 @@ static int ohci_omap_init(struct usb_hcd *hcd) static void ohci_omap_stop(struct usb_hcd *hcd) { dev_dbg(hcd->self.controller, "stopping USB Controller\n"); + ohci_stop(hcd); omap_ohci_clock_power(0); } diff --git a/drivers/usb/host/xhci-dbg.c b/drivers/usb/host/xhci-dbg.c index 2501c571f85..705e3432415 100644 --- a/drivers/usb/host/xhci-dbg.c +++ b/drivers/usb/host/xhci-dbg.c @@ -173,6 +173,7 @@ void xhci_print_ir_set(struct xhci_hcd *xhci, struct xhci_intr_reg *ir_set, int { void *addr; u32 temp; + u64 temp_64; addr = &ir_set->irq_pending; temp = xhci_readl(xhci, addr); @@ -200,25 +201,15 @@ void xhci_print_ir_set(struct xhci_hcd *xhci, struct xhci_intr_reg *ir_set, int xhci_dbg(xhci, " WARN: %p: ir_set.rsvd = 0x%x\n", addr, (unsigned int)temp); - addr = &ir_set->erst_base[0]; - temp = xhci_readl(xhci, addr); - xhci_dbg(xhci, " %p: ir_set.erst_base[0] = 0x%x\n", - addr, (unsigned int) temp); - - addr = &ir_set->erst_base[1]; - temp = xhci_readl(xhci, addr); - xhci_dbg(xhci, " %p: ir_set.erst_base[1] = 0x%x\n", - addr, (unsigned int) temp); + addr = &ir_set->erst_base; + temp_64 = xhci_read_64(xhci, addr); + xhci_dbg(xhci, " %p: ir_set.erst_base = @%08llx\n", + addr, temp_64); - addr = &ir_set->erst_dequeue[0]; - temp = xhci_readl(xhci, addr); - xhci_dbg(xhci, " %p: ir_set.erst_dequeue[0] = 0x%x\n", - addr, (unsigned int) temp); - - addr = &ir_set->erst_dequeue[1]; - temp = xhci_readl(xhci, addr); - xhci_dbg(xhci, " %p: ir_set.erst_dequeue[1] = 0x%x\n", - addr, (unsigned int) temp); + addr = &ir_set->erst_dequeue; + temp_64 = xhci_read_64(xhci, addr); + xhci_dbg(xhci, " %p: ir_set.erst_dequeue = @%08llx\n", + addr, temp_64); } void xhci_print_run_regs(struct xhci_hcd *xhci) @@ -268,8 +259,7 @@ void xhci_debug_trb(struct xhci_hcd *xhci, union xhci_trb *trb) xhci_dbg(xhci, "Link TRB:\n"); xhci_print_trb_offsets(xhci, trb); - address = trb->link.segment_ptr[0] + - (((u64) trb->link.segment_ptr[1]) << 32); + address = trb->link.segment_ptr; xhci_dbg(xhci, "Next ring segment DMA address = 0x%llx\n", address); xhci_dbg(xhci, "Interrupter target = 0x%x\n", @@ -282,8 +272,7 @@ void xhci_debug_trb(struct xhci_hcd *xhci, union xhci_trb *trb) (unsigned int) (trb->link.control & TRB_NO_SNOOP)); break; case TRB_TYPE(TRB_TRANSFER): - address = trb->trans_event.buffer[0] + - (((u64) trb->trans_event.buffer[1]) << 32); + address = trb->trans_event.buffer; /* * FIXME: look at flags to figure out if it's an address or if * the data is directly in the buffer field. @@ -291,8 +280,7 @@ void xhci_debug_trb(struct xhci_hcd *xhci, union xhci_trb *trb) xhci_dbg(xhci, "DMA address or buffer contents= %llu\n", address); break; case TRB_TYPE(TRB_COMPLETION): - address = trb->event_cmd.cmd_trb[0] + - (((u64) trb->event_cmd.cmd_trb[1]) << 32); + address = trb->event_cmd.cmd_trb; xhci_dbg(xhci, "Command TRB pointer = %llu\n", address); xhci_dbg(xhci, "Completion status = %u\n", (unsigned int) GET_COMP_CODE(trb->event_cmd.status)); @@ -328,8 +316,8 @@ void xhci_debug_segment(struct xhci_hcd *xhci, struct xhci_segment *seg) for (i = 0; i < TRBS_PER_SEGMENT; ++i) { trb = &seg->trbs[i]; xhci_dbg(xhci, "@%08x %08x %08x %08x %08x\n", addr, - (unsigned int) trb->link.segment_ptr[0], - (unsigned int) trb->link.segment_ptr[1], + lower_32_bits(trb->link.segment_ptr), + upper_32_bits(trb->link.segment_ptr), (unsigned int) trb->link.intr_target, (unsigned int) trb->link.control); addr += sizeof(*trb); @@ -386,8 +374,8 @@ void xhci_dbg_erst(struct xhci_hcd *xhci, struct xhci_erst *erst) entry = &erst->entries[i]; xhci_dbg(xhci, "@%08x %08x %08x %08x %08x\n", (unsigned int) addr, - (unsigned int) entry->seg_addr[0], - (unsigned int) entry->seg_addr[1], + lower_32_bits(entry->seg_addr), + upper_32_bits(entry->seg_addr), (unsigned int) entry->seg_size, (unsigned int) entry->rsvd); addr += sizeof(*entry); @@ -396,90 +384,147 @@ void xhci_dbg_erst(struct xhci_hcd *xhci, struct xhci_erst *erst) void xhci_dbg_cmd_ptrs(struct xhci_hcd *xhci) { - u32 val; + u64 val; - val = xhci_readl(xhci, &xhci->op_regs->cmd_ring[0]); - xhci_dbg(xhci, "// xHC command ring deq ptr low bits + flags = 0x%x\n", val); - val = xhci_readl(xhci, &xhci->op_regs->cmd_ring[1]); - xhci_dbg(xhci, "// xHC command ring deq ptr high bits = 0x%x\n", val); + val = xhci_read_64(xhci, &xhci->op_regs->cmd_ring); + xhci_dbg(xhci, "// xHC command ring deq ptr low bits + flags = @%08x\n", + lower_32_bits(val)); + xhci_dbg(xhci, "// xHC command ring deq ptr high bits = @%08x\n", + upper_32_bits(val)); } -void xhci_dbg_ctx(struct xhci_hcd *xhci, struct xhci_device_control *ctx, dma_addr_t dma, unsigned int last_ep) +/* Print the last 32 bytes for 64-byte contexts */ +static void dbg_rsvd64(struct xhci_hcd *xhci, u64 *ctx, dma_addr_t dma) +{ + int i; + for (i = 0; i < 4; ++i) { + xhci_dbg(xhci, "@%p (virt) @%08llx " + "(dma) %#08llx - rsvd64[%d]\n", + &ctx[4 + i], (unsigned long long)dma, + ctx[4 + i], i); + dma += 8; + } +} + +void xhci_dbg_slot_ctx(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx) { - int i, j; - int last_ep_ctx = 31; /* Fields are 32 bits wide, DMA addresses are in bytes */ int field_size = 32 / 8; + int i; - xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - drop flags\n", - &ctx->drop_flags, (unsigned long long)dma, - ctx->drop_flags); - dma += field_size; - xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - add flags\n", - &ctx->add_flags, (unsigned long long)dma, - ctx->add_flags); - dma += field_size; - for (i = 0; i > 6; ++i) { - xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - rsvd[%d]\n", - &ctx->rsvd[i], (unsigned long long)dma, - ctx->rsvd[i], i); - dma += field_size; - } + struct xhci_slot_ctx *slot_ctx = xhci_get_slot_ctx(xhci, ctx); + dma_addr_t dma = ctx->dma + ((unsigned long)slot_ctx - (unsigned long)ctx); + int csz = HCC_64BYTE_CONTEXT(xhci->hcc_params); xhci_dbg(xhci, "Slot Context:\n"); xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - dev_info\n", - &ctx->slot.dev_info, - (unsigned long long)dma, ctx->slot.dev_info); + &slot_ctx->dev_info, + (unsigned long long)dma, slot_ctx->dev_info); dma += field_size; xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - dev_info2\n", - &ctx->slot.dev_info2, - (unsigned long long)dma, ctx->slot.dev_info2); + &slot_ctx->dev_info2, + (unsigned long long)dma, slot_ctx->dev_info2); dma += field_size; xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - tt_info\n", - &ctx->slot.tt_info, - (unsigned long long)dma, ctx->slot.tt_info); + &slot_ctx->tt_info, + (unsigned long long)dma, slot_ctx->tt_info); dma += field_size; xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - dev_state\n", - &ctx->slot.dev_state, - (unsigned long long)dma, ctx->slot.dev_state); + &slot_ctx->dev_state, + (unsigned long long)dma, slot_ctx->dev_state); dma += field_size; - for (i = 0; i > 4; ++i) { + for (i = 0; i < 4; ++i) { xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - rsvd[%d]\n", - &ctx->slot.reserved[i], (unsigned long long)dma, - ctx->slot.reserved[i], i); + &slot_ctx->reserved[i], (unsigned long long)dma, + slot_ctx->reserved[i], i); dma += field_size; } + if (csz) + dbg_rsvd64(xhci, (u64 *)slot_ctx, dma); +} + +void xhci_dbg_ep_ctx(struct xhci_hcd *xhci, + struct xhci_container_ctx *ctx, + unsigned int last_ep) +{ + int i, j; + int last_ep_ctx = 31; + /* Fields are 32 bits wide, DMA addresses are in bytes */ + int field_size = 32 / 8; + int csz = HCC_64BYTE_CONTEXT(xhci->hcc_params); + if (last_ep < 31) last_ep_ctx = last_ep + 1; for (i = 0; i < last_ep_ctx; ++i) { + struct xhci_ep_ctx *ep_ctx = xhci_get_ep_ctx(xhci, ctx, i); + dma_addr_t dma = ctx->dma + + ((unsigned long)ep_ctx - (unsigned long)ctx); + xhci_dbg(xhci, "Endpoint %02d Context:\n", i); xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - ep_info\n", - &ctx->ep[i].ep_info, - (unsigned long long)dma, ctx->ep[i].ep_info); + &ep_ctx->ep_info, + (unsigned long long)dma, ep_ctx->ep_info); dma += field_size; xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - ep_info2\n", - &ctx->ep[i].ep_info2, - (unsigned long long)dma, ctx->ep[i].ep_info2); - dma += field_size; - xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - deq[0]\n", - &ctx->ep[i].deq[0], - (unsigned long long)dma, ctx->ep[i].deq[0]); - dma += field_size; - xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - deq[1]\n", - &ctx->ep[i].deq[1], - (unsigned long long)dma, ctx->ep[i].deq[1]); + &ep_ctx->ep_info2, + (unsigned long long)dma, ep_ctx->ep_info2); dma += field_size; + xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08llx - deq\n", + &ep_ctx->deq, + (unsigned long long)dma, ep_ctx->deq); + dma += 2*field_size; xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - tx_info\n", - &ctx->ep[i].tx_info, - (unsigned long long)dma, ctx->ep[i].tx_info); + &ep_ctx->tx_info, + (unsigned long long)dma, ep_ctx->tx_info); dma += field_size; for (j = 0; j < 3; ++j) { xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - rsvd[%d]\n", - &ctx->ep[i].reserved[j], + &ep_ctx->reserved[j], (unsigned long long)dma, - ctx->ep[i].reserved[j], j); + ep_ctx->reserved[j], j); + dma += field_size; + } + + if (csz) + dbg_rsvd64(xhci, (u64 *)ep_ctx, dma); + } +} + +void xhci_dbg_ctx(struct xhci_hcd *xhci, + struct xhci_container_ctx *ctx, + unsigned int last_ep) +{ + int i; + /* Fields are 32 bits wide, DMA addresses are in bytes */ + int field_size = 32 / 8; + struct xhci_slot_ctx *slot_ctx; + dma_addr_t dma = ctx->dma; + int csz = HCC_64BYTE_CONTEXT(xhci->hcc_params); + + if (ctx->type == XHCI_CTX_TYPE_INPUT) { + struct xhci_input_control_ctx *ctrl_ctx = + xhci_get_input_control_ctx(xhci, ctx); + xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - drop flags\n", + &ctrl_ctx->drop_flags, (unsigned long long)dma, + ctrl_ctx->drop_flags); + dma += field_size; + xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - add flags\n", + &ctrl_ctx->add_flags, (unsigned long long)dma, + ctrl_ctx->add_flags); + dma += field_size; + for (i = 0; i < 6; ++i) { + xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - rsvd2[%d]\n", + &ctrl_ctx->rsvd2[i], (unsigned long long)dma, + ctrl_ctx->rsvd2[i], i); dma += field_size; } + + if (csz) + dbg_rsvd64(xhci, (u64 *)ctrl_ctx, dma); } + + slot_ctx = xhci_get_slot_ctx(xhci, ctx); + xhci_dbg_slot_ctx(xhci, ctx); + xhci_dbg_ep_ctx(xhci, ctx, last_ep); } diff --git a/drivers/usb/host/xhci-hcd.c b/drivers/usb/host/xhci-hcd.c index dba3e07ccd0..816c39caca1 100644 --- a/drivers/usb/host/xhci-hcd.c +++ b/drivers/usb/host/xhci-hcd.c @@ -103,7 +103,10 @@ int xhci_reset(struct xhci_hcd *xhci) u32 state; state = xhci_readl(xhci, &xhci->op_regs->status); - BUG_ON((state & STS_HALT) == 0); + if ((state & STS_HALT) == 0) { + xhci_warn(xhci, "Host controller not halted, aborting reset.\n"); + return 0; + } xhci_dbg(xhci, "// Reset the HC\n"); command = xhci_readl(xhci, &xhci->op_regs->command); @@ -226,6 +229,7 @@ int xhci_init(struct usb_hcd *hcd) static void xhci_work(struct xhci_hcd *xhci) { u32 temp; + u64 temp_64; /* * Clear the op reg interrupt status first, @@ -248,9 +252,9 @@ static void xhci_work(struct xhci_hcd *xhci) /* FIXME this should be a delayed service routine that clears the EHB */ xhci_handle_event(xhci); - /* Clear the event handler busy flag; the event ring should be empty. */ - temp = xhci_readl(xhci, &xhci->ir_set->erst_dequeue[0]); - xhci_writel(xhci, temp & ~ERST_EHB, &xhci->ir_set->erst_dequeue[0]); + /* Clear the event handler busy flag (RW1C); the event ring should be empty. */ + temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); + xhci_write_64(xhci, temp_64 | ERST_EHB, &xhci->ir_set->erst_dequeue); /* Flush posted writes -- FIXME is this necessary? */ xhci_readl(xhci, &xhci->ir_set->irq_pending); } @@ -266,19 +270,34 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); u32 temp, temp2; + union xhci_trb *trb; spin_lock(&xhci->lock); + trb = xhci->event_ring->dequeue; /* Check if the xHC generated the interrupt, or the irq is shared */ temp = xhci_readl(xhci, &xhci->op_regs->status); temp2 = xhci_readl(xhci, &xhci->ir_set->irq_pending); + if (temp == 0xffffffff && temp2 == 0xffffffff) + goto hw_died; + if (!(temp & STS_EINT) && !ER_IRQ_PENDING(temp2)) { spin_unlock(&xhci->lock); return IRQ_NONE; } + xhci_dbg(xhci, "op reg status = %08x\n", temp); + xhci_dbg(xhci, "ir set irq_pending = %08x\n", temp2); + xhci_dbg(xhci, "Event ring dequeue ptr:\n"); + xhci_dbg(xhci, "@%llx %08x %08x %08x %08x\n", + (unsigned long long)xhci_trb_virt_to_dma(xhci->event_ring->deq_seg, trb), + lower_32_bits(trb->link.segment_ptr), + upper_32_bits(trb->link.segment_ptr), + (unsigned int) trb->link.intr_target, + (unsigned int) trb->link.control); if (temp & STS_FATAL) { xhci_warn(xhci, "WARNING: Host System Error\n"); xhci_halt(xhci); +hw_died: xhci_to_hcd(xhci)->state = HC_STATE_HALT; spin_unlock(&xhci->lock); return -ESHUTDOWN; @@ -295,6 +314,7 @@ void xhci_event_ring_work(unsigned long arg) { unsigned long flags; int temp; + u64 temp_64; struct xhci_hcd *xhci = (struct xhci_hcd *) arg; int i, j; @@ -311,9 +331,9 @@ void xhci_event_ring_work(unsigned long arg) xhci_dbg(xhci, "Event ring:\n"); xhci_debug_segment(xhci, xhci->event_ring->deq_seg); xhci_dbg_ring_ptrs(xhci, xhci->event_ring); - temp = xhci_readl(xhci, &xhci->ir_set->erst_dequeue[0]); - temp &= ERST_PTR_MASK; - xhci_dbg(xhci, "ERST deq = 0x%x\n", temp); + temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); + temp_64 &= ~ERST_PTR_MASK; + xhci_dbg(xhci, "ERST deq = 64'h%0lx\n", (long unsigned int) temp_64); xhci_dbg(xhci, "Command ring:\n"); xhci_debug_segment(xhci, xhci->cmd_ring->deq_seg); xhci_dbg_ring_ptrs(xhci, xhci->cmd_ring); @@ -356,6 +376,7 @@ void xhci_event_ring_work(unsigned long arg) int xhci_run(struct usb_hcd *hcd) { u32 temp; + u64 temp_64; struct xhci_hcd *xhci = hcd_to_xhci(hcd); void (*doorbell)(struct xhci_hcd *) = NULL; @@ -382,6 +403,20 @@ int xhci_run(struct usb_hcd *hcd) add_timer(&xhci->event_ring_timer); #endif + xhci_dbg(xhci, "Command ring memory map follows:\n"); + xhci_debug_ring(xhci, xhci->cmd_ring); + xhci_dbg_ring_ptrs(xhci, xhci->cmd_ring); + xhci_dbg_cmd_ptrs(xhci); + + xhci_dbg(xhci, "ERST memory map follows:\n"); + xhci_dbg_erst(xhci, &xhci->erst); + xhci_dbg(xhci, "Event ring:\n"); + xhci_debug_ring(xhci, xhci->event_ring); + xhci_dbg_ring_ptrs(xhci, xhci->event_ring); + temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); + temp_64 &= ~ERST_PTR_MASK; + xhci_dbg(xhci, "ERST deq = 64'h%0lx\n", (long unsigned int) temp_64); + xhci_dbg(xhci, "// Set the interrupt modulation register\n"); temp = xhci_readl(xhci, &xhci->ir_set->irq_control); temp &= ~ER_IRQ_INTERVAL_MASK; @@ -406,22 +441,6 @@ int xhci_run(struct usb_hcd *hcd) if (NUM_TEST_NOOPS > 0) doorbell = xhci_setup_one_noop(xhci); - xhci_dbg(xhci, "Command ring memory map follows:\n"); - xhci_debug_ring(xhci, xhci->cmd_ring); - xhci_dbg_ring_ptrs(xhci, xhci->cmd_ring); - xhci_dbg_cmd_ptrs(xhci); - - xhci_dbg(xhci, "ERST memory map follows:\n"); - xhci_dbg_erst(xhci, &xhci->erst); - xhci_dbg(xhci, "Event ring:\n"); - xhci_debug_ring(xhci, xhci->event_ring); - xhci_dbg_ring_ptrs(xhci, xhci->event_ring); - temp = xhci_readl(xhci, &xhci->ir_set->erst_dequeue[0]); - temp &= ERST_PTR_MASK; - xhci_dbg(xhci, "ERST deq = 0x%x\n", temp); - temp = xhci_readl(xhci, &xhci->ir_set->erst_dequeue[1]); - xhci_dbg(xhci, "ERST deq upper = 0x%x\n", temp); - temp = xhci_readl(xhci, &xhci->op_regs->command); temp |= (CMD_RUN); xhci_dbg(xhci, "// Turn on HC, cmd = 0x%x.\n", @@ -601,10 +620,13 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) goto exit; } if (usb_endpoint_xfer_control(&urb->ep->desc)) - ret = xhci_queue_ctrl_tx(xhci, mem_flags, urb, + /* We have a spinlock and interrupts disabled, so we must pass + * atomic context to this function, which may allocate memory. + */ + ret = xhci_queue_ctrl_tx(xhci, GFP_ATOMIC, urb, slot_id, ep_index); else if (usb_endpoint_xfer_bulk(&urb->ep->desc)) - ret = xhci_queue_bulk_tx(xhci, mem_flags, urb, + ret = xhci_queue_bulk_tx(xhci, GFP_ATOMIC, urb, slot_id, ep_index); else ret = -EINVAL; @@ -661,8 +683,12 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) goto done; xhci_dbg(xhci, "Cancel URB %p\n", urb); + xhci_dbg(xhci, "Event ring:\n"); + xhci_debug_ring(xhci, xhci->event_ring); ep_index = xhci_get_endpoint_index(&urb->ep->desc); ep_ring = xhci->devs[urb->dev->slot_id]->ep_rings[ep_index]; + xhci_dbg(xhci, "Endpoint ring:\n"); + xhci_debug_ring(xhci, ep_ring); td = (struct xhci_td *) urb->hcpriv; ep_ring->cancels_pending++; @@ -696,7 +722,9 @@ int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev, struct usb_host_endpoint *ep) { struct xhci_hcd *xhci; - struct xhci_device_control *in_ctx; + struct xhci_container_ctx *in_ctx, *out_ctx; + struct xhci_input_control_ctx *ctrl_ctx; + struct xhci_slot_ctx *slot_ctx; unsigned int last_ctx; unsigned int ep_index; struct xhci_ep_ctx *ep_ctx; @@ -724,31 +752,34 @@ int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev, } in_ctx = xhci->devs[udev->slot_id]->in_ctx; + out_ctx = xhci->devs[udev->slot_id]->out_ctx; + ctrl_ctx = xhci_get_input_control_ctx(xhci, in_ctx); ep_index = xhci_get_endpoint_index(&ep->desc); - ep_ctx = &xhci->devs[udev->slot_id]->out_ctx->ep[ep_index]; + ep_ctx = xhci_get_ep_ctx(xhci, out_ctx, ep_index); /* If the HC already knows the endpoint is disabled, * or the HCD has noted it is disabled, ignore this request */ if ((ep_ctx->ep_info & EP_STATE_MASK) == EP_STATE_DISABLED || - in_ctx->drop_flags & xhci_get_endpoint_flag(&ep->desc)) { + ctrl_ctx->drop_flags & xhci_get_endpoint_flag(&ep->desc)) { xhci_warn(xhci, "xHCI %s called with disabled ep %p\n", __func__, ep); return 0; } - in_ctx->drop_flags |= drop_flag; - new_drop_flags = in_ctx->drop_flags; + ctrl_ctx->drop_flags |= drop_flag; + new_drop_flags = ctrl_ctx->drop_flags; - in_ctx->add_flags = ~drop_flag; - new_add_flags = in_ctx->add_flags; + ctrl_ctx->add_flags = ~drop_flag; + new_add_flags = ctrl_ctx->add_flags; - last_ctx = xhci_last_valid_endpoint(in_ctx->add_flags); + last_ctx = xhci_last_valid_endpoint(ctrl_ctx->add_flags); + slot_ctx = xhci_get_slot_ctx(xhci, in_ctx); /* Update the last valid endpoint context, if we deleted the last one */ - if ((in_ctx->slot.dev_info & LAST_CTX_MASK) > LAST_CTX(last_ctx)) { - in_ctx->slot.dev_info &= ~LAST_CTX_MASK; - in_ctx->slot.dev_info |= LAST_CTX(last_ctx); + if ((slot_ctx->dev_info & LAST_CTX_MASK) > LAST_CTX(last_ctx)) { + slot_ctx->dev_info &= ~LAST_CTX_MASK; + slot_ctx->dev_info |= LAST_CTX(last_ctx); } - new_slot_info = in_ctx->slot.dev_info; + new_slot_info = slot_ctx->dev_info; xhci_endpoint_zero(xhci, xhci->devs[udev->slot_id], ep); @@ -778,17 +809,22 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev, struct usb_host_endpoint *ep) { struct xhci_hcd *xhci; - struct xhci_device_control *in_ctx; + struct xhci_container_ctx *in_ctx, *out_ctx; unsigned int ep_index; struct xhci_ep_ctx *ep_ctx; + struct xhci_slot_ctx *slot_ctx; + struct xhci_input_control_ctx *ctrl_ctx; u32 added_ctxs; unsigned int last_ctx; u32 new_add_flags, new_drop_flags, new_slot_info; int ret = 0; ret = xhci_check_args(hcd, udev, ep, 1, __func__); - if (ret <= 0) + if (ret <= 0) { + /* So we won't queue a reset ep command for a root hub */ + ep->hcpriv = NULL; return ret; + } xhci = hcd_to_xhci(hcd); added_ctxs = xhci_get_endpoint_flag(&ep->desc); @@ -810,12 +846,14 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev, } in_ctx = xhci->devs[udev->slot_id]->in_ctx; + out_ctx = xhci->devs[udev->slot_id]->out_ctx; + ctrl_ctx = xhci_get_input_control_ctx(xhci, in_ctx); ep_index = xhci_get_endpoint_index(&ep->desc); - ep_ctx = &xhci->devs[udev->slot_id]->out_ctx->ep[ep_index]; + ep_ctx = xhci_get_ep_ctx(xhci, out_ctx, ep_index); /* If the HCD has already noted the endpoint is enabled, * ignore this request. */ - if (in_ctx->add_flags & xhci_get_endpoint_flag(&ep->desc)) { + if (ctrl_ctx->add_flags & xhci_get_endpoint_flag(&ep->desc)) { xhci_warn(xhci, "xHCI %s called with enabled ep %p\n", __func__, ep); return 0; @@ -833,8 +871,8 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev, return -ENOMEM; } - in_ctx->add_flags |= added_ctxs; - new_add_flags = in_ctx->add_flags; + ctrl_ctx->add_flags |= added_ctxs; + new_add_flags = ctrl_ctx->add_flags; /* If xhci_endpoint_disable() was called for this endpoint, but the * xHC hasn't been notified yet through the check_bandwidth() call, @@ -842,14 +880,18 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev, * descriptors. We must drop and re-add this endpoint, so we leave the * drop flags alone. */ - new_drop_flags = in_ctx->drop_flags; + new_drop_flags = ctrl_ctx->drop_flags; + slot_ctx = xhci_get_slot_ctx(xhci, in_ctx); /* Update the last valid endpoint context, if we just added one past */ - if ((in_ctx->slot.dev_info & LAST_CTX_MASK) < LAST_CTX(last_ctx)) { - in_ctx->slot.dev_info &= ~LAST_CTX_MASK; - in_ctx->slot.dev_info |= LAST_CTX(last_ctx); + if ((slot_ctx->dev_info & LAST_CTX_MASK) < LAST_CTX(last_ctx)) { + slot_ctx->dev_info &= ~LAST_CTX_MASK; + slot_ctx->dev_info |= LAST_CTX(last_ctx); } - new_slot_info = in_ctx->slot.dev_info; + new_slot_info = slot_ctx->dev_info; + + /* Store the usb_device pointer for later use */ + ep->hcpriv = udev; xhci_dbg(xhci, "add ep 0x%x, slot id %d, new drop flags = %#x, new add flags = %#x, new slot info = %#x\n", (unsigned int) ep->desc.bEndpointAddress, @@ -860,9 +902,11 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev, return 0; } -static void xhci_zero_in_ctx(struct xhci_virt_device *virt_dev) +static void xhci_zero_in_ctx(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev) { + struct xhci_input_control_ctx *ctrl_ctx; struct xhci_ep_ctx *ep_ctx; + struct xhci_slot_ctx *slot_ctx; int i; /* When a device's add flag and drop flag are zero, any subsequent @@ -870,17 +914,18 @@ static void xhci_zero_in_ctx(struct xhci_virt_device *virt_dev) * untouched. Make sure we don't leave any old state in the input * endpoint contexts. */ - virt_dev->in_ctx->drop_flags = 0; - virt_dev->in_ctx->add_flags = 0; - virt_dev->in_ctx->slot.dev_info &= ~LAST_CTX_MASK; + ctrl_ctx = xhci_get_input_control_ctx(xhci, virt_dev->in_ctx); + ctrl_ctx->drop_flags = 0; + ctrl_ctx->add_flags = 0; + slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->in_ctx); + slot_ctx->dev_info &= ~LAST_CTX_MASK; /* Endpoint 0 is always valid */ - virt_dev->in_ctx->slot.dev_info |= LAST_CTX(1); + slot_ctx->dev_info |= LAST_CTX(1); for (i = 1; i < 31; ++i) { - ep_ctx = &virt_dev->in_ctx->ep[i]; + ep_ctx = xhci_get_ep_ctx(xhci, virt_dev->in_ctx, i); ep_ctx->ep_info = 0; ep_ctx->ep_info2 = 0; - ep_ctx->deq[0] = 0; - ep_ctx->deq[1] = 0; + ep_ctx->deq = 0; ep_ctx->tx_info = 0; } } @@ -903,6 +948,8 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) unsigned long flags; struct xhci_hcd *xhci; struct xhci_virt_device *virt_dev; + struct xhci_input_control_ctx *ctrl_ctx; + struct xhci_slot_ctx *slot_ctx; ret = xhci_check_args(hcd, udev, NULL, 0, __func__); if (ret <= 0) @@ -918,16 +965,18 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) virt_dev = xhci->devs[udev->slot_id]; /* See section 4.6.6 - A0 = 1; A1 = D0 = D1 = 0 */ - virt_dev->in_ctx->add_flags |= SLOT_FLAG; - virt_dev->in_ctx->add_flags &= ~EP0_FLAG; - virt_dev->in_ctx->drop_flags &= ~SLOT_FLAG; - virt_dev->in_ctx->drop_flags &= ~EP0_FLAG; + ctrl_ctx = xhci_get_input_control_ctx(xhci, virt_dev->in_ctx); + ctrl_ctx->add_flags |= SLOT_FLAG; + ctrl_ctx->add_flags &= ~EP0_FLAG; + ctrl_ctx->drop_flags &= ~SLOT_FLAG; + ctrl_ctx->drop_flags &= ~EP0_FLAG; xhci_dbg(xhci, "New Input Control Context:\n"); - xhci_dbg_ctx(xhci, virt_dev->in_ctx, virt_dev->in_ctx_dma, - LAST_CTX_TO_EP_NUM(virt_dev->in_ctx->slot.dev_info)); + slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->in_ctx); + xhci_dbg_ctx(xhci, virt_dev->in_ctx, + LAST_CTX_TO_EP_NUM(slot_ctx->dev_info)); spin_lock_irqsave(&xhci->lock, flags); - ret = xhci_queue_configure_endpoint(xhci, virt_dev->in_ctx_dma, + ret = xhci_queue_configure_endpoint(xhci, virt_dev->in_ctx->dma, udev->slot_id); if (ret < 0) { spin_unlock_irqrestore(&xhci->lock, flags); @@ -982,10 +1031,10 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) } xhci_dbg(xhci, "Output context after successful config ep cmd:\n"); - xhci_dbg_ctx(xhci, virt_dev->out_ctx, virt_dev->out_ctx_dma, - LAST_CTX_TO_EP_NUM(virt_dev->in_ctx->slot.dev_info)); + xhci_dbg_ctx(xhci, virt_dev->out_ctx, + LAST_CTX_TO_EP_NUM(slot_ctx->dev_info)); - xhci_zero_in_ctx(virt_dev); + xhci_zero_in_ctx(xhci, virt_dev); /* Free any old rings */ for (i = 1; i < 31; ++i) { if (virt_dev->new_ep_rings[i]) { @@ -1023,7 +1072,67 @@ void xhci_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) virt_dev->new_ep_rings[i] = NULL; } } - xhci_zero_in_ctx(virt_dev); + xhci_zero_in_ctx(xhci, virt_dev); +} + +/* Deal with stalled endpoints. The core should have sent the control message + * to clear the halt condition. However, we need to make the xHCI hardware + * reset its sequence number, since a device will expect a sequence number of + * zero after the halt condition is cleared. + * Context: in_interrupt + */ +void xhci_endpoint_reset(struct usb_hcd *hcd, + struct usb_host_endpoint *ep) +{ + struct xhci_hcd *xhci; + struct usb_device *udev; + unsigned int ep_index; + unsigned long flags; + int ret; + struct xhci_dequeue_state deq_state; + struct xhci_ring *ep_ring; + + xhci = hcd_to_xhci(hcd); + udev = (struct usb_device *) ep->hcpriv; + /* Called with a root hub endpoint (or an endpoint that wasn't added + * with xhci_add_endpoint() + */ + if (!ep->hcpriv) + return; + ep_index = xhci_get_endpoint_index(&ep->desc); + ep_ring = xhci->devs[udev->slot_id]->ep_rings[ep_index]; + if (!ep_ring->stopped_td) { + xhci_dbg(xhci, "Endpoint 0x%x not halted, refusing to reset.\n", + ep->desc.bEndpointAddress); + return; + } + + xhci_dbg(xhci, "Queueing reset endpoint command\n"); + spin_lock_irqsave(&xhci->lock, flags); + ret = xhci_queue_reset_ep(xhci, udev->slot_id, ep_index); + /* + * Can't change the ring dequeue pointer until it's transitioned to the + * stopped state, which is only upon a successful reset endpoint + * command. Better hope that last command worked! + */ + if (!ret) { + xhci_dbg(xhci, "Cleaning up stalled endpoint ring\n"); + /* We need to move the HW's dequeue pointer past this TD, + * or it will attempt to resend it on the next doorbell ring. + */ + xhci_find_new_dequeue_state(xhci, udev->slot_id, + ep_index, ep_ring->stopped_td, &deq_state); + xhci_dbg(xhci, "Queueing new dequeue state\n"); + xhci_queue_new_dequeue_state(xhci, ep_ring, + udev->slot_id, + ep_index, &deq_state); + kfree(ep_ring->stopped_td); + xhci_ring_cmd_db(xhci); + } + spin_unlock_irqrestore(&xhci->lock, flags); + + if (ret) + xhci_warn(xhci, "FIXME allocate a new ring segment\n"); } /* @@ -1120,7 +1229,9 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) struct xhci_virt_device *virt_dev; int ret = 0; struct xhci_hcd *xhci = hcd_to_xhci(hcd); - u32 temp; + struct xhci_slot_ctx *slot_ctx; + struct xhci_input_control_ctx *ctrl_ctx; + u64 temp_64; if (!udev->slot_id) { xhci_dbg(xhci, "Bad Slot ID %d\n", udev->slot_id); @@ -1133,10 +1244,12 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) if (!udev->config) xhci_setup_addressable_virt_dev(xhci, udev); /* Otherwise, assume the core has the device configured how it wants */ + xhci_dbg(xhci, "Slot ID %d Input Context:\n", udev->slot_id); + xhci_dbg_ctx(xhci, virt_dev->in_ctx, 2); spin_lock_irqsave(&xhci->lock, flags); - ret = xhci_queue_address_device(xhci, virt_dev->in_ctx_dma, - udev->slot_id); + ret = xhci_queue_address_device(xhci, virt_dev->in_ctx->dma, + udev->slot_id); if (ret) { spin_unlock_irqrestore(&xhci->lock, flags); xhci_dbg(xhci, "FIXME: allocate a command ring segment\n"); @@ -1176,41 +1289,37 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) default: xhci_err(xhci, "ERROR: unexpected command completion " "code 0x%x.\n", virt_dev->cmd_status); + xhci_dbg(xhci, "Slot ID %d Output Context:\n", udev->slot_id); + xhci_dbg_ctx(xhci, virt_dev->out_ctx, 2); ret = -EINVAL; break; } if (ret) { return ret; } - temp = xhci_readl(xhci, &xhci->op_regs->dcbaa_ptr[0]); - xhci_dbg(xhci, "Op regs DCBAA ptr[0] = %#08x\n", temp); - temp = xhci_readl(xhci, &xhci->op_regs->dcbaa_ptr[1]); - xhci_dbg(xhci, "Op regs DCBAA ptr[1] = %#08x\n", temp); - xhci_dbg(xhci, "Slot ID %d dcbaa entry[0] @%p = %#08x\n", - udev->slot_id, - &xhci->dcbaa->dev_context_ptrs[2*udev->slot_id], - xhci->dcbaa->dev_context_ptrs[2*udev->slot_id]); - xhci_dbg(xhci, "Slot ID %d dcbaa entry[1] @%p = %#08x\n", + temp_64 = xhci_read_64(xhci, &xhci->op_regs->dcbaa_ptr); + xhci_dbg(xhci, "Op regs DCBAA ptr = %#016llx\n", temp_64); + xhci_dbg(xhci, "Slot ID %d dcbaa entry @%p = %#016llx\n", udev->slot_id, - &xhci->dcbaa->dev_context_ptrs[2*udev->slot_id+1], - xhci->dcbaa->dev_context_ptrs[2*udev->slot_id+1]); + &xhci->dcbaa->dev_context_ptrs[udev->slot_id], + (unsigned long long) + xhci->dcbaa->dev_context_ptrs[udev->slot_id]); xhci_dbg(xhci, "Output Context DMA address = %#08llx\n", - (unsigned long long)virt_dev->out_ctx_dma); + (unsigned long long)virt_dev->out_ctx->dma); xhci_dbg(xhci, "Slot ID %d Input Context:\n", udev->slot_id); - xhci_dbg_ctx(xhci, virt_dev->in_ctx, virt_dev->in_ctx_dma, 2); + xhci_dbg_ctx(xhci, virt_dev->in_ctx, 2); xhci_dbg(xhci, "Slot ID %d Output Context:\n", udev->slot_id); - xhci_dbg_ctx(xhci, virt_dev->out_ctx, virt_dev->out_ctx_dma, 2); + xhci_dbg_ctx(xhci, virt_dev->out_ctx, 2); /* * USB core uses address 1 for the roothubs, so we add one to the * address given back to us by the HC. */ - udev->devnum = (virt_dev->out_ctx->slot.dev_state & DEV_ADDR_MASK) + 1; + slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->out_ctx); + udev->devnum = (slot_ctx->dev_state & DEV_ADDR_MASK) + 1; /* Zero the input context control for later use */ - virt_dev->in_ctx->add_flags = 0; - virt_dev->in_ctx->drop_flags = 0; - /* Mirror flags in the output context for future ep enable/disable */ - virt_dev->out_ctx->add_flags = SLOT_FLAG | EP0_FLAG; - virt_dev->out_ctx->drop_flags = 0; + ctrl_ctx = xhci_get_input_control_ctx(xhci, virt_dev->in_ctx); + ctrl_ctx->add_flags = 0; + ctrl_ctx->drop_flags = 0; xhci_dbg(xhci, "Device address = %d\n", udev->devnum); /* XXX Meh, not sure if anyone else but choose_address uses this. */ @@ -1252,7 +1361,6 @@ static int __init xhci_hcd_init(void) /* xhci_device_control has eight fields, and also * embeds one xhci_slot_ctx and 31 xhci_ep_ctx */ - BUILD_BUG_ON(sizeof(struct xhci_device_control) != (8+8+8*31)*32/8); BUILD_BUG_ON(sizeof(struct xhci_stream_ctx) != 4*32/8); BUILD_BUG_ON(sizeof(union xhci_trb) != 4*32/8); BUILD_BUG_ON(sizeof(struct xhci_erst_entry) != 4*32/8); diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index c8a72de1c50..e6b9a1c6002 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -88,7 +88,7 @@ static void xhci_link_segments(struct xhci_hcd *xhci, struct xhci_segment *prev, return; prev->next = next; if (link_trbs) { - prev->trbs[TRBS_PER_SEGMENT-1].link.segment_ptr[0] = next->dma; + prev->trbs[TRBS_PER_SEGMENT-1].link.segment_ptr = next->dma; /* Set the last TRB in the segment to have a TRB type ID of Link TRB */ val = prev->trbs[TRBS_PER_SEGMENT-1].link.control; @@ -189,6 +189,63 @@ fail: return 0; } +#define CTX_SIZE(_hcc) (HCC_64BYTE_CONTEXT(_hcc) ? 64 : 32) + +struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci, + int type, gfp_t flags) +{ + struct xhci_container_ctx *ctx = kzalloc(sizeof(*ctx), flags); + if (!ctx) + return NULL; + + BUG_ON((type != XHCI_CTX_TYPE_DEVICE) && (type != XHCI_CTX_TYPE_INPUT)); + ctx->type = type; + ctx->size = HCC_64BYTE_CONTEXT(xhci->hcc_params) ? 2048 : 1024; + if (type == XHCI_CTX_TYPE_INPUT) + ctx->size += CTX_SIZE(xhci->hcc_params); + + ctx->bytes = dma_pool_alloc(xhci->device_pool, flags, &ctx->dma); + memset(ctx->bytes, 0, ctx->size); + return ctx; +} + +void xhci_free_container_ctx(struct xhci_hcd *xhci, + struct xhci_container_ctx *ctx) +{ + dma_pool_free(xhci->device_pool, ctx->bytes, ctx->dma); + kfree(ctx); +} + +struct xhci_input_control_ctx *xhci_get_input_control_ctx(struct xhci_hcd *xhci, + struct xhci_container_ctx *ctx) +{ + BUG_ON(ctx->type != XHCI_CTX_TYPE_INPUT); + return (struct xhci_input_control_ctx *)ctx->bytes; +} + +struct xhci_slot_ctx *xhci_get_slot_ctx(struct xhci_hcd *xhci, + struct xhci_container_ctx *ctx) +{ + if (ctx->type == XHCI_CTX_TYPE_DEVICE) + return (struct xhci_slot_ctx *)ctx->bytes; + + return (struct xhci_slot_ctx *) + (ctx->bytes + CTX_SIZE(xhci->hcc_params)); +} + +struct xhci_ep_ctx *xhci_get_ep_ctx(struct xhci_hcd *xhci, + struct xhci_container_ctx *ctx, + unsigned int ep_index) +{ + /* increment ep index by offset of start of ep ctx array */ + ep_index++; + if (ctx->type == XHCI_CTX_TYPE_INPUT) + ep_index++; + + return (struct xhci_ep_ctx *) + (ctx->bytes + (ep_index * CTX_SIZE(xhci->hcc_params))); +} + /* All the xhci_tds in the ring's TD list should be freed at this point */ void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id) { @@ -200,8 +257,7 @@ void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id) return; dev = xhci->devs[slot_id]; - xhci->dcbaa->dev_context_ptrs[2*slot_id] = 0; - xhci->dcbaa->dev_context_ptrs[2*slot_id + 1] = 0; + xhci->dcbaa->dev_context_ptrs[slot_id] = 0; if (!dev) return; @@ -210,11 +266,10 @@ void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id) xhci_ring_free(xhci, dev->ep_rings[i]); if (dev->in_ctx) - dma_pool_free(xhci->device_pool, - dev->in_ctx, dev->in_ctx_dma); + xhci_free_container_ctx(xhci, dev->in_ctx); if (dev->out_ctx) - dma_pool_free(xhci->device_pool, - dev->out_ctx, dev->out_ctx_dma); + xhci_free_container_ctx(xhci, dev->out_ctx); + kfree(xhci->devs[slot_id]); xhci->devs[slot_id] = 0; } @@ -222,7 +277,6 @@ void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id) int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, struct usb_device *udev, gfp_t flags) { - dma_addr_t dma; struct xhci_virt_device *dev; /* Slot ID 0 is reserved */ @@ -236,23 +290,21 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, return 0; dev = xhci->devs[slot_id]; - /* Allocate the (output) device context that will be used in the HC */ - dev->out_ctx = dma_pool_alloc(xhci->device_pool, flags, &dma); + /* Allocate the (output) device context that will be used in the HC. */ + dev->out_ctx = xhci_alloc_container_ctx(xhci, XHCI_CTX_TYPE_DEVICE, flags); if (!dev->out_ctx) goto fail; - dev->out_ctx_dma = dma; + xhci_dbg(xhci, "Slot %d output ctx = 0x%llx (dma)\n", slot_id, - (unsigned long long)dma); - memset(dev->out_ctx, 0, sizeof(*dev->out_ctx)); + (unsigned long long)dev->out_ctx->dma); /* Allocate the (input) device context for address device command */ - dev->in_ctx = dma_pool_alloc(xhci->device_pool, flags, &dma); + dev->in_ctx = xhci_alloc_container_ctx(xhci, XHCI_CTX_TYPE_INPUT, flags); if (!dev->in_ctx) goto fail; - dev->in_ctx_dma = dma; + xhci_dbg(xhci, "Slot %d input ctx = 0x%llx (dma)\n", slot_id, - (unsigned long long)dma); - memset(dev->in_ctx, 0, sizeof(*dev->in_ctx)); + (unsigned long long)dev->in_ctx->dma); /* Allocate endpoint 0 ring */ dev->ep_rings[0] = xhci_ring_alloc(xhci, 1, true, flags); @@ -261,17 +313,12 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, init_completion(&dev->cmd_completion); - /* - * Point to output device context in dcbaa; skip the output control - * context, which is eight 32 bit fields (or 32 bytes long) - */ - xhci->dcbaa->dev_context_ptrs[2*slot_id] = - (u32) dev->out_ctx_dma + (32); + /* Point to output device context in dcbaa. */ + xhci->dcbaa->dev_context_ptrs[slot_id] = dev->out_ctx->dma; xhci_dbg(xhci, "Set slot id %d dcbaa entry %p to 0x%llx\n", slot_id, - &xhci->dcbaa->dev_context_ptrs[2*slot_id], - (unsigned long long)dev->out_ctx_dma); - xhci->dcbaa->dev_context_ptrs[2*slot_id + 1] = 0; + &xhci->dcbaa->dev_context_ptrs[slot_id], + (unsigned long long) xhci->dcbaa->dev_context_ptrs[slot_id]); return 1; fail: @@ -285,6 +332,8 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud struct xhci_virt_device *dev; struct xhci_ep_ctx *ep0_ctx; struct usb_device *top_dev; + struct xhci_slot_ctx *slot_ctx; + struct xhci_input_control_ctx *ctrl_ctx; dev = xhci->devs[udev->slot_id]; /* Slot ID 0 is reserved */ @@ -293,27 +342,29 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud udev->slot_id); return -EINVAL; } - ep0_ctx = &dev->in_ctx->ep[0]; + ep0_ctx = xhci_get_ep_ctx(xhci, dev->in_ctx, 0); + ctrl_ctx = xhci_get_input_control_ctx(xhci, dev->in_ctx); + slot_ctx = xhci_get_slot_ctx(xhci, dev->in_ctx); /* 2) New slot context and endpoint 0 context are valid*/ - dev->in_ctx->add_flags = SLOT_FLAG | EP0_FLAG; + ctrl_ctx->add_flags = SLOT_FLAG | EP0_FLAG; /* 3) Only the control endpoint is valid - one endpoint context */ - dev->in_ctx->slot.dev_info |= LAST_CTX(1); + slot_ctx->dev_info |= LAST_CTX(1); switch (udev->speed) { case USB_SPEED_SUPER: - dev->in_ctx->slot.dev_info |= (u32) udev->route; - dev->in_ctx->slot.dev_info |= (u32) SLOT_SPEED_SS; + slot_ctx->dev_info |= (u32) udev->route; + slot_ctx->dev_info |= (u32) SLOT_SPEED_SS; break; case USB_SPEED_HIGH: - dev->in_ctx->slot.dev_info |= (u32) SLOT_SPEED_HS; + slot_ctx->dev_info |= (u32) SLOT_SPEED_HS; break; case USB_SPEED_FULL: - dev->in_ctx->slot.dev_info |= (u32) SLOT_SPEED_FS; + slot_ctx->dev_info |= (u32) SLOT_SPEED_FS; break; case USB_SPEED_LOW: - dev->in_ctx->slot.dev_info |= (u32) SLOT_SPEED_LS; + slot_ctx->dev_info |= (u32) SLOT_SPEED_LS; break; case USB_SPEED_VARIABLE: xhci_dbg(xhci, "FIXME xHCI doesn't support wireless speeds\n"); @@ -327,7 +378,7 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud for (top_dev = udev; top_dev->parent && top_dev->parent->parent; top_dev = top_dev->parent) /* Found device below root hub */; - dev->in_ctx->slot.dev_info2 |= (u32) ROOT_HUB_PORT(top_dev->portnum); + slot_ctx->dev_info2 |= (u32) ROOT_HUB_PORT(top_dev->portnum); xhci_dbg(xhci, "Set root hub portnum to %d\n", top_dev->portnum); /* Is this a LS/FS device under a HS hub? */ @@ -337,8 +388,8 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud */ if ((udev->speed == USB_SPEED_LOW || udev->speed == USB_SPEED_FULL) && udev->tt) { - dev->in_ctx->slot.tt_info = udev->tt->hub->slot_id; - dev->in_ctx->slot.tt_info |= udev->ttport << 8; + slot_ctx->tt_info = udev->tt->hub->slot_id; + slot_ctx->tt_info |= udev->ttport << 8; } xhci_dbg(xhci, "udev->tt = %p\n", udev->tt); xhci_dbg(xhci, "udev->ttport = 0x%x\n", udev->ttport); @@ -360,10 +411,9 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud ep0_ctx->ep_info2 |= MAX_BURST(0); ep0_ctx->ep_info2 |= ERROR_COUNT(3); - ep0_ctx->deq[0] = + ep0_ctx->deq = dev->ep_rings[0]->first_seg->dma; - ep0_ctx->deq[0] |= dev->ep_rings[0]->cycle_state; - ep0_ctx->deq[1] = 0; + ep0_ctx->deq |= dev->ep_rings[0]->cycle_state; /* Steps 7 and 8 were done in xhci_alloc_virt_device() */ @@ -470,25 +520,26 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, unsigned int max_burst; ep_index = xhci_get_endpoint_index(&ep->desc); - ep_ctx = &virt_dev->in_ctx->ep[ep_index]; + ep_ctx = xhci_get_ep_ctx(xhci, virt_dev->in_ctx, ep_index); /* Set up the endpoint ring */ virt_dev->new_ep_rings[ep_index] = xhci_ring_alloc(xhci, 1, true, mem_flags); if (!virt_dev->new_ep_rings[ep_index]) return -ENOMEM; ep_ring = virt_dev->new_ep_rings[ep_index]; - ep_ctx->deq[0] = ep_ring->first_seg->dma | ep_ring->cycle_state; - ep_ctx->deq[1] = 0; + ep_ctx->deq = ep_ring->first_seg->dma | ep_ring->cycle_state; ep_ctx->ep_info = xhci_get_endpoint_interval(udev, ep); /* FIXME dig Mult and streams info out of ep companion desc */ - /* Allow 3 retries for everything but isoc */ + /* Allow 3 retries for everything but isoc; + * error count = 0 means infinite retries. + */ if (!usb_endpoint_xfer_isoc(&ep->desc)) ep_ctx->ep_info2 = ERROR_COUNT(3); else - ep_ctx->ep_info2 = ERROR_COUNT(0); + ep_ctx->ep_info2 = ERROR_COUNT(1); ep_ctx->ep_info2 |= xhci_get_endpoint_type(udev, ep); @@ -498,7 +549,12 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, max_packet = ep->desc.wMaxPacketSize; ep_ctx->ep_info2 |= MAX_PACKET(max_packet); /* dig out max burst from ep companion desc */ - max_packet = ep->ss_ep_comp->desc.bMaxBurst; + if (!ep->ss_ep_comp) { + xhci_warn(xhci, "WARN no SS endpoint companion descriptor.\n"); + max_packet = 0; + } else { + max_packet = ep->ss_ep_comp->desc.bMaxBurst; + } ep_ctx->ep_info2 |= MAX_BURST(max_packet); break; case USB_SPEED_HIGH: @@ -531,18 +587,114 @@ void xhci_endpoint_zero(struct xhci_hcd *xhci, struct xhci_ep_ctx *ep_ctx; ep_index = xhci_get_endpoint_index(&ep->desc); - ep_ctx = &virt_dev->in_ctx->ep[ep_index]; + ep_ctx = xhci_get_ep_ctx(xhci, virt_dev->in_ctx, ep_index); ep_ctx->ep_info = 0; ep_ctx->ep_info2 = 0; - ep_ctx->deq[0] = 0; - ep_ctx->deq[1] = 0; + ep_ctx->deq = 0; ep_ctx->tx_info = 0; /* Don't free the endpoint ring until the set interface or configuration * request succeeds. */ } +/* Set up the scratchpad buffer array and scratchpad buffers, if needed. */ +static int scratchpad_alloc(struct xhci_hcd *xhci, gfp_t flags) +{ + int i; + struct device *dev = xhci_to_hcd(xhci)->self.controller; + int num_sp = HCS_MAX_SCRATCHPAD(xhci->hcs_params2); + + xhci_dbg(xhci, "Allocating %d scratchpad buffers\n", num_sp); + + if (!num_sp) + return 0; + + xhci->scratchpad = kzalloc(sizeof(*xhci->scratchpad), flags); + if (!xhci->scratchpad) + goto fail_sp; + + xhci->scratchpad->sp_array = + pci_alloc_consistent(to_pci_dev(dev), + num_sp * sizeof(u64), + &xhci->scratchpad->sp_dma); + if (!xhci->scratchpad->sp_array) + goto fail_sp2; + + xhci->scratchpad->sp_buffers = kzalloc(sizeof(void *) * num_sp, flags); + if (!xhci->scratchpad->sp_buffers) + goto fail_sp3; + + xhci->scratchpad->sp_dma_buffers = + kzalloc(sizeof(dma_addr_t) * num_sp, flags); + + if (!xhci->scratchpad->sp_dma_buffers) + goto fail_sp4; + + xhci->dcbaa->dev_context_ptrs[0] = xhci->scratchpad->sp_dma; + for (i = 0; i < num_sp; i++) { + dma_addr_t dma; + void *buf = pci_alloc_consistent(to_pci_dev(dev), + xhci->page_size, &dma); + if (!buf) + goto fail_sp5; + + xhci->scratchpad->sp_array[i] = dma; + xhci->scratchpad->sp_buffers[i] = buf; + xhci->scratchpad->sp_dma_buffers[i] = dma; + } + + return 0; + + fail_sp5: + for (i = i - 1; i >= 0; i--) { + pci_free_consistent(to_pci_dev(dev), xhci->page_size, + xhci->scratchpad->sp_buffers[i], + xhci->scratchpad->sp_dma_buffers[i]); + } + kfree(xhci->scratchpad->sp_dma_buffers); + + fail_sp4: + kfree(xhci->scratchpad->sp_buffers); + + fail_sp3: + pci_free_consistent(to_pci_dev(dev), num_sp * sizeof(u64), + xhci->scratchpad->sp_array, + xhci->scratchpad->sp_dma); + + fail_sp2: + kfree(xhci->scratchpad); + xhci->scratchpad = NULL; + + fail_sp: + return -ENOMEM; +} + +static void scratchpad_free(struct xhci_hcd *xhci) +{ + int num_sp; + int i; + struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); + + if (!xhci->scratchpad) + return; + + num_sp = HCS_MAX_SCRATCHPAD(xhci->hcs_params2); + + for (i = 0; i < num_sp; i++) { + pci_free_consistent(pdev, xhci->page_size, + xhci->scratchpad->sp_buffers[i], + xhci->scratchpad->sp_dma_buffers[i]); + } + kfree(xhci->scratchpad->sp_dma_buffers); + kfree(xhci->scratchpad->sp_buffers); + pci_free_consistent(pdev, num_sp * sizeof(u64), + xhci->scratchpad->sp_array, + xhci->scratchpad->sp_dma); + kfree(xhci->scratchpad); + xhci->scratchpad = NULL; +} + void xhci_mem_cleanup(struct xhci_hcd *xhci) { struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); @@ -551,10 +703,8 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) /* Free the Event Ring Segment Table and the actual Event Ring */ xhci_writel(xhci, 0, &xhci->ir_set->erst_size); - xhci_writel(xhci, 0, &xhci->ir_set->erst_base[0]); - xhci_writel(xhci, 0, &xhci->ir_set->erst_base[1]); - xhci_writel(xhci, 0, &xhci->ir_set->erst_dequeue[0]); - xhci_writel(xhci, 0, &xhci->ir_set->erst_dequeue[1]); + xhci_write_64(xhci, 0, &xhci->ir_set->erst_base); + xhci_write_64(xhci, 0, &xhci->ir_set->erst_dequeue); size = sizeof(struct xhci_erst_entry)*(xhci->erst.num_entries); if (xhci->erst.entries) pci_free_consistent(pdev, size, @@ -566,8 +716,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) xhci->event_ring = NULL; xhci_dbg(xhci, "Freed event ring\n"); - xhci_writel(xhci, 0, &xhci->op_regs->cmd_ring[0]); - xhci_writel(xhci, 0, &xhci->op_regs->cmd_ring[1]); + xhci_write_64(xhci, 0, &xhci->op_regs->cmd_ring); if (xhci->cmd_ring) xhci_ring_free(xhci, xhci->cmd_ring); xhci->cmd_ring = NULL; @@ -586,8 +735,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) xhci->device_pool = NULL; xhci_dbg(xhci, "Freed device context pool\n"); - xhci_writel(xhci, 0, &xhci->op_regs->dcbaa_ptr[0]); - xhci_writel(xhci, 0, &xhci->op_regs->dcbaa_ptr[1]); + xhci_write_64(xhci, 0, &xhci->op_regs->dcbaa_ptr); if (xhci->dcbaa) pci_free_consistent(pdev, sizeof(*xhci->dcbaa), xhci->dcbaa, xhci->dcbaa->dma); @@ -595,6 +743,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) xhci->page_size = 0; xhci->page_shift = 0; + scratchpad_free(xhci); } int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) @@ -602,6 +751,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) dma_addr_t dma; struct device *dev = xhci_to_hcd(xhci)->self.controller; unsigned int val, val2; + u64 val_64; struct xhci_segment *seg; u32 page_size; int i; @@ -647,8 +797,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) xhci->dcbaa->dma = dma; xhci_dbg(xhci, "// Device context base array address = 0x%llx (DMA), %p (virt)\n", (unsigned long long)xhci->dcbaa->dma, xhci->dcbaa); - xhci_writel(xhci, dma, &xhci->op_regs->dcbaa_ptr[0]); - xhci_writel(xhci, (u32) 0, &xhci->op_regs->dcbaa_ptr[1]); + xhci_write_64(xhci, dma, &xhci->op_regs->dcbaa_ptr); /* * Initialize the ring segment pool. The ring must be a contiguous @@ -658,11 +807,10 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) */ xhci->segment_pool = dma_pool_create("xHCI ring segments", dev, SEGMENT_SIZE, 64, xhci->page_size); + /* See Table 46 and Note on Figure 55 */ - /* FIXME support 64-byte contexts */ xhci->device_pool = dma_pool_create("xHCI input/output contexts", dev, - sizeof(struct xhci_device_control), - 64, xhci->page_size); + 2112, 64, xhci->page_size); if (!xhci->segment_pool || !xhci->device_pool) goto fail; @@ -675,14 +823,12 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) (unsigned long long)xhci->cmd_ring->first_seg->dma); /* Set the address in the Command Ring Control register */ - val = xhci_readl(xhci, &xhci->op_regs->cmd_ring[0]); - val = (val & ~CMD_RING_ADDR_MASK) | - (xhci->cmd_ring->first_seg->dma & CMD_RING_ADDR_MASK) | + val_64 = xhci_read_64(xhci, &xhci->op_regs->cmd_ring); + val_64 = (val_64 & (u64) CMD_RING_RSVD_BITS) | + (xhci->cmd_ring->first_seg->dma & (u64) ~CMD_RING_RSVD_BITS) | xhci->cmd_ring->cycle_state; - xhci_dbg(xhci, "// Setting command ring address low bits to 0x%x\n", val); - xhci_writel(xhci, val, &xhci->op_regs->cmd_ring[0]); - xhci_dbg(xhci, "// Setting command ring address high bits to 0x0\n"); - xhci_writel(xhci, (u32) 0, &xhci->op_regs->cmd_ring[1]); + xhci_dbg(xhci, "// Setting command ring address to 0x%x\n", val); + xhci_write_64(xhci, val_64, &xhci->op_regs->cmd_ring); xhci_dbg_cmd_ptrs(xhci); val = xhci_readl(xhci, &xhci->cap_regs->db_off); @@ -722,8 +868,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) /* set ring base address and size for each segment table entry */ for (val = 0, seg = xhci->event_ring->first_seg; val < ERST_NUM_SEGS; val++) { struct xhci_erst_entry *entry = &xhci->erst.entries[val]; - entry->seg_addr[0] = seg->dma; - entry->seg_addr[1] = 0; + entry->seg_addr = seg->dma; entry->seg_size = TRBS_PER_SEGMENT; entry->rsvd = 0; seg = seg->next; @@ -741,11 +886,10 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) /* set the segment table base address */ xhci_dbg(xhci, "// Set ERST base address for ir_set 0 = 0x%llx\n", (unsigned long long)xhci->erst.erst_dma_addr); - val = xhci_readl(xhci, &xhci->ir_set->erst_base[0]); - val &= ERST_PTR_MASK; - val |= (xhci->erst.erst_dma_addr & ~ERST_PTR_MASK); - xhci_writel(xhci, val, &xhci->ir_set->erst_base[0]); - xhci_writel(xhci, 0, &xhci->ir_set->erst_base[1]); + val_64 = xhci_read_64(xhci, &xhci->ir_set->erst_base); + val_64 &= ERST_PTR_MASK; + val_64 |= (xhci->erst.erst_dma_addr & (u64) ~ERST_PTR_MASK); + xhci_write_64(xhci, val_64, &xhci->ir_set->erst_base); /* Set the event ring dequeue address */ xhci_set_hc_event_deq(xhci); @@ -761,7 +905,11 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) for (i = 0; i < MAX_HC_SLOTS; ++i) xhci->devs[i] = 0; + if (scratchpad_alloc(xhci, flags)) + goto fail; + return 0; + fail: xhci_warn(xhci, "Couldn't initialize memory\n"); xhci_mem_cleanup(xhci); diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 1462709e26c..592fe7e623f 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -117,6 +117,7 @@ static const struct hc_driver xhci_pci_hc_driver = { .free_dev = xhci_free_dev, .add_endpoint = xhci_add_endpoint, .drop_endpoint = xhci_drop_endpoint, + .endpoint_reset = xhci_endpoint_reset, .check_bandwidth = xhci_check_bandwidth, .reset_bandwidth = xhci_reset_bandwidth, .address_device = xhci_address_device, diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 02d81985c45..aa88a067148 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -135,6 +135,7 @@ static void next_trb(struct xhci_hcd *xhci, static void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring, bool consumer) { union xhci_trb *next = ++(ring->dequeue); + unsigned long long addr; ring->deq_updates++; /* Update the dequeue pointer further if that was a link TRB or we're at @@ -152,6 +153,13 @@ static void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring, bool consumer ring->dequeue = ring->deq_seg->trbs; next = ring->dequeue; } + addr = (unsigned long long) xhci_trb_virt_to_dma(ring->deq_seg, ring->dequeue); + if (ring == xhci->event_ring) + xhci_dbg(xhci, "Event ring deq = 0x%llx (DMA)\n", addr); + else if (ring == xhci->cmd_ring) + xhci_dbg(xhci, "Command ring deq = 0x%llx (DMA)\n", addr); + else + xhci_dbg(xhci, "Ring deq = 0x%llx (DMA)\n", addr); } /* @@ -171,6 +179,7 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring, bool consumer { u32 chain; union xhci_trb *next; + unsigned long long addr; chain = ring->enqueue->generic.field[3] & TRB_CHAIN; next = ++(ring->enqueue); @@ -204,6 +213,13 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring, bool consumer ring->enqueue = ring->enq_seg->trbs; next = ring->enqueue; } + addr = (unsigned long long) xhci_trb_virt_to_dma(ring->enq_seg, ring->enqueue); + if (ring == xhci->event_ring) + xhci_dbg(xhci, "Event ring enq = 0x%llx (DMA)\n", addr); + else if (ring == xhci->cmd_ring) + xhci_dbg(xhci, "Command ring enq = 0x%llx (DMA)\n", addr); + else + xhci_dbg(xhci, "Ring enq = 0x%llx (DMA)\n", addr); } /* @@ -237,7 +253,7 @@ static int room_on_ring(struct xhci_hcd *xhci, struct xhci_ring *ring, void xhci_set_hc_event_deq(struct xhci_hcd *xhci) { - u32 temp; + u64 temp; dma_addr_t deq; deq = xhci_trb_virt_to_dma(xhci->event_ring->deq_seg, @@ -246,13 +262,15 @@ void xhci_set_hc_event_deq(struct xhci_hcd *xhci) xhci_warn(xhci, "WARN something wrong with SW event ring " "dequeue ptr.\n"); /* Update HC event ring dequeue pointer */ - temp = xhci_readl(xhci, &xhci->ir_set->erst_dequeue[0]); + temp = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); temp &= ERST_PTR_MASK; - if (!in_interrupt()) - xhci_dbg(xhci, "// Write event ring dequeue pointer\n"); - xhci_writel(xhci, 0, &xhci->ir_set->erst_dequeue[1]); - xhci_writel(xhci, (deq & ~ERST_PTR_MASK) | temp, - &xhci->ir_set->erst_dequeue[0]); + /* Don't clear the EHB bit (which is RW1C) because + * there might be more events to service. + */ + temp &= ~ERST_EHB; + xhci_dbg(xhci, "// Write event ring dequeue pointer, preserving EHB bit\n"); + xhci_write_64(xhci, ((u64) deq & (u64) ~ERST_PTR_MASK) | temp, + &xhci->ir_set->erst_dequeue); } /* Ring the host controller doorbell after placing a command on the ring */ @@ -279,7 +297,8 @@ static void ring_ep_doorbell(struct xhci_hcd *xhci, /* Don't ring the doorbell for this endpoint if there are pending * cancellations because the we don't want to interrupt processing. */ - if (!ep_ring->cancels_pending && !(ep_ring->state & SET_DEQ_PENDING)) { + if (!ep_ring->cancels_pending && !(ep_ring->state & SET_DEQ_PENDING) + && !(ep_ring->state & EP_HALTED)) { field = xhci_readl(xhci, db_addr) & DB_MASK; xhci_writel(xhci, field | EPI_TO_DB(ep_index), db_addr); /* Flush PCI posted writes - FIXME Matthew Wilcox says this @@ -316,12 +335,6 @@ static struct xhci_segment *find_trb_seg( return cur_seg; } -struct dequeue_state { - struct xhci_segment *new_deq_seg; - union xhci_trb *new_deq_ptr; - int new_cycle_state; -}; - /* * Move the xHC's endpoint ring dequeue pointer past cur_td. * Record the new state of the xHC's endpoint ring dequeue segment, @@ -336,24 +349,30 @@ struct dequeue_state { * - Finally we move the dequeue state one TRB further, toggling the cycle bit * if we've moved it past a link TRB with the toggle cycle bit set. */ -static void find_new_dequeue_state(struct xhci_hcd *xhci, +void xhci_find_new_dequeue_state(struct xhci_hcd *xhci, unsigned int slot_id, unsigned int ep_index, - struct xhci_td *cur_td, struct dequeue_state *state) + struct xhci_td *cur_td, struct xhci_dequeue_state *state) { struct xhci_virt_device *dev = xhci->devs[slot_id]; struct xhci_ring *ep_ring = dev->ep_rings[ep_index]; struct xhci_generic_trb *trb; + struct xhci_ep_ctx *ep_ctx; + dma_addr_t addr; state->new_cycle_state = 0; + xhci_dbg(xhci, "Finding segment containing stopped TRB.\n"); state->new_deq_seg = find_trb_seg(cur_td->start_seg, ep_ring->stopped_trb, &state->new_cycle_state); if (!state->new_deq_seg) BUG(); /* Dig out the cycle state saved by the xHC during the stop ep cmd */ - state->new_cycle_state = 0x1 & dev->out_ctx->ep[ep_index].deq[0]; + xhci_dbg(xhci, "Finding endpoint context\n"); + ep_ctx = xhci_get_ep_ctx(xhci, dev->out_ctx, ep_index); + state->new_cycle_state = 0x1 & ep_ctx->deq; state->new_deq_ptr = cur_td->last_trb; + xhci_dbg(xhci, "Finding segment containing last TRB in TD.\n"); state->new_deq_seg = find_trb_seg(state->new_deq_seg, state->new_deq_ptr, &state->new_cycle_state); @@ -367,6 +386,12 @@ static void find_new_dequeue_state(struct xhci_hcd *xhci, next_trb(xhci, ep_ring, &state->new_deq_seg, &state->new_deq_ptr); /* Don't update the ring cycle state for the producer (us). */ + xhci_dbg(xhci, "New dequeue segment = %p (virtual)\n", + state->new_deq_seg); + addr = xhci_trb_virt_to_dma(state->new_deq_seg, state->new_deq_ptr); + xhci_dbg(xhci, "New dequeue pointer = 0x%llx (DMA)\n", + (unsigned long long) addr); + xhci_dbg(xhci, "Setting dequeue pointer in internal ring state.\n"); ep_ring->dequeue = state->new_deq_ptr; ep_ring->deq_seg = state->new_deq_seg; } @@ -416,6 +441,30 @@ static int queue_set_tr_deq(struct xhci_hcd *xhci, int slot_id, unsigned int ep_index, struct xhci_segment *deq_seg, union xhci_trb *deq_ptr, u32 cycle_state); +void xhci_queue_new_dequeue_state(struct xhci_hcd *xhci, + struct xhci_ring *ep_ring, unsigned int slot_id, + unsigned int ep_index, struct xhci_dequeue_state *deq_state) +{ + xhci_dbg(xhci, "Set TR Deq Ptr cmd, new deq seg = %p (0x%llx dma), " + "new deq ptr = %p (0x%llx dma), new cycle = %u\n", + deq_state->new_deq_seg, + (unsigned long long)deq_state->new_deq_seg->dma, + deq_state->new_deq_ptr, + (unsigned long long)xhci_trb_virt_to_dma(deq_state->new_deq_seg, deq_state->new_deq_ptr), + deq_state->new_cycle_state); + queue_set_tr_deq(xhci, slot_id, ep_index, + deq_state->new_deq_seg, + deq_state->new_deq_ptr, + (u32) deq_state->new_cycle_state); + /* Stop the TD queueing code from ringing the doorbell until + * this command completes. The HC won't set the dequeue pointer + * if the ring is running, and ringing the doorbell starts the + * ring running. + */ + ep_ring->state |= SET_DEQ_PENDING; + xhci_ring_cmd_db(xhci); +} + /* * When we get a command completion for a Stop Endpoint Command, we need to * unlink any cancelled TDs from the ring. There are two ways to do that: @@ -436,7 +485,7 @@ static void handle_stopped_endpoint(struct xhci_hcd *xhci, struct xhci_td *cur_td = 0; struct xhci_td *last_unlinked_td; - struct dequeue_state deq_state; + struct xhci_dequeue_state deq_state; #ifdef CONFIG_USB_HCD_STAT ktime_t stop_time = ktime_get(); #endif @@ -464,7 +513,7 @@ static void handle_stopped_endpoint(struct xhci_hcd *xhci, * move the xHC endpoint ring dequeue pointer past this TD. */ if (cur_td == ep_ring->stopped_td) - find_new_dequeue_state(xhci, slot_id, ep_index, cur_td, + xhci_find_new_dequeue_state(xhci, slot_id, ep_index, cur_td, &deq_state); else td_to_noop(xhci, ep_ring, cur_td); @@ -480,24 +529,8 @@ static void handle_stopped_endpoint(struct xhci_hcd *xhci, /* If necessary, queue a Set Transfer Ring Dequeue Pointer command */ if (deq_state.new_deq_ptr && deq_state.new_deq_seg) { - xhci_dbg(xhci, "Set TR Deq Ptr cmd, new deq seg = %p (0x%llx dma), " - "new deq ptr = %p (0x%llx dma), new cycle = %u\n", - deq_state.new_deq_seg, - (unsigned long long)deq_state.new_deq_seg->dma, - deq_state.new_deq_ptr, - (unsigned long long)xhci_trb_virt_to_dma(deq_state.new_deq_seg, deq_state.new_deq_ptr), - deq_state.new_cycle_state); - queue_set_tr_deq(xhci, slot_id, ep_index, - deq_state.new_deq_seg, - deq_state.new_deq_ptr, - (u32) deq_state.new_cycle_state); - /* Stop the TD queueing code from ringing the doorbell until - * this command completes. The HC won't set the dequeue pointer - * if the ring is running, and ringing the doorbell starts the - * ring running. - */ - ep_ring->state |= SET_DEQ_PENDING; - xhci_ring_cmd_db(xhci); + xhci_queue_new_dequeue_state(xhci, ep_ring, + slot_id, ep_index, &deq_state); } else { /* Otherwise just ring the doorbell to restart the ring */ ring_ep_doorbell(xhci, slot_id, ep_index); @@ -551,11 +584,15 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci, unsigned int ep_index; struct xhci_ring *ep_ring; struct xhci_virt_device *dev; + struct xhci_ep_ctx *ep_ctx; + struct xhci_slot_ctx *slot_ctx; slot_id = TRB_TO_SLOT_ID(trb->generic.field[3]); ep_index = TRB_TO_EP_INDEX(trb->generic.field[3]); dev = xhci->devs[slot_id]; ep_ring = dev->ep_rings[ep_index]; + ep_ctx = xhci_get_ep_ctx(xhci, dev->out_ctx, ep_index); + slot_ctx = xhci_get_slot_ctx(xhci, dev->out_ctx); if (GET_COMP_CODE(event->status) != COMP_SUCCESS) { unsigned int ep_state; @@ -569,9 +606,9 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci, case COMP_CTX_STATE: xhci_warn(xhci, "WARN Set TR Deq Ptr cmd failed due " "to incorrect slot or ep state.\n"); - ep_state = dev->out_ctx->ep[ep_index].ep_info; + ep_state = ep_ctx->ep_info; ep_state &= EP_STATE_MASK; - slot_state = dev->out_ctx->slot.dev_state; + slot_state = slot_ctx->dev_state; slot_state = GET_SLOT_STATE(slot_state); xhci_dbg(xhci, "Slot state = %u, EP state = %u\n", slot_state, ep_state); @@ -593,16 +630,33 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci, * cancelling URBs, which might not be an error... */ } else { - xhci_dbg(xhci, "Successful Set TR Deq Ptr cmd, deq[0] = 0x%x, " - "deq[1] = 0x%x.\n", - dev->out_ctx->ep[ep_index].deq[0], - dev->out_ctx->ep[ep_index].deq[1]); + xhci_dbg(xhci, "Successful Set TR Deq Ptr cmd, deq = @%08llx\n", + ep_ctx->deq); } ep_ring->state &= ~SET_DEQ_PENDING; ring_ep_doorbell(xhci, slot_id, ep_index); } +static void handle_reset_ep_completion(struct xhci_hcd *xhci, + struct xhci_event_cmd *event, + union xhci_trb *trb) +{ + int slot_id; + unsigned int ep_index; + + slot_id = TRB_TO_SLOT_ID(trb->generic.field[3]); + ep_index = TRB_TO_EP_INDEX(trb->generic.field[3]); + /* This command will only fail if the endpoint wasn't halted, + * but we don't care. + */ + xhci_dbg(xhci, "Ignoring reset ep completion code of %u\n", + (unsigned int) GET_COMP_CODE(event->status)); + + /* Clear our internal halted state and restart the ring */ + xhci->devs[slot_id]->ep_rings[ep_index]->state &= ~EP_HALTED; + ring_ep_doorbell(xhci, slot_id, ep_index); +} static void handle_cmd_completion(struct xhci_hcd *xhci, struct xhci_event_cmd *event) @@ -611,7 +665,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, u64 cmd_dma; dma_addr_t cmd_dequeue_dma; - cmd_dma = (((u64) event->cmd_trb[1]) << 32) + event->cmd_trb[0]; + cmd_dma = event->cmd_trb; cmd_dequeue_dma = xhci_trb_virt_to_dma(xhci->cmd_ring->deq_seg, xhci->cmd_ring->dequeue); /* Is the command ring deq ptr out of sync with the deq seg ptr? */ @@ -653,6 +707,9 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, case TRB_TYPE(TRB_CMD_NOOP): ++xhci->noops_handled; break; + case TRB_TYPE(TRB_RESET_EP): + handle_reset_ep_completion(xhci, event, xhci->cmd_ring->dequeue); + break; default: /* Skip over unknown commands on the event ring */ xhci->error_bitmask |= 1 << 6; @@ -756,7 +813,9 @@ static int handle_tx_event(struct xhci_hcd *xhci, union xhci_trb *event_trb; struct urb *urb = 0; int status = -EINPROGRESS; + struct xhci_ep_ctx *ep_ctx; + xhci_dbg(xhci, "In %s\n", __func__); xdev = xhci->devs[TRB_TO_SLOT_ID(event->flags)]; if (!xdev) { xhci_err(xhci, "ERROR Transfer event pointed to bad slot\n"); @@ -765,17 +824,17 @@ static int handle_tx_event(struct xhci_hcd *xhci, /* Endpoint ID is 1 based, our index is zero based */ ep_index = TRB_TO_EP_ID(event->flags) - 1; + xhci_dbg(xhci, "%s - ep index = %d\n", __func__, ep_index); ep_ring = xdev->ep_rings[ep_index]; - if (!ep_ring || (xdev->out_ctx->ep[ep_index].ep_info & EP_STATE_MASK) == EP_STATE_DISABLED) { + ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index); + if (!ep_ring || (ep_ctx->ep_info & EP_STATE_MASK) == EP_STATE_DISABLED) { xhci_err(xhci, "ERROR Transfer event pointed to disabled endpoint\n"); return -ENODEV; } - event_dma = event->buffer[0]; - if (event->buffer[1] != 0) - xhci_warn(xhci, "WARN ignoring upper 32-bits of 64-bit TRB dma address\n"); - + event_dma = event->buffer; /* This TRB should be in the TD at the head of this ring's TD list */ + xhci_dbg(xhci, "%s - checking for list empty\n", __func__); if (list_empty(&ep_ring->td_list)) { xhci_warn(xhci, "WARN Event TRB for slot %d ep %d with no TDs queued?\n", TRB_TO_SLOT_ID(event->flags), ep_index); @@ -785,11 +844,14 @@ static int handle_tx_event(struct xhci_hcd *xhci, urb = NULL; goto cleanup; } + xhci_dbg(xhci, "%s - getting list entry\n", __func__); td = list_entry(ep_ring->td_list.next, struct xhci_td, td_list); /* Is this a TRB in the currently executing TD? */ + xhci_dbg(xhci, "%s - looking for TD\n", __func__); event_seg = trb_in_td(ep_ring->deq_seg, ep_ring->dequeue, td->last_trb, event_dma); + xhci_dbg(xhci, "%s - found event_seg = %p\n", __func__, event_seg); if (!event_seg) { /* HC is busted, give up! */ xhci_err(xhci, "ERROR Transfer event TRB DMA ptr not part of current TD\n"); @@ -798,10 +860,10 @@ static int handle_tx_event(struct xhci_hcd *xhci, event_trb = &event_seg->trbs[(event_dma - event_seg->dma) / sizeof(*event_trb)]; xhci_dbg(xhci, "Event TRB with TRB type ID %u\n", (unsigned int) (event->flags & TRB_TYPE_BITMASK)>>10); - xhci_dbg(xhci, "Offset 0x00 (buffer[0]) = 0x%x\n", - (unsigned int) event->buffer[0]); - xhci_dbg(xhci, "Offset 0x04 (buffer[0]) = 0x%x\n", - (unsigned int) event->buffer[1]); + xhci_dbg(xhci, "Offset 0x00 (buffer lo) = 0x%x\n", + lower_32_bits(event->buffer)); + xhci_dbg(xhci, "Offset 0x04 (buffer hi) = 0x%x\n", + upper_32_bits(event->buffer)); xhci_dbg(xhci, "Offset 0x08 (transfer length) = 0x%x\n", (unsigned int) event->transfer_len); xhci_dbg(xhci, "Offset 0x0C (flags) = 0x%x\n", @@ -823,6 +885,7 @@ static int handle_tx_event(struct xhci_hcd *xhci, break; case COMP_STALL: xhci_warn(xhci, "WARN: Stalled endpoint\n"); + ep_ring->state |= EP_HALTED; status = -EPIPE; break; case COMP_TRB_ERR: @@ -833,6 +896,10 @@ static int handle_tx_event(struct xhci_hcd *xhci, xhci_warn(xhci, "WARN: transfer error on endpoint\n"); status = -EPROTO; break; + case COMP_BABBLE: + xhci_warn(xhci, "WARN: babble error on endpoint\n"); + status = -EOVERFLOW; + break; case COMP_DB_ERR: xhci_warn(xhci, "WARN: HC couldn't access mem fast enough\n"); status = -ENOSR; @@ -874,15 +941,26 @@ static int handle_tx_event(struct xhci_hcd *xhci, if (event_trb != ep_ring->dequeue) { /* The event was for the status stage */ if (event_trb == td->last_trb) { - td->urb->actual_length = - td->urb->transfer_buffer_length; + if (td->urb->actual_length != 0) { + /* Don't overwrite a previously set error code */ + if (status == -EINPROGRESS || status == 0) + /* Did we already see a short data stage? */ + status = -EREMOTEIO; + } else { + td->urb->actual_length = + td->urb->transfer_buffer_length; + } } else { /* Maybe the event was for the data stage? */ - if (GET_COMP_CODE(event->transfer_len) != COMP_STOP_INVAL) + if (GET_COMP_CODE(event->transfer_len) != COMP_STOP_INVAL) { /* We didn't stop on a link TRB in the middle */ td->urb->actual_length = td->urb->transfer_buffer_length - TRB_LEN(event->transfer_len); + xhci_dbg(xhci, "Waiting for status stage event\n"); + urb = NULL; + goto cleanup; + } } } } else { @@ -929,16 +1007,20 @@ static int handle_tx_event(struct xhci_hcd *xhci, TRB_LEN(event->transfer_len)); td->urb->actual_length = 0; } - if (td->urb->transfer_flags & URB_SHORT_NOT_OK) - status = -EREMOTEIO; - else - status = 0; + /* Don't overwrite a previously set error code */ + if (status == -EINPROGRESS) { + if (td->urb->transfer_flags & URB_SHORT_NOT_OK) + status = -EREMOTEIO; + else + status = 0; + } } else { td->urb->actual_length = td->urb->transfer_buffer_length; /* Ignore a short packet completion if the * untransferred length was zero. */ - status = 0; + if (status == -EREMOTEIO) + status = 0; } } else { /* Slow path - walk the list, starting from the dequeue @@ -965,19 +1047,30 @@ static int handle_tx_event(struct xhci_hcd *xhci, TRB_LEN(event->transfer_len); } } - /* The Endpoint Stop Command completion will take care of - * any stopped TDs. A stopped TD may be restarted, so don't update the - * ring dequeue pointer or take this TD off any lists yet. - */ if (GET_COMP_CODE(event->transfer_len) == COMP_STOP_INVAL || GET_COMP_CODE(event->transfer_len) == COMP_STOP) { + /* The Endpoint Stop Command completion will take care of any + * stopped TDs. A stopped TD may be restarted, so don't update + * the ring dequeue pointer or take this TD off any lists yet. + */ ep_ring->stopped_td = td; ep_ring->stopped_trb = event_trb; } else { - /* Update ring dequeue pointer */ - while (ep_ring->dequeue != td->last_trb) + if (GET_COMP_CODE(event->transfer_len) == COMP_STALL) { + /* The transfer is completed from the driver's + * perspective, but we need to issue a set dequeue + * command for this stalled endpoint to move the dequeue + * pointer past the TD. We can't do that here because + * the halt condition must be cleared first. + */ + ep_ring->stopped_td = td; + ep_ring->stopped_trb = event_trb; + } else { + /* Update ring dequeue pointer */ + while (ep_ring->dequeue != td->last_trb) + inc_deq(xhci, ep_ring, false); inc_deq(xhci, ep_ring, false); - inc_deq(xhci, ep_ring, false); + } /* Clean up the endpoint's TD list */ urb = td->urb; @@ -987,7 +1080,10 @@ static int handle_tx_event(struct xhci_hcd *xhci, list_del(&td->cancelled_td_list); ep_ring->cancels_pending--; } - kfree(td); + /* Leave the TD around for the reset endpoint function to use */ + if (GET_COMP_CODE(event->transfer_len) != COMP_STALL) { + kfree(td); + } urb->hcpriv = NULL; } cleanup: @@ -997,6 +1093,8 @@ cleanup: /* FIXME for multi-TD URBs (who have buffers bigger than 64MB) */ if (urb) { usb_hcd_unlink_urb_from_ep(xhci_to_hcd(xhci), urb); + xhci_dbg(xhci, "Giveback URB %p, len = %d, status = %d\n", + urb, td->urb->actual_length, status); spin_unlock(&xhci->lock); usb_hcd_giveback_urb(xhci_to_hcd(xhci), urb, status); spin_lock(&xhci->lock); @@ -1014,6 +1112,7 @@ void xhci_handle_event(struct xhci_hcd *xhci) int update_ptrs = 1; int ret; + xhci_dbg(xhci, "In %s\n", __func__); if (!xhci->event_ring || !xhci->event_ring->dequeue) { xhci->error_bitmask |= 1 << 1; return; @@ -1026,18 +1125,25 @@ void xhci_handle_event(struct xhci_hcd *xhci) xhci->error_bitmask |= 1 << 2; return; } + xhci_dbg(xhci, "%s - OS owns TRB\n", __func__); /* FIXME: Handle more event types. */ switch ((event->event_cmd.flags & TRB_TYPE_BITMASK)) { case TRB_TYPE(TRB_COMPLETION): + xhci_dbg(xhci, "%s - calling handle_cmd_completion\n", __func__); handle_cmd_completion(xhci, &event->event_cmd); + xhci_dbg(xhci, "%s - returned from handle_cmd_completion\n", __func__); break; case TRB_TYPE(TRB_PORT_STATUS): + xhci_dbg(xhci, "%s - calling handle_port_status\n", __func__); handle_port_status(xhci, event); + xhci_dbg(xhci, "%s - returned from handle_port_status\n", __func__); update_ptrs = 0; break; case TRB_TYPE(TRB_TRANSFER): + xhci_dbg(xhci, "%s - calling handle_tx_event\n", __func__); ret = handle_tx_event(xhci, &event->trans_event); + xhci_dbg(xhci, "%s - returned from handle_tx_event\n", __func__); if (ret < 0) xhci->error_bitmask |= 1 << 9; else @@ -1093,13 +1199,13 @@ static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring, */ xhci_warn(xhci, "WARN urb submitted to disabled ep\n"); return -ENOENT; - case EP_STATE_HALTED: case EP_STATE_ERROR: - xhci_warn(xhci, "WARN waiting for halt or error on ep " - "to be cleared\n"); + xhci_warn(xhci, "WARN waiting for error on ep to be cleared\n"); /* FIXME event handling code for error needs to clear it */ /* XXX not sure if this should be -ENOENT or not */ return -EINVAL; + case EP_STATE_HALTED: + xhci_dbg(xhci, "WARN halted endpoint, queueing URB anyway.\n"); case EP_STATE_STOPPED: case EP_STATE_RUNNING: break; @@ -1128,9 +1234,9 @@ static int prepare_transfer(struct xhci_hcd *xhci, gfp_t mem_flags) { int ret; - + struct xhci_ep_ctx *ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index); ret = prepare_ring(xhci, xdev->ep_rings[ep_index], - xdev->out_ctx->ep[ep_index].ep_info & EP_STATE_MASK, + ep_ctx->ep_info & EP_STATE_MASK, num_trbs, mem_flags); if (ret) return ret; @@ -1285,6 +1391,7 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, /* Queue the first TRB, even if it's zero-length */ do { u32 field = 0; + u32 length_field = 0; /* Don't change the cycle bit of the first TRB until later */ if (first_trb) @@ -1314,10 +1421,13 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, (unsigned int) (addr + TRB_MAX_BUFF_SIZE) & ~(TRB_MAX_BUFF_SIZE - 1), (unsigned int) addr + trb_buff_len); } + length_field = TRB_LEN(trb_buff_len) | + TD_REMAINDER(urb->transfer_buffer_length - running_total) | + TRB_INTR_TARGET(0); queue_trb(xhci, ep_ring, false, - (u32) addr, - (u32) ((u64) addr >> 32), - TRB_LEN(trb_buff_len) | TRB_INTR_TARGET(0), + lower_32_bits(addr), + upper_32_bits(addr), + length_field, /* We always want to know if the TRB was short, * or we won't get an event when it completes. * (Unless we use event data TRBs, which are a @@ -1365,7 +1475,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct xhci_generic_trb *start_trb; bool first_trb; int start_cycle; - u32 field; + u32 field, length_field; int running_total, trb_buff_len, ret; u64 addr; @@ -1443,10 +1553,13 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, td->last_trb = ep_ring->enqueue; field |= TRB_IOC; } + length_field = TRB_LEN(trb_buff_len) | + TD_REMAINDER(urb->transfer_buffer_length - running_total) | + TRB_INTR_TARGET(0); queue_trb(xhci, ep_ring, false, - (u32) addr, - (u32) ((u64) addr >> 32), - TRB_LEN(trb_buff_len) | TRB_INTR_TARGET(0), + lower_32_bits(addr), + upper_32_bits(addr), + length_field, /* We always want to know if the TRB was short, * or we won't get an event when it completes. * (Unless we use event data TRBs, which are a @@ -1478,7 +1591,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct usb_ctrlrequest *setup; struct xhci_generic_trb *start_trb; int start_cycle; - u32 field; + u32 field, length_field; struct xhci_td *td; ep_ring = xhci->devs[slot_id]->ep_rings[ep_index]; @@ -1528,13 +1641,16 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, /* If there's data, queue data TRBs */ field = 0; + length_field = TRB_LEN(urb->transfer_buffer_length) | + TD_REMAINDER(urb->transfer_buffer_length) | + TRB_INTR_TARGET(0); if (urb->transfer_buffer_length > 0) { if (setup->bRequestType & USB_DIR_IN) field |= TRB_DIR_IN; queue_trb(xhci, ep_ring, false, lower_32_bits(urb->transfer_dma), upper_32_bits(urb->transfer_dma), - TRB_LEN(urb->transfer_buffer_length) | TRB_INTR_TARGET(0), + length_field, /* Event on short tx */ field | TRB_ISP | TRB_TYPE(TRB_DATA) | ep_ring->cycle_state); } @@ -1603,7 +1719,8 @@ int xhci_queue_slot_control(struct xhci_hcd *xhci, u32 trb_type, u32 slot_id) int xhci_queue_address_device(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr, u32 slot_id) { - return queue_command(xhci, in_ctx_ptr, 0, 0, + return queue_command(xhci, lower_32_bits(in_ctx_ptr), + upper_32_bits(in_ctx_ptr), 0, TRB_TYPE(TRB_ADDR_DEV) | SLOT_ID_FOR_TRB(slot_id)); } @@ -1611,7 +1728,8 @@ int xhci_queue_address_device(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr, int xhci_queue_configure_endpoint(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr, u32 slot_id) { - return queue_command(xhci, in_ctx_ptr, 0, 0, + return queue_command(xhci, lower_32_bits(in_ctx_ptr), + upper_32_bits(in_ctx_ptr), 0, TRB_TYPE(TRB_CONFIG_EP) | SLOT_ID_FOR_TRB(slot_id)); } @@ -1639,10 +1757,23 @@ static int queue_set_tr_deq(struct xhci_hcd *xhci, int slot_id, u32 type = TRB_TYPE(TRB_SET_DEQ); addr = xhci_trb_virt_to_dma(deq_seg, deq_ptr); - if (addr == 0) + if (addr == 0) { xhci_warn(xhci, "WARN Cannot submit Set TR Deq Ptr\n"); xhci_warn(xhci, "WARN deq seg = %p, deq pt = %p\n", deq_seg, deq_ptr); - return queue_command(xhci, (u32) addr | cycle_state, 0, 0, + return 0; + } + return queue_command(xhci, lower_32_bits(addr) | cycle_state, + upper_32_bits(addr), 0, trb_slot_id | trb_ep_index | type); } + +int xhci_queue_reset_ep(struct xhci_hcd *xhci, int slot_id, + unsigned int ep_index) +{ + u32 trb_slot_id = SLOT_ID_FOR_TRB(slot_id); + u32 trb_ep_index = EP_ID_FOR_TRB(ep_index); + u32 type = TRB_TYPE(TRB_RESET_EP); + + return queue_command(xhci, 0, 0, 0, trb_slot_id | trb_ep_index | type); +} diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 8936eeb5588..d31d32206ba 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -25,6 +25,7 @@ #include <linux/usb.h> #include <linux/timer.h> +#include <linux/kernel.h> #include "../core/hcd.h" /* Code sharing between pci-quirks and xhci hcd */ @@ -42,14 +43,6 @@ * xHCI register interface. * This corresponds to the eXtensible Host Controller Interface (xHCI) * Revision 0.95 specification - * - * Registers should always be accessed with double word or quad word accesses. - * - * Some xHCI implementations may support 64-bit address pointers. Registers - * with 64-bit address pointers should be written to with dword accesses by - * writing the low dword first (ptr[0]), then the high dword (ptr[1]) second. - * xHCI implementations that do not support 64-bit address pointers will ignore - * the high dword, and write order is irrelevant. */ /** @@ -96,6 +89,7 @@ struct xhci_cap_regs { #define HCS_ERST_MAX(p) (((p) >> 4) & 0xf) /* bit 26 Scratchpad restore - for save/restore HW state - not used yet */ /* bits 27:31 number of Scratchpad buffers SW must allocate for the HW */ +#define HCS_MAX_SCRATCHPAD(p) (((p) >> 27) & 0x1f) /* HCSPARAMS3 - hcs_params3 - bitmasks */ /* bits 0:7, Max U1 to U0 latency for the roothub ports */ @@ -166,10 +160,10 @@ struct xhci_op_regs { u32 reserved1; u32 reserved2; u32 dev_notification; - u32 cmd_ring[2]; + u64 cmd_ring; /* rsvd: offset 0x20-2F */ u32 reserved3[4]; - u32 dcbaa_ptr[2]; + u64 dcbaa_ptr; u32 config_reg; /* rsvd: offset 0x3C-3FF */ u32 reserved4[241]; @@ -254,7 +248,7 @@ struct xhci_op_regs { #define CMD_RING_RUNNING (1 << 3) /* bits 4:5 reserved and should be preserved */ /* Command Ring pointer - bit mask for the lower 32 bits. */ -#define CMD_RING_ADDR_MASK (0xffffffc0) +#define CMD_RING_RSVD_BITS (0x3f) /* CONFIG - Configure Register - config_reg bitmasks */ /* bits 0:7 - maximum number of device slots enabled (NumSlotsEn) */ @@ -382,8 +376,8 @@ struct xhci_intr_reg { u32 irq_control; u32 erst_size; u32 rsvd; - u32 erst_base[2]; - u32 erst_dequeue[2]; + u64 erst_base; + u64 erst_dequeue; }; /* irq_pending bitmasks */ @@ -453,6 +447,27 @@ struct xhci_doorbell_array { /** + * struct xhci_container_ctx + * @type: Type of context. Used to calculated offsets to contained contexts. + * @size: Size of the context data + * @bytes: The raw context data given to HW + * @dma: dma address of the bytes + * + * Represents either a Device or Input context. Holds a pointer to the raw + * memory used for the context (bytes) and dma address of it (dma). + */ +struct xhci_container_ctx { + unsigned type; +#define XHCI_CTX_TYPE_DEVICE 0x1 +#define XHCI_CTX_TYPE_INPUT 0x2 + + int size; + + u8 *bytes; + dma_addr_t dma; +}; + +/** * struct xhci_slot_ctx * @dev_info: Route string, device speed, hub info, and last valid endpoint * @dev_info2: Max exit latency for device number, root hub port number @@ -538,7 +553,7 @@ struct xhci_slot_ctx { struct xhci_ep_ctx { u32 ep_info; u32 ep_info2; - u32 deq[2]; + u64 deq; u32 tx_info; /* offset 0x14 - 0x1f reserved for HC internal use */ u32 reserved[3]; @@ -589,18 +604,16 @@ struct xhci_ep_ctx { /** - * struct xhci_device_control - * Input/Output context; see section 6.2.5. + * struct xhci_input_control_context + * Input control context; see section 6.2.5. * * @drop_context: set the bit of the endpoint context you want to disable * @add_context: set the bit of the endpoint context you want to enable */ -struct xhci_device_control { +struct xhci_input_control_ctx { u32 drop_flags; u32 add_flags; - u32 rsvd[6]; - struct xhci_slot_ctx slot; - struct xhci_ep_ctx ep[31]; + u32 rsvd2[6]; }; /* drop context bitmasks */ @@ -608,7 +621,6 @@ struct xhci_device_control { /* add context bitmasks */ #define ADD_EP(x) (0x1 << x) - struct xhci_virt_device { /* * Commands to the hardware are passed an "input context" that @@ -618,11 +630,10 @@ struct xhci_virt_device { * track of input and output contexts separately because * these commands might fail and we don't trust the hardware. */ - struct xhci_device_control *out_ctx; - dma_addr_t out_ctx_dma; + struct xhci_container_ctx *out_ctx; /* Used for addressing devices and configuration changes */ - struct xhci_device_control *in_ctx; - dma_addr_t in_ctx_dma; + struct xhci_container_ctx *in_ctx; + /* FIXME when stream support is added */ struct xhci_ring *ep_rings[31]; /* Temporary storage in case the configure endpoint command fails and we @@ -641,7 +652,7 @@ struct xhci_virt_device { */ struct xhci_device_context_array { /* 64-bit device addresses; we only write 32-bit addresses */ - u32 dev_context_ptrs[2*MAX_HC_SLOTS]; + u64 dev_context_ptrs[MAX_HC_SLOTS]; /* private xHCD pointers */ dma_addr_t dma; }; @@ -654,7 +665,7 @@ struct xhci_device_context_array { struct xhci_stream_ctx { /* 64-bit stream ring address, cycle state, and stream type */ - u32 stream_ring[2]; + u64 stream_ring; /* offset 0x14 - 0x1f reserved for HC internal use */ u32 reserved[2]; }; @@ -662,7 +673,7 @@ struct xhci_stream_ctx { struct xhci_transfer_event { /* 64-bit buffer address, or immediate data */ - u32 buffer[2]; + u64 buffer; u32 transfer_len; /* This field is interpreted differently based on the type of TRB */ u32 flags; @@ -744,7 +755,7 @@ struct xhci_transfer_event { struct xhci_link_trb { /* 64-bit segment pointer*/ - u32 segment_ptr[2]; + u64 segment_ptr; u32 intr_target; u32 control; }; @@ -755,7 +766,7 @@ struct xhci_link_trb { /* Command completion event TRB */ struct xhci_event_cmd { /* Pointer to command TRB, or the value passed by the event data trb */ - u32 cmd_trb[2]; + u64 cmd_trb; u32 status; u32 flags; }; @@ -848,8 +859,8 @@ union xhci_trb { #define TRB_CONFIG_EP 12 /* Evaluate Context Command */ #define TRB_EVAL_CONTEXT 13 -/* Reset Transfer Ring Command */ -#define TRB_RESET_RING 14 +/* Reset Endpoint Command */ +#define TRB_RESET_EP 14 /* Stop Transfer Ring Command */ #define TRB_STOP_RING 15 /* Set Transfer Ring Dequeue Pointer Command */ @@ -929,6 +940,7 @@ struct xhci_ring { unsigned int cancels_pending; unsigned int state; #define SET_DEQ_PENDING (1 << 0) +#define EP_HALTED (1 << 1) /* The TRB that was last reported in a stopped endpoint ring */ union xhci_trb *stopped_trb; struct xhci_td *stopped_td; @@ -940,9 +952,15 @@ struct xhci_ring { u32 cycle_state; }; +struct xhci_dequeue_state { + struct xhci_segment *new_deq_seg; + union xhci_trb *new_deq_ptr; + int new_cycle_state; +}; + struct xhci_erst_entry { /* 64-bit event ring segment address */ - u32 seg_addr[2]; + u64 seg_addr; u32 seg_size; /* Set to zero */ u32 rsvd; @@ -957,6 +975,13 @@ struct xhci_erst { unsigned int erst_size; }; +struct xhci_scratchpad { + u64 *sp_array; + dma_addr_t sp_dma; + void **sp_buffers; + dma_addr_t *sp_dma_buffers; +}; + /* * Each segment table entry is 4*32bits long. 1K seems like an ok size: * (1K bytes * 8bytes/bit) / (4*32 bits) = 64 segment entries in the table, @@ -1011,6 +1036,9 @@ struct xhci_hcd { struct xhci_ring *cmd_ring; struct xhci_ring *event_ring; struct xhci_erst erst; + /* Scratchpad */ + struct xhci_scratchpad *scratchpad; + /* slot enabling and address device helpers */ struct completion addr_dev; int slot_id; @@ -1071,13 +1099,43 @@ static inline unsigned int xhci_readl(const struct xhci_hcd *xhci, static inline void xhci_writel(struct xhci_hcd *xhci, const unsigned int val, __u32 __iomem *regs) { - if (!in_interrupt()) - xhci_dbg(xhci, - "`MEM_WRITE_DWORD(3'b000, 32'h%p, 32'h%0x, 4'hf);\n", - regs, val); + xhci_dbg(xhci, + "`MEM_WRITE_DWORD(3'b000, 32'h%p, 32'h%0x, 4'hf);\n", + regs, val); writel(val, regs); } +/* + * Registers should always be accessed with double word or quad word accesses. + * + * Some xHCI implementations may support 64-bit address pointers. Registers + * with 64-bit address pointers should be written to with dword accesses by + * writing the low dword first (ptr[0]), then the high dword (ptr[1]) second. + * xHCI implementations that do not support 64-bit address pointers will ignore + * the high dword, and write order is irrelevant. + */ +static inline u64 xhci_read_64(const struct xhci_hcd *xhci, + __u64 __iomem *regs) +{ + __u32 __iomem *ptr = (__u32 __iomem *) regs; + u64 val_lo = readl(ptr); + u64 val_hi = readl(ptr + 1); + return val_lo + (val_hi << 32); +} +static inline void xhci_write_64(struct xhci_hcd *xhci, + const u64 val, __u64 __iomem *regs) +{ + __u32 __iomem *ptr = (__u32 __iomem *) regs; + u32 val_lo = lower_32_bits(val); + u32 val_hi = upper_32_bits(val); + + xhci_dbg(xhci, + "`MEM_WRITE_DWORD(3'b000, 64'h%p, 64'h%0lx, 4'hf);\n", + regs, (long unsigned int) val); + writel(val_lo, ptr); + writel(val_hi, ptr + 1); +} + /* xHCI debugging */ void xhci_print_ir_set(struct xhci_hcd *xhci, struct xhci_intr_reg *ir_set, int set_num); void xhci_print_registers(struct xhci_hcd *xhci); @@ -1090,7 +1148,7 @@ void xhci_debug_ring(struct xhci_hcd *xhci, struct xhci_ring *ring); void xhci_dbg_erst(struct xhci_hcd *xhci, struct xhci_erst *erst); void xhci_dbg_cmd_ptrs(struct xhci_hcd *xhci); void xhci_dbg_ring_ptrs(struct xhci_hcd *xhci, struct xhci_ring *ring); -void xhci_dbg_ctx(struct xhci_hcd *xhci, struct xhci_device_control *ctx, dma_addr_t dma, unsigned int last_ep); +void xhci_dbg_ctx(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx, unsigned int last_ep); /* xHCI memory managment */ void xhci_mem_cleanup(struct xhci_hcd *xhci); @@ -1128,6 +1186,7 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags); int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status); int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev, struct usb_host_endpoint *ep); int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev, struct usb_host_endpoint *ep); +void xhci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep); int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev); void xhci_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev); @@ -1148,10 +1207,23 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct urb *urb, int slot_id, unsigned int ep_index); int xhci_queue_configure_endpoint(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr, u32 slot_id); +int xhci_queue_reset_ep(struct xhci_hcd *xhci, int slot_id, + unsigned int ep_index); +void xhci_find_new_dequeue_state(struct xhci_hcd *xhci, + unsigned int slot_id, unsigned int ep_index, + struct xhci_td *cur_td, struct xhci_dequeue_state *state); +void xhci_queue_new_dequeue_state(struct xhci_hcd *xhci, + struct xhci_ring *ep_ring, unsigned int slot_id, + unsigned int ep_index, struct xhci_dequeue_state *deq_state); /* xHCI roothub code */ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength); int xhci_hub_status_data(struct usb_hcd *hcd, char *buf); +/* xHCI contexts */ +struct xhci_input_control_ctx *xhci_get_input_control_ctx(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx); +struct xhci_slot_ctx *xhci_get_slot_ctx(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx); +struct xhci_ep_ctx *xhci_get_ep_ctx(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx, unsigned int ep_index); + #endif /* __LINUX_XHCI_HCD_H */ diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig index a68d91a11be..abe3aa67ed0 100644 --- a/drivers/usb/misc/Kconfig +++ b/drivers/usb/misc/Kconfig @@ -220,7 +220,7 @@ config USB_IOWARRIOR config USB_TEST tristate "USB testing driver" - depends on USB && USB_DEVICEFS + depends on USB help This driver is for testing host controller software. It is used with specialized device firmware for regression and stress testing, diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 554a414f65d..c7c1ca0494c 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1326,7 +1326,6 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb) int i; /* log core options (read using indexed model) */ - musb_ep_select(mbase, 0); reg = musb_read_configdata(mbase); strcpy(aInfo, (reg & MUSB_CONFIGDATA_UTMIDW) ? "UTMI-16" : "UTMI-8"); @@ -1990,7 +1989,7 @@ bad_config: if (status < 0) goto fail2; -#ifdef CONFIG_USB_OTG +#ifdef CONFIG_USB_MUSB_OTG setup_timer(&musb->otg_timer, musb_otg_timer_func, (unsigned long) musb); #endif diff --git a/drivers/usb/musb/musb_gadget_ep0.c b/drivers/usb/musb/musb_gadget_ep0.c index 40ed50ecedf..7a6778675ad 100644 --- a/drivers/usb/musb/musb_gadget_ep0.c +++ b/drivers/usb/musb/musb_gadget_ep0.c @@ -407,7 +407,7 @@ stall: csr |= MUSB_RXCSR_P_SENDSTALL | MUSB_RXCSR_FLUSHFIFO | MUSB_RXCSR_CLRDATATOG - | MUSB_TXCSR_P_WZC_BITS; + | MUSB_RXCSR_P_WZC_BITS; musb_writew(regs, MUSB_RXCSR, csr); } diff --git a/drivers/usb/musb/musb_regs.h b/drivers/usb/musb/musb_regs.h index de3b2f18db4..fbfd3fd9ce1 100644 --- a/drivers/usb/musb/musb_regs.h +++ b/drivers/usb/musb/musb_regs.h @@ -323,6 +323,7 @@ static inline void musb_write_rxfifoadd(void __iomem *mbase, u16 c_off) static inline u8 musb_read_configdata(void __iomem *mbase) { + musb_writeb(mbase, MUSB_INDEX, 0); return musb_readb(mbase, 0x10 + MUSB_CONFIGDATA); } diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index e9a40b820fd..985cbcf48bd 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -80,6 +80,7 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(0x10C4, 0x80F6) }, /* Suunto sports instrument */ { USB_DEVICE(0x10C4, 0x8115) }, /* Arygon NFC/Mifare Reader */ { USB_DEVICE(0x10C4, 0x813D) }, /* Burnside Telecom Deskmobile */ + { USB_DEVICE(0x10C4, 0x813F) }, /* Tams Master Easy Control */ { USB_DEVICE(0x10C4, 0x814A) }, /* West Mountain Radio RIGblaster P&P */ { USB_DEVICE(0x10C4, 0x814B) }, /* West Mountain Radio RIGtalk */ { USB_DEVICE(0x10C4, 0x815E) }, /* Helicomm IP-Link 1220-DVM */ @@ -96,7 +97,9 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(0x10c4, 0x8293) }, /* Telegesys ETRX2USB */ { USB_DEVICE(0x10C4, 0x82F9) }, /* Procyon AVS */ { USB_DEVICE(0x10C4, 0x8341) }, /* Siemens MC35PU GPRS Modem */ + { USB_DEVICE(0x10C4, 0x8382) }, /* Cygnal Integrated Products, Inc. */ { USB_DEVICE(0x10C4, 0x83A8) }, /* Amber Wireless AMB2560 */ + { USB_DEVICE(0x10C4, 0x8411) }, /* Kyocera GPS Module */ { USB_DEVICE(0x10C4, 0x846E) }, /* BEI USB Sensor Interface (VCP) */ { USB_DEVICE(0x10C4, 0xEA60) }, /* Silicon Labs factory default */ { USB_DEVICE(0x10C4, 0xEA61) }, /* Silicon Labs factory default */ diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 60c64cc5be2..b574878c78b 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -698,6 +698,7 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(MARVELL_VID, MARVELL_SHEEVAPLUG_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, { USB_DEVICE(LARSENBRUSGAARD_VID, LB_ALTITRACK_PID) }, + { USB_DEVICE(GN_OTOMETRICS_VID, AURICAL_USB_PID) }, { }, /* Optional parameter entry */ { } /* Terminating entry */ }; diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h index c9fbd741509..24dbd99e87d 100644 --- a/drivers/usb/serial/ftdi_sio.h +++ b/drivers/usb/serial/ftdi_sio.h @@ -947,6 +947,13 @@ #define FTDI_TURTELIZER_PID 0xBDC8 /* JTAG/RS-232 adapter by egnite GmBH */ /* + * GN Otometrics (http://www.otometrics.com) + * Submitted by Ville Sundberg. + */ +#define GN_OTOMETRICS_VID 0x0c33 /* Vendor ID */ +#define AURICAL_USB_PID 0x0010 /* Aurical USB Audiometer */ + +/* * BmRequestType: 1100 0000b * bRequest: FTDI_E2_READ * wValue: 0 diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index bfc5ce000ef..ccd4dd340d2 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -521,7 +521,7 @@ static int mos7720_chars_in_buffer(struct tty_struct *tty) mos7720_port = usb_get_serial_port_data(port); if (mos7720_port == NULL) { dbg("%s:leaving ...........", __func__); - return -ENODEV; + return 0; } for (i = 0; i < NUM_URBS; ++i) { diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index c31940a307f..270009afdf7 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -124,10 +124,13 @@ #define BANDB_DEVICE_ID_USOPTL4_4 0xAC44 #define BANDB_DEVICE_ID_USOPTL4_2 0xAC42 -/* This driver also supports the ATEN UC2324 device since it is mos7840 based - * - if I knew the device id it would also support the ATEN UC2322 */ +/* This driver also supports + * ATEN UC2324 device using Moschip MCS7840 + * ATEN UC2322 device using Moschip MCS7820 + */ #define USB_VENDOR_ID_ATENINTL 0x0557 #define ATENINTL_DEVICE_ID_UC2324 0x2011 +#define ATENINTL_DEVICE_ID_UC2322 0x7820 /* Interrupt Routine Defines */ @@ -177,6 +180,7 @@ static struct usb_device_id moschip_port_id_table[] = { {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL4_4)}, {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL4_2)}, {USB_DEVICE(USB_VENDOR_ID_ATENINTL, ATENINTL_DEVICE_ID_UC2324)}, + {USB_DEVICE(USB_VENDOR_ID_ATENINTL, ATENINTL_DEVICE_ID_UC2322)}, {} /* terminating entry */ }; @@ -186,6 +190,7 @@ static __devinitdata struct usb_device_id moschip_id_table_combined[] = { {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL4_4)}, {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL4_2)}, {USB_DEVICE(USB_VENDOR_ID_ATENINTL, ATENINTL_DEVICE_ID_UC2324)}, + {USB_DEVICE(USB_VENDOR_ID_ATENINTL, ATENINTL_DEVICE_ID_UC2322)}, {} /* terminating entry */ }; diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 98262dd552b..c784ddbe7b6 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -66,8 +66,10 @@ static int option_tiocmget(struct tty_struct *tty, struct file *file); static int option_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); static int option_send_setup(struct usb_serial_port *port); +#ifdef CONFIG_PM static int option_suspend(struct usb_serial *serial, pm_message_t message); static int option_resume(struct usb_serial *serial); +#endif /* Vendor and product IDs */ #define OPTION_VENDOR_ID 0x0AF0 @@ -205,6 +207,7 @@ static int option_resume(struct usb_serial *serial); #define NOVATELWIRELESS_PRODUCT_MC727 0x4100 #define NOVATELWIRELESS_PRODUCT_MC950D 0x4400 #define NOVATELWIRELESS_PRODUCT_U727 0x5010 +#define NOVATELWIRELESS_PRODUCT_MC727_NEW 0x5100 #define NOVATELWIRELESS_PRODUCT_MC760 0x6000 #define NOVATELWIRELESS_PRODUCT_OVMC760 0x6002 @@ -259,11 +262,6 @@ static int option_resume(struct usb_serial *serial); #define AXESSTEL_VENDOR_ID 0x1726 #define AXESSTEL_PRODUCT_MV110H 0x1000 -#define ONDA_VENDOR_ID 0x19d2 -#define ONDA_PRODUCT_MSA501HS 0x0001 -#define ONDA_PRODUCT_ET502HS 0x0002 -#define ONDA_PRODUCT_MT503HS 0x2000 - #define BANDRICH_VENDOR_ID 0x1A8D #define BANDRICH_PRODUCT_C100_1 0x1002 #define BANDRICH_PRODUCT_C100_2 0x1003 @@ -301,6 +299,7 @@ static int option_resume(struct usb_serial *serial); #define ZTE_PRODUCT_MF628 0x0015 #define ZTE_PRODUCT_MF626 0x0031 #define ZTE_PRODUCT_CDMA_TECH 0xfffe +#define ZTE_PRODUCT_AC8710 0xfff1 #define BENQ_VENDOR_ID 0x04a5 #define BENQ_PRODUCT_H10 0x4068 @@ -322,6 +321,11 @@ static int option_resume(struct usb_serial *serial); #define ALINK_VENDOR_ID 0x1e0e #define ALINK_PRODUCT_3GU 0x9200 +/* ALCATEL PRODUCTS */ +#define ALCATEL_VENDOR_ID 0x1bbb +#define ALCATEL_PRODUCT_X060S 0x0000 + + static struct usb_device_id option_ids[] = { { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) }, @@ -438,6 +442,7 @@ static struct usb_device_id option_ids[] = { { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_EU870D) }, /* Novatel EU850D/EU860D/EU870D */ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_MC950D) }, /* Novatel MC930D/MC950D */ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_MC727) }, /* Novatel MC727/U727/USB727 */ + { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_MC727_NEW) }, /* Novatel MC727/U727/USB727 refresh */ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_U727) }, /* Novatel MC727/U727/USB727 */ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_MC760) }, /* Novatel MC760/U760/USB760 */ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_OVMC760) }, /* Novatel Ovation MC760 */ @@ -474,42 +479,6 @@ static struct usb_device_id option_ids[] = { { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ADU_500A) }, { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ADU_620UW) }, { USB_DEVICE(AXESSTEL_VENDOR_ID, AXESSTEL_PRODUCT_MV110H) }, - { USB_DEVICE(ONDA_VENDOR_ID, ONDA_PRODUCT_MSA501HS) }, - { USB_DEVICE(ONDA_VENDOR_ID, ONDA_PRODUCT_ET502HS) }, - { USB_DEVICE(ONDA_VENDOR_ID, 0x0003) }, - { USB_DEVICE(ONDA_VENDOR_ID, 0x0004) }, - { USB_DEVICE(ONDA_VENDOR_ID, 0x0005) }, - { USB_DEVICE(ONDA_VENDOR_ID, 0x0006) }, - { USB_DEVICE(ONDA_VENDOR_ID, 0x0007) }, - { USB_DEVICE(ONDA_VENDOR_ID, 0x0008) }, - { USB_DEVICE(ONDA_VENDOR_ID, 0x0009) }, - { USB_DEVICE(ONDA_VENDOR_ID, 0x000a) }, - { USB_DEVICE(ONDA_VENDOR_ID, 0x000b) }, - { USB_DEVICE(ONDA_VENDOR_ID, 0x000c) }, - { USB_DEVICE(ONDA_VENDOR_ID, 0x000d) }, - { USB_DEVICE(ONDA_VENDOR_ID, 0x000e) }, - { USB_DEVICE(ONDA_VENDOR_ID, 0x000f) }, - { USB_DEVICE(ONDA_VENDOR_ID, 0x0010) }, - { USB_DEVICE(ONDA_VENDOR_ID, 0x0011) }, - { USB_DEVICE(ONDA_VENDOR_ID, 0x0012) }, - { USB_DEVICE(ONDA_VENDOR_ID, 0x0013) }, - { USB_DEVICE(ONDA_VENDOR_ID, 0x0014) }, - { USB_DEVICE(ONDA_VENDOR_ID, 0x0015) }, - { USB_DEVICE(ONDA_VENDOR_ID, 0x0016) }, - { USB_DEVICE(ONDA_VENDOR_ID, 0x0017) }, - { USB_DEVICE(ONDA_VENDOR_ID, 0x0018) }, - { USB_DEVICE(ONDA_VENDOR_ID, 0x0019) }, - { USB_DEVICE(ONDA_VENDOR_ID, 0x0020) }, - { USB_DEVICE(ONDA_VENDOR_ID, 0x0021) }, - { USB_DEVICE(ONDA_VENDOR_ID, 0x0022) }, - { USB_DEVICE(ONDA_VENDOR_ID, 0x0023) }, - { USB_DEVICE(ONDA_VENDOR_ID, 0x0024) }, - { USB_DEVICE(ONDA_VENDOR_ID, 0x0025) }, - { USB_DEVICE(ONDA_VENDOR_ID, 0x0026) }, - { USB_DEVICE(ONDA_VENDOR_ID, 0x0027) }, - { USB_DEVICE(ONDA_VENDOR_ID, 0x0028) }, - { USB_DEVICE(ONDA_VENDOR_ID, 0x0029) }, - { USB_DEVICE(ONDA_VENDOR_ID, ONDA_PRODUCT_MT503HS) }, { USB_DEVICE(YISO_VENDOR_ID, YISO_PRODUCT_U893) }, { USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_C100_1) }, { USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_C100_2) }, @@ -534,10 +503,75 @@ static struct usb_device_id option_ids[] = { { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6613)}, /* Onda H600/ZTE MF330 */ { USB_DEVICE(MAXON_VENDOR_ID, 0x6280) }, /* BP3-USB & BP3-EXT HSDPA */ { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UC864E) }, - { USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_MF622) }, - { USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_MF626) }, - { USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_MF628) }, - { USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_CDMA_TECH) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF622, 0xff, 0xff, 0xff) }, /* ZTE WCDMA products */ + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0002, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0003, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0004, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0005, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0006, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0007, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0008, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0009, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x000a, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x000b, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x000c, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x000d, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x000e, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x000f, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0010, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0011, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0012, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0013, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF628, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0016, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0017, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0018, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0019, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0020, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0021, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0022, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0023, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0024, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0025, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0026, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0028, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0029, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0030, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF626, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0032, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0033, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0037, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0039, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0042, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0043, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0048, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0049, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0051, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0052, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0054, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0055, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0057, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0058, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0061, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0062, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0063, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0064, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0066, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0069, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0076, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0078, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0082, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0086, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x2002, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x2003, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0014, 0xff, 0xff, 0xff) }, /* ZTE CDMA products */ + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0027, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0059, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0060, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0070, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0073, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_CDMA_TECH, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AC8710, 0xff, 0xff, 0xff) }, { USB_DEVICE(BENQ_VENDOR_ID, BENQ_PRODUCT_H10) }, { USB_DEVICE(DLINK_VENDOR_ID, DLINK_PRODUCT_DWM_652) }, { USB_DEVICE(QISDA_VENDOR_ID, QISDA_PRODUCT_H21_4512) }, @@ -547,6 +581,7 @@ static struct usb_device_id option_ids[] = { { USB_DEVICE(TOSHIBA_VENDOR_ID, TOSHIBA_PRODUCT_HSDPA_MINICARD ) }, /* Toshiba 3G HSDPA == Novatel Expedite EU870D MiniCard */ { USB_DEVICE(ALINK_VENDOR_ID, 0x9000) }, { USB_DEVICE_AND_INTERFACE_INFO(ALINK_VENDOR_ID, ALINK_PRODUCT_3GU, 0xff, 0xff, 0xff) }, + { USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_X060S) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, option_ids); @@ -555,8 +590,10 @@ static struct usb_driver option_driver = { .name = "option", .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, +#ifdef CONFIG_PM .suspend = usb_serial_suspend, .resume = usb_serial_resume, +#endif .id_table = option_ids, .no_dynamic_id = 1, }; @@ -588,8 +625,10 @@ static struct usb_serial_driver option_1port_device = { .disconnect = option_disconnect, .release = option_release, .read_int_callback = option_instat_callback, +#ifdef CONFIG_PM .suspend = option_suspend, .resume = option_resume, +#endif }; static int debug; @@ -831,7 +870,6 @@ static void option_instat_callback(struct urb *urb) int status = urb->status; struct usb_serial_port *port = urb->context; struct option_port_private *portdata = usb_get_serial_port_data(port); - struct usb_serial *serial = port->serial; dbg("%s", __func__); dbg("%s: urb %p port %p has data %p", __func__, urb, port, portdata); @@ -927,7 +965,6 @@ static int option_open(struct tty_struct *tty, struct usb_serial_port *port, struct file *filp) { struct option_port_private *portdata; - struct usb_serial *serial = port->serial; int i, err; struct urb *urb; @@ -1187,6 +1224,7 @@ static void option_release(struct usb_serial *serial) } } +#ifdef CONFIG_PM static int option_suspend(struct usb_serial *serial, pm_message_t message) { dbg("%s entered", __func__); @@ -1245,6 +1283,7 @@ static int option_resume(struct usb_serial *serial) } return 0; } +#endif MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index 14971a92699..3bc609fe224 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -727,7 +727,7 @@ static int ti_write_room(struct tty_struct *tty) dbg("%s - port %d", __func__, port->number); if (tport == NULL) - return -ENODEV; + return 0; spin_lock_irqsave(&tport->tp_lock, flags); room = ti_buf_space_avail(tport->tp_write_buf); @@ -748,7 +748,7 @@ static int ti_chars_in_buffer(struct tty_struct *tty) dbg("%s - port %d", __func__, port->number); if (tport == NULL) - return -ENODEV; + return 0; spin_lock_irqsave(&tport->tp_lock, flags); chars = ti_buf_data_avail(tport->tp_write_buf); diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index bd7581b3a48..99188c92068 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -32,6 +32,7 @@ #include <linux/mutex.h> #include <linux/list.h> #include <linux/uaccess.h> +#include <linux/serial.h> #include <linux/usb.h> #include <linux/usb/serial.h> #include "pl2303.h" @@ -184,6 +185,7 @@ static int serial_open (struct tty_struct *tty, struct file *filp) struct usb_serial_port *port; unsigned int portNumber; int retval = 0; + int first = 0; dbg("%s", __func__); @@ -223,7 +225,7 @@ static int serial_open (struct tty_struct *tty, struct file *filp) /* If the console is attached, the device is already open */ if (port->port.count == 1 && !port->console) { - + first = 1; /* lock this module before we call it * this may fail, which means we must bail out, * safe because we are called with BKL held */ @@ -246,13 +248,21 @@ static int serial_open (struct tty_struct *tty, struct file *filp) if (retval) goto bailout_interface_put; mutex_unlock(&serial->disc_mutex); + set_bit(ASYNCB_INITIALIZED, &port->port.flags); } mutex_unlock(&port->mutex); /* Now do the correct tty layer semantics */ retval = tty_port_block_til_ready(&port->port, tty, filp); - if (retval == 0) + if (retval == 0) { + if (!first) + usb_serial_put(serial); return 0; - + } + mutex_lock(&port->mutex); + if (first == 0) + goto bailout_mutex_unlock; + /* Undo the initial port actions */ + mutex_lock(&serial->disc_mutex); bailout_interface_put: usb_autopm_put_interface(serial->interface); bailout_module_put: @@ -340,6 +350,22 @@ static void serial_close(struct tty_struct *tty, struct file *filp) dbg("%s - port %d", __func__, port->number); + /* FIXME: + This leaves a very narrow race. Really we should do the + serial_do_free() on tty->shutdown(), but tty->shutdown can + be called from IRQ context and serial_do_free can sleep. + + The right fix is probably to make the tty free (which is rare) + and thus tty->shutdown() occur via a work queue and simplify all + the drivers that use it. + */ + if (tty_hung_up_p(filp)) { + /* serial_hangup already called serial_down at this point. + Another user may have already reopened the port but + serial_do_free is refcounted */ + serial_do_free(port); + return; + } if (tty_port_close_start(&port->port, tty, filp) == 0) return; @@ -355,7 +381,8 @@ static void serial_hangup(struct tty_struct *tty) struct usb_serial_port *port = tty->driver_data; serial_do_down(port); tty_port_hangup(&port->port); - serial_do_free(port); + /* We must not free port yet - the USB serial layer depends on it's + continued existence */ } static int serial_write(struct tty_struct *tty, const unsigned char *buf, @@ -394,7 +421,6 @@ static int serial_chars_in_buffer(struct tty_struct *tty) struct usb_serial_port *port = tty->driver_data; dbg("%s = port %d", __func__, port->number); - WARN_ON(!port->port.count); /* if the device was unplugged then any remaining characters fell out of the connector ;) */ if (port->serial->disconnected) diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index fcb32021721..e20dc525d17 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -961,7 +961,7 @@ int usb_stor_Bulk_max_lun(struct us_data *us) US_BULK_GET_MAX_LUN, USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, - 0, us->ifnum, us->iobuf, 1, HZ); + 0, us->ifnum, us->iobuf, 1, 10*HZ); US_DEBUGP("GetMaxLUN command result is %d, data is %d\n", result, us->iobuf[0]); diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 8afcf08eba9..3b54b394017 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -1119,12 +1119,13 @@ config FB_CARILLO_RANCH config FB_INTEL tristate "Intel 830M/845G/852GM/855GM/865G/915G/945G/945GM/965G/965GM support (EXPERIMENTAL)" - depends on EXPERIMENTAL && FB && PCI && X86 && AGP_INTEL + depends on EXPERIMENTAL && FB && PCI && X86 && AGP_INTEL && EMBEDDED select FB_MODE_HELPERS select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT select FB_BOOT_VESA_SUPPORT if FB_INTEL = y + depends on !DRM_I915 help This driver supports the on-board graphics built in to the Intel 830M/845G/852GM/855GM/865G/915G/915GM/945G/945GM/965G/965GM chipsets. diff --git a/drivers/video/backlight/jornada720_bl.c b/drivers/video/backlight/jornada720_bl.c index c3ebb6b41ce..7aed2565c1b 100644 --- a/drivers/video/backlight/jornada720_bl.c +++ b/drivers/video/backlight/jornada720_bl.c @@ -72,7 +72,7 @@ static int jornada_bl_update_status(struct backlight_device *bd) if (jornada_ssp_byte(SETBRIGHTNESS) != TXDUMMY) { printk(KERN_INFO "bl : failed to set brightness\n"); ret = -ETIMEDOUT; - goto out + goto out; } /* at this point we expect that the mcu has accepted diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c index e641584e212..88716626744 100644 --- a/drivers/video/backlight/pwm_bl.c +++ b/drivers/video/backlight/pwm_bl.c @@ -145,6 +145,8 @@ static int pwm_backlight_suspend(struct platform_device *pdev, struct backlight_device *bl = platform_get_drvdata(pdev); struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev); + if (pb->notify) + pb->notify(0); pwm_config(pb->pwm, 0, pb->period); pwm_disable(pb->pwm); return 0; diff --git a/drivers/video/console/sticore.c b/drivers/video/console/sticore.c index ef7870f5ea0..857b3668b3b 100644 --- a/drivers/video/console/sticore.c +++ b/drivers/video/console/sticore.c @@ -957,9 +957,14 @@ static int __devinit sticore_pci_init(struct pci_dev *pd, #ifdef CONFIG_PCI unsigned long fb_base, rom_base; unsigned int fb_len, rom_len; + int err; struct sti_struct *sti; - pci_enable_device(pd); + err = pci_enable_device(pd); + if (err < 0) { + dev_err(&pd->dev, "Cannot enable PCI device\n"); + return err; + } fb_base = pci_resource_start(pd, 0); fb_len = pci_resource_len(pd, 0); @@ -1048,7 +1053,7 @@ static void __devinit sti_init_roms(void) /* Register drivers for native & PCI cards */ register_parisc_driver(&pa_sti_driver); - pci_register_driver(&pci_sti_driver); + WARN_ON(pci_register_driver(&pci_sti_driver)); /* if we didn't find the given default sti, take the first one */ if (!default_sti) diff --git a/drivers/video/fbmon.c b/drivers/video/fbmon.c index 5c1a2c01778..9ae9cd32bd0 100644 --- a/drivers/video/fbmon.c +++ b/drivers/video/fbmon.c @@ -256,8 +256,8 @@ static void fix_edid(unsigned char *edid, int fix) static int edid_checksum(unsigned char *edid) { - unsigned char i, csum = 0, all_null = 0; - int err = 0, fix = check_edid(edid); + unsigned char csum = 0, all_null = 0; + int i, err = 0, fix = check_edid(edid); if (fix) fix_edid(edid, fix); diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c index bb63c07e13d..5a72083dc67 100644 --- a/drivers/video/s3c-fb.c +++ b/drivers/video/s3c-fb.c @@ -964,7 +964,7 @@ 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++) + for (win = 0; win < S3C_FB_MAX_WIN; win++) if (sfb->windows[win]) s3c_fb_release_win(sfb, sfb->windows[win]); @@ -988,7 +988,7 @@ static int s3c_fb_suspend(struct platform_device *pdev, pm_message_t state) struct s3c_fb_win *win; int win_no; - for (win_no = S3C_FB_MAX_WIN; win_no >= 0; win_no--) { + for (win_no = S3C_FB_MAX_WIN - 1; win_no >= 0; win_no--) { win = sfb->windows[win_no]; if (!win) continue; diff --git a/drivers/virtio/virtio_pci.c b/drivers/virtio/virtio_pci.c index 193c8f0e5cc..248e00ec4dc 100644 --- a/drivers/virtio/virtio_pci.c +++ b/drivers/virtio/virtio_pci.c @@ -52,8 +52,10 @@ struct virtio_pci_device char (*msix_names)[256]; /* Number of available vectors */ unsigned msix_vectors; - /* Vectors allocated */ + /* Vectors allocated, excluding per-vq vectors if any */ unsigned msix_used_vectors; + /* Whether we have vector per vq */ + bool per_vq_vectors; }; /* Constants for MSI-X */ @@ -258,7 +260,6 @@ static void vp_free_vectors(struct virtio_device *vdev) for (i = 0; i < vp_dev->msix_used_vectors; ++i) free_irq(vp_dev->msix_entries[i].vector, vp_dev); - vp_dev->msix_used_vectors = 0; if (vp_dev->msix_enabled) { /* Disable the vector used for configuration */ @@ -267,80 +268,77 @@ static void vp_free_vectors(struct virtio_device *vdev) /* Flush the write out to device */ ioread16(vp_dev->ioaddr + VIRTIO_MSI_CONFIG_VECTOR); - vp_dev->msix_enabled = 0; pci_disable_msix(vp_dev->pci_dev); + vp_dev->msix_enabled = 0; + vp_dev->msix_vectors = 0; } -} -static int vp_enable_msix(struct pci_dev *dev, struct msix_entry *entries, - int *options, int noptions) -{ - int i; - for (i = 0; i < noptions; ++i) - if (!pci_enable_msix(dev, entries, options[i])) - return options[i]; - return -EBUSY; + vp_dev->msix_used_vectors = 0; + kfree(vp_dev->msix_names); + vp_dev->msix_names = NULL; + kfree(vp_dev->msix_entries); + vp_dev->msix_entries = NULL; } -static int vp_request_vectors(struct virtio_device *vdev, unsigned max_vqs) +static int vp_request_vectors(struct virtio_device *vdev, int nvectors, + bool per_vq_vectors) { struct virtio_pci_device *vp_dev = to_vp_device(vdev); const char *name = dev_name(&vp_dev->vdev.dev); unsigned i, v; int err = -ENOMEM; - /* We want at most one vector per queue and one for config changes. - * Fallback to separate vectors for config and a shared for queues. - * Finally fall back to regular interrupts. */ - int options[] = { max_vqs + 1, 2 }; - int nvectors = max(options[0], options[1]); + + if (!nvectors) { + /* Can't allocate MSI-X vectors, use regular interrupt */ + vp_dev->msix_vectors = 0; + err = request_irq(vp_dev->pci_dev->irq, vp_interrupt, + IRQF_SHARED, name, vp_dev); + if (err) + return err; + vp_dev->intx_enabled = 1; + return 0; + } vp_dev->msix_entries = kmalloc(nvectors * sizeof *vp_dev->msix_entries, GFP_KERNEL); if (!vp_dev->msix_entries) - goto error_entries; + goto error; vp_dev->msix_names = kmalloc(nvectors * sizeof *vp_dev->msix_names, GFP_KERNEL); if (!vp_dev->msix_names) - goto error_names; + goto error; for (i = 0; i < nvectors; ++i) vp_dev->msix_entries[i].entry = i; - err = vp_enable_msix(vp_dev->pci_dev, vp_dev->msix_entries, - options, ARRAY_SIZE(options)); - if (err < 0) { - /* Can't allocate enough MSI-X vectors, use regular interrupt */ - vp_dev->msix_vectors = 0; - err = request_irq(vp_dev->pci_dev->irq, vp_interrupt, - IRQF_SHARED, name, vp_dev); - if (err) - goto error_irq; - vp_dev->intx_enabled = 1; - } else { - vp_dev->msix_vectors = err; - vp_dev->msix_enabled = 1; - - /* Set the vector used for configuration */ - v = vp_dev->msix_used_vectors; - snprintf(vp_dev->msix_names[v], sizeof *vp_dev->msix_names, - "%s-config", name); - err = request_irq(vp_dev->msix_entries[v].vector, - vp_config_changed, 0, vp_dev->msix_names[v], - vp_dev); - if (err) - goto error_irq; - ++vp_dev->msix_used_vectors; - - iowrite16(v, vp_dev->ioaddr + VIRTIO_MSI_CONFIG_VECTOR); - /* Verify we had enough resources to assign the vector */ - v = ioread16(vp_dev->ioaddr + VIRTIO_MSI_CONFIG_VECTOR); - if (v == VIRTIO_MSI_NO_VECTOR) { - err = -EBUSY; - goto error_irq; - } + err = pci_enable_msix(vp_dev->pci_dev, vp_dev->msix_entries, nvectors); + if (err > 0) + err = -ENOSPC; + if (err) + goto error; + vp_dev->msix_vectors = nvectors; + vp_dev->msix_enabled = 1; + + /* Set the vector used for configuration */ + v = vp_dev->msix_used_vectors; + snprintf(vp_dev->msix_names[v], sizeof *vp_dev->msix_names, + "%s-config", name); + err = request_irq(vp_dev->msix_entries[v].vector, + vp_config_changed, 0, vp_dev->msix_names[v], + vp_dev); + if (err) + goto error; + ++vp_dev->msix_used_vectors; + + iowrite16(v, vp_dev->ioaddr + VIRTIO_MSI_CONFIG_VECTOR); + /* Verify we had enough resources to assign the vector */ + v = ioread16(vp_dev->ioaddr + VIRTIO_MSI_CONFIG_VECTOR); + if (v == VIRTIO_MSI_NO_VECTOR) { + err = -EBUSY; + goto error; } - if (vp_dev->msix_vectors && vp_dev->msix_vectors != max_vqs + 1) { + if (!per_vq_vectors) { /* Shared vector for all VQs */ v = vp_dev->msix_used_vectors; snprintf(vp_dev->msix_names[v], sizeof *vp_dev->msix_names, @@ -349,28 +347,25 @@ static int vp_request_vectors(struct virtio_device *vdev, unsigned max_vqs) vp_vring_interrupt, 0, vp_dev->msix_names[v], vp_dev); if (err) - goto error_irq; + goto error; ++vp_dev->msix_used_vectors; } return 0; -error_irq: +error: vp_free_vectors(vdev); - kfree(vp_dev->msix_names); -error_names: - kfree(vp_dev->msix_entries); -error_entries: return err; } static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index, void (*callback)(struct virtqueue *vq), - const char *name) + const char *name, + u16 vector) { struct virtio_pci_device *vp_dev = to_vp_device(vdev); struct virtio_pci_vq_info *info; struct virtqueue *vq; unsigned long flags, size; - u16 num, vector; + u16 num; int err; /* Select the queue we're interested in */ @@ -389,7 +384,7 @@ static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index, info->queue_index = index; info->num = num; - info->vector = VIRTIO_MSI_NO_VECTOR; + info->vector = vector; size = PAGE_ALIGN(vring_size(num, VIRTIO_PCI_VRING_ALIGN)); info->queue = alloc_pages_exact(size, GFP_KERNEL|__GFP_ZERO); @@ -413,22 +408,7 @@ static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index, vq->priv = info; info->vq = vq; - /* allocate per-vq vector if available and necessary */ - if (callback && vp_dev->msix_used_vectors < vp_dev->msix_vectors) { - vector = vp_dev->msix_used_vectors; - snprintf(vp_dev->msix_names[vector], sizeof *vp_dev->msix_names, - "%s-%s", dev_name(&vp_dev->vdev.dev), name); - err = request_irq(vp_dev->msix_entries[vector].vector, - vring_interrupt, 0, - vp_dev->msix_names[vector], vq); - if (err) - goto out_request_irq; - info->vector = vector; - ++vp_dev->msix_used_vectors; - } else - vector = VP_MSIX_VQ_VECTOR; - - if (callback && vp_dev->msix_enabled) { + if (vector != VIRTIO_MSI_NO_VECTOR) { iowrite16(vector, vp_dev->ioaddr + VIRTIO_MSI_QUEUE_VECTOR); vector = ioread16(vp_dev->ioaddr + VIRTIO_MSI_QUEUE_VECTOR); if (vector == VIRTIO_MSI_NO_VECTOR) { @@ -444,11 +424,6 @@ static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index, return vq; out_assign: - if (info->vector != VIRTIO_MSI_NO_VECTOR) { - free_irq(vp_dev->msix_entries[info->vector].vector, vq); - --vp_dev->msix_used_vectors; - } -out_request_irq: vring_del_virtqueue(vq); out_activate_queue: iowrite32(0, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN); @@ -462,12 +437,13 @@ static void vp_del_vq(struct virtqueue *vq) { struct virtio_pci_device *vp_dev = to_vp_device(vq->vdev); struct virtio_pci_vq_info *info = vq->priv; - unsigned long size; + unsigned long flags, size; - iowrite16(info->queue_index, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_SEL); + spin_lock_irqsave(&vp_dev->lock, flags); + list_del(&info->node); + spin_unlock_irqrestore(&vp_dev->lock, flags); - if (info->vector != VIRTIO_MSI_NO_VECTOR) - free_irq(vp_dev->msix_entries[info->vector].vector, vq); + iowrite16(info->queue_index, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_SEL); if (vp_dev->msix_enabled) { iowrite16(VIRTIO_MSI_NO_VECTOR, @@ -489,36 +465,62 @@ static void vp_del_vq(struct virtqueue *vq) /* the config->del_vqs() implementation */ static void vp_del_vqs(struct virtio_device *vdev) { + struct virtio_pci_device *vp_dev = to_vp_device(vdev); struct virtqueue *vq, *n; + struct virtio_pci_vq_info *info; - list_for_each_entry_safe(vq, n, &vdev->vqs, list) + list_for_each_entry_safe(vq, n, &vdev->vqs, list) { + info = vq->priv; + if (vp_dev->per_vq_vectors) + free_irq(vp_dev->msix_entries[info->vector].vector, vq); vp_del_vq(vq); + } + vp_dev->per_vq_vectors = false; vp_free_vectors(vdev); } -/* the config->find_vqs() implementation */ -static int vp_find_vqs(struct virtio_device *vdev, unsigned nvqs, - struct virtqueue *vqs[], - vq_callback_t *callbacks[], - const char *names[]) +static int vp_try_to_find_vqs(struct virtio_device *vdev, unsigned nvqs, + struct virtqueue *vqs[], + vq_callback_t *callbacks[], + const char *names[], + int nvectors, + bool per_vq_vectors) { - int vectors = 0; - int i, err; - - /* How many vectors would we like? */ - for (i = 0; i < nvqs; ++i) - if (callbacks[i]) - ++vectors; + struct virtio_pci_device *vp_dev = to_vp_device(vdev); + u16 vector; + int i, err, allocated_vectors; - err = vp_request_vectors(vdev, vectors); + err = vp_request_vectors(vdev, nvectors, per_vq_vectors); if (err) goto error_request; + vp_dev->per_vq_vectors = per_vq_vectors; + allocated_vectors = vp_dev->msix_used_vectors; for (i = 0; i < nvqs; ++i) { - vqs[i] = vp_find_vq(vdev, i, callbacks[i], names[i]); - if (IS_ERR(vqs[i])) + if (!callbacks[i] || !vp_dev->msix_enabled) + vector = VIRTIO_MSI_NO_VECTOR; + else if (vp_dev->per_vq_vectors) + vector = allocated_vectors++; + else + vector = VP_MSIX_VQ_VECTOR; + vqs[i] = vp_find_vq(vdev, i, callbacks[i], names[i], vector); + if (IS_ERR(vqs[i])) { + err = PTR_ERR(vqs[i]); goto error_find; + } + /* allocate per-vq irq if available and necessary */ + if (vp_dev->per_vq_vectors && vector != VIRTIO_MSI_NO_VECTOR) { + snprintf(vp_dev->msix_names[vector], sizeof *vp_dev->msix_names, + "%s-%s", dev_name(&vp_dev->vdev.dev), names[i]); + err = request_irq(vp_dev->msix_entries[vector].vector, + vring_interrupt, 0, + vp_dev->msix_names[vector], vqs[i]); + if (err) { + vp_del_vq(vqs[i]); + goto error_find; + } + } } return 0; @@ -526,7 +528,37 @@ error_find: vp_del_vqs(vdev); error_request: - return PTR_ERR(vqs[i]); + return err; +} + +/* the config->find_vqs() implementation */ +static int vp_find_vqs(struct virtio_device *vdev, unsigned nvqs, + struct virtqueue *vqs[], + vq_callback_t *callbacks[], + const char *names[]) +{ + int vectors = 0; + int i, uninitialized_var(err); + + /* How many vectors would we like? */ + for (i = 0; i < nvqs; ++i) + if (callbacks[i]) + ++vectors; + + /* We want at most one vector per queue and one for config changes. */ + err = vp_try_to_find_vqs(vdev, nvqs, vqs, callbacks, names, + vectors + 1, true); + if (!err) + return 0; + /* Fallback to separate vectors for config and a shared for queues. */ + err = vp_try_to_find_vqs(vdev, nvqs, vqs, callbacks, names, + 2, false); + if (!err) + return 0; + /* Finally fall back to regular interrupts. */ + err = vp_try_to_find_vqs(vdev, nvqs, vqs, callbacks, names, + 0, false); + return err; } static struct virtio_config_ops virtio_pci_config_ops = { @@ -669,7 +701,7 @@ static int __init virtio_pci_init(void) err = pci_register_driver(&virtio_pci_driver); if (err) - device_unregister(virtio_pci_root); + root_device_unregister(virtio_pci_root); return err; } diff --git a/drivers/watchdog/coh901327_wdt.c b/drivers/watchdog/coh901327_wdt.c index fecb307d28e..aec7cefdef2 100644 --- a/drivers/watchdog/coh901327_wdt.c +++ b/drivers/watchdog/coh901327_wdt.c @@ -18,6 +18,7 @@ #include <linux/bitops.h> #include <linux/uaccess.h> #include <linux/clk.h> +#include <linux/delay.h> #define DRV_NAME "WDOG COH 901 327" @@ -92,6 +93,8 @@ static struct clk *clk; static void coh901327_enable(u16 timeout) { u16 val; + unsigned long freq; + unsigned long delay_ns; clk_enable(clk); /* Restart timer if it is disabled */ @@ -102,6 +105,14 @@ static void coh901327_enable(u16 timeout) /* Acknowledge any pending interrupt so it doesn't just fire off */ writew(U300_WDOG_IER_WILL_BARK_IRQ_ACK_ENABLE, virtbase + U300_WDOG_IER); + /* + * The interrupt is cleared in the 32 kHz clock domain. + * Wait 3 32 kHz cycles for it to take effect + */ + freq = clk_get_rate(clk); + delay_ns = (1000000000 + freq - 1) / freq; /* Freq to ns and round up */ + delay_ns = 3 * delay_ns; /* Wait 3 cycles */ + ndelay(delay_ns); /* Enable the watchdog interrupt */ writew(U300_WDOG_IMR_WILL_BARK_IRQ_ENABLE, virtbase + U300_WDOG_IMR); /* Activate the watchdog timer */ diff --git a/drivers/watchdog/ep93xx_wdt.c b/drivers/watchdog/ep93xx_wdt.c index e9f950ff86e..cdd55e0d09f 100644 --- a/drivers/watchdog/ep93xx_wdt.c +++ b/drivers/watchdog/ep93xx_wdt.c @@ -29,6 +29,7 @@ #include <linux/watchdog.h> #include <linux/timer.h> #include <linux/uaccess.h> +#include <linux/io.h> #include <mach/hardware.h> #define WDT_VERSION "0.3" |