diff options
Diffstat (limited to 'drivers')
190 files changed, 9996 insertions, 775 deletions
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index a7799a99f2d..8a851d0f438 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -254,13 +254,6 @@ config ACPI_PCI_SLOT help you correlate PCI bus addresses with the physical geography of your slots. If you are unsure, say N. -config ACPI_SYSTEM - bool - default y - help - This driver will enable your system to shut down using ACPI, and - dump your ACPI DSDT table using /proc/acpi/dsdt. - config X86_PM_TIMER bool "Power Management Timer Support" if EMBEDDED depends on X86 diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 65d90c720b5..b130ea0d075 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -52,7 +52,7 @@ obj-$(CONFIG_ACPI_PROCESSOR) += processor.o obj-$(CONFIG_ACPI_CONTAINER) += container.o obj-$(CONFIG_ACPI_THERMAL) += thermal.o obj-y += power.o -obj-$(CONFIG_ACPI_SYSTEM) += system.o event.o +obj-y += system.o event.o obj-$(CONFIG_ACPI_DEBUG) += debug.o obj-$(CONFIG_ACPI_NUMA) += numa.o obj-$(CONFIG_ACPI_HOTPLUG_MEMORY) += acpi_memhotplug.o diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index 65132f92045..69cbc57c2d1 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -138,6 +138,29 @@ static int acpi_battery_technology(struct acpi_battery *battery) static int acpi_battery_get_state(struct acpi_battery *battery); +static int acpi_battery_is_charged(struct acpi_battery *battery) +{ + /* either charging or discharging */ + if (battery->state != 0) + return 0; + + /* battery not reporting charge */ + if (battery->capacity_now == ACPI_BATTERY_VALUE_UNKNOWN || + battery->capacity_now == 0) + return 0; + + /* good batteries update full_charge as the batteries degrade */ + if (battery->full_charge_capacity == battery->capacity_now) + return 1; + + /* fallback to using design values for broken batteries */ + if (battery->design_capacity == battery->capacity_now) + return 1; + + /* we don't do any sort of metric based on percentages */ + return 0; +} + static int acpi_battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) @@ -155,7 +178,7 @@ static int acpi_battery_get_property(struct power_supply *psy, val->intval = POWER_SUPPLY_STATUS_DISCHARGING; else if (battery->state & 0x02) val->intval = POWER_SUPPLY_STATUS_CHARGING; - else if (battery->state == 0) + else if (acpi_battery_is_charged(battery)) val->intval = POWER_SUPPLY_STATUS_FULL; else val->intval = POWER_SUPPLY_STATUS_UNKNOWN; diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 5c2f5d343be..2fe15060dcd 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -120,6 +120,8 @@ static struct acpi_ec { spinlock_t curr_lock; } *boot_ec, *first_ec; +static int EC_FLAGS_MSI; /* Out-of-spec MSI controller */ + /* -------------------------------------------------------------------------- Transaction Management -------------------------------------------------------------------------- */ @@ -259,6 +261,8 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, clear_bit(EC_FLAGS_GPE_MODE, &ec->flags); acpi_disable_gpe(NULL, ec->gpe); } + if (EC_FLAGS_MSI) + udelay(ACPI_EC_DELAY); /* start transaction */ spin_lock_irqsave(&ec->curr_lock, tmp); /* following two actions should be kept atomic */ @@ -967,6 +971,11 @@ int __init acpi_ec_ecdt_probe(void) /* * Generate a boot ec context */ + if (dmi_name_in_vendors("Micro-Star") || + dmi_name_in_vendors("Notebook")) { + pr_info(PREFIX "Enabling special treatment for EC from MSI.\n"); + EC_FLAGS_MSI = 1; + } status = acpi_get_table(ACPI_SIG_ECDT, 1, (struct acpi_table_header **)&ecdt_ptr); if (ACPI_SUCCESS(status)) { diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c index 0b299b0f817..714cb046b59 100644 --- a/drivers/ata/libata-sff.c +++ b/drivers/ata/libata-sff.c @@ -773,18 +773,32 @@ unsigned int ata_sff_data_xfer32(struct ata_device *dev, unsigned char *buf, else iowrite32_rep(data_addr, buf, words); + /* Transfer trailing bytes, if any */ if (unlikely(slop)) { - __le32 pad; + unsigned char pad[4]; + + /* Point buf to the tail of buffer */ + buf += buflen - slop; + + /* + * Use io*_rep() accessors here as well to avoid pointlessly + * swapping bytes to and fro on the big endian machines... + */ if (rw == READ) { - pad = cpu_to_le32(ioread32(ap->ioaddr.data_addr)); - memcpy(buf + buflen - slop, &pad, slop); + if (slop < 3) + ioread16_rep(data_addr, pad, 1); + else + ioread32_rep(data_addr, pad, 1); + memcpy(buf, pad, slop); } else { - memcpy(&pad, buf + buflen - slop, slop); - iowrite32(le32_to_cpu(pad), ap->ioaddr.data_addr); + memcpy(pad, buf, slop); + if (slop < 3) + iowrite16_rep(data_addr, pad, 1); + else + iowrite32_rep(data_addr, pad, 1); } - words++; } - return words << 2; + return (buflen + 1) & ~1; } EXPORT_SYMBOL_GPL(ata_sff_data_xfer32); diff --git a/drivers/ata/pata_amd.c b/drivers/ata/pata_amd.c index 63719ab9ea4..115b1cd6dcf 100644 --- a/drivers/ata/pata_amd.c +++ b/drivers/ata/pata_amd.c @@ -24,7 +24,7 @@ #include <linux/libata.h> #define DRV_NAME "pata_amd" -#define DRV_VERSION "0.3.11" +#define DRV_VERSION "0.4.1" /** * timing_setup - shared timing computation and load @@ -145,6 +145,13 @@ static int amd_pre_reset(struct ata_link *link, unsigned long deadline) return ata_sff_prereset(link, deadline); } +/** + * amd_cable_detect - report cable type + * @ap: port + * + * AMD controller/BIOS setups record the cable type in word 0x42 + */ + static int amd_cable_detect(struct ata_port *ap) { static const u32 bitmask[2] = {0x03, 0x0C}; @@ -158,6 +165,40 @@ static int amd_cable_detect(struct ata_port *ap) } /** + * amd_fifo_setup - set the PIO FIFO for ATA/ATAPI + * @ap: ATA interface + * @adev: ATA device + * + * Set the PCI fifo for this device according to the devices present + * on the bus at this point in time. We need to turn the post write buffer + * off for ATAPI devices as we may need to issue a word sized write to the + * device as the final I/O + */ + +static void amd_fifo_setup(struct ata_port *ap) +{ + struct ata_device *adev; + struct pci_dev *pdev = to_pci_dev(ap->host->dev); + static const u8 fifobit[2] = { 0xC0, 0x30}; + u8 fifo = fifobit[ap->port_no]; + u8 r; + + + ata_for_each_dev(adev, &ap->link, ENABLED) { + if (adev->class == ATA_DEV_ATAPI) + fifo = 0; + } + if (pdev->device == PCI_DEVICE_ID_AMD_VIPER_7411) /* FIFO is broken */ + fifo = 0; + + /* On the later chips the read prefetch bits become no-op bits */ + pci_read_config_byte(pdev, 0x41, &r); + r &= ~fifobit[ap->port_no]; + r |= fifo; + pci_write_config_byte(pdev, 0x41, r); +} + +/** * amd33_set_piomode - set initial PIO mode data * @ap: ATA interface * @adev: ATA device @@ -167,21 +208,25 @@ static int amd_cable_detect(struct ata_port *ap) static void amd33_set_piomode(struct ata_port *ap, struct ata_device *adev) { + amd_fifo_setup(ap); timing_setup(ap, adev, 0x40, adev->pio_mode, 1); } static void amd66_set_piomode(struct ata_port *ap, struct ata_device *adev) { + amd_fifo_setup(ap); timing_setup(ap, adev, 0x40, adev->pio_mode, 2); } static void amd100_set_piomode(struct ata_port *ap, struct ata_device *adev) { + amd_fifo_setup(ap); timing_setup(ap, adev, 0x40, adev->pio_mode, 3); } static void amd133_set_piomode(struct ata_port *ap, struct ata_device *adev) { + amd_fifo_setup(ap); timing_setup(ap, adev, 0x40, adev->pio_mode, 4); } @@ -397,6 +442,16 @@ static struct ata_port_operations nv133_port_ops = { .set_dmamode = nv133_set_dmamode, }; +static void amd_clear_fifo(struct pci_dev *pdev) +{ + u8 fifo; + /* Disable the FIFO, the FIFO logic will re-enable it as + appropriate */ + pci_read_config_byte(pdev, 0x41, &fifo); + fifo &= 0x0F; + pci_write_config_byte(pdev, 0x41, fifo); +} + static int amd_init_one(struct pci_dev *pdev, const struct pci_device_id *id) { static const struct ata_port_info info[10] = { @@ -503,14 +558,8 @@ static int amd_init_one(struct pci_dev *pdev, const struct pci_device_id *id) if (type < 3) ata_pci_bmdma_clear_simplex(pdev); - - /* Check for AMD7411 */ - if (type == 3) - /* FIFO is broken */ - pci_write_config_byte(pdev, 0x41, fifo & 0x0F); - else - pci_write_config_byte(pdev, 0x41, fifo | 0xF0); - + if (pdev->vendor == PCI_VENDOR_ID_AMD) + amd_clear_fifo(pdev); /* Cable detection on Nvidia chips doesn't work too well, * cache BIOS programmed UDMA mode. */ @@ -536,18 +585,11 @@ static int amd_reinit_one(struct pci_dev *pdev) return rc; if (pdev->vendor == PCI_VENDOR_ID_AMD) { - u8 fifo; - pci_read_config_byte(pdev, 0x41, &fifo); - if (pdev->device == PCI_DEVICE_ID_AMD_VIPER_7411) - /* FIFO is broken */ - pci_write_config_byte(pdev, 0x41, fifo & 0x0F); - else - pci_write_config_byte(pdev, 0x41, fifo | 0xF0); + amd_clear_fifo(pdev); if (pdev->device == PCI_DEVICE_ID_AMD_VIPER_7409 || pdev->device == PCI_DEVICE_ID_AMD_COBRA_7401) ata_pci_bmdma_clear_simplex(pdev); } - ata_host_resume(host); return 0; } diff --git a/drivers/ata/pata_it821x.c b/drivers/ata/pata_it821x.c index f1bb2f9fecb..b05b86a912c 100644 --- a/drivers/ata/pata_it821x.c +++ b/drivers/ata/pata_it821x.c @@ -557,6 +557,9 @@ static unsigned int it821x_read_id(struct ata_device *adev, id[83] |= 0x4400; /* Word 83 is valid and LBA48 */ id[86] |= 0x0400; /* LBA48 on */ id[ATA_ID_MAJOR_VER] |= 0x1F; + /* Clear the serial number because it's different each boot + which breaks validation on resume */ + memset(&id[ATA_ID_SERNO], 0x20, ATA_ID_SERNO_LEN); } return err_mask; } diff --git a/drivers/ata/pata_legacy.c b/drivers/ata/pata_legacy.c index 6c1d778b63a..e3bc1b43628 100644 --- a/drivers/ata/pata_legacy.c +++ b/drivers/ata/pata_legacy.c @@ -283,9 +283,10 @@ static void pdc20230_set_piomode(struct ata_port *ap, struct ata_device *adev) static unsigned int pdc_data_xfer_vlb(struct ata_device *dev, unsigned char *buf, unsigned int buflen, int rw) { - if (ata_id_has_dword_io(dev->id)) { + int slop = buflen & 3; + /* 32bit I/O capable *and* we need to write a whole number of dwords */ + if (ata_id_has_dword_io(dev->id) && (slop == 0 || slop == 3)) { struct ata_port *ap = dev->link->ap; - int slop = buflen & 3; unsigned long flags; local_irq_save(flags); @@ -735,7 +736,7 @@ static unsigned int vlb32_data_xfer(struct ata_device *adev, unsigned char *buf, struct ata_port *ap = adev->link->ap; int slop = buflen & 3; - if (ata_id_has_dword_io(adev->id)) { + if (ata_id_has_dword_io(adev->id) && (slop == 0 || slop == 3)) { if (rw == WRITE) iowrite32_rep(ap->ioaddr.data_addr, buf, buflen >> 2); else diff --git a/drivers/ata/pata_via.c b/drivers/ata/pata_via.c index 79a6c9a0b72..ba556d3e696 100644 --- a/drivers/ata/pata_via.c +++ b/drivers/ata/pata_via.c @@ -110,7 +110,8 @@ static const struct via_isa_bridge { { "vt8237s", PCI_DEVICE_ID_VIA_8237S, 0x00, 0x2f, VIA_UDMA_133 | VIA_BAD_AST }, { "vt8251", PCI_DEVICE_ID_VIA_8251, 0x00, 0x2f, VIA_UDMA_133 | VIA_BAD_AST }, { "cx700", PCI_DEVICE_ID_VIA_CX700, 0x00, 0x2f, VIA_UDMA_133 | VIA_BAD_AST | VIA_SATA_PATA }, - { "vt6410", PCI_DEVICE_ID_VIA_6410, 0x00, 0x2f, VIA_UDMA_133 | VIA_BAD_AST | VIA_NO_ENABLES}, + { "vt6410", PCI_DEVICE_ID_VIA_6410, 0x00, 0x2f, VIA_UDMA_133 | VIA_BAD_AST | VIA_NO_ENABLES }, + { "vt6415", PCI_DEVICE_ID_VIA_6415, 0x00, 0x2f, VIA_UDMA_133 | VIA_BAD_AST | VIA_NO_ENABLES }, { "vt8237a", PCI_DEVICE_ID_VIA_8237A, 0x00, 0x2f, VIA_UDMA_133 | VIA_BAD_AST }, { "vt8237", PCI_DEVICE_ID_VIA_8237, 0x00, 0x2f, VIA_UDMA_133 | VIA_BAD_AST }, { "vt8235", PCI_DEVICE_ID_VIA_8235, 0x00, 0x2f, VIA_UDMA_133 | VIA_BAD_AST }, @@ -593,6 +594,7 @@ static int via_reinit_one(struct pci_dev *pdev) #endif static const struct pci_device_id via[] = { + { PCI_VDEVICE(VIA, 0x0415), }, { PCI_VDEVICE(VIA, 0x0571), }, { PCI_VDEVICE(VIA, 0x0581), }, { PCI_VDEVICE(VIA, 0x1571), }, diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index 4ae1a4138b4..7007edd2d45 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -3114,19 +3114,17 @@ static int mv_init_host(struct ata_host *host, unsigned int board_idx) writelfl(0, hc_mmio + HC_IRQ_CAUSE_OFS); } - if (!IS_SOC(hpriv)) { - /* Clear any currently outstanding host interrupt conditions */ - writelfl(0, mmio + hpriv->irq_cause_ofs); + /* Clear any currently outstanding host interrupt conditions */ + writelfl(0, mmio + hpriv->irq_cause_ofs); - /* and unmask interrupt generation for host regs */ - writelfl(hpriv->unmask_all_irqs, mmio + hpriv->irq_mask_ofs); + /* and unmask interrupt generation for host regs */ + writelfl(hpriv->unmask_all_irqs, mmio + hpriv->irq_mask_ofs); - /* - * enable only global host interrupts for now. - * The per-port interrupts get done later as ports are set up. - */ - mv_set_main_irq_mask(host, 0, PCI_ERR); - } + /* + * enable only global host interrupts for now. + * The per-port interrupts get done later as ports are set up. + */ + mv_set_main_irq_mask(host, 0, PCI_ERR); done: return rc; } diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c index 444af0415ca..55a8eed3f3a 100644 --- a/drivers/ata/sata_nv.c +++ b/drivers/ata/sata_nv.c @@ -421,19 +421,21 @@ static struct ata_port_operations nv_generic_ops = { .hardreset = ATA_OP_NULL, }; -/* OSDL bz3352 reports that nf2/3 controllers can't determine device - * signature reliably. Also, the following thread reports detection - * failure on cold boot with the standard debouncing timing. +/* nf2 is ripe with hardreset related problems. + * + * kernel bz#3352 reports nf2/3 controllers can't determine device + * signature reliably. The following thread reports detection failure + * on cold boot with the standard debouncing timing. * * http://thread.gmane.org/gmane.linux.ide/34098 * - * Debounce with hotplug timing and request follow-up SRST. + * And bz#12176 reports that hardreset simply doesn't work on nf2. + * Give up on it and just don't do hardreset. */ static struct ata_port_operations nv_nf2_ops = { - .inherits = &nv_common_ops, + .inherits = &nv_generic_ops, .freeze = nv_nf2_freeze, .thaw = nv_nf2_thaw, - .hardreset = nv_noclassify_hardreset, }; /* For initial probing after boot and hot plugging, hardreset mostly diff --git a/drivers/atm/lanai.c b/drivers/atm/lanai.c index 144a49f1522..8733a2ea04c 100644 --- a/drivers/atm/lanai.c +++ b/drivers/atm/lanai.c @@ -901,7 +901,7 @@ static int __devinit eeprom_read(struct lanai_dev *lanai) clock_l(); udelay(5); for (i = 128; i != 0; i >>= 1) { /* write command out */ tmp = (lanai->conf1 & ~CONFIG1_PROMDATA) | - (data & i) ? CONFIG1_PROMDATA : 0; + ((data & i) ? CONFIG1_PROMDATA : 0); if (lanai->conf1 != tmp) { set_config1(tmp); udelay(5); /* Let new data settle */ diff --git a/drivers/base/base.h b/drivers/base/base.h index 0a5f055dffb..9f50f1b545d 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -88,8 +88,6 @@ extern void driver_detach(struct device_driver *drv); extern int driver_probe_device(struct device_driver *drv, struct device *dev); extern void sysdev_shutdown(void); -extern int sysdev_suspend(pm_message_t state); -extern int sysdev_resume(void); extern char *make_class_name(const char *name, struct kobject *kobj); diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 315bed8d5e7..13523123910 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -18,9 +18,11 @@ */ #include <linux/device.h> +#include <linux/delay.h> #include <linux/module.h> #include <linux/kthread.h> #include <linux/wait.h> +#include <linux/async.h> #include "base.h" #include "power/power.h" @@ -168,6 +170,21 @@ int driver_probe_done(void) } /** + * wait_for_device_probe + * Wait for device probing to be completed. + * + * Note: this function polls at 100 msec intervals. + */ +int wait_for_device_probe(void) +{ + /* wait for the known devices to complete their probing */ + while (driver_probe_done() != 0) + msleep(100); + async_synchronize_full(); + return 0; +} + +/** * driver_probe_device - attempt to bind device & driver together * @drv: driver to bind a device to * @dev: device to try to bind to the driver diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 670c9d6c140..2d14f4ae6c0 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -333,7 +333,6 @@ static void dpm_power_up(pm_message_t state) */ void device_power_up(pm_message_t state) { - sysdev_resume(); dpm_power_up(state); } EXPORT_SYMBOL_GPL(device_power_up); @@ -577,8 +576,6 @@ int device_power_down(pm_message_t state) } dev->power.status = DPM_OFF_IRQ; } - if (!error) - error = sysdev_suspend(state); if (error) dpm_power_up(resume_event(state)); return error; diff --git a/drivers/base/sys.c b/drivers/base/sys.c index c98c31ec2f7..b428c8c4bc6 100644 --- a/drivers/base/sys.c +++ b/drivers/base/sys.c @@ -303,7 +303,6 @@ void sysdev_unregister(struct sys_device * sysdev) * is guaranteed by virtue of the fact that child devices are registered * after their parents. */ - void sysdev_shutdown(void) { struct sysdev_class * cls; @@ -363,7 +362,6 @@ static void __sysdev_resume(struct sys_device *dev) * This is only called by the device PM core, so we let them handle * all synchronization. */ - int sysdev_suspend(pm_message_t state) { struct sysdev_class * cls; @@ -432,7 +430,7 @@ aux_driver: } return ret; } - +EXPORT_SYMBOL_GPL(sysdev_suspend); /** * sysdev_resume - Bring system devices back to life. @@ -442,7 +440,6 @@ aux_driver: * * Note: Interrupts are disabled when called. */ - int sysdev_resume(void) { struct sysdev_class * cls; @@ -463,7 +460,7 @@ int sysdev_resume(void) } return 0; } - +EXPORT_SYMBOL_GPL(sysdev_resume); int __init system_bus_init(void) { diff --git a/drivers/block/aoe/aoe.h b/drivers/block/aoe/aoe.h index c237527b1aa..5e41e6dd657 100644 --- a/drivers/block/aoe/aoe.h +++ b/drivers/block/aoe/aoe.h @@ -18,6 +18,7 @@ enum { AOECMD_ATA, AOECMD_CFG, + AOECMD_VEND_MIN = 0xf0, AOEFL_RSP = (1<<3), AOEFL_ERR = (1<<2), diff --git a/drivers/block/aoe/aoenet.c b/drivers/block/aoe/aoenet.c index 30de5b1c647..c6099ba9a4b 100644 --- a/drivers/block/aoe/aoenet.c +++ b/drivers/block/aoe/aoenet.c @@ -142,6 +142,8 @@ aoenet_rcv(struct sk_buff *skb, struct net_device *ifp, struct packet_type *pt, aoecmd_cfg_rsp(skb); break; default: + if (h->cmd >= AOECMD_VEND_MIN) + break; /* don't complain about vendor commands */ printk(KERN_INFO "aoe: unknown cmd %d\n", h->cmd); } exit: diff --git a/drivers/block/ataflop.c b/drivers/block/ataflop.c index 69e1df7dfa1..4234c11c1e4 100644 --- a/drivers/block/ataflop.c +++ b/drivers/block/ataflop.c @@ -1730,7 +1730,7 @@ static int __init fd_test_drive_present( int drive ) timeout = jiffies + 2*HZ+HZ/2; while (time_before(jiffies, timeout)) - if (!(mfp.par_dt_reg & 0x20)) + if (!(st_mfp.par_dt_reg & 0x20)) break; status = FDC_READ( FDCREG_STATUS ); @@ -1747,7 +1747,7 @@ static int __init fd_test_drive_present( int drive ) /* dummy seek command to make WP bit accessible */ FDC_WRITE( FDCREG_DATA, 0 ); FDC_WRITE( FDCREG_CMD, FDCCMD_SEEK ); - while( mfp.par_dt_reg & 0x20 ) + while( st_mfp.par_dt_reg & 0x20 ) ; status = FDC_READ( FDCREG_STATUS ); } diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index 01e69383d9c..b5a06111463 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -3390,6 +3390,203 @@ static void free_hba(int i) kfree(p); } +/* Send a message CDB to the firmware. */ +static __devinit int cciss_message(struct pci_dev *pdev, unsigned char opcode, unsigned char type) +{ + typedef struct { + CommandListHeader_struct CommandHeader; + RequestBlock_struct Request; + ErrDescriptor_struct ErrorDescriptor; + } Command; + static const size_t cmd_sz = sizeof(Command) + sizeof(ErrorInfo_struct); + Command *cmd; + dma_addr_t paddr64; + uint32_t paddr32, tag; + void __iomem *vaddr; + int i, err; + + vaddr = ioremap_nocache(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); + if (vaddr == NULL) + return -ENOMEM; + + /* The Inbound Post Queue only accepts 32-bit physical addresses for the + CCISS commands, so they must be allocated from the lower 4GiB of + memory. */ + err = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK); + if (err) { + iounmap(vaddr); + return -ENOMEM; + } + + cmd = pci_alloc_consistent(pdev, cmd_sz, &paddr64); + if (cmd == NULL) { + iounmap(vaddr); + return -ENOMEM; + } + + /* This must fit, because of the 32-bit consistent DMA mask. Also, + although there's no guarantee, we assume that the address is at + least 4-byte aligned (most likely, it's page-aligned). */ + paddr32 = paddr64; + + cmd->CommandHeader.ReplyQueue = 0; + cmd->CommandHeader.SGList = 0; + cmd->CommandHeader.SGTotal = 0; + cmd->CommandHeader.Tag.lower = paddr32; + cmd->CommandHeader.Tag.upper = 0; + memset(&cmd->CommandHeader.LUN.LunAddrBytes, 0, 8); + + cmd->Request.CDBLen = 16; + cmd->Request.Type.Type = TYPE_MSG; + cmd->Request.Type.Attribute = ATTR_HEADOFQUEUE; + cmd->Request.Type.Direction = XFER_NONE; + cmd->Request.Timeout = 0; /* Don't time out */ + cmd->Request.CDB[0] = opcode; + cmd->Request.CDB[1] = type; + memset(&cmd->Request.CDB[2], 0, 14); /* the rest of the CDB is reserved */ + + cmd->ErrorDescriptor.Addr.lower = paddr32 + sizeof(Command); + cmd->ErrorDescriptor.Addr.upper = 0; + cmd->ErrorDescriptor.Len = sizeof(ErrorInfo_struct); + + writel(paddr32, vaddr + SA5_REQUEST_PORT_OFFSET); + + for (i = 0; i < 10; i++) { + tag = readl(vaddr + SA5_REPLY_PORT_OFFSET); + if ((tag & ~3) == paddr32) + break; + schedule_timeout_uninterruptible(HZ); + } + + iounmap(vaddr); + + /* we leak the DMA buffer here ... no choice since the controller could + still complete the command. */ + if (i == 10) { + printk(KERN_ERR "cciss: controller message %02x:%02x timed out\n", + opcode, type); + return -ETIMEDOUT; + } + + pci_free_consistent(pdev, cmd_sz, cmd, paddr64); + + if (tag & 2) { + printk(KERN_ERR "cciss: controller message %02x:%02x failed\n", + opcode, type); + return -EIO; + } + + printk(KERN_INFO "cciss: controller message %02x:%02x succeeded\n", + opcode, type); + return 0; +} + +#define cciss_soft_reset_controller(p) cciss_message(p, 1, 0) +#define cciss_noop(p) cciss_message(p, 3, 0) + +static __devinit int cciss_reset_msi(struct pci_dev *pdev) +{ +/* the #defines are stolen from drivers/pci/msi.h. */ +#define msi_control_reg(base) (base + PCI_MSI_FLAGS) +#define PCI_MSIX_FLAGS_ENABLE (1 << 15) + + int pos; + u16 control = 0; + + pos = pci_find_capability(pdev, PCI_CAP_ID_MSI); + if (pos) { + pci_read_config_word(pdev, msi_control_reg(pos), &control); + if (control & PCI_MSI_FLAGS_ENABLE) { + printk(KERN_INFO "cciss: resetting MSI\n"); + pci_write_config_word(pdev, msi_control_reg(pos), control & ~PCI_MSI_FLAGS_ENABLE); + } + } + + pos = pci_find_capability(pdev, PCI_CAP_ID_MSIX); + if (pos) { + pci_read_config_word(pdev, msi_control_reg(pos), &control); + if (control & PCI_MSIX_FLAGS_ENABLE) { + printk(KERN_INFO "cciss: resetting MSI-X\n"); + pci_write_config_word(pdev, msi_control_reg(pos), control & ~PCI_MSIX_FLAGS_ENABLE); + } + } + + return 0; +} + +/* This does a hard reset of the controller using PCI power management + * states. */ +static __devinit int cciss_hard_reset_controller(struct pci_dev *pdev) +{ + u16 pmcsr, saved_config_space[32]; + int i, pos; + + printk(KERN_INFO "cciss: using PCI PM to reset controller\n"); + + /* This is very nearly the same thing as + + pci_save_state(pci_dev); + pci_set_power_state(pci_dev, PCI_D3hot); + pci_set_power_state(pci_dev, PCI_D0); + pci_restore_state(pci_dev); + + but we can't use these nice canned kernel routines on + kexec, because they also check the MSI/MSI-X state in PCI + configuration space and do the wrong thing when it is + set/cleared. Also, the pci_save/restore_state functions + violate the ordering requirements for restoring the + configuration space from the CCISS document (see the + comment below). So we roll our own .... */ + + for (i = 0; i < 32; i++) + pci_read_config_word(pdev, 2*i, &saved_config_space[i]); + + pos = pci_find_capability(pdev, PCI_CAP_ID_PM); + if (pos == 0) { + printk(KERN_ERR "cciss_reset_controller: PCI PM not supported\n"); + return -ENODEV; + } + + /* Quoting from the Open CISS Specification: "The Power + * Management Control/Status Register (CSR) controls the power + * state of the device. The normal operating state is D0, + * CSR=00h. The software off state is D3, CSR=03h. To reset + * the controller, place the interface device in D3 then to + * D0, this causes a secondary PCI reset which will reset the + * controller." */ + + /* enter the D3hot power management state */ + pci_read_config_word(pdev, pos + PCI_PM_CTRL, &pmcsr); + pmcsr &= ~PCI_PM_CTRL_STATE_MASK; + pmcsr |= PCI_D3hot; + pci_write_config_word(pdev, pos + PCI_PM_CTRL, pmcsr); + + schedule_timeout_uninterruptible(HZ >> 1); + + /* enter the D0 power management state */ + pmcsr &= ~PCI_PM_CTRL_STATE_MASK; + pmcsr |= PCI_D0; + pci_write_config_word(pdev, pos + PCI_PM_CTRL, pmcsr); + + schedule_timeout_uninterruptible(HZ >> 1); + + /* Restore the PCI configuration space. The Open CISS + * Specification says, "Restore the PCI Configuration + * Registers, offsets 00h through 60h. It is important to + * restore the command register, 16-bits at offset 04h, + * last. Do not restore the configuration status register, + * 16-bits at offset 06h." Note that the offset is 2*i. */ + for (i = 0; i < 32; i++) { + if (i == 2 || i == 3) + continue; + pci_write_config_word(pdev, 2*i, saved_config_space[i]); + } + wmb(); + pci_write_config_word(pdev, 4, saved_config_space[2]); + + return 0; +} + /* * This is it. Find all the controllers and register them. I really hate * stealing all these major device numbers. @@ -3404,6 +3601,28 @@ static int __devinit cciss_init_one(struct pci_dev *pdev, int dac, return_code; InquiryData_struct *inq_buff = NULL; + if (reset_devices) { + /* Reset the controller with a PCI power-cycle */ + if (cciss_hard_reset_controller(pdev) || cciss_reset_msi(pdev)) + return -ENODEV; + + /* Some devices (notably the HP Smart Array 5i Controller) + need a little pause here */ + schedule_timeout_uninterruptible(30*HZ); + + /* Now try to get the controller to respond to a no-op */ + for (i=0; i<30; i++) { + if (cciss_noop(pdev) == 0) + break; + + schedule_timeout_uninterruptible(HZ); + } + if (i == 30) { + printk(KERN_ERR "cciss: controller seems dead\n"); + return -EBUSY; + } + } + i = alloc_cciss_hba(); if (i < 0) return -1; diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index cf29cc4e6ab..83d8ed39433 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -558,6 +558,8 @@ static void process_fd_request(void); static void recalibrate_floppy(void); static void floppy_shutdown(unsigned long); +static int floppy_request_regions(int); +static void floppy_release_regions(int); static int floppy_grab_irq_and_dma(void); static void floppy_release_irq_and_dma(void); @@ -4274,8 +4276,7 @@ static int __init floppy_init(void) FDCS->rawcmd = 2; if (user_reset_fdc(-1, FD_RESET_ALWAYS, 0)) { /* free ioports reserved by floppy_grab_irq_and_dma() */ - release_region(FDCS->address + 2, 4); - release_region(FDCS->address + 7, 1); + floppy_release_regions(fdc); FDCS->address = -1; FDCS->version = FDC_NONE; continue; @@ -4284,8 +4285,7 @@ static int __init floppy_init(void) FDCS->version = get_fdc_version(); if (FDCS->version == FDC_NONE) { /* free ioports reserved by floppy_grab_irq_and_dma() */ - release_region(FDCS->address + 2, 4); - release_region(FDCS->address + 7, 1); + floppy_release_regions(fdc); FDCS->address = -1; continue; } @@ -4358,6 +4358,47 @@ out_put_disk: static DEFINE_SPINLOCK(floppy_usage_lock); +static const struct io_region { + int offset; + int size; +} io_regions[] = { + { 2, 1 }, + /* address + 3 is sometimes reserved by pnp bios for motherboard */ + { 4, 2 }, + /* address + 6 is reserved, and may be taken by IDE. + * Unfortunately, Adaptec doesn't know this :-(, */ + { 7, 1 }, +}; + +static void floppy_release_allocated_regions(int fdc, const struct io_region *p) +{ + while (p != io_regions) { + p--; + release_region(FDCS->address + p->offset, p->size); + } +} + +#define ARRAY_END(X) (&((X)[ARRAY_SIZE(X)])) + +static int floppy_request_regions(int fdc) +{ + const struct io_region *p; + + for (p = io_regions; p < ARRAY_END(io_regions); p++) { + if (!request_region(FDCS->address + p->offset, p->size, "floppy")) { + DPRINT("Floppy io-port 0x%04lx in use\n", FDCS->address + p->offset); + floppy_release_allocated_regions(fdc, p); + return -EBUSY; + } + } + return 0; +} + +static void floppy_release_regions(int fdc) +{ + floppy_release_allocated_regions(fdc, ARRAY_END(io_regions)); +} + static int floppy_grab_irq_and_dma(void) { unsigned long flags; @@ -4399,18 +4440,8 @@ static int floppy_grab_irq_and_dma(void) for (fdc = 0; fdc < N_FDC; fdc++) { if (FDCS->address != -1) { - if (!request_region(FDCS->address + 2, 4, "floppy")) { - DPRINT("Floppy io-port 0x%04lx in use\n", - FDCS->address + 2); - goto cleanup1; - } - if (!request_region(FDCS->address + 7, 1, "floppy DIR")) { - DPRINT("Floppy io-port 0x%04lx in use\n", - FDCS->address + 7); - goto cleanup2; - } - /* address + 6 is reserved, and may be taken by IDE. - * Unfortunately, Adaptec doesn't know this :-(, */ + if (floppy_request_regions(fdc)) + goto cleanup; } } for (fdc = 0; fdc < N_FDC; fdc++) { @@ -4432,15 +4463,11 @@ static int floppy_grab_irq_and_dma(void) fdc = 0; irqdma_allocated = 1; return 0; -cleanup2: - release_region(FDCS->address + 2, 4); -cleanup1: +cleanup: fd_free_irq(); fd_free_dma(); - while (--fdc >= 0) { - release_region(FDCS->address + 2, 4); - release_region(FDCS->address + 7, 1); - } + while (--fdc >= 0) + floppy_release_regions(fdc); spin_lock_irqsave(&floppy_usage_lock, flags); usage_count--; spin_unlock_irqrestore(&floppy_usage_lock, flags); @@ -4501,10 +4528,8 @@ static void floppy_release_irq_and_dma(void) #endif old_fdc = fdc; for (fdc = 0; fdc < N_FDC; fdc++) - if (FDCS->address != -1) { - release_region(FDCS->address + 2, 4); - release_region(FDCS->address + 7, 1); - } + if (FDCS->address != -1) + floppy_release_regions(fdc); fdc = old_fdc; } diff --git a/drivers/block/paride/pg.c b/drivers/block/paride/pg.c index 9dfa2716300..c397b3ddba9 100644 --- a/drivers/block/paride/pg.c +++ b/drivers/block/paride/pg.c @@ -422,7 +422,7 @@ static void xs(char *buf, char *targ, int len) for (k = 0; k < len; k++) { char c = *buf++; - if (c != ' ' || c != l) + if (c != ' ' && c != l) l = *targ++ = c; } if (l == ' ') diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c index 918ef725de4..b6c8ce25435 100644 --- a/drivers/block/xen-blkfront.c +++ b/drivers/block/xen-blkfront.c @@ -40,6 +40,7 @@ #include <linux/hdreg.h> #include <linux/cdrom.h> #include <linux/module.h> +#include <linux/scatterlist.h> #include <xen/xenbus.h> #include <xen/grant_table.h> @@ -82,6 +83,7 @@ struct blkfront_info enum blkif_state connected; int ring_ref; struct blkif_front_ring ring; + struct scatterlist sg[BLKIF_MAX_SEGMENTS_PER_REQUEST]; unsigned int evtchn, irq; struct request_queue *rq; struct work_struct work; @@ -204,12 +206,11 @@ static int blkif_queue_request(struct request *req) struct blkfront_info *info = req->rq_disk->private_data; unsigned long buffer_mfn; struct blkif_request *ring_req; - struct req_iterator iter; - struct bio_vec *bvec; unsigned long id; unsigned int fsect, lsect; - int ref; + int i, ref; grant_ref_t gref_head; + struct scatterlist *sg; if (unlikely(info->connected != BLKIF_STATE_CONNECTED)) return 1; @@ -238,12 +239,13 @@ static int blkif_queue_request(struct request *req) if (blk_barrier_rq(req)) ring_req->operation = BLKIF_OP_WRITE_BARRIER; - ring_req->nr_segments = 0; - rq_for_each_segment(bvec, req, iter) { - BUG_ON(ring_req->nr_segments == BLKIF_MAX_SEGMENTS_PER_REQUEST); - buffer_mfn = pfn_to_mfn(page_to_pfn(bvec->bv_page)); - fsect = bvec->bv_offset >> 9; - lsect = fsect + (bvec->bv_len >> 9) - 1; + ring_req->nr_segments = blk_rq_map_sg(req->q, req, info->sg); + BUG_ON(ring_req->nr_segments > BLKIF_MAX_SEGMENTS_PER_REQUEST); + + for_each_sg(info->sg, sg, ring_req->nr_segments, i) { + buffer_mfn = pfn_to_mfn(page_to_pfn(sg_page(sg))); + fsect = sg->offset >> 9; + lsect = fsect + (sg->length >> 9) - 1; /* install a grant reference. */ ref = gnttab_claim_grant_reference(&gref_head); BUG_ON(ref == -ENOSPC); @@ -254,16 +256,12 @@ static int blkif_queue_request(struct request *req) buffer_mfn, rq_data_dir(req) ); - info->shadow[id].frame[ring_req->nr_segments] = - mfn_to_pfn(buffer_mfn); - - ring_req->seg[ring_req->nr_segments] = + info->shadow[id].frame[i] = mfn_to_pfn(buffer_mfn); + ring_req->seg[i] = (struct blkif_request_segment) { .gref = ref, .first_sect = fsect, .last_sect = lsect }; - - ring_req->nr_segments++; } info->ring.req_prod_pvt++; @@ -622,6 +620,8 @@ static int setup_blkring(struct xenbus_device *dev, SHARED_RING_INIT(sring); FRONT_RING_INIT(&info->ring, sring, PAGE_SIZE); + sg_init_table(info->sg, BLKIF_MAX_SEGMENTS_PER_REQUEST); + err = xenbus_grant_ring(dev, virt_to_mfn(info->ring.sring)); if (err < 0) { free_page((unsigned long)sring); diff --git a/drivers/char/scc.h b/drivers/char/scc.h index 93998f5baff..341b1142bea 100644 --- a/drivers/char/scc.h +++ b/drivers/char/scc.h @@ -387,7 +387,7 @@ struct scc_port { /* The SCC needs 3.5 PCLK cycles recovery time between to register * accesses. PCLK runs with 8 MHz on an Atari, so this delay is 3.5 * * 125 ns = 437.5 ns. This is too short for udelay(). - * 10/16/95: A tstb mfp.par_dt_reg takes 600ns (sure?) and thus should be + * 10/16/95: A tstb st_mfp.par_dt_reg takes 600ns (sure?) and thus should be * quite right */ diff --git a/drivers/char/sx.c b/drivers/char/sx.c index f146e90404f..518f2a25d91 100644 --- a/drivers/char/sx.c +++ b/drivers/char/sx.c @@ -1746,9 +1746,10 @@ static long sx_fw_ioctl(struct file *filp, unsigned int cmd, sx_dprintk(SX_DEBUG_FIRMWARE, "returning type= %ld\n", rc); break; case SXIO_DO_RAMTEST: - if (sx_initialized) /* Already initialized: better not ramtest the board. */ + if (sx_initialized) { /* Already initialized: better not ramtest the board. */ rc = -EPERM; break; + } if (IS_SX_BOARD(board)) { rc = do_memtest(board, 0, 0x7000); if (!rc) @@ -1788,7 +1789,7 @@ static long sx_fw_ioctl(struct file *filp, unsigned int cmd, nbytes - i : SX_CHUNK_SIZE)) { kfree(tmp); rc = -EFAULT; - break; + goto out; } memcpy_toio(board->base2 + offset + i, tmp, (i + SX_CHUNK_SIZE > nbytes) ? diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index a58993011ed..280a9d263eb 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -518,6 +518,7 @@ struct dma_chan *__dma_request_channel(dma_cap_mask_t *mask, dma_filter_fn fn, v dma_chan_name(chan), err); else break; + chan->private = NULL; chan = NULL; } } @@ -536,6 +537,7 @@ void dma_release_channel(struct dma_chan *chan) WARN_ONCE(chan->client_count != 1, "chan reference count %d != 1\n", chan->client_count); dma_chan_put(chan); + chan->private = NULL; mutex_unlock(&dma_list_mutex); } EXPORT_SYMBOL_GPL(dma_release_channel); diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 6b702cc46b3..a97c07eef7e 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -560,7 +560,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, unsigned long flags) { struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - struct dw_dma_slave *dws = dwc->dws; + struct dw_dma_slave *dws = chan->private; struct dw_desc *prev; struct dw_desc *first; u32 ctllo; @@ -790,7 +790,7 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) cfghi = DWC_CFGH_FIFO_MODE; cfglo = 0; - dws = dwc->dws; + dws = chan->private; if (dws) { /* * We need controller-specific data to set up slave @@ -866,7 +866,6 @@ static void dwc_free_chan_resources(struct dma_chan *chan) spin_lock_bh(&dwc->lock); list_splice_init(&dwc->free_list, &list); dwc->descs_allocated = 0; - dwc->dws = NULL; /* Disable interrupts */ channel_clear_bit(dw, MASK.XFER, dwc->mask); diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h index 00fdd187bb0..b252b202c5c 100644 --- a/drivers/dma/dw_dmac_regs.h +++ b/drivers/dma/dw_dmac_regs.h @@ -139,8 +139,6 @@ struct dw_dma_chan { struct list_head queue; struct list_head free_list; - struct dw_dma_slave *dws; - unsigned int descs_allocated; }; diff --git a/drivers/firmware/memmap.c b/drivers/firmware/memmap.c index 261b9aa3f24..05aa2d406ac 100644 --- a/drivers/firmware/memmap.c +++ b/drivers/firmware/memmap.c @@ -1,7 +1,7 @@ /* * linux/drivers/firmware/memmap.c * Copyright (C) 2008 SUSE LINUX Products GmbH - * by Bernhard Walle <bwalle@suse.de> + * by Bernhard Walle <bernhard.walle@gmx.de> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License v2.0 as published by diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 4be3acbaaf9..3a22eb9be37 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -80,18 +80,17 @@ config DRM_I915 XFree86 4.4 and above. If unsure, build this and i830 as modules and the X server will load the correct one. -endchoice - config DRM_I915_KMS bool "Enable modesetting on intel by default" depends on DRM_I915 help - Choose this option if you want kernel modesetting enabled by default, - and you have a new enough userspace to support this. Running old - userspaces with this enabled will cause pain. Note that this causes - the driver to bind to PCI devices, which precludes loading things - like intelfb. + Choose this option if you want kernel modesetting enabled by default, + and you have a new enough userspace to support this. Running old + userspaces with this enabled will cause pain. Note that this causes + the driver to bind to PCI devices, which precludes loading things + like intelfb. +endchoice config DRM_MGA tristate "Matrox g200/g400" diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index bfce0992fef..94a76887173 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -1741,9 +1741,8 @@ out: * RETURNS: * Zero on success, errno on failure. */ -void drm_fb_release(struct file *filp) +void drm_fb_release(struct drm_file *priv) { - struct drm_file *priv = filp->private_data; struct drm_device *dev = priv->minor->dev; struct drm_framebuffer *fb, *tfb; diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index 964c5eb1fad..1c3a8c55714 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -452,6 +452,59 @@ static void drm_setup_crtcs(struct drm_device *dev) kfree(modes); kfree(enabled); } + +/** + * drm_encoder_crtc_ok - can a given crtc drive a given encoder? + * @encoder: encoder to test + * @crtc: crtc to test + * + * Return false if @encoder can't be driven by @crtc, true otherwise. + */ +static bool drm_encoder_crtc_ok(struct drm_encoder *encoder, + struct drm_crtc *crtc) +{ + struct drm_device *dev; + struct drm_crtc *tmp; + int crtc_mask = 1; + + WARN(!crtc, "checking null crtc?"); + + dev = crtc->dev; + + list_for_each_entry(tmp, &dev->mode_config.crtc_list, head) { + if (tmp == crtc) + break; + crtc_mask <<= 1; + } + + if (encoder->possible_crtcs & crtc_mask) + return true; + return false; +} + +/* + * Check the CRTC we're going to map each output to vs. its current + * CRTC. If they don't match, we have to disable the output and the CRTC + * since the driver will have to re-route things. + */ +static void +drm_crtc_prepare_encoders(struct drm_device *dev) +{ + struct drm_encoder_helper_funcs *encoder_funcs; + struct drm_encoder *encoder; + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + encoder_funcs = encoder->helper_private; + /* Disable unused encoders */ + if (encoder->crtc == NULL) + (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF); + /* Disable encoders whose CRTC is about to change */ + if (encoder_funcs->get_crtc && + encoder->crtc != (*encoder_funcs->get_crtc)(encoder)) + (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF); + } +} + /** * drm_crtc_set_mode - set a mode * @crtc: CRTC to program @@ -512,8 +565,8 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, if (drm_mode_equal(&saved_mode, &crtc->mode)) { if (saved_x != crtc->x || saved_y != crtc->y || depth_changed || bpp_changed) { - crtc_funcs->mode_set_base(crtc, crtc->x, crtc->y, - old_fb); + ret = !crtc_funcs->mode_set_base(crtc, crtc->x, crtc->y, + old_fb); goto done; } } @@ -547,12 +600,16 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, encoder_funcs->prepare(encoder); } + drm_crtc_prepare_encoders(dev); + crtc_funcs->prepare(crtc); /* Set up the DPLL and any encoders state that needs to adjust or depend * on the DPLL. */ - crtc_funcs->mode_set(crtc, mode, adjusted_mode, x, y, old_fb); + ret = !crtc_funcs->mode_set(crtc, mode, adjusted_mode, x, y, old_fb); + if (!ret) + goto done; list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { @@ -615,7 +672,7 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) struct drm_device *dev; struct drm_crtc **save_crtcs, *new_crtc; struct drm_encoder **save_encoders, *new_encoder; - struct drm_framebuffer *old_fb; + struct drm_framebuffer *old_fb = NULL; bool save_enabled; bool mode_changed = false; bool fb_changed = false; @@ -666,9 +723,10 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) * and then just flip_or_move it */ if (set->crtc->fb != set->fb) { /* If we have no fb then treat it as a full mode set */ - if (set->crtc->fb == NULL) + if (set->crtc->fb == NULL) { + DRM_DEBUG("crtc has no fb, full mode set\n"); mode_changed = true; - else if ((set->fb->bits_per_pixel != + } else if ((set->fb->bits_per_pixel != set->crtc->fb->bits_per_pixel) || set->fb->depth != set->crtc->fb->depth) fb_changed = true; @@ -680,7 +738,7 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) fb_changed = true; if (set->mode && !drm_mode_equal(set->mode, &set->crtc->mode)) { - DRM_DEBUG("modes are different\n"); + DRM_DEBUG("modes are different, full mode set\n"); drm_mode_debug_printmodeline(&set->crtc->mode); drm_mode_debug_printmodeline(set->mode); mode_changed = true; @@ -706,6 +764,7 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) } if (new_encoder != connector->encoder) { + DRM_DEBUG("encoder changed, full mode switch\n"); mode_changed = true; connector->encoder = new_encoder; } @@ -732,10 +791,20 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) if (set->connectors[ro] == connector) new_crtc = set->crtc; } + + /* Make sure the new CRTC will work with the encoder */ + if (new_crtc && + !drm_encoder_crtc_ok(connector->encoder, new_crtc)) { + ret = -EINVAL; + goto fail_set_mode; + } if (new_crtc != connector->encoder->crtc) { + DRM_DEBUG("crtc changed, full mode switch\n"); mode_changed = true; connector->encoder->crtc = new_crtc; } + DRM_DEBUG("setting connector %d crtc to %p\n", + connector->base.id, new_crtc); } /* mode_set_base is not a required function */ @@ -752,6 +821,8 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) if (!drm_crtc_helper_set_mode(set->crtc, set->mode, set->x, set->y, old_fb)) { + DRM_ERROR("failed to set mode on crtc %p\n", + set->crtc); ret = -EINVAL; goto fail_set_mode; } @@ -765,7 +836,10 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) old_fb = set->crtc->fb; if (set->crtc->fb != set->fb) set->crtc->fb = set->fb; - crtc_funcs->mode_set_base(set->crtc, set->x, set->y, old_fb); + ret = crtc_funcs->mode_set_base(set->crtc, + set->x, set->y, old_fb); + if (ret != 0) + goto fail_set_mode; } kfree(save_encoders); @@ -774,9 +848,14 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) fail_set_mode: set->crtc->enabled = save_enabled; + set->crtc->fb = old_fb; count = 0; - list_for_each_entry(connector, &dev->mode_config.connector_list, head) + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + if (!connector->encoder) + continue; + connector->encoder->crtc = save_crtcs[count++]; + } fail_no_encoder: kfree(save_crtcs); count = 0; diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 5a4d3244758..a839a28d8ee 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -125,7 +125,7 @@ static bool edid_is_valid(struct edid *edid) DRM_ERROR("EDID has major version %d, instead of 1\n", edid->version); goto bad; } - if (edid->revision <= 0 || edid->revision > 3) { + if (edid->revision > 3) { DRM_ERROR("EDID has minor version %d, which is not between 0-3\n", edid->revision); goto bad; } @@ -320,10 +320,10 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev, mode->htotal = mode->hdisplay + ((pt->hblank_hi << 8) | pt->hblank_lo); mode->vdisplay = (pt->vactive_hi << 8) | pt->vactive_lo; - mode->vsync_start = mode->vdisplay + ((pt->vsync_offset_hi << 8) | + mode->vsync_start = mode->vdisplay + ((pt->vsync_offset_hi << 4) | pt->vsync_offset_lo); mode->vsync_end = mode->vsync_start + - ((pt->vsync_pulse_width_hi << 8) | + ((pt->vsync_pulse_width_hi << 4) | pt->vsync_pulse_width_lo); mode->vtotal = mode->vdisplay + ((pt->vblank_hi << 8) | pt->vblank_lo); diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index b06a5371585..6c020fe5431 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -457,6 +457,9 @@ int drm_release(struct inode *inode, struct file *filp) if (dev->driver->driver_features & DRIVER_GEM) drm_gem_release(dev, file_priv); + if (dev->driver->driver_features & DRIVER_MODESET) + drm_fb_release(file_priv); + mutex_lock(&dev->ctxlist_mutex); if (!list_empty(&dev->ctxlist)) { struct drm_ctx_list *pos, *n; diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 6915fb82d0b..88d3368ffdd 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -104,8 +104,8 @@ drm_gem_init(struct drm_device *dev) if (drm_mm_init(&mm->offset_manager, DRM_FILE_PAGE_OFFSET_START, DRM_FILE_PAGE_OFFSET_SIZE)) { - drm_free(mm, sizeof(struct drm_gem_mm), DRM_MEM_MM); drm_ht_remove(&mm->offset_hash); + drm_free(mm, sizeof(struct drm_gem_mm), DRM_MEM_MM); return -ENOMEM; } @@ -295,35 +295,37 @@ drm_gem_flink_ioctl(struct drm_device *dev, void *data, return -EBADF; again: - if (idr_pre_get(&dev->object_name_idr, GFP_KERNEL) == 0) - return -ENOMEM; + if (idr_pre_get(&dev->object_name_idr, GFP_KERNEL) == 0) { + ret = -ENOMEM; + goto err; + } spin_lock(&dev->object_name_lock); - if (obj->name) { - args->name = obj->name; + if (!obj->name) { + ret = idr_get_new_above(&dev->object_name_idr, obj, 1, + &obj->name); + args->name = (uint64_t) obj->name; spin_unlock(&dev->object_name_lock); - return 0; - } - ret = idr_get_new_above(&dev->object_name_idr, obj, 1, - &obj->name); - spin_unlock(&dev->object_name_lock); - if (ret == -EAGAIN) - goto again; - if (ret != 0) { - mutex_lock(&dev->struct_mutex); - drm_gem_object_unreference(obj); - mutex_unlock(&dev->struct_mutex); - return ret; - } + if (ret == -EAGAIN) + goto again; - /* - * Leave the reference from the lookup around as the - * name table now holds one - */ - args->name = (uint64_t) obj->name; + if (ret != 0) + goto err; - return 0; + /* Allocate a reference for the name table. */ + drm_gem_object_reference(obj); + } else { + args->name = (uint64_t) obj->name; + spin_unlock(&dev->object_name_lock); + ret = 0; + } + +err: + mutex_lock(&dev->struct_mutex); + drm_gem_object_unreference(obj); + mutex_unlock(&dev->struct_mutex); + return ret; } /** @@ -448,6 +450,7 @@ drm_gem_object_handle_free(struct kref *kref) spin_lock(&dev->object_name_lock); if (obj->name) { idr_remove(&dev->object_name_idr, obj->name); + obj->name = 0; spin_unlock(&dev->object_name_lock); /* * The object name held a reference to this object, drop @@ -460,6 +463,26 @@ drm_gem_object_handle_free(struct kref *kref) } EXPORT_SYMBOL(drm_gem_object_handle_free); +void drm_gem_vm_open(struct vm_area_struct *vma) +{ + struct drm_gem_object *obj = vma->vm_private_data; + + drm_gem_object_reference(obj); +} +EXPORT_SYMBOL(drm_gem_vm_open); + +void drm_gem_vm_close(struct vm_area_struct *vma) +{ + struct drm_gem_object *obj = vma->vm_private_data; + struct drm_device *dev = obj->dev; + + mutex_lock(&dev->struct_mutex); + drm_gem_object_unreference(obj); + mutex_unlock(&dev->struct_mutex); +} +EXPORT_SYMBOL(drm_gem_vm_close); + + /** * drm_gem_mmap - memory map routine for GEM objects * @filp: DRM file pointer @@ -521,6 +544,14 @@ int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) #endif vma->vm_page_prot = __pgprot(prot); + /* Take a ref for this mapping of the object, so that the fault + * handler can dereference the mmap offset's pointer to the object. + * This reference is cleaned up by the corresponding vm_close + * (which should happen whether the vma was created by this call, or + * by a vm_open due to mremap or partial unmap or whatever). + */ + drm_gem_object_reference(obj); + vma->vm_file = filp; /* Needed for drm_vm_open() */ drm_vm_open_locked(vma); diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 3795dbc0f50..93e677a481f 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -435,6 +435,8 @@ EXPORT_SYMBOL(drm_vblank_get); */ void drm_vblank_put(struct drm_device *dev, int crtc) { + BUG_ON (atomic_read (&dev->vblank_refcount[crtc]) == 0); + /* Last user schedules interrupt disable */ if (atomic_dec_and_test(&dev->vblank_refcount[crtc])) mod_timer(&dev->vblank_disable_timer, jiffies + 5*DRM_HZ); @@ -460,8 +462,9 @@ void drm_vblank_pre_modeset(struct drm_device *dev, int crtc) * so that interrupts remain enabled in the interim. */ if (!dev->vblank_inmodeset[crtc]) { - dev->vblank_inmodeset[crtc] = 1; - drm_vblank_get(dev, crtc); + dev->vblank_inmodeset[crtc] = 0x1; + if (drm_vblank_get(dev, crtc) == 0) + dev->vblank_inmodeset[crtc] |= 0x2; } } EXPORT_SYMBOL(drm_vblank_pre_modeset); @@ -473,9 +476,12 @@ void drm_vblank_post_modeset(struct drm_device *dev, int crtc) if (dev->vblank_inmodeset[crtc]) { spin_lock_irqsave(&dev->vbl_lock, irqflags); dev->vblank_disable_allowed = 1; - dev->vblank_inmodeset[crtc] = 0; spin_unlock_irqrestore(&dev->vbl_lock, irqflags); - drm_vblank_put(dev, crtc); + + if (dev->vblank_inmodeset[crtc] & 0x2) + drm_vblank_put(dev, crtc); + + dev->vblank_inmodeset[crtc] = 0; } } EXPORT_SYMBOL(drm_vblank_post_modeset); diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 81f1cff56fd..cc4649577a6 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -202,7 +202,7 @@ static int i915_initialize(struct drm_device * dev, drm_i915_init_t * init) dev_priv->ring.map.flags = 0; dev_priv->ring.map.mtrr = 0; - drm_core_ioremap(&dev_priv->ring.map, dev); + drm_core_ioremap_wc(&dev_priv->ring.map, dev); if (dev_priv->ring.map.handle == NULL) { i915_dma_cleanup(dev); @@ -811,7 +811,7 @@ static int i915_set_status_page(struct drm_device *dev, void *data, dev_priv->hws_map.flags = 0; dev_priv->hws_map.mtrr = 0; - drm_core_ioremap(&dev_priv->hws_map, dev); + drm_core_ioremap_wc(&dev_priv->hws_map, dev); if (dev_priv->hws_map.handle == NULL) { i915_dma_cleanup(dev); dev_priv->status_gfx_addr = 0; diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index aac12ee31a4..b293ef0bae7 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -27,6 +27,7 @@ * */ +#include <linux/device.h> #include "drmP.h" #include "drm.h" #include "i915_drm.h" @@ -66,6 +67,14 @@ static int i915_suspend(struct drm_device *dev, pm_message_t state) i915_save_state(dev); + /* If KMS is active, we do the leavevt stuff here */ + if (drm_core_check_feature(dev, DRIVER_MODESET)) { + if (i915_gem_idle(dev)) + dev_err(&dev->pdev->dev, + "GEM idle failed, resume may fail\n"); + drm_irq_uninstall(dev); + } + intel_opregion_free(dev); if (state.event == PM_EVENT_SUSPEND) { @@ -79,6 +88,9 @@ static int i915_suspend(struct drm_device *dev, pm_message_t state) static int i915_resume(struct drm_device *dev) { + struct drm_i915_private *dev_priv = dev->dev_private; + int ret = 0; + pci_set_power_state(dev->pdev, PCI_D0); pci_restore_state(dev->pdev); if (pci_enable_device(dev->pdev)) @@ -89,11 +101,26 @@ static int i915_resume(struct drm_device *dev) intel_opregion_init(dev); - return 0; + /* KMS EnterVT equivalent */ + if (drm_core_check_feature(dev, DRIVER_MODESET)) { + mutex_lock(&dev->struct_mutex); + dev_priv->mm.suspended = 0; + + ret = i915_gem_init_ringbuffer(dev); + if (ret != 0) + ret = -1; + mutex_unlock(&dev->struct_mutex); + + drm_irq_install(dev); + } + + return ret; } static struct vm_operations_struct i915_gem_vm_ops = { .fault = i915_gem_fault, + .open = drm_gem_vm_open, + .close = drm_gem_vm_close, }; static struct drm_driver driver = { diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 7325363164f..17fa40858d2 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -184,6 +184,8 @@ typedef struct drm_i915_private { unsigned int lvds_dither:1; unsigned int lvds_vbt:1; unsigned int int_crt_support:1; + unsigned int lvds_use_ssc:1; + int lvds_ssc_freq; struct drm_i915_fence_reg fence_regs[16]; /* assume 965 */ int fence_reg_start; /* 4 if userland hasn't ioctl'd us yet */ @@ -616,6 +618,7 @@ int i915_gem_init_ringbuffer(struct drm_device *dev); void i915_gem_cleanup_ringbuffer(struct drm_device *dev); int i915_gem_do_init(struct drm_device *dev, unsigned long start, unsigned long end); +int i915_gem_idle(struct drm_device *dev); int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf); int i915_gem_object_set_to_gtt_domain(struct drm_gem_object *obj, int write); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 81857665409..85685bfd12d 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -34,10 +34,6 @@ #define I915_GEM_GPU_DOMAINS (~(I915_GEM_DOMAIN_CPU | I915_GEM_DOMAIN_GTT)) -static void -i915_gem_object_set_to_gpu_domain(struct drm_gem_object *obj, - uint32_t read_domains, - uint32_t write_domain); static void i915_gem_object_flush_gpu_write_domain(struct drm_gem_object *obj); static void i915_gem_object_flush_gtt_write_domain(struct drm_gem_object *obj); static void i915_gem_object_flush_cpu_write_domain(struct drm_gem_object *obj); @@ -607,8 +603,6 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) case -EAGAIN: return VM_FAULT_OOM; case -EFAULT: - case -EBUSY: - DRM_ERROR("can't insert pfn?? fault or busy...\n"); return VM_FAULT_SIGBUS; default: return VM_FAULT_NOPAGE; @@ -684,6 +678,30 @@ out_free_list: return ret; } +static void +i915_gem_free_mmap_offset(struct drm_gem_object *obj) +{ + struct drm_device *dev = obj->dev; + struct drm_i915_gem_object *obj_priv = obj->driver_private; + struct drm_gem_mm *mm = dev->mm_private; + struct drm_map_list *list; + + list = &obj->map_list; + drm_ht_remove_item(&mm->offset_hash, &list->hash); + + if (list->file_offset_node) { + drm_mm_put_block(list->file_offset_node); + list->file_offset_node = NULL; + } + + if (list->map) { + drm_free(list->map, sizeof(struct drm_map), DRM_MEM_DRIVER); + list->map = NULL; + } + + obj_priv->mmap_offset = 0; +} + /** * i915_gem_get_gtt_alignment - return required GTT alignment for an object * @obj: object to check @@ -758,8 +776,11 @@ i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data, if (!obj_priv->mmap_offset) { ret = i915_gem_create_mmap_offset(obj); - if (ret) + if (ret) { + drm_gem_object_unreference(obj); + mutex_unlock(&dev->struct_mutex); return ret; + } } args->offset = obj_priv->mmap_offset; @@ -1030,6 +1051,9 @@ i915_gem_retire_requests(struct drm_device *dev) drm_i915_private_t *dev_priv = dev->dev_private; uint32_t seqno; + if (!dev_priv->hw_status_page) + return; + seqno = i915_get_gem_seqno(dev); while (!list_empty(&dev_priv->mm.request_list)) { @@ -1996,30 +2020,28 @@ i915_gem_object_set_to_cpu_domain(struct drm_gem_object *obj, int write) * drm_agp_chipset_flush */ static void -i915_gem_object_set_to_gpu_domain(struct drm_gem_object *obj, - uint32_t read_domains, - uint32_t write_domain) +i915_gem_object_set_to_gpu_domain(struct drm_gem_object *obj) { struct drm_device *dev = obj->dev; struct drm_i915_gem_object *obj_priv = obj->driver_private; uint32_t invalidate_domains = 0; uint32_t flush_domains = 0; - BUG_ON(read_domains & I915_GEM_DOMAIN_CPU); - BUG_ON(write_domain == I915_GEM_DOMAIN_CPU); + BUG_ON(obj->pending_read_domains & I915_GEM_DOMAIN_CPU); + BUG_ON(obj->pending_write_domain == I915_GEM_DOMAIN_CPU); #if WATCH_BUF DRM_INFO("%s: object %p read %08x -> %08x write %08x -> %08x\n", __func__, obj, - obj->read_domains, read_domains, - obj->write_domain, write_domain); + obj->read_domains, obj->pending_read_domains, + obj->write_domain, obj->pending_write_domain); #endif /* * If the object isn't moving to a new write domain, * let the object stay in multiple read domains */ - if (write_domain == 0) - read_domains |= obj->read_domains; + if (obj->pending_write_domain == 0) + obj->pending_read_domains |= obj->read_domains; else obj_priv->dirty = 1; @@ -2029,15 +2051,17 @@ i915_gem_object_set_to_gpu_domain(struct drm_gem_object *obj, * any read domains which differ from the old * write domain */ - if (obj->write_domain && obj->write_domain != read_domains) { + if (obj->write_domain && + obj->write_domain != obj->pending_read_domains) { flush_domains |= obj->write_domain; - invalidate_domains |= read_domains & ~obj->write_domain; + invalidate_domains |= + obj->pending_read_domains & ~obj->write_domain; } /* * Invalidate any read caches which may have * stale data. That is, any new read domains. */ - invalidate_domains |= read_domains & ~obj->read_domains; + invalidate_domains |= obj->pending_read_domains & ~obj->read_domains; if ((flush_domains | invalidate_domains) & I915_GEM_DOMAIN_CPU) { #if WATCH_BUF DRM_INFO("%s: CPU domain flush %08x invalidate %08x\n", @@ -2046,9 +2070,15 @@ i915_gem_object_set_to_gpu_domain(struct drm_gem_object *obj, i915_gem_clflush_object(obj); } - if ((write_domain | flush_domains) != 0) - obj->write_domain = write_domain; - obj->read_domains = read_domains; + /* The actual obj->write_domain will be updated with + * pending_write_domain after we emit the accumulated flush for all + * of our domain changes in execbuffers (which clears objects' + * write_domains). So if we have a current write domain that we + * aren't changing, set pending_write_domain to that. + */ + if (flush_domains == 0 && obj->pending_write_domain == 0) + obj->pending_write_domain = obj->write_domain; + obj->read_domains = obj->pending_read_domains; dev->invalidate_domains |= invalidate_domains; dev->flush_domains |= flush_domains; @@ -2251,6 +2281,8 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj, (int) reloc.offset, reloc.read_domains, reloc.write_domain); + drm_gem_object_unreference(target_obj); + i915_gem_object_unpin(obj); return -EINVAL; } @@ -2480,13 +2512,15 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, if (dev_priv->mm.wedged) { DRM_ERROR("Execbuf while wedged\n"); mutex_unlock(&dev->struct_mutex); - return -EIO; + ret = -EIO; + goto pre_mutex_err; } if (dev_priv->mm.suspended) { DRM_ERROR("Execbuf while VT-switched.\n"); mutex_unlock(&dev->struct_mutex); - return -EBUSY; + ret = -EBUSY; + goto pre_mutex_err; } /* Look up object handles */ @@ -2554,9 +2588,7 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, struct drm_gem_object *obj = object_list[i]; /* Compute new gpu domains and update invalidate/flush */ - i915_gem_object_set_to_gpu_domain(obj, - obj->pending_read_domains, - obj->pending_write_domain); + i915_gem_object_set_to_gpu_domain(obj); } i915_verify_inactive(dev, __FILE__, __LINE__); @@ -2575,6 +2607,12 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, (void)i915_add_request(dev, dev->flush_domains); } + for (i = 0; i < args->buffer_count; i++) { + struct drm_gem_object *obj = object_list[i]; + + obj->write_domain = obj->pending_write_domain; + } + i915_verify_inactive(dev, __FILE__, __LINE__); #if WATCH_COHERENCY @@ -2632,15 +2670,6 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, i915_verify_inactive(dev, __FILE__, __LINE__); - /* Copy the new buffer offsets back to the user's exec list. */ - ret = copy_to_user((struct drm_i915_relocation_entry __user *) - (uintptr_t) args->buffers_ptr, - exec_list, - sizeof(*exec_list) * args->buffer_count); - if (ret) - DRM_ERROR("failed to copy %d exec entries " - "back to user (%d)\n", - args->buffer_count, ret); err: for (i = 0; i < pinned; i++) i915_gem_object_unpin(object_list[i]); @@ -2650,6 +2679,18 @@ err: mutex_unlock(&dev->struct_mutex); + if (!ret) { + /* Copy the new buffer offsets back to the user's exec list. */ + ret = copy_to_user((struct drm_i915_relocation_entry __user *) + (uintptr_t) args->buffers_ptr, + exec_list, + sizeof(*exec_list) * args->buffer_count); + if (ret) + DRM_ERROR("failed to copy %d exec entries " + "back to user (%d)\n", + args->buffer_count, ret); + } + pre_mutex_err: drm_free(object_list, sizeof(*object_list) * args->buffer_count, DRM_MEM_DRIVER); @@ -2753,6 +2794,7 @@ i915_gem_pin_ioctl(struct drm_device *dev, void *data, if (obj_priv->pin_filp != NULL && obj_priv->pin_filp != file_priv) { DRM_ERROR("Already pinned in i915_gem_pin_ioctl(): %d\n", args->handle); + drm_gem_object_unreference(obj); mutex_unlock(&dev->struct_mutex); return -EINVAL; } @@ -2833,6 +2875,13 @@ i915_gem_busy_ioctl(struct drm_device *dev, void *data, return -EBADF; } + /* Update the active list for the hardware's current position. + * Otherwise this only updates on a delayed timer or when irqs are + * actually unmasked, and our working set ends up being larger than + * required. + */ + i915_gem_retire_requests(dev); + obj_priv = obj->driver_private; /* Don't count being on the flushing list against the object being * done. Otherwise, a buffer left on the flushing list but not getting @@ -2885,9 +2934,6 @@ int i915_gem_init_object(struct drm_gem_object *obj) void i915_gem_free_object(struct drm_gem_object *obj) { struct drm_device *dev = obj->dev; - struct drm_gem_mm *mm = dev->mm_private; - struct drm_map_list *list; - struct drm_map *map; struct drm_i915_gem_object *obj_priv = obj->driver_private; while (obj_priv->pin_count > 0) @@ -2898,19 +2944,7 @@ void i915_gem_free_object(struct drm_gem_object *obj) i915_gem_object_unbind(obj); - list = &obj->map_list; - drm_ht_remove_item(&mm->offset_hash, &list->hash); - - if (list->file_offset_node) { - drm_mm_put_block(list->file_offset_node); - list->file_offset_node = NULL; - } - - map = list->map; - if (map) { - drm_free(map, sizeof(*map), DRM_MEM_DRIVER); - list->map = NULL; - } + i915_gem_free_mmap_offset(obj); drm_free(obj_priv->page_cpu_valid, 1, DRM_MEM_DRIVER); drm_free(obj->driver_private, 1, DRM_MEM_DRIVER); @@ -2949,7 +2983,7 @@ i915_gem_evict_from_list(struct drm_device *dev, struct list_head *head) return 0; } -static int +int i915_gem_idle(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; @@ -3095,6 +3129,7 @@ i915_gem_init_hws(struct drm_device *dev) if (dev_priv->hw_status_page == NULL) { DRM_ERROR("Failed to map status page.\n"); memset(&dev_priv->hws_map, 0, sizeof(dev_priv->hws_map)); + i915_gem_object_unpin(obj); drm_gem_object_unreference(obj); return -EINVAL; } @@ -3107,6 +3142,31 @@ i915_gem_init_hws(struct drm_device *dev) return 0; } +static void +i915_gem_cleanup_hws(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_gem_object *obj; + struct drm_i915_gem_object *obj_priv; + + if (dev_priv->hws_obj == NULL) + return; + + obj = dev_priv->hws_obj; + obj_priv = obj->driver_private; + + kunmap(obj_priv->page_list[0]); + i915_gem_object_unpin(obj); + drm_gem_object_unreference(obj); + dev_priv->hws_obj = NULL; + + memset(&dev_priv->hws_map, 0, sizeof(dev_priv->hws_map)); + dev_priv->hw_status_page = NULL; + + /* Write high address into HWS_PGA when disabling. */ + I915_WRITE(HWS_PGA, 0x1ffff000); +} + int i915_gem_init_ringbuffer(struct drm_device *dev) { @@ -3124,6 +3184,7 @@ i915_gem_init_ringbuffer(struct drm_device *dev) obj = drm_gem_object_alloc(dev, 128 * 1024); if (obj == NULL) { DRM_ERROR("Failed to allocate ringbuffer\n"); + i915_gem_cleanup_hws(dev); return -ENOMEM; } obj_priv = obj->driver_private; @@ -3131,6 +3192,7 @@ i915_gem_init_ringbuffer(struct drm_device *dev) ret = i915_gem_object_pin(obj, 4096); if (ret != 0) { drm_gem_object_unreference(obj); + i915_gem_cleanup_hws(dev); return ret; } @@ -3148,7 +3210,9 @@ i915_gem_init_ringbuffer(struct drm_device *dev) if (ring->map.handle == NULL) { DRM_ERROR("Failed to map ringbuffer.\n"); memset(&dev_priv->ring, 0, sizeof(dev_priv->ring)); + i915_gem_object_unpin(obj); drm_gem_object_unreference(obj); + i915_gem_cleanup_hws(dev); return -EINVAL; } ring->ring_obj = obj; @@ -3228,20 +3292,7 @@ i915_gem_cleanup_ringbuffer(struct drm_device *dev) dev_priv->ring.ring_obj = NULL; memset(&dev_priv->ring, 0, sizeof(dev_priv->ring)); - if (dev_priv->hws_obj != NULL) { - struct drm_gem_object *obj = dev_priv->hws_obj; - struct drm_i915_gem_object *obj_priv = obj->driver_private; - - kunmap(obj_priv->page_list[0]); - i915_gem_object_unpin(obj); - drm_gem_object_unreference(obj); - dev_priv->hws_obj = NULL; - memset(&dev_priv->hws_map, 0, sizeof(dev_priv->hws_map)); - dev_priv->hw_status_page = NULL; - - /* Write high address into HWS_PGA when disabling. */ - I915_WRITE(HWS_PGA, 0x1ffff000); - } + i915_gem_cleanup_hws(dev); } int @@ -3497,7 +3548,7 @@ i915_gem_phys_pwrite(struct drm_device *dev, struct drm_gem_object *obj, user_data = (char __user *) (uintptr_t) args->data_ptr; obj_addr = obj_priv->phys_obj->handle->vaddr + args->offset; - DRM_ERROR("obj_addr %p, %lld\n", obj_addr, args->size); + DRM_DEBUG("obj_addr %p, %lld\n", obj_addr, args->size); ret = copy_from_user(obj_addr, user_data, args->size); if (ret) return -EFAULT; diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c index fa1685cba84..7fb4191ef93 100644 --- a/drivers/gpu/drm/i915/i915_gem_tiling.c +++ b/drivers/gpu/drm/i915/i915_gem_tiling.c @@ -299,9 +299,8 @@ i915_gem_set_tiling(struct drm_device *dev, void *data, } obj_priv->stride = args->stride; - mutex_unlock(&dev->struct_mutex); - drm_gem_object_unreference(obj); + mutex_unlock(&dev->struct_mutex); return 0; } @@ -340,9 +339,8 @@ i915_gem_get_tiling(struct drm_device *dev, void *data, DRM_ERROR("unknown tiling mode\n"); } - mutex_unlock(&dev->struct_mutex); - drm_gem_object_unreference(obj); + mutex_unlock(&dev->struct_mutex); return 0; } diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c index 4ca82a02552..fc28e2bbd54 100644 --- a/drivers/gpu/drm/i915/intel_bios.c +++ b/drivers/gpu/drm/i915/intel_bios.c @@ -111,6 +111,12 @@ parse_panel_data(struct drm_i915_private *dev_priv, struct bdb_header *bdb) panel_fixed_mode->clock = dvo_timing->clock * 10; panel_fixed_mode->type = DRM_MODE_TYPE_PREFERRED; + /* Some VBTs have bogus h/vtotal values */ + if (panel_fixed_mode->hsync_end > panel_fixed_mode->htotal) + panel_fixed_mode->htotal = panel_fixed_mode->hsync_end + 1; + if (panel_fixed_mode->vsync_end > panel_fixed_mode->vtotal) + panel_fixed_mode->vtotal = panel_fixed_mode->vsync_end + 1; + drm_mode_set_name(panel_fixed_mode); dev_priv->vbt_mode = panel_fixed_mode; @@ -135,6 +141,14 @@ parse_general_features(struct drm_i915_private *dev_priv, if (general) { dev_priv->int_tv_support = general->int_tv_support; dev_priv->int_crt_support = general->int_crt_support; + dev_priv->lvds_use_ssc = general->enable_ssc; + + if (dev_priv->lvds_use_ssc) { + if (IS_I855(dev_priv->dev)) + dev_priv->lvds_ssc_freq = general->ssc_freq ? 66 : 48; + else + dev_priv->lvds_ssc_freq = general->ssc_freq ? 100 : 96; + } } } diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index bbdd72909a1..a2834276cb3 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -90,12 +90,12 @@ typedef struct { #define I9XX_DOT_MAX 400000 #define I9XX_VCO_MIN 1400000 #define I9XX_VCO_MAX 2800000 -#define I9XX_N_MIN 3 -#define I9XX_N_MAX 8 +#define I9XX_N_MIN 1 +#define I9XX_N_MAX 6 #define I9XX_M_MIN 70 #define I9XX_M_MAX 120 #define I9XX_M1_MIN 10 -#define I9XX_M1_MAX 20 +#define I9XX_M1_MAX 22 #define I9XX_M2_MIN 5 #define I9XX_M2_MAX 9 #define I9XX_P_SDVO_DAC_MIN 5 @@ -189,9 +189,7 @@ static const intel_limit_t *intel_limit(struct drm_crtc *crtc) return limit; } -/** Derive the pixel clock for the given refclk and divisors for 8xx chips. */ - -static void i8xx_clock(int refclk, intel_clock_t *clock) +static void intel_clock(int refclk, intel_clock_t *clock) { clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2); clock->p = clock->p1 * clock->p2; @@ -199,25 +197,6 @@ static void i8xx_clock(int refclk, intel_clock_t *clock) clock->dot = clock->vco / clock->p; } -/** Derive the pixel clock for the given refclk and divisors for 9xx chips. */ - -static void i9xx_clock(int refclk, intel_clock_t *clock) -{ - clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2); - clock->p = clock->p1 * clock->p2; - clock->vco = refclk * clock->m / (clock->n + 2); - clock->dot = clock->vco / clock->p; -} - -static void intel_clock(struct drm_device *dev, int refclk, - intel_clock_t *clock) -{ - if (IS_I9XX(dev)) - i9xx_clock (refclk, clock); - else - i8xx_clock (refclk, clock); -} - /** * Returns whether any output on the specified pipe is of the specified type */ @@ -238,7 +217,7 @@ bool intel_pipe_has_type (struct drm_crtc *crtc, int type) return false; } -#define INTELPllInvalid(s) { /* ErrorF (s) */; return false; } +#define INTELPllInvalid(s) do { /* DRM_DEBUG(s); */ return false; } while (0) /** * Returns whether the given set of divisors are valid for a given refclk with * the given connectors. @@ -318,7 +297,7 @@ static bool intel_find_best_PLL(struct drm_crtc *crtc, int target, clock.p1 <= limit->p1.max; clock.p1++) { int this_err; - intel_clock(dev, refclk, &clock); + intel_clock(refclk, &clock); if (!intel_PLL_is_valid(crtc, &clock)) continue; @@ -343,7 +322,7 @@ intel_wait_for_vblank(struct drm_device *dev) udelay(20000); } -static void +static int intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb) { @@ -361,11 +340,21 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, int dspstride = (pipe == 0) ? DSPASTRIDE : DSPBSTRIDE; int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; u32 dspcntr, alignment; + int ret; /* no fb bound */ if (!crtc->fb) { DRM_DEBUG("No FB bound\n"); - return; + return 0; + } + + switch (pipe) { + case 0: + case 1: + break; + default: + DRM_ERROR("Can't update pipe %d in SAREA\n", pipe); + return -EINVAL; } intel_fb = to_intel_framebuffer(crtc->fb); @@ -377,28 +366,30 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, alignment = 64 * 1024; break; case I915_TILING_X: - if (IS_I9XX(dev)) - alignment = 1024 * 1024; - else - alignment = 512 * 1024; + /* pin() will align the object as required by fence */ + alignment = 0; break; case I915_TILING_Y: /* FIXME: Is this true? */ DRM_ERROR("Y tiled not allowed for scan out buffers\n"); - return; + return -EINVAL; default: BUG(); } - if (i915_gem_object_pin(intel_fb->obj, alignment)) - return; - - i915_gem_object_set_to_gtt_domain(intel_fb->obj, 1); - - Start = obj_priv->gtt_offset; - Offset = y * crtc->fb->pitch + x * (crtc->fb->bits_per_pixel / 8); + mutex_lock(&dev->struct_mutex); + ret = i915_gem_object_pin(intel_fb->obj, alignment); + if (ret != 0) { + mutex_unlock(&dev->struct_mutex); + return ret; + } - I915_WRITE(dspstride, crtc->fb->pitch); + ret = i915_gem_object_set_to_gtt_domain(intel_fb->obj, 1); + if (ret != 0) { + i915_gem_object_unpin(intel_fb->obj); + mutex_unlock(&dev->struct_mutex); + return ret; + } dspcntr = I915_READ(dspcntr_reg); /* Mask out pixel format bits in case we change it */ @@ -419,11 +410,17 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, break; default: DRM_ERROR("Unknown color depth\n"); - return; + i915_gem_object_unpin(intel_fb->obj); + mutex_unlock(&dev->struct_mutex); + return -EINVAL; } I915_WRITE(dspcntr_reg, dspcntr); + Start = obj_priv->gtt_offset; + Offset = y * crtc->fb->pitch + x * (crtc->fb->bits_per_pixel / 8); + DRM_DEBUG("Writing base %08lX %08lX %d %d\n", Start, Offset, x, y); + I915_WRITE(dspstride, crtc->fb->pitch); if (IS_I965G(dev)) { I915_WRITE(dspbase, Offset); I915_READ(dspbase); @@ -440,27 +437,24 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, intel_fb = to_intel_framebuffer(old_fb); i915_gem_object_unpin(intel_fb->obj); } + mutex_unlock(&dev->struct_mutex); if (!dev->primary->master) - return; + return 0; master_priv = dev->primary->master->driver_priv; if (!master_priv->sarea_priv) - return; + return 0; - switch (pipe) { - case 0: - master_priv->sarea_priv->pipeA_x = x; - master_priv->sarea_priv->pipeA_y = y; - break; - case 1: + if (pipe) { master_priv->sarea_priv->pipeB_x = x; master_priv->sarea_priv->pipeB_y = y; - break; - default: - DRM_ERROR("Can't update pipe %d in SAREA\n", pipe); - break; + } else { + master_priv->sarea_priv->pipeA_x = x; + master_priv->sarea_priv->pipeA_y = y; } + + return 0; } @@ -708,11 +702,11 @@ static int intel_panel_fitter_pipe (struct drm_device *dev) return 1; } -static void intel_crtc_mode_set(struct drm_crtc *crtc, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode, - int x, int y, - struct drm_framebuffer *old_fb) +static int intel_crtc_mode_set(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode, + int x, int y, + struct drm_framebuffer *old_fb) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; @@ -732,13 +726,14 @@ static void intel_crtc_mode_set(struct drm_crtc *crtc, int dspsize_reg = (pipe == 0) ? DSPASIZE : DSPBSIZE; int dsppos_reg = (pipe == 0) ? DSPAPOS : DSPBPOS; int pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC; - int refclk; + int refclk, num_outputs = 0; intel_clock_t clock; u32 dpll = 0, fp = 0, dspcntr, pipeconf; bool ok, is_sdvo = false, is_dvo = false; bool is_crt = false, is_lvds = false, is_tv = false; struct drm_mode_config *mode_config = &dev->mode_config; struct drm_connector *connector; + int ret; drm_vblank_pre_modeset(dev, pipe); @@ -768,9 +763,14 @@ static void intel_crtc_mode_set(struct drm_crtc *crtc, is_crt = true; break; } + + num_outputs++; } - if (IS_I9XX(dev)) { + if (is_lvds && dev_priv->lvds_use_ssc && num_outputs < 2) { + refclk = dev_priv->lvds_ssc_freq * 1000; + DRM_DEBUG("using SSC reference clock of %d MHz\n", refclk / 1000); + } else if (IS_I9XX(dev)) { refclk = 96000; } else { refclk = 48000; @@ -779,7 +779,7 @@ static void intel_crtc_mode_set(struct drm_crtc *crtc, ok = intel_find_best_PLL(crtc, adjusted_mode->clock, refclk, &clock); if (!ok) { DRM_ERROR("Couldn't find PLL settings for mode!\n"); - return; + return -EINVAL; } fp = clock.n << 16 | clock.m1 << 8 | clock.m2; @@ -829,11 +829,14 @@ static void intel_crtc_mode_set(struct drm_crtc *crtc, } } - if (is_tv) { + if (is_sdvo && is_tv) + dpll |= PLL_REF_INPUT_TVCLKINBC; + else if (is_tv) /* XXX: just matching BIOS for now */ -/* dpll |= PLL_REF_INPUT_TVCLKINBC; */ + /* dpll |= PLL_REF_INPUT_TVCLKINBC; */ dpll |= 3; - } + else if (is_lvds && dev_priv->lvds_use_ssc && num_outputs < 2) + dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN; else dpll |= PLL_REF_INPUT_DREFCLK; @@ -950,9 +953,13 @@ static void intel_crtc_mode_set(struct drm_crtc *crtc, I915_WRITE(dspcntr_reg, dspcntr); /* Flush the plane changes */ - intel_pipe_set_base(crtc, x, y, old_fb); + ret = intel_pipe_set_base(crtc, x, y, old_fb); + if (ret != 0) + return ret; drm_vblank_post_modeset(dev, pipe); + + return 0; } /** Loads the palette/gamma unit for the CRTC with the prepared values */ @@ -1001,6 +1008,7 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc, temp = CURSOR_MODE_DISABLE; addr = 0; bo = NULL; + mutex_lock(&dev->struct_mutex); goto finish; } @@ -1023,18 +1031,19 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc, } /* we only need to pin inside GTT if cursor is non-phy */ + mutex_lock(&dev->struct_mutex); if (!dev_priv->cursor_needs_physical) { ret = i915_gem_object_pin(bo, PAGE_SIZE); if (ret) { DRM_ERROR("failed to pin cursor bo\n"); - goto fail; + goto fail_locked; } addr = obj_priv->gtt_offset; } else { ret = i915_gem_attach_phys_object(dev, bo, (pipe == 0) ? I915_GEM_PHYS_CURSOR_0 : I915_GEM_PHYS_CURSOR_1); if (ret) { DRM_ERROR("failed to attach phys object\n"); - goto fail; + goto fail_locked; } addr = obj_priv->phys_obj->handle->busaddr; } @@ -1054,10 +1063,9 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc, i915_gem_detach_phys_object(dev, intel_crtc->cursor_bo); } else i915_gem_object_unpin(intel_crtc->cursor_bo); - mutex_lock(&dev->struct_mutex); drm_gem_object_unreference(intel_crtc->cursor_bo); - mutex_unlock(&dev->struct_mutex); } + mutex_unlock(&dev->struct_mutex); intel_crtc->cursor_addr = addr; intel_crtc->cursor_bo = bo; @@ -1065,6 +1073,7 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc, return 0; fail: mutex_lock(&dev->struct_mutex); +fail_locked: drm_gem_object_unreference(bo); mutex_unlock(&dev->struct_mutex); return ret; @@ -1292,7 +1301,7 @@ static int intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc) } /* XXX: Handle the 100Mhz refclk */ - i9xx_clock(96000, &clock); + intel_clock(96000, &clock); } else { bool is_lvds = (pipe == 1) && (I915_READ(LVDS) & LVDS_PORT_EN); @@ -1304,9 +1313,9 @@ static int intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc) if ((dpll & PLL_REF_INPUT_MASK) == PLLB_REF_INPUT_SPREADSPECTRUMIN) { /* XXX: might not be 66MHz */ - i8xx_clock(66000, &clock); + intel_clock(66000, &clock); } else - i8xx_clock(48000, &clock); + intel_clock(48000, &clock); } else { if (dpll & PLL_P1_DIVIDE_BY_TWO) clock.p1 = 2; @@ -1319,7 +1328,7 @@ static int intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc) else clock.p2 = 2; - i8xx_clock(48000, &clock); + intel_clock(48000, &clock); } } @@ -1598,7 +1607,9 @@ intel_user_framebuffer_create(struct drm_device *dev, ret = intel_framebuffer_create(dev, mode_cmd, &fb, obj); if (ret) { + mutex_lock(&dev->struct_mutex); drm_gem_object_unreference(obj); + mutex_unlock(&dev->struct_mutex); return NULL; } diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index afd1217b8a0..b7f0ebe9f81 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -473,7 +473,7 @@ static int intelfb_create(struct drm_device *dev, uint32_t fb_width, ret = intel_framebuffer_create(dev, &mode_cmd, &fb, fbo); if (ret) { DRM_ERROR("failed to allocate fb.\n"); - goto out_unref; + goto out_unpin; } list_add(&fb->filp_head, &dev->mode_config.fb_kernel_list); @@ -484,7 +484,7 @@ static int intelfb_create(struct drm_device *dev, uint32_t fb_width, info = framebuffer_alloc(sizeof(struct intelfb_par), device); if (!info) { ret = -ENOMEM; - goto out_unref; + goto out_unpin; } par = info->par; @@ -513,7 +513,7 @@ static int intelfb_create(struct drm_device *dev, uint32_t fb_width, size); if (!info->screen_base) { ret = -ENOSPC; - goto out_unref; + goto out_unpin; } info->screen_size = size; @@ -608,6 +608,8 @@ static int intelfb_create(struct drm_device *dev, uint32_t fb_width, mutex_unlock(&dev->struct_mutex); return 0; +out_unpin: + i915_gem_object_unpin(fbo); out_unref: drm_gem_object_unreference(fbo); mutex_unlock(&dev->struct_mutex); diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 6d4f9126535..0d211af9885 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -481,8 +481,6 @@ void intel_lvds_init(struct drm_device *dev) if (dev_priv->panel_fixed_mode) { dev_priv->panel_fixed_mode->type |= DRM_MODE_TYPE_PREFERRED; - drm_mode_probed_add(connector, - dev_priv->panel_fixed_mode); goto out; } } diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index a30508b639b..fbe6f3931b1 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -193,7 +193,7 @@ static bool intel_sdvo_write_byte(struct intel_output *intel_output, int addr, #define SDVO_CMD_NAME_ENTRY(cmd) {cmd, #cmd} /** Mapping of command numbers to names, for debug output */ -const static struct _sdvo_cmd_name { +static const struct _sdvo_cmd_name { u8 cmd; char *name; } sdvo_cmd_names[] = { diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index fbb35dc56f5..56485d67369 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -411,7 +411,7 @@ struct tv_mode { * These values account for -1s required. */ -const static struct tv_mode tv_modes[] = { +static const struct tv_mode tv_modes[] = { { .name = "NTSC-M", .clock = 107520, diff --git a/drivers/gpu/drm/radeon/radeon_cp.c b/drivers/gpu/drm/radeon/radeon_cp.c index df4cf97e5d9..92965dbb3c1 100644 --- a/drivers/gpu/drm/radeon/radeon_cp.c +++ b/drivers/gpu/drm/radeon/radeon_cp.c @@ -557,8 +557,10 @@ static int radeon_do_engine_reset(struct drm_device * dev) } static void radeon_cp_init_ring_buffer(struct drm_device * dev, - drm_radeon_private_t * dev_priv) + drm_radeon_private_t *dev_priv, + struct drm_file *file_priv) { + struct drm_radeon_master_private *master_priv; u32 ring_start, cur_read_ptr; u32 tmp; @@ -677,6 +679,14 @@ static void radeon_cp_init_ring_buffer(struct drm_device * dev, dev_priv->scratch[2] = 0; RADEON_WRITE(RADEON_LAST_CLEAR_REG, 0); + /* reset sarea copies of these */ + master_priv = file_priv->master->driver_priv; + if (master_priv->sarea_priv) { + master_priv->sarea_priv->last_frame = 0; + master_priv->sarea_priv->last_dispatch = 0; + master_priv->sarea_priv->last_clear = 0; + } + radeon_do_wait_for_idle(dev_priv); /* Sync everything up */ @@ -1215,7 +1225,7 @@ static int radeon_do_init_cp(struct drm_device *dev, drm_radeon_init_t *init, } radeon_cp_load_microcode(dev_priv); - radeon_cp_init_ring_buffer(dev, dev_priv); + radeon_cp_init_ring_buffer(dev, dev_priv, file_priv); dev_priv->last_buf = 0; @@ -1281,7 +1291,7 @@ static int radeon_do_cleanup_cp(struct drm_device * dev) * * Charl P. Botha <http://cpbotha.net> */ -static int radeon_do_resume_cp(struct drm_device * dev) +static int radeon_do_resume_cp(struct drm_device *dev, struct drm_file *file_priv) { drm_radeon_private_t *dev_priv = dev->dev_private; @@ -1304,7 +1314,7 @@ static int radeon_do_resume_cp(struct drm_device * dev) } radeon_cp_load_microcode(dev_priv); - radeon_cp_init_ring_buffer(dev, dev_priv); + radeon_cp_init_ring_buffer(dev, dev_priv, file_priv); radeon_do_engine_reset(dev); radeon_irq_set_state(dev, RADEON_SW_INT_ENABLE, 1); @@ -1479,8 +1489,7 @@ int radeon_cp_idle(struct drm_device *dev, void *data, struct drm_file *file_pri */ int radeon_cp_resume(struct drm_device *dev, void *data, struct drm_file *file_priv) { - - return radeon_do_resume_cp(dev); + return radeon_do_resume_cp(dev, file_priv); } int radeon_engine_reset(struct drm_device *dev, void *data, struct drm_file *file_priv) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 6cad69ed21c..1cc967448f4 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1300,7 +1300,13 @@ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) }, { HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) }, + { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300) }, + { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304) }, + { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb651) }, + { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb654) }, { HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED, USB_DEVICE_ID_TOPSEED_CYBERLINK) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0005) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, 0x030c) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT) }, @@ -1605,6 +1611,7 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0002) }, { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0003) }, { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0004) }, + { HID_USB_DEVICE(USB_VENDOR_ID_POWERCOM, USB_DEVICE_ID_POWERCOM_UPS) }, { HID_USB_DEVICE(USB_VENDOR_ID_SOUNDGRAPH, USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD) }, { HID_USB_DEVICE(USB_VENDOR_ID_SOUNDGRAPH, USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD2) }, { HID_USB_DEVICE(USB_VENDOR_ID_SOUNDGRAPH, USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD3) }, @@ -1612,10 +1619,6 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_SOUNDGRAPH, USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD5) }, { HID_USB_DEVICE(USB_VENDOR_ID_TENX, USB_DEVICE_ID_TENX_IBUDDY1) }, { HID_USB_DEVICE(USB_VENDOR_ID_TENX, USB_DEVICE_ID_TENX_IBUDDY2) }, - { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300) }, - { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304) }, - { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb651) }, - { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb654) }, { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LABPRO) }, { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP) }, { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP) }, @@ -1626,8 +1629,6 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20) }, { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_8_8_4_IF_KIT) }, { HID_USB_DEVICE(USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K) }, - { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0005) }, - { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) }, { } }; diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index e899f510ebe..88511970508 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -348,6 +348,9 @@ #define USB_VENDOR_ID_PLAYDOTCOM 0x0b43 #define USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII 0x0003 +#define USB_VENDOR_ID_POWERCOM 0x0d9f +#define USB_DEVICE_ID_POWERCOM_UPS 0x0002 + #define USB_VENDOR_ID_SAITEK 0x06a3 #define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17 diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index 73244962897..02b19db5442 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -267,8 +267,10 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd, default: { struct hid_device *hid = dev->hid; - if (_IOC_TYPE(cmd) != 'H' || _IOC_DIR(cmd) != _IOC_READ) - return -EINVAL; + if (_IOC_TYPE(cmd) != 'H' || _IOC_DIR(cmd) != _IOC_READ) { + ret = -EINVAL; + break; + } if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGRAWNAME(0))) { int len; @@ -277,8 +279,9 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd, len = strlen(hid->name) + 1; if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); - return copy_to_user(user_arg, hid->name, len) ? + ret = copy_to_user(user_arg, hid->name, len) ? -EFAULT : len; + break; } if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGRAWPHYS(0))) { @@ -288,12 +291,13 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd, len = strlen(hid->phys) + 1; if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); - return copy_to_user(user_arg, hid->phys, len) ? + ret = copy_to_user(user_arg, hid->phys, len) ? -EFAULT : len; + break; } } - ret = -ENOTTY; + ret = -ENOTTY; } unlock_kernel(); return ret; diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c index 609cafff86b..5f81ddf7150 100644 --- a/drivers/hwmon/f71882fg.c +++ b/drivers/hwmon/f71882fg.c @@ -1872,7 +1872,7 @@ static int __init f71882fg_find(int sioaddr, unsigned short *address, devid = superio_inw(sioaddr, SIO_REG_MANID); if (devid != SIO_FINTEK_ID) { - printk(KERN_INFO DRVNAME ": Not a Fintek device\n"); + pr_debug(DRVNAME ": Not a Fintek device\n"); goto exit; } @@ -1932,7 +1932,7 @@ static int __init f71882fg_device_add(unsigned short address, res.name = f71882fg_pdev->name; err = acpi_check_resource_conflict(&res); if (err) - return err; + goto exit_device_put; err = platform_device_add_resources(f71882fg_pdev, &res, 1); if (err) { diff --git a/drivers/hwmon/hp_accel.c b/drivers/hwmon/hp_accel.c index abf4dfc8ec2..29c83b5b969 100644 --- a/drivers/hwmon/hp_accel.c +++ b/drivers/hwmon/hp_accel.c @@ -166,6 +166,18 @@ static struct axis_conversion lis3lv02d_axis_xy_swap_yz_inverted = {2, -1, -3}; }, \ .driver_data = &lis3lv02d_axis_##_axis \ } + +#define AXIS_DMI_MATCH2(_ident, _class1, _name1, \ + _class2, _name2, \ + _axis) { \ + .ident = _ident, \ + .callback = lis3lv02d_dmi_matched, \ + .matches = { \ + DMI_MATCH(DMI_##_class1, _name1), \ + DMI_MATCH(DMI_##_class2, _name2), \ + }, \ + .driver_data = &lis3lv02d_axis_##_axis \ +} static struct dmi_system_id lis3lv02d_dmi_ids[] = { /* product names are truncated to match all kinds of a same model */ AXIS_DMI_MATCH("NC64x0", "HP Compaq nc64", x_inverted), @@ -179,6 +191,16 @@ static struct dmi_system_id lis3lv02d_dmi_ids[] = { AXIS_DMI_MATCH("NC673x", "HP Compaq 673", xy_rotated_left_usd), AXIS_DMI_MATCH("NC651xx", "HP Compaq 651", xy_rotated_right), AXIS_DMI_MATCH("NC671xx", "HP Compaq 671", xy_swap_yz_inverted), + /* Intel-based HP Pavilion dv5 */ + AXIS_DMI_MATCH2("HPDV5_I", + PRODUCT_NAME, "HP Pavilion dv5", + BOARD_NAME, "3603", + x_inverted), + /* AMD-based HP Pavilion dv5 */ + AXIS_DMI_MATCH2("HPDV5_A", + PRODUCT_NAME, "HP Pavilion dv5", + BOARD_NAME, "3600", + y_inverted), { NULL, } /* Laptop models without axis info (yet): * "NC6910" "HP Compaq 6910" @@ -213,9 +235,49 @@ static struct delayed_led_classdev hpled_led = { .set_brightness = hpled_set, }; +static acpi_status +lis3lv02d_get_resource(struct acpi_resource *resource, void *context) +{ + if (resource->type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ) { + struct acpi_resource_extended_irq *irq; + u32 *device_irq = context; + + irq = &resource->data.extended_irq; + *device_irq = irq->interrupts[0]; + } + + return AE_OK; +} + +static void lis3lv02d_enum_resources(struct acpi_device *device) +{ + acpi_status status; + + status = acpi_walk_resources(device->handle, METHOD_NAME__CRS, + lis3lv02d_get_resource, &adev.irq); + if (ACPI_FAILURE(status)) + printk(KERN_DEBUG DRIVER_NAME ": Error getting resources\n"); +} + +static s16 lis3lv02d_read_16(acpi_handle handle, int reg) +{ + u8 lo, hi; + + adev.read(handle, reg - 1, &lo); + adev.read(handle, reg, &hi); + /* In "12 bit right justified" mode, bit 6, bit 7, bit 8 = bit 5 */ + return (s16)((hi << 8) | lo); +} + +static s16 lis3lv02d_read_8(acpi_handle handle, int reg) +{ + s8 lo; + adev.read(handle, reg, &lo); + return lo; +} + static int lis3lv02d_add(struct acpi_device *device) { - u8 val; int ret; if (!device) @@ -229,10 +291,22 @@ static int lis3lv02d_add(struct acpi_device *device) strcpy(acpi_device_class(device), ACPI_MDPS_CLASS); device->driver_data = &adev; - lis3lv02d_acpi_read(device->handle, WHO_AM_I, &val); - if ((val != LIS3LV02DL_ID) && (val != LIS302DL_ID)) { + lis3lv02d_acpi_read(device->handle, WHO_AM_I, &adev.whoami); + switch (adev.whoami) { + case LIS_DOUBLE_ID: + printk(KERN_INFO DRIVER_NAME ": 2-byte sensor found\n"); + adev.read_data = lis3lv02d_read_16; + adev.mdps_max_val = 2048; + break; + case LIS_SINGLE_ID: + printk(KERN_INFO DRIVER_NAME ": 1-byte sensor found\n"); + adev.read_data = lis3lv02d_read_8; + adev.mdps_max_val = 128; + break; + default: printk(KERN_ERR DRIVER_NAME - ": Accelerometer chip not LIS3LV02D{L,Q}\n"); + ": unknown sensor type 0x%X\n", adev.whoami); + return -EINVAL; } /* If possible use a "standard" axes order */ @@ -247,6 +321,9 @@ static int lis3lv02d_add(struct acpi_device *device) if (ret) return ret; + /* obtain IRQ number of our device from ACPI */ + lis3lv02d_enum_resources(adev.device); + ret = lis3lv02d_init_device(&adev); if (ret) { flush_work(&hpled_led.work); diff --git a/drivers/hwmon/lis3lv02d.c b/drivers/hwmon/lis3lv02d.c index 219d2d0d5a6..8bb2158f045 100644 --- a/drivers/hwmon/lis3lv02d.c +++ b/drivers/hwmon/lis3lv02d.c @@ -3,7 +3,7 @@ * * Copyright (C) 2007-2008 Yan Burman * Copyright (C) 2008 Eric Piel - * Copyright (C) 2008 Pavel Machek + * Copyright (C) 2008-2009 Pavel Machek * * 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 @@ -35,6 +35,7 @@ #include <linux/poll.h> #include <linux/freezer.h> #include <linux/uaccess.h> +#include <linux/miscdevice.h> #include <acpi/acpi_drivers.h> #include <asm/atomic.h> #include "lis3lv02d.h" @@ -52,24 +53,14 @@ * joystick. */ -/* Maximum value our axis may get for the input device (signed 12 bits) */ -#define MDPS_MAX_VAL 2048 +struct acpi_lis3lv02d adev = { + .misc_wait = __WAIT_QUEUE_HEAD_INITIALIZER(adev.misc_wait), +}; -struct acpi_lis3lv02d adev; EXPORT_SYMBOL_GPL(adev); static int lis3lv02d_add_fs(struct acpi_device *device); -static s16 lis3lv02d_read_16(acpi_handle handle, int reg) -{ - u8 lo, hi; - - adev.read(handle, reg, &lo); - adev.read(handle, reg + 1, &hi); - /* In "12 bit right justified" mode, bit 6, bit 7, bit 8 = bit 5 */ - return (s16)((hi << 8) | lo); -} - /** * lis3lv02d_get_axis - For the given axis, give the value converted * @axis: 1,2,3 - can also be negative @@ -98,9 +89,9 @@ static void lis3lv02d_get_xyz(acpi_handle handle, int *x, int *y, int *z) { int position[3]; - position[0] = lis3lv02d_read_16(handle, OUTX_L); - position[1] = lis3lv02d_read_16(handle, OUTY_L); - position[2] = lis3lv02d_read_16(handle, OUTZ_L); + position[0] = adev.read_data(handle, OUTX); + position[1] = adev.read_data(handle, OUTY); + position[2] = adev.read_data(handle, OUTZ); *x = lis3lv02d_get_axis(adev.ac.x, position); *y = lis3lv02d_get_axis(adev.ac.y, position); @@ -110,26 +101,13 @@ static void lis3lv02d_get_xyz(acpi_handle handle, int *x, int *y, int *z) void lis3lv02d_poweroff(acpi_handle handle) { adev.is_on = 0; - /* disable X,Y,Z axis and power down */ - adev.write(handle, CTRL_REG1, 0x00); } EXPORT_SYMBOL_GPL(lis3lv02d_poweroff); void lis3lv02d_poweron(acpi_handle handle) { - u8 val; - adev.is_on = 1; adev.init(handle); - adev.write(handle, FF_WU_CFG, 0); - /* - * BDU: LSB and MSB values are not updated until both have been read. - * So the value read will always be correct. - * IEN: Interrupt for free-fall and DD, not for data-ready. - */ - adev.read(handle, CTRL_REG2, &val); - val |= CTRL2_BDU | CTRL2_IEN; - adev.write(handle, CTRL_REG2, val); } EXPORT_SYMBOL_GPL(lis3lv02d_poweron); @@ -162,6 +140,140 @@ static void lis3lv02d_decrease_use(struct acpi_lis3lv02d *dev) mutex_unlock(&dev->lock); } +static irqreturn_t lis302dl_interrupt(int irq, void *dummy) +{ + /* + * Be careful: on some HP laptops the bios force DD when on battery and + * the lid is closed. This leads to interrupts as soon as a little move + * is done. + */ + atomic_inc(&adev.count); + + wake_up_interruptible(&adev.misc_wait); + kill_fasync(&adev.async_queue, SIGIO, POLL_IN); + return IRQ_HANDLED; +} + +static int lis3lv02d_misc_open(struct inode *inode, struct file *file) +{ + int ret; + + if (test_and_set_bit(0, &adev.misc_opened)) + return -EBUSY; /* already open */ + + atomic_set(&adev.count, 0); + + /* + * The sensor can generate interrupts for free-fall and direction + * detection (distinguishable with FF_WU_SRC and DD_SRC) but to keep + * the things simple and _fast_ we activate it only for free-fall, so + * no need to read register (very slow with ACPI). For the same reason, + * we forbid shared interrupts. + * + * IRQF_TRIGGER_RISING seems pointless on HP laptops because the + * io-apic is not configurable (and generates a warning) but I keep it + * in case of support for other hardware. + */ + ret = request_irq(adev.irq, lis302dl_interrupt, IRQF_TRIGGER_RISING, + DRIVER_NAME, &adev); + + if (ret) { + clear_bit(0, &adev.misc_opened); + printk(KERN_ERR DRIVER_NAME ": IRQ%d allocation failed\n", adev.irq); + return -EBUSY; + } + lis3lv02d_increase_use(&adev); + printk("lis3: registered interrupt %d\n", adev.irq); + return 0; +} + +static int lis3lv02d_misc_release(struct inode *inode, struct file *file) +{ + fasync_helper(-1, file, 0, &adev.async_queue); + lis3lv02d_decrease_use(&adev); + free_irq(adev.irq, &adev); + clear_bit(0, &adev.misc_opened); /* release the device */ + return 0; +} + +static ssize_t lis3lv02d_misc_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + DECLARE_WAITQUEUE(wait, current); + u32 data; + unsigned char byte_data; + ssize_t retval = 1; + + if (count < 1) + return -EINVAL; + + add_wait_queue(&adev.misc_wait, &wait); + while (true) { + set_current_state(TASK_INTERRUPTIBLE); + data = atomic_xchg(&adev.count, 0); + if (data) + break; + + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + goto out; + } + + if (signal_pending(current)) { + retval = -ERESTARTSYS; + goto out; + } + + schedule(); + } + + if (data < 255) + byte_data = data; + else + byte_data = 255; + + /* make sure we are not going into copy_to_user() with + * TASK_INTERRUPTIBLE state */ + set_current_state(TASK_RUNNING); + if (copy_to_user(buf, &byte_data, sizeof(byte_data))) + retval = -EFAULT; + +out: + __set_current_state(TASK_RUNNING); + remove_wait_queue(&adev.misc_wait, &wait); + + return retval; +} + +static unsigned int lis3lv02d_misc_poll(struct file *file, poll_table *wait) +{ + poll_wait(file, &adev.misc_wait, wait); + if (atomic_read(&adev.count)) + return POLLIN | POLLRDNORM; + return 0; +} + +static int lis3lv02d_misc_fasync(int fd, struct file *file, int on) +{ + return fasync_helper(fd, file, on, &adev.async_queue); +} + +static const struct file_operations lis3lv02d_misc_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = lis3lv02d_misc_read, + .open = lis3lv02d_misc_open, + .release = lis3lv02d_misc_release, + .poll = lis3lv02d_misc_poll, + .fasync = lis3lv02d_misc_fasync, +}; + +static struct miscdevice lis3lv02d_misc_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "freefall", + .fops = &lis3lv02d_misc_fops, +}; + /** * lis3lv02d_joystick_kthread - Kthread polling function * @data: unused - here to conform to threadfn prototype @@ -203,7 +315,6 @@ static void lis3lv02d_joystick_close(struct input_dev *input) lis3lv02d_decrease_use(&adev); } - static inline void lis3lv02d_calibrate_joystick(void) { lis3lv02d_get_xyz(adev.device->handle, &adev.xcalib, &adev.ycalib, &adev.zcalib); @@ -231,9 +342,9 @@ int lis3lv02d_joystick_enable(void) adev.idev->close = lis3lv02d_joystick_close; set_bit(EV_ABS, adev.idev->evbit); - input_set_abs_params(adev.idev, ABS_X, -MDPS_MAX_VAL, MDPS_MAX_VAL, 3, 3); - input_set_abs_params(adev.idev, ABS_Y, -MDPS_MAX_VAL, MDPS_MAX_VAL, 3, 3); - input_set_abs_params(adev.idev, ABS_Z, -MDPS_MAX_VAL, MDPS_MAX_VAL, 3, 3); + input_set_abs_params(adev.idev, ABS_X, -adev.mdps_max_val, adev.mdps_max_val, 3, 3); + input_set_abs_params(adev.idev, ABS_Y, -adev.mdps_max_val, adev.mdps_max_val, 3, 3); + input_set_abs_params(adev.idev, ABS_Z, -adev.mdps_max_val, adev.mdps_max_val, 3, 3); err = input_register_device(adev.idev); if (err) { @@ -250,6 +361,7 @@ void lis3lv02d_joystick_disable(void) if (!adev.idev) return; + misc_deregister(&lis3lv02d_misc_device); input_unregister_device(adev.idev); adev.idev = NULL; } @@ -268,6 +380,19 @@ int lis3lv02d_init_device(struct acpi_lis3lv02d *dev) if (lis3lv02d_joystick_enable()) printk(KERN_ERR DRIVER_NAME ": joystick initialization failed\n"); + printk("lis3_init_device: irq %d\n", dev->irq); + + /* if we did not get an IRQ from ACPI - we have nothing more to do */ + if (!dev->irq) { + printk(KERN_ERR DRIVER_NAME + ": No IRQ in ACPI. Disabling /dev/freefall\n"); + goto out; + } + + printk("lis3: registering device\n"); + if (misc_register(&lis3lv02d_misc_device)) + printk(KERN_ERR DRIVER_NAME ": misc_register failed\n"); +out: lis3lv02d_decrease_use(dev); return 0; } @@ -351,6 +476,6 @@ int lis3lv02d_remove_fs(void) EXPORT_SYMBOL_GPL(lis3lv02d_remove_fs); MODULE_DESCRIPTION("ST LIS3LV02Dx three-axis digital accelerometer driver"); -MODULE_AUTHOR("Yan Burman and Eric Piel"); +MODULE_AUTHOR("Yan Burman, Eric Piel, Pavel Machek"); MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/lis3lv02d.h b/drivers/hwmon/lis3lv02d.h index 223f1c0763b..75972bf372f 100644 --- a/drivers/hwmon/lis3lv02d.h +++ b/drivers/hwmon/lis3lv02d.h @@ -22,12 +22,15 @@ /* * The actual chip is STMicroelectronics LIS3LV02DL or LIS3LV02DQ that seems to * be connected via SPI. There exists also several similar chips (such as LIS302DL or - * LIS3L02DQ) but not in the HP laptops and they have slightly different registers. + * LIS3L02DQ) and they have slightly different registers, but we can provide a + * common interface for all of them. * They can also be connected via I²C. */ -#define LIS3LV02DL_ID 0x3A /* Also the LIS3LV02DQ */ -#define LIS302DL_ID 0x3B /* Also the LIS202DL! */ +/* 2-byte registers */ +#define LIS_DOUBLE_ID 0x3A /* LIS3LV02D[LQ] */ +/* 1-byte registers */ +#define LIS_SINGLE_ID 0x3B /* LIS[32]02DL and others */ enum lis3lv02d_reg { WHO_AM_I = 0x0F, @@ -44,10 +47,13 @@ enum lis3lv02d_reg { STATUS_REG = 0x27, OUTX_L = 0x28, OUTX_H = 0x29, + OUTX = 0x29, OUTY_L = 0x2A, OUTY_H = 0x2B, + OUTY = 0x2B, OUTZ_L = 0x2C, OUTZ_H = 0x2D, + OUTZ = 0x2D, FF_WU_CFG = 0x30, FF_WU_SRC = 0x31, FF_WU_ACK = 0x32, @@ -159,6 +165,10 @@ struct acpi_lis3lv02d { acpi_status (*write) (acpi_handle handle, int reg, u8 val); acpi_status (*read) (acpi_handle handle, int reg, u8 *ret); + u8 whoami; /* 3Ah: 2-byte registries, 3Bh: 1-byte registries */ + s16 (*read_data) (acpi_handle handle, int reg); + int mdps_max_val; + struct input_dev *idev; /* input device */ struct task_struct *kthread; /* kthread for input */ struct mutex lock; @@ -170,6 +180,11 @@ struct acpi_lis3lv02d { unsigned char is_on; /* whether the device is on or off */ unsigned char usage; /* usage counter */ struct axis_conversion ac; /* hw -> logical axis */ + + u32 irq; /* IRQ number */ + struct fasync_struct *async_queue; /* queue for the misc device */ + wait_queue_head_t misc_wait; /* Wait queue for the misc device */ + unsigned long misc_opened; /* bit0: whether the device is open */ }; int lis3lv02d_init_device(struct acpi_lis3lv02d *dev); diff --git a/drivers/hwmon/vt1211.c b/drivers/hwmon/vt1211.c index b0ce3785228..73f77a9b8b1 100644 --- a/drivers/hwmon/vt1211.c +++ b/drivers/hwmon/vt1211.c @@ -1262,7 +1262,7 @@ static int __init vt1211_device_add(unsigned short address) res.name = pdev->name; err = acpi_check_resource_conflict(&res); if (err) - goto EXIT; + goto EXIT_DEV_PUT; err = platform_device_add_resources(pdev, &res, 1); if (err) { diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c index cb808d01536..feae743ba99 100644 --- a/drivers/hwmon/w83627ehf.c +++ b/drivers/hwmon/w83627ehf.c @@ -1548,7 +1548,7 @@ static int __init sensors_w83627ehf_init(void) err = acpi_check_resource_conflict(&res); if (err) - goto exit; + goto exit_device_put; err = platform_device_add_resources(pdev, &res, 1); if (err) { diff --git a/drivers/i2c/busses/i2c-acorn.c b/drivers/i2c/busses/i2c-acorn.c index 9fee3ca1734..9aefb5e5864 100644 --- a/drivers/i2c/busses/i2c-acorn.c +++ b/drivers/i2c/busses/i2c-acorn.c @@ -79,10 +79,11 @@ static struct i2c_algo_bit_data ioc_data = { .getsda = ioc_getsda, .getscl = ioc_getscl, .udelay = 80, - .timeout = 100 + .timeout = HZ, }; static struct i2c_adapter ioc_ops = { + .nr = 0, .algo_data = &ioc_data, }; @@ -90,7 +91,7 @@ static int __init i2c_ioc_init(void) { force_ones = FORCE_ONES | SCL | SDA; - return i2c_bit_add_bus(&ioc_ops); + return i2c_bit_add_numbered_bus(&ioc_ops); } module_init(i2c_ioc_init); diff --git a/drivers/i2c/busses/i2c-amd8111.c b/drivers/i2c/busses/i2c-amd8111.c index edab51973bf..a7c59908c45 100644 --- a/drivers/i2c/busses/i2c-amd8111.c +++ b/drivers/i2c/busses/i2c-amd8111.c @@ -72,7 +72,7 @@ static unsigned int amd_ec_wait_write(struct amd_smbus *smbus) { int timeout = 500; - while (timeout-- && (inb(smbus->base + AMD_EC_SC) & AMD_EC_SC_IBF)) + while ((inb(smbus->base + AMD_EC_SC) & AMD_EC_SC_IBF) && --timeout) udelay(1); if (!timeout) { @@ -88,7 +88,7 @@ static unsigned int amd_ec_wait_read(struct amd_smbus *smbus) { int timeout = 500; - while (timeout-- && (~inb(smbus->base + AMD_EC_SC) & AMD_EC_SC_OBF)) + while ((~inb(smbus->base + AMD_EC_SC) & AMD_EC_SC_OBF) && --timeout) udelay(1); if (!timeout) { diff --git a/drivers/i2c/busses/i2c-ixp2000.c b/drivers/i2c/busses/i2c-ixp2000.c index 8e846797048..c016f7a2c5f 100644 --- a/drivers/i2c/busses/i2c-ixp2000.c +++ b/drivers/i2c/busses/i2c-ixp2000.c @@ -114,7 +114,7 @@ static int ixp2000_i2c_probe(struct platform_device *plat_dev) drv_data->algo_data.getsda = ixp2000_bit_getsda; drv_data->algo_data.getscl = ixp2000_bit_getscl; drv_data->algo_data.udelay = 6; - drv_data->algo_data.timeout = 100; + drv_data->algo_data.timeout = HZ; strlcpy(drv_data->adapter.name, plat_dev->dev.driver->name, sizeof(drv_data->adapter.name)); diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c index 6af68146c34..bdb1f7510e9 100644 --- a/drivers/i2c/busses/i2c-pxa.c +++ b/drivers/i2c/busses/i2c-pxa.c @@ -644,7 +644,7 @@ static int i2c_pxa_do_pio_xfer(struct pxa_i2c *i2c, i2c_pxa_start_message(i2c); - while (timeout-- && i2c->msg_num > 0) { + while (i2c->msg_num > 0 && --timeout) { i2c_pxa_handler(0, i2c); udelay(10); } diff --git a/drivers/i2c/busses/scx200_i2c.c b/drivers/i2c/busses/scx200_i2c.c index 162b74a0488..42df0eca43d 100644 --- a/drivers/i2c/busses/scx200_i2c.c +++ b/drivers/i2c/busses/scx200_i2c.c @@ -76,7 +76,7 @@ static struct i2c_algo_bit_data scx200_i2c_data = { .getsda = scx200_i2c_getsda, .getscl = scx200_i2c_getscl, .udelay = 10, - .timeout = 100, + .timeout = HZ, }; static struct i2c_adapter scx200_i2c_ops = { diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index b1c9abe24c7..e7d984866de 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -1831,7 +1831,8 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr, case I2C_SMBUS_QUICK: msg[0].len = 0; /* Special case: The read/write field is used as data */ - msg[0].flags = flags | (read_write==I2C_SMBUS_READ)?I2C_M_RD:0; + msg[0].flags = flags | (read_write == I2C_SMBUS_READ ? + I2C_M_RD : 0); num = 1; break; case I2C_SMBUS_BYTE: diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c index c171988a9f5..7e13d2df9af 100644 --- a/drivers/i2c/i2c-dev.c +++ b/drivers/i2c/i2c-dev.c @@ -35,6 +35,7 @@ #include <linux/i2c.h> #include <linux/i2c-dev.h> #include <linux/smp_lock.h> +#include <linux/jiffies.h> #include <asm/uaccess.h> static struct i2c_driver i2cdev_driver; @@ -422,7 +423,10 @@ static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) client->adapter->retries = arg; break; case I2C_TIMEOUT: - client->adapter->timeout = arg; + /* For historical reasons, user-space sets the timeout + * value in units of 10 ms. + */ + client->adapter->timeout = msecs_to_jiffies(arg * 10); break; default: /* NOTE: returning a fault code here could cause trouble diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig index 3dad2299d9c..e072903b12f 100644 --- a/drivers/ide/Kconfig +++ b/drivers/ide/Kconfig @@ -46,7 +46,7 @@ menuconfig IDE SMART parameters from disk drives. To compile this driver as a module, choose M here: the - module will be called ide. + module will be called ide-core.ko. For further information, please read <file:Documentation/ide/ide.txt>. diff --git a/drivers/ide/amd74xx.c b/drivers/ide/amd74xx.c index 69660a431cd..77267c85996 100644 --- a/drivers/ide/amd74xx.c +++ b/drivers/ide/amd74xx.c @@ -166,7 +166,7 @@ static unsigned int init_chipset_amd74xx(struct pci_dev *dev) * Check for broken FIFO support. */ if (dev->vendor == PCI_VENDOR_ID_AMD && - dev->vendor == PCI_DEVICE_ID_AMD_VIPER_7411) + dev->device == PCI_DEVICE_ID_AMD_VIPER_7411) t &= 0x0f; else t |= 0xf0; diff --git a/drivers/ide/atiixp.c b/drivers/ide/atiixp.c index b2735d28f5c..ecd1e62ca91 100644 --- a/drivers/ide/atiixp.c +++ b/drivers/ide/atiixp.c @@ -52,7 +52,7 @@ static void atiixp_set_pio_mode(ide_drive_t *drive, const u8 pio) { struct pci_dev *dev = to_pci_dev(drive->hwif->dev); unsigned long flags; - int timing_shift = (drive->dn & 2) ? 16 : 0 + (drive->dn & 1) ? 0 : 8; + int timing_shift = (drive->dn ^ 1) * 8; u32 pio_timing_data; u16 pio_mode_data; @@ -85,7 +85,7 @@ static void atiixp_set_dma_mode(ide_drive_t *drive, const u8 speed) { struct pci_dev *dev = to_pci_dev(drive->hwif->dev); unsigned long flags; - int timing_shift = (drive->dn & 2) ? 16 : 0 + (drive->dn & 1) ? 0 : 8; + int timing_shift = (drive->dn ^ 1) * 8; u32 tmp32; u16 tmp16; u16 udma_ctl = 0; diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 0bfeb0c79d6..ddfbea41d29 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -55,7 +55,7 @@ static DEFINE_MUTEX(idecd_ref_mutex); -static void ide_cd_release(struct kref *); +static void ide_cd_release(struct device *); static struct cdrom_info *ide_cd_get(struct gendisk *disk) { @@ -67,7 +67,7 @@ static struct cdrom_info *ide_cd_get(struct gendisk *disk) if (ide_device_get(cd->drive)) cd = NULL; else - kref_get(&cd->kref); + get_device(&cd->dev); } mutex_unlock(&idecd_ref_mutex); @@ -79,7 +79,7 @@ static void ide_cd_put(struct cdrom_info *cd) ide_drive_t *drive = cd->drive; mutex_lock(&idecd_ref_mutex); - kref_put(&cd->kref, ide_cd_release); + put_device(&cd->dev); ide_device_put(drive); mutex_unlock(&idecd_ref_mutex); } @@ -194,6 +194,14 @@ static void cdrom_analyze_sense_data(ide_drive_t *drive, bio_sectors = max(bio_sectors(failed_command->bio), 4U); sector &= ~(bio_sectors - 1); + /* + * The SCSI specification allows for the value + * returned by READ CAPACITY to be up to 75 2K + * sectors past the last readable block. + * Therefore, if we hit a medium error within the + * last 75 2K sectors, we decrease the saved size + * value. + */ if (sector < get_capacity(info->disk) && drive->probed_capacity - sector < 4 * 75) set_capacity(info->disk, sector); @@ -1790,15 +1798,17 @@ static void ide_cd_remove(ide_drive_t *drive) ide_debug_log(IDE_DBG_FUNC, "Call %s\n", __func__); ide_proc_unregister_driver(drive, info->driver); - + device_del(&info->dev); del_gendisk(info->disk); - ide_cd_put(info); + mutex_lock(&idecd_ref_mutex); + put_device(&info->dev); + mutex_unlock(&idecd_ref_mutex); } -static void ide_cd_release(struct kref *kref) +static void ide_cd_release(struct device *dev) { - struct cdrom_info *info = to_ide_drv(kref, cdrom_info); + struct cdrom_info *info = to_ide_drv(dev, cdrom_info); struct cdrom_device_info *devinfo = &info->devinfo; ide_drive_t *drive = info->drive; struct gendisk *g = info->disk; @@ -1997,7 +2007,12 @@ static int ide_cd_probe(ide_drive_t *drive) ide_init_disk(g, drive); - kref_init(&info->kref); + info->dev.parent = &drive->gendev; + info->dev.release = ide_cd_release; + dev_set_name(&info->dev, dev_name(&drive->gendev)); + + if (device_register(&info->dev)) + goto out_free_disk; info->drive = drive; info->driver = &ide_cdrom_driver; @@ -2011,7 +2026,7 @@ static int ide_cd_probe(ide_drive_t *drive) g->driverfs_dev = &drive->gendev; g->flags = GENHD_FL_CD | GENHD_FL_REMOVABLE; if (ide_cdrom_setup(drive)) { - ide_cd_release(&info->kref); + put_device(&info->dev); goto failed; } @@ -2021,6 +2036,8 @@ static int ide_cd_probe(ide_drive_t *drive) add_disk(g); return 0; +out_free_disk: + put_disk(g); out_free_cd: kfree(info); failed: diff --git a/drivers/ide/ide-cd.h b/drivers/ide/ide-cd.h index ac40d6cb90a..c878bfcf111 100644 --- a/drivers/ide/ide-cd.h +++ b/drivers/ide/ide-cd.h @@ -80,7 +80,7 @@ struct cdrom_info { ide_drive_t *drive; struct ide_driver *driver; struct gendisk *disk; - struct kref kref; + struct device dev; /* Buffer for table of contents. NULL if we haven't allocated a TOC buffer for this device yet. */ diff --git a/drivers/ide/ide-gd.c b/drivers/ide/ide-gd.c index 7857b209c6d..04710941990 100644 --- a/drivers/ide/ide-gd.c +++ b/drivers/ide/ide-gd.c @@ -25,7 +25,7 @@ module_param(debug_mask, ulong, 0644); static DEFINE_MUTEX(ide_disk_ref_mutex); -static void ide_disk_release(struct kref *); +static void ide_disk_release(struct device *); static struct ide_disk_obj *ide_disk_get(struct gendisk *disk) { @@ -37,7 +37,7 @@ static struct ide_disk_obj *ide_disk_get(struct gendisk *disk) if (ide_device_get(idkp->drive)) idkp = NULL; else - kref_get(&idkp->kref); + get_device(&idkp->dev); } mutex_unlock(&ide_disk_ref_mutex); return idkp; @@ -48,7 +48,7 @@ static void ide_disk_put(struct ide_disk_obj *idkp) ide_drive_t *drive = idkp->drive; mutex_lock(&ide_disk_ref_mutex); - kref_put(&idkp->kref, ide_disk_release); + put_device(&idkp->dev); ide_device_put(drive); mutex_unlock(&ide_disk_ref_mutex); } @@ -66,17 +66,18 @@ static void ide_gd_remove(ide_drive_t *drive) struct gendisk *g = idkp->disk; ide_proc_unregister_driver(drive, idkp->driver); - + device_del(&idkp->dev); del_gendisk(g); - drive->disk_ops->flush(drive); - ide_disk_put(idkp); + mutex_lock(&ide_disk_ref_mutex); + put_device(&idkp->dev); + mutex_unlock(&ide_disk_ref_mutex); } -static void ide_disk_release(struct kref *kref) +static void ide_disk_release(struct device *dev) { - struct ide_disk_obj *idkp = to_ide_drv(kref, ide_disk_obj); + struct ide_disk_obj *idkp = to_ide_drv(dev, ide_disk_obj); ide_drive_t *drive = idkp->drive; struct gendisk *g = idkp->disk; @@ -348,7 +349,12 @@ static int ide_gd_probe(ide_drive_t *drive) ide_init_disk(g, drive); - kref_init(&idkp->kref); + idkp->dev.parent = &drive->gendev; + idkp->dev.release = ide_disk_release; + dev_set_name(&idkp->dev, dev_name(&drive->gendev)); + + if (device_register(&idkp->dev)) + goto out_free_disk; idkp->drive = drive; idkp->driver = &ide_gd_driver; @@ -373,6 +379,8 @@ static int ide_gd_probe(ide_drive_t *drive) add_disk(g); return 0; +out_free_disk: + put_disk(g); out_free_idkp: kfree(idkp); failed: diff --git a/drivers/ide/ide-gd.h b/drivers/ide/ide-gd.h index a86779f0756..b604bdd318a 100644 --- a/drivers/ide/ide-gd.h +++ b/drivers/ide/ide-gd.h @@ -17,7 +17,7 @@ struct ide_disk_obj { ide_drive_t *drive; struct ide_driver *driver; struct gendisk *disk; - struct kref kref; + struct device dev; unsigned int openers; /* protected by BKL for now */ /* Last failed packet command */ diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index d7ecd3c7975..bb450a7608c 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -169,7 +169,7 @@ typedef struct ide_tape_obj { ide_drive_t *drive; struct ide_driver *driver; struct gendisk *disk; - struct kref kref; + struct device dev; /* * failed_pc points to the last failed packet command, or contains @@ -267,7 +267,7 @@ static DEFINE_MUTEX(idetape_ref_mutex); static struct class *idetape_sysfs_class; -static void ide_tape_release(struct kref *); +static void ide_tape_release(struct device *); static struct ide_tape_obj *ide_tape_get(struct gendisk *disk) { @@ -279,7 +279,7 @@ static struct ide_tape_obj *ide_tape_get(struct gendisk *disk) if (ide_device_get(tape->drive)) tape = NULL; else - kref_get(&tape->kref); + get_device(&tape->dev); } mutex_unlock(&idetape_ref_mutex); return tape; @@ -290,7 +290,7 @@ static void ide_tape_put(struct ide_tape_obj *tape) ide_drive_t *drive = tape->drive; mutex_lock(&idetape_ref_mutex); - kref_put(&tape->kref, ide_tape_release); + put_device(&tape->dev); ide_device_put(drive); mutex_unlock(&idetape_ref_mutex); } @@ -308,7 +308,7 @@ static struct ide_tape_obj *ide_tape_chrdev_get(unsigned int i) mutex_lock(&idetape_ref_mutex); tape = idetape_devs[i]; if (tape) - kref_get(&tape->kref); + get_device(&tape->dev); mutex_unlock(&idetape_ref_mutex); return tape; } @@ -2256,15 +2256,17 @@ static void ide_tape_remove(ide_drive_t *drive) idetape_tape_t *tape = drive->driver_data; ide_proc_unregister_driver(drive, tape->driver); - + device_del(&tape->dev); ide_unregister_region(tape->disk); - ide_tape_put(tape); + mutex_lock(&idetape_ref_mutex); + put_device(&tape->dev); + mutex_unlock(&idetape_ref_mutex); } -static void ide_tape_release(struct kref *kref) +static void ide_tape_release(struct device *dev) { - struct ide_tape_obj *tape = to_ide_drv(kref, ide_tape_obj); + struct ide_tape_obj *tape = to_ide_drv(dev, ide_tape_obj); ide_drive_t *drive = tape->drive; struct gendisk *g = tape->disk; @@ -2407,7 +2409,12 @@ static int ide_tape_probe(ide_drive_t *drive) ide_init_disk(g, drive); - kref_init(&tape->kref); + tape->dev.parent = &drive->gendev; + tape->dev.release = ide_tape_release; + dev_set_name(&tape->dev, dev_name(&drive->gendev)); + + if (device_register(&tape->dev)) + goto out_free_disk; tape->drive = drive; tape->driver = &idetape_driver; @@ -2436,6 +2443,8 @@ static int ide_tape_probe(ide_drive_t *drive) return 0; +out_free_disk: + put_disk(g); out_free_tape: kfree(tape); failed: diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index 258805da15c..0920e3b0c96 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -337,6 +337,7 @@ static int ide_set_dev_param_mask(const char *s, struct kernel_param *kp) int a, b, i, j = 1; unsigned int *dev_param_mask = (unsigned int *)kp->arg; + /* controller . device (0 or 1) [ : 1 (set) | 0 (clear) ] */ if (sscanf(s, "%d.%d:%d", &a, &b, &j) != 3 && sscanf(s, "%d.%d", &a, &b) != 2) return -EINVAL; @@ -349,7 +350,7 @@ static int ide_set_dev_param_mask(const char *s, struct kernel_param *kp) if (j) *dev_param_mask |= (1 << i); else - *dev_param_mask &= (1 << i); + *dev_param_mask &= ~(1 << i); return 0; } @@ -392,6 +393,8 @@ static int ide_set_disk_chs(const char *str, struct kernel_param *kp) { int a, b, c = 0, h = 0, s = 0, i, j = 1; + /* controller . device (0 or 1) : Cylinders , Heads , Sectors */ + /* controller . device (0 or 1) : 1 (use CHS) | 0 (ignore CHS) */ if (sscanf(str, "%d.%d:%d,%d,%d", &a, &b, &c, &h, &s) != 5 && sscanf(str, "%d.%d:%d", &a, &b, &j) != 3) return -EINVAL; @@ -407,7 +410,7 @@ static int ide_set_disk_chs(const char *str, struct kernel_param *kp) if (j) ide_disks |= (1 << i); else - ide_disks &= (1 << i); + ide_disks &= ~(1 << i); ide_disks_chs[i].cyl = c; ide_disks_chs[i].head = h; @@ -469,6 +472,8 @@ static int ide_set_ignore_cable(const char *s, struct kernel_param *kp) { int i, j = 1; + /* controller (ignore) */ + /* controller : 1 (ignore) | 0 (use) */ if (sscanf(s, "%d:%d", &i, &j) != 2 && sscanf(s, "%d", &i) != 1) return -EINVAL; @@ -478,7 +483,7 @@ static int ide_set_ignore_cable(const char *s, struct kernel_param *kp) if (j) ide_ignore_cable |= (1 << i); else - ide_ignore_cable &= (1 << i); + ide_ignore_cable &= ~(1 << i); return 0; } diff --git a/drivers/ide/it821x.c b/drivers/ide/it821x.c index e1c4f543739..13b8153112e 100644 --- a/drivers/ide/it821x.c +++ b/drivers/ide/it821x.c @@ -5,9 +5,8 @@ * May be copied or modified under the terms of the GNU General Public License * Based in part on the ITE vendor provided SCSI driver. * - * Documentation available from - * http://www.ite.com.tw/pc/IT8212F_V04.pdf - * Some other documents are NDA. + * Documentation: + * Datasheet is freely available, some other documents under NDA. * * The ITE8212 isn't exactly a standard IDE controller. It has two * modes. In pass through mode then it is an IDE controller. In its smart diff --git a/drivers/ieee1394/dma.h b/drivers/ieee1394/dma.h index 2727bcd2419..467373cab8e 100644 --- a/drivers/ieee1394/dma.h +++ b/drivers/ieee1394/dma.h @@ -12,6 +12,7 @@ #include <asm/types.h> +struct file; struct pci_dev; struct scatterlist; struct vm_area_struct; diff --git a/drivers/ieee1394/ieee1394_core.c b/drivers/ieee1394/ieee1394_core.c index 2beb8d94f7b..87233800372 100644 --- a/drivers/ieee1394/ieee1394_core.c +++ b/drivers/ieee1394/ieee1394_core.c @@ -1275,7 +1275,7 @@ static void __exit ieee1394_cleanup(void) unregister_chrdev_region(IEEE1394_CORE_DEV, 256); } -module_init(ieee1394_init); +fs_initcall(ieee1394_init); module_exit(ieee1394_cleanup); /* Exported symbols */ @@ -1314,6 +1314,7 @@ EXPORT_SYMBOL(hpsb_make_lock64packet); EXPORT_SYMBOL(hpsb_make_phypacket); EXPORT_SYMBOL(hpsb_read); EXPORT_SYMBOL(hpsb_write); +EXPORT_SYMBOL(hpsb_lock); EXPORT_SYMBOL(hpsb_packet_success); /** highlevel.c **/ diff --git a/drivers/ieee1394/ieee1394_transactions.c b/drivers/ieee1394/ieee1394_transactions.c index 10c3d9f8c03..675b3135d5f 100644 --- a/drivers/ieee1394/ieee1394_transactions.c +++ b/drivers/ieee1394/ieee1394_transactions.c @@ -501,8 +501,6 @@ int hpsb_read(struct hpsb_host *host, nodeid_t node, unsigned int generation, if (length == 0) return -EINVAL; - BUG_ON(in_interrupt()); // We can't be called in an interrupt, yet - packet = hpsb_make_readpacket(host, node, addr, length); if (!packet) { @@ -550,8 +548,6 @@ int hpsb_write(struct hpsb_host *host, nodeid_t node, unsigned int generation, if (length == 0) return -EINVAL; - BUG_ON(in_interrupt()); // We can't be called in an interrupt, yet - packet = hpsb_make_writepacket(host, node, addr, buffer, length); if (!packet) @@ -570,3 +566,30 @@ int hpsb_write(struct hpsb_host *host, nodeid_t node, unsigned int generation, return retval; } + +int hpsb_lock(struct hpsb_host *host, nodeid_t node, unsigned int generation, + u64 addr, int extcode, quadlet_t *data, quadlet_t arg) +{ + struct hpsb_packet *packet; + int retval = 0; + + packet = hpsb_make_lockpacket(host, node, addr, extcode, data, arg); + if (!packet) + return -ENOMEM; + + packet->generation = generation; + retval = hpsb_send_packet_and_wait(packet); + if (retval < 0) + goto hpsb_lock_fail; + + retval = hpsb_packet_success(packet); + + if (retval == 0) + *data = packet->data[0]; + +hpsb_lock_fail: + hpsb_free_tlabel(packet); + hpsb_free_packet(packet); + + return retval; +} diff --git a/drivers/ieee1394/ieee1394_transactions.h b/drivers/ieee1394/ieee1394_transactions.h index d2d5bc3546d..20b693be14b 100644 --- a/drivers/ieee1394/ieee1394_transactions.h +++ b/drivers/ieee1394/ieee1394_transactions.h @@ -30,6 +30,8 @@ int hpsb_read(struct hpsb_host *host, nodeid_t node, unsigned int generation, u64 addr, quadlet_t *buffer, size_t length); int hpsb_write(struct hpsb_host *host, nodeid_t node, unsigned int generation, u64 addr, quadlet_t *buffer, size_t length); +int hpsb_lock(struct hpsb_host *host, nodeid_t node, unsigned int generation, + u64 addr, int extcode, quadlet_t *data, quadlet_t arg); #ifdef HPSB_DEBUG_TLABELS extern spinlock_t hpsb_tlabel_lock; diff --git a/drivers/ieee1394/iso.h b/drivers/ieee1394/iso.h index b5de5f21ef7..c2089c093aa 100644 --- a/drivers/ieee1394/iso.h +++ b/drivers/ieee1394/iso.h @@ -13,6 +13,7 @@ #define IEEE1394_ISO_H #include <linux/spinlock_types.h> +#include <linux/wait.h> #include <asm/atomic.h> #include <asm/types.h> diff --git a/drivers/ieee1394/nodemgr.c b/drivers/ieee1394/nodemgr.c index 906c5a98d81..53aada5bbe1 100644 --- a/drivers/ieee1394/nodemgr.c +++ b/drivers/ieee1394/nodemgr.c @@ -971,6 +971,9 @@ static struct unit_directory *nodemgr_process_unit_directory ud->ud_kv = ud_kv; ud->id = (*id)++; + /* inherit vendor_id from root directory if none exists in unit dir */ + ud->vendor_id = ne->vendor_id; + csr1212_for_each_dir_entry(ne->csr, kv, ud_kv, dentry) { switch (kv->key.id) { case CSR1212_KV_ID_VENDOR: @@ -1265,7 +1268,8 @@ static void nodemgr_update_node(struct node_entry *ne, struct csr1212_csr *csr, csr1212_destroy_csr(csr); } - /* Mark the node current */ + /* Finally, mark the node current */ + smp_wmb(); ne->generation = generation; if (ne->in_limbo) { @@ -1798,7 +1802,7 @@ void hpsb_node_fill_packet(struct node_entry *ne, struct hpsb_packet *packet) { packet->host = ne->host; packet->generation = ne->generation; - barrier(); + smp_rmb(); packet->node_id = ne->nodeid; } @@ -1807,7 +1811,7 @@ int hpsb_node_write(struct node_entry *ne, u64 addr, { unsigned int generation = ne->generation; - barrier(); + smp_rmb(); return hpsb_write(ne->host, ne->nodeid, generation, addr, buffer, length); } diff --git a/drivers/ieee1394/nodemgr.h b/drivers/ieee1394/nodemgr.h index 15ea09733e8..ee5acdbd114 100644 --- a/drivers/ieee1394/nodemgr.h +++ b/drivers/ieee1394/nodemgr.h @@ -21,9 +21,11 @@ #define _IEEE1394_NODEMGR_H #include <linux/device.h> +#include <asm/system.h> #include <asm/types.h> #include "ieee1394_core.h" +#include "ieee1394_transactions.h" #include "ieee1394_types.h" struct csr1212_csr; @@ -154,6 +156,22 @@ static inline int hpsb_node_entry_valid(struct node_entry *ne) void hpsb_node_fill_packet(struct node_entry *ne, struct hpsb_packet *packet); int hpsb_node_write(struct node_entry *ne, u64 addr, quadlet_t *buffer, size_t length); +static inline int hpsb_node_read(struct node_entry *ne, u64 addr, + quadlet_t *buffer, size_t length) +{ + unsigned int g = ne->generation; + + smp_rmb(); + return hpsb_read(ne->host, ne->nodeid, g, addr, buffer, length); +} +static inline int hpsb_node_lock(struct node_entry *ne, u64 addr, int extcode, + quadlet_t *buffer, quadlet_t arg) +{ + unsigned int g = ne->generation; + + smp_rmb(); + return hpsb_lock(ne->host, ne->nodeid, g, addr, extcode, buffer, arg); +} int nodemgr_for_each_host(void *data, int (*cb)(struct hpsb_host *, void *)); int init_ieee1394_nodemgr(void); diff --git a/drivers/isdn/sc/shmem.c b/drivers/isdn/sc/shmem.c index 712220cef13..7f16d75d2d8 100644 --- a/drivers/isdn/sc/shmem.c +++ b/drivers/isdn/sc/shmem.c @@ -54,7 +54,7 @@ void memcpy_toshmem(int card, void *dest, const void *src, size_t n) spin_unlock_irqrestore(&sc_adapter[card]->lock, flags); pr_debug("%s: set page to %#x\n",sc_adapter[card]->devicename, ((sc_adapter[card]->shmem_magic + ch * SRAM_PAGESIZE)>>14)|0x80); - pr_debug("%s: copying %d bytes from %#lx to %#lx\n", + pr_debug("%s: copying %zu bytes from %#lx to %#lx\n", sc_adapter[card]->devicename, n, (unsigned long) src, sc_adapter[card]->rambase + ((unsigned long) dest %0x4000)); diff --git a/drivers/md/dm-io.c b/drivers/md/dm-io.c index a34338567a2..f14813be4ef 100644 --- a/drivers/md/dm-io.c +++ b/drivers/md/dm-io.c @@ -328,7 +328,7 @@ static void dispatch_io(int rw, unsigned int num_regions, struct dpages old_pages = *dp; if (sync) - rw |= (1 << BIO_RW_SYNC); + rw |= (1 << BIO_RW_SYNCIO) | (1 << BIO_RW_UNPLUG); /* * For multiple regions we need to be careful to rewind diff --git a/drivers/md/dm-kcopyd.c b/drivers/md/dm-kcopyd.c index 3073618269e..0a225da2127 100644 --- a/drivers/md/dm-kcopyd.c +++ b/drivers/md/dm-kcopyd.c @@ -344,7 +344,7 @@ static int run_io_job(struct kcopyd_job *job) { int r; struct dm_io_request io_req = { - .bi_rw = job->rw | (1 << BIO_RW_SYNC), + .bi_rw = job->rw | (1 << BIO_RW_SYNCIO) | (1 << BIO_RW_UNPLUG), .mem.type = DM_IO_PAGE_LIST, .mem.ptr.pl = job->pages, .mem.offset = job->offset, diff --git a/drivers/md/md.c b/drivers/md/md.c index 4495104f6c9..03b4cd0a634 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -474,7 +474,7 @@ void md_super_write(mddev_t *mddev, mdk_rdev_t *rdev, * causes ENOTSUPP, we allocate a spare bio... */ struct bio *bio = bio_alloc(GFP_NOIO, 1); - int rw = (1<<BIO_RW) | (1<<BIO_RW_SYNC); + int rw = (1<<BIO_RW) | (1<<BIO_RW_SYNCIO) | (1<<BIO_RW_UNPLUG); bio->bi_bdev = rdev->bdev; bio->bi_sector = sector; @@ -531,7 +531,7 @@ int sync_page_io(struct block_device *bdev, sector_t sector, int size, struct completion event; int ret; - rw |= (1 << BIO_RW_SYNC); + rw |= (1 << BIO_RW_SYNCIO) | (1 << BIO_RW_UNPLUG); bio->bi_bdev = bdev; bio->bi_sector = sector; diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 01e3cffd03b..e2466425d9c 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -1237,8 +1237,9 @@ static void end_sync_write(struct bio *bio, int error) update_head_pos(mirror, r1_bio); if (atomic_dec_and_test(&r1_bio->remaining)) { - md_done_sync(mddev, r1_bio->sectors, uptodate); + sector_t s = r1_bio->sectors; put_buf(r1_bio); + md_done_sync(mddev, s, uptodate); } } diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 6736d6dff98..7301631abe0 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -1236,6 +1236,7 @@ static void end_sync_read(struct bio *bio, int error) /* for reconstruct, we always reschedule after a read. * for resync, only after all reads */ + rdev_dec_pending(conf->mirrors[d].rdev, conf->mddev); if (test_bit(R10BIO_IsRecover, &r10_bio->state) || atomic_dec_and_test(&r10_bio->remaining)) { /* we have read all the blocks, @@ -1243,7 +1244,6 @@ static void end_sync_read(struct bio *bio, int error) */ reschedule_retry(r10_bio); } - rdev_dec_pending(conf->mirrors[d].rdev, conf->mddev); } static void end_sync_write(struct bio *bio, int error) @@ -1264,11 +1264,13 @@ static void end_sync_write(struct bio *bio, int error) update_head_pos(i, r10_bio); + rdev_dec_pending(conf->mirrors[d].rdev, mddev); while (atomic_dec_and_test(&r10_bio->remaining)) { if (r10_bio->master_bio == NULL) { /* the primary of several recovery bios */ - md_done_sync(mddev, r10_bio->sectors, 1); + sector_t s = r10_bio->sectors; put_buf(r10_bio); + md_done_sync(mddev, s, 1); break; } else { r10bio_t *r10_bio2 = (r10bio_t *)r10_bio->master_bio; @@ -1276,7 +1278,6 @@ static void end_sync_write(struct bio *bio, int error) r10_bio = r10_bio2; } } - rdev_dec_pending(conf->mirrors[d].rdev, mddev); } /* @@ -1749,8 +1750,6 @@ static sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *skipped, i if (!go_faster && conf->nr_waiting) msleep_interruptible(1000); - bitmap_cond_end_sync(mddev->bitmap, sector_nr); - /* Again, very different code for resync and recovery. * Both must result in an r10bio with a list of bios that * have bi_end_io, bi_sector, bi_bdev set, @@ -1886,6 +1885,8 @@ static sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *skipped, i /* resync. Schedule a read for every block at this virt offset */ int count = 0; + bitmap_cond_end_sync(mddev->bitmap, sector_nr); + if (!bitmap_start_sync(mddev->bitmap, sector_nr, &sync_blocks, mddev->degraded) && !conf->fullsync && !test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery)) { @@ -2010,13 +2011,13 @@ static sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *skipped, i /* There is nowhere to write, so all non-sync * drives must be failed, so try the next chunk... */ - { - sector_t sec = max_sector - sector_nr; - sectors_skipped += sec; + if (sector_nr + max_sync < max_sector) + max_sector = sector_nr + max_sync; + + sectors_skipped += (max_sector - sector_nr); chunks_skipped ++; sector_nr = max_sector; goto skipped; - } } static int run(mddev_t *mddev) diff --git a/drivers/media/common/tuners/tuner-simple.c b/drivers/media/common/tuners/tuner-simple.c index de7adaf5fa5..78412c9c424 100644 --- a/drivers/media/common/tuners/tuner-simple.c +++ b/drivers/media/common/tuners/tuner-simple.c @@ -318,7 +318,6 @@ static int simple_std_setup(struct dvb_frontend *fe, u8 *config, u8 *cb) { struct tuner_simple_priv *priv = fe->tuner_priv; - u8 tuneraddr; int rc; /* tv norm specific stuff for multi-norm tuners */ @@ -387,6 +386,7 @@ static int simple_std_setup(struct dvb_frontend *fe, case TUNER_PHILIPS_TUV1236D: { + struct tuner_i2c_props i2c = priv->i2c_props; /* 0x40 -> ATSC antenna input 1 */ /* 0x48 -> ATSC antenna input 2 */ /* 0x00 -> NTSC antenna input 1 */ @@ -398,17 +398,15 @@ static int simple_std_setup(struct dvb_frontend *fe, buffer[1] = 0x04; } /* set to the correct mode (analog or digital) */ - tuneraddr = priv->i2c_props.addr; - priv->i2c_props.addr = 0x0a; - rc = tuner_i2c_xfer_send(&priv->i2c_props, &buffer[0], 2); + i2c.addr = 0x0a; + rc = tuner_i2c_xfer_send(&i2c, &buffer[0], 2); if (2 != rc) tuner_warn("i2c i/o error: rc == %d " "(should be 2)\n", rc); - rc = tuner_i2c_xfer_send(&priv->i2c_props, &buffer[2], 2); + rc = tuner_i2c_xfer_send(&i2c, &buffer[2], 2); if (2 != rc) tuner_warn("i2c i/o error: rc == %d " "(should be 2)\n", rc); - priv->i2c_props.addr = tuneraddr; break; } } diff --git a/drivers/media/dvb/Kconfig b/drivers/media/dvb/Kconfig index 40ebde53b3c..b0198691892 100644 --- a/drivers/media/dvb/Kconfig +++ b/drivers/media/dvb/Kconfig @@ -51,6 +51,10 @@ comment "Supported SDMC DM1105 Adapters" depends on DVB_CORE && PCI && I2C source "drivers/media/dvb/dm1105/Kconfig" +comment "Supported FireWire (IEEE 1394) Adapters" + depends on DVB_CORE && IEEE1394 +source "drivers/media/dvb/firewire/Kconfig" + comment "Supported DVB Frontends" depends on DVB_CORE source "drivers/media/dvb/frontends/Kconfig" diff --git a/drivers/media/dvb/Makefile b/drivers/media/dvb/Makefile index f91e9eb15e5..6092a5bb5a7 100644 --- a/drivers/media/dvb/Makefile +++ b/drivers/media/dvb/Makefile @@ -3,3 +3,5 @@ # obj-y := dvb-core/ frontends/ ttpci/ ttusb-dec/ ttusb-budget/ b2c2/ bt8xx/ dvb-usb/ pluto2/ siano/ dm1105/ + +obj-$(CONFIG_DVB_FIREDTV) += firewire/ diff --git a/drivers/media/dvb/dvb-core/dmxdev.c b/drivers/media/dvb/dvb-core/dmxdev.c index 0c733c66a44..069d847ba88 100644 --- a/drivers/media/dvb/dvb-core/dmxdev.c +++ b/drivers/media/dvb/dvb-core/dmxdev.c @@ -364,16 +364,15 @@ static int dvb_dmxdev_section_callback(const u8 *buffer1, size_t buffer1_len, enum dmx_success success) { struct dmxdev_filter *dmxdevfilter = filter->priv; - unsigned long flags; int ret; if (dmxdevfilter->buffer.error) { wake_up(&dmxdevfilter->buffer.queue); return 0; } - spin_lock_irqsave(&dmxdevfilter->dev->lock, flags); + spin_lock(&dmxdevfilter->dev->lock); if (dmxdevfilter->state != DMXDEV_STATE_GO) { - spin_unlock_irqrestore(&dmxdevfilter->dev->lock, flags); + spin_unlock(&dmxdevfilter->dev->lock); return 0; } del_timer(&dmxdevfilter->timer); @@ -392,7 +391,7 @@ static int dvb_dmxdev_section_callback(const u8 *buffer1, size_t buffer1_len, } if (dmxdevfilter->params.sec.flags & DMX_ONESHOT) dmxdevfilter->state = DMXDEV_STATE_DONE; - spin_unlock_irqrestore(&dmxdevfilter->dev->lock, flags); + spin_unlock(&dmxdevfilter->dev->lock); wake_up(&dmxdevfilter->buffer.queue); return 0; } @@ -404,12 +403,11 @@ static int dvb_dmxdev_ts_callback(const u8 *buffer1, size_t buffer1_len, { struct dmxdev_filter *dmxdevfilter = feed->priv; struct dvb_ringbuffer *buffer; - unsigned long flags; int ret; - spin_lock_irqsave(&dmxdevfilter->dev->lock, flags); + spin_lock(&dmxdevfilter->dev->lock); if (dmxdevfilter->params.pes.output == DMX_OUT_DECODER) { - spin_unlock_irqrestore(&dmxdevfilter->dev->lock, flags); + spin_unlock(&dmxdevfilter->dev->lock); return 0; } @@ -419,7 +417,7 @@ static int dvb_dmxdev_ts_callback(const u8 *buffer1, size_t buffer1_len, else buffer = &dmxdevfilter->dev->dvr_buffer; if (buffer->error) { - spin_unlock_irqrestore(&dmxdevfilter->dev->lock, flags); + spin_unlock(&dmxdevfilter->dev->lock); wake_up(&buffer->queue); return 0; } @@ -430,7 +428,7 @@ static int dvb_dmxdev_ts_callback(const u8 *buffer1, size_t buffer1_len, dvb_ringbuffer_flush(buffer); buffer->error = ret; } - spin_unlock_irqrestore(&dmxdevfilter->dev->lock, flags); + spin_unlock(&dmxdevfilter->dev->lock); wake_up(&buffer->queue); return 0; } diff --git a/drivers/media/dvb/dvb-core/dvb_demux.c b/drivers/media/dvb/dvb-core/dvb_demux.c index a2c1fd5d2f6..e2eca0b1fe7 100644 --- a/drivers/media/dvb/dvb-core/dvb_demux.c +++ b/drivers/media/dvb/dvb-core/dvb_demux.c @@ -399,9 +399,7 @@ static void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf) void dvb_dmx_swfilter_packets(struct dvb_demux *demux, const u8 *buf, size_t count) { - unsigned long flags; - - spin_lock_irqsave(&demux->lock, flags); + spin_lock(&demux->lock); while (count--) { if (buf[0] == 0x47) @@ -409,17 +407,16 @@ void dvb_dmx_swfilter_packets(struct dvb_demux *demux, const u8 *buf, buf += 188; } - spin_unlock_irqrestore(&demux->lock, flags); + spin_unlock(&demux->lock); } EXPORT_SYMBOL(dvb_dmx_swfilter_packets); void dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf, size_t count) { - unsigned long flags; int p = 0, i, j; - spin_lock_irqsave(&demux->lock, flags); + spin_lock(&demux->lock); if (demux->tsbufp) { i = demux->tsbufp; @@ -452,18 +449,17 @@ void dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf, size_t count) } bailout: - spin_unlock_irqrestore(&demux->lock, flags); + spin_unlock(&demux->lock); } EXPORT_SYMBOL(dvb_dmx_swfilter); void dvb_dmx_swfilter_204(struct dvb_demux *demux, const u8 *buf, size_t count) { - unsigned long flags; int p = 0, i, j; u8 tmppack[188]; - spin_lock_irqsave(&demux->lock, flags); + spin_lock(&demux->lock); if (demux->tsbufp) { i = demux->tsbufp; @@ -504,7 +500,7 @@ void dvb_dmx_swfilter_204(struct dvb_demux *demux, const u8 *buf, size_t count) } bailout: - spin_unlock_irqrestore(&demux->lock, flags); + spin_unlock(&demux->lock); } EXPORT_SYMBOL(dvb_dmx_swfilter_204); diff --git a/drivers/media/dvb/firewire/Kconfig b/drivers/media/dvb/firewire/Kconfig new file mode 100644 index 00000000000..69028253e98 --- /dev/null +++ b/drivers/media/dvb/firewire/Kconfig @@ -0,0 +1,22 @@ +config DVB_FIREDTV + tristate "FireDTV and FloppyDTV" + depends on DVB_CORE && IEEE1394 + help + Support for DVB receivers from Digital Everywhere + which are connected via IEEE 1394 (FireWire). + + These devices don't have an MPEG decoder built in, + so you need an external software decoder to watch TV. + + To compile this driver as a module, say M here: + the module will be called firedtv. + +if DVB_FIREDTV + +config DVB_FIREDTV_IEEE1394 + def_bool IEEE1394 + +config DVB_FIREDTV_INPUT + def_bool INPUT = y || (INPUT = m && DVB_FIREDTV = m) + +endif # DVB_FIREDTV diff --git a/drivers/media/dvb/firewire/Makefile b/drivers/media/dvb/firewire/Makefile new file mode 100644 index 00000000000..2034695ba19 --- /dev/null +++ b/drivers/media/dvb/firewire/Makefile @@ -0,0 +1,8 @@ +obj-$(CONFIG_DVB_FIREDTV) += firedtv.o + +firedtv-y := firedtv-avc.o firedtv-ci.o firedtv-dvb.o firedtv-fe.o +firedtv-$(CONFIG_DVB_FIREDTV_IEEE1394) += firedtv-1394.o +firedtv-$(CONFIG_DVB_FIREDTV_INPUT) += firedtv-rc.o + +ccflags-y += -Idrivers/media/dvb/dvb-core +ccflags-$(CONFIG_DVB_FIREDTV_IEEE1394) += -Idrivers/ieee1394 diff --git a/drivers/media/dvb/firewire/firedtv-1394.c b/drivers/media/dvb/firewire/firedtv-1394.c new file mode 100644 index 00000000000..4e207658c5d --- /dev/null +++ b/drivers/media/dvb/firewire/firedtv-1394.c @@ -0,0 +1,285 @@ +/* + * FireDTV driver (formerly known as FireSAT) + * + * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> + * Copyright (C) 2007-2008 Ben Backx <ben@bbackx.com> + * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/spinlock.h> +#include <linux/types.h> + +#include <dma.h> +#include <csr1212.h> +#include <highlevel.h> +#include <hosts.h> +#include <ieee1394.h> +#include <iso.h> +#include <nodemgr.h> + +#include "firedtv.h" + +static LIST_HEAD(node_list); +static DEFINE_SPINLOCK(node_list_lock); + +#define FIREWIRE_HEADER_SIZE 4 +#define CIP_HEADER_SIZE 8 + +static void rawiso_activity_cb(struct hpsb_iso *iso) +{ + struct firedtv *f, *fdtv = NULL; + unsigned int i, num, packet; + unsigned char *buf; + unsigned long flags; + int count; + + spin_lock_irqsave(&node_list_lock, flags); + list_for_each_entry(f, &node_list, list) + if (f->backend_data == iso) { + fdtv = f; + break; + } + spin_unlock_irqrestore(&node_list_lock, flags); + + packet = iso->first_packet; + num = hpsb_iso_n_ready(iso); + + if (!fdtv) { + dev_err(fdtv->device, "received at unknown iso channel\n"); + goto out; + } + + for (i = 0; i < num; i++, packet = (packet + 1) % iso->buf_packets) { + buf = dma_region_i(&iso->data_buf, unsigned char, + iso->infos[packet].offset + CIP_HEADER_SIZE); + count = (iso->infos[packet].len - CIP_HEADER_SIZE) / + (188 + FIREWIRE_HEADER_SIZE); + + /* ignore empty packet */ + if (iso->infos[packet].len <= CIP_HEADER_SIZE) + continue; + + while (count--) { + if (buf[FIREWIRE_HEADER_SIZE] == 0x47) + dvb_dmx_swfilter_packets(&fdtv->demux, + &buf[FIREWIRE_HEADER_SIZE], 1); + else + dev_err(fdtv->device, + "skipping invalid packet\n"); + buf += 188 + FIREWIRE_HEADER_SIZE; + } + } +out: + hpsb_iso_recv_release_packets(iso, num); +} + +static inline struct node_entry *node_of(struct firedtv *fdtv) +{ + return container_of(fdtv->device, struct unit_directory, device)->ne; +} + +static int node_lock(struct firedtv *fdtv, u64 addr, void *data, __be32 arg) +{ + return hpsb_node_lock(node_of(fdtv), addr, EXTCODE_COMPARE_SWAP, data, + (__force quadlet_t)arg); +} + +static int node_read(struct firedtv *fdtv, u64 addr, void *data, size_t len) +{ + return hpsb_node_read(node_of(fdtv), addr, data, len); +} + +static int node_write(struct firedtv *fdtv, u64 addr, void *data, size_t len) +{ + return hpsb_node_write(node_of(fdtv), addr, data, len); +} + +#define FDTV_ISO_BUFFER_PACKETS 256 +#define FDTV_ISO_BUFFER_SIZE (FDTV_ISO_BUFFER_PACKETS * 200) + +static int start_iso(struct firedtv *fdtv) +{ + struct hpsb_iso *iso_handle; + int ret; + + iso_handle = hpsb_iso_recv_init(node_of(fdtv)->host, + FDTV_ISO_BUFFER_SIZE, FDTV_ISO_BUFFER_PACKETS, + fdtv->isochannel, HPSB_ISO_DMA_DEFAULT, + -1, /* stat.config.irq_interval */ + rawiso_activity_cb); + if (iso_handle == NULL) { + dev_err(fdtv->device, "cannot initialize iso receive\n"); + return -ENOMEM; + } + fdtv->backend_data = iso_handle; + + ret = hpsb_iso_recv_start(iso_handle, -1, -1, 0); + if (ret != 0) { + dev_err(fdtv->device, "cannot start iso receive\n"); + hpsb_iso_shutdown(iso_handle); + fdtv->backend_data = NULL; + } + return ret; +} + +static void stop_iso(struct firedtv *fdtv) +{ + struct hpsb_iso *iso_handle = fdtv->backend_data; + + if (iso_handle != NULL) { + hpsb_iso_stop(iso_handle); + hpsb_iso_shutdown(iso_handle); + } + fdtv->backend_data = NULL; +} + +static const struct firedtv_backend fdtv_1394_backend = { + .lock = node_lock, + .read = node_read, + .write = node_write, + .start_iso = start_iso, + .stop_iso = stop_iso, +}; + +static void fcp_request(struct hpsb_host *host, int nodeid, int direction, + int cts, u8 *data, size_t length) +{ + struct firedtv *f, *fdtv = NULL; + unsigned long flags; + int su; + + if (length == 0 || (data[0] & 0xf0) != 0) + return; + + su = data[1] & 0x7; + + spin_lock_irqsave(&node_list_lock, flags); + list_for_each_entry(f, &node_list, list) + if (node_of(f)->host == host && + node_of(f)->nodeid == nodeid && + (f->subunit == su || (f->subunit == 0 && su == 0x7))) { + fdtv = f; + break; + } + spin_unlock_irqrestore(&node_list_lock, flags); + + if (fdtv) + avc_recv(fdtv, data, length); +} + +static int node_probe(struct device *dev) +{ + struct unit_directory *ud = + container_of(dev, struct unit_directory, device); + struct firedtv *fdtv; + int kv_len, err; + void *kv_str; + + kv_len = (ud->model_name_kv->value.leaf.len - 2) * sizeof(quadlet_t); + kv_str = CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(ud->model_name_kv); + + fdtv = fdtv_alloc(dev, &fdtv_1394_backend, kv_str, kv_len); + if (!fdtv) + return -ENOMEM; + + /* + * Work around a bug in udev's path_id script: Use the fw-host's dev + * instead of the unit directory's dev as parent of the input device. + */ + err = fdtv_register_rc(fdtv, dev->parent->parent); + if (err) + goto fail_free; + + spin_lock_irq(&node_list_lock); + list_add_tail(&fdtv->list, &node_list); + spin_unlock_irq(&node_list_lock); + + err = avc_identify_subunit(fdtv); + if (err) + goto fail; + + err = fdtv_dvb_register(fdtv); + if (err) + goto fail; + + avc_register_remote_control(fdtv); + return 0; +fail: + spin_lock_irq(&node_list_lock); + list_del(&fdtv->list); + spin_unlock_irq(&node_list_lock); + fdtv_unregister_rc(fdtv); +fail_free: + kfree(fdtv); + return err; +} + +static int node_remove(struct device *dev) +{ + struct firedtv *fdtv = dev->driver_data; + + fdtv_dvb_unregister(fdtv); + + spin_lock_irq(&node_list_lock); + list_del(&fdtv->list); + spin_unlock_irq(&node_list_lock); + + cancel_work_sync(&fdtv->remote_ctrl_work); + fdtv_unregister_rc(fdtv); + + kfree(fdtv); + return 0; +} + +static int node_update(struct unit_directory *ud) +{ + struct firedtv *fdtv = ud->device.driver_data; + + if (fdtv->isochannel >= 0) + cmp_establish_pp_connection(fdtv, fdtv->subunit, + fdtv->isochannel); + return 0; +} + +static struct hpsb_protocol_driver fdtv_driver = { + .name = "firedtv", + .update = node_update, + .driver = { + .probe = node_probe, + .remove = node_remove, + }, +}; + +static struct hpsb_highlevel fdtv_highlevel = { + .name = "firedtv", + .fcp_request = fcp_request, +}; + +int __init fdtv_1394_init(struct ieee1394_device_id id_table[]) +{ + int ret; + + hpsb_register_highlevel(&fdtv_highlevel); + fdtv_driver.id_table = id_table; + ret = hpsb_register_protocol(&fdtv_driver); + if (ret) { + printk(KERN_ERR "firedtv: failed to register protocol\n"); + hpsb_unregister_highlevel(&fdtv_highlevel); + } + return ret; +} + +void __exit fdtv_1394_exit(void) +{ + hpsb_unregister_protocol(&fdtv_driver); + hpsb_unregister_highlevel(&fdtv_highlevel); +} diff --git a/drivers/media/dvb/firewire/firedtv-avc.c b/drivers/media/dvb/firewire/firedtv-avc.c new file mode 100644 index 00000000000..b55d9ccaf33 --- /dev/null +++ b/drivers/media/dvb/firewire/firedtv-avc.c @@ -0,0 +1,1315 @@ +/* + * FireDTV driver (formerly known as FireSAT) + * + * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> + * Copyright (C) 2008 Ben Backx <ben@bbackx.com> + * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> + * + * 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/bug.h> +#include <linux/crc32.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/jiffies.h> +#include <linux/kernel.h> +#include <linux/moduleparam.h> +#include <linux/mutex.h> +#include <linux/string.h> +#include <linux/stringify.h> +#include <linux/wait.h> +#include <linux/workqueue.h> + +#include "firedtv.h" + +#define FCP_COMMAND_REGISTER 0xfffff0000b00ULL + +#define AVC_CTYPE_CONTROL 0x0 +#define AVC_CTYPE_STATUS 0x1 +#define AVC_CTYPE_NOTIFY 0x3 + +#define AVC_RESPONSE_ACCEPTED 0x9 +#define AVC_RESPONSE_STABLE 0xc +#define AVC_RESPONSE_CHANGED 0xd +#define AVC_RESPONSE_INTERIM 0xf + +#define AVC_SUBUNIT_TYPE_TUNER (0x05 << 3) +#define AVC_SUBUNIT_TYPE_UNIT (0x1f << 3) + +#define AVC_OPCODE_VENDOR 0x00 +#define AVC_OPCODE_READ_DESCRIPTOR 0x09 +#define AVC_OPCODE_DSIT 0xc8 +#define AVC_OPCODE_DSD 0xcb + +#define DESCRIPTOR_TUNER_STATUS 0x80 +#define DESCRIPTOR_SUBUNIT_IDENTIFIER 0x00 + +#define SFE_VENDOR_DE_COMPANYID_0 0x00 /* OUI of Digital Everywhere */ +#define SFE_VENDOR_DE_COMPANYID_1 0x12 +#define SFE_VENDOR_DE_COMPANYID_2 0x87 + +#define SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL 0x0a +#define SFE_VENDOR_OPCODE_LNB_CONTROL 0x52 +#define SFE_VENDOR_OPCODE_TUNE_QPSK 0x58 /* for DVB-S */ + +#define SFE_VENDOR_OPCODE_GET_FIRMWARE_VERSION 0x00 +#define SFE_VENDOR_OPCODE_HOST2CA 0x56 +#define SFE_VENDOR_OPCODE_CA2HOST 0x57 +#define SFE_VENDOR_OPCODE_CISTATUS 0x59 +#define SFE_VENDOR_OPCODE_TUNE_QPSK2 0x60 /* for DVB-S2 */ + +#define SFE_VENDOR_TAG_CA_RESET 0x00 +#define SFE_VENDOR_TAG_CA_APPLICATION_INFO 0x01 +#define SFE_VENDOR_TAG_CA_PMT 0x02 +#define SFE_VENDOR_TAG_CA_DATE_TIME 0x04 +#define SFE_VENDOR_TAG_CA_MMI 0x05 +#define SFE_VENDOR_TAG_CA_ENTER_MENU 0x07 + +#define EN50221_LIST_MANAGEMENT_ONLY 0x03 +#define EN50221_TAG_APP_INFO 0x9f8021 +#define EN50221_TAG_CA_INFO 0x9f8031 + +struct avc_command_frame { + int length; + u8 ctype; + u8 subunit; + u8 opcode; + u8 operand[509]; +}; + +struct avc_response_frame { + int length; + u8 response; + u8 subunit; + u8 opcode; + u8 operand[509]; +}; + +#define AVC_DEBUG_FCP_SUBACTIONS 1 +#define AVC_DEBUG_FCP_PAYLOADS 2 + +static int avc_debug; +module_param_named(debug, avc_debug, int, 0644); +MODULE_PARM_DESC(debug, "Verbose logging (default = 0" + ", FCP subactions = " __stringify(AVC_DEBUG_FCP_SUBACTIONS) + ", FCP payloads = " __stringify(AVC_DEBUG_FCP_PAYLOADS) + ", or all = -1)"); + +static const char *debug_fcp_ctype(unsigned int ctype) +{ + static const char *ctypes[] = { + [0x0] = "CONTROL", [0x1] = "STATUS", + [0x2] = "SPECIFIC INQUIRY", [0x3] = "NOTIFY", + [0x4] = "GENERAL INQUIRY", [0x8] = "NOT IMPLEMENTED", + [0x9] = "ACCEPTED", [0xa] = "REJECTED", + [0xb] = "IN TRANSITION", [0xc] = "IMPLEMENTED/STABLE", + [0xd] = "CHANGED", [0xf] = "INTERIM", + }; + const char *ret = ctype < ARRAY_SIZE(ctypes) ? ctypes[ctype] : NULL; + + return ret ? ret : "?"; +} + +static const char *debug_fcp_opcode(unsigned int opcode, + const u8 *data, size_t length) +{ + switch (opcode) { + case AVC_OPCODE_VENDOR: break; + case AVC_OPCODE_READ_DESCRIPTOR: return "ReadDescriptor"; + case AVC_OPCODE_DSIT: return "DirectSelectInfo.Type"; + case AVC_OPCODE_DSD: return "DirectSelectData"; + default: return "?"; + } + + if (length < 7 || + data[3] != SFE_VENDOR_DE_COMPANYID_0 || + data[4] != SFE_VENDOR_DE_COMPANYID_1 || + data[5] != SFE_VENDOR_DE_COMPANYID_2) + return "Vendor"; + + switch (data[6]) { + case SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL: return "RegisterRC"; + case SFE_VENDOR_OPCODE_LNB_CONTROL: return "LNBControl"; + case SFE_VENDOR_OPCODE_TUNE_QPSK: return "TuneQPSK"; + case SFE_VENDOR_OPCODE_HOST2CA: return "Host2CA"; + case SFE_VENDOR_OPCODE_CA2HOST: return "CA2Host"; + } + return "Vendor"; +} + +static void debug_fcp(const u8 *data, size_t length) +{ + unsigned int subunit_type, subunit_id, op; + const char *prefix = data[0] > 7 ? "FCP <- " : "FCP -> "; + + if (avc_debug & AVC_DEBUG_FCP_SUBACTIONS) { + subunit_type = data[1] >> 3; + subunit_id = data[1] & 7; + op = subunit_type == 0x1e || subunit_id == 5 ? ~0 : data[2]; + printk(KERN_INFO "%ssu=%x.%x l=%d: %-8s - %s\n", + prefix, subunit_type, subunit_id, length, + debug_fcp_ctype(data[0]), + debug_fcp_opcode(op, data, length)); + } + + if (avc_debug & AVC_DEBUG_FCP_PAYLOADS) + print_hex_dump(KERN_INFO, prefix, DUMP_PREFIX_NONE, 16, 1, + data, length, false); +} + +static int __avc_write(struct firedtv *fdtv, + const struct avc_command_frame *c, struct avc_response_frame *r) +{ + int err, retry; + + if (r) + fdtv->avc_reply_received = false; + + for (retry = 0; retry < 6; retry++) { + if (unlikely(avc_debug)) + debug_fcp(&c->ctype, c->length); + + err = fdtv->backend->write(fdtv, FCP_COMMAND_REGISTER, + (void *)&c->ctype, c->length); + if (err) { + fdtv->avc_reply_received = true; + dev_err(fdtv->device, "FCP command write failed\n"); + return err; + } + + if (!r) + return 0; + + /* + * AV/C specs say that answers should be sent within 150 ms. + * Time out after 200 ms. + */ + if (wait_event_timeout(fdtv->avc_wait, + fdtv->avc_reply_received, + msecs_to_jiffies(200)) != 0) { + r->length = fdtv->response_length; + memcpy(&r->response, fdtv->response, r->length); + + return 0; + } + } + dev_err(fdtv->device, "FCP response timed out\n"); + return -ETIMEDOUT; +} + +static int avc_write(struct firedtv *fdtv, + const struct avc_command_frame *c, struct avc_response_frame *r) +{ + int ret; + + if (mutex_lock_interruptible(&fdtv->avc_mutex)) + return -EINTR; + + ret = __avc_write(fdtv, c, r); + + mutex_unlock(&fdtv->avc_mutex); + return ret; +} + +int avc_recv(struct firedtv *fdtv, void *data, size_t length) +{ + struct avc_response_frame *r = + data - offsetof(struct avc_response_frame, response); + + if (unlikely(avc_debug)) + debug_fcp(data, length); + + if (length >= 8 && + r->operand[0] == SFE_VENDOR_DE_COMPANYID_0 && + r->operand[1] == SFE_VENDOR_DE_COMPANYID_1 && + r->operand[2] == SFE_VENDOR_DE_COMPANYID_2 && + r->operand[3] == SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL) { + if (r->response == AVC_RESPONSE_CHANGED) { + fdtv_handle_rc(fdtv, + r->operand[4] << 8 | r->operand[5]); + schedule_work(&fdtv->remote_ctrl_work); + } else if (r->response != AVC_RESPONSE_INTERIM) { + dev_info(fdtv->device, + "remote control result = %d\n", r->response); + } + return 0; + } + + if (fdtv->avc_reply_received) { + dev_err(fdtv->device, "out-of-order AVC response, ignored\n"); + return -EIO; + } + + memcpy(fdtv->response, data, length); + fdtv->response_length = length; + + fdtv->avc_reply_received = true; + wake_up(&fdtv->avc_wait); + + return 0; +} + +/* + * tuning command for setting the relative LNB frequency + * (not supported by the AVC standard) + */ +static void avc_tuner_tuneqpsk(struct firedtv *fdtv, + struct dvb_frontend_parameters *params, + struct avc_command_frame *c) +{ + c->opcode = AVC_OPCODE_VENDOR; + + c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; + c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; + c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; + c->operand[3] = SFE_VENDOR_OPCODE_TUNE_QPSK; + + c->operand[4] = (params->frequency >> 24) & 0xff; + c->operand[5] = (params->frequency >> 16) & 0xff; + c->operand[6] = (params->frequency >> 8) & 0xff; + c->operand[7] = params->frequency & 0xff; + + c->operand[8] = ((params->u.qpsk.symbol_rate / 1000) >> 8) & 0xff; + c->operand[9] = (params->u.qpsk.symbol_rate / 1000) & 0xff; + + switch (params->u.qpsk.fec_inner) { + case FEC_1_2: c->operand[10] = 0x1; break; + case FEC_2_3: c->operand[10] = 0x2; break; + case FEC_3_4: c->operand[10] = 0x3; break; + case FEC_5_6: c->operand[10] = 0x4; break; + case FEC_7_8: c->operand[10] = 0x5; break; + case FEC_4_5: + case FEC_8_9: + case FEC_AUTO: + default: c->operand[10] = 0x0; + } + + if (fdtv->voltage == 0xff) + c->operand[11] = 0xff; + else if (fdtv->voltage == SEC_VOLTAGE_18) /* polarisation */ + c->operand[11] = 0; + else + c->operand[11] = 1; + + if (fdtv->tone == 0xff) + c->operand[12] = 0xff; + else if (fdtv->tone == SEC_TONE_ON) /* band */ + c->operand[12] = 1; + else + c->operand[12] = 0; + + if (fdtv->type == FIREDTV_DVB_S2) { + c->operand[13] = 0x1; + c->operand[14] = 0xff; + c->operand[15] = 0xff; + c->length = 20; + } else { + c->length = 16; + } +} + +static void avc_tuner_dsd_dvb_c(struct dvb_frontend_parameters *params, + struct avc_command_frame *c) +{ + c->opcode = AVC_OPCODE_DSD; + + c->operand[0] = 0; /* source plug */ + c->operand[1] = 0xd2; /* subfunction replace */ + c->operand[2] = 0x20; /* system id = DVB */ + c->operand[3] = 0x00; /* antenna number */ + c->operand[4] = 0x11; /* system_specific_multiplex selection_length */ + + /* multiplex_valid_flags, high byte */ + c->operand[5] = 0 << 7 /* reserved */ + | 0 << 6 /* Polarisation */ + | 0 << 5 /* Orbital_Pos */ + | 1 << 4 /* Frequency */ + | 1 << 3 /* Symbol_Rate */ + | 0 << 2 /* FEC_outer */ + | (params->u.qam.fec_inner != FEC_AUTO ? 1 << 1 : 0) + | (params->u.qam.modulation != QAM_AUTO ? 1 << 0 : 0); + + /* multiplex_valid_flags, low byte */ + c->operand[6] = 0 << 7 /* NetworkID */ + | 0 << 0 /* reserved */ ; + + c->operand[7] = 0x00; + c->operand[8] = 0x00; + c->operand[9] = 0x00; + c->operand[10] = 0x00; + + c->operand[11] = (((params->frequency / 4000) >> 16) & 0xff) | (2 << 6); + c->operand[12] = ((params->frequency / 4000) >> 8) & 0xff; + c->operand[13] = (params->frequency / 4000) & 0xff; + c->operand[14] = ((params->u.qpsk.symbol_rate / 1000) >> 12) & 0xff; + c->operand[15] = ((params->u.qpsk.symbol_rate / 1000) >> 4) & 0xff; + c->operand[16] = ((params->u.qpsk.symbol_rate / 1000) << 4) & 0xf0; + c->operand[17] = 0x00; + + switch (params->u.qpsk.fec_inner) { + case FEC_1_2: c->operand[18] = 0x1; break; + case FEC_2_3: c->operand[18] = 0x2; break; + case FEC_3_4: c->operand[18] = 0x3; break; + case FEC_5_6: c->operand[18] = 0x4; break; + case FEC_7_8: c->operand[18] = 0x5; break; + case FEC_8_9: c->operand[18] = 0x6; break; + case FEC_4_5: c->operand[18] = 0x8; break; + case FEC_AUTO: + default: c->operand[18] = 0x0; + } + + switch (params->u.qam.modulation) { + case QAM_16: c->operand[19] = 0x08; break; + case QAM_32: c->operand[19] = 0x10; break; + case QAM_64: c->operand[19] = 0x18; break; + case QAM_128: c->operand[19] = 0x20; break; + case QAM_256: c->operand[19] = 0x28; break; + case QAM_AUTO: + default: c->operand[19] = 0x00; + } + + c->operand[20] = 0x00; + c->operand[21] = 0x00; + /* Nr_of_dsd_sel_specs = 0 -> no PIDs are transmitted */ + c->operand[22] = 0x00; + + c->length = 28; +} + +static void avc_tuner_dsd_dvb_t(struct dvb_frontend_parameters *params, + struct avc_command_frame *c) +{ + struct dvb_ofdm_parameters *ofdm = ¶ms->u.ofdm; + + c->opcode = AVC_OPCODE_DSD; + + c->operand[0] = 0; /* source plug */ + c->operand[1] = 0xd2; /* subfunction replace */ + c->operand[2] = 0x20; /* system id = DVB */ + c->operand[3] = 0x00; /* antenna number */ + c->operand[4] = 0x0c; /* system_specific_multiplex selection_length */ + + /* multiplex_valid_flags, high byte */ + c->operand[5] = + 0 << 7 /* reserved */ + | 1 << 6 /* CenterFrequency */ + | (ofdm->bandwidth != BANDWIDTH_AUTO ? 1 << 5 : 0) + | (ofdm->constellation != QAM_AUTO ? 1 << 4 : 0) + | (ofdm->hierarchy_information != HIERARCHY_AUTO ? 1 << 3 : 0) + | (ofdm->code_rate_HP != FEC_AUTO ? 1 << 2 : 0) + | (ofdm->code_rate_LP != FEC_AUTO ? 1 << 1 : 0) + | (ofdm->guard_interval != GUARD_INTERVAL_AUTO ? 1 << 0 : 0); + + /* multiplex_valid_flags, low byte */ + c->operand[6] = + 0 << 7 /* NetworkID */ + | (ofdm->transmission_mode != TRANSMISSION_MODE_AUTO ? 1 << 6 : 0) + | 0 << 5 /* OtherFrequencyFlag */ + | 0 << 0 /* reserved */ ; + + c->operand[7] = 0x0; + c->operand[8] = (params->frequency / 10) >> 24; + c->operand[9] = ((params->frequency / 10) >> 16) & 0xff; + c->operand[10] = ((params->frequency / 10) >> 8) & 0xff; + c->operand[11] = (params->frequency / 10) & 0xff; + + switch (ofdm->bandwidth) { + case BANDWIDTH_7_MHZ: c->operand[12] = 0x20; break; + case BANDWIDTH_8_MHZ: + case BANDWIDTH_6_MHZ: /* not defined by AVC spec */ + case BANDWIDTH_AUTO: + default: c->operand[12] = 0x00; + } + + switch (ofdm->constellation) { + case QAM_16: c->operand[13] = 1 << 6; break; + case QAM_64: c->operand[13] = 2 << 6; break; + case QPSK: + default: c->operand[13] = 0x00; + } + + switch (ofdm->hierarchy_information) { + case HIERARCHY_1: c->operand[13] |= 1 << 3; break; + case HIERARCHY_2: c->operand[13] |= 2 << 3; break; + case HIERARCHY_4: c->operand[13] |= 3 << 3; break; + case HIERARCHY_AUTO: + case HIERARCHY_NONE: + default: break; + } + + switch (ofdm->code_rate_HP) { + case FEC_2_3: c->operand[13] |= 1; break; + case FEC_3_4: c->operand[13] |= 2; break; + case FEC_5_6: c->operand[13] |= 3; break; + case FEC_7_8: c->operand[13] |= 4; break; + case FEC_1_2: + default: break; + } + + switch (ofdm->code_rate_LP) { + case FEC_2_3: c->operand[14] = 1 << 5; break; + case FEC_3_4: c->operand[14] = 2 << 5; break; + case FEC_5_6: c->operand[14] = 3 << 5; break; + case FEC_7_8: c->operand[14] = 4 << 5; break; + case FEC_1_2: + default: c->operand[14] = 0x00; break; + } + + switch (ofdm->guard_interval) { + case GUARD_INTERVAL_1_16: c->operand[14] |= 1 << 3; break; + case GUARD_INTERVAL_1_8: c->operand[14] |= 2 << 3; break; + case GUARD_INTERVAL_1_4: c->operand[14] |= 3 << 3; break; + case GUARD_INTERVAL_1_32: + case GUARD_INTERVAL_AUTO: + default: break; + } + + switch (ofdm->transmission_mode) { + case TRANSMISSION_MODE_8K: c->operand[14] |= 1 << 1; break; + case TRANSMISSION_MODE_2K: + case TRANSMISSION_MODE_AUTO: + default: break; + } + + c->operand[15] = 0x00; /* network_ID[0] */ + c->operand[16] = 0x00; /* network_ID[1] */ + /* Nr_of_dsd_sel_specs = 0 -> no PIDs are transmitted */ + c->operand[17] = 0x00; + + c->length = 24; +} + +int avc_tuner_dsd(struct firedtv *fdtv, + struct dvb_frontend_parameters *params) +{ + char buffer[sizeof(struct avc_command_frame)]; + struct avc_command_frame *c = (void *)buffer; + struct avc_response_frame *r = (void *)buffer; /* FIXME: unused */ + + memset(c, 0, sizeof(*c)); + + c->ctype = AVC_CTYPE_CONTROL; + c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; + + switch (fdtv->type) { + case FIREDTV_DVB_S: + case FIREDTV_DVB_S2: avc_tuner_tuneqpsk(fdtv, params, c); break; + case FIREDTV_DVB_C: avc_tuner_dsd_dvb_c(params, c); break; + case FIREDTV_DVB_T: avc_tuner_dsd_dvb_t(params, c); break; + default: + BUG(); + } + + if (avc_write(fdtv, c, r) < 0) + return -EIO; + + msleep(500); +#if 0 + /* FIXME: */ + /* u8 *status was an out-parameter of avc_tuner_dsd, unused by caller */ + if (status) + *status = r->operand[2]; +#endif + return 0; +} + +int avc_tuner_set_pids(struct firedtv *fdtv, unsigned char pidc, u16 pid[]) +{ + char buffer[sizeof(struct avc_command_frame)]; + struct avc_command_frame *c = (void *)buffer; + struct avc_response_frame *r = (void *)buffer; /* FIXME: unused */ + int pos, k; + + if (pidc > 16 && pidc != 0xff) + return -EINVAL; + + memset(c, 0, sizeof(*c)); + + c->ctype = AVC_CTYPE_CONTROL; + c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; + c->opcode = AVC_OPCODE_DSD; + + c->operand[0] = 0; /* source plug */ + c->operand[1] = 0xd2; /* subfunction replace */ + c->operand[2] = 0x20; /* system id = DVB */ + c->operand[3] = 0x00; /* antenna number */ + c->operand[4] = 0x00; /* system_specific_multiplex selection_length */ + c->operand[5] = pidc; /* Nr_of_dsd_sel_specs */ + + pos = 6; + if (pidc != 0xff) + for (k = 0; k < pidc; k++) { + c->operand[pos++] = 0x13; /* flowfunction relay */ + c->operand[pos++] = 0x80; /* dsd_sel_spec_valid_flags -> PID */ + c->operand[pos++] = (pid[k] >> 8) & 0x1f; + c->operand[pos++] = pid[k] & 0xff; + c->operand[pos++] = 0x00; /* tableID */ + c->operand[pos++] = 0x00; /* filter_length */ + } + + c->length = ALIGN(3 + pos, 4); + + if (avc_write(fdtv, c, r) < 0) + return -EIO; + + msleep(50); + return 0; +} + +int avc_tuner_get_ts(struct firedtv *fdtv) +{ + char buffer[sizeof(struct avc_command_frame)]; + struct avc_command_frame *c = (void *)buffer; + struct avc_response_frame *r = (void *)buffer; /* FIXME: unused */ + int sl; + + memset(c, 0, sizeof(*c)); + + c->ctype = AVC_CTYPE_CONTROL; + c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; + c->opcode = AVC_OPCODE_DSIT; + + sl = fdtv->type == FIREDTV_DVB_T ? 0x0c : 0x11; + + c->operand[0] = 0; /* source plug */ + c->operand[1] = 0xd2; /* subfunction replace */ + c->operand[2] = 0xff; /* status */ + c->operand[3] = 0x20; /* system id = DVB */ + c->operand[4] = 0x00; /* antenna number */ + c->operand[5] = 0x0; /* system_specific_search_flags */ + c->operand[6] = sl; /* system_specific_multiplex selection_length */ + c->operand[7] = 0x00; /* valid_flags [0] */ + c->operand[8] = 0x00; /* valid_flags [1] */ + c->operand[7 + sl] = 0x00; /* nr_of_dsit_sel_specs (always 0) */ + + c->length = fdtv->type == FIREDTV_DVB_T ? 24 : 28; + + if (avc_write(fdtv, c, r) < 0) + return -EIO; + + msleep(250); + return 0; +} + +int avc_identify_subunit(struct firedtv *fdtv) +{ + char buffer[sizeof(struct avc_command_frame)]; + struct avc_command_frame *c = (void *)buffer; + struct avc_response_frame *r = (void *)buffer; + + memset(c, 0, sizeof(*c)); + + c->ctype = AVC_CTYPE_CONTROL; + c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; + c->opcode = AVC_OPCODE_READ_DESCRIPTOR; + + c->operand[0] = DESCRIPTOR_SUBUNIT_IDENTIFIER; + c->operand[1] = 0xff; + c->operand[2] = 0x00; + c->operand[3] = 0x00; /* length highbyte */ + c->operand[4] = 0x08; /* length lowbyte */ + c->operand[5] = 0x00; /* offset highbyte */ + c->operand[6] = 0x0d; /* offset lowbyte */ + + c->length = 12; + + if (avc_write(fdtv, c, r) < 0) + return -EIO; + + if ((r->response != AVC_RESPONSE_STABLE && + r->response != AVC_RESPONSE_ACCEPTED) || + (r->operand[3] << 8) + r->operand[4] != 8) { + dev_err(fdtv->device, "cannot read subunit identifier\n"); + return -EINVAL; + } + return 0; +} + +#define SIZEOF_ANTENNA_INPUT_INFO 22 + +int avc_tuner_status(struct firedtv *fdtv, struct firedtv_tuner_status *stat) +{ + char buffer[sizeof(struct avc_command_frame)]; + struct avc_command_frame *c = (void *)buffer; + struct avc_response_frame *r = (void *)buffer; + int length; + + memset(c, 0, sizeof(*c)); + + c->ctype = AVC_CTYPE_CONTROL; + c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; + c->opcode = AVC_OPCODE_READ_DESCRIPTOR; + + c->operand[0] = DESCRIPTOR_TUNER_STATUS; + c->operand[1] = 0xff; /* read_result_status */ + c->operand[2] = 0x00; /* reserved */ + c->operand[3] = 0; /* SIZEOF_ANTENNA_INPUT_INFO >> 8; */ + c->operand[4] = 0; /* SIZEOF_ANTENNA_INPUT_INFO & 0xff; */ + c->operand[5] = 0x00; + c->operand[6] = 0x00; + + c->length = 12; + + if (avc_write(fdtv, c, r) < 0) + return -EIO; + + if (r->response != AVC_RESPONSE_STABLE && + r->response != AVC_RESPONSE_ACCEPTED) { + dev_err(fdtv->device, "cannot read tuner status\n"); + return -EINVAL; + } + + length = r->operand[9]; + if (r->operand[1] != 0x10 || length != SIZEOF_ANTENNA_INPUT_INFO) { + dev_err(fdtv->device, "got invalid tuner status\n"); + return -EINVAL; + } + + stat->active_system = r->operand[10]; + stat->searching = r->operand[11] >> 7 & 1; + stat->moving = r->operand[11] >> 6 & 1; + stat->no_rf = r->operand[11] >> 5 & 1; + stat->input = r->operand[12] >> 7 & 1; + stat->selected_antenna = r->operand[12] & 0x7f; + stat->ber = r->operand[13] << 24 | + r->operand[14] << 16 | + r->operand[15] << 8 | + r->operand[16]; + stat->signal_strength = r->operand[17]; + stat->raster_frequency = r->operand[18] >> 6 & 2; + stat->rf_frequency = (r->operand[18] & 0x3f) << 16 | + r->operand[19] << 8 | + r->operand[20]; + stat->man_dep_info_length = r->operand[21]; + stat->front_end_error = r->operand[22] >> 4 & 1; + stat->antenna_error = r->operand[22] >> 3 & 1; + stat->front_end_power_status = r->operand[22] >> 1 & 1; + stat->power_supply = r->operand[22] & 1; + stat->carrier_noise_ratio = r->operand[23] << 8 | + r->operand[24]; + stat->power_supply_voltage = r->operand[27]; + stat->antenna_voltage = r->operand[28]; + stat->firewire_bus_voltage = r->operand[29]; + stat->ca_mmi = r->operand[30] & 1; + stat->ca_pmt_reply = r->operand[31] >> 7 & 1; + stat->ca_date_time_request = r->operand[31] >> 6 & 1; + stat->ca_application_info = r->operand[31] >> 5 & 1; + stat->ca_module_present_status = r->operand[31] >> 4 & 1; + stat->ca_dvb_flag = r->operand[31] >> 3 & 1; + stat->ca_error_flag = r->operand[31] >> 2 & 1; + stat->ca_initialization_status = r->operand[31] >> 1 & 1; + + return 0; +} + +int avc_lnb_control(struct firedtv *fdtv, char voltage, char burst, + char conttone, char nrdiseq, + struct dvb_diseqc_master_cmd *diseqcmd) +{ + char buffer[sizeof(struct avc_command_frame)]; + struct avc_command_frame *c = (void *)buffer; + struct avc_response_frame *r = (void *)buffer; + int i, j, k; + + memset(c, 0, sizeof(*c)); + + c->ctype = AVC_CTYPE_CONTROL; + c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; + c->opcode = AVC_OPCODE_VENDOR; + + c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; + c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; + c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; + c->operand[3] = SFE_VENDOR_OPCODE_LNB_CONTROL; + + c->operand[4] = voltage; + c->operand[5] = nrdiseq; + + i = 6; + + for (j = 0; j < nrdiseq; j++) { + c->operand[i++] = diseqcmd[j].msg_len; + + for (k = 0; k < diseqcmd[j].msg_len; k++) + c->operand[i++] = diseqcmd[j].msg[k]; + } + + c->operand[i++] = burst; + c->operand[i++] = conttone; + + c->length = ALIGN(3 + i, 4); + + if (avc_write(fdtv, c, r) < 0) + return -EIO; + + if (r->response != AVC_RESPONSE_ACCEPTED) { + dev_err(fdtv->device, "LNB control failed\n"); + return -EINVAL; + } + + return 0; +} + +int avc_register_remote_control(struct firedtv *fdtv) +{ + char buffer[sizeof(struct avc_command_frame)]; + struct avc_command_frame *c = (void *)buffer; + + memset(c, 0, sizeof(*c)); + + c->ctype = AVC_CTYPE_NOTIFY; + c->subunit = AVC_SUBUNIT_TYPE_UNIT | 7; + c->opcode = AVC_OPCODE_VENDOR; + + c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; + c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; + c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; + c->operand[3] = SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL; + + c->length = 8; + + return avc_write(fdtv, c, NULL); +} + +void avc_remote_ctrl_work(struct work_struct *work) +{ + struct firedtv *fdtv = + container_of(work, struct firedtv, remote_ctrl_work); + + /* Should it be rescheduled in failure cases? */ + avc_register_remote_control(fdtv); +} + +#if 0 /* FIXME: unused */ +int avc_tuner_host2ca(struct firedtv *fdtv) +{ + char buffer[sizeof(struct avc_command_frame)]; + struct avc_command_frame *c = (void *)buffer; + struct avc_response_frame *r = (void *)buffer; /* FIXME: unused */ + + memset(c, 0, sizeof(*c)); + + c->ctype = AVC_CTYPE_CONTROL; + c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; + c->opcode = AVC_OPCODE_VENDOR; + + c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; + c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; + c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; + c->operand[3] = SFE_VENDOR_OPCODE_HOST2CA; + c->operand[4] = 0; /* slot */ + c->operand[5] = SFE_VENDOR_TAG_CA_APPLICATION_INFO; /* ca tag */ + c->operand[6] = 0; /* more/last */ + c->operand[7] = 0; /* length */ + + c->length = 12; + + if (avc_write(fdtv, c, r) < 0) + return -EIO; + + return 0; +} +#endif + +static int get_ca_object_pos(struct avc_response_frame *r) +{ + int length = 1; + + /* Check length of length field */ + if (r->operand[7] & 0x80) + length = (r->operand[7] & 0x7f) + 1; + return length + 7; +} + +static int get_ca_object_length(struct avc_response_frame *r) +{ +#if 0 /* FIXME: unused */ + int size = 0; + int i; + + if (r->operand[7] & 0x80) + for (i = 0; i < (r->operand[7] & 0x7f); i++) { + size <<= 8; + size += r->operand[8 + i]; + } +#endif + return r->operand[7]; +} + +int avc_ca_app_info(struct firedtv *fdtv, char *app_info, unsigned int *len) +{ + char buffer[sizeof(struct avc_command_frame)]; + struct avc_command_frame *c = (void *)buffer; + struct avc_response_frame *r = (void *)buffer; + int pos; + + memset(c, 0, sizeof(*c)); + + c->ctype = AVC_CTYPE_STATUS; + c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; + c->opcode = AVC_OPCODE_VENDOR; + + c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; + c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; + c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; + c->operand[3] = SFE_VENDOR_OPCODE_CA2HOST; + c->operand[4] = 0; /* slot */ + c->operand[5] = SFE_VENDOR_TAG_CA_APPLICATION_INFO; /* ca tag */ + + c->length = 12; + + if (avc_write(fdtv, c, r) < 0) + return -EIO; + + /* FIXME: check response code and validate response data */ + + pos = get_ca_object_pos(r); + app_info[0] = (EN50221_TAG_APP_INFO >> 16) & 0xff; + app_info[1] = (EN50221_TAG_APP_INFO >> 8) & 0xff; + app_info[2] = (EN50221_TAG_APP_INFO >> 0) & 0xff; + app_info[3] = 6 + r->operand[pos + 4]; + app_info[4] = 0x01; + memcpy(&app_info[5], &r->operand[pos], 5 + r->operand[pos + 4]); + *len = app_info[3] + 4; + + return 0; +} + +int avc_ca_info(struct firedtv *fdtv, char *app_info, unsigned int *len) +{ + char buffer[sizeof(struct avc_command_frame)]; + struct avc_command_frame *c = (void *)buffer; + struct avc_response_frame *r = (void *)buffer; + int pos; + + memset(c, 0, sizeof(*c)); + + c->ctype = AVC_CTYPE_STATUS; + c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; + c->opcode = AVC_OPCODE_VENDOR; + + c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; + c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; + c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; + c->operand[3] = SFE_VENDOR_OPCODE_CA2HOST; + c->operand[4] = 0; /* slot */ + c->operand[5] = SFE_VENDOR_TAG_CA_APPLICATION_INFO; /* ca tag */ + + c->length = 12; + + if (avc_write(fdtv, c, r) < 0) + return -EIO; + + pos = get_ca_object_pos(r); + app_info[0] = (EN50221_TAG_CA_INFO >> 16) & 0xff; + app_info[1] = (EN50221_TAG_CA_INFO >> 8) & 0xff; + app_info[2] = (EN50221_TAG_CA_INFO >> 0) & 0xff; + app_info[3] = 2; + app_info[4] = r->operand[pos + 0]; + app_info[5] = r->operand[pos + 1]; + *len = app_info[3] + 4; + + return 0; +} + +int avc_ca_reset(struct firedtv *fdtv) +{ + char buffer[sizeof(struct avc_command_frame)]; + struct avc_command_frame *c = (void *)buffer; + struct avc_response_frame *r = (void *)buffer; /* FIXME: unused */ + + memset(c, 0, sizeof(*c)); + + c->ctype = AVC_CTYPE_CONTROL; + c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; + c->opcode = AVC_OPCODE_VENDOR; + + c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; + c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; + c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; + c->operand[3] = SFE_VENDOR_OPCODE_HOST2CA; + c->operand[4] = 0; /* slot */ + c->operand[5] = SFE_VENDOR_TAG_CA_RESET; /* ca tag */ + c->operand[6] = 0; /* more/last */ + c->operand[7] = 1; /* length */ + c->operand[8] = 0; /* force hardware reset */ + + c->length = 12; + + if (avc_write(fdtv, c, r) < 0) + return -EIO; + + return 0; +} + +int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length) +{ + char buffer[sizeof(struct avc_command_frame)]; + struct avc_command_frame *c = (void *)buffer; + struct avc_response_frame *r = (void *)buffer; + int list_management; + int program_info_length; + int pmt_cmd_id; + int read_pos; + int write_pos; + int es_info_length; + int crc32_csum; + + memset(c, 0, sizeof(*c)); + + c->ctype = AVC_CTYPE_CONTROL; + c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; + c->opcode = AVC_OPCODE_VENDOR; + + if (msg[0] != EN50221_LIST_MANAGEMENT_ONLY) { + dev_info(fdtv->device, "forcing list_management to ONLY\n"); + msg[0] = EN50221_LIST_MANAGEMENT_ONLY; + } + /* We take the cmd_id from the programme level only! */ + list_management = msg[0]; + program_info_length = ((msg[4] & 0x0f) << 8) + msg[5]; + if (program_info_length > 0) + program_info_length--; /* Remove pmt_cmd_id */ + pmt_cmd_id = msg[6]; + + c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; + c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; + c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; + c->operand[3] = SFE_VENDOR_OPCODE_HOST2CA; + c->operand[4] = 0; /* slot */ + c->operand[5] = SFE_VENDOR_TAG_CA_PMT; /* ca tag */ + c->operand[6] = 0; /* more/last */ + /* c->operand[7] = XXXprogram_info_length + 17; */ /* length */ + c->operand[8] = list_management; + c->operand[9] = 0x01; /* pmt_cmd=OK_descramble */ + + /* TS program map table */ + + c->operand[10] = 0x02; /* Table id=2 */ + c->operand[11] = 0x80; /* Section syntax + length */ + /* c->operand[12] = XXXprogram_info_length + 12; */ + c->operand[13] = msg[1]; /* Program number */ + c->operand[14] = msg[2]; + c->operand[15] = 0x01; /* Version number=0 + current/next=1 */ + c->operand[16] = 0x00; /* Section number=0 */ + c->operand[17] = 0x00; /* Last section number=0 */ + c->operand[18] = 0x1f; /* PCR_PID=1FFF */ + c->operand[19] = 0xff; + c->operand[20] = (program_info_length >> 8); /* Program info length */ + c->operand[21] = (program_info_length & 0xff); + + /* CA descriptors at programme level */ + read_pos = 6; + write_pos = 22; + if (program_info_length > 0) { + pmt_cmd_id = msg[read_pos++]; + if (pmt_cmd_id != 1 && pmt_cmd_id != 4) + dev_err(fdtv->device, + "invalid pmt_cmd_id %d\n", pmt_cmd_id); + + memcpy(&c->operand[write_pos], &msg[read_pos], + program_info_length); + read_pos += program_info_length; + write_pos += program_info_length; + } + while (read_pos < length) { + c->operand[write_pos++] = msg[read_pos++]; + c->operand[write_pos++] = msg[read_pos++]; + c->operand[write_pos++] = msg[read_pos++]; + es_info_length = + ((msg[read_pos] & 0x0f) << 8) + msg[read_pos + 1]; + read_pos += 2; + if (es_info_length > 0) + es_info_length--; /* Remove pmt_cmd_id */ + c->operand[write_pos++] = es_info_length >> 8; + c->operand[write_pos++] = es_info_length & 0xff; + if (es_info_length > 0) { + pmt_cmd_id = msg[read_pos++]; + if (pmt_cmd_id != 1 && pmt_cmd_id != 4) + dev_err(fdtv->device, "invalid pmt_cmd_id %d " + "at stream level\n", pmt_cmd_id); + + memcpy(&c->operand[write_pos], &msg[read_pos], + es_info_length); + read_pos += es_info_length; + write_pos += es_info_length; + } + } + + /* CRC */ + c->operand[write_pos++] = 0x00; + c->operand[write_pos++] = 0x00; + c->operand[write_pos++] = 0x00; + c->operand[write_pos++] = 0x00; + + c->operand[7] = write_pos - 8; + c->operand[12] = write_pos - 13; + + crc32_csum = crc32_be(0, &c->operand[10], c->operand[12] - 1); + c->operand[write_pos - 4] = (crc32_csum >> 24) & 0xff; + c->operand[write_pos - 3] = (crc32_csum >> 16) & 0xff; + c->operand[write_pos - 2] = (crc32_csum >> 8) & 0xff; + c->operand[write_pos - 1] = (crc32_csum >> 0) & 0xff; + + c->length = ALIGN(3 + write_pos, 4); + + if (avc_write(fdtv, c, r) < 0) + return -EIO; + + if (r->response != AVC_RESPONSE_ACCEPTED) { + dev_err(fdtv->device, + "CA PMT failed with response 0x%x\n", r->response); + return -EFAULT; + } + + return 0; +} + +int avc_ca_get_time_date(struct firedtv *fdtv, int *interval) +{ + char buffer[sizeof(struct avc_command_frame)]; + struct avc_command_frame *c = (void *)buffer; + struct avc_response_frame *r = (void *)buffer; + + memset(c, 0, sizeof(*c)); + + c->ctype = AVC_CTYPE_STATUS; + c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; + c->opcode = AVC_OPCODE_VENDOR; + + c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; + c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; + c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; + c->operand[3] = SFE_VENDOR_OPCODE_CA2HOST; + c->operand[4] = 0; /* slot */ + c->operand[5] = SFE_VENDOR_TAG_CA_DATE_TIME; /* ca tag */ + c->operand[6] = 0; /* more/last */ + c->operand[7] = 0; /* length */ + + c->length = 12; + + if (avc_write(fdtv, c, r) < 0) + return -EIO; + + /* FIXME: check response code and validate response data */ + + *interval = r->operand[get_ca_object_pos(r)]; + + return 0; +} + +int avc_ca_enter_menu(struct firedtv *fdtv) +{ + char buffer[sizeof(struct avc_command_frame)]; + struct avc_command_frame *c = (void *)buffer; + struct avc_response_frame *r = (void *)buffer; /* FIXME: unused */ + + memset(c, 0, sizeof(*c)); + + c->ctype = AVC_CTYPE_STATUS; + c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; + c->opcode = AVC_OPCODE_VENDOR; + + c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; + c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; + c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; + c->operand[3] = SFE_VENDOR_OPCODE_HOST2CA; + c->operand[4] = 0; /* slot */ + c->operand[5] = SFE_VENDOR_TAG_CA_ENTER_MENU; + c->operand[6] = 0; /* more/last */ + c->operand[7] = 0; /* length */ + + c->length = 12; + + if (avc_write(fdtv, c, r) < 0) + return -EIO; + + return 0; +} + +int avc_ca_get_mmi(struct firedtv *fdtv, char *mmi_object, unsigned int *len) +{ + char buffer[sizeof(struct avc_command_frame)]; + struct avc_command_frame *c = (void *)buffer; + struct avc_response_frame *r = (void *)buffer; + + memset(c, 0, sizeof(*c)); + + c->ctype = AVC_CTYPE_STATUS; + c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; + c->opcode = AVC_OPCODE_VENDOR; + + c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; + c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; + c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; + c->operand[3] = SFE_VENDOR_OPCODE_CA2HOST; + c->operand[4] = 0; /* slot */ + c->operand[5] = SFE_VENDOR_TAG_CA_MMI; + c->operand[6] = 0; /* more/last */ + c->operand[7] = 0; /* length */ + + c->length = 12; + + if (avc_write(fdtv, c, r) < 0) + return -EIO; + + /* FIXME: check response code and validate response data */ + + *len = get_ca_object_length(r); + memcpy(mmi_object, &r->operand[get_ca_object_pos(r)], *len); + + return 0; +} + +#define CMP_OUTPUT_PLUG_CONTROL_REG_0 0xfffff0000904ULL + +static int cmp_read(struct firedtv *fdtv, void *buf, u64 addr, size_t len) +{ + int ret; + + if (mutex_lock_interruptible(&fdtv->avc_mutex)) + return -EINTR; + + ret = fdtv->backend->read(fdtv, addr, buf, len); + if (ret < 0) + dev_err(fdtv->device, "CMP: read I/O error\n"); + + mutex_unlock(&fdtv->avc_mutex); + return ret; +} + +static int cmp_lock(struct firedtv *fdtv, void *data, u64 addr, __be32 arg) +{ + int ret; + + if (mutex_lock_interruptible(&fdtv->avc_mutex)) + return -EINTR; + + ret = fdtv->backend->lock(fdtv, addr, data, arg); + if (ret < 0) + dev_err(fdtv->device, "CMP: lock I/O error\n"); + + mutex_unlock(&fdtv->avc_mutex); + return ret; +} + +static inline u32 get_opcr(__be32 opcr, u32 mask, u32 shift) +{ + return (be32_to_cpu(opcr) >> shift) & mask; +} + +static inline void set_opcr(__be32 *opcr, u32 value, u32 mask, u32 shift) +{ + *opcr &= ~cpu_to_be32(mask << shift); + *opcr |= cpu_to_be32((value & mask) << shift); +} + +#define get_opcr_online(v) get_opcr((v), 0x1, 31) +#define get_opcr_p2p_connections(v) get_opcr((v), 0x3f, 24) +#define get_opcr_channel(v) get_opcr((v), 0x3f, 16) + +#define set_opcr_p2p_connections(p, v) set_opcr((p), (v), 0x3f, 24) +#define set_opcr_channel(p, v) set_opcr((p), (v), 0x3f, 16) +#define set_opcr_data_rate(p, v) set_opcr((p), (v), 0x3, 14) +#define set_opcr_overhead_id(p, v) set_opcr((p), (v), 0xf, 10) + +int cmp_establish_pp_connection(struct firedtv *fdtv, int plug, int channel) +{ + __be32 old_opcr, opcr; + u64 opcr_address = CMP_OUTPUT_PLUG_CONTROL_REG_0 + (plug << 2); + int attempts = 0; + int ret; + + ret = cmp_read(fdtv, &opcr, opcr_address, 4); + if (ret < 0) + return ret; + +repeat: + if (!get_opcr_online(opcr)) { + dev_err(fdtv->device, "CMP: output offline\n"); + return -EBUSY; + } + + old_opcr = opcr; + + if (get_opcr_p2p_connections(opcr)) { + if (get_opcr_channel(opcr) != channel) { + dev_err(fdtv->device, "CMP: cannot change channel\n"); + return -EBUSY; + } + dev_info(fdtv->device, "CMP: overlaying connection\n"); + + /* We don't allocate isochronous resources. */ + } else { + set_opcr_channel(&opcr, channel); + set_opcr_data_rate(&opcr, 2); /* S400 */ + + /* FIXME: this is for the worst case - optimize */ + set_opcr_overhead_id(&opcr, 0); + + /* + * FIXME: allocate isochronous channel and bandwidth at IRM + * fdtv->backend->alloc_resources(fdtv, channels_mask, bw); + */ + } + + set_opcr_p2p_connections(&opcr, get_opcr_p2p_connections(opcr) + 1); + + ret = cmp_lock(fdtv, &opcr, opcr_address, old_opcr); + if (ret < 0) + return ret; + + if (old_opcr != opcr) { + /* + * FIXME: if old_opcr.P2P_Connections > 0, + * deallocate isochronous channel and bandwidth at IRM + * if (...) + * fdtv->backend->dealloc_resources(fdtv, channel, bw); + */ + + if (++attempts < 6) /* arbitrary limit */ + goto repeat; + return -EBUSY; + } + + return 0; +} + +void cmp_break_pp_connection(struct firedtv *fdtv, int plug, int channel) +{ + __be32 old_opcr, opcr; + u64 opcr_address = CMP_OUTPUT_PLUG_CONTROL_REG_0 + (plug << 2); + int attempts = 0; + + if (cmp_read(fdtv, &opcr, opcr_address, 4) < 0) + return; + +repeat: + if (!get_opcr_online(opcr) || !get_opcr_p2p_connections(opcr) || + get_opcr_channel(opcr) != channel) { + dev_err(fdtv->device, "CMP: no connection to break\n"); + return; + } + + old_opcr = opcr; + set_opcr_p2p_connections(&opcr, get_opcr_p2p_connections(opcr) - 1); + + if (cmp_lock(fdtv, &opcr, opcr_address, old_opcr) < 0) + return; + + if (old_opcr != opcr) { + /* + * FIXME: if old_opcr.P2P_Connections == 1, i.e. we were last + * owner, deallocate isochronous channel and bandwidth at IRM + * if (...) + * fdtv->backend->dealloc_resources(fdtv, channel, bw); + */ + + if (++attempts < 6) /* arbitrary limit */ + goto repeat; + } +} diff --git a/drivers/media/dvb/firewire/firedtv-ci.c b/drivers/media/dvb/firewire/firedtv-ci.c new file mode 100644 index 00000000000..eeb80d0ea3f --- /dev/null +++ b/drivers/media/dvb/firewire/firedtv-ci.c @@ -0,0 +1,260 @@ +/* + * FireDTV driver (formerly known as FireSAT) + * + * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> + * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#include <linux/device.h> +#include <linux/dvb/ca.h> +#include <linux/fs.h> +#include <linux/module.h> + +#include <dvbdev.h> + +#include "firedtv.h" + +#define EN50221_TAG_APP_INFO_ENQUIRY 0x9f8020 +#define EN50221_TAG_CA_INFO_ENQUIRY 0x9f8030 +#define EN50221_TAG_CA_PMT 0x9f8032 +#define EN50221_TAG_ENTER_MENU 0x9f8022 + +static int fdtv_ca_ready(struct firedtv_tuner_status *stat) +{ + return stat->ca_initialization_status == 1 && + stat->ca_error_flag == 0 && + stat->ca_dvb_flag == 1 && + stat->ca_module_present_status == 1; +} + +static int fdtv_get_ca_flags(struct firedtv_tuner_status *stat) +{ + int flags = 0; + + if (stat->ca_module_present_status == 1) + flags |= CA_CI_MODULE_PRESENT; + if (stat->ca_initialization_status == 1 && + stat->ca_error_flag == 0 && + stat->ca_dvb_flag == 1) + flags |= CA_CI_MODULE_READY; + return flags; +} + +static int fdtv_ca_reset(struct firedtv *fdtv) +{ + return avc_ca_reset(fdtv) ? -EFAULT : 0; +} + +static int fdtv_ca_get_caps(void *arg) +{ + struct ca_caps *cap = arg; + + cap->slot_num = 1; + cap->slot_type = CA_CI; + cap->descr_num = 1; + cap->descr_type = CA_ECD; + return 0; +} + +static int fdtv_ca_get_slot_info(struct firedtv *fdtv, void *arg) +{ + struct firedtv_tuner_status stat; + struct ca_slot_info *slot = arg; + + if (avc_tuner_status(fdtv, &stat)) + return -EFAULT; + + if (slot->num != 0) + return -EFAULT; + + slot->type = CA_CI; + slot->flags = fdtv_get_ca_flags(&stat); + return 0; +} + +static int fdtv_ca_app_info(struct firedtv *fdtv, void *arg) +{ + struct ca_msg *reply = arg; + + return avc_ca_app_info(fdtv, reply->msg, &reply->length) ? -EFAULT : 0; +} + +static int fdtv_ca_info(struct firedtv *fdtv, void *arg) +{ + struct ca_msg *reply = arg; + + return avc_ca_info(fdtv, reply->msg, &reply->length) ? -EFAULT : 0; +} + +static int fdtv_ca_get_mmi(struct firedtv *fdtv, void *arg) +{ + struct ca_msg *reply = arg; + + return avc_ca_get_mmi(fdtv, reply->msg, &reply->length) ? -EFAULT : 0; +} + +static int fdtv_ca_get_msg(struct firedtv *fdtv, void *arg) +{ + struct firedtv_tuner_status stat; + int err; + + switch (fdtv->ca_last_command) { + case EN50221_TAG_APP_INFO_ENQUIRY: + err = fdtv_ca_app_info(fdtv, arg); + break; + case EN50221_TAG_CA_INFO_ENQUIRY: + err = fdtv_ca_info(fdtv, arg); + break; + default: + if (avc_tuner_status(fdtv, &stat)) + err = -EFAULT; + else if (stat.ca_mmi == 1) + err = fdtv_ca_get_mmi(fdtv, arg); + else { + dev_info(fdtv->device, "unhandled CA message 0x%08x\n", + fdtv->ca_last_command); + err = -EFAULT; + } + } + fdtv->ca_last_command = 0; + return err; +} + +static int fdtv_ca_pmt(struct firedtv *fdtv, void *arg) +{ + struct ca_msg *msg = arg; + int data_pos; + int data_length; + int i; + + data_pos = 4; + if (msg->msg[3] & 0x80) { + data_length = 0; + for (i = 0; i < (msg->msg[3] & 0x7f); i++) + data_length = (data_length << 8) + msg->msg[data_pos++]; + } else { + data_length = msg->msg[3]; + } + + return avc_ca_pmt(fdtv, &msg->msg[data_pos], data_length) ? -EFAULT : 0; +} + +static int fdtv_ca_send_msg(struct firedtv *fdtv, void *arg) +{ + struct ca_msg *msg = arg; + int err; + + /* Do we need a semaphore for this? */ + fdtv->ca_last_command = + (msg->msg[0] << 16) + (msg->msg[1] << 8) + msg->msg[2]; + switch (fdtv->ca_last_command) { + case EN50221_TAG_CA_PMT: + err = fdtv_ca_pmt(fdtv, arg); + break; + case EN50221_TAG_APP_INFO_ENQUIRY: + /* handled in ca_get_msg */ + err = 0; + break; + case EN50221_TAG_CA_INFO_ENQUIRY: + /* handled in ca_get_msg */ + err = 0; + break; + case EN50221_TAG_ENTER_MENU: + err = avc_ca_enter_menu(fdtv); + break; + default: + dev_err(fdtv->device, "unhandled CA message 0x%08x\n", + fdtv->ca_last_command); + err = -EFAULT; + } + return err; +} + +static int fdtv_ca_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct dvb_device *dvbdev = file->private_data; + struct firedtv *fdtv = dvbdev->priv; + struct firedtv_tuner_status stat; + int err; + + switch (cmd) { + case CA_RESET: + err = fdtv_ca_reset(fdtv); + break; + case CA_GET_CAP: + err = fdtv_ca_get_caps(arg); + break; + case CA_GET_SLOT_INFO: + err = fdtv_ca_get_slot_info(fdtv, arg); + break; + case CA_GET_MSG: + err = fdtv_ca_get_msg(fdtv, arg); + break; + case CA_SEND_MSG: + err = fdtv_ca_send_msg(fdtv, arg); + break; + default: + dev_info(fdtv->device, "unhandled CA ioctl %u\n", cmd); + err = -EOPNOTSUPP; + } + + /* FIXME Is this necessary? */ + avc_tuner_status(fdtv, &stat); + + return err; +} + +static unsigned int fdtv_ca_io_poll(struct file *file, poll_table *wait) +{ + return POLLIN; +} + +static struct file_operations fdtv_ca_fops = { + .owner = THIS_MODULE, + .ioctl = dvb_generic_ioctl, + .open = dvb_generic_open, + .release = dvb_generic_release, + .poll = fdtv_ca_io_poll, +}; + +static struct dvb_device fdtv_ca = { + .users = 1, + .readers = 1, + .writers = 1, + .fops = &fdtv_ca_fops, + .kernel_ioctl = fdtv_ca_ioctl, +}; + +int fdtv_ca_register(struct firedtv *fdtv) +{ + struct firedtv_tuner_status stat; + int err; + + if (avc_tuner_status(fdtv, &stat)) + return -EINVAL; + + if (!fdtv_ca_ready(&stat)) + return -EFAULT; + + err = dvb_register_device(&fdtv->adapter, &fdtv->cadev, + &fdtv_ca, fdtv, DVB_DEVICE_CA); + + if (stat.ca_application_info == 0) + dev_err(fdtv->device, "CaApplicationInfo is not set\n"); + if (stat.ca_date_time_request == 1) + avc_ca_get_time_date(fdtv, &fdtv->ca_time_interval); + + return err; +} + +void fdtv_ca_release(struct firedtv *fdtv) +{ + if (fdtv->cadev) + dvb_unregister_device(fdtv->cadev); +} diff --git a/drivers/media/dvb/firewire/firedtv-dvb.c b/drivers/media/dvb/firewire/firedtv-dvb.c new file mode 100644 index 00000000000..9d308dd32a5 --- /dev/null +++ b/drivers/media/dvb/firewire/firedtv-dvb.c @@ -0,0 +1,364 @@ +/* + * FireDTV driver (formerly known as FireSAT) + * + * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> + * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> + * + * 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/bitops.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/wait.h> +#include <linux/workqueue.h> + +#include <dmxdev.h> +#include <dvb_demux.h> +#include <dvbdev.h> +#include <dvb_frontend.h> + +#include "firedtv.h" + +static int alloc_channel(struct firedtv *fdtv) +{ + int i; + + for (i = 0; i < 16; i++) + if (!__test_and_set_bit(i, &fdtv->channel_active)) + break; + return i; +} + +static void collect_channels(struct firedtv *fdtv, int *pidc, u16 pid[]) +{ + int i, n; + + for (i = 0, n = 0; i < 16; i++) + if (test_bit(i, &fdtv->channel_active)) + pid[n++] = fdtv->channel_pid[i]; + *pidc = n; +} + +static inline void dealloc_channel(struct firedtv *fdtv, int i) +{ + __clear_bit(i, &fdtv->channel_active); +} + +int fdtv_start_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + struct firedtv *fdtv = dvbdmxfeed->demux->priv; + int pidc, c, ret; + u16 pids[16]; + + switch (dvbdmxfeed->type) { + case DMX_TYPE_TS: + case DMX_TYPE_SEC: + break; + default: + dev_err(fdtv->device, "can't start dmx feed: invalid type %u\n", + dvbdmxfeed->type); + return -EINVAL; + } + + if (mutex_lock_interruptible(&fdtv->demux_mutex)) + return -EINTR; + + if (dvbdmxfeed->type == DMX_TYPE_TS) { + switch (dvbdmxfeed->pes_type) { + case DMX_TS_PES_VIDEO: + case DMX_TS_PES_AUDIO: + case DMX_TS_PES_TELETEXT: + case DMX_TS_PES_PCR: + case DMX_TS_PES_OTHER: + c = alloc_channel(fdtv); + break; + default: + dev_err(fdtv->device, + "can't start dmx feed: invalid pes type %u\n", + dvbdmxfeed->pes_type); + ret = -EINVAL; + goto out; + } + } else { + c = alloc_channel(fdtv); + } + + if (c > 15) { + dev_err(fdtv->device, "can't start dmx feed: busy\n"); + ret = -EBUSY; + goto out; + } + + dvbdmxfeed->priv = (typeof(dvbdmxfeed->priv))(unsigned long)c; + fdtv->channel_pid[c] = dvbdmxfeed->pid; + collect_channels(fdtv, &pidc, pids); + + if (dvbdmxfeed->pid == 8192) { + ret = avc_tuner_get_ts(fdtv); + if (ret) { + dealloc_channel(fdtv, c); + dev_err(fdtv->device, "can't get TS\n"); + goto out; + } + } else { + ret = avc_tuner_set_pids(fdtv, pidc, pids); + if (ret) { + dealloc_channel(fdtv, c); + dev_err(fdtv->device, "can't set PIDs\n"); + goto out; + } + } +out: + mutex_unlock(&fdtv->demux_mutex); + + return ret; +} + +int fdtv_stop_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + struct dvb_demux *demux = dvbdmxfeed->demux; + struct firedtv *fdtv = demux->priv; + int pidc, c, ret; + u16 pids[16]; + + if (dvbdmxfeed->type == DMX_TYPE_TS && + !((dvbdmxfeed->ts_type & TS_PACKET) && + (demux->dmx.frontend->source != DMX_MEMORY_FE))) { + + if (dvbdmxfeed->ts_type & TS_DECODER) { + if (dvbdmxfeed->pes_type >= DMX_TS_PES_OTHER || + !demux->pesfilter[dvbdmxfeed->pes_type]) + return -EINVAL; + + demux->pids[dvbdmxfeed->pes_type] |= 0x8000; + demux->pesfilter[dvbdmxfeed->pes_type] = NULL; + } + + if (!(dvbdmxfeed->ts_type & TS_DECODER && + dvbdmxfeed->pes_type < DMX_TS_PES_OTHER)) + return 0; + } + + if (mutex_lock_interruptible(&fdtv->demux_mutex)) + return -EINTR; + + c = (unsigned long)dvbdmxfeed->priv; + dealloc_channel(fdtv, c); + collect_channels(fdtv, &pidc, pids); + + ret = avc_tuner_set_pids(fdtv, pidc, pids); + + mutex_unlock(&fdtv->demux_mutex); + + return ret; +} + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +int fdtv_dvb_register(struct firedtv *fdtv) +{ + int err; + + err = dvb_register_adapter(&fdtv->adapter, fdtv_model_names[fdtv->type], + THIS_MODULE, fdtv->device, adapter_nr); + if (err < 0) + goto fail_log; + + /*DMX_TS_FILTERING | DMX_SECTION_FILTERING*/ + fdtv->demux.dmx.capabilities = 0; + + fdtv->demux.priv = fdtv; + fdtv->demux.filternum = 16; + fdtv->demux.feednum = 16; + fdtv->demux.start_feed = fdtv_start_feed; + fdtv->demux.stop_feed = fdtv_stop_feed; + fdtv->demux.write_to_decoder = NULL; + + err = dvb_dmx_init(&fdtv->demux); + if (err) + goto fail_unreg_adapter; + + fdtv->dmxdev.filternum = 16; + fdtv->dmxdev.demux = &fdtv->demux.dmx; + fdtv->dmxdev.capabilities = 0; + + err = dvb_dmxdev_init(&fdtv->dmxdev, &fdtv->adapter); + if (err) + goto fail_dmx_release; + + fdtv->frontend.source = DMX_FRONTEND_0; + + err = fdtv->demux.dmx.add_frontend(&fdtv->demux.dmx, &fdtv->frontend); + if (err) + goto fail_dmxdev_release; + + err = fdtv->demux.dmx.connect_frontend(&fdtv->demux.dmx, + &fdtv->frontend); + if (err) + goto fail_rem_frontend; + + dvb_net_init(&fdtv->adapter, &fdtv->dvbnet, &fdtv->demux.dmx); + + fdtv_frontend_init(fdtv); + err = dvb_register_frontend(&fdtv->adapter, &fdtv->fe); + if (err) + goto fail_net_release; + + err = fdtv_ca_register(fdtv); + if (err) + dev_info(fdtv->device, + "Conditional Access Module not enabled\n"); + return 0; + +fail_net_release: + dvb_net_release(&fdtv->dvbnet); + fdtv->demux.dmx.close(&fdtv->demux.dmx); +fail_rem_frontend: + fdtv->demux.dmx.remove_frontend(&fdtv->demux.dmx, &fdtv->frontend); +fail_dmxdev_release: + dvb_dmxdev_release(&fdtv->dmxdev); +fail_dmx_release: + dvb_dmx_release(&fdtv->demux); +fail_unreg_adapter: + dvb_unregister_adapter(&fdtv->adapter); +fail_log: + dev_err(fdtv->device, "DVB initialization failed\n"); + return err; +} + +void fdtv_dvb_unregister(struct firedtv *fdtv) +{ + fdtv_ca_release(fdtv); + dvb_unregister_frontend(&fdtv->fe); + dvb_net_release(&fdtv->dvbnet); + fdtv->demux.dmx.close(&fdtv->demux.dmx); + fdtv->demux.dmx.remove_frontend(&fdtv->demux.dmx, &fdtv->frontend); + dvb_dmxdev_release(&fdtv->dmxdev); + dvb_dmx_release(&fdtv->demux); + dvb_unregister_adapter(&fdtv->adapter); +} + +const char *fdtv_model_names[] = { + [FIREDTV_UNKNOWN] = "unknown type", + [FIREDTV_DVB_S] = "FireDTV S/CI", + [FIREDTV_DVB_C] = "FireDTV C/CI", + [FIREDTV_DVB_T] = "FireDTV T/CI", + [FIREDTV_DVB_S2] = "FireDTV S2 ", +}; + +struct firedtv *fdtv_alloc(struct device *dev, + const struct firedtv_backend *backend, + const char *name, size_t name_len) +{ + struct firedtv *fdtv; + int i; + + fdtv = kzalloc(sizeof(*fdtv), GFP_KERNEL); + if (!fdtv) + return NULL; + + dev->driver_data = fdtv; + fdtv->device = dev; + fdtv->isochannel = -1; + fdtv->voltage = 0xff; + fdtv->tone = 0xff; + fdtv->backend = backend; + + mutex_init(&fdtv->avc_mutex); + init_waitqueue_head(&fdtv->avc_wait); + fdtv->avc_reply_received = true; + mutex_init(&fdtv->demux_mutex); + INIT_WORK(&fdtv->remote_ctrl_work, avc_remote_ctrl_work); + + for (i = ARRAY_SIZE(fdtv_model_names); --i; ) + if (strlen(fdtv_model_names[i]) <= name_len && + strncmp(name, fdtv_model_names[i], name_len) == 0) + break; + fdtv->type = i; + + return fdtv; +} + +#define MATCH_FLAGS (IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID | \ + IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION) + +#define DIGITAL_EVERYWHERE_OUI 0x001287 +#define AVC_UNIT_SPEC_ID_ENTRY 0x00a02d +#define AVC_SW_VERSION_ENTRY 0x010001 + +static struct ieee1394_device_id fdtv_id_table[] = { + { + /* FloppyDTV S/CI and FloppyDTV S2 */ + .match_flags = MATCH_FLAGS, + .vendor_id = DIGITAL_EVERYWHERE_OUI, + .model_id = 0x000024, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, + .version = AVC_SW_VERSION_ENTRY, + }, { + /* FloppyDTV T/CI */ + .match_flags = MATCH_FLAGS, + .vendor_id = DIGITAL_EVERYWHERE_OUI, + .model_id = 0x000025, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, + .version = AVC_SW_VERSION_ENTRY, + }, { + /* FloppyDTV C/CI */ + .match_flags = MATCH_FLAGS, + .vendor_id = DIGITAL_EVERYWHERE_OUI, + .model_id = 0x000026, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, + .version = AVC_SW_VERSION_ENTRY, + }, { + /* FireDTV S/CI and FloppyDTV S2 */ + .match_flags = MATCH_FLAGS, + .vendor_id = DIGITAL_EVERYWHERE_OUI, + .model_id = 0x000034, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, + .version = AVC_SW_VERSION_ENTRY, + }, { + /* FireDTV T/CI */ + .match_flags = MATCH_FLAGS, + .vendor_id = DIGITAL_EVERYWHERE_OUI, + .model_id = 0x000035, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, + .version = AVC_SW_VERSION_ENTRY, + }, { + /* FireDTV C/CI */ + .match_flags = MATCH_FLAGS, + .vendor_id = DIGITAL_EVERYWHERE_OUI, + .model_id = 0x000036, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, + .version = AVC_SW_VERSION_ENTRY, + }, {} +}; +MODULE_DEVICE_TABLE(ieee1394, fdtv_id_table); + +static int __init fdtv_init(void) +{ + return fdtv_1394_init(fdtv_id_table); +} + +static void __exit fdtv_exit(void) +{ + fdtv_1394_exit(); +} + +module_init(fdtv_init); +module_exit(fdtv_exit); + +MODULE_AUTHOR("Andreas Monitzer <andy@monitzer.com>"); +MODULE_AUTHOR("Ben Backx <ben@bbackx.com>"); +MODULE_DESCRIPTION("FireDTV DVB Driver"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("FireDTV DVB"); diff --git a/drivers/media/dvb/firewire/firedtv-fe.c b/drivers/media/dvb/firewire/firedtv-fe.c new file mode 100644 index 00000000000..7ba43630a25 --- /dev/null +++ b/drivers/media/dvb/firewire/firedtv-fe.c @@ -0,0 +1,247 @@ +/* + * FireDTV driver (formerly known as FireSAT) + * + * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> + * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/types.h> + +#include <dvb_frontend.h> + +#include "firedtv.h" + +static int fdtv_dvb_init(struct dvb_frontend *fe) +{ + struct firedtv *fdtv = fe->sec_priv; + int err; + + /* FIXME - allocate free channel at IRM */ + fdtv->isochannel = fdtv->adapter.num; + + err = cmp_establish_pp_connection(fdtv, fdtv->subunit, + fdtv->isochannel); + if (err) { + dev_err(fdtv->device, + "could not establish point to point connection\n"); + return err; + } + + return fdtv->backend->start_iso(fdtv); +} + +static int fdtv_sleep(struct dvb_frontend *fe) +{ + struct firedtv *fdtv = fe->sec_priv; + + fdtv->backend->stop_iso(fdtv); + cmp_break_pp_connection(fdtv, fdtv->subunit, fdtv->isochannel); + fdtv->isochannel = -1; + return 0; +} + +#define LNBCONTROL_DONTCARE 0xff + +static int fdtv_diseqc_send_master_cmd(struct dvb_frontend *fe, + struct dvb_diseqc_master_cmd *cmd) +{ + struct firedtv *fdtv = fe->sec_priv; + + return avc_lnb_control(fdtv, LNBCONTROL_DONTCARE, LNBCONTROL_DONTCARE, + LNBCONTROL_DONTCARE, 1, cmd); +} + +static int fdtv_diseqc_send_burst(struct dvb_frontend *fe, + fe_sec_mini_cmd_t minicmd) +{ + return 0; +} + +static int fdtv_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) +{ + struct firedtv *fdtv = fe->sec_priv; + + fdtv->tone = tone; + return 0; +} + +static int fdtv_set_voltage(struct dvb_frontend *fe, + fe_sec_voltage_t voltage) +{ + struct firedtv *fdtv = fe->sec_priv; + + fdtv->voltage = voltage; + return 0; +} + +static int fdtv_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + struct firedtv *fdtv = fe->sec_priv; + struct firedtv_tuner_status stat; + + if (avc_tuner_status(fdtv, &stat)) + return -EINVAL; + + if (stat.no_rf) + *status = 0; + else + *status = FE_HAS_SIGNAL | FE_HAS_VITERBI | FE_HAS_SYNC | + FE_HAS_CARRIER | FE_HAS_LOCK; + return 0; +} + +static int fdtv_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct firedtv *fdtv = fe->sec_priv; + struct firedtv_tuner_status stat; + + if (avc_tuner_status(fdtv, &stat)) + return -EINVAL; + + *ber = stat.ber; + return 0; +} + +static int fdtv_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct firedtv *fdtv = fe->sec_priv; + struct firedtv_tuner_status stat; + + if (avc_tuner_status(fdtv, &stat)) + return -EINVAL; + + *strength = stat.signal_strength << 8; + return 0; +} + +static int fdtv_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct firedtv *fdtv = fe->sec_priv; + struct firedtv_tuner_status stat; + + if (avc_tuner_status(fdtv, &stat)) + return -EINVAL; + + /* C/N[dB] = -10 * log10(snr / 65535) */ + *snr = stat.carrier_noise_ratio * 257; + return 0; +} + +static int fdtv_read_uncorrected_blocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + return -EOPNOTSUPP; +} + +#define ACCEPTED 0x9 + +static int fdtv_set_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) +{ + struct firedtv *fdtv = fe->sec_priv; + + /* FIXME: avc_tuner_dsd never returns ACCEPTED. Check status? */ + if (avc_tuner_dsd(fdtv, params) != ACCEPTED) + return -EINVAL; + else + return 0; /* not sure of this... */ +} + +static int fdtv_get_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) +{ + return -EOPNOTSUPP; +} + +void fdtv_frontend_init(struct firedtv *fdtv) +{ + struct dvb_frontend_ops *ops = &fdtv->fe.ops; + struct dvb_frontend_info *fi = &ops->info; + + ops->init = fdtv_dvb_init; + ops->sleep = fdtv_sleep; + + ops->set_frontend = fdtv_set_frontend; + ops->get_frontend = fdtv_get_frontend; + + ops->read_status = fdtv_read_status; + ops->read_ber = fdtv_read_ber; + ops->read_signal_strength = fdtv_read_signal_strength; + ops->read_snr = fdtv_read_snr; + ops->read_ucblocks = fdtv_read_uncorrected_blocks; + + ops->diseqc_send_master_cmd = fdtv_diseqc_send_master_cmd; + ops->diseqc_send_burst = fdtv_diseqc_send_burst; + ops->set_tone = fdtv_set_tone; + ops->set_voltage = fdtv_set_voltage; + + switch (fdtv->type) { + case FIREDTV_DVB_S: + case FIREDTV_DVB_S2: + fi->type = FE_QPSK; + + fi->frequency_min = 950000; + fi->frequency_max = 2150000; + fi->frequency_stepsize = 125; + fi->symbol_rate_min = 1000000; + fi->symbol_rate_max = 40000000; + + fi->caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | + FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | + FE_CAN_FEC_7_8 | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK; + break; + + case FIREDTV_DVB_C: + fi->type = FE_QAM; + + fi->frequency_min = 47000000; + fi->frequency_max = 866000000; + fi->frequency_stepsize = 62500; + fi->symbol_rate_min = 870000; + fi->symbol_rate_max = 6900000; + + fi->caps = FE_CAN_INVERSION_AUTO | + FE_CAN_QAM_16 | + FE_CAN_QAM_32 | + FE_CAN_QAM_64 | + FE_CAN_QAM_128 | + FE_CAN_QAM_256 | + FE_CAN_QAM_AUTO; + break; + + case FIREDTV_DVB_T: + fi->type = FE_OFDM; + + fi->frequency_min = 49000000; + fi->frequency_max = 861000000; + fi->frequency_stepsize = 62500; + + fi->caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_2_3 | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO; + break; + + default: + dev_err(fdtv->device, "no frontend for model type %d\n", + fdtv->type); + } + strcpy(fi->name, fdtv_model_names[fdtv->type]); + + fdtv->fe.dvb = &fdtv->adapter; + fdtv->fe.sec_priv = fdtv; +} diff --git a/drivers/media/dvb/firewire/firedtv-rc.c b/drivers/media/dvb/firewire/firedtv-rc.c new file mode 100644 index 00000000000..46a6324d7b7 --- /dev/null +++ b/drivers/media/dvb/firewire/firedtv-rc.c @@ -0,0 +1,190 @@ +/* + * FireDTV driver (formerly known as FireSAT) + * + * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#include <linux/bitops.h> +#include <linux/input.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/types.h> + +#include "firedtv.h" + +/* fixed table with older keycodes, geared towards MythTV */ +const static u16 oldtable[] = { + + /* code from device: 0x4501...0x451f */ + + KEY_ESC, + KEY_F9, + KEY_1, + KEY_2, + KEY_3, + KEY_4, + KEY_5, + KEY_6, + KEY_7, + KEY_8, + KEY_9, + KEY_I, + KEY_0, + KEY_ENTER, + KEY_RED, + KEY_UP, + KEY_GREEN, + KEY_F10, + KEY_SPACE, + KEY_F11, + KEY_YELLOW, + KEY_DOWN, + KEY_BLUE, + KEY_Z, + KEY_P, + KEY_PAGEDOWN, + KEY_LEFT, + KEY_W, + KEY_RIGHT, + KEY_P, + KEY_M, + + /* code from device: 0x4540...0x4542 */ + + KEY_R, + KEY_V, + KEY_C, +}; + +/* user-modifiable table for a remote as sold in 2008 */ +const static u16 keytable[] = { + + /* code from device: 0x0300...0x031f */ + + [0x00] = KEY_POWER, + [0x01] = KEY_SLEEP, + [0x02] = KEY_STOP, + [0x03] = KEY_OK, + [0x04] = KEY_RIGHT, + [0x05] = KEY_1, + [0x06] = KEY_2, + [0x07] = KEY_3, + [0x08] = KEY_LEFT, + [0x09] = KEY_4, + [0x0a] = KEY_5, + [0x0b] = KEY_6, + [0x0c] = KEY_UP, + [0x0d] = KEY_7, + [0x0e] = KEY_8, + [0x0f] = KEY_9, + [0x10] = KEY_DOWN, + [0x11] = KEY_TITLE, /* "OSD" - fixme */ + [0x12] = KEY_0, + [0x13] = KEY_F20, /* "16:9" - fixme */ + [0x14] = KEY_SCREEN, /* "FULL" - fixme */ + [0x15] = KEY_MUTE, + [0x16] = KEY_SUBTITLE, + [0x17] = KEY_RECORD, + [0x18] = KEY_TEXT, + [0x19] = KEY_AUDIO, + [0x1a] = KEY_RED, + [0x1b] = KEY_PREVIOUS, + [0x1c] = KEY_REWIND, + [0x1d] = KEY_PLAYPAUSE, + [0x1e] = KEY_NEXT, + [0x1f] = KEY_VOLUMEUP, + + /* code from device: 0x0340...0x0354 */ + + [0x20] = KEY_CHANNELUP, + [0x21] = KEY_F21, /* "4:3" - fixme */ + [0x22] = KEY_TV, + [0x23] = KEY_DVD, + [0x24] = KEY_VCR, + [0x25] = KEY_AUX, + [0x26] = KEY_GREEN, + [0x27] = KEY_YELLOW, + [0x28] = KEY_BLUE, + [0x29] = KEY_CHANNEL, /* "CH.LIST" */ + [0x2a] = KEY_VENDOR, /* "CI" - fixme */ + [0x2b] = KEY_VOLUMEDOWN, + [0x2c] = KEY_CHANNELDOWN, + [0x2d] = KEY_LAST, + [0x2e] = KEY_INFO, + [0x2f] = KEY_FORWARD, + [0x30] = KEY_LIST, + [0x31] = KEY_FAVORITES, + [0x32] = KEY_MENU, + [0x33] = KEY_EPG, + [0x34] = KEY_EXIT, +}; + +int fdtv_register_rc(struct firedtv *fdtv, struct device *dev) +{ + struct input_dev *idev; + int i, err; + + idev = input_allocate_device(); + if (!idev) + return -ENOMEM; + + fdtv->remote_ctrl_dev = idev; + idev->name = "FireDTV remote control"; + idev->dev.parent = dev; + idev->evbit[0] = BIT_MASK(EV_KEY); + idev->keycode = kmemdup(keytable, sizeof(keytable), GFP_KERNEL); + if (!idev->keycode) { + err = -ENOMEM; + goto fail; + } + idev->keycodesize = sizeof(keytable[0]); + idev->keycodemax = ARRAY_SIZE(keytable); + + for (i = 0; i < ARRAY_SIZE(keytable); i++) + set_bit(keytable[i], idev->keybit); + + err = input_register_device(idev); + if (err) + goto fail_free_keymap; + + return 0; + +fail_free_keymap: + kfree(idev->keycode); +fail: + input_free_device(idev); + return err; +} + +void fdtv_unregister_rc(struct firedtv *fdtv) +{ + kfree(fdtv->remote_ctrl_dev->keycode); + input_unregister_device(fdtv->remote_ctrl_dev); +} + +void fdtv_handle_rc(struct firedtv *fdtv, unsigned int code) +{ + u16 *keycode = fdtv->remote_ctrl_dev->keycode; + + if (code >= 0x0300 && code <= 0x031f) + code = keycode[code - 0x0300]; + else if (code >= 0x0340 && code <= 0x0354) + code = keycode[code - 0x0320]; + else if (code >= 0x4501 && code <= 0x451f) + code = oldtable[code - 0x4501]; + else if (code >= 0x4540 && code <= 0x4542) + code = oldtable[code - 0x4521]; + else { + printk(KERN_DEBUG "firedtv: invalid key code 0x%04x " + "from remote control\n", code); + return; + } + + input_report_key(fdtv->remote_ctrl_dev, code, 1); + input_report_key(fdtv->remote_ctrl_dev, code, 0); +} diff --git a/drivers/media/dvb/firewire/firedtv.h b/drivers/media/dvb/firewire/firedtv.h new file mode 100644 index 00000000000..d48530b81e6 --- /dev/null +++ b/drivers/media/dvb/firewire/firedtv.h @@ -0,0 +1,182 @@ +/* + * FireDTV driver (formerly known as FireSAT) + * + * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> + * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> + * + * 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 _FIREDTV_H +#define _FIREDTV_H + +#include <linux/dvb/dmx.h> +#include <linux/dvb/frontend.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/spinlock_types.h> +#include <linux/types.h> +#include <linux/wait.h> +#include <linux/workqueue.h> + +#include <demux.h> +#include <dmxdev.h> +#include <dvb_demux.h> +#include <dvb_frontend.h> +#include <dvb_net.h> +#include <dvbdev.h> + +struct firedtv_tuner_status { + unsigned active_system:8; + unsigned searching:1; + unsigned moving:1; + unsigned no_rf:1; + unsigned input:1; + unsigned selected_antenna:7; + unsigned ber:32; + unsigned signal_strength:8; + unsigned raster_frequency:2; + unsigned rf_frequency:22; + unsigned man_dep_info_length:8; + unsigned front_end_error:1; + unsigned antenna_error:1; + unsigned front_end_power_status:1; + unsigned power_supply:1; + unsigned carrier_noise_ratio:16; + unsigned power_supply_voltage:8; + unsigned antenna_voltage:8; + unsigned firewire_bus_voltage:8; + unsigned ca_mmi:1; + unsigned ca_pmt_reply:1; + unsigned ca_date_time_request:1; + unsigned ca_application_info:1; + unsigned ca_module_present_status:1; + unsigned ca_dvb_flag:1; + unsigned ca_error_flag:1; + unsigned ca_initialization_status:1; +}; + +enum model_type { + FIREDTV_UNKNOWN = 0, + FIREDTV_DVB_S = 1, + FIREDTV_DVB_C = 2, + FIREDTV_DVB_T = 3, + FIREDTV_DVB_S2 = 4, +}; + +struct device; +struct input_dev; +struct firedtv; + +struct firedtv_backend { + int (*lock)(struct firedtv *fdtv, u64 addr, void *data, __be32 arg); + int (*read)(struct firedtv *fdtv, u64 addr, void *data, size_t len); + int (*write)(struct firedtv *fdtv, u64 addr, void *data, size_t len); + int (*start_iso)(struct firedtv *fdtv); + void (*stop_iso)(struct firedtv *fdtv); +}; + +struct firedtv { + struct device *device; + struct list_head list; + + struct dvb_adapter adapter; + struct dmxdev dmxdev; + struct dvb_demux demux; + struct dmx_frontend frontend; + struct dvb_net dvbnet; + struct dvb_frontend fe; + + struct dvb_device *cadev; + int ca_last_command; + int ca_time_interval; + + struct mutex avc_mutex; + wait_queue_head_t avc_wait; + bool avc_reply_received; + struct work_struct remote_ctrl_work; + struct input_dev *remote_ctrl_dev; + + enum model_type type; + char subunit; + char isochannel; + fe_sec_voltage_t voltage; + fe_sec_tone_mode_t tone; + + const struct firedtv_backend *backend; + void *backend_data; + + struct mutex demux_mutex; + unsigned long channel_active; + u16 channel_pid[16]; + + size_t response_length; + u8 response[512]; +}; + +/* firedtv-1394.c */ +#ifdef CONFIG_DVB_FIREDTV_IEEE1394 +int fdtv_1394_init(struct ieee1394_device_id id_table[]); +void fdtv_1394_exit(void); +#else +static inline int fdtv_1394_init(struct ieee1394_device_id it[]) { return 0; } +static inline void fdtv_1394_exit(void) {} +#endif + +/* firedtv-avc.c */ +int avc_recv(struct firedtv *fdtv, void *data, size_t length); +int avc_tuner_status(struct firedtv *fdtv, struct firedtv_tuner_status *stat); +struct dvb_frontend_parameters; +int avc_tuner_dsd(struct firedtv *fdtv, struct dvb_frontend_parameters *params); +int avc_tuner_set_pids(struct firedtv *fdtv, unsigned char pidc, u16 pid[]); +int avc_tuner_get_ts(struct firedtv *fdtv); +int avc_identify_subunit(struct firedtv *fdtv); +struct dvb_diseqc_master_cmd; +int avc_lnb_control(struct firedtv *fdtv, char voltage, char burst, + char conttone, char nrdiseq, + struct dvb_diseqc_master_cmd *diseqcmd); +void avc_remote_ctrl_work(struct work_struct *work); +int avc_register_remote_control(struct firedtv *fdtv); +int avc_ca_app_info(struct firedtv *fdtv, char *app_info, unsigned int *len); +int avc_ca_info(struct firedtv *fdtv, char *app_info, unsigned int *len); +int avc_ca_reset(struct firedtv *fdtv); +int avc_ca_pmt(struct firedtv *fdtv, char *app_info, int length); +int avc_ca_get_time_date(struct firedtv *fdtv, int *interval); +int avc_ca_enter_menu(struct firedtv *fdtv); +int avc_ca_get_mmi(struct firedtv *fdtv, char *mmi_object, unsigned int *len); +int cmp_establish_pp_connection(struct firedtv *fdtv, int plug, int channel); +void cmp_break_pp_connection(struct firedtv *fdtv, int plug, int channel); + +/* firedtv-ci.c */ +int fdtv_ca_register(struct firedtv *fdtv); +void fdtv_ca_release(struct firedtv *fdtv); + +/* firedtv-dvb.c */ +int fdtv_start_feed(struct dvb_demux_feed *dvbdmxfeed); +int fdtv_stop_feed(struct dvb_demux_feed *dvbdmxfeed); +int fdtv_dvb_register(struct firedtv *fdtv); +void fdtv_dvb_unregister(struct firedtv *fdtv); +struct firedtv *fdtv_alloc(struct device *dev, + const struct firedtv_backend *backend, + const char *name, size_t name_len); +extern const char *fdtv_model_names[]; + +/* firedtv-fe.c */ +void fdtv_frontend_init(struct firedtv *fdtv); + +/* firedtv-rc.c */ +#ifdef CONFIG_DVB_FIREDTV_INPUT +int fdtv_register_rc(struct firedtv *fdtv, struct device *dev); +void fdtv_unregister_rc(struct firedtv *fdtv); +void fdtv_handle_rc(struct firedtv *fdtv, unsigned int code); +#else +static inline int fdtv_register_rc(struct firedtv *fdtv, + struct device *dev) { return 0; } +static inline void fdtv_unregister_rc(struct firedtv *fdtv) {} +static inline void fdtv_handle_rc(struct firedtv *fdtv, unsigned int code) {} +#endif + +#endif /* _FIREDTV_H */ diff --git a/drivers/media/radio/radio-si470x.c b/drivers/media/radio/radio-si470x.c index 67cbce82cb9..4dfed6aa2db 100644 --- a/drivers/media/radio/radio-si470x.c +++ b/drivers/media/radio/radio-si470x.c @@ -98,11 +98,16 @@ * - blacklisted KWorld radio in hid-core.c and hid-ids.h * 2008-12-03 Mark Lord <mlord@pobox.com> * - add support for DealExtreme USB Radio + * 2009-01-31 Bob Ross <pigiron@gmx.com> + * - correction of stereo detection/setting + * - correction of signal strength indicator scaling + * 2009-01-31 Rick Bronson <rick@efn.org> + * Tobias Lorenz <tobias.lorenz@gmx.net> + * - add LED status output * * ToDo: * - add firmware download/update support * - RDS support: interrupt mode, instead of polling - * - add LED status output (check if that's not already done in firmware) */ @@ -882,6 +887,30 @@ static int si470x_rds_on(struct si470x_device *radio) /************************************************************************** + * General Driver Functions - LED_REPORT + **************************************************************************/ + +/* + * si470x_set_led_state - sets the led state + */ +static int si470x_set_led_state(struct si470x_device *radio, + unsigned char led_state) +{ + unsigned char buf[LED_REPORT_SIZE]; + int retval; + + buf[0] = LED_REPORT; + buf[1] = LED_COMMAND; + buf[2] = led_state; + + retval = si470x_set_report(radio, (void *) &buf, sizeof(buf)); + + return (retval < 0) ? -EINVAL : 0; +} + + + +/************************************************************************** * RDS Driver Functions **************************************************************************/ @@ -1385,20 +1414,22 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv, }; /* stereo indicator == stereo (instead of mono) */ - if ((radio->registers[STATUSRSSI] & STATUSRSSI_ST) == 1) - tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; - else + if ((radio->registers[STATUSRSSI] & STATUSRSSI_ST) == 0) tuner->rxsubchans = V4L2_TUNER_SUB_MONO; + else + tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; /* mono/stereo selector */ - if ((radio->registers[POWERCFG] & POWERCFG_MONO) == 1) - tuner->audmode = V4L2_TUNER_MODE_MONO; - else + if ((radio->registers[POWERCFG] & POWERCFG_MONO) == 0) tuner->audmode = V4L2_TUNER_MODE_STEREO; + else + tuner->audmode = V4L2_TUNER_MODE_MONO; /* min is worst, max is best; signal:0..0xffff; rssi: 0..0xff */ - tuner->signal = (radio->registers[STATUSRSSI] & STATUSRSSI_RSSI) - * 0x0101; + /* measured in units of dbµV in 1 db increments (max at ~75 dbµV) */ + tuner->signal = (radio->registers[STATUSRSSI] & STATUSRSSI_RSSI); + /* the ideal factor is 0xffff/75 = 873,8 */ + tuner->signal = (tuner->signal * 873) + (8 * tuner->signal / 10); /* automatic frequency control: -1: freq to low, 1 freq to high */ /* AFCRL does only indicate that freq. differs, not if too low/high */ @@ -1632,6 +1663,9 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, /* set initial frequency */ si470x_set_freq(radio, 87.5 * FREQ_MUL); /* available in all regions */ + /* set led to connect state */ + si470x_set_led_state(radio, BLINK_GREEN_LED); + /* rds buffer allocation */ radio->buf_size = rds_buf * 3; radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL); @@ -1715,6 +1749,9 @@ static void si470x_usb_driver_disconnect(struct usb_interface *intf) cancel_delayed_work_sync(&radio->work); usb_set_intfdata(intf, NULL); if (radio->users == 0) { + /* set led to disconnect state */ + si470x_set_led_state(radio, BLINK_ORANGE_LED); + video_unregister_device(radio->videodev); kfree(radio->buffer); kfree(radio); diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 2ed24527ecd..65e4901f4db 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -422,6 +422,7 @@ static void destroy_urbs(struct gspca_dev *gspca_dev) if (urb == NULL) break; + BUG_ON(!gspca_dev->dev); gspca_dev->urb[i] = NULL; if (!gspca_dev->present) usb_kill_urb(urb); @@ -1950,8 +1951,12 @@ void gspca_disconnect(struct usb_interface *intf) { struct gspca_dev *gspca_dev = usb_get_intfdata(intf); + mutex_lock(&gspca_dev->usb_lock); gspca_dev->present = 0; + mutex_unlock(&gspca_dev->usb_lock); + destroy_urbs(gspca_dev); + gspca_dev->dev = NULL; usb_set_intfdata(intf, NULL); /* release the device */ diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c index f6b3ef6e691..c13bd2aa0be 100644 --- a/drivers/media/video/ivtv/ivtv-ioctl.c +++ b/drivers/media/video/ivtv/ivtv-ioctl.c @@ -393,7 +393,7 @@ static int ivtv_g_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_fo return 0; } - v4l2_subdev_call(itv->sd_video, video, s_fmt, fmt); + v4l2_subdev_call(itv->sd_video, video, g_fmt, fmt); vbifmt->service_set = ivtv_get_service_set(vbifmt); return 0; } @@ -1748,6 +1748,18 @@ static long ivtv_default(struct file *file, void *fh, int cmd, void *arg) break; } + case IVTV_IOC_DMA_FRAME: + case VIDEO_GET_PTS: + case VIDEO_GET_FRAME_COUNT: + case VIDEO_GET_EVENT: + case VIDEO_PLAY: + case VIDEO_STOP: + case VIDEO_FREEZE: + case VIDEO_CONTINUE: + case VIDEO_COMMAND: + case VIDEO_TRY_COMMAND: + return ivtv_decoder_ioctls(file, cmd, (void *)arg); + default: return -EINVAL; } @@ -1790,18 +1802,6 @@ static long ivtv_serialized_ioctl(struct ivtv *itv, struct file *filp, ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode); return 0; - case IVTV_IOC_DMA_FRAME: - case VIDEO_GET_PTS: - case VIDEO_GET_FRAME_COUNT: - case VIDEO_GET_EVENT: - case VIDEO_PLAY: - case VIDEO_STOP: - case VIDEO_FREEZE: - case VIDEO_CONTINUE: - case VIDEO_COMMAND: - case VIDEO_TRY_COMMAND: - return ivtv_decoder_ioctls(filp, cmd, (void *)arg); - default: break; } diff --git a/drivers/mfd/htc-egpio.c b/drivers/mfd/htc-egpio.c index 1a4d04664d6..aa266e1f69b 100644 --- a/drivers/mfd/htc-egpio.c +++ b/drivers/mfd/htc-egpio.c @@ -286,7 +286,7 @@ static int __init egpio_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) goto fail; - ei->base_addr = ioremap_nocache(res->start, res->end - res->start); + ei->base_addr = ioremap_nocache(res->start, resource_size(res)); if (!ei->base_addr) goto fail; pr_debug("EGPIO phys=%08x virt=%p\n", (u32)res->start, ei->base_addr); @@ -307,7 +307,7 @@ static int __init egpio_probe(struct platform_device *pdev) ei->nchips = pdata->num_chips; ei->chip = kzalloc(sizeof(struct egpio_chip) * ei->nchips, GFP_KERNEL); - if (!ei) { + if (!ei->chip) { ret = -ENOMEM; goto fail; } diff --git a/drivers/mfd/pcf50633-core.c b/drivers/mfd/pcf50633-core.c index ea9488e7ad6..2e36057659e 100644 --- a/drivers/mfd/pcf50633-core.c +++ b/drivers/mfd/pcf50633-core.c @@ -678,6 +678,7 @@ static int __devexit pcf50633_remove(struct i2c_client *client) static struct i2c_device_id pcf50633_id_table[] = { {"pcf50633", 0x73}, + {/* end of list */} }; static struct i2c_driver pcf50633_driver = { diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c index 0e5761f1263..4c7b7962f6b 100644 --- a/drivers/mfd/sm501.c +++ b/drivers/mfd/sm501.c @@ -1050,7 +1050,7 @@ static int __devinit sm501_gpio_register_chip(struct sm501_devdata *sm, return gpiochip_add(gchip); } -static int sm501_register_gpio(struct sm501_devdata *sm) +static int __devinit sm501_register_gpio(struct sm501_devdata *sm) { struct sm501_gpio *gpio = &sm->gpio; resource_size_t iobase = sm->io_res->start + SM501_GPIO; @@ -1321,7 +1321,7 @@ static unsigned int sm501_mem_local[] = { * Common init code for an SM501 */ -static int sm501_init_dev(struct sm501_devdata *sm) +static int __devinit sm501_init_dev(struct sm501_devdata *sm) { struct sm501_initdata *idata; struct sm501_platdata *pdata; @@ -1397,7 +1397,7 @@ static int sm501_init_dev(struct sm501_devdata *sm) return 0; } -static int sm501_plat_probe(struct platform_device *dev) +static int __devinit sm501_plat_probe(struct platform_device *dev) { struct sm501_devdata *sm; int ret; @@ -1586,8 +1586,8 @@ static struct sm501_platdata sm501_pci_platdata = { .gpio_base = -1, }; -static int sm501_pci_probe(struct pci_dev *dev, - const struct pci_device_id *id) +static int __devinit sm501_pci_probe(struct pci_dev *dev, + const struct pci_device_id *id) { struct sm501_devdata *sm; int err; @@ -1693,7 +1693,7 @@ static void sm501_dev_remove(struct sm501_devdata *sm) sm501_gpio_remove(sm); } -static void sm501_pci_remove(struct pci_dev *dev) +static void __devexit sm501_pci_remove(struct pci_dev *dev) { struct sm501_devdata *sm = pci_get_drvdata(dev); @@ -1727,16 +1727,16 @@ static struct pci_device_id sm501_pci_tbl[] = { MODULE_DEVICE_TABLE(pci, sm501_pci_tbl); -static struct pci_driver sm501_pci_drv = { +static struct pci_driver sm501_pci_driver = { .name = "sm501", .id_table = sm501_pci_tbl, .probe = sm501_pci_probe, - .remove = sm501_pci_remove, + .remove = __devexit_p(sm501_pci_remove), }; MODULE_ALIAS("platform:sm501"); -static struct platform_driver sm501_plat_drv = { +static struct platform_driver sm501_plat_driver = { .driver = { .name = "sm501", .owner = THIS_MODULE, @@ -1749,14 +1749,14 @@ static struct platform_driver sm501_plat_drv = { static int __init sm501_base_init(void) { - platform_driver_register(&sm501_plat_drv); - return pci_register_driver(&sm501_pci_drv); + platform_driver_register(&sm501_plat_driver); + return pci_register_driver(&sm501_pci_driver); } static void __exit sm501_base_exit(void) { - platform_driver_unregister(&sm501_plat_drv); - pci_unregister_driver(&sm501_pci_drv); + platform_driver_unregister(&sm501_plat_driver); + pci_unregister_driver(&sm501_pci_driver); } module_init(sm501_base_init); diff --git a/drivers/mfd/twl4030-core.c b/drivers/mfd/twl4030-core.c index e7ab0035d30..68826f1e36b 100644 --- a/drivers/mfd/twl4030-core.c +++ b/drivers/mfd/twl4030-core.c @@ -38,7 +38,7 @@ #include <linux/i2c.h> #include <linux/i2c/twl4030.h> -#ifdef CONFIG_ARM +#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) #include <mach/cpu.h> #endif diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c index f92595c8f16..84d5ea1ec17 100644 --- a/drivers/mfd/wm8350-core.c +++ b/drivers/mfd/wm8350-core.c @@ -1111,7 +1111,7 @@ int wm8350_read_auxadc(struct wm8350 *wm8350, int channel, int scale, int vref) do { schedule_timeout_interruptible(1); reg = wm8350_reg_read(wm8350, WM8350_DIGITISER_CONTROL_1); - } while (tries-- && (reg & WM8350_AUXADC_POLL)); + } while (--tries && (reg & WM8350_AUXADC_POLL)); if (!tries) dev_err(wm8350->dev, "adc chn %d read timeout\n", channel); @@ -1297,14 +1297,29 @@ static void wm8350_client_dev_register(struct wm8350 *wm8350, int wm8350_device_init(struct wm8350 *wm8350, int irq, struct wm8350_platform_data *pdata) { - int ret = -EINVAL; + int ret; u16 id1, id2, mask_rev; u16 cust_id, mode, chip_rev; /* get WM8350 revision and config mode */ - wm8350->read_dev(wm8350, WM8350_RESET_ID, sizeof(id1), &id1); - wm8350->read_dev(wm8350, WM8350_ID, sizeof(id2), &id2); - wm8350->read_dev(wm8350, WM8350_REVISION, sizeof(mask_rev), &mask_rev); + ret = wm8350->read_dev(wm8350, WM8350_RESET_ID, sizeof(id1), &id1); + if (ret != 0) { + dev_err(wm8350->dev, "Failed to read ID: %d\n", ret); + goto err; + } + + ret = wm8350->read_dev(wm8350, WM8350_ID, sizeof(id2), &id2); + if (ret != 0) { + dev_err(wm8350->dev, "Failed to read ID: %d\n", ret); + goto err; + } + + ret = wm8350->read_dev(wm8350, WM8350_REVISION, sizeof(mask_rev), + &mask_rev); + if (ret != 0) { + dev_err(wm8350->dev, "Failed to read revision: %d\n", ret); + goto err; + } id1 = be16_to_cpu(id1); id2 = be16_to_cpu(id2); @@ -1404,14 +1419,12 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq, return ret; } - if (pdata && pdata->init) { - ret = pdata->init(wm8350); - if (ret != 0) { - dev_err(wm8350->dev, "Platform init() failed: %d\n", - ret); - goto err; - } - } + wm8350_reg_write(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK, 0xFFFF); + wm8350_reg_write(wm8350, WM8350_INT_STATUS_1_MASK, 0xFFFF); + wm8350_reg_write(wm8350, WM8350_INT_STATUS_2_MASK, 0xFFFF); + wm8350_reg_write(wm8350, WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, 0xFFFF); + wm8350_reg_write(wm8350, WM8350_GPIO_INT_STATUS_MASK, 0xFFFF); + wm8350_reg_write(wm8350, WM8350_COMPARATOR_INT_STATUS_MASK, 0xFFFF); mutex_init(&wm8350->auxadc_mutex); mutex_init(&wm8350->irq_mutex); @@ -1430,6 +1443,15 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq, } wm8350->chip_irq = irq; + if (pdata && pdata->init) { + ret = pdata->init(wm8350); + if (ret != 0) { + dev_err(wm8350->dev, "Platform init() failed: %d\n", + ret); + goto err; + } + } + wm8350_reg_write(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK, 0x0); wm8350_client_dev_register(wm8350, "wm8350-codec", diff --git a/drivers/mfd/wm8350-regmap.c b/drivers/mfd/wm8350-regmap.c index 68887b817d1..9a4cc954cb7 100644 --- a/drivers/mfd/wm8350-regmap.c +++ b/drivers/mfd/wm8350-regmap.c @@ -3188,7 +3188,7 @@ const struct wm8350_reg_access wm8350_reg_io_map[] = { { 0x7CFF, 0x0C00, 0x7FFF }, /* R1 - ID */ { 0x0000, 0x0000, 0x0000 }, /* R2 */ { 0xBE3B, 0xBE3B, 0x8000 }, /* R3 - System Control 1 */ - { 0xFCF7, 0xFCF7, 0xF800 }, /* R4 - System Control 2 */ + { 0xFEF7, 0xFEF7, 0xF800 }, /* R4 - System Control 2 */ { 0x80FF, 0x80FF, 0x8000 }, /* R5 - System Hibernate */ { 0xFB0E, 0xFB0E, 0x0000 }, /* R6 - Interface Control */ { 0x0000, 0x0000, 0x0000 }, /* R7 */ diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 45b1f430685..513eb09a638 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -584,7 +584,7 @@ static int mmc_blk_probe(struct mmc_card *card) if (err) goto out; - string_get_size(get_capacity(md->disk) << 9, STRING_UNITS_2, + string_get_size((u64)get_capacity(md->disk) << 9, STRING_UNITS_2, cap_str, sizeof(cap_str)); printk(KERN_INFO "%s: %s %s %s %s\n", md->disk->disk_name, mmc_card_id(card), mmc_card_name(card), diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c index b92b172074e..b9f1e84897c 100644 --- a/drivers/mmc/card/mmc_test.c +++ b/drivers/mmc/card/mmc_test.c @@ -494,7 +494,7 @@ static int mmc_test_basic_read(struct mmc_test_card *test) sg_init_one(&sg, test->buffer, 512); - ret = mmc_test_simple_transfer(test, &sg, 1, 0, 1, 512, 1); + ret = mmc_test_simple_transfer(test, &sg, 1, 0, 1, 512, 0); if (ret) return ret; diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 76bfe16c09b..2b1196e6142 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -1548,9 +1548,10 @@ static bool filter(struct dma_chan *chan, void *slave) { struct dw_dma_slave *dws = slave; - if (dws->dma_dev == chan->device->dev) + if (dws->dma_dev == chan->device->dev) { + chan->private = dws; return true; - else + } else return false; } #endif diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index db37490f67e..a631c81dce1 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -55,6 +55,7 @@ #define VS30 (1 << 25) #define SDVS18 (0x5 << 9) #define SDVS30 (0x6 << 9) +#define SDVS33 (0x7 << 9) #define SDVSCLR 0xFFFFF1FF #define SDVSDET 0x00000400 #define AUTOIDLE 0x1 @@ -375,6 +376,32 @@ static void mmc_omap_report_irq(struct mmc_omap_host *host, u32 status) } #endif /* CONFIG_MMC_DEBUG */ +/* + * MMC controller internal state machines reset + * + * Used to reset command or data internal state machines, using respectively + * SRC or SRD bit of SYSCTL register + * Can be called from interrupt context + */ +static inline void mmc_omap_reset_controller_fsm(struct mmc_omap_host *host, + unsigned long bit) +{ + unsigned long i = 0; + unsigned long limit = (loops_per_jiffy * + msecs_to_jiffies(MMC_TIMEOUT_MS)); + + OMAP_HSMMC_WRITE(host->base, SYSCTL, + OMAP_HSMMC_READ(host->base, SYSCTL) | bit); + + while ((OMAP_HSMMC_READ(host->base, SYSCTL) & bit) && + (i++ < limit)) + cpu_relax(); + + if (OMAP_HSMMC_READ(host->base, SYSCTL) & bit) + dev_err(mmc_dev(host->mmc), + "Timeout waiting on controller reset in %s\n", + __func__); +} /* * MMC controller IRQ handler @@ -403,21 +430,17 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) (status & CMD_CRC)) { if (host->cmd) { if (status & CMD_TIMEOUT) { - OMAP_HSMMC_WRITE(host->base, SYSCTL, - OMAP_HSMMC_READ(host->base, - SYSCTL) | SRC); - while (OMAP_HSMMC_READ(host->base, - SYSCTL) & SRC) - ; - + mmc_omap_reset_controller_fsm(host, SRC); host->cmd->error = -ETIMEDOUT; } else { host->cmd->error = -EILSEQ; } end_cmd = 1; } - if (host->data) + if (host->data) { mmc_dma_cleanup(host); + mmc_omap_reset_controller_fsm(host, SRD); + } } if ((status & DATA_TIMEOUT) || (status & DATA_CRC)) { @@ -426,12 +449,7 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) mmc_dma_cleanup(host); else host->data->error = -EILSEQ; - OMAP_HSMMC_WRITE(host->base, SYSCTL, - OMAP_HSMMC_READ(host->base, - SYSCTL) | SRD); - while (OMAP_HSMMC_READ(host->base, - SYSCTL) & SRD) - ; + mmc_omap_reset_controller_fsm(host, SRD); end_trans = 1; } } @@ -456,13 +474,20 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) } /* - * Switch MMC operating voltage + * Switch MMC interface voltage ... only relevant for MMC1. + * + * MMC2 and MMC3 use fixed 1.8V levels, and maybe a transceiver. + * The MMC2 transceiver controls are used instead of DAT4..DAT7. + * Some chips, like eMMC ones, use internal transceivers. */ static int omap_mmc_switch_opcond(struct mmc_omap_host *host, int vdd) { u32 reg_val = 0; int ret; + if (host->id != OMAP_MMC1_DEVID) + return 0; + /* Disable the clocks */ clk_disable(host->fclk); clk_disable(host->iclk); @@ -485,19 +510,26 @@ static int omap_mmc_switch_opcond(struct mmc_omap_host *host, int vdd) OMAP_HSMMC_WRITE(host->base, HCTL, OMAP_HSMMC_READ(host->base, HCTL) & SDVSCLR); reg_val = OMAP_HSMMC_READ(host->base, HCTL); + /* * If a MMC dual voltage card is detected, the set_ios fn calls * this fn with VDD bit set for 1.8V. Upon card removal from the * slot, omap_mmc_set_ios sets the VDD back to 3V on MMC_POWER_OFF. * - * Only MMC1 supports 3.0V. MMC2 will not function if SDVS30 is - * set in HCTL. + * Cope with a bit of slop in the range ... per data sheets: + * - "1.8V" for vdds_mmc1/vdds_mmc1a can be up to 2.45V max, + * but recommended values are 1.71V to 1.89V + * - "3.0V" for vdds_mmc1/vdds_mmc1a can be up to 3.5V max, + * but recommended values are 2.7V to 3.3V + * + * Board setup code shouldn't permit anything very out-of-range. + * TWL4030-family VMMC1 and VSIM regulators are fine (avoiding the + * middle range) but VSIM can't power DAT4..DAT7 at more than 3V. */ - if (host->id == OMAP_MMC1_DEVID && (((1 << vdd) == MMC_VDD_32_33) || - ((1 << vdd) == MMC_VDD_33_34))) - reg_val |= SDVS30; - if ((1 << vdd) == MMC_VDD_165_195) + if ((1 << vdd) <= MMC_VDD_23_24) reg_val |= SDVS18; + else + reg_val |= SDVS30; OMAP_HSMMC_WRITE(host->base, HCTL, reg_val); @@ -517,16 +549,15 @@ static void mmc_omap_detect(struct work_struct *work) { struct mmc_omap_host *host = container_of(work, struct mmc_omap_host, mmc_carddetect_work); + struct omap_mmc_slot_data *slot = &mmc_slot(host); + + host->carddetect = slot->card_detect(slot->card_detect_irq); sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch"); if (host->carddetect) { mmc_detect_change(host->mmc, (HZ * 200) / 1000); } else { - OMAP_HSMMC_WRITE(host->base, SYSCTL, - OMAP_HSMMC_READ(host->base, SYSCTL) | SRD); - while (OMAP_HSMMC_READ(host->base, SYSCTL) & SRD) - ; - + mmc_omap_reset_controller_fsm(host, SRD); mmc_detect_change(host->mmc, (HZ * 50) / 1000); } } @@ -538,7 +569,6 @@ static irqreturn_t omap_mmc_cd_handler(int irq, void *dev_id) { struct mmc_omap_host *host = (struct mmc_omap_host *)dev_id; - host->carddetect = mmc_slot(host).card_detect(irq); schedule_work(&host->mmc_carddetect_work); return IRQ_HANDLED; @@ -757,10 +787,14 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) case MMC_POWER_OFF: mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0); /* - * Reset bus voltage to 3V if it got set to 1.8V earlier. + * Reset interface voltage to 3V if it's 1.8V now; + * only relevant on MMC-1, the others always use 1.8V. + * * REVISIT: If we are able to detect cards after unplugging * a 1.8V card, this code should not be needed. */ + if (host->id != OMAP_MMC1_DEVID) + break; if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) { int vdd = fls(host->mmc->ocr_avail) - 1; if (omap_mmc_switch_opcond(host, vdd) != 0) @@ -784,7 +818,9 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) } if (host->id == OMAP_MMC1_DEVID) { - /* Only MMC1 can operate at 3V/1.8V */ + /* Only MMC1 can interface at 3V without some flavor + * of external transceiver; but they all handle 1.8V. + */ if ((OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET) && (ios->vdd == DUAL_VOLT_OCR_BIT)) { /* @@ -1137,7 +1173,9 @@ static int omap_mmc_suspend(struct platform_device *pdev, pm_message_t state) " level suspend\n"); } - if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) { + if (host->id == OMAP_MMC1_DEVID + && !(OMAP_HSMMC_READ(host->base, HCTL) + & SDVSDET)) { OMAP_HSMMC_WRITE(host->base, HCTL, OMAP_HSMMC_READ(host->base, HCTL) & SDVSCLR); diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c index 35a98eec741..f4a67c65d30 100644 --- a/drivers/mmc/host/s3cmci.c +++ b/drivers/mmc/host/s3cmci.c @@ -329,7 +329,7 @@ static void do_pio_write(struct s3cmci_host *host) to_ptr = host->base + host->sdidata; - while ((fifo = fifo_free(host))) { + while ((fifo = fifo_free(host)) > 3) { if (!host->pio_bytes) { res = get_data_buffer(host, &host->pio_bytes, &host->pio_ptr); diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index f07255cb17e..8cff5f5e7f8 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -144,8 +144,7 @@ static int jmicron_probe(struct sdhci_pci_chip *chip) SDHCI_QUIRK_32BIT_DMA_SIZE | SDHCI_QUIRK_32BIT_ADMA_SIZE | SDHCI_QUIRK_RESET_AFTER_REQUEST | - SDHCI_QUIRK_BROKEN_SMALL_PIO | - SDHCI_QUIRK_FORCE_HIGHSPEED; + SDHCI_QUIRK_BROKEN_SMALL_PIO; } /* diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 6b2d1f99af6..f52f3053ed9 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1636,8 +1636,7 @@ int sdhci_add_host(struct sdhci_host *host) mmc->f_max = host->max_clk; mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ; - if ((caps & SDHCI_CAN_DO_HISPD) || - (host->quirks & SDHCI_QUIRK_FORCE_HIGHSPEED)) + if (caps & SDHCI_CAN_DO_HISPD) mmc->caps |= MMC_CAP_SD_HIGHSPEED; mmc->ocr_avail = 0; @@ -1723,7 +1722,9 @@ int sdhci_add_host(struct sdhci_host *host) #endif #ifdef SDHCI_USE_LEDS_CLASS - host->led.name = mmc_hostname(mmc); + snprintf(host->led_name, sizeof(host->led_name), + "%s::", mmc_hostname(mmc)); + host->led.name = host->led_name; host->led.brightness = LED_OFF; host->led.default_trigger = mmc_hostname(mmc); host->led.brightness_set = sdhci_led_control; diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 3efba236394..ebb83657e27 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -208,8 +208,6 @@ struct sdhci_host { #define SDHCI_QUIRK_BROKEN_TIMEOUT_VAL (1<<12) /* Controller has an issue with buffer bits for small transfers */ #define SDHCI_QUIRK_BROKEN_SMALL_PIO (1<<13) -/* Controller supports high speed but doesn't have the caps bit set */ -#define SDHCI_QUIRK_FORCE_HIGHSPEED (1<<14) int irq; /* Device IRQ */ void __iomem * ioaddr; /* Mapped address */ @@ -222,6 +220,7 @@ struct sdhci_host { #if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) struct led_classdev led; /* LED control */ + char led_name[32]; #endif spinlock_t lock; /* Mutex */ diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index c98c1570a40..47a33cec379 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -139,7 +139,8 @@ static int atmel_nand_device_ready(struct mtd_info *mtd) struct nand_chip *nand_chip = mtd->priv; struct atmel_nand_host *host = nand_chip->priv; - return gpio_get_value(host->board->rdy_pin); + return gpio_get_value(host->board->rdy_pin) ^ + !!host->board->rdy_pin_active_low; } /* diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 6bdfd47d679..a2f185fd707 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -2342,6 +2342,17 @@ config ATL1E To compile this driver as a module, choose M here. The module will be called atl1e. +config ATL1C + tristate "Atheros L1C Gigabit Ethernet support (EXPERIMENTAL)" + depends on PCI && EXPERIMENTAL + select CRC32 + select MII + help + This driver supports the Atheros L1C gigabit ethernet adapter. + + To compile this driver as a module, choose M here. The module + will be called atl1c. + config JME tristate "JMicron(R) PCI-Express Gigabit Ethernet support" depends on PCI diff --git a/drivers/net/Makefile b/drivers/net/Makefile index a3c5c002f22..aca8492db65 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_BONDING) += bonding/ obj-$(CONFIG_ATL1) += atlx/ obj-$(CONFIG_ATL2) += atlx/ obj-$(CONFIG_ATL1E) += atl1e/ +obj-$(CONFIG_ATL1C) += atl1c/ obj-$(CONFIG_GIANFAR) += gianfar_driver.o obj-$(CONFIG_TEHUTI) += tehuti.o obj-$(CONFIG_ENIC) += enic/ diff --git a/drivers/net/atl1c/Makefile b/drivers/net/atl1c/Makefile new file mode 100644 index 00000000000..c37d966952e --- /dev/null +++ b/drivers/net/atl1c/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_ATL1C) += atl1c.o +atl1c-objs := atl1c_main.o atl1c_hw.o atl1c_ethtool.o diff --git a/drivers/net/atl1c/atl1c.h b/drivers/net/atl1c/atl1c.h new file mode 100644 index 00000000000..ac11b84b837 --- /dev/null +++ b/drivers/net/atl1c/atl1c.h @@ -0,0 +1,606 @@ +/* + * Copyright(c) 2008 - 2009 Atheros Corporation. All rights reserved. + * + * Derived from Intel e1000 driver + * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _ATL1C_H_ +#define _ATL1C_H_ + +#include <linux/version.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/ioport.h> +#include <linux/slab.h> +#include <linux/list.h> +#include <linux/delay.h> +#include <linux/sched.h> +#include <linux/in.h> +#include <linux/ip.h> +#include <linux/ipv6.h> +#include <linux/udp.h> +#include <linux/mii.h> +#include <linux/io.h> +#include <linux/vmalloc.h> +#include <linux/pagemap.h> +#include <linux/tcp.h> +#include <linux/mii.h> +#include <linux/ethtool.h> +#include <linux/if_vlan.h> +#include <linux/workqueue.h> +#include <net/checksum.h> +#include <net/ip6_checksum.h> + +#include "atl1c_hw.h" + +/* Wake Up Filter Control */ +#define AT_WUFC_LNKC 0x00000001 /* Link Status Change Wakeup Enable */ +#define AT_WUFC_MAG 0x00000002 /* Magic Packet Wakeup Enable */ +#define AT_WUFC_EX 0x00000004 /* Directed Exact Wakeup Enable */ +#define AT_WUFC_MC 0x00000008 /* Multicast Wakeup Enable */ +#define AT_WUFC_BC 0x00000010 /* Broadcast Wakeup Enable */ + +#define AT_VLAN_TO_TAG(_vlan, _tag) \ + _tag = ((((_vlan) >> 8) & 0xFF) |\ + (((_vlan) & 0xFF) << 8)) + +#define AT_TAG_TO_VLAN(_tag, _vlan) \ + _vlan = ((((_tag) >> 8) & 0xFF) |\ + (((_tag) & 0xFF) << 8)) + +#define SPEED_0 0xffff +#define HALF_DUPLEX 1 +#define FULL_DUPLEX 2 + +#define AT_RX_BUF_SIZE (ETH_FRAME_LEN + VLAN_HLEN + ETH_FCS_LEN) +#define MAX_JUMBO_FRAME_SIZE (9*1024) +#define MAX_TX_OFFLOAD_THRESH (9*1024) + +#define AT_MAX_RECEIVE_QUEUE 4 +#define AT_DEF_RECEIVE_QUEUE 1 +#define AT_MAX_TRANSMIT_QUEUE 2 + +#define AT_DMA_HI_ADDR_MASK 0xffffffff00000000ULL +#define AT_DMA_LO_ADDR_MASK 0x00000000ffffffffULL + +#define AT_TX_WATCHDOG (5 * HZ) +#define AT_MAX_INT_WORK 5 +#define AT_TWSI_EEPROM_TIMEOUT 100 +#define AT_HW_MAX_IDLE_DELAY 10 +#define AT_SUSPEND_LINK_TIMEOUT 28 + +#define AT_ASPM_L0S_TIMER 6 +#define AT_ASPM_L1_TIMER 12 + +#define ATL1C_PCIE_L0S_L1_DISABLE 0x01 +#define ATL1C_PCIE_PHY_RESET 0x02 + +#define ATL1C_ASPM_L0s_ENABLE 0x0001 +#define ATL1C_ASPM_L1_ENABLE 0x0002 + +#define AT_REGS_LEN (75 * sizeof(u32)) +#define AT_EEPROM_LEN 512 + +#define ATL1C_GET_DESC(R, i, type) (&(((type *)((R)->desc))[i])) +#define ATL1C_RFD_DESC(R, i) ATL1C_GET_DESC(R, i, struct atl1c_rx_free_desc) +#define ATL1C_TPD_DESC(R, i) ATL1C_GET_DESC(R, i, struct atl1c_tpd_desc) +#define ATL1C_RRD_DESC(R, i) ATL1C_GET_DESC(R, i, struct atl1c_recv_ret_status) + +/* tpd word 1 bit 0:7 General Checksum task offload */ +#define TPD_L4HDR_OFFSET_MASK 0x00FF +#define TPD_L4HDR_OFFSET_SHIFT 0 + +/* tpd word 1 bit 0:7 Large Send task offload (IPv4/IPV6) */ +#define TPD_TCPHDR_OFFSET_MASK 0x00FF +#define TPD_TCPHDR_OFFSET_SHIFT 0 + +/* tpd word 1 bit 0:7 Custom Checksum task offload */ +#define TPD_PLOADOFFSET_MASK 0x00FF +#define TPD_PLOADOFFSET_SHIFT 0 + +/* tpd word 1 bit 8:17 */ +#define TPD_CCSUM_EN_MASK 0x0001 +#define TPD_CCSUM_EN_SHIFT 8 +#define TPD_IP_CSUM_MASK 0x0001 +#define TPD_IP_CSUM_SHIFT 9 +#define TPD_TCP_CSUM_MASK 0x0001 +#define TPD_TCP_CSUM_SHIFT 10 +#define TPD_UDP_CSUM_MASK 0x0001 +#define TPD_UDP_CSUM_SHIFT 11 +#define TPD_LSO_EN_MASK 0x0001 /* TCP Large Send Offload */ +#define TPD_LSO_EN_SHIFT 12 +#define TPD_LSO_VER_MASK 0x0001 +#define TPD_LSO_VER_SHIFT 13 /* 0 : ipv4; 1 : ipv4/ipv6 */ +#define TPD_CON_VTAG_MASK 0x0001 +#define TPD_CON_VTAG_SHIFT 14 +#define TPD_INS_VTAG_MASK 0x0001 +#define TPD_INS_VTAG_SHIFT 15 +#define TPD_IPV4_PACKET_MASK 0x0001 /* valid when LSO VER is 1 */ +#define TPD_IPV4_PACKET_SHIFT 16 +#define TPD_ETH_TYPE_MASK 0x0001 +#define TPD_ETH_TYPE_SHIFT 17 /* 0 : 802.3 frame; 1 : Ethernet */ + +/* tpd word 18:25 Custom Checksum task offload */ +#define TPD_CCSUM_OFFSET_MASK 0x00FF +#define TPD_CCSUM_OFFSET_SHIFT 18 +#define TPD_CCSUM_EPAD_MASK 0x0001 +#define TPD_CCSUM_EPAD_SHIFT 30 + +/* tpd word 18:30 Large Send task offload (IPv4/IPV6) */ +#define TPD_MSS_MASK 0x1FFF +#define TPD_MSS_SHIFT 18 + +#define TPD_EOP_MASK 0x0001 +#define TPD_EOP_SHIFT 31 + +struct atl1c_tpd_desc { + __le16 buffer_len; /* include 4-byte CRC */ + __le16 vlan_tag; + __le32 word1; + __le64 buffer_addr; +}; + +struct atl1c_tpd_ext_desc { + u32 reservd_0; + __le32 word1; + __le32 pkt_len; + u32 reservd_1; +}; +/* rrs word 0 bit 0:31 */ +#define RRS_RX_CSUM_MASK 0xFFFF +#define RRS_RX_CSUM_SHIFT 0 +#define RRS_RX_RFD_CNT_MASK 0x000F +#define RRS_RX_RFD_CNT_SHIFT 16 +#define RRS_RX_RFD_INDEX_MASK 0x0FFF +#define RRS_RX_RFD_INDEX_SHIFT 20 + +/* rrs flag bit 0:16 */ +#define RRS_HEAD_LEN_MASK 0x00FF +#define RRS_HEAD_LEN_SHIFT 0 +#define RRS_HDS_TYPE_MASK 0x0003 +#define RRS_HDS_TYPE_SHIFT 8 +#define RRS_CPU_NUM_MASK 0x0003 +#define RRS_CPU_NUM_SHIFT 10 +#define RRS_HASH_FLG_MASK 0x000F +#define RRS_HASH_FLG_SHIFT 12 + +#define RRS_HDS_TYPE_HEAD 1 +#define RRS_HDS_TYPE_DATA 2 + +#define RRS_IS_NO_HDS_TYPE(flag) \ + (((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 == \ + RRS_HDS_TYPE_HEAD) + +#define RRS_IS_HDS_DATA(flag) \ + (((flag) >> (RRS_HDS_TYPE_SHIFT)) & RRS_HDS_TYPE_MASK == \ + RRS_HDS_TYPE_DATA) + +/* rrs word 3 bit 0:31 */ +#define RRS_PKT_SIZE_MASK 0x3FFF +#define RRS_PKT_SIZE_SHIFT 0 +#define RRS_ERR_L4_CSUM_MASK 0x0001 +#define RRS_ERR_L4_CSUM_SHIFT 14 +#define RRS_ERR_IP_CSUM_MASK 0x0001 +#define RRS_ERR_IP_CSUM_SHIFT 15 +#define RRS_VLAN_INS_MASK 0x0001 +#define RRS_VLAN_INS_SHIFT 16 +#define RRS_PROT_ID_MASK 0x0007 +#define RRS_PROT_ID_SHIFT 17 +#define RRS_RX_ERR_SUM_MASK 0x0001 +#define RRS_RX_ERR_SUM_SHIFT 20 +#define RRS_RX_ERR_CRC_MASK 0x0001 +#define RRS_RX_ERR_CRC_SHIFT 21 +#define RRS_RX_ERR_FAE_MASK 0x0001 +#define RRS_RX_ERR_FAE_SHIFT 22 +#define RRS_RX_ERR_TRUNC_MASK 0x0001 +#define RRS_RX_ERR_TRUNC_SHIFT 23 +#define RRS_RX_ERR_RUNC_MASK 0x0001 +#define RRS_RX_ERR_RUNC_SHIFT 24 +#define RRS_RX_ERR_ICMP_MASK 0x0001 +#define RRS_RX_ERR_ICMP_SHIFT 25 +#define RRS_PACKET_BCAST_MASK 0x0001 +#define RRS_PACKET_BCAST_SHIFT 26 +#define RRS_PACKET_MCAST_MASK 0x0001 +#define RRS_PACKET_MCAST_SHIFT 27 +#define RRS_PACKET_TYPE_MASK 0x0001 +#define RRS_PACKET_TYPE_SHIFT 28 +#define RRS_FIFO_FULL_MASK 0x0001 +#define RRS_FIFO_FULL_SHIFT 29 +#define RRS_802_3_LEN_ERR_MASK 0x0001 +#define RRS_802_3_LEN_ERR_SHIFT 30 +#define RRS_RXD_UPDATED_MASK 0x0001 +#define RRS_RXD_UPDATED_SHIFT 31 + +#define RRS_ERR_L4_CSUM 0x00004000 +#define RRS_ERR_IP_CSUM 0x00008000 +#define RRS_VLAN_INS 0x00010000 +#define RRS_RX_ERR_SUM 0x00100000 +#define RRS_RX_ERR_CRC 0x00200000 +#define RRS_802_3_LEN_ERR 0x40000000 +#define RRS_RXD_UPDATED 0x80000000 + +#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 == \ + RRS_PACKET_TYPE_ETH) +#define RRS_RXD_IS_VALID(word) \ + ((((word) >> RRS_RXD_UPDATED_SHIFT) & RRS_RXD_UPDATED_MASK) == 1) + +#define RRS_PACKET_PROT_IS_IPV4_ONLY(word) \ + ((((word) >> RRS_PROT_ID_SHIFT) & RRS_PROT_ID_MASK) == 1) +#define RRS_PACKET_PROT_IS_IPV6_ONLY(word) \ + ((((word) >> RRS_PROT_ID_SHIFT) & RRS_PROT_ID_MASK) == 6) + +struct atl1c_recv_ret_status { + __le32 word0; + __le32 rss_hash; + __le16 vlan_tag; + __le16 flag; + __le32 word3; +}; + +/* RFD desciptor */ +struct atl1c_rx_free_desc { + __le64 buffer_addr; +}; + +/* DMA Order Settings */ +enum atl1c_dma_order { + atl1c_dma_ord_in = 1, + atl1c_dma_ord_enh = 2, + atl1c_dma_ord_out = 4 +}; + +enum atl1c_dma_rcb { + atl1c_rcb_64 = 0, + atl1c_rcb_128 = 1 +}; + +enum atl1c_mac_speed { + atl1c_mac_speed_0 = 0, + atl1c_mac_speed_10_100 = 1, + atl1c_mac_speed_1000 = 2 +}; + +enum atl1c_dma_req_block { + atl1c_dma_req_128 = 0, + atl1c_dma_req_256 = 1, + atl1c_dma_req_512 = 2, + atl1c_dma_req_1024 = 3, + atl1c_dma_req_2048 = 4, + atl1c_dma_req_4096 = 5 +}; + +enum atl1c_rss_mode { + atl1c_rss_mode_disable = 0, + atl1c_rss_sig_que = 1, + atl1c_rss_mul_que_sig_int = 2, + atl1c_rss_mul_que_mul_int = 4, +}; + +enum atl1c_rss_type { + atl1c_rss_disable = 0, + atl1c_rss_ipv4 = 1, + atl1c_rss_ipv4_tcp = 2, + atl1c_rss_ipv6 = 4, + atl1c_rss_ipv6_tcp = 8 +}; + +enum atl1c_nic_type { + athr_l1c = 0, + athr_l2c = 1, +}; + +enum atl1c_trans_queue { + atl1c_trans_normal = 0, + atl1c_trans_high = 1 +}; + +struct atl1c_hw_stats { + /* rx */ + unsigned long rx_ok; /* The number of good packet received. */ + unsigned long rx_bcast; /* The number of good broadcast packet received. */ + unsigned long rx_mcast; /* The number of good multicast packet received. */ + unsigned long rx_pause; /* The number of Pause packet received. */ + unsigned long rx_ctrl; /* The number of Control packet received other than Pause frame. */ + unsigned long rx_fcs_err; /* The number of packets with bad FCS. */ + unsigned long rx_len_err; /* The number of packets with mismatch of length field and actual size. */ + unsigned long rx_byte_cnt; /* The number of bytes of good packet received. FCS is NOT included. */ + unsigned long rx_runt; /* The number of packets received that are less than 64 byte long and with good FCS. */ + unsigned long rx_frag; /* The number of packets received that are less than 64 byte long and with bad FCS. */ + unsigned long rx_sz_64; /* The number of good and bad packets received that are 64 byte long. */ + unsigned long rx_sz_65_127; /* The number of good and bad packets received that are between 65 and 127-byte long. */ + unsigned long rx_sz_128_255; /* The number of good and bad packets received that are between 128 and 255-byte long. */ + unsigned long rx_sz_256_511; /* The number of good and bad packets received that are between 256 and 511-byte long. */ + unsigned long rx_sz_512_1023; /* The number of good and bad packets received that are between 512 and 1023-byte long. */ + unsigned long rx_sz_1024_1518; /* The number of good and bad packets received that are between 1024 and 1518-byte long. */ + unsigned long rx_sz_1519_max; /* The number of good and bad packets received that are between 1519-byte and MTU. */ + unsigned long rx_sz_ov; /* The number of good and bad packets received that are more than MTU size truncated by Selene. */ + unsigned long rx_rxf_ov; /* The number of frame dropped due to occurrence of RX FIFO overflow. */ + unsigned long rx_rrd_ov; /* The number of frame dropped due to occurrence of RRD overflow. */ + unsigned long rx_align_err; /* Alignment Error */ + unsigned long rx_bcast_byte_cnt; /* The byte count of broadcast packet received, excluding FCS. */ + unsigned long rx_mcast_byte_cnt; /* The byte count of multicast packet received, excluding FCS. */ + unsigned long rx_err_addr; /* The number of packets dropped due to address filtering. */ + + /* tx */ + unsigned long tx_ok; /* The number of good packet transmitted. */ + unsigned long tx_bcast; /* The number of good broadcast packet transmitted. */ + unsigned long tx_mcast; /* The number of good multicast packet transmitted. */ + unsigned long tx_pause; /* The number of Pause packet transmitted. */ + unsigned long tx_exc_defer; /* The number of packets transmitted with excessive deferral. */ + unsigned long tx_ctrl; /* The number of packets transmitted is a control frame, excluding Pause frame. */ + unsigned long tx_defer; /* The number of packets transmitted that is deferred. */ + unsigned long tx_byte_cnt; /* The number of bytes of data transmitted. FCS is NOT included. */ + unsigned long tx_sz_64; /* The number of good and bad packets transmitted that are 64 byte long. */ + unsigned long tx_sz_65_127; /* The number of good and bad packets transmitted that are between 65 and 127-byte long. */ + unsigned long tx_sz_128_255; /* The number of good and bad packets transmitted that are between 128 and 255-byte long. */ + unsigned long tx_sz_256_511; /* The number of good and bad packets transmitted that are between 256 and 511-byte long. */ + unsigned long tx_sz_512_1023; /* The number of good and bad packets transmitted that are between 512 and 1023-byte long. */ + unsigned long tx_sz_1024_1518; /* The number of good and bad packets transmitted that are between 1024 and 1518-byte long. */ + unsigned long tx_sz_1519_max; /* The number of good and bad packets transmitted that are between 1519-byte and MTU. */ + unsigned long tx_1_col; /* The number of packets subsequently transmitted successfully with a single prior collision. */ + unsigned long tx_2_col; /* The number of packets subsequently transmitted successfully with multiple prior collisions. */ + unsigned long tx_late_col; /* The number of packets transmitted with late collisions. */ + unsigned long tx_abort_col; /* The number of transmit packets aborted due to excessive collisions. */ + unsigned long tx_underrun; /* The number of transmit packets aborted due to transmit FIFO underrun, or TRD FIFO underrun */ + unsigned long tx_rd_eop; /* The number of times that read beyond the EOP into the next frame area when TRD was not written timely */ + unsigned long tx_len_err; /* The number of transmit packets with length field does NOT match the actual frame size. */ + unsigned long tx_trunc; /* The number of transmit packets truncated due to size exceeding MTU. */ + unsigned long tx_bcast_byte; /* The byte count of broadcast packet transmitted, excluding FCS. */ + unsigned long tx_mcast_byte; /* The byte count of multicast packet transmitted, excluding FCS. */ +}; + +struct atl1c_hw { + u8 __iomem *hw_addr; /* inner register address */ + struct atl1c_adapter *adapter; + enum atl1c_nic_type nic_type; + enum atl1c_dma_order dma_order; + enum atl1c_dma_rcb rcb_value; + enum atl1c_dma_req_block dmar_block; + enum atl1c_dma_req_block dmaw_block; + + u16 device_id; + u16 vendor_id; + u16 subsystem_id; + u16 subsystem_vendor_id; + u8 revision_id; + + u32 intr_mask; + u8 dmaw_dly_cnt; + u8 dmar_dly_cnt; + + u8 preamble_len; + u16 max_frame_size; + u16 min_frame_size; + + enum atl1c_mac_speed mac_speed; + bool mac_duplex; + bool hibernate; + u16 media_type; +#define MEDIA_TYPE_AUTO_SENSOR 0 +#define MEDIA_TYPE_100M_FULL 1 +#define MEDIA_TYPE_100M_HALF 2 +#define MEDIA_TYPE_10M_FULL 3 +#define MEDIA_TYPE_10M_HALF 4 + + u16 autoneg_advertised; + u16 mii_autoneg_adv_reg; + u16 mii_1000t_ctrl_reg; + + u16 tx_imt; /* TX Interrupt Moderator timer ( 2us resolution) */ + u16 rx_imt; /* RX Interrupt Moderator timer ( 2us resolution) */ + u16 ict; /* Interrupt Clear timer (2us resolution) */ + u16 ctrl_flags; +#define ATL1C_INTR_CLEAR_ON_READ 0x0001 +#define ATL1C_INTR_MODRT_ENABLE 0x0002 +#define ATL1C_CMB_ENABLE 0x0004 +#define ATL1C_SMB_ENABLE 0x0010 +#define ATL1C_TXQ_MODE_ENHANCE 0x0020 +#define ATL1C_RX_IPV6_CHKSUM 0x0040 +#define ATL1C_ASPM_L0S_SUPPORT 0x0080 +#define ATL1C_ASPM_L1_SUPPORT 0x0100 +#define ATL1C_ASPM_CTRL_MON 0x0200 +#define ATL1C_HIB_DISABLE 0x0400 +#define ATL1C_LINK_CAP_1000M 0x0800 +#define ATL1C_FPGA_VERSION 0x8000 + u16 cmb_tpd; + u16 cmb_rrd; + u16 cmb_rx_timer; /* 2us resolution */ + u16 cmb_tx_timer; + u32 smb_timer; + + u16 rrd_thresh; /* Threshold of number of RRD produced to trigger + interrupt request */ + u16 tpd_thresh; + u8 tpd_burst; /* Number of TPD to prefetch in cache-aligned burst. */ + u8 rfd_burst; + enum atl1c_rss_type rss_type; + enum atl1c_rss_mode rss_mode; + u8 rss_hash_bits; + u32 base_cpu; + u32 indirect_tab; + u8 mac_addr[ETH_ALEN]; + u8 perm_mac_addr[ETH_ALEN]; + + bool phy_configured; + bool re_autoneg; + bool emi_ca; +}; + +/* + * atl1c_ring_header represents a single, contiguous block of DMA space + * mapped for the three descriptor rings (tpd, rfd, rrd) and the two + * message blocks (cmb, smb) described below + */ +struct atl1c_ring_header { + void *desc; /* virtual address */ + dma_addr_t dma; /* physical address*/ + unsigned int size; /* length in bytes */ +}; + +/* + * atl1c_buffer is wrapper around a pointer to a socket buffer + * so a DMA handle can be stored along with the skb + */ +struct atl1c_buffer { + struct sk_buff *skb; /* socket buffer */ + u16 length; /* rx buffer length */ + u16 state; /* state of buffer */ +#define ATL1_BUFFER_FREE 0 +#define ATL1_BUFFER_BUSY 1 + dma_addr_t dma; +}; + +/* transimit packet descriptor (tpd) ring */ +struct atl1c_tpd_ring { + void *desc; /* descriptor ring virtual address */ + dma_addr_t dma; /* descriptor ring physical address */ + u16 size; /* descriptor ring length in bytes */ + u16 count; /* number of descriptors in the ring */ + u16 next_to_use; /* this is protectd by adapter->tx_lock */ + atomic_t next_to_clean; + struct atl1c_buffer *buffer_info; +}; + +/* receive free descriptor (rfd) ring */ +struct atl1c_rfd_ring { + void *desc; /* descriptor ring virtual address */ + dma_addr_t dma; /* descriptor ring physical address */ + u16 size; /* descriptor ring length in bytes */ + u16 count; /* number of descriptors in the ring */ + u16 next_to_use; + u16 next_to_clean; + struct atl1c_buffer *buffer_info; +}; + +/* receive return desciptor (rrd) ring */ +struct atl1c_rrd_ring { + void *desc; /* descriptor ring virtual address */ + dma_addr_t dma; /* descriptor ring physical address */ + u16 size; /* descriptor ring length in bytes */ + u16 count; /* number of descriptors in the ring */ + u16 next_to_use; + u16 next_to_clean; +}; + +struct atl1c_cmb { + void *cmb; + dma_addr_t dma; +}; + +struct atl1c_smb { + void *smb; + dma_addr_t dma; +}; + +/* board specific private data structure */ +struct atl1c_adapter { + struct net_device *netdev; + struct pci_dev *pdev; + struct vlan_group *vlgrp; + struct napi_struct napi; + struct atl1c_hw hw; + struct atl1c_hw_stats hw_stats; + struct net_device_stats net_stats; + struct mii_if_info mii; /* MII interface info */ + u16 rx_buffer_len; + + unsigned long flags; +#define __AT_TESTING 0x0001 +#define __AT_RESETTING 0x0002 +#define __AT_DOWN 0x0003 + u32 msg_enable; + + bool have_msi; + u32 wol; + u16 link_speed; + u16 link_duplex; + + spinlock_t mdio_lock; + spinlock_t tx_lock; + atomic_t irq_sem; + + struct work_struct reset_task; + struct work_struct link_chg_task; + struct timer_list watchdog_timer; + struct timer_list phy_config_timer; + + /* All Descriptor memory */ + struct atl1c_ring_header ring_header; + struct atl1c_tpd_ring tpd_ring[AT_MAX_TRANSMIT_QUEUE]; + struct atl1c_rfd_ring rfd_ring[AT_MAX_RECEIVE_QUEUE]; + struct atl1c_rrd_ring rrd_ring[AT_MAX_RECEIVE_QUEUE]; + struct atl1c_cmb cmb; + struct atl1c_smb smb; + int num_rx_queues; + u32 bd_number; /* board number;*/ +}; + +#define AT_WRITE_REG(a, reg, value) ( \ + writel((value), ((a)->hw_addr + reg))) + +#define AT_WRITE_FLUSH(a) (\ + readl((a)->hw_addr)) + +#define AT_READ_REG(a, reg, pdata) do { \ + if (unlikely((a)->hibernate)) { \ + readl((a)->hw_addr + reg); \ + *(u32 *)pdata = readl((a)->hw_addr + reg); \ + } else { \ + *(u32 *)pdata = readl((a)->hw_addr + reg); \ + } \ + } while (0) + +#define AT_WRITE_REGB(a, reg, value) (\ + writeb((value), ((a)->hw_addr + reg))) + +#define AT_READ_REGB(a, reg) (\ + readb((a)->hw_addr + reg)) + +#define AT_WRITE_REGW(a, reg, value) (\ + writew((value), ((a)->hw_addr + reg))) + +#define AT_READ_REGW(a, reg) (\ + readw((a)->hw_addr + reg)) + +#define AT_WRITE_REG_ARRAY(a, reg, offset, value) ( \ + writel((value), (((a)->hw_addr + reg) + ((offset) << 2)))) + +#define AT_READ_REG_ARRAY(a, reg, offset) ( \ + readl(((a)->hw_addr + reg) + ((offset) << 2))) + +extern char atl1c_driver_name[]; +extern char atl1c_driver_version[]; + +extern int atl1c_up(struct atl1c_adapter *adapter); +extern void atl1c_down(struct atl1c_adapter *adapter); +extern void atl1c_reinit_locked(struct atl1c_adapter *adapter); +extern s32 atl1c_reset_hw(struct atl1c_hw *hw); +extern void atl1c_set_ethtool_ops(struct net_device *netdev); +#endif /* _ATL1C_H_ */ diff --git a/drivers/net/atl1c/atl1c_ethtool.c b/drivers/net/atl1c/atl1c_ethtool.c new file mode 100644 index 00000000000..45c5b7332cd --- /dev/null +++ b/drivers/net/atl1c/atl1c_ethtool.c @@ -0,0 +1,317 @@ +/* + * Copyright(c) 2009 - 2009 Atheros Corporation. All rights reserved. + * + * Derived from Intel e1000 driver + * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include <linux/netdevice.h> +#include <linux/ethtool.h> + +#include "atl1c.h" + +static int atl1c_get_settings(struct net_device *netdev, + struct ethtool_cmd *ecmd) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + struct atl1c_hw *hw = &adapter->hw; + + ecmd->supported = (SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_Autoneg | + SUPPORTED_TP); + if (hw->ctrl_flags & ATL1C_LINK_CAP_1000M) + ecmd->supported |= SUPPORTED_1000baseT_Full; + + ecmd->advertising = ADVERTISED_TP; + + ecmd->advertising |= hw->autoneg_advertised; + + ecmd->port = PORT_TP; + ecmd->phy_address = 0; + ecmd->transceiver = XCVR_INTERNAL; + + if (adapter->link_speed != SPEED_0) { + ecmd->speed = adapter->link_speed; + if (adapter->link_duplex == FULL_DUPLEX) + ecmd->duplex = DUPLEX_FULL; + else + ecmd->duplex = DUPLEX_HALF; + } else { + ecmd->speed = -1; + ecmd->duplex = -1; + } + + ecmd->autoneg = AUTONEG_ENABLE; + return 0; +} + +static int atl1c_set_settings(struct net_device *netdev, + struct ethtool_cmd *ecmd) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + struct atl1c_hw *hw = &adapter->hw; + u16 autoneg_advertised; + + while (test_and_set_bit(__AT_RESETTING, &adapter->flags)) + msleep(1); + + if (ecmd->autoneg == AUTONEG_ENABLE) { + autoneg_advertised = ADVERTISED_Autoneg; + } else { + if (ecmd->speed == SPEED_1000) { + if (ecmd->duplex != DUPLEX_FULL) { + if (netif_msg_link(adapter)) + dev_warn(&adapter->pdev->dev, + "1000M half is invalid\n"); + clear_bit(__AT_RESETTING, &adapter->flags); + return -EINVAL; + } + autoneg_advertised = ADVERTISED_1000baseT_Full; + } else if (ecmd->speed == SPEED_100) { + if (ecmd->duplex == DUPLEX_FULL) + autoneg_advertised = ADVERTISED_100baseT_Full; + else + autoneg_advertised = ADVERTISED_100baseT_Half; + } else { + if (ecmd->duplex == DUPLEX_FULL) + autoneg_advertised = ADVERTISED_10baseT_Full; + else + autoneg_advertised = ADVERTISED_10baseT_Half; + } + } + + if (hw->autoneg_advertised != autoneg_advertised) { + hw->autoneg_advertised = autoneg_advertised; + if (atl1c_restart_autoneg(hw) != 0) { + if (netif_msg_link(adapter)) + dev_warn(&adapter->pdev->dev, + "ethtool speed/duplex setting failed\n"); + clear_bit(__AT_RESETTING, &adapter->flags); + return -EINVAL; + } + } + clear_bit(__AT_RESETTING, &adapter->flags); + return 0; +} + +static u32 atl1c_get_tx_csum(struct net_device *netdev) +{ + return (netdev->features & NETIF_F_HW_CSUM) != 0; +} + +static u32 atl1c_get_msglevel(struct net_device *netdev) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + return adapter->msg_enable; +} + +static void atl1c_set_msglevel(struct net_device *netdev, u32 data) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + adapter->msg_enable = data; +} + +static int atl1c_get_regs_len(struct net_device *netdev) +{ + return AT_REGS_LEN; +} + +static void atl1c_get_regs(struct net_device *netdev, + struct ethtool_regs *regs, void *p) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + struct atl1c_hw *hw = &adapter->hw; + u32 *regs_buff = p; + u16 phy_data; + + memset(p, 0, AT_REGS_LEN); + + regs->version = 0; + AT_READ_REG(hw, REG_VPD_CAP, p++); + AT_READ_REG(hw, REG_PM_CTRL, p++); + AT_READ_REG(hw, REG_MAC_HALF_DUPLX_CTRL, p++); + AT_READ_REG(hw, REG_TWSI_CTRL, p++); + AT_READ_REG(hw, REG_PCIE_DEV_MISC_CTRL, p++); + AT_READ_REG(hw, REG_MASTER_CTRL, p++); + AT_READ_REG(hw, REG_MANUAL_TIMER_INIT, p++); + AT_READ_REG(hw, REG_IRQ_MODRT_TIMER_INIT, p++); + AT_READ_REG(hw, REG_GPHY_CTRL, p++); + AT_READ_REG(hw, REG_LINK_CTRL, p++); + AT_READ_REG(hw, REG_IDLE_STATUS, p++); + AT_READ_REG(hw, REG_MDIO_CTRL, p++); + AT_READ_REG(hw, REG_SERDES_LOCK, p++); + AT_READ_REG(hw, REG_MAC_CTRL, p++); + AT_READ_REG(hw, REG_MAC_IPG_IFG, p++); + AT_READ_REG(hw, REG_MAC_STA_ADDR, p++); + AT_READ_REG(hw, REG_MAC_STA_ADDR+4, p++); + AT_READ_REG(hw, REG_RX_HASH_TABLE, p++); + AT_READ_REG(hw, REG_RX_HASH_TABLE+4, p++); + AT_READ_REG(hw, REG_RXQ_CTRL, p++); + AT_READ_REG(hw, REG_TXQ_CTRL, p++); + AT_READ_REG(hw, REG_MTU, p++); + AT_READ_REG(hw, REG_WOL_CTRL, p++); + + atl1c_read_phy_reg(hw, MII_BMCR, &phy_data); + regs_buff[73] = (u32) phy_data; + atl1c_read_phy_reg(hw, MII_BMSR, &phy_data); + regs_buff[74] = (u32) phy_data; +} + +static int atl1c_get_eeprom_len(struct net_device *netdev) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + + if (atl1c_check_eeprom_exist(&adapter->hw)) + return AT_EEPROM_LEN; + else + return 0; +} + +static int atl1c_get_eeprom(struct net_device *netdev, + struct ethtool_eeprom *eeprom, u8 *bytes) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + struct atl1c_hw *hw = &adapter->hw; + u32 *eeprom_buff; + int first_dword, last_dword; + int ret_val = 0; + int i; + + if (eeprom->len == 0) + return -EINVAL; + + if (!atl1c_check_eeprom_exist(hw)) /* not exist */ + return -EINVAL; + + eeprom->magic = adapter->pdev->vendor | + (adapter->pdev->device << 16); + + first_dword = eeprom->offset >> 2; + last_dword = (eeprom->offset + eeprom->len - 1) >> 2; + + eeprom_buff = kmalloc(sizeof(u32) * + (last_dword - first_dword + 1), GFP_KERNEL); + if (eeprom_buff == NULL) + return -ENOMEM; + + for (i = first_dword; i < last_dword; i++) { + if (!atl1c_read_eeprom(hw, i * 4, &(eeprom_buff[i-first_dword]))) { + kfree(eeprom_buff); + return -EIO; + } + } + + memcpy(bytes, (u8 *)eeprom_buff + (eeprom->offset & 3), + eeprom->len); + kfree(eeprom_buff); + + return ret_val; + return 0; +} + +static void atl1c_get_drvinfo(struct net_device *netdev, + struct ethtool_drvinfo *drvinfo) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + + strncpy(drvinfo->driver, atl1c_driver_name, sizeof(drvinfo->driver)); + strncpy(drvinfo->version, atl1c_driver_version, + sizeof(drvinfo->version)); + strncpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version)); + strncpy(drvinfo->bus_info, pci_name(adapter->pdev), + sizeof(drvinfo->bus_info)); + drvinfo->n_stats = 0; + drvinfo->testinfo_len = 0; + drvinfo->regdump_len = atl1c_get_regs_len(netdev); + drvinfo->eedump_len = atl1c_get_eeprom_len(netdev); +} + +static void atl1c_get_wol(struct net_device *netdev, + struct ethtool_wolinfo *wol) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + + wol->supported = WAKE_MAGIC | WAKE_PHY; + wol->wolopts = 0; + + if (adapter->wol & AT_WUFC_EX) + wol->wolopts |= WAKE_UCAST; + if (adapter->wol & AT_WUFC_MC) + wol->wolopts |= WAKE_MCAST; + if (adapter->wol & AT_WUFC_BC) + wol->wolopts |= WAKE_BCAST; + if (adapter->wol & AT_WUFC_MAG) + wol->wolopts |= WAKE_MAGIC; + if (adapter->wol & AT_WUFC_LNKC) + wol->wolopts |= WAKE_PHY; + + return; +} + +static int atl1c_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + + if (wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE | + WAKE_MCAST | WAKE_BCAST | WAKE_MCAST)) + return -EOPNOTSUPP; + /* these settings will always override what we currently have */ + adapter->wol = 0; + + if (wol->wolopts & WAKE_MAGIC) + adapter->wol |= AT_WUFC_MAG; + if (wol->wolopts & WAKE_PHY) + adapter->wol |= AT_WUFC_LNKC; + + return 0; +} + +static int atl1c_nway_reset(struct net_device *netdev) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + if (netif_running(netdev)) + atl1c_reinit_locked(adapter); + return 0; +} + +static struct ethtool_ops atl1c_ethtool_ops = { + .get_settings = atl1c_get_settings, + .set_settings = atl1c_set_settings, + .get_drvinfo = atl1c_get_drvinfo, + .get_regs_len = atl1c_get_regs_len, + .get_regs = atl1c_get_regs, + .get_wol = atl1c_get_wol, + .set_wol = atl1c_set_wol, + .get_msglevel = atl1c_get_msglevel, + .set_msglevel = atl1c_set_msglevel, + .nway_reset = atl1c_nway_reset, + .get_link = ethtool_op_get_link, + .get_eeprom_len = atl1c_get_eeprom_len, + .get_eeprom = atl1c_get_eeprom, + .get_tx_csum = atl1c_get_tx_csum, + .get_sg = ethtool_op_get_sg, + .set_sg = ethtool_op_set_sg, +}; + +void atl1c_set_ethtool_ops(struct net_device *netdev) +{ + SET_ETHTOOL_OPS(netdev, &atl1c_ethtool_ops); +} diff --git a/drivers/net/atl1c/atl1c_hw.c b/drivers/net/atl1c/atl1c_hw.c new file mode 100644 index 00000000000..3e69b940b8f --- /dev/null +++ b/drivers/net/atl1c/atl1c_hw.c @@ -0,0 +1,527 @@ +/* + * Copyright(c) 2007 Atheros Corporation. All rights reserved. + * + * Derived from Intel e1000 driver + * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/mii.h> +#include <linux/crc32.h> + +#include "atl1c.h" + +/* + * check_eeprom_exist + * return 1 if eeprom exist + */ +int atl1c_check_eeprom_exist(struct atl1c_hw *hw) +{ + u32 data; + + AT_READ_REG(hw, REG_TWSI_DEBUG, &data); + if (data & TWSI_DEBUG_DEV_EXIST) + return 1; + + return 0; +} + +void atl1c_hw_set_mac_addr(struct atl1c_hw *hw) +{ + u32 value; + /* + * 00-0B-6A-F6-00-DC + * 0: 6AF600DC 1: 000B + * low dword + */ + value = (((u32)hw->mac_addr[2]) << 24) | + (((u32)hw->mac_addr[3]) << 16) | + (((u32)hw->mac_addr[4]) << 8) | + (((u32)hw->mac_addr[5])) ; + AT_WRITE_REG_ARRAY(hw, REG_MAC_STA_ADDR, 0, value); + /* hight dword */ + value = (((u32)hw->mac_addr[0]) << 8) | + (((u32)hw->mac_addr[1])) ; + AT_WRITE_REG_ARRAY(hw, REG_MAC_STA_ADDR, 1, value); +} + +/* + * atl1c_get_permanent_address + * return 0 if get valid mac address, + */ +static int atl1c_get_permanent_address(struct atl1c_hw *hw) +{ + u32 addr[2]; + u32 i; + u32 otp_ctrl_data; + u32 twsi_ctrl_data; + u8 eth_addr[ETH_ALEN]; + + /* init */ + addr[0] = addr[1] = 0; + AT_READ_REG(hw, REG_OTP_CTRL, &otp_ctrl_data); + if (atl1c_check_eeprom_exist(hw)) { + /* Enable OTP CLK */ + if (!(otp_ctrl_data & OTP_CTRL_CLK_EN)) { + otp_ctrl_data |= OTP_CTRL_CLK_EN; + AT_WRITE_REG(hw, REG_OTP_CTRL, otp_ctrl_data); + AT_WRITE_FLUSH(hw); + msleep(1); + } + + AT_READ_REG(hw, REG_TWSI_CTRL, &twsi_ctrl_data); + twsi_ctrl_data |= TWSI_CTRL_SW_LDSTART; + AT_WRITE_REG(hw, REG_TWSI_CTRL, twsi_ctrl_data); + for (i = 0; i < AT_TWSI_EEPROM_TIMEOUT; i++) { + msleep(10); + AT_READ_REG(hw, REG_TWSI_CTRL, &twsi_ctrl_data); + if ((twsi_ctrl_data & TWSI_CTRL_SW_LDSTART) == 0) + break; + } + if (i >= AT_TWSI_EEPROM_TIMEOUT) + return -1; + } + /* Disable OTP_CLK */ + if (otp_ctrl_data & OTP_CTRL_CLK_EN) { + otp_ctrl_data &= ~OTP_CTRL_CLK_EN; + AT_WRITE_REG(hw, REG_OTP_CTRL, otp_ctrl_data); + AT_WRITE_FLUSH(hw); + msleep(1); + } + + /* maybe MAC-address is from BIOS */ + AT_READ_REG(hw, REG_MAC_STA_ADDR, &addr[0]); + AT_READ_REG(hw, REG_MAC_STA_ADDR + 4, &addr[1]); + *(u32 *) ð_addr[2] = swab32(addr[0]); + *(u16 *) ð_addr[0] = swab16(*(u16 *)&addr[1]); + + if (is_valid_ether_addr(eth_addr)) { + memcpy(hw->perm_mac_addr, eth_addr, ETH_ALEN); + return 0; + } + + return -1; +} + +bool atl1c_read_eeprom(struct atl1c_hw *hw, u32 offset, u32 *p_value) +{ + int i; + int ret = false; + u32 otp_ctrl_data; + u32 control; + u32 data; + + if (offset & 3) + return ret; /* address do not align */ + + AT_READ_REG(hw, REG_OTP_CTRL, &otp_ctrl_data); + if (!(otp_ctrl_data & OTP_CTRL_CLK_EN)) + AT_WRITE_REG(hw, REG_OTP_CTRL, + (otp_ctrl_data | OTP_CTRL_CLK_EN)); + + AT_WRITE_REG(hw, REG_EEPROM_DATA_LO, 0); + control = (offset & EEPROM_CTRL_ADDR_MASK) << EEPROM_CTRL_ADDR_SHIFT; + AT_WRITE_REG(hw, REG_EEPROM_CTRL, control); + + for (i = 0; i < 10; i++) { + udelay(100); + AT_READ_REG(hw, REG_EEPROM_CTRL, &control); + if (control & EEPROM_CTRL_RW) + break; + } + if (control & EEPROM_CTRL_RW) { + AT_READ_REG(hw, REG_EEPROM_CTRL, &data); + AT_READ_REG(hw, REG_EEPROM_DATA_LO, p_value); + data = data & 0xFFFF; + *p_value = swab32((data << 16) | (*p_value >> 16)); + ret = true; + } + if (!(otp_ctrl_data & OTP_CTRL_CLK_EN)) + AT_WRITE_REG(hw, REG_OTP_CTRL, otp_ctrl_data); + + return ret; +} +/* + * Reads the adapter's MAC address from the EEPROM + * + * hw - Struct containing variables accessed by shared code + */ +int atl1c_read_mac_addr(struct atl1c_hw *hw) +{ + int err = 0; + + err = atl1c_get_permanent_address(hw); + if (err) + random_ether_addr(hw->perm_mac_addr); + + memcpy(hw->mac_addr, hw->perm_mac_addr, sizeof(hw->perm_mac_addr)); + return 0; +} + +/* + * atl1c_hash_mc_addr + * purpose + * set hash value for a multicast address + * hash calcu processing : + * 1. calcu 32bit CRC for multicast address + * 2. reverse crc with MSB to LSB + */ +u32 atl1c_hash_mc_addr(struct atl1c_hw *hw, u8 *mc_addr) +{ + u32 crc32; + u32 value = 0; + int i; + + crc32 = ether_crc_le(6, mc_addr); + for (i = 0; i < 32; i++) + value |= (((crc32 >> i) & 1) << (31 - i)); + + return value; +} + +/* + * Sets the bit in the multicast table corresponding to the hash value. + * hw - Struct containing variables accessed by shared code + * hash_value - Multicast address hash value + */ +void atl1c_hash_set(struct atl1c_hw *hw, u32 hash_value) +{ + u32 hash_bit, hash_reg; + u32 mta; + + /* + * The HASH Table is a register array of 2 32-bit registers. + * It is treated like an array of 64 bits. We want to set + * bit BitArray[hash_value]. So we figure out what register + * the bit is in, read it, OR in the new bit, then write + * back the new value. The register is determined by the + * upper bit of the hash value and the bit within that + * register are determined by the lower 5 bits of the value. + */ + hash_reg = (hash_value >> 31) & 0x1; + hash_bit = (hash_value >> 26) & 0x1F; + + mta = AT_READ_REG_ARRAY(hw, REG_RX_HASH_TABLE, hash_reg); + + mta |= (1 << hash_bit); + + AT_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, hash_reg, mta); +} + +/* + * Reads the value from a PHY register + * hw - Struct containing variables accessed by shared code + * reg_addr - address of the PHY register to read + */ +int atl1c_read_phy_reg(struct atl1c_hw *hw, u16 reg_addr, u16 *phy_data) +{ + u32 val; + int i; + + val = ((u32)(reg_addr & MDIO_REG_ADDR_MASK)) << MDIO_REG_ADDR_SHIFT | + MDIO_START | MDIO_SUP_PREAMBLE | MDIO_RW | + MDIO_CLK_25_4 << MDIO_CLK_SEL_SHIFT; + + AT_WRITE_REG(hw, REG_MDIO_CTRL, val); + + for (i = 0; i < MDIO_WAIT_TIMES; i++) { + udelay(2); + AT_READ_REG(hw, REG_MDIO_CTRL, &val); + if (!(val & (MDIO_START | MDIO_BUSY))) + break; + } + if (!(val & (MDIO_START | MDIO_BUSY))) { + *phy_data = (u16)val; + return 0; + } + + return -1; +} + +/* + * Writes a value to a PHY register + * hw - Struct containing variables accessed by shared code + * reg_addr - address of the PHY register to write + * data - data to write to the PHY + */ +int atl1c_write_phy_reg(struct atl1c_hw *hw, u32 reg_addr, u16 phy_data) +{ + int i; + u32 val; + + val = ((u32)(phy_data & MDIO_DATA_MASK)) << MDIO_DATA_SHIFT | + (reg_addr & MDIO_REG_ADDR_MASK) << MDIO_REG_ADDR_SHIFT | + MDIO_SUP_PREAMBLE | MDIO_START | + MDIO_CLK_25_4 << MDIO_CLK_SEL_SHIFT; + + AT_WRITE_REG(hw, REG_MDIO_CTRL, val); + + for (i = 0; i < MDIO_WAIT_TIMES; i++) { + udelay(2); + AT_READ_REG(hw, REG_MDIO_CTRL, &val); + if (!(val & (MDIO_START | MDIO_BUSY))) + break; + } + + if (!(val & (MDIO_START | MDIO_BUSY))) + return 0; + + return -1; +} + +/* + * Configures PHY autoneg and flow control advertisement settings + * + * hw - Struct containing variables accessed by shared code + */ +static int atl1c_phy_setup_adv(struct atl1c_hw *hw) +{ + u16 mii_adv_data = ADVERTISE_DEFAULT_CAP & ~ADVERTISE_SPEED_MASK; + u16 mii_giga_ctrl_data = GIGA_CR_1000T_DEFAULT_CAP & + ~GIGA_CR_1000T_SPEED_MASK; + + if (hw->autoneg_advertised & ADVERTISED_10baseT_Half) + mii_adv_data |= ADVERTISE_10HALF; + if (hw->autoneg_advertised & ADVERTISED_10baseT_Full) + mii_adv_data |= ADVERTISE_10FULL; + if (hw->autoneg_advertised & ADVERTISED_100baseT_Half) + mii_adv_data |= ADVERTISE_100HALF; + if (hw->autoneg_advertised & ADVERTISED_100baseT_Full) + mii_adv_data |= ADVERTISE_100FULL; + + if (hw->autoneg_advertised & ADVERTISED_Autoneg) + mii_adv_data |= ADVERTISE_10HALF | ADVERTISE_10FULL | + ADVERTISE_100HALF | ADVERTISE_100FULL; + + if (hw->ctrl_flags & ATL1C_LINK_CAP_1000M) { + if (hw->autoneg_advertised & ADVERTISED_1000baseT_Half) + mii_giga_ctrl_data |= ADVERTISE_1000HALF; + if (hw->autoneg_advertised & ADVERTISED_1000baseT_Full) + mii_giga_ctrl_data |= ADVERTISE_1000FULL; + if (hw->autoneg_advertised & ADVERTISED_Autoneg) + mii_giga_ctrl_data |= ADVERTISE_1000HALF | + ADVERTISE_1000FULL; + } + + if (atl1c_write_phy_reg(hw, MII_ADVERTISE, mii_adv_data) != 0 || + atl1c_write_phy_reg(hw, MII_GIGA_CR, mii_giga_ctrl_data) != 0) + return -1; + return 0; +} + +void atl1c_phy_disable(struct atl1c_hw *hw) +{ + AT_WRITE_REGW(hw, REG_GPHY_CTRL, + GPHY_CTRL_PW_WOL_DIS | GPHY_CTRL_EXT_RESET); +} + +static void atl1c_phy_magic_data(struct atl1c_hw *hw) +{ + u16 data; + + data = ANA_LOOP_SEL_10BT | ANA_EN_MASK_TB | ANA_EN_10BT_IDLE | + ((1 & ANA_INTERVAL_SEL_TIMER_MASK) << + ANA_INTERVAL_SEL_TIMER_SHIFT); + + atl1c_write_phy_reg(hw, MII_DBG_ADDR, MII_ANA_CTRL_18); + atl1c_write_phy_reg(hw, MII_DBG_DATA, data); + + data = (2 & ANA_SERDES_CDR_BW_MASK) | ANA_MS_PAD_DBG | + ANA_SERDES_EN_DEEM | ANA_SERDES_SEL_HSP | ANA_SERDES_EN_PLL | + ANA_SERDES_EN_LCKDT; + + atl1c_write_phy_reg(hw, MII_DBG_ADDR, MII_ANA_CTRL_5); + atl1c_write_phy_reg(hw, MII_DBG_DATA, data); + + data = (44 & ANA_LONG_CABLE_TH_100_MASK) | + ((33 & ANA_SHORT_CABLE_TH_100_MASK) << + ANA_SHORT_CABLE_TH_100_SHIFT) | ANA_BP_BAD_LINK_ACCUM | + ANA_BP_SMALL_BW; + + atl1c_write_phy_reg(hw, MII_DBG_ADDR, MII_ANA_CTRL_54); + atl1c_write_phy_reg(hw, MII_DBG_DATA, data); + + data = (11 & ANA_IECHO_ADJ_MASK) | ((11 & ANA_IECHO_ADJ_MASK) << + ANA_IECHO_ADJ_2_SHIFT) | ((8 & ANA_IECHO_ADJ_MASK) << + ANA_IECHO_ADJ_1_SHIFT) | ((8 & ANA_IECHO_ADJ_MASK) << + ANA_IECHO_ADJ_0_SHIFT); + + atl1c_write_phy_reg(hw, MII_DBG_ADDR, MII_ANA_CTRL_4); + atl1c_write_phy_reg(hw, MII_DBG_DATA, data); + + data = ANA_RESTART_CAL | ((7 & ANA_MANUL_SWICH_ON_MASK) << + ANA_MANUL_SWICH_ON_SHIFT) | ANA_MAN_ENABLE | + ANA_SEL_HSP | ANA_EN_HB | ANA_OEN_125M; + + atl1c_write_phy_reg(hw, MII_DBG_ADDR, MII_ANA_CTRL_0); + atl1c_write_phy_reg(hw, MII_DBG_DATA, data); + + if (hw->ctrl_flags & ATL1C_HIB_DISABLE) { + atl1c_write_phy_reg(hw, MII_DBG_ADDR, MII_ANA_CTRL_41); + if (atl1c_read_phy_reg(hw, MII_DBG_DATA, &data) != 0) + return; + data &= ~ANA_TOP_PS_EN; + atl1c_write_phy_reg(hw, MII_DBG_DATA, data); + + atl1c_write_phy_reg(hw, MII_DBG_ADDR, MII_ANA_CTRL_11); + if (atl1c_read_phy_reg(hw, MII_DBG_DATA, &data) != 0) + return; + data &= ~ANA_PS_HIB_EN; + atl1c_write_phy_reg(hw, MII_DBG_DATA, data); + } +} + +int atl1c_phy_reset(struct atl1c_hw *hw) +{ + struct atl1c_adapter *adapter = hw->adapter; + struct pci_dev *pdev = adapter->pdev; + u32 phy_ctrl_data = GPHY_CTRL_DEFAULT; + u32 mii_ier_data = IER_LINK_UP | IER_LINK_DOWN; + int err; + + if (hw->ctrl_flags & ATL1C_HIB_DISABLE) + phy_ctrl_data &= ~GPHY_CTRL_HIB_EN; + + AT_WRITE_REG(hw, REG_GPHY_CTRL, phy_ctrl_data); + AT_WRITE_FLUSH(hw); + msleep(40); + phy_ctrl_data |= GPHY_CTRL_EXT_RESET; + AT_WRITE_REG(hw, REG_GPHY_CTRL, phy_ctrl_data); + AT_WRITE_FLUSH(hw); + msleep(10); + + /*Enable PHY LinkChange Interrupt */ + err = atl1c_write_phy_reg(hw, MII_IER, mii_ier_data); + if (err) { + if (netif_msg_hw(adapter)) + dev_err(&pdev->dev, + "Error enable PHY linkChange Interrupt\n"); + return err; + } + if (!(hw->ctrl_flags & ATL1C_FPGA_VERSION)) + atl1c_phy_magic_data(hw); + return 0; +} + +int atl1c_phy_init(struct atl1c_hw *hw) +{ + struct atl1c_adapter *adapter = (struct atl1c_adapter *)hw->adapter; + struct pci_dev *pdev = adapter->pdev; + int ret_val; + u16 mii_bmcr_data = BMCR_RESET; + u16 phy_id1, phy_id2; + + if ((atl1c_read_phy_reg(hw, MII_PHYSID1, &phy_id1) != 0) || + (atl1c_read_phy_reg(hw, MII_PHYSID2, &phy_id2) != 0)) { + if (netif_msg_link(adapter)) + dev_err(&pdev->dev, "Error get phy ID\n"); + return -1; + } + switch (hw->media_type) { + case MEDIA_TYPE_AUTO_SENSOR: + ret_val = atl1c_phy_setup_adv(hw); + if (ret_val) { + if (netif_msg_link(adapter)) + dev_err(&pdev->dev, + "Error Setting up Auto-Negotiation\n"); + return ret_val; + } + mii_bmcr_data |= BMCR_AUTO_NEG_EN | BMCR_RESTART_AUTO_NEG; + break; + case MEDIA_TYPE_100M_FULL: + mii_bmcr_data |= BMCR_SPEED_100 | BMCR_FULL_DUPLEX; + break; + case MEDIA_TYPE_100M_HALF: + mii_bmcr_data |= BMCR_SPEED_100; + break; + case MEDIA_TYPE_10M_FULL: + mii_bmcr_data |= BMCR_SPEED_10 | BMCR_FULL_DUPLEX; + break; + case MEDIA_TYPE_10M_HALF: + mii_bmcr_data |= BMCR_SPEED_10; + break; + default: + if (netif_msg_link(adapter)) + dev_err(&pdev->dev, "Wrong Media type %d\n", + hw->media_type); + return -1; + break; + } + + ret_val = atl1c_write_phy_reg(hw, MII_BMCR, mii_bmcr_data); + if (ret_val) + return ret_val; + hw->phy_configured = true; + + return 0; +} + +/* + * Detects the current speed and duplex settings of the hardware. + * + * hw - Struct containing variables accessed by shared code + * speed - Speed of the connection + * duplex - Duplex setting of the connection + */ +int atl1c_get_speed_and_duplex(struct atl1c_hw *hw, u16 *speed, u16 *duplex) +{ + int err; + u16 phy_data; + + /* Read PHY Specific Status Register (17) */ + err = atl1c_read_phy_reg(hw, MII_GIGA_PSSR, &phy_data); + if (err) + return err; + + if (!(phy_data & GIGA_PSSR_SPD_DPLX_RESOLVED)) + return -1; + + switch (phy_data & GIGA_PSSR_SPEED) { + case GIGA_PSSR_1000MBS: + *speed = SPEED_1000; + break; + case GIGA_PSSR_100MBS: + *speed = SPEED_100; + break; + case GIGA_PSSR_10MBS: + *speed = SPEED_10; + break; + default: + return -1; + break; + } + + if (phy_data & GIGA_PSSR_DPLX) + *duplex = FULL_DUPLEX; + else + *duplex = HALF_DUPLEX; + + return 0; +} + +int atl1c_restart_autoneg(struct atl1c_hw *hw) +{ + int err = 0; + u16 mii_bmcr_data = BMCR_RESET; + + err = atl1c_phy_setup_adv(hw); + if (err) + return err; + mii_bmcr_data |= BMCR_AUTO_NEG_EN | BMCR_RESTART_AUTO_NEG; + + return atl1c_write_phy_reg(hw, MII_BMCR, mii_bmcr_data); +} diff --git a/drivers/net/atl1c/atl1c_hw.h b/drivers/net/atl1c/atl1c_hw.h new file mode 100644 index 00000000000..c2c738df5c6 --- /dev/null +++ b/drivers/net/atl1c/atl1c_hw.h @@ -0,0 +1,859 @@ +/* + * Copyright(c) 2008 - 2009 Atheros Corporation. All rights reserved. + * + * Derived from Intel e1000 driver + * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _ATL1C_HW_H_ +#define _ATL1C_HW_H_ + +#include <linux/types.h> +#include <linux/mii.h> + +struct atl1c_adapter; +struct atl1c_hw; + +/* function prototype */ +void atl1c_phy_disable(struct atl1c_hw *hw); +void atl1c_hw_set_mac_addr(struct atl1c_hw *hw); +int atl1c_phy_reset(struct atl1c_hw *hw); +int atl1c_read_mac_addr(struct atl1c_hw *hw); +int atl1c_get_speed_and_duplex(struct atl1c_hw *hw, u16 *speed, u16 *duplex); +u32 atl1c_hash_mc_addr(struct atl1c_hw *hw, u8 *mc_addr); +void atl1c_hash_set(struct atl1c_hw *hw, u32 hash_value); +int atl1c_read_phy_reg(struct atl1c_hw *hw, u16 reg_addr, u16 *phy_data); +int atl1c_write_phy_reg(struct atl1c_hw *hw, u32 reg_addr, u16 phy_data); +bool atl1c_read_eeprom(struct atl1c_hw *hw, u32 offset, u32 *p_value); +int atl1c_phy_init(struct atl1c_hw *hw); +int atl1c_check_eeprom_exist(struct atl1c_hw *hw); +int atl1c_restart_autoneg(struct atl1c_hw *hw); + +/* register definition */ +#define REG_DEVICE_CAP 0x5C +#define DEVICE_CAP_MAX_PAYLOAD_MASK 0x7 +#define DEVICE_CAP_MAX_PAYLOAD_SHIFT 0 + +#define REG_DEVICE_CTRL 0x60 +#define DEVICE_CTRL_MAX_PAYLOAD_MASK 0x7 +#define DEVICE_CTRL_MAX_PAYLOAD_SHIFT 5 +#define DEVICE_CTRL_MAX_RREQ_SZ_MASK 0x7 +#define DEVICE_CTRL_MAX_RREQ_SZ_SHIFT 12 + +#define REG_LINK_CTRL 0x68 +#define LINK_CTRL_L0S_EN 0x01 +#define LINK_CTRL_L1_EN 0x02 + +#define REG_VPD_CAP 0x6C +#define VPD_CAP_ID_MASK 0xff +#define VPD_CAP_ID_SHIFT 0 +#define VPD_CAP_NEXT_PTR_MASK 0xFF +#define VPD_CAP_NEXT_PTR_SHIFT 8 +#define VPD_CAP_VPD_ADDR_MASK 0x7FFF +#define VPD_CAP_VPD_ADDR_SHIFT 16 +#define VPD_CAP_VPD_FLAG 0x80000000 + +#define REG_VPD_DATA 0x70 + +#define REG_PCIE_UC_SEVERITY 0x10C +#define PCIE_UC_SERVRITY_TRN 0x00000001 +#define PCIE_UC_SERVRITY_DLP 0x00000010 +#define PCIE_UC_SERVRITY_PSN_TLP 0x00001000 +#define PCIE_UC_SERVRITY_FCP 0x00002000 +#define PCIE_UC_SERVRITY_CPL_TO 0x00004000 +#define PCIE_UC_SERVRITY_CA 0x00008000 +#define PCIE_UC_SERVRITY_UC 0x00010000 +#define PCIE_UC_SERVRITY_ROV 0x00020000 +#define PCIE_UC_SERVRITY_MLFP 0x00040000 +#define PCIE_UC_SERVRITY_ECRC 0x00080000 +#define PCIE_UC_SERVRITY_UR 0x00100000 + +#define REG_DEV_SERIALNUM_CTRL 0x200 +#define REG_DEV_MAC_SEL_MASK 0x0 /* 0:EUI; 1:MAC */ +#define REG_DEV_MAC_SEL_SHIFT 0 +#define REG_DEV_SERIAL_NUM_EN_MASK 0x1 +#define REG_DEV_SERIAL_NUM_EN_SHIFT 1 + +#define REG_TWSI_CTRL 0x218 +#define TWSI_CTRL_LD_OFFSET_MASK 0xFF +#define TWSI_CTRL_LD_OFFSET_SHIFT 0 +#define TWSI_CTRL_LD_SLV_ADDR_MASK 0x7 +#define TWSI_CTRL_LD_SLV_ADDR_SHIFT 8 +#define TWSI_CTRL_SW_LDSTART 0x800 +#define TWSI_CTRL_HW_LDSTART 0x1000 +#define TWSI_CTRL_SMB_SLV_ADDR_MASK 0x7F +#define TWSI_CTRL_SMB_SLV_ADDR_SHIFT 15 +#define TWSI_CTRL_LD_EXIST 0x400000 +#define TWSI_CTRL_READ_FREQ_SEL_MASK 0x3 +#define TWSI_CTRL_READ_FREQ_SEL_SHIFT 23 +#define TWSI_CTRL_FREQ_SEL_100K 0 +#define TWSI_CTRL_FREQ_SEL_200K 1 +#define TWSI_CTRL_FREQ_SEL_300K 2 +#define TWSI_CTRL_FREQ_SEL_400K 3 +#define TWSI_CTRL_SMB_SLV_ADDR +#define TWSI_CTRL_WRITE_FREQ_SEL_MASK 0x3 +#define TWSI_CTRL_WRITE_FREQ_SEL_SHIFT 24 + + +#define REG_PCIE_DEV_MISC_CTRL 0x21C +#define PCIE_DEV_MISC_EXT_PIPE 0x2 +#define PCIE_DEV_MISC_RETRY_BUFDIS 0x1 +#define PCIE_DEV_MISC_SPIROM_EXIST 0x4 +#define PCIE_DEV_MISC_SERDES_ENDIAN 0x8 +#define PCIE_DEV_MISC_SERDES_SEL_DIN 0x10 + +#define REG_PCIE_PHYMISC 0x1000 +#define PCIE_PHYMISC_FORCE_RCV_DET 0x4 + +#define REG_TWSI_DEBUG 0x1108 +#define TWSI_DEBUG_DEV_EXIST 0x20000000 + +#define REG_EEPROM_CTRL 0x12C0 +#define EEPROM_CTRL_DATA_HI_MASK 0xFFFF +#define EEPROM_CTRL_DATA_HI_SHIFT 0 +#define EEPROM_CTRL_ADDR_MASK 0x3FF +#define EEPROM_CTRL_ADDR_SHIFT 16 +#define EEPROM_CTRL_ACK 0x40000000 +#define EEPROM_CTRL_RW 0x80000000 + +#define REG_EEPROM_DATA_LO 0x12C4 + +#define REG_OTP_CTRL 0x12F0 +#define OTP_CTRL_CLK_EN 0x0002 + +#define REG_PM_CTRL 0x12F8 +#define PM_CTRL_SDES_EN 0x00000001 +#define PM_CTRL_RBER_EN 0x00000002 +#define PM_CTRL_CLK_REQ_EN 0x00000004 +#define PM_CTRL_ASPM_L1_EN 0x00000008 +#define PM_CTRL_SERDES_L1_EN 0x00000010 +#define PM_CTRL_SERDES_PLL_L1_EN 0x00000020 +#define PM_CTRL_SERDES_PD_EX_L1 0x00000040 +#define PM_CTRL_SERDES_BUDS_RX_L1_EN 0x00000080 +#define PM_CTRL_L0S_ENTRY_TIMER_MASK 0xF +#define PM_CTRL_L0S_ENTRY_TIMER_SHIFT 8 +#define PM_CTRL_ASPM_L0S_EN 0x00001000 +#define PM_CTRL_CLK_SWH_L1 0x00002000 +#define PM_CTRL_CLK_PWM_VER1_1 0x00004000 +#define PM_CTRL_PCIE_RECV 0x00008000 +#define PM_CTRL_L1_ENTRY_TIMER_MASK 0xF +#define PM_CTRL_L1_ENTRY_TIMER_SHIFT 16 +#define PM_CTRL_PM_REQ_TIMER_MASK 0xF +#define PM_CTRL_PM_REQ_TIMER_SHIFT 20 +#define PM_CTRL_LCKDET_TIMER_MASK 0x3F +#define PM_CTRL_LCKDET_TIMER_SHIFT 24 +#define PM_CTRL_MAC_ASPM_CHK 0x40000000 +#define PM_CTRL_HOTRST 0x80000000 + +/* Selene Master Control Register */ +#define REG_MASTER_CTRL 0x1400 +#define MASTER_CTRL_SOFT_RST 0x1 +#define MASTER_CTRL_TEST_MODE_MASK 0x3 +#define MASTER_CTRL_TEST_MODE_SHIFT 2 +#define MASTER_CTRL_BERT_START 0x10 +#define MASTER_CTRL_MTIMER_EN 0x100 +#define MASTER_CTRL_MANUAL_INT 0x200 +#define MASTER_CTRL_TX_ITIMER_EN 0x400 +#define MASTER_CTRL_RX_ITIMER_EN 0x800 +#define MASTER_CTRL_CLK_SEL_DIS 0x1000 +#define MASTER_CTRL_CLK_SWH_MODE 0x2000 +#define MASTER_CTRL_INT_RDCLR 0x4000 +#define MASTER_CTRL_REV_NUM_SHIFT 16 +#define MASTER_CTRL_REV_NUM_MASK 0xff +#define MASTER_CTRL_DEV_ID_SHIFT 24 +#define MASTER_CTRL_DEV_ID_MASK 0x7f +#define MASTER_CTRL_OTP_SEL 0x80000000 + +/* Timer Initial Value Register */ +#define REG_MANUAL_TIMER_INIT 0x1404 + +/* IRQ ModeratorTimer Initial Value Register */ +#define REG_IRQ_MODRT_TIMER_INIT 0x1408 +#define IRQ_MODRT_TIMER_MASK 0xffff +#define IRQ_MODRT_TX_TIMER_SHIFT 0 +#define IRQ_MODRT_RX_TIMER_SHIFT 16 + +#define REG_GPHY_CTRL 0x140C +#define GPHY_CTRL_EXT_RESET 0x1 +#define GPHY_CTRL_RTL_MODE 0x2 +#define GPHY_CTRL_LED_MODE 0x4 +#define GPHY_CTRL_ANEG_NOW 0x8 +#define GPHY_CTRL_REV_ANEG 0x10 +#define GPHY_CTRL_GATE_25M_EN 0x20 +#define GPHY_CTRL_LPW_EXIT 0x40 +#define GPHY_CTRL_PHY_IDDQ 0x80 +#define GPHY_CTRL_PHY_IDDQ_DIS 0x100 +#define GPHY_CTRL_GIGA_DIS 0x200 +#define GPHY_CTRL_HIB_EN 0x400 +#define GPHY_CTRL_HIB_PULSE 0x800 +#define GPHY_CTRL_SEL_ANA_RST 0x1000 +#define GPHY_CTRL_PHY_PLL_ON 0x2000 +#define GPHY_CTRL_PWDOWN_HW 0x4000 +#define GPHY_CTRL_PHY_PLL_BYPASS 0x8000 + +#define GPHY_CTRL_DEFAULT ( \ + GPHY_CTRL_SEL_ANA_RST |\ + GPHY_CTRL_HIB_PULSE |\ + GPHY_CTRL_HIB_EN) + +#define GPHY_CTRL_PW_WOL_DIS ( \ + GPHY_CTRL_SEL_ANA_RST |\ + GPHY_CTRL_HIB_PULSE |\ + GPHY_CTRL_HIB_EN |\ + GPHY_CTRL_PWDOWN_HW |\ + GPHY_CTRL_PHY_IDDQ) + +/* Block IDLE Status Register */ +#define REG_IDLE_STATUS 0x1410 +#define IDLE_STATUS_MASK 0x00FF +#define IDLE_STATUS_RXMAC_NO_IDLE 0x1 +#define IDLE_STATUS_TXMAC_NO_IDLE 0x2 +#define IDLE_STATUS_RXQ_NO_IDLE 0x4 +#define IDLE_STATUS_TXQ_NO_IDLE 0x8 +#define IDLE_STATUS_DMAR_NO_IDLE 0x10 +#define IDLE_STATUS_DMAW_NO_IDLE 0x20 +#define IDLE_STATUS_SMB_NO_IDLE 0x40 +#define IDLE_STATUS_CMB_NO_IDLE 0x80 + +/* MDIO Control Register */ +#define REG_MDIO_CTRL 0x1414 +#define MDIO_DATA_MASK 0xffff /* On MDIO write, the 16-bit + * control data to write to PHY + * MII management register */ +#define MDIO_DATA_SHIFT 0 /* On MDIO read, the 16-bit + * status data that was read + * from the PHY MII management register */ +#define MDIO_REG_ADDR_MASK 0x1f /* MDIO register address */ +#define MDIO_REG_ADDR_SHIFT 16 +#define MDIO_RW 0x200000 /* 1: read, 0: write */ +#define MDIO_SUP_PREAMBLE 0x400000 /* Suppress preamble */ +#define MDIO_START 0x800000 /* Write 1 to initiate the MDIO + * master. And this bit is self + * cleared after one cycle */ +#define MDIO_CLK_SEL_SHIFT 24 +#define MDIO_CLK_25_4 0 +#define MDIO_CLK_25_6 2 +#define MDIO_CLK_25_8 3 +#define MDIO_CLK_25_10 4 +#define MDIO_CLK_25_14 5 +#define MDIO_CLK_25_20 6 +#define MDIO_CLK_25_28 7 +#define MDIO_BUSY 0x8000000 +#define MDIO_AP_EN 0x10000000 +#define MDIO_WAIT_TIMES 10 + +/* MII PHY Status Register */ +#define REG_PHY_STATUS 0x1418 +#define PHY_GENERAL_STATUS_MASK 0xFFFF +#define PHY_STATUS_RECV_ENABLE 0x0001 +#define PHY_OE_PWSP_STATUS_MASK 0x07FF +#define PHY_OE_PWSP_STATUS_SHIFT 16 +#define PHY_STATUS_LPW_STATE 0x80000000 +/* BIST Control and Status Register0 (for the Packet Memory) */ +#define REG_BIST0_CTRL 0x141c +#define BIST0_NOW 0x1 +#define BIST0_SRAM_FAIL 0x2 /* 1: The SRAM failure is + * un-repairable because + * it has address decoder + * failure or more than 1 cell + * stuck-to-x failure */ +#define BIST0_FUSE_FLAG 0x4 + +/* BIST Control and Status Register1(for the retry buffer of PCI Express) */ +#define REG_BIST1_CTRL 0x1420 +#define BIST1_NOW 0x1 +#define BIST1_SRAM_FAIL 0x2 +#define BIST1_FUSE_FLAG 0x4 + +/* SerDes Lock Detect Control and Status Register */ +#define REG_SERDES_LOCK 0x1424 +#define SERDES_LOCK_DETECT 0x1 /* SerDes lock detected. This signal + * comes from Analog SerDes */ +#define SERDES_LOCK_DETECT_EN 0x2 /* 1: Enable SerDes Lock detect function */ + +/* MAC Control Register */ +#define REG_MAC_CTRL 0x1480 +#define MAC_CTRL_TX_EN 0x1 +#define MAC_CTRL_RX_EN 0x2 +#define MAC_CTRL_TX_FLOW 0x4 +#define MAC_CTRL_RX_FLOW 0x8 +#define MAC_CTRL_LOOPBACK 0x10 +#define MAC_CTRL_DUPLX 0x20 +#define MAC_CTRL_ADD_CRC 0x40 +#define MAC_CTRL_PAD 0x80 +#define MAC_CTRL_LENCHK 0x100 +#define MAC_CTRL_HUGE_EN 0x200 +#define MAC_CTRL_PRMLEN_SHIFT 10 +#define MAC_CTRL_PRMLEN_MASK 0xf +#define MAC_CTRL_RMV_VLAN 0x4000 +#define MAC_CTRL_PROMIS_EN 0x8000 +#define MAC_CTRL_TX_PAUSE 0x10000 +#define MAC_CTRL_SCNT 0x20000 +#define MAC_CTRL_SRST_TX 0x40000 +#define MAC_CTRL_TX_SIMURST 0x80000 +#define MAC_CTRL_SPEED_SHIFT 20 +#define MAC_CTRL_SPEED_MASK 0x3 +#define MAC_CTRL_DBG_TX_BKPRESURE 0x400000 +#define MAC_CTRL_TX_HUGE 0x800000 +#define MAC_CTRL_RX_CHKSUM_EN 0x1000000 +#define MAC_CTRL_MC_ALL_EN 0x2000000 +#define MAC_CTRL_BC_EN 0x4000000 +#define MAC_CTRL_DBG 0x8000000 +#define MAC_CTRL_SINGLE_PAUSE_EN 0x10000000 + +/* MAC IPG/IFG Control Register */ +#define REG_MAC_IPG_IFG 0x1484 +#define MAC_IPG_IFG_IPGT_SHIFT 0 /* Desired back to back + * inter-packet gap. The + * default is 96-bit time */ +#define MAC_IPG_IFG_IPGT_MASK 0x7f +#define MAC_IPG_IFG_MIFG_SHIFT 8 /* Minimum number of IFG to + * enforce in between RX frames */ +#define MAC_IPG_IFG_MIFG_MASK 0xff /* Frame gap below such IFP is dropped */ +#define MAC_IPG_IFG_IPGR1_SHIFT 16 /* 64bit Carrier-Sense window */ +#define MAC_IPG_IFG_IPGR1_MASK 0x7f +#define MAC_IPG_IFG_IPGR2_SHIFT 24 /* 96-bit IPG window */ +#define MAC_IPG_IFG_IPGR2_MASK 0x7f + +/* MAC STATION ADDRESS */ +#define REG_MAC_STA_ADDR 0x1488 + +/* Hash table for multicast address */ +#define REG_RX_HASH_TABLE 0x1490 + +/* MAC Half-Duplex Control Register */ +#define REG_MAC_HALF_DUPLX_CTRL 0x1498 +#define MAC_HALF_DUPLX_CTRL_LCOL_SHIFT 0 /* Collision Window */ +#define MAC_HALF_DUPLX_CTRL_LCOL_MASK 0x3ff +#define MAC_HALF_DUPLX_CTRL_RETRY_SHIFT 12 +#define MAC_HALF_DUPLX_CTRL_RETRY_MASK 0xf +#define MAC_HALF_DUPLX_CTRL_EXC_DEF_EN 0x10000 +#define MAC_HALF_DUPLX_CTRL_NO_BACK_C 0x20000 +#define MAC_HALF_DUPLX_CTRL_NO_BACK_P 0x40000 /* No back-off on backpressure, + * immediately start the + * transmission after back pressure */ +#define MAC_HALF_DUPLX_CTRL_ABEBE 0x80000 /* 1: Alternative Binary Exponential Back-off Enabled */ +#define MAC_HALF_DUPLX_CTRL_ABEBT_SHIFT 20 /* Maximum binary exponential number */ +#define MAC_HALF_DUPLX_CTRL_ABEBT_MASK 0xf +#define MAC_HALF_DUPLX_CTRL_JAMIPG_SHIFT 24 /* IPG to start JAM for collision based flow control in half-duplex */ +#define MAC_HALF_DUPLX_CTRL_JAMIPG_MASK 0xf /* mode. In unit of 8-bit time */ + +/* Maximum Frame Length Control Register */ +#define REG_MTU 0x149c + +/* Wake-On-Lan control register */ +#define REG_WOL_CTRL 0x14a0 +#define WOL_PATTERN_EN 0x00000001 +#define WOL_PATTERN_PME_EN 0x00000002 +#define WOL_MAGIC_EN 0x00000004 +#define WOL_MAGIC_PME_EN 0x00000008 +#define WOL_LINK_CHG_EN 0x00000010 +#define WOL_LINK_CHG_PME_EN 0x00000020 +#define WOL_PATTERN_ST 0x00000100 +#define WOL_MAGIC_ST 0x00000200 +#define WOL_LINKCHG_ST 0x00000400 +#define WOL_CLK_SWITCH_EN 0x00008000 +#define WOL_PT0_EN 0x00010000 +#define WOL_PT1_EN 0x00020000 +#define WOL_PT2_EN 0x00040000 +#define WOL_PT3_EN 0x00080000 +#define WOL_PT4_EN 0x00100000 +#define WOL_PT5_EN 0x00200000 +#define WOL_PT6_EN 0x00400000 + +/* WOL Length ( 2 DWORD ) */ +#define REG_WOL_PATTERN_LEN 0x14a4 +#define WOL_PT_LEN_MASK 0x7f +#define WOL_PT0_LEN_SHIFT 0 +#define WOL_PT1_LEN_SHIFT 8 +#define WOL_PT2_LEN_SHIFT 16 +#define WOL_PT3_LEN_SHIFT 24 +#define WOL_PT4_LEN_SHIFT 0 +#define WOL_PT5_LEN_SHIFT 8 +#define WOL_PT6_LEN_SHIFT 16 + +/* Internal SRAM Partition Register */ +#define RFDX_HEAD_ADDR_MASK 0x03FF +#define RFDX_HARD_ADDR_SHIFT 0 +#define RFDX_TAIL_ADDR_MASK 0x03FF +#define RFDX_TAIL_ADDR_SHIFT 16 + +#define REG_SRAM_RFD0_INFO 0x1500 +#define REG_SRAM_RFD1_INFO 0x1504 +#define REG_SRAM_RFD2_INFO 0x1508 +#define REG_SRAM_RFD3_INFO 0x150C + +#define REG_RFD_NIC_LEN 0x1510 /* In 8-bytes */ +#define RFD_NIC_LEN_MASK 0x03FF + +#define REG_SRAM_TRD_ADDR 0x1518 +#define TPD_HEAD_ADDR_MASK 0x03FF +#define TPD_HEAD_ADDR_SHIFT 0 +#define TPD_TAIL_ADDR_MASK 0x03FF +#define TPD_TAIL_ADDR_SHIFT 16 + +#define REG_SRAM_TRD_LEN 0x151C /* In 8-bytes */ +#define TPD_NIC_LEN_MASK 0x03FF + +#define REG_SRAM_RXF_ADDR 0x1520 +#define REG_SRAM_RXF_LEN 0x1524 +#define REG_SRAM_TXF_ADDR 0x1528 +#define REG_SRAM_TXF_LEN 0x152C +#define REG_SRAM_TCPH_ADDR 0x1530 +#define REG_SRAM_PKTH_ADDR 0x1532 + +/* + * Load Ptr Register + * Software sets this bit after the initialization of the head and tail */ +#define REG_LOAD_PTR 0x1534 + +/* + * addresses of all descriptors, as well as the following descriptor + * control register, which triggers each function block to load the head + * pointer to prepare for the operation. This bit is then self-cleared + * after one cycle. + */ +#define REG_RX_BASE_ADDR_HI 0x1540 +#define REG_TX_BASE_ADDR_HI 0x1544 +#define REG_SMB_BASE_ADDR_HI 0x1548 +#define REG_SMB_BASE_ADDR_LO 0x154C +#define REG_RFD0_HEAD_ADDR_LO 0x1550 +#define REG_RFD1_HEAD_ADDR_LO 0x1554 +#define REG_RFD2_HEAD_ADDR_LO 0x1558 +#define REG_RFD3_HEAD_ADDR_LO 0x155C +#define REG_RFD_RING_SIZE 0x1560 +#define RFD_RING_SIZE_MASK 0x0FFF +#define REG_RX_BUF_SIZE 0x1564 +#define RX_BUF_SIZE_MASK 0xFFFF +#define REG_RRD0_HEAD_ADDR_LO 0x1568 +#define REG_RRD1_HEAD_ADDR_LO 0x156C +#define REG_RRD2_HEAD_ADDR_LO 0x1570 +#define REG_RRD3_HEAD_ADDR_LO 0x1574 +#define REG_RRD_RING_SIZE 0x1578 +#define RRD_RING_SIZE_MASK 0x0FFF +#define REG_HTPD_HEAD_ADDR_LO 0x157C +#define REG_NTPD_HEAD_ADDR_LO 0x1580 +#define REG_TPD_RING_SIZE 0x1584 +#define TPD_RING_SIZE_MASK 0xFFFF +#define REG_CMB_BASE_ADDR_LO 0x1588 + +/* RSS about */ +#define REG_RSS_KEY0 0x14B0 +#define REG_RSS_KEY1 0x14B4 +#define REG_RSS_KEY2 0x14B8 +#define REG_RSS_KEY3 0x14BC +#define REG_RSS_KEY4 0x14C0 +#define REG_RSS_KEY5 0x14C4 +#define REG_RSS_KEY6 0x14C8 +#define REG_RSS_KEY7 0x14CC +#define REG_RSS_KEY8 0x14D0 +#define REG_RSS_KEY9 0x14D4 +#define REG_IDT_TABLE0 0x14E0 +#define REG_IDT_TABLE1 0x14E4 +#define REG_IDT_TABLE2 0x14E8 +#define REG_IDT_TABLE3 0x14EC +#define REG_IDT_TABLE4 0x14F0 +#define REG_IDT_TABLE5 0x14F4 +#define REG_IDT_TABLE6 0x14F8 +#define REG_IDT_TABLE7 0x14FC +#define REG_IDT_TABLE REG_IDT_TABLE0 +#define REG_RSS_HASH_VALUE 0x15B0 +#define REG_RSS_HASH_FLAG 0x15B4 +#define REG_BASE_CPU_NUMBER 0x15B8 + +/* TXQ Control Register */ +#define REG_TXQ_CTRL 0x1590 +#define TXQ_NUM_TPD_BURST_MASK 0xF +#define TXQ_NUM_TPD_BURST_SHIFT 0 +#define TXQ_CTRL_IP_OPTION_EN 0x10 +#define TXQ_CTRL_EN 0x20 +#define TXQ_CTRL_ENH_MODE 0x40 +#define TXQ_CTRL_LS_8023_EN 0x80 +#define TXQ_TXF_BURST_NUM_SHIFT 16 +#define TXQ_TXF_BURST_NUM_MASK 0xFFFF + +/* Jumbo packet Threshold for task offload */ +#define REG_TX_TSO_OFFLOAD_THRESH 0x1594 /* In 8-bytes */ +#define TX_TSO_OFFLOAD_THRESH_MASK 0x07FF + +#define REG_TXF_WATER_MARK 0x1598 /* In 8-bytes */ +#define TXF_WATER_MARK_MASK 0x0FFF +#define TXF_LOW_WATER_MARK_SHIFT 0 +#define TXF_HIGH_WATER_MARK_SHIFT 16 +#define TXQ_CTRL_BURST_MODE_EN 0x80000000 + +#define REG_THRUPUT_MON_CTRL 0x159C +#define THRUPUT_MON_RATE_MASK 0x3 +#define THRUPUT_MON_RATE_SHIFT 0 +#define THRUPUT_MON_EN 0x80 + +/* RXQ Control Register */ +#define REG_RXQ_CTRL 0x15A0 +#define ASPM_THRUPUT_LIMIT_MASK 0x3 +#define ASPM_THRUPUT_LIMIT_SHIFT 0 +#define ASPM_THRUPUT_LIMIT_NO 0x00 +#define ASPM_THRUPUT_LIMIT_1M 0x01 +#define ASPM_THRUPUT_LIMIT_10M 0x02 +#define ASPM_THRUPUT_LIMIT_100M 0x04 +#define RXQ1_CTRL_EN 0x10 +#define RXQ2_CTRL_EN 0x20 +#define RXQ3_CTRL_EN 0x40 +#define IPV6_CHKSUM_CTRL_EN 0x80 +#define RSS_HASH_BITS_MASK 0x00FF +#define RSS_HASH_BITS_SHIFT 8 +#define RSS_HASH_IPV4 0x10000 +#define RSS_HASH_IPV4_TCP 0x20000 +#define RSS_HASH_IPV6 0x40000 +#define RSS_HASH_IPV6_TCP 0x80000 +#define RXQ_RFD_BURST_NUM_MASK 0x003F +#define RXQ_RFD_BURST_NUM_SHIFT 20 +#define RSS_MODE_MASK 0x0003 +#define RSS_MODE_SHIFT 26 +#define RSS_NIP_QUEUE_SEL_MASK 0x1 +#define RSS_NIP_QUEUE_SEL_SHIFT 28 +#define RRS_HASH_CTRL_EN 0x20000000 +#define RX_CUT_THRU_EN 0x40000000 +#define RXQ_CTRL_EN 0x80000000 + +#define REG_RFD_FREE_THRESH 0x15A4 +#define RFD_FREE_THRESH_MASK 0x003F +#define RFD_FREE_HI_THRESH_SHIFT 0 +#define RFD_FREE_LO_THRESH_SHIFT 6 + +/* RXF flow control register */ +#define REG_RXQ_RXF_PAUSE_THRESH 0x15A8 +#define RXQ_RXF_PAUSE_TH_HI_SHIFT 0 +#define RXQ_RXF_PAUSE_TH_HI_MASK 0x0FFF +#define RXQ_RXF_PAUSE_TH_LO_SHIFT 16 +#define RXQ_RXF_PAUSE_TH_LO_MASK 0x0FFF + +#define REG_RXD_DMA_CTRL 0x15AC +#define RXD_DMA_THRESH_MASK 0x0FFF /* In 8-bytes */ +#define RXD_DMA_THRESH_SHIFT 0 +#define RXD_DMA_DOWN_TIMER_MASK 0xFFFF +#define RXD_DMA_DOWN_TIMER_SHIFT 16 + +/* DMA Engine Control Register */ +#define REG_DMA_CTRL 0x15C0 +#define DMA_CTRL_DMAR_IN_ORDER 0x1 +#define DMA_CTRL_DMAR_ENH_ORDER 0x2 +#define DMA_CTRL_DMAR_OUT_ORDER 0x4 +#define DMA_CTRL_RCB_VALUE 0x8 +#define DMA_CTRL_DMAR_BURST_LEN_MASK 0x0007 +#define DMA_CTRL_DMAR_BURST_LEN_SHIFT 4 +#define DMA_CTRL_DMAW_BURST_LEN_MASK 0x0007 +#define DMA_CTRL_DMAW_BURST_LEN_SHIFT 7 +#define DMA_CTRL_DMAR_REQ_PRI 0x400 +#define DMA_CTRL_DMAR_DLY_CNT_MASK 0x001F +#define DMA_CTRL_DMAR_DLY_CNT_SHIFT 11 +#define DMA_CTRL_DMAW_DLY_CNT_MASK 0x000F +#define DMA_CTRL_DMAW_DLY_CNT_SHIFT 16 +#define DMA_CTRL_CMB_EN 0x100000 +#define DMA_CTRL_SMB_EN 0x200000 +#define DMA_CTRL_CMB_NOW 0x400000 +#define MAC_CTRL_SMB_DIS 0x1000000 +#define DMA_CTRL_SMB_NOW 0x80000000 + +/* CMB/SMB Control Register */ +#define REG_SMB_STAT_TIMER 0x15C4 /* 2us resolution */ +#define SMB_STAT_TIMER_MASK 0xFFFFFF +#define REG_CMB_TPD_THRESH 0x15C8 +#define CMB_TPD_THRESH_MASK 0xFFFF +#define REG_CMB_TX_TIMER 0x15CC /* 2us resolution */ +#define CMB_TX_TIMER_MASK 0xFFFF + +/* Mail box */ +#define MB_RFDX_PROD_IDX_MASK 0xFFFF +#define REG_MB_RFD0_PROD_IDX 0x15E0 +#define REG_MB_RFD1_PROD_IDX 0x15E4 +#define REG_MB_RFD2_PROD_IDX 0x15E8 +#define REG_MB_RFD3_PROD_IDX 0x15EC + +#define MB_PRIO_PROD_IDX_MASK 0xFFFF +#define REG_MB_PRIO_PROD_IDX 0x15F0 +#define MB_HTPD_PROD_IDX_SHIFT 0 +#define MB_NTPD_PROD_IDX_SHIFT 16 + +#define MB_PRIO_CONS_IDX_MASK 0xFFFF +#define REG_MB_PRIO_CONS_IDX 0x15F4 +#define MB_HTPD_CONS_IDX_SHIFT 0 +#define MB_NTPD_CONS_IDX_SHIFT 16 + +#define REG_MB_RFD01_CONS_IDX 0x15F8 +#define MB_RFD0_CONS_IDX_MASK 0x0000FFFF +#define MB_RFD1_CONS_IDX_MASK 0xFFFF0000 +#define REG_MB_RFD23_CONS_IDX 0x15FC +#define MB_RFD2_CONS_IDX_MASK 0x0000FFFF +#define MB_RFD3_CONS_IDX_MASK 0xFFFF0000 + +/* Interrupt Status Register */ +#define REG_ISR 0x1600 +#define ISR_SMB 0x00000001 +#define ISR_TIMER 0x00000002 +/* + * Software manual interrupt, for debug. Set when SW_MAN_INT_EN is set + * in Table 51 Selene Master Control Register (Offset 0x1400). + */ +#define ISR_MANUAL 0x00000004 +#define ISR_HW_RXF_OV 0x00000008 /* RXF overflow interrupt */ +#define ISR_RFD0_UR 0x00000010 /* RFD0 under run */ +#define ISR_RFD1_UR 0x00000020 +#define ISR_RFD2_UR 0x00000040 +#define ISR_RFD3_UR 0x00000080 +#define ISR_TXF_UR 0x00000100 +#define ISR_DMAR_TO_RST 0x00000200 +#define ISR_DMAW_TO_RST 0x00000400 +#define ISR_TX_CREDIT 0x00000800 +#define ISR_GPHY 0x00001000 +/* GPHY low power state interrupt */ +#define ISR_GPHY_LPW 0x00002000 +#define ISR_TXQ_TO_RST 0x00004000 +#define ISR_TX_PKT 0x00008000 +#define ISR_RX_PKT_0 0x00010000 +#define ISR_RX_PKT_1 0x00020000 +#define ISR_RX_PKT_2 0x00040000 +#define ISR_RX_PKT_3 0x00080000 +#define ISR_MAC_RX 0x00100000 +#define ISR_MAC_TX 0x00200000 +#define ISR_UR_DETECTED 0x00400000 +#define ISR_FERR_DETECTED 0x00800000 +#define ISR_NFERR_DETECTED 0x01000000 +#define ISR_CERR_DETECTED 0x02000000 +#define ISR_PHY_LINKDOWN 0x04000000 +#define ISR_DIS_INT 0x80000000 + +/* Interrupt Mask Register */ +#define REG_IMR 0x1604 + +#define IMR_NORMAL_MASK (\ + ISR_MANUAL |\ + ISR_HW_RXF_OV |\ + ISR_RFD0_UR |\ + ISR_TXF_UR |\ + ISR_DMAR_TO_RST |\ + ISR_TXQ_TO_RST |\ + ISR_DMAW_TO_RST |\ + ISR_GPHY |\ + ISR_TX_PKT |\ + ISR_RX_PKT_0 |\ + ISR_GPHY_LPW |\ + ISR_PHY_LINKDOWN) + +#define ISR_RX_PKT (\ + ISR_RX_PKT_0 |\ + ISR_RX_PKT_1 |\ + ISR_RX_PKT_2 |\ + ISR_RX_PKT_3) + +#define ISR_OVER (\ + ISR_RFD0_UR |\ + ISR_RFD1_UR |\ + ISR_RFD2_UR |\ + ISR_RFD3_UR |\ + ISR_HW_RXF_OV |\ + ISR_TXF_UR) + +#define ISR_ERROR (\ + ISR_DMAR_TO_RST |\ + ISR_TXQ_TO_RST |\ + ISR_DMAW_TO_RST |\ + ISR_PHY_LINKDOWN) + +#define REG_INT_RETRIG_TIMER 0x1608 +#define INT_RETRIG_TIMER_MASK 0xFFFF + +#define REG_HDS_CTRL 0x160C +#define HDS_CTRL_EN 0x0001 +#define HDS_CTRL_BACKFILLSIZE_SHIFT 8 +#define HDS_CTRL_BACKFILLSIZE_MASK 0x0FFF +#define HDS_CTRL_MAX_HDRSIZE_SHIFT 20 +#define HDS_CTRL_MAC_HDRSIZE_MASK 0x0FFF + +#define REG_MAC_RX_STATUS_BIN 0x1700 +#define REG_MAC_RX_STATUS_END 0x175c +#define REG_MAC_TX_STATUS_BIN 0x1760 +#define REG_MAC_TX_STATUS_END 0x17c0 + +/* DEBUG ADDR */ +#define REG_DEBUG_DATA0 0x1900 +#define REG_DEBUG_DATA1 0x1904 + +/* PHY Control Register */ +#define MII_BMCR 0x00 +#define BMCR_SPEED_SELECT_MSB 0x0040 /* bits 6,13: 10=1000, 01=100, 00=10 */ +#define BMCR_COLL_TEST_ENABLE 0x0080 /* Collision test enable */ +#define BMCR_FULL_DUPLEX 0x0100 /* FDX =1, half duplex =0 */ +#define BMCR_RESTART_AUTO_NEG 0x0200 /* Restart auto negotiation */ +#define BMCR_ISOLATE 0x0400 /* Isolate PHY from MII */ +#define BMCR_POWER_DOWN 0x0800 /* Power down */ +#define BMCR_AUTO_NEG_EN 0x1000 /* Auto Neg Enable */ +#define BMCR_SPEED_SELECT_LSB 0x2000 /* bits 6,13: 10=1000, 01=100, 00=10 */ +#define BMCR_LOOPBACK 0x4000 /* 0 = normal, 1 = loopback */ +#define BMCR_RESET 0x8000 /* 0 = normal, 1 = PHY reset */ +#define BMCR_SPEED_MASK 0x2040 +#define BMCR_SPEED_1000 0x0040 +#define BMCR_SPEED_100 0x2000 +#define BMCR_SPEED_10 0x0000 + +/* PHY Status Register */ +#define MII_BMSR 0x01 +#define BMMSR_EXTENDED_CAPS 0x0001 /* Extended register capabilities */ +#define BMSR_JABBER_DETECT 0x0002 /* Jabber Detected */ +#define BMSR_LINK_STATUS 0x0004 /* Link Status 1 = link */ +#define BMSR_AUTONEG_CAPS 0x0008 /* Auto Neg Capable */ +#define BMSR_REMOTE_FAULT 0x0010 /* Remote Fault Detect */ +#define BMSR_AUTONEG_COMPLETE 0x0020 /* Auto Neg Complete */ +#define BMSR_PREAMBLE_SUPPRESS 0x0040 /* Preamble may be suppressed */ +#define BMSR_EXTENDED_STATUS 0x0100 /* Ext. status info in Reg 0x0F */ +#define BMSR_100T2_HD_CAPS 0x0200 /* 100T2 Half Duplex Capable */ +#define BMSR_100T2_FD_CAPS 0x0400 /* 100T2 Full Duplex Capable */ +#define BMSR_10T_HD_CAPS 0x0800 /* 10T Half Duplex Capable */ +#define BMSR_10T_FD_CAPS 0x1000 /* 10T Full Duplex Capable */ +#define BMSR_100X_HD_CAPS 0x2000 /* 100X Half Duplex Capable */ +#define BMMII_SR_100X_FD_CAPS 0x4000 /* 100X Full Duplex Capable */ +#define BMMII_SR_100T4_CAPS 0x8000 /* 100T4 Capable */ + +#define MII_PHYSID1 0x02 +#define MII_PHYSID2 0x03 + +/* Autoneg Advertisement Register */ +#define MII_ADVERTISE 0x04 +#define ADVERTISE_SPEED_MASK 0x01E0 +#define ADVERTISE_DEFAULT_CAP 0x0DE0 + +/* 1000BASE-T Control Register */ +#define MII_GIGA_CR 0x09 +#define GIGA_CR_1000T_REPEATER_DTE 0x0400 /* 1=Repeater/switch device port 0=DTE device */ + +#define GIGA_CR_1000T_MS_VALUE 0x0800 /* 1=Configure PHY as Master 0=Configure PHY as Slave */ +#define GIGA_CR_1000T_MS_ENABLE 0x1000 /* 1=Master/Slave manual config value 0=Automatic Master/Slave config */ +#define GIGA_CR_1000T_TEST_MODE_NORMAL 0x0000 /* Normal Operation */ +#define GIGA_CR_1000T_TEST_MODE_1 0x2000 /* Transmit Waveform test */ +#define GIGA_CR_1000T_TEST_MODE_2 0x4000 /* Master Transmit Jitter test */ +#define GIGA_CR_1000T_TEST_MODE_3 0x6000 /* Slave Transmit Jitter test */ +#define GIGA_CR_1000T_TEST_MODE_4 0x8000 /* Transmitter Distortion test */ +#define GIGA_CR_1000T_SPEED_MASK 0x0300 +#define GIGA_CR_1000T_DEFAULT_CAP 0x0300 + +/* PHY Specific Status Register */ +#define MII_GIGA_PSSR 0x11 +#define GIGA_PSSR_SPD_DPLX_RESOLVED 0x0800 /* 1=Speed & Duplex resolved */ +#define GIGA_PSSR_DPLX 0x2000 /* 1=Duplex 0=Half Duplex */ +#define GIGA_PSSR_SPEED 0xC000 /* Speed, bits 14:15 */ +#define GIGA_PSSR_10MBS 0x0000 /* 00=10Mbs */ +#define GIGA_PSSR_100MBS 0x4000 /* 01=100Mbs */ +#define GIGA_PSSR_1000MBS 0x8000 /* 10=1000Mbs */ + +/* PHY Interrupt Enable Register */ +#define MII_IER 0x12 +#define IER_LINK_UP 0x0400 +#define IER_LINK_DOWN 0x0800 + +/* PHY Interrupt Status Register */ +#define MII_ISR 0x13 +#define ISR_LINK_UP 0x0400 +#define ISR_LINK_DOWN 0x0800 + +/* Cable-Detect-Test Control Register */ +#define MII_CDTC 0x16 +#define CDTC_EN_OFF 0 /* sc */ +#define CDTC_EN_BITS 1 +#define CDTC_PAIR_OFF 8 +#define CDTC_PAIR_BIT 2 + +/* Cable-Detect-Test Status Register */ +#define MII_CDTS 0x1C +#define CDTS_STATUS_OFF 8 +#define CDTS_STATUS_BITS 2 +#define CDTS_STATUS_NORMAL 0 +#define CDTS_STATUS_SHORT 1 +#define CDTS_STATUS_OPEN 2 +#define CDTS_STATUS_INVALID 3 + +#define MII_DBG_ADDR 0x1D +#define MII_DBG_DATA 0x1E + +#define MII_ANA_CTRL_0 0x0 +#define ANA_RESTART_CAL 0x0001 +#define ANA_MANUL_SWICH_ON_SHIFT 0x1 +#define ANA_MANUL_SWICH_ON_MASK 0xF +#define ANA_MAN_ENABLE 0x0020 +#define ANA_SEL_HSP 0x0040 +#define ANA_EN_HB 0x0080 +#define ANA_EN_HBIAS 0x0100 +#define ANA_OEN_125M 0x0200 +#define ANA_EN_LCKDT 0x0400 +#define ANA_LCKDT_PHY 0x0800 +#define ANA_AFE_MODE 0x1000 +#define ANA_VCO_SLOW 0x2000 +#define ANA_VCO_FAST 0x4000 +#define ANA_SEL_CLK125M_DSP 0x8000 + +#define MII_ANA_CTRL_4 0x4 +#define ANA_IECHO_ADJ_MASK 0xF +#define ANA_IECHO_ADJ_3_SHIFT 0 +#define ANA_IECHO_ADJ_2_SHIFT 4 +#define ANA_IECHO_ADJ_1_SHIFT 8 +#define ANA_IECHO_ADJ_0_SHIFT 12 + +#define MII_ANA_CTRL_5 0x5 +#define ANA_SERDES_CDR_BW_SHIFT 0 +#define ANA_SERDES_CDR_BW_MASK 0x3 +#define ANA_MS_PAD_DBG 0x0004 +#define ANA_SPEEDUP_DBG 0x0008 +#define ANA_SERDES_TH_LOS_SHIFT 4 +#define ANA_SERDES_TH_LOS_MASK 0x3 +#define ANA_SERDES_EN_DEEM 0x0040 +#define ANA_SERDES_TXELECIDLE 0x0080 +#define ANA_SERDES_BEACON 0x0100 +#define ANA_SERDES_HALFTXDR 0x0200 +#define ANA_SERDES_SEL_HSP 0x0400 +#define ANA_SERDES_EN_PLL 0x0800 +#define ANA_SERDES_EN 0x1000 +#define ANA_SERDES_EN_LCKDT 0x2000 + +#define MII_ANA_CTRL_11 0xB +#define ANA_PS_HIB_EN 0x8000 + +#define MII_ANA_CTRL_18 0x12 +#define ANA_TEST_MODE_10BT_01SHIFT 0 +#define ANA_TEST_MODE_10BT_01MASK 0x3 +#define ANA_LOOP_SEL_10BT 0x0004 +#define ANA_RGMII_MODE_SW 0x0008 +#define ANA_EN_LONGECABLE 0x0010 +#define ANA_TEST_MODE_10BT_2 0x0020 +#define ANA_EN_10BT_IDLE 0x0400 +#define ANA_EN_MASK_TB 0x0800 +#define ANA_TRIGGER_SEL_TIMER_SHIFT 12 +#define ANA_TRIGGER_SEL_TIMER_MASK 0x3 +#define ANA_INTERVAL_SEL_TIMER_SHIFT 14 +#define ANA_INTERVAL_SEL_TIMER_MASK 0x3 + +#define MII_ANA_CTRL_41 0x29 +#define ANA_TOP_PS_EN 0x8000 + +#define MII_ANA_CTRL_54 0x36 +#define ANA_LONG_CABLE_TH_100_SHIFT 0 +#define ANA_LONG_CABLE_TH_100_MASK 0x3F +#define ANA_DESERVED 0x0040 +#define ANA_EN_LIT_CH 0x0080 +#define ANA_SHORT_CABLE_TH_100_SHIFT 8 +#define ANA_SHORT_CABLE_TH_100_MASK 0x3F +#define ANA_BP_BAD_LINK_ACCUM 0x4000 +#define ANA_BP_SMALL_BW 0x8000 + +#endif /*_ATL1C_HW_H_*/ diff --git a/drivers/net/atl1c/atl1c_main.c b/drivers/net/atl1c/atl1c_main.c new file mode 100644 index 00000000000..deb7b53167e --- /dev/null +++ b/drivers/net/atl1c/atl1c_main.c @@ -0,0 +1,2797 @@ +/* + * Copyright(c) 2008 - 2009 Atheros Corporation. All rights reserved. + * + * Derived from Intel e1000 driver + * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "atl1c.h" + +#define ATL1C_DRV_VERSION "1.0.0.1-NAPI" +char atl1c_driver_name[] = "atl1c"; +char atl1c_driver_version[] = ATL1C_DRV_VERSION; +#define PCI_DEVICE_ID_ATTANSIC_L2C 0x1062 +#define PCI_DEVICE_ID_ATTANSIC_L1C 0x1063 +/* + * atl1c_pci_tbl - PCI Device ID Table + * + * Wildcard entries (PCI_ANY_ID) should come last + * Last entry must be all 0s + * + * { Vendor ID, Device ID, SubVendor ID, SubDevice ID, + * Class, Class Mask, private data (not used) } + */ +static struct pci_device_id atl1c_pci_tbl[] = { + {PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATTANSIC_L1C)}, + {PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATTANSIC_L2C)}, + /* required last entry */ + { 0 } +}; +MODULE_DEVICE_TABLE(pci, atl1c_pci_tbl); + +MODULE_AUTHOR("Jie Yang <jie.yang@atheros.com>"); +MODULE_DESCRIPTION("Atheros 1000M Ethernet Network Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(ATL1C_DRV_VERSION); + +static int atl1c_stop_mac(struct atl1c_hw *hw); +static void atl1c_enable_rx_ctrl(struct atl1c_hw *hw); +static void atl1c_enable_tx_ctrl(struct atl1c_hw *hw); +static void atl1c_disable_l0s_l1(struct atl1c_hw *hw); +static void atl1c_set_aspm(struct atl1c_hw *hw, bool linkup); +static void atl1c_setup_mac_ctrl(struct atl1c_adapter *adapter); +static void atl1c_clean_rx_irq(struct atl1c_adapter *adapter, u8 que, + int *work_done, int work_to_do); + +static const u16 atl1c_pay_load_size[] = { + 128, 256, 512, 1024, 2048, 4096, +}; + +static const u16 atl1c_rfd_prod_idx_regs[AT_MAX_RECEIVE_QUEUE] = +{ + REG_MB_RFD0_PROD_IDX, + REG_MB_RFD1_PROD_IDX, + REG_MB_RFD2_PROD_IDX, + REG_MB_RFD3_PROD_IDX +}; + +static const u16 atl1c_rfd_addr_lo_regs[AT_MAX_RECEIVE_QUEUE] = +{ + REG_RFD0_HEAD_ADDR_LO, + REG_RFD1_HEAD_ADDR_LO, + REG_RFD2_HEAD_ADDR_LO, + REG_RFD3_HEAD_ADDR_LO +}; + +static const u16 atl1c_rrd_addr_lo_regs[AT_MAX_RECEIVE_QUEUE] = +{ + REG_RRD0_HEAD_ADDR_LO, + REG_RRD1_HEAD_ADDR_LO, + REG_RRD2_HEAD_ADDR_LO, + REG_RRD3_HEAD_ADDR_LO +}; + +static const u32 atl1c_default_msg = NETIF_MSG_DRV | NETIF_MSG_PROBE | + NETIF_MSG_LINK | NETIF_MSG_TIMER | NETIF_MSG_IFDOWN | NETIF_MSG_IFUP; + +/* + * atl1c_init_pcie - init PCIE module + */ +static void atl1c_reset_pcie(struct atl1c_hw *hw, u32 flag) +{ + u32 data; + u32 pci_cmd; + struct pci_dev *pdev = hw->adapter->pdev; + + AT_READ_REG(hw, PCI_COMMAND, &pci_cmd); + pci_cmd &= ~PCI_COMMAND_INTX_DISABLE; + pci_cmd |= (PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | + PCI_COMMAND_IO); + AT_WRITE_REG(hw, PCI_COMMAND, pci_cmd); + + /* + * Clear any PowerSaveing Settings + */ + pci_enable_wake(pdev, PCI_D3hot, 0); + pci_enable_wake(pdev, PCI_D3cold, 0); + + /* + * Mask some pcie error bits + */ + AT_READ_REG(hw, REG_PCIE_UC_SEVERITY, &data); + data &= ~PCIE_UC_SERVRITY_DLP; + data &= ~PCIE_UC_SERVRITY_FCP; + AT_WRITE_REG(hw, REG_PCIE_UC_SEVERITY, data); + + if (flag & ATL1C_PCIE_L0S_L1_DISABLE) + atl1c_disable_l0s_l1(hw); + if (flag & ATL1C_PCIE_PHY_RESET) + AT_WRITE_REG(hw, REG_GPHY_CTRL, GPHY_CTRL_DEFAULT); + else + AT_WRITE_REG(hw, REG_GPHY_CTRL, + GPHY_CTRL_DEFAULT | GPHY_CTRL_EXT_RESET); + + msleep(1); +} + +/* + * atl1c_irq_enable - Enable default interrupt generation settings + * @adapter: board private structure + */ +static inline void atl1c_irq_enable(struct atl1c_adapter *adapter) +{ + if (likely(atomic_dec_and_test(&adapter->irq_sem))) { + AT_WRITE_REG(&adapter->hw, REG_ISR, 0x7FFFFFFF); + AT_WRITE_REG(&adapter->hw, REG_IMR, adapter->hw.intr_mask); + AT_WRITE_FLUSH(&adapter->hw); + } +} + +/* + * atl1c_irq_disable - Mask off interrupt generation on the NIC + * @adapter: board private structure + */ +static inline void atl1c_irq_disable(struct atl1c_adapter *adapter) +{ + atomic_inc(&adapter->irq_sem); + AT_WRITE_REG(&adapter->hw, REG_IMR, 0); + AT_WRITE_FLUSH(&adapter->hw); + synchronize_irq(adapter->pdev->irq); +} + +/* + * atl1c_irq_reset - reset interrupt confiure on the NIC + * @adapter: board private structure + */ +static inline void atl1c_irq_reset(struct atl1c_adapter *adapter) +{ + atomic_set(&adapter->irq_sem, 1); + atl1c_irq_enable(adapter); +} + +/* + * atl1c_phy_config - Timer Call-back + * @data: pointer to netdev cast into an unsigned long + */ +static void atl1c_phy_config(unsigned long data) +{ + struct atl1c_adapter *adapter = (struct atl1c_adapter *) data; + struct atl1c_hw *hw = &adapter->hw; + unsigned long flags; + + spin_lock_irqsave(&adapter->mdio_lock, flags); + atl1c_restart_autoneg(hw); + spin_unlock_irqrestore(&adapter->mdio_lock, flags); +} + +void atl1c_reinit_locked(struct atl1c_adapter *adapter) +{ + + WARN_ON(in_interrupt()); + atl1c_down(adapter); + atl1c_up(adapter); + clear_bit(__AT_RESETTING, &adapter->flags); +} + +static void atl1c_reset_task(struct work_struct *work) +{ + struct atl1c_adapter *adapter; + struct net_device *netdev; + + adapter = container_of(work, struct atl1c_adapter, reset_task); + netdev = adapter->netdev; + + netif_device_detach(netdev); + atl1c_down(adapter); + atl1c_up(adapter); + netif_device_attach(netdev); +} + +static void atl1c_check_link_status(struct atl1c_adapter *adapter) +{ + struct atl1c_hw *hw = &adapter->hw; + struct net_device *netdev = adapter->netdev; + struct pci_dev *pdev = adapter->pdev; + int err; + unsigned long flags; + u16 speed, duplex, phy_data; + + spin_lock_irqsave(&adapter->mdio_lock, flags); + /* MII_BMSR must read twise */ + atl1c_read_phy_reg(hw, MII_BMSR, &phy_data); + atl1c_read_phy_reg(hw, MII_BMSR, &phy_data); + spin_unlock_irqrestore(&adapter->mdio_lock, flags); + + if ((phy_data & BMSR_LSTATUS) == 0) { + /* link down */ + if (netif_carrier_ok(netdev)) { + hw->hibernate = true; + atl1c_set_aspm(hw, false); + if (atl1c_stop_mac(hw) != 0) + if (netif_msg_hw(adapter)) + dev_warn(&pdev->dev, + "stop mac failed\n"); + } + netif_carrier_off(netdev); + } else { + /* Link Up */ + hw->hibernate = false; + spin_lock_irqsave(&adapter->mdio_lock, flags); + err = atl1c_get_speed_and_duplex(hw, &speed, &duplex); + spin_unlock_irqrestore(&adapter->mdio_lock, flags); + if (unlikely(err)) + return; + /* link result is our setting */ + if (adapter->link_speed != speed || + adapter->link_duplex != duplex) { + adapter->link_speed = speed; + adapter->link_duplex = duplex; + atl1c_enable_tx_ctrl(hw); + atl1c_enable_rx_ctrl(hw); + atl1c_setup_mac_ctrl(adapter); + atl1c_set_aspm(hw, true); + if (netif_msg_link(adapter)) + dev_info(&pdev->dev, + "%s: %s NIC Link is Up<%d Mbps %s>\n", + atl1c_driver_name, netdev->name, + adapter->link_speed, + adapter->link_duplex == FULL_DUPLEX ? + "Full Duplex" : "Half Duplex"); + } + if (!netif_carrier_ok(netdev)) + netif_carrier_on(netdev); + } +} + +/* + * atl1c_link_chg_task - deal with link change event Out of interrupt context + * @netdev: network interface device structure + */ +static void atl1c_link_chg_task(struct work_struct *work) +{ + struct atl1c_adapter *adapter; + + adapter = container_of(work, struct atl1c_adapter, link_chg_task); + atl1c_check_link_status(adapter); +} + +static void atl1c_link_chg_event(struct atl1c_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + struct pci_dev *pdev = adapter->pdev; + u16 phy_data; + u16 link_up; + + spin_lock(&adapter->mdio_lock); + atl1c_read_phy_reg(&adapter->hw, MII_BMSR, &phy_data); + atl1c_read_phy_reg(&adapter->hw, MII_BMSR, &phy_data); + spin_unlock(&adapter->mdio_lock); + link_up = phy_data & BMSR_LSTATUS; + /* notify upper layer link down ASAP */ + if (!link_up) { + if (netif_carrier_ok(netdev)) { + /* old link state: Up */ + netif_carrier_off(netdev); + if (netif_msg_link(adapter)) + dev_info(&pdev->dev, + "%s: %s NIC Link is Down\n", + atl1c_driver_name, netdev->name); + adapter->link_speed = SPEED_0; + } + } + schedule_work(&adapter->link_chg_task); +} + +static void atl1c_del_timer(struct atl1c_adapter *adapter) +{ + del_timer_sync(&adapter->phy_config_timer); +} + +static void atl1c_cancel_work(struct atl1c_adapter *adapter) +{ + cancel_work_sync(&adapter->reset_task); + cancel_work_sync(&adapter->link_chg_task); +} + +/* + * atl1c_tx_timeout - Respond to a Tx Hang + * @netdev: network interface device structure + */ +static void atl1c_tx_timeout(struct net_device *netdev) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + + /* Do the reset outside of interrupt context */ + schedule_work(&adapter->reset_task); +} + +/* + * atl1c_set_multi - Multicast and Promiscuous mode set + * @netdev: network interface device structure + * + * The set_multi entry point is called whenever the multicast address + * list or the network interface flags are updated. This routine is + * responsible for configuring the hardware for proper multicast, + * promiscuous mode, and all-multi behavior. + */ +static void atl1c_set_multi(struct net_device *netdev) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + struct atl1c_hw *hw = &adapter->hw; + struct dev_mc_list *mc_ptr; + u32 mac_ctrl_data; + u32 hash_value; + + /* Check for Promiscuous and All Multicast modes */ + AT_READ_REG(hw, REG_MAC_CTRL, &mac_ctrl_data); + + if (netdev->flags & IFF_PROMISC) { + mac_ctrl_data |= MAC_CTRL_PROMIS_EN; + } else if (netdev->flags & IFF_ALLMULTI) { + mac_ctrl_data |= MAC_CTRL_MC_ALL_EN; + mac_ctrl_data &= ~MAC_CTRL_PROMIS_EN; + } else { + mac_ctrl_data &= ~(MAC_CTRL_PROMIS_EN | MAC_CTRL_MC_ALL_EN); + } + + AT_WRITE_REG(hw, REG_MAC_CTRL, mac_ctrl_data); + + /* clear the old settings from the multicast hash table */ + AT_WRITE_REG(hw, REG_RX_HASH_TABLE, 0); + AT_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, 1, 0); + + /* comoute mc addresses' hash value ,and put it into hash table */ + for (mc_ptr = netdev->mc_list; mc_ptr; mc_ptr = mc_ptr->next) { + hash_value = atl1c_hash_mc_addr(hw, mc_ptr->dmi_addr); + atl1c_hash_set(hw, hash_value); + } +} + +static void atl1c_vlan_rx_register(struct net_device *netdev, + struct vlan_group *grp) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + struct pci_dev *pdev = adapter->pdev; + u32 mac_ctrl_data = 0; + + if (netif_msg_pktdata(adapter)) + dev_dbg(&pdev->dev, "atl1c_vlan_rx_register\n"); + + atl1c_irq_disable(adapter); + + adapter->vlgrp = grp; + AT_READ_REG(&adapter->hw, REG_MAC_CTRL, &mac_ctrl_data); + + if (grp) { + /* enable VLAN tag insert/strip */ + mac_ctrl_data |= MAC_CTRL_RMV_VLAN; + } else { + /* disable VLAN tag insert/strip */ + mac_ctrl_data &= ~MAC_CTRL_RMV_VLAN; + } + + AT_WRITE_REG(&adapter->hw, REG_MAC_CTRL, mac_ctrl_data); + atl1c_irq_enable(adapter); +} + +static void atl1c_restore_vlan(struct atl1c_adapter *adapter) +{ + struct pci_dev *pdev = adapter->pdev; + + if (netif_msg_pktdata(adapter)) + dev_dbg(&pdev->dev, "atl1c_restore_vlan !"); + atl1c_vlan_rx_register(adapter->netdev, adapter->vlgrp); +} +/* + * atl1c_set_mac - Change the Ethernet Address of the NIC + * @netdev: network interface device structure + * @p: pointer to an address structure + * + * Returns 0 on success, negative on failure + */ +static int atl1c_set_mac_addr(struct net_device *netdev, void *p) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + struct sockaddr *addr = p; + + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + + if (netif_running(netdev)) + return -EBUSY; + + memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); + memcpy(adapter->hw.mac_addr, addr->sa_data, netdev->addr_len); + + atl1c_hw_set_mac_addr(&adapter->hw); + + return 0; +} + +static void atl1c_set_rxbufsize(struct atl1c_adapter *adapter, + struct net_device *dev) +{ + int mtu = dev->mtu; + + adapter->rx_buffer_len = mtu > AT_RX_BUF_SIZE ? + roundup(mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN, 8) : AT_RX_BUF_SIZE; +} +/* + * atl1c_change_mtu - Change the Maximum Transfer Unit + * @netdev: network interface device structure + * @new_mtu: new value for maximum frame size + * + * Returns 0 on success, negative on failure + */ +static int atl1c_change_mtu(struct net_device *netdev, int new_mtu) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + int old_mtu = netdev->mtu; + int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; + + if ((max_frame < ETH_ZLEN + ETH_FCS_LEN) || + (max_frame > MAX_JUMBO_FRAME_SIZE)) { + if (netif_msg_link(adapter)) + dev_warn(&adapter->pdev->dev, "invalid MTU setting\n"); + return -EINVAL; + } + /* set MTU */ + if (old_mtu != new_mtu && netif_running(netdev)) { + while (test_and_set_bit(__AT_RESETTING, &adapter->flags)) + msleep(1); + netdev->mtu = new_mtu; + adapter->hw.max_frame_size = new_mtu; + atl1c_set_rxbufsize(adapter, netdev); + atl1c_down(adapter); + atl1c_up(adapter); + clear_bit(__AT_RESETTING, &adapter->flags); + if (adapter->hw.ctrl_flags & ATL1C_FPGA_VERSION) { + u32 phy_data; + + AT_READ_REG(&adapter->hw, 0x1414, &phy_data); + phy_data |= 0x10000000; + AT_WRITE_REG(&adapter->hw, 0x1414, phy_data); + } + + } + return 0; +} + +/* + * caller should hold mdio_lock + */ +static int atl1c_mdio_read(struct net_device *netdev, int phy_id, int reg_num) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + u16 result; + + atl1c_read_phy_reg(&adapter->hw, reg_num & MDIO_REG_ADDR_MASK, &result); + return result; +} + +static void atl1c_mdio_write(struct net_device *netdev, int phy_id, + int reg_num, int val) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + + atl1c_write_phy_reg(&adapter->hw, reg_num & MDIO_REG_ADDR_MASK, val); +} + +/* + * atl1c_mii_ioctl - + * @netdev: + * @ifreq: + * @cmd: + */ +static int atl1c_mii_ioctl(struct net_device *netdev, + struct ifreq *ifr, int cmd) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + struct pci_dev *pdev = adapter->pdev; + struct mii_ioctl_data *data = if_mii(ifr); + unsigned long flags; + int retval = 0; + + if (!netif_running(netdev)) + return -EINVAL; + + spin_lock_irqsave(&adapter->mdio_lock, flags); + switch (cmd) { + case SIOCGMIIPHY: + data->phy_id = 0; + break; + + case SIOCGMIIREG: + if (!capable(CAP_NET_ADMIN)) { + retval = -EPERM; + goto out; + } + if (atl1c_read_phy_reg(&adapter->hw, data->reg_num & 0x1F, + &data->val_out)) { + retval = -EIO; + goto out; + } + break; + + case SIOCSMIIREG: + if (!capable(CAP_NET_ADMIN)) { + retval = -EPERM; + goto out; + } + if (data->reg_num & ~(0x1F)) { + retval = -EFAULT; + goto out; + } + + dev_dbg(&pdev->dev, "<atl1c_mii_ioctl> write %x %x", + data->reg_num, data->val_in); + if (atl1c_write_phy_reg(&adapter->hw, + data->reg_num, data->val_in)) { + retval = -EIO; + goto out; + } + break; + + default: + retval = -EOPNOTSUPP; + break; + } +out: + spin_unlock_irqrestore(&adapter->mdio_lock, flags); + return retval; +} + +/* + * atl1c_ioctl - + * @netdev: + * @ifreq: + * @cmd: + */ +static int atl1c_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) +{ + switch (cmd) { + case SIOCGMIIPHY: + case SIOCGMIIREG: + case SIOCSMIIREG: + return atl1c_mii_ioctl(netdev, ifr, cmd); + default: + return -EOPNOTSUPP; + } +} + +/* + * atl1c_alloc_queues - Allocate memory for all rings + * @adapter: board private structure to initialize + * + */ +static int __devinit atl1c_alloc_queues(struct atl1c_adapter *adapter) +{ + return 0; +} + +static void atl1c_set_mac_type(struct atl1c_hw *hw) +{ + switch (hw->device_id) { + case PCI_DEVICE_ID_ATTANSIC_L2C: + hw->nic_type = athr_l2c; + break; + + case PCI_DEVICE_ID_ATTANSIC_L1C: + hw->nic_type = athr_l1c; + break; + + default: + break; + } +} + +static int atl1c_setup_mac_funcs(struct atl1c_hw *hw) +{ + u32 phy_status_data; + u32 link_ctrl_data; + + atl1c_set_mac_type(hw); + AT_READ_REG(hw, REG_PHY_STATUS, &phy_status_data); + AT_READ_REG(hw, REG_LINK_CTRL, &link_ctrl_data); + + hw->ctrl_flags = ATL1C_INTR_CLEAR_ON_READ | + ATL1C_INTR_MODRT_ENABLE | + ATL1C_RX_IPV6_CHKSUM | + ATL1C_TXQ_MODE_ENHANCE; + if (link_ctrl_data & LINK_CTRL_L0S_EN) + hw->ctrl_flags |= ATL1C_ASPM_L0S_SUPPORT; + if (link_ctrl_data & LINK_CTRL_L1_EN) + hw->ctrl_flags |= ATL1C_ASPM_L1_SUPPORT; + + if (hw->nic_type == athr_l1c) { + hw->ctrl_flags |= ATL1C_ASPM_CTRL_MON; + hw->ctrl_flags |= ATL1C_LINK_CAP_1000M; + } + return 0; +} +/* + * atl1c_sw_init - Initialize general software structures (struct atl1c_adapter) + * @adapter: board private structure to initialize + * + * atl1c_sw_init initializes the Adapter private data structure. + * Fields are initialized based on PCI device information and + * OS network device settings (MTU size). + */ +static int __devinit atl1c_sw_init(struct atl1c_adapter *adapter) +{ + struct atl1c_hw *hw = &adapter->hw; + struct pci_dev *pdev = adapter->pdev; + + adapter->wol = 0; + adapter->link_speed = SPEED_0; + adapter->link_duplex = FULL_DUPLEX; + adapter->num_rx_queues = AT_DEF_RECEIVE_QUEUE; + adapter->tpd_ring[0].count = 1024; + adapter->rfd_ring[0].count = 512; + + hw->vendor_id = pdev->vendor; + hw->device_id = pdev->device; + hw->subsystem_vendor_id = pdev->subsystem_vendor; + hw->subsystem_id = pdev->subsystem_device; + + /* before link up, we assume hibernate is true */ + hw->hibernate = true; + hw->media_type = MEDIA_TYPE_AUTO_SENSOR; + if (atl1c_setup_mac_funcs(hw) != 0) { + dev_err(&pdev->dev, "set mac function pointers failed\n"); + return -1; + } + hw->intr_mask = IMR_NORMAL_MASK; + hw->phy_configured = false; + hw->preamble_len = 7; + hw->max_frame_size = adapter->netdev->mtu; + if (adapter->num_rx_queues < 2) { + hw->rss_type = atl1c_rss_disable; + hw->rss_mode = atl1c_rss_mode_disable; + } else { + hw->rss_type = atl1c_rss_ipv4; + hw->rss_mode = atl1c_rss_mul_que_mul_int; + hw->rss_hash_bits = 16; + } + hw->autoneg_advertised = ADVERTISED_Autoneg; + hw->indirect_tab = 0xE4E4E4E4; + hw->base_cpu = 0; + + hw->ict = 50000; /* 100ms */ + hw->smb_timer = 200000; /* 400ms */ + hw->cmb_tpd = 4; + hw->cmb_tx_timer = 1; /* 2 us */ + hw->rx_imt = 200; + hw->tx_imt = 1000; + + hw->tpd_burst = 5; + hw->rfd_burst = 8; + hw->dma_order = atl1c_dma_ord_out; + hw->dmar_block = atl1c_dma_req_1024; + hw->dmaw_block = atl1c_dma_req_1024; + hw->dmar_dly_cnt = 15; + hw->dmaw_dly_cnt = 4; + + if (atl1c_alloc_queues(adapter)) { + dev_err(&pdev->dev, "Unable to allocate memory for queues\n"); + return -ENOMEM; + } + /* TODO */ + atl1c_set_rxbufsize(adapter, adapter->netdev); + atomic_set(&adapter->irq_sem, 1); + spin_lock_init(&adapter->mdio_lock); + spin_lock_init(&adapter->tx_lock); + set_bit(__AT_DOWN, &adapter->flags); + + return 0; +} + +/* + * atl1c_clean_tx_ring - Free Tx-skb + * @adapter: board private structure + */ +static void atl1c_clean_tx_ring(struct atl1c_adapter *adapter, + enum atl1c_trans_queue type) +{ + struct atl1c_tpd_ring *tpd_ring = &adapter->tpd_ring[type]; + struct atl1c_buffer *buffer_info; + struct pci_dev *pdev = adapter->pdev; + u16 index, ring_count; + + ring_count = tpd_ring->count; + for (index = 0; index < ring_count; index++) { + buffer_info = &tpd_ring->buffer_info[index]; + if (buffer_info->state == ATL1_BUFFER_FREE) + continue; + if (buffer_info->dma) + pci_unmap_single(pdev, buffer_info->dma, + buffer_info->length, + PCI_DMA_TODEVICE); + if (buffer_info->skb) + dev_kfree_skb(buffer_info->skb); + buffer_info->dma = 0; + buffer_info->skb = NULL; + buffer_info->state = ATL1_BUFFER_FREE; + } + + /* Zero out Tx-buffers */ + memset(tpd_ring->desc, 0, sizeof(struct atl1c_tpd_desc) * + ring_count); + atomic_set(&tpd_ring->next_to_clean, 0); + tpd_ring->next_to_use = 0; +} + +/* + * atl1c_clean_rx_ring - Free rx-reservation skbs + * @adapter: board private structure + */ +static void atl1c_clean_rx_ring(struct atl1c_adapter *adapter) +{ + struct atl1c_rfd_ring *rfd_ring = adapter->rfd_ring; + struct atl1c_rrd_ring *rrd_ring = adapter->rrd_ring; + struct atl1c_buffer *buffer_info; + struct pci_dev *pdev = adapter->pdev; + int i, j; + + for (i = 0; i < adapter->num_rx_queues; i++) { + for (j = 0; j < rfd_ring[i].count; j++) { + buffer_info = &rfd_ring[i].buffer_info[j]; + if (buffer_info->state == ATL1_BUFFER_FREE) + continue; + if (buffer_info->dma) + pci_unmap_single(pdev, buffer_info->dma, + buffer_info->length, + PCI_DMA_FROMDEVICE); + if (buffer_info->skb) + dev_kfree_skb(buffer_info->skb); + buffer_info->state = ATL1_BUFFER_FREE; + buffer_info->skb = NULL; + } + /* zero out the descriptor ring */ + memset(rfd_ring[i].desc, 0, rfd_ring[i].size); + rfd_ring[i].next_to_clean = 0; + rfd_ring[i].next_to_use = 0; + rrd_ring[i].next_to_use = 0; + rrd_ring[i].next_to_clean = 0; + } +} + +/* + * Read / Write Ptr Initialize: + */ +static void atl1c_init_ring_ptrs(struct atl1c_adapter *adapter) +{ + struct atl1c_tpd_ring *tpd_ring = adapter->tpd_ring; + struct atl1c_rfd_ring *rfd_ring = adapter->rfd_ring; + struct atl1c_rrd_ring *rrd_ring = adapter->rrd_ring; + struct atl1c_buffer *buffer_info; + int i, j; + + for (i = 0; i < AT_MAX_TRANSMIT_QUEUE; i++) { + tpd_ring[i].next_to_use = 0; + atomic_set(&tpd_ring[i].next_to_clean, 0); + buffer_info = tpd_ring[i].buffer_info; + for (j = 0; j < tpd_ring->count; j++) + buffer_info[i].state = ATL1_BUFFER_FREE; + } + for (i = 0; i < adapter->num_rx_queues; i++) { + rfd_ring[i].next_to_use = 0; + rfd_ring[i].next_to_clean = 0; + rrd_ring[i].next_to_use = 0; + rrd_ring[i].next_to_clean = 0; + for (j = 0; j < rfd_ring[i].count; j++) { + buffer_info = &rfd_ring[i].buffer_info[j]; + buffer_info->state = ATL1_BUFFER_FREE; + } + } +} + +/* + * atl1c_free_ring_resources - Free Tx / RX descriptor Resources + * @adapter: board private structure + * + * Free all transmit software resources + */ +static void atl1c_free_ring_resources(struct atl1c_adapter *adapter) +{ + struct pci_dev *pdev = adapter->pdev; + + pci_free_consistent(pdev, adapter->ring_header.size, + adapter->ring_header.desc, + adapter->ring_header.dma); + adapter->ring_header.desc = NULL; + + /* Note: just free tdp_ring.buffer_info, + * it contain rfd_ring.buffer_info, do not double free */ + if (adapter->tpd_ring[0].buffer_info) { + kfree(adapter->tpd_ring[0].buffer_info); + adapter->tpd_ring[0].buffer_info = NULL; + } +} + +/* + * atl1c_setup_mem_resources - allocate Tx / RX descriptor resources + * @adapter: board private structure + * + * Return 0 on success, negative on failure + */ +static int atl1c_setup_ring_resources(struct atl1c_adapter *adapter) +{ + struct pci_dev *pdev = adapter->pdev; + struct atl1c_tpd_ring *tpd_ring = adapter->tpd_ring; + struct atl1c_rfd_ring *rfd_ring = adapter->rfd_ring; + struct atl1c_rrd_ring *rrd_ring = adapter->rrd_ring; + struct atl1c_ring_header *ring_header = &adapter->ring_header; + int num_rx_queues = adapter->num_rx_queues; + int size; + int i; + int count = 0; + int rx_desc_count = 0; + u32 offset = 0; + + rrd_ring[0].count = rfd_ring[0].count; + for (i = 1; i < AT_MAX_TRANSMIT_QUEUE; i++) + tpd_ring[i].count = tpd_ring[0].count; + + for (i = 1; i < adapter->num_rx_queues; i++) + rfd_ring[i].count = rrd_ring[i].count = rfd_ring[0].count; + + /* 2 tpd queue, one high priority queue, + * another normal priority queue */ + size = sizeof(struct atl1c_buffer) * (tpd_ring->count * 2 + + rfd_ring->count * num_rx_queues); + tpd_ring->buffer_info = kzalloc(size, GFP_KERNEL); + if (unlikely(!tpd_ring->buffer_info)) { + dev_err(&pdev->dev, "kzalloc failed, size = %d\n", + size); + goto err_nomem; + } + for (i = 0; i < AT_MAX_TRANSMIT_QUEUE; i++) { + tpd_ring[i].buffer_info = + (struct atl1c_buffer *) (tpd_ring->buffer_info + count); + count += tpd_ring[i].count; + } + + for (i = 0; i < num_rx_queues; i++) { + rfd_ring[i].buffer_info = + (struct atl1c_buffer *) (tpd_ring->buffer_info + count); + count += rfd_ring[i].count; + rx_desc_count += rfd_ring[i].count; + } + /* + * real ring DMA buffer + * each ring/block may need up to 8 bytes for alignment, hence the + * additional bytes tacked onto the end. + */ + ring_header->size = size = + sizeof(struct atl1c_tpd_desc) * tpd_ring->count * 2 + + sizeof(struct atl1c_rx_free_desc) * rx_desc_count + + sizeof(struct atl1c_recv_ret_status) * rx_desc_count + + sizeof(struct atl1c_hw_stats) + + 8 * 4 + 8 * 2 * num_rx_queues; + + ring_header->desc = pci_alloc_consistent(pdev, ring_header->size, + &ring_header->dma); + if (unlikely(!ring_header->desc)) { + dev_err(&pdev->dev, "pci_alloc_consistend failed\n"); + goto err_nomem; + } + memset(ring_header->desc, 0, ring_header->size); + /* init TPD ring */ + + tpd_ring[0].dma = roundup(ring_header->dma, 8); + offset = tpd_ring[0].dma - ring_header->dma; + for (i = 0; i < AT_MAX_TRANSMIT_QUEUE; i++) { + tpd_ring[i].dma = ring_header->dma + offset; + tpd_ring[i].desc = (u8 *) ring_header->desc + offset; + tpd_ring[i].size = + sizeof(struct atl1c_tpd_desc) * tpd_ring[i].count; + offset += roundup(tpd_ring[i].size, 8); + } + /* init RFD ring */ + for (i = 0; i < num_rx_queues; i++) { + rfd_ring[i].dma = ring_header->dma + offset; + rfd_ring[i].desc = (u8 *) ring_header->desc + offset; + rfd_ring[i].size = sizeof(struct atl1c_rx_free_desc) * + rfd_ring[i].count; + offset += roundup(rfd_ring[i].size, 8); + } + + /* init RRD ring */ + for (i = 0; i < num_rx_queues; i++) { + rrd_ring[i].dma = ring_header->dma + offset; + rrd_ring[i].desc = (u8 *) ring_header->desc + offset; + rrd_ring[i].size = sizeof(struct atl1c_recv_ret_status) * + rrd_ring[i].count; + offset += roundup(rrd_ring[i].size, 8); + } + + adapter->smb.dma = ring_header->dma + offset; + adapter->smb.smb = (u8 *)ring_header->desc + offset; + return 0; + +err_nomem: + kfree(tpd_ring->buffer_info); + return -ENOMEM; +} + +static void atl1c_configure_des_ring(struct atl1c_adapter *adapter) +{ + struct atl1c_hw *hw = &adapter->hw; + struct atl1c_rfd_ring *rfd_ring = (struct atl1c_rfd_ring *) + adapter->rfd_ring; + struct atl1c_rrd_ring *rrd_ring = (struct atl1c_rrd_ring *) + adapter->rrd_ring; + struct atl1c_tpd_ring *tpd_ring = (struct atl1c_tpd_ring *) + adapter->tpd_ring; + struct atl1c_cmb *cmb = (struct atl1c_cmb *) &adapter->cmb; + struct atl1c_smb *smb = (struct atl1c_smb *) &adapter->smb; + int i; + + /* TPD */ + AT_WRITE_REG(hw, REG_TX_BASE_ADDR_HI, + (u32)((tpd_ring[atl1c_trans_normal].dma & + AT_DMA_HI_ADDR_MASK) >> 32)); + /* just enable normal priority TX queue */ + AT_WRITE_REG(hw, REG_NTPD_HEAD_ADDR_LO, + (u32)(tpd_ring[atl1c_trans_normal].dma & + AT_DMA_LO_ADDR_MASK)); + AT_WRITE_REG(hw, REG_HTPD_HEAD_ADDR_LO, + (u32)(tpd_ring[atl1c_trans_high].dma & + AT_DMA_LO_ADDR_MASK)); + AT_WRITE_REG(hw, REG_TPD_RING_SIZE, + (u32)(tpd_ring[0].count & TPD_RING_SIZE_MASK)); + + + /* RFD */ + AT_WRITE_REG(hw, REG_RX_BASE_ADDR_HI, + (u32)((rfd_ring[0].dma & AT_DMA_HI_ADDR_MASK) >> 32)); + for (i = 0; i < adapter->num_rx_queues; i++) + AT_WRITE_REG(hw, atl1c_rfd_addr_lo_regs[i], + (u32)(rfd_ring[i].dma & AT_DMA_LO_ADDR_MASK)); + + AT_WRITE_REG(hw, REG_RFD_RING_SIZE, + rfd_ring[0].count & RFD_RING_SIZE_MASK); + AT_WRITE_REG(hw, REG_RX_BUF_SIZE, + adapter->rx_buffer_len & RX_BUF_SIZE_MASK); + + /* RRD */ + for (i = 0; i < adapter->num_rx_queues; i++) + AT_WRITE_REG(hw, atl1c_rrd_addr_lo_regs[i], + (u32)(rrd_ring[i].dma & AT_DMA_LO_ADDR_MASK)); + AT_WRITE_REG(hw, REG_RRD_RING_SIZE, + (rrd_ring[0].count & RRD_RING_SIZE_MASK)); + + /* CMB */ + AT_WRITE_REG(hw, REG_CMB_BASE_ADDR_LO, cmb->dma & AT_DMA_LO_ADDR_MASK); + + /* SMB */ + AT_WRITE_REG(hw, REG_SMB_BASE_ADDR_HI, + (u32)((smb->dma & AT_DMA_HI_ADDR_MASK) >> 32)); + AT_WRITE_REG(hw, REG_SMB_BASE_ADDR_LO, + (u32)(smb->dma & AT_DMA_LO_ADDR_MASK)); + /* Load all of base address above */ + AT_WRITE_REG(hw, REG_LOAD_PTR, 1); +} + +static void atl1c_configure_tx(struct atl1c_adapter *adapter) +{ + struct atl1c_hw *hw = &adapter->hw; + u32 dev_ctrl_data; + u32 max_pay_load; + u16 tx_offload_thresh; + u32 txq_ctrl_data; + u32 extra_size = 0; /* Jumbo frame threshold in QWORD unit */ + + extra_size = ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN; + tx_offload_thresh = MAX_TX_OFFLOAD_THRESH; + AT_WRITE_REG(hw, REG_TX_TSO_OFFLOAD_THRESH, + (tx_offload_thresh >> 3) & TX_TSO_OFFLOAD_THRESH_MASK); + AT_READ_REG(hw, REG_DEVICE_CTRL, &dev_ctrl_data); + max_pay_load = (dev_ctrl_data >> DEVICE_CTRL_MAX_PAYLOAD_SHIFT) & + DEVICE_CTRL_MAX_PAYLOAD_MASK; + hw->dmaw_block = min(max_pay_load, hw->dmaw_block); + max_pay_load = (dev_ctrl_data >> DEVICE_CTRL_MAX_RREQ_SZ_SHIFT) & + DEVICE_CTRL_MAX_RREQ_SZ_MASK; + hw->dmar_block = min(max_pay_load, hw->dmar_block); + + txq_ctrl_data = (hw->tpd_burst & TXQ_NUM_TPD_BURST_MASK) << + TXQ_NUM_TPD_BURST_SHIFT; + if (hw->ctrl_flags & ATL1C_TXQ_MODE_ENHANCE) + txq_ctrl_data |= TXQ_CTRL_ENH_MODE; + txq_ctrl_data |= (atl1c_pay_load_size[hw->dmar_block] & + TXQ_TXF_BURST_NUM_MASK) << TXQ_TXF_BURST_NUM_SHIFT; + + AT_WRITE_REG(hw, REG_TXQ_CTRL, txq_ctrl_data); +} + +static void atl1c_configure_rx(struct atl1c_adapter *adapter) +{ + struct atl1c_hw *hw = &adapter->hw; + u32 rxq_ctrl_data; + + rxq_ctrl_data = (hw->rfd_burst & RXQ_RFD_BURST_NUM_MASK) << + RXQ_RFD_BURST_NUM_SHIFT; + + if (hw->ctrl_flags & ATL1C_RX_IPV6_CHKSUM) + rxq_ctrl_data |= IPV6_CHKSUM_CTRL_EN; + if (hw->rss_type == atl1c_rss_ipv4) + rxq_ctrl_data |= RSS_HASH_IPV4; + if (hw->rss_type == atl1c_rss_ipv4_tcp) + rxq_ctrl_data |= RSS_HASH_IPV4_TCP; + if (hw->rss_type == atl1c_rss_ipv6) + rxq_ctrl_data |= RSS_HASH_IPV6; + if (hw->rss_type == atl1c_rss_ipv6_tcp) + rxq_ctrl_data |= RSS_HASH_IPV6_TCP; + if (hw->rss_type != atl1c_rss_disable) + rxq_ctrl_data |= RRS_HASH_CTRL_EN; + + rxq_ctrl_data |= (hw->rss_mode & RSS_MODE_MASK) << + RSS_MODE_SHIFT; + rxq_ctrl_data |= (hw->rss_hash_bits & RSS_HASH_BITS_MASK) << + RSS_HASH_BITS_SHIFT; + if (hw->ctrl_flags & ATL1C_ASPM_CTRL_MON) + rxq_ctrl_data |= (ASPM_THRUPUT_LIMIT_100M & + ASPM_THRUPUT_LIMIT_MASK) << ASPM_THRUPUT_LIMIT_SHIFT; + + AT_WRITE_REG(hw, REG_RXQ_CTRL, rxq_ctrl_data); +} + +static void atl1c_configure_rss(struct atl1c_adapter *adapter) +{ + struct atl1c_hw *hw = &adapter->hw; + + AT_WRITE_REG(hw, REG_IDT_TABLE, hw->indirect_tab); + AT_WRITE_REG(hw, REG_BASE_CPU_NUMBER, hw->base_cpu); +} + +static void atl1c_configure_dma(struct atl1c_adapter *adapter) +{ + struct atl1c_hw *hw = &adapter->hw; + u32 dma_ctrl_data; + + dma_ctrl_data = DMA_CTRL_DMAR_REQ_PRI; + if (hw->ctrl_flags & ATL1C_CMB_ENABLE) + dma_ctrl_data |= DMA_CTRL_CMB_EN; + if (hw->ctrl_flags & ATL1C_SMB_ENABLE) + dma_ctrl_data |= DMA_CTRL_SMB_EN; + else + dma_ctrl_data |= MAC_CTRL_SMB_DIS; + + switch (hw->dma_order) { + case atl1c_dma_ord_in: + dma_ctrl_data |= DMA_CTRL_DMAR_IN_ORDER; + break; + case atl1c_dma_ord_enh: + dma_ctrl_data |= DMA_CTRL_DMAR_ENH_ORDER; + break; + case atl1c_dma_ord_out: + dma_ctrl_data |= DMA_CTRL_DMAR_OUT_ORDER; + break; + default: + break; + } + + dma_ctrl_data |= (((u32)hw->dmar_block) & DMA_CTRL_DMAR_BURST_LEN_MASK) + << DMA_CTRL_DMAR_BURST_LEN_SHIFT; + dma_ctrl_data |= (((u32)hw->dmaw_block) & DMA_CTRL_DMAW_BURST_LEN_MASK) + << DMA_CTRL_DMAW_BURST_LEN_SHIFT; + dma_ctrl_data |= (((u32)hw->dmar_dly_cnt) & DMA_CTRL_DMAR_DLY_CNT_MASK) + << DMA_CTRL_DMAR_DLY_CNT_SHIFT; + dma_ctrl_data |= (((u32)hw->dmaw_dly_cnt) & DMA_CTRL_DMAW_DLY_CNT_MASK) + << DMA_CTRL_DMAW_DLY_CNT_SHIFT; + + AT_WRITE_REG(hw, REG_DMA_CTRL, dma_ctrl_data); +} + +/* + * Stop the mac, transmit and receive units + * hw - Struct containing variables accessed by shared code + * return : 0 or idle status (if error) + */ +static int atl1c_stop_mac(struct atl1c_hw *hw) +{ + u32 data; + int timeout; + + AT_READ_REG(hw, REG_RXQ_CTRL, &data); + data &= ~(RXQ1_CTRL_EN | RXQ2_CTRL_EN | + RXQ3_CTRL_EN | RXQ_CTRL_EN); + AT_WRITE_REG(hw, REG_RXQ_CTRL, data); + + AT_READ_REG(hw, REG_TXQ_CTRL, &data); + data &= ~TXQ_CTRL_EN; + AT_WRITE_REG(hw, REG_TWSI_CTRL, data); + + for (timeout = 0; timeout < AT_HW_MAX_IDLE_DELAY; timeout++) { + AT_READ_REG(hw, REG_IDLE_STATUS, &data); + if ((data & (IDLE_STATUS_RXQ_NO_IDLE | + IDLE_STATUS_TXQ_NO_IDLE)) == 0) + break; + msleep(1); + } + + AT_READ_REG(hw, REG_MAC_CTRL, &data); + data &= ~(MAC_CTRL_TX_EN | MAC_CTRL_RX_EN); + AT_WRITE_REG(hw, REG_MAC_CTRL, data); + + for (timeout = 0; timeout < AT_HW_MAX_IDLE_DELAY; timeout++) { + AT_READ_REG(hw, REG_IDLE_STATUS, &data); + if ((data & IDLE_STATUS_MASK) == 0) + return 0; + msleep(1); + } + return data; +} + +static void atl1c_enable_rx_ctrl(struct atl1c_hw *hw) +{ + u32 data; + + AT_READ_REG(hw, REG_RXQ_CTRL, &data); + switch (hw->adapter->num_rx_queues) { + case 4: + data |= (RXQ3_CTRL_EN | RXQ2_CTRL_EN | RXQ1_CTRL_EN); + break; + case 3: + data |= (RXQ2_CTRL_EN | RXQ1_CTRL_EN); + break; + case 2: + data |= RXQ1_CTRL_EN; + break; + default: + break; + } + data |= RXQ_CTRL_EN; + AT_WRITE_REG(hw, REG_RXQ_CTRL, data); +} + +static void atl1c_enable_tx_ctrl(struct atl1c_hw *hw) +{ + u32 data; + + AT_READ_REG(hw, REG_TXQ_CTRL, &data); + data |= TXQ_CTRL_EN; + AT_WRITE_REG(hw, REG_TXQ_CTRL, data); +} + +/* + * Reset the transmit and receive units; mask and clear all interrupts. + * hw - Struct containing variables accessed by shared code + * return : 0 or idle status (if error) + */ +static int atl1c_reset_mac(struct atl1c_hw *hw) +{ + struct atl1c_adapter *adapter = (struct atl1c_adapter *)hw->adapter; + struct pci_dev *pdev = adapter->pdev; + u32 idle_status_data = 0; + int timeout = 0; + int ret; + + AT_WRITE_REG(hw, REG_IMR, 0); + AT_WRITE_REG(hw, REG_ISR, ISR_DIS_INT); + + ret = atl1c_stop_mac(hw); + if (ret) + return ret; + /* + * Issue Soft Reset to the MAC. This will reset the chip's + * transmit, receive, DMA. It will not effect + * the current PCI configuration. The global reset bit is self- + * clearing, and should clear within a microsecond. + */ + AT_WRITE_REGW(hw, REG_MASTER_CTRL, MASTER_CTRL_SOFT_RST); + AT_WRITE_FLUSH(hw); + msleep(10); + /* Wait at least 10ms for All module to be Idle */ + for (timeout = 0; timeout < AT_HW_MAX_IDLE_DELAY; timeout++) { + AT_READ_REG(hw, REG_IDLE_STATUS, &idle_status_data); + if ((idle_status_data & IDLE_STATUS_MASK) == 0) + break; + msleep(1); + } + if (timeout >= AT_HW_MAX_IDLE_DELAY) { + dev_err(&pdev->dev, + "MAC state machine cann't be idle since" + " disabled for 10ms second\n"); + return -1; + } + return 0; +} + +static void atl1c_disable_l0s_l1(struct atl1c_hw *hw) +{ + u32 pm_ctrl_data; + + AT_READ_REG(hw, REG_PM_CTRL, &pm_ctrl_data); + pm_ctrl_data &= ~(PM_CTRL_L1_ENTRY_TIMER_MASK << + PM_CTRL_L1_ENTRY_TIMER_SHIFT); + pm_ctrl_data &= ~PM_CTRL_CLK_SWH_L1; + pm_ctrl_data &= ~PM_CTRL_ASPM_L0S_EN; + pm_ctrl_data &= ~PM_CTRL_ASPM_L1_EN; + pm_ctrl_data &= ~PM_CTRL_MAC_ASPM_CHK; + pm_ctrl_data &= ~PM_CTRL_SERDES_PD_EX_L1; + + pm_ctrl_data |= PM_CTRL_SERDES_BUDS_RX_L1_EN; + pm_ctrl_data |= PM_CTRL_SERDES_PLL_L1_EN; + pm_ctrl_data |= PM_CTRL_SERDES_L1_EN; + AT_WRITE_REG(hw, REG_PM_CTRL, pm_ctrl_data); +} + +/* + * Set ASPM state. + * Enable/disable L0s/L1 depend on link state. + */ +static void atl1c_set_aspm(struct atl1c_hw *hw, bool linkup) +{ + u32 pm_ctrl_data; + + AT_READ_REG(hw, REG_PM_CTRL, &pm_ctrl_data); + + pm_ctrl_data &= PM_CTRL_SERDES_PD_EX_L1; + pm_ctrl_data |= ~PM_CTRL_SERDES_BUDS_RX_L1_EN; + pm_ctrl_data |= ~PM_CTRL_SERDES_L1_EN; + pm_ctrl_data &= ~(PM_CTRL_L1_ENTRY_TIMER_MASK << + PM_CTRL_L1_ENTRY_TIMER_SHIFT); + + pm_ctrl_data |= PM_CTRL_MAC_ASPM_CHK; + + if (linkup) { + pm_ctrl_data |= PM_CTRL_SERDES_PLL_L1_EN; + pm_ctrl_data &= ~PM_CTRL_CLK_SWH_L1; + + if (hw->ctrl_flags & ATL1C_ASPM_L1_SUPPORT) { + pm_ctrl_data |= AT_ASPM_L1_TIMER << + PM_CTRL_L1_ENTRY_TIMER_SHIFT; + pm_ctrl_data |= PM_CTRL_ASPM_L1_EN; + } else + pm_ctrl_data &= ~PM_CTRL_ASPM_L1_EN; + + if (hw->ctrl_flags & ATL1C_ASPM_L0S_SUPPORT) + pm_ctrl_data |= PM_CTRL_ASPM_L0S_EN; + else + pm_ctrl_data &= ~PM_CTRL_ASPM_L0S_EN; + + } else { + pm_ctrl_data &= ~PM_CTRL_ASPM_L0S_EN; + pm_ctrl_data &= ~PM_CTRL_SERDES_PLL_L1_EN; + + pm_ctrl_data |= PM_CTRL_CLK_SWH_L1; + + if (hw->ctrl_flags & ATL1C_ASPM_L1_SUPPORT) + pm_ctrl_data |= PM_CTRL_ASPM_L1_EN; + else + pm_ctrl_data &= ~PM_CTRL_ASPM_L1_EN; + } + + AT_WRITE_REG(hw, REG_PM_CTRL, pm_ctrl_data); +} + +static void atl1c_setup_mac_ctrl(struct atl1c_adapter *adapter) +{ + struct atl1c_hw *hw = &adapter->hw; + struct net_device *netdev = adapter->netdev; + u32 mac_ctrl_data; + + mac_ctrl_data = MAC_CTRL_TX_EN | MAC_CTRL_RX_EN; + mac_ctrl_data |= (MAC_CTRL_TX_FLOW | MAC_CTRL_RX_FLOW); + + if (adapter->link_duplex == FULL_DUPLEX) { + hw->mac_duplex = true; + mac_ctrl_data |= MAC_CTRL_DUPLX; + } + + if (adapter->link_speed == SPEED_1000) + hw->mac_speed = atl1c_mac_speed_1000; + else + hw->mac_speed = atl1c_mac_speed_10_100; + + mac_ctrl_data |= (hw->mac_speed & MAC_CTRL_SPEED_MASK) << + MAC_CTRL_SPEED_SHIFT; + + mac_ctrl_data |= (MAC_CTRL_ADD_CRC | MAC_CTRL_PAD); + mac_ctrl_data |= ((hw->preamble_len & MAC_CTRL_PRMLEN_MASK) << + MAC_CTRL_PRMLEN_SHIFT); + + if (adapter->vlgrp) + mac_ctrl_data |= MAC_CTRL_RMV_VLAN; + + mac_ctrl_data |= MAC_CTRL_BC_EN; + if (netdev->flags & IFF_PROMISC) + mac_ctrl_data |= MAC_CTRL_PROMIS_EN; + if (netdev->flags & IFF_ALLMULTI) + mac_ctrl_data |= MAC_CTRL_MC_ALL_EN; + + mac_ctrl_data |= MAC_CTRL_SINGLE_PAUSE_EN; + AT_WRITE_REG(hw, REG_MAC_CTRL, mac_ctrl_data); +} + +/* + * atl1c_configure - Configure Transmit&Receive Unit after Reset + * @adapter: board private structure + * + * Configure the Tx /Rx unit of the MAC after a reset. + */ +static int atl1c_configure(struct atl1c_adapter *adapter) +{ + struct atl1c_hw *hw = &adapter->hw; + u32 master_ctrl_data = 0; + u32 intr_modrt_data; + + /* clear interrupt status */ + AT_WRITE_REG(hw, REG_ISR, 0xFFFFFFFF); + /* Clear any WOL status */ + AT_WRITE_REG(hw, REG_WOL_CTRL, 0); + /* set Interrupt Clear Timer + * HW will enable self to assert interrupt event to system after + * waiting x-time for software to notify it accept interrupt. + */ + AT_WRITE_REG(hw, REG_INT_RETRIG_TIMER, + hw->ict & INT_RETRIG_TIMER_MASK); + + atl1c_configure_des_ring(adapter); + + if (hw->ctrl_flags & ATL1C_INTR_MODRT_ENABLE) { + intr_modrt_data = (hw->tx_imt & IRQ_MODRT_TIMER_MASK) << + IRQ_MODRT_TX_TIMER_SHIFT; + intr_modrt_data |= (hw->rx_imt & IRQ_MODRT_TIMER_MASK) << + IRQ_MODRT_RX_TIMER_SHIFT; + AT_WRITE_REG(hw, REG_IRQ_MODRT_TIMER_INIT, intr_modrt_data); + master_ctrl_data |= + MASTER_CTRL_TX_ITIMER_EN | MASTER_CTRL_RX_ITIMER_EN; + } + + if (hw->ctrl_flags & ATL1C_INTR_CLEAR_ON_READ) + master_ctrl_data |= MASTER_CTRL_INT_RDCLR; + + AT_WRITE_REG(hw, REG_MASTER_CTRL, master_ctrl_data); + + if (hw->ctrl_flags & ATL1C_CMB_ENABLE) { + AT_WRITE_REG(hw, REG_CMB_TPD_THRESH, + hw->cmb_tpd & CMB_TPD_THRESH_MASK); + AT_WRITE_REG(hw, REG_CMB_TX_TIMER, + hw->cmb_tx_timer & CMB_TX_TIMER_MASK); + } + + if (hw->ctrl_flags & ATL1C_SMB_ENABLE) + AT_WRITE_REG(hw, REG_SMB_STAT_TIMER, + hw->smb_timer & SMB_STAT_TIMER_MASK); + /* set MTU */ + AT_WRITE_REG(hw, REG_MTU, hw->max_frame_size + ETH_HLEN + + VLAN_HLEN + ETH_FCS_LEN); + /* HDS, disable */ + AT_WRITE_REG(hw, REG_HDS_CTRL, 0); + + atl1c_configure_tx(adapter); + atl1c_configure_rx(adapter); + atl1c_configure_rss(adapter); + atl1c_configure_dma(adapter); + + return 0; +} + +static void atl1c_update_hw_stats(struct atl1c_adapter *adapter) +{ + u16 hw_reg_addr = 0; + unsigned long *stats_item = NULL; + u32 data; + + /* update rx status */ + hw_reg_addr = REG_MAC_RX_STATUS_BIN; + stats_item = &adapter->hw_stats.rx_ok; + while (hw_reg_addr <= REG_MAC_RX_STATUS_END) { + AT_READ_REG(&adapter->hw, hw_reg_addr, &data); + *stats_item += data; + stats_item++; + hw_reg_addr += 4; + } +/* update tx status */ + hw_reg_addr = REG_MAC_TX_STATUS_BIN; + stats_item = &adapter->hw_stats.tx_ok; + while (hw_reg_addr <= REG_MAC_TX_STATUS_END) { + AT_READ_REG(&adapter->hw, hw_reg_addr, &data); + *stats_item += data; + stats_item++; + hw_reg_addr += 4; + } +} + +/* + * atl1c_get_stats - Get System Network Statistics + * @netdev: network interface device structure + * + * Returns the address of the device statistics structure. + * The statistics are actually updated from the timer callback. + */ +static struct net_device_stats *atl1c_get_stats(struct net_device *netdev) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + struct atl1c_hw_stats *hw_stats = &adapter->hw_stats; + struct net_device_stats *net_stats = &adapter->net_stats; + + atl1c_update_hw_stats(adapter); + net_stats->rx_packets = hw_stats->rx_ok; + net_stats->tx_packets = hw_stats->tx_ok; + net_stats->rx_bytes = hw_stats->rx_byte_cnt; + net_stats->tx_bytes = hw_stats->tx_byte_cnt; + net_stats->multicast = hw_stats->rx_mcast; + net_stats->collisions = hw_stats->tx_1_col + + hw_stats->tx_2_col * 2 + + hw_stats->tx_late_col + hw_stats->tx_abort_col; + net_stats->rx_errors = hw_stats->rx_frag + hw_stats->rx_fcs_err + + hw_stats->rx_len_err + hw_stats->rx_sz_ov + + hw_stats->rx_rrd_ov + hw_stats->rx_align_err; + net_stats->rx_fifo_errors = hw_stats->rx_rxf_ov; + net_stats->rx_length_errors = hw_stats->rx_len_err; + net_stats->rx_crc_errors = hw_stats->rx_fcs_err; + net_stats->rx_frame_errors = hw_stats->rx_align_err; + net_stats->rx_over_errors = hw_stats->rx_rrd_ov + hw_stats->rx_rxf_ov; + + net_stats->rx_missed_errors = hw_stats->rx_rrd_ov + hw_stats->rx_rxf_ov; + + net_stats->tx_errors = hw_stats->tx_late_col + hw_stats->tx_abort_col + + hw_stats->tx_underrun + hw_stats->tx_trunc; + net_stats->tx_fifo_errors = hw_stats->tx_underrun; + net_stats->tx_aborted_errors = hw_stats->tx_abort_col; + net_stats->tx_window_errors = hw_stats->tx_late_col; + + return &adapter->net_stats; +} + +static inline void atl1c_clear_phy_int(struct atl1c_adapter *adapter) +{ + u16 phy_data; + + spin_lock(&adapter->mdio_lock); + atl1c_read_phy_reg(&adapter->hw, MII_ISR, &phy_data); + spin_unlock(&adapter->mdio_lock); +} + +static bool atl1c_clean_tx_irq(struct atl1c_adapter *adapter, + enum atl1c_trans_queue type) +{ + struct atl1c_tpd_ring *tpd_ring = (struct atl1c_tpd_ring *) + &adapter->tpd_ring[type]; + struct atl1c_buffer *buffer_info; + u16 next_to_clean = atomic_read(&tpd_ring->next_to_clean); + u16 hw_next_to_clean; + u16 shift; + u32 data; + + if (type == atl1c_trans_high) + shift = MB_HTPD_CONS_IDX_SHIFT; + else + shift = MB_NTPD_CONS_IDX_SHIFT; + + AT_READ_REG(&adapter->hw, REG_MB_PRIO_CONS_IDX, &data); + hw_next_to_clean = (data >> shift) & MB_PRIO_PROD_IDX_MASK; + + while (next_to_clean != hw_next_to_clean) { + buffer_info = &tpd_ring->buffer_info[next_to_clean]; + if (buffer_info->state == ATL1_BUFFER_BUSY) { + pci_unmap_page(adapter->pdev, buffer_info->dma, + buffer_info->length, PCI_DMA_TODEVICE); + buffer_info->dma = 0; + if (buffer_info->skb) { + dev_kfree_skb_irq(buffer_info->skb); + buffer_info->skb = NULL; + } + buffer_info->state = ATL1_BUFFER_FREE; + } + if (++next_to_clean == tpd_ring->count) + next_to_clean = 0; + atomic_set(&tpd_ring->next_to_clean, next_to_clean); + } + + if (netif_queue_stopped(adapter->netdev) && + netif_carrier_ok(adapter->netdev)) { + netif_wake_queue(adapter->netdev); + } + + return true; +} + +/* + * atl1c_intr - Interrupt Handler + * @irq: interrupt number + * @data: pointer to a network interface device structure + * @pt_regs: CPU registers structure + */ +static irqreturn_t atl1c_intr(int irq, void *data) +{ + struct net_device *netdev = data; + struct atl1c_adapter *adapter = netdev_priv(netdev); + struct pci_dev *pdev = adapter->pdev; + struct atl1c_hw *hw = &adapter->hw; + int max_ints = AT_MAX_INT_WORK; + int handled = IRQ_NONE; + u32 status; + u32 reg_data; + + do { + AT_READ_REG(hw, REG_ISR, ®_data); + status = reg_data & hw->intr_mask; + + if (status == 0 || (status & ISR_DIS_INT) != 0) { + if (max_ints != AT_MAX_INT_WORK) + handled = IRQ_HANDLED; + break; + } + /* link event */ + if (status & ISR_GPHY) + atl1c_clear_phy_int(adapter); + /* Ack ISR */ + AT_WRITE_REG(hw, REG_ISR, status | ISR_DIS_INT); + if (status & ISR_RX_PKT) { + if (likely(napi_schedule_prep(&adapter->napi))) { + hw->intr_mask &= ~ISR_RX_PKT; + AT_WRITE_REG(hw, REG_IMR, hw->intr_mask); + __napi_schedule(&adapter->napi); + } + } + if (status & ISR_TX_PKT) + atl1c_clean_tx_irq(adapter, atl1c_trans_normal); + + handled = IRQ_HANDLED; + /* check if PCIE PHY Link down */ + if (status & ISR_ERROR) { + if (netif_msg_hw(adapter)) + dev_err(&pdev->dev, + "atl1c hardware error (status = 0x%x)\n", + status & ISR_ERROR); + /* reset MAC */ + hw->intr_mask &= ~ISR_ERROR; + AT_WRITE_REG(hw, REG_IMR, hw->intr_mask); + schedule_work(&adapter->reset_task); + break; + } + + if (status & ISR_OVER) + if (netif_msg_intr(adapter)) + dev_warn(&pdev->dev, + "TX/RX over flow (status = 0x%x)\n", + status & ISR_OVER); + + /* link event */ + if (status & (ISR_GPHY | ISR_MANUAL)) { + adapter->net_stats.tx_carrier_errors++; + atl1c_link_chg_event(adapter); + break; + } + + } while (--max_ints > 0); + /* re-enable Interrupt*/ + AT_WRITE_REG(&adapter->hw, REG_ISR, 0); + return handled; +} + +static inline void atl1c_rx_checksum(struct atl1c_adapter *adapter, + struct sk_buff *skb, struct atl1c_recv_ret_status *prrs) +{ + /* + * The pid field in RRS in not correct sometimes, so we + * cannot figure out if the packet is fragmented or not, + * so we tell the KERNEL CHECKSUM_NONE + */ + skb->ip_summed = CHECKSUM_NONE; +} + +static int atl1c_alloc_rx_buffer(struct atl1c_adapter *adapter, const int ringid) +{ + struct atl1c_rfd_ring *rfd_ring = &adapter->rfd_ring[ringid]; + struct pci_dev *pdev = adapter->pdev; + struct atl1c_buffer *buffer_info, *next_info; + struct sk_buff *skb; + void *vir_addr = NULL; + u16 num_alloc = 0; + u16 rfd_next_to_use, next_next; + struct atl1c_rx_free_desc *rfd_desc; + + next_next = rfd_next_to_use = rfd_ring->next_to_use; + if (++next_next == rfd_ring->count) + next_next = 0; + buffer_info = &rfd_ring->buffer_info[rfd_next_to_use]; + next_info = &rfd_ring->buffer_info[next_next]; + + while (next_info->state == ATL1_BUFFER_FREE) { + rfd_desc = ATL1C_RFD_DESC(rfd_ring, rfd_next_to_use); + + skb = dev_alloc_skb(adapter->rx_buffer_len); + if (unlikely(!skb)) { + if (netif_msg_rx_err(adapter)) + dev_warn(&pdev->dev, "alloc rx buffer failed\n"); + break; + } + + /* + * Make buffer alignment 2 beyond a 16 byte boundary + * this will result in a 16 byte aligned IP header after + * the 14 byte MAC header is removed + */ + vir_addr = skb->data; + buffer_info->state = ATL1_BUFFER_BUSY; + buffer_info->skb = skb; + buffer_info->length = adapter->rx_buffer_len; + buffer_info->dma = pci_map_single(pdev, vir_addr, + buffer_info->length, + PCI_DMA_FROMDEVICE); + rfd_desc->buffer_addr = cpu_to_le64(buffer_info->dma); + rfd_next_to_use = next_next; + if (++next_next == rfd_ring->count) + next_next = 0; + buffer_info = &rfd_ring->buffer_info[rfd_next_to_use]; + next_info = &rfd_ring->buffer_info[next_next]; + num_alloc++; + } + + if (num_alloc) { + /* TODO: update mailbox here */ + wmb(); + rfd_ring->next_to_use = rfd_next_to_use; + AT_WRITE_REG(&adapter->hw, atl1c_rfd_prod_idx_regs[ringid], + rfd_ring->next_to_use & MB_RFDX_PROD_IDX_MASK); + } + + return num_alloc; +} + +static void atl1c_clean_rrd(struct atl1c_rrd_ring *rrd_ring, + struct atl1c_recv_ret_status *rrs, u16 num) +{ + u16 i; + /* the relationship between rrd and rfd is one map one */ + for (i = 0; i < num; i++, rrs = ATL1C_RRD_DESC(rrd_ring, + rrd_ring->next_to_clean)) { + rrs->word3 &= ~RRS_RXD_UPDATED; + if (++rrd_ring->next_to_clean == rrd_ring->count) + rrd_ring->next_to_clean = 0; + } +} + +static void atl1c_clean_rfd(struct atl1c_rfd_ring *rfd_ring, + struct atl1c_recv_ret_status *rrs, u16 num) +{ + u16 i; + u16 rfd_index; + struct atl1c_buffer *buffer_info = rfd_ring->buffer_info; + + rfd_index = (rrs->word0 >> RRS_RX_RFD_INDEX_SHIFT) & + RRS_RX_RFD_INDEX_MASK; + for (i = 0; i < num; i++) { + buffer_info[rfd_index].skb = NULL; + buffer_info[rfd_index].state = ATL1_BUFFER_FREE; + if (++rfd_index == rfd_ring->count) + rfd_index = 0; + } + rfd_ring->next_to_clean = rfd_index; +} + +static void atl1c_clean_rx_irq(struct atl1c_adapter *adapter, u8 que, + int *work_done, int work_to_do) +{ + u16 rfd_num, rfd_index; + u16 count = 0; + u16 length; + struct pci_dev *pdev = adapter->pdev; + struct net_device *netdev = adapter->netdev; + struct atl1c_rfd_ring *rfd_ring = &adapter->rfd_ring[que]; + struct atl1c_rrd_ring *rrd_ring = &adapter->rrd_ring[que]; + struct sk_buff *skb; + struct atl1c_recv_ret_status *rrs; + struct atl1c_buffer *buffer_info; + + while (1) { + if (*work_done >= work_to_do) + break; + rrs = ATL1C_RRD_DESC(rrd_ring, rrd_ring->next_to_clean); + 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) + /* TODO support mul rfd*/ + if (netif_msg_rx_err(adapter)) + dev_warn(&pdev->dev, + "Multi rfd not support yet!\n"); + goto rrs_checked; + } else { + break; + } +rrs_checked: + atl1c_clean_rrd(rrd_ring, rrs, rfd_num); + if (rrs->word3 & (RRS_RX_ERR_SUM | RRS_802_3_LEN_ERR)) { + atl1c_clean_rfd(rfd_ring, rrs, rfd_num); + if (netif_msg_rx_err(adapter)) + dev_warn(&pdev->dev, + "wrong packet! rrs word3 is %x\n", + rrs->word3); + continue; + } + + length = le16_to_cpu((rrs->word3 >> RRS_PKT_SIZE_SHIFT) & + RRS_PKT_SIZE_MASK); + /* Good Receive */ + if (likely(rfd_num == 1)) { + rfd_index = (rrs->word0 >> RRS_RX_RFD_INDEX_SHIFT) & + RRS_RX_RFD_INDEX_MASK; + buffer_info = &rfd_ring->buffer_info[rfd_index]; + pci_unmap_single(pdev, buffer_info->dma, + buffer_info->length, PCI_DMA_FROMDEVICE); + skb = buffer_info->skb; + } else { + /* TODO */ + if (netif_msg_rx_err(adapter)) + dev_warn(&pdev->dev, + "Multi rfd not support yet!\n"); + break; + } + atl1c_clean_rfd(rfd_ring, rrs, rfd_num); + skb_put(skb, length - ETH_FCS_LEN); + skb->protocol = eth_type_trans(skb, netdev); + skb->dev = netdev; + atl1c_rx_checksum(adapter, skb, rrs); + if (unlikely(adapter->vlgrp) && rrs->word3 & RRS_VLAN_INS) { + u16 vlan; + + AT_TAG_TO_VLAN(rrs->vlan_tag, vlan); + vlan = le16_to_cpu(vlan); + vlan_hwaccel_receive_skb(skb, adapter->vlgrp, vlan); + } else + netif_receive_skb(skb); + + netdev->last_rx = jiffies; + (*work_done)++; + count++; + } + if (count) + atl1c_alloc_rx_buffer(adapter, que); +} + +/* + * atl1c_clean - NAPI Rx polling callback + * @adapter: board private structure + */ +static int atl1c_clean(struct napi_struct *napi, int budget) +{ + struct atl1c_adapter *adapter = + container_of(napi, struct atl1c_adapter, napi); + int work_done = 0; + + /* Keep link state information with original netdev */ + if (!netif_carrier_ok(adapter->netdev)) + goto quit_polling; + /* just enable one RXQ */ + atl1c_clean_rx_irq(adapter, 0, &work_done, budget); + + if (work_done < budget) { +quit_polling: + napi_complete(napi); + adapter->hw.intr_mask |= ISR_RX_PKT; + AT_WRITE_REG(&adapter->hw, REG_IMR, adapter->hw.intr_mask); + } + return work_done; +} + +#ifdef CONFIG_NET_POLL_CONTROLLER + +/* + * Polling 'interrupt' - used by things like netconsole to send skbs + * without having to re-enable interrupts. It's not called while + * the interrupt routine is executing. + */ +static void atl1c_netpoll(struct net_device *netdev) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + + disable_irq(adapter->pdev->irq); + atl1c_intr(adapter->pdev->irq, netdev); + enable_irq(adapter->pdev->irq); +} +#endif + +static inline u16 atl1c_tpd_avail(struct atl1c_adapter *adapter, enum atl1c_trans_queue type) +{ + struct atl1c_tpd_ring *tpd_ring = &adapter->tpd_ring[type]; + u16 next_to_use = 0; + u16 next_to_clean = 0; + + next_to_clean = atomic_read(&tpd_ring->next_to_clean); + next_to_use = tpd_ring->next_to_use; + + return (u16)(next_to_clean > next_to_use) ? + (next_to_clean - next_to_use - 1) : + (tpd_ring->count + next_to_clean - next_to_use - 1); +} + +/* + * get next usable tpd + * Note: should call atl1c_tdp_avail to make sure + * there is enough tpd to use + */ +static struct atl1c_tpd_desc *atl1c_get_tpd(struct atl1c_adapter *adapter, + enum atl1c_trans_queue type) +{ + struct atl1c_tpd_ring *tpd_ring = &adapter->tpd_ring[type]; + struct atl1c_tpd_desc *tpd_desc; + u16 next_to_use = 0; + + next_to_use = tpd_ring->next_to_use; + if (++tpd_ring->next_to_use == tpd_ring->count) + tpd_ring->next_to_use = 0; + tpd_desc = ATL1C_TPD_DESC(tpd_ring, next_to_use); + memset(tpd_desc, 0, sizeof(struct atl1c_tpd_desc)); + return tpd_desc; +} + +static struct atl1c_buffer * +atl1c_get_tx_buffer(struct atl1c_adapter *adapter, struct atl1c_tpd_desc *tpd) +{ + struct atl1c_tpd_ring *tpd_ring = adapter->tpd_ring; + + return &tpd_ring->buffer_info[tpd - + (struct atl1c_tpd_desc *)tpd_ring->desc]; +} + +/* Calculate the transmit packet descript needed*/ +static u16 atl1c_cal_tpd_req(const struct sk_buff *skb) +{ + u16 tpd_req; + u16 proto_hdr_len = 0; + + tpd_req = skb_shinfo(skb)->nr_frags + 1; + + if (skb_is_gso(skb)) { + proto_hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); + if (proto_hdr_len < skb_headlen(skb)) + tpd_req++; + if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6) + tpd_req++; + } + return tpd_req; +} + +static int atl1c_tso_csum(struct atl1c_adapter *adapter, + struct sk_buff *skb, + struct atl1c_tpd_desc **tpd, + enum atl1c_trans_queue type) +{ + struct pci_dev *pdev = adapter->pdev; + u8 hdr_len; + u32 real_len; + unsigned short offload_type; + int err; + + if (skb_is_gso(skb)) { + if (skb_header_cloned(skb)) { + err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC); + if (unlikely(err)) + return -1; + } + offload_type = skb_shinfo(skb)->gso_type; + + if (offload_type & SKB_GSO_TCPV4) { + real_len = (((unsigned char *)ip_hdr(skb) - skb->data) + + ntohs(ip_hdr(skb)->tot_len)); + + if (real_len < skb->len) + pskb_trim(skb, real_len); + + hdr_len = (skb_transport_offset(skb) + tcp_hdrlen(skb)); + if (unlikely(skb->len == hdr_len)) { + /* only xsum need */ + if (netif_msg_tx_queued(adapter)) + dev_warn(&pdev->dev, + "IPV4 tso with zero data??\n"); + goto check_sum; + } else { + ip_hdr(skb)->check = 0; + tcp_hdr(skb)->check = ~csum_tcpudp_magic( + ip_hdr(skb)->saddr, + ip_hdr(skb)->daddr, + 0, IPPROTO_TCP, 0); + (*tpd)->word1 |= 1 << TPD_IPV4_PACKET_SHIFT; + } + } + + if (offload_type & SKB_GSO_TCPV6) { + struct atl1c_tpd_ext_desc *etpd = + *(struct atl1c_tpd_ext_desc **)(tpd); + + memset(etpd, 0, sizeof(struct atl1c_tpd_ext_desc)); + *tpd = atl1c_get_tpd(adapter, type); + ipv6_hdr(skb)->payload_len = 0; + /* check payload == 0 byte ? */ + hdr_len = (skb_transport_offset(skb) + tcp_hdrlen(skb)); + if (unlikely(skb->len == hdr_len)) { + /* only xsum need */ + if (netif_msg_tx_queued(adapter)) + dev_warn(&pdev->dev, + "IPV6 tso with zero data??\n"); + goto check_sum; + } else + tcp_hdr(skb)->check = ~csum_ipv6_magic( + &ipv6_hdr(skb)->saddr, + &ipv6_hdr(skb)->daddr, + 0, IPPROTO_TCP, 0); + etpd->word1 |= 1 << TPD_LSO_EN_SHIFT; + etpd->word1 |= 1 << TPD_LSO_VER_SHIFT; + etpd->pkt_len = cpu_to_le32(skb->len); + (*tpd)->word1 |= 1 << TPD_LSO_VER_SHIFT; + } + + (*tpd)->word1 |= 1 << TPD_LSO_EN_SHIFT; + (*tpd)->word1 |= (skb_transport_offset(skb) & TPD_TCPHDR_OFFSET_MASK) << + TPD_TCPHDR_OFFSET_SHIFT; + (*tpd)->word1 |= (skb_shinfo(skb)->gso_size & TPD_MSS_MASK) << + TPD_MSS_SHIFT; + return 0; + } + +check_sum: + if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) { + u8 css, cso; + cso = skb_transport_offset(skb); + + if (unlikely(cso & 0x1)) { + if (netif_msg_tx_err(adapter)) + dev_err(&adapter->pdev->dev, + "payload offset should not an event number\n"); + return -1; + } else { + css = cso + skb->csum_offset; + + (*tpd)->word1 |= ((cso >> 1) & TPD_PLOADOFFSET_MASK) << + TPD_PLOADOFFSET_SHIFT; + (*tpd)->word1 |= ((css >> 1) & TPD_CCSUM_OFFSET_MASK) << + TPD_CCSUM_OFFSET_SHIFT; + (*tpd)->word1 |= 1 << TPD_CCSUM_EN_SHIFT; + } + } + return 0; +} + +static void atl1c_tx_map(struct atl1c_adapter *adapter, + struct sk_buff *skb, struct atl1c_tpd_desc *tpd, + enum atl1c_trans_queue type) +{ + struct atl1c_tpd_desc *use_tpd = NULL; + struct atl1c_buffer *buffer_info = NULL; + u16 buf_len = skb_headlen(skb); + u16 map_len = 0; + u16 mapped_len = 0; + u16 hdr_len = 0; + u16 nr_frags; + u16 f; + int tso; + + nr_frags = skb_shinfo(skb)->nr_frags; + tso = (tpd->word1 >> TPD_LSO_EN_SHIFT) & TPD_LSO_EN_MASK; + if (tso) { + /* TSO */ + map_len = hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); + use_tpd = tpd; + + buffer_info = atl1c_get_tx_buffer(adapter, use_tpd); + buffer_info->length = map_len; + buffer_info->dma = pci_map_single(adapter->pdev, + skb->data, hdr_len, PCI_DMA_TODEVICE); + buffer_info->state = ATL1_BUFFER_BUSY; + mapped_len += map_len; + use_tpd->buffer_addr = cpu_to_le64(buffer_info->dma); + use_tpd->buffer_len = cpu_to_le16(buffer_info->length); + } + + if (mapped_len < buf_len) { + /* mapped_len == 0, means we should use the first tpd, + which is given by caller */ + if (mapped_len == 0) + use_tpd = tpd; + else { + use_tpd = atl1c_get_tpd(adapter, type); + memcpy(use_tpd, tpd, sizeof(struct atl1c_tpd_desc)); + use_tpd = atl1c_get_tpd(adapter, type); + memcpy(use_tpd, tpd, sizeof(struct atl1c_tpd_desc)); + } + buffer_info = atl1c_get_tx_buffer(adapter, use_tpd); + buffer_info->length = buf_len - mapped_len; + buffer_info->dma = + pci_map_single(adapter->pdev, skb->data + mapped_len, + buffer_info->length, PCI_DMA_TODEVICE); + buffer_info->state = ATL1_BUFFER_BUSY; + + use_tpd->buffer_addr = cpu_to_le64(buffer_info->dma); + use_tpd->buffer_len = cpu_to_le16(buffer_info->length); + } + + for (f = 0; f < nr_frags; f++) { + struct skb_frag_struct *frag; + + frag = &skb_shinfo(skb)->frags[f]; + + use_tpd = atl1c_get_tpd(adapter, type); + memcpy(use_tpd, tpd, sizeof(struct atl1c_tpd_desc)); + + buffer_info = atl1c_get_tx_buffer(adapter, use_tpd); + buffer_info->length = frag->size; + buffer_info->dma = + pci_map_page(adapter->pdev, frag->page, + frag->page_offset, + buffer_info->length, + PCI_DMA_TODEVICE); + buffer_info->state = ATL1_BUFFER_BUSY; + + use_tpd->buffer_addr = cpu_to_le64(buffer_info->dma); + use_tpd->buffer_len = cpu_to_le16(buffer_info->length); + } + + /* The last tpd */ + use_tpd->word1 |= 1 << TPD_EOP_SHIFT; + /* The last buffer info contain the skb address, + so it will be free after unmap */ + buffer_info->skb = skb; +} + +static void atl1c_tx_queue(struct atl1c_adapter *adapter, struct sk_buff *skb, + struct atl1c_tpd_desc *tpd, enum atl1c_trans_queue type) +{ + struct atl1c_tpd_ring *tpd_ring = &adapter->tpd_ring[type]; + u32 prod_data; + + AT_READ_REG(&adapter->hw, REG_MB_PRIO_PROD_IDX, &prod_data); + switch (type) { + case atl1c_trans_high: + prod_data &= 0xFFFF0000; + prod_data |= tpd_ring->next_to_use & 0xFFFF; + break; + case atl1c_trans_normal: + prod_data &= 0x0000FFFF; + prod_data |= (tpd_ring->next_to_use & 0xFFFF) << 16; + break; + default: + break; + } + wmb(); + AT_WRITE_REG(&adapter->hw, REG_MB_PRIO_PROD_IDX, prod_data); +} + +static int atl1c_xmit_frame(struct sk_buff *skb, struct net_device *netdev) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + unsigned long flags; + u16 tpd_req = 1; + struct atl1c_tpd_desc *tpd; + enum atl1c_trans_queue type = atl1c_trans_normal; + + if (test_bit(__AT_DOWN, &adapter->flags)) { + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + } + + tpd_req = atl1c_cal_tpd_req(skb); + if (!spin_trylock_irqsave(&adapter->tx_lock, flags)) { + if (netif_msg_pktdata(adapter)) + dev_info(&adapter->pdev->dev, "tx locked\n"); + return NETDEV_TX_LOCKED; + } + if (skb->mark == 0x01) + type = atl1c_trans_high; + else + type = atl1c_trans_normal; + + if (atl1c_tpd_avail(adapter, type) < tpd_req) { + /* no enough descriptor, just stop queue */ + netif_stop_queue(netdev); + spin_unlock_irqrestore(&adapter->tx_lock, flags); + return NETDEV_TX_BUSY; + } + + tpd = atl1c_get_tpd(adapter, type); + + /* do TSO and check sum */ + if (atl1c_tso_csum(adapter, skb, &tpd, type) != 0) { + spin_unlock_irqrestore(&adapter->tx_lock, flags); + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + } + + if (unlikely(adapter->vlgrp && vlan_tx_tag_present(skb))) { + u16 vlan = vlan_tx_tag_get(skb); + __le16 tag; + + vlan = cpu_to_le16(vlan); + AT_VLAN_TO_TAG(vlan, tag); + tpd->word1 |= 1 << TPD_INS_VTAG_SHIFT; + tpd->vlan_tag = tag; + } + + if (skb_network_offset(skb) != ETH_HLEN) + tpd->word1 |= 1 << TPD_ETH_TYPE_SHIFT; /* Ethernet frame */ + + atl1c_tx_map(adapter, skb, tpd, type); + atl1c_tx_queue(adapter, skb, tpd, type); + + netdev->trans_start = jiffies; + spin_unlock_irqrestore(&adapter->tx_lock, flags); + return NETDEV_TX_OK; +} + +static void atl1c_free_irq(struct atl1c_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + + free_irq(adapter->pdev->irq, netdev); + + if (adapter->have_msi) + pci_disable_msi(adapter->pdev); +} + +static int atl1c_request_irq(struct atl1c_adapter *adapter) +{ + struct pci_dev *pdev = adapter->pdev; + struct net_device *netdev = adapter->netdev; + int flags = 0; + int err = 0; + + adapter->have_msi = true; + err = pci_enable_msi(adapter->pdev); + if (err) { + if (netif_msg_ifup(adapter)) + dev_err(&pdev->dev, + "Unable to allocate MSI interrupt Error: %d\n", + err); + adapter->have_msi = false; + } else + netdev->irq = pdev->irq; + + if (!adapter->have_msi) + flags |= IRQF_SHARED; + err = request_irq(adapter->pdev->irq, &atl1c_intr, flags, + netdev->name, netdev); + if (err) { + if (netif_msg_ifup(adapter)) + dev_err(&pdev->dev, + "Unable to allocate interrupt Error: %d\n", + err); + if (adapter->have_msi) + pci_disable_msi(adapter->pdev); + return err; + } + if (netif_msg_ifup(adapter)) + dev_dbg(&pdev->dev, "atl1c_request_irq OK\n"); + return err; +} + +int atl1c_up(struct atl1c_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + int num; + int err; + int i; + + netif_carrier_off(netdev); + atl1c_init_ring_ptrs(adapter); + atl1c_set_multi(netdev); + atl1c_restore_vlan(adapter); + + for (i = 0; i < adapter->num_rx_queues; i++) { + num = atl1c_alloc_rx_buffer(adapter, i); + if (unlikely(num == 0)) { + err = -ENOMEM; + goto err_alloc_rx; + } + } + + if (atl1c_configure(adapter)) { + err = -EIO; + goto err_up; + } + + err = atl1c_request_irq(adapter); + if (unlikely(err)) + goto err_up; + + clear_bit(__AT_DOWN, &adapter->flags); + napi_enable(&adapter->napi); + atl1c_irq_enable(adapter); + atl1c_check_link_status(adapter); + netif_start_queue(netdev); + return err; + +err_up: +err_alloc_rx: + atl1c_clean_rx_ring(adapter); + return err; +} + +void atl1c_down(struct atl1c_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + + atl1c_del_timer(adapter); + atl1c_cancel_work(adapter); + + /* signal that we're down so the interrupt handler does not + * reschedule our watchdog timer */ + set_bit(__AT_DOWN, &adapter->flags); + netif_carrier_off(netdev); + napi_disable(&adapter->napi); + atl1c_irq_disable(adapter); + atl1c_free_irq(adapter); + AT_WRITE_REG(&adapter->hw, REG_ISR, ISR_DIS_INT); + /* reset MAC to disable all RX/TX */ + atl1c_reset_mac(&adapter->hw); + msleep(1); + + adapter->link_speed = SPEED_0; + adapter->link_duplex = -1; + atl1c_clean_tx_ring(adapter, atl1c_trans_normal); + atl1c_clean_tx_ring(adapter, atl1c_trans_high); + atl1c_clean_rx_ring(adapter); +} + +/* + * atl1c_open - Called when a network interface is made active + * @netdev: network interface device structure + * + * Returns 0 on success, negative value on failure + * + * The open entry point is called when a network interface is made + * active by the system (IFF_UP). At this point all resources needed + * for transmit and receive operations are allocated, the interrupt + * handler is registered with the OS, the watchdog timer is started, + * and the stack is notified that the interface is ready. + */ +static int atl1c_open(struct net_device *netdev) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + int err; + + /* disallow open during test */ + if (test_bit(__AT_TESTING, &adapter->flags)) + return -EBUSY; + + /* allocate rx/tx dma buffer & descriptors */ + err = atl1c_setup_ring_resources(adapter); + if (unlikely(err)) + return err; + + err = atl1c_up(adapter); + if (unlikely(err)) + goto err_up; + + if (adapter->hw.ctrl_flags & ATL1C_FPGA_VERSION) { + u32 phy_data; + + AT_READ_REG(&adapter->hw, REG_MDIO_CTRL, &phy_data); + phy_data |= MDIO_AP_EN; + AT_WRITE_REG(&adapter->hw, REG_MDIO_CTRL, phy_data); + } + return 0; + +err_up: + atl1c_free_irq(adapter); + atl1c_free_ring_resources(adapter); + atl1c_reset_mac(&adapter->hw); + return err; +} + +/* + * atl1c_close - Disables a network interface + * @netdev: network interface device structure + * + * Returns 0, this is not allowed to fail + * + * The close entry point is called when an interface is de-activated + * by the OS. The hardware is still under the drivers control, but + * needs to be disabled. A global MAC reset is issued to stop the + * hardware, and all transmit and receive resources are freed. + */ +static int atl1c_close(struct net_device *netdev) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + + WARN_ON(test_bit(__AT_RESETTING, &adapter->flags)); + atl1c_down(adapter); + atl1c_free_ring_resources(adapter); + return 0; +} + +static int atl1c_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + struct atl1c_adapter *adapter = netdev_priv(netdev); + struct atl1c_hw *hw = &adapter->hw; + u32 ctrl; + u32 mac_ctrl_data; + u32 master_ctrl_data; + u32 wol_ctrl_data; + u16 mii_bmsr_data; + u16 save_autoneg_advertised; + u16 mii_intr_status_data; + u32 wufc = adapter->wol; + u32 i; + int retval = 0; + + if (netif_running(netdev)) { + WARN_ON(test_bit(__AT_RESETTING, &adapter->flags)); + atl1c_down(adapter); + } + netif_device_detach(netdev); + atl1c_disable_l0s_l1(hw); + retval = pci_save_state(pdev); + if (retval) + return retval; + if (wufc) { + AT_READ_REG(hw, REG_MASTER_CTRL, &master_ctrl_data); + master_ctrl_data &= ~MASTER_CTRL_CLK_SEL_DIS; + + /* get link status */ + atl1c_read_phy_reg(hw, MII_BMSR, (u16 *)&mii_bmsr_data); + atl1c_read_phy_reg(hw, MII_BMSR, (u16 *)&mii_bmsr_data); + save_autoneg_advertised = hw->autoneg_advertised; + hw->autoneg_advertised = ADVERTISED_10baseT_Half; + if (atl1c_restart_autoneg(hw) != 0) + if (netif_msg_link(adapter)) + dev_warn(&pdev->dev, "phy autoneg failed\n"); + hw->phy_configured = false; /* re-init PHY when resume */ + hw->autoneg_advertised = save_autoneg_advertised; + /* turn on magic packet wol */ + if (wufc & AT_WUFC_MAG) + wol_ctrl_data = WOL_MAGIC_EN | WOL_MAGIC_PME_EN; + + if (wufc & AT_WUFC_LNKC) { + for (i = 0; i < AT_SUSPEND_LINK_TIMEOUT; i++) { + msleep(100); + atl1c_read_phy_reg(hw, MII_BMSR, + (u16 *)&mii_bmsr_data); + if (mii_bmsr_data & BMSR_LSTATUS) + break; + } + if ((mii_bmsr_data & BMSR_LSTATUS) == 0) + if (netif_msg_link(adapter)) + dev_warn(&pdev->dev, + "%s: Link may change" + "when suspend\n", + atl1c_driver_name); + wol_ctrl_data |= WOL_LINK_CHG_EN | WOL_LINK_CHG_PME_EN; + /* only link up can wake up */ + if (atl1c_write_phy_reg(hw, MII_IER, IER_LINK_UP) != 0) { + if (netif_msg_link(adapter)) + dev_err(&pdev->dev, + "%s: read write phy " + "register failed.\n", + atl1c_driver_name); + goto wol_dis; + } + } + /* clear phy interrupt */ + atl1c_read_phy_reg(hw, MII_ISR, &mii_intr_status_data); + /* Config MAC Ctrl register */ + mac_ctrl_data = MAC_CTRL_RX_EN; + /* set to 10/100M halt duplex */ + mac_ctrl_data |= atl1c_mac_speed_10_100 << MAC_CTRL_SPEED_SHIFT; + mac_ctrl_data |= (((u32)adapter->hw.preamble_len & + MAC_CTRL_PRMLEN_MASK) << + MAC_CTRL_PRMLEN_SHIFT); + + if (adapter->vlgrp) + mac_ctrl_data |= MAC_CTRL_RMV_VLAN; + + /* magic packet maybe Broadcast&multicast&Unicast frame */ + if (wufc & AT_WUFC_MAG) + mac_ctrl_data |= MAC_CTRL_BC_EN; + + if (netif_msg_hw(adapter)) + dev_dbg(&pdev->dev, + "%s: suspend MAC=0x%x\n", + atl1c_driver_name, mac_ctrl_data); + AT_WRITE_REG(hw, REG_MASTER_CTRL, master_ctrl_data); + AT_WRITE_REG(hw, REG_WOL_CTRL, wol_ctrl_data); + AT_WRITE_REG(hw, REG_MAC_CTRL, mac_ctrl_data); + + /* pcie patch */ + AT_READ_REG(hw, REG_PCIE_PHYMISC, &ctrl); + ctrl |= PCIE_PHYMISC_FORCE_RCV_DET; + AT_WRITE_REG(hw, REG_PCIE_PHYMISC, ctrl); + + pci_enable_wake(pdev, pci_choose_state(pdev, state), 1); + goto suspend_exit; + } +wol_dis: + + /* WOL disabled */ + AT_WRITE_REG(hw, REG_WOL_CTRL, 0); + + /* pcie patch */ + AT_READ_REG(hw, REG_PCIE_PHYMISC, &ctrl); + ctrl |= PCIE_PHYMISC_FORCE_RCV_DET; + AT_WRITE_REG(hw, REG_PCIE_PHYMISC, ctrl); + + atl1c_phy_disable(hw); + hw->phy_configured = false; /* re-init PHY when resume */ + + pci_enable_wake(pdev, pci_choose_state(pdev, state), 0); +suspend_exit: + + pci_disable_device(pdev); + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + + return 0; +} + +static int atl1c_resume(struct pci_dev *pdev) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + struct atl1c_adapter *adapter = netdev_priv(netdev); + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + pci_enable_wake(pdev, PCI_D3hot, 0); + pci_enable_wake(pdev, PCI_D3cold, 0); + + AT_WRITE_REG(&adapter->hw, REG_WOL_CTRL, 0); + + atl1c_phy_reset(&adapter->hw); + atl1c_reset_mac(&adapter->hw); + netif_device_attach(netdev); + if (netif_running(netdev)) + atl1c_up(adapter); + + return 0; +} + +static void atl1c_shutdown(struct pci_dev *pdev) +{ + atl1c_suspend(pdev, PMSG_SUSPEND); +} + +static const struct net_device_ops atl1c_netdev_ops = { + .ndo_open = atl1c_open, + .ndo_stop = atl1c_close, + .ndo_validate_addr = eth_validate_addr, + .ndo_start_xmit = atl1c_xmit_frame, + .ndo_set_mac_address = atl1c_set_mac_addr, + .ndo_set_multicast_list = atl1c_set_multi, + .ndo_change_mtu = atl1c_change_mtu, + .ndo_do_ioctl = atl1c_ioctl, + .ndo_tx_timeout = atl1c_tx_timeout, + .ndo_get_stats = atl1c_get_stats, + .ndo_vlan_rx_register = atl1c_vlan_rx_register, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = atl1c_netpoll, +#endif +}; + +static int atl1c_init_netdev(struct net_device *netdev, struct pci_dev *pdev) +{ + SET_NETDEV_DEV(netdev, &pdev->dev); + pci_set_drvdata(pdev, netdev); + + netdev->irq = pdev->irq; + netdev->netdev_ops = &atl1c_netdev_ops; + netdev->watchdog_timeo = AT_TX_WATCHDOG; + atl1c_set_ethtool_ops(netdev); + + /* TODO: add when ready */ + netdev->features = NETIF_F_SG | + NETIF_F_HW_CSUM | + NETIF_F_HW_VLAN_TX | + NETIF_F_HW_VLAN_RX | + NETIF_F_TSO | + NETIF_F_TSO6; + return 0; +} + +/* + * atl1c_probe - Device Initialization Routine + * @pdev: PCI device information struct + * @ent: entry in atl1c_pci_tbl + * + * Returns 0 on success, negative on failure + * + * atl1c_probe initializes an adapter identified by a pci_dev structure. + * The OS initialization, configuring of the adapter private structure, + * and a hardware reset occur. + */ +static int __devinit atl1c_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct net_device *netdev; + struct atl1c_adapter *adapter; + static int cards_found; + + int err = 0; + + /* enable device (incl. PCI PM wakeup and hotplug setup) */ + err = pci_enable_device_mem(pdev); + if (err) { + dev_err(&pdev->dev, "cannot enable PCI device\n"); + return err; + } + + /* + * The atl1c chip can DMA to 64-bit addresses, but it uses a single + * shared register for the high 32 bits, so only a single, aligned, + * 4 GB physical address range can be used at a time. + * + * Supporting 64-bit DMA on this hardware is more trouble than it's + * worth. It is far easier to limit to 32-bit DMA than update + * various kernel subsystems to support the mechanics required by a + * fixed-high-32-bit system. + */ + if ((pci_set_dma_mask(pdev, DMA_32BIT_MASK) != 0) || + (pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK) != 0)) { + dev_err(&pdev->dev, "No usable DMA configuration,aborting\n"); + goto err_dma; + } + + err = pci_request_regions(pdev, atl1c_driver_name); + if (err) { + dev_err(&pdev->dev, "cannot obtain PCI resources\n"); + goto err_pci_reg; + } + + pci_set_master(pdev); + + netdev = alloc_etherdev(sizeof(struct atl1c_adapter)); + if (netdev == NULL) { + err = -ENOMEM; + dev_err(&pdev->dev, "etherdev alloc failed\n"); + goto err_alloc_etherdev; + } + + err = atl1c_init_netdev(netdev, pdev); + if (err) { + dev_err(&pdev->dev, "init netdevice failed\n"); + goto err_init_netdev; + } + adapter = netdev_priv(netdev); + adapter->bd_number = cards_found; + adapter->netdev = netdev; + adapter->pdev = pdev; + adapter->hw.adapter = adapter; + adapter->msg_enable = netif_msg_init(-1, atl1c_default_msg); + adapter->hw.hw_addr = ioremap(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); + if (!adapter->hw.hw_addr) { + err = -EIO; + dev_err(&pdev->dev, "cannot map device registers\n"); + goto err_ioremap; + } + netdev->base_addr = (unsigned long)adapter->hw.hw_addr; + + /* init mii data */ + adapter->mii.dev = netdev; + adapter->mii.mdio_read = atl1c_mdio_read; + adapter->mii.mdio_write = atl1c_mdio_write; + adapter->mii.phy_id_mask = 0x1f; + adapter->mii.reg_num_mask = MDIO_REG_ADDR_MASK; + netif_napi_add(netdev, &adapter->napi, atl1c_clean, 64); + setup_timer(&adapter->phy_config_timer, atl1c_phy_config, + (unsigned long)adapter); + /* setup the private structure */ + err = atl1c_sw_init(adapter); + if (err) { + dev_err(&pdev->dev, "net device private data init failed\n"); + goto err_sw_init; + } + atl1c_reset_pcie(&adapter->hw, ATL1C_PCIE_L0S_L1_DISABLE | + ATL1C_PCIE_PHY_RESET); + + /* Init GPHY as early as possible due to power saving issue */ + atl1c_phy_reset(&adapter->hw); + + err = atl1c_reset_mac(&adapter->hw); + if (err) { + err = -EIO; + goto err_reset; + } + + device_init_wakeup(&pdev->dev, 1); + /* reset the controller to + * put the device in a known good starting state */ + err = atl1c_phy_init(&adapter->hw); + if (err) { + err = -EIO; + goto err_reset; + } + if (atl1c_read_mac_addr(&adapter->hw) != 0) { + err = -EIO; + dev_err(&pdev->dev, "get mac address failed\n"); + goto err_eeprom; + } + memcpy(netdev->dev_addr, adapter->hw.mac_addr, netdev->addr_len); + memcpy(netdev->perm_addr, adapter->hw.mac_addr, netdev->addr_len); + if (netif_msg_probe(adapter)) + dev_dbg(&pdev->dev, + "mac address : %02x-%02x-%02x-%02x-%02x-%02x\n", + adapter->hw.mac_addr[0], adapter->hw.mac_addr[1], + adapter->hw.mac_addr[2], adapter->hw.mac_addr[3], + adapter->hw.mac_addr[4], adapter->hw.mac_addr[5]); + + atl1c_hw_set_mac_addr(&adapter->hw); + INIT_WORK(&adapter->reset_task, atl1c_reset_task); + INIT_WORK(&adapter->link_chg_task, atl1c_link_chg_task); + err = register_netdev(netdev); + if (err) { + dev_err(&pdev->dev, "register netdevice failed\n"); + goto err_register; + } + + if (netif_msg_probe(adapter)) + dev_info(&pdev->dev, "version %s\n", ATL1C_DRV_VERSION); + cards_found++; + return 0; + +err_reset: +err_register: +err_sw_init: +err_eeprom: + iounmap(adapter->hw.hw_addr); +err_init_netdev: +err_ioremap: + free_netdev(netdev); +err_alloc_etherdev: + pci_release_regions(pdev); +err_pci_reg: +err_dma: + pci_disable_device(pdev); + return err; +} + +/* + * atl1c_remove - Device Removal Routine + * @pdev: PCI device information struct + * + * atl1c_remove is called by the PCI subsystem to alert the driver + * that it should release a PCI device. The could be caused by a + * Hot-Plug event, or because the driver is going to be removed from + * memory. + */ +static void __devexit atl1c_remove(struct pci_dev *pdev) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + struct atl1c_adapter *adapter = netdev_priv(netdev); + + unregister_netdev(netdev); + atl1c_phy_disable(&adapter->hw); + + iounmap(adapter->hw.hw_addr); + + pci_release_regions(pdev); + pci_disable_device(pdev); + free_netdev(netdev); +} + +/* + * atl1c_io_error_detected - called when PCI error is detected + * @pdev: Pointer to PCI device + * @state: The current pci connection state + * + * This function is called after a PCI bus error affecting + * this device has been detected. + */ +static pci_ers_result_t atl1c_io_error_detected(struct pci_dev *pdev, + pci_channel_state_t state) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + struct atl1c_adapter *adapter = netdev_priv(netdev); + + netif_device_detach(netdev); + + if (netif_running(netdev)) + atl1c_down(adapter); + + pci_disable_device(pdev); + + /* Request a slot slot reset. */ + return PCI_ERS_RESULT_NEED_RESET; +} + +/* + * atl1c_io_slot_reset - called after the pci bus has been reset. + * @pdev: Pointer to PCI device + * + * Restart the card from scratch, as if from a cold-boot. Implementation + * resembles the first-half of the e1000_resume routine. + */ +static pci_ers_result_t atl1c_io_slot_reset(struct pci_dev *pdev) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + struct atl1c_adapter *adapter = netdev_priv(netdev); + + if (pci_enable_device(pdev)) { + if (netif_msg_hw(adapter)) + dev_err(&pdev->dev, + "Cannot re-enable PCI device after reset\n"); + return PCI_ERS_RESULT_DISCONNECT; + } + pci_set_master(pdev); + + pci_enable_wake(pdev, PCI_D3hot, 0); + pci_enable_wake(pdev, PCI_D3cold, 0); + + atl1c_reset_mac(&adapter->hw); + + return PCI_ERS_RESULT_RECOVERED; +} + +/* + * atl1c_io_resume - called when traffic can start flowing again. + * @pdev: Pointer to PCI device + * + * This callback is called when the error recovery driver tells us that + * its OK to resume normal operation. Implementation resembles the + * second-half of the atl1c_resume routine. + */ +static void atl1c_io_resume(struct pci_dev *pdev) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + struct atl1c_adapter *adapter = netdev_priv(netdev); + + if (netif_running(netdev)) { + if (atl1c_up(adapter)) { + if (netif_msg_hw(adapter)) + dev_err(&pdev->dev, + "Cannot bring device back up after reset\n"); + return; + } + } + + netif_device_attach(netdev); +} + +static struct pci_error_handlers atl1c_err_handler = { + .error_detected = atl1c_io_error_detected, + .slot_reset = atl1c_io_slot_reset, + .resume = atl1c_io_resume, +}; + +static struct pci_driver atl1c_driver = { + .name = atl1c_driver_name, + .id_table = atl1c_pci_tbl, + .probe = atl1c_probe, + .remove = __devexit_p(atl1c_remove), + /* Power Managment Hooks */ + .suspend = atl1c_suspend, + .resume = atl1c_resume, + .shutdown = atl1c_shutdown, + .err_handler = &atl1c_err_handler +}; + +/* + * atl1c_init_module - Driver Registration Routine + * + * atl1c_init_module is the first routine called when the driver is + * loaded. All it does is register with the PCI subsystem. + */ +static int __init atl1c_init_module(void) +{ + return pci_register_driver(&atl1c_driver); +} + +/* + * atl1c_exit_module - Driver Exit Cleanup Routine + * + * atl1c_exit_module is called just before the driver is removed + * from memory. + */ +static void __exit atl1c_exit_module(void) +{ + pci_unregister_driver(&atl1c_driver); +} + +module_init(atl1c_init_module); +module_exit(atl1c_exit_module); diff --git a/drivers/net/cxgb3/cxgb3_main.c b/drivers/net/cxgb3/cxgb3_main.c index 0089746b8d0..bab8a934c33 100644 --- a/drivers/net/cxgb3/cxgb3_main.c +++ b/drivers/net/cxgb3/cxgb3_main.c @@ -90,6 +90,7 @@ static const struct pci_device_id cxgb3_pci_tbl[] = { CH_DEVICE(0x30, 2), /* T3B10 */ CH_DEVICE(0x31, 3), /* T3B20 */ CH_DEVICE(0x32, 1), /* T3B02 */ + CH_DEVICE(0x35, 6), /* T3C20-derived T3C10 */ {0,} }; diff --git a/drivers/net/cxgb3/t3_hw.c b/drivers/net/cxgb3/t3_hw.c index 2d1433077a8..ac2a974dfe3 100644 --- a/drivers/net/cxgb3/t3_hw.c +++ b/drivers/net/cxgb3/t3_hw.c @@ -512,6 +512,13 @@ static const struct adapter_info t3_adap_info[] = { F_GPIO5_OUT_VAL | F_GPIO6_OUT_VAL | F_GPIO10_OUT_VAL, { S_GPIO9, S_GPIO3 }, SUPPORTED_10000baseT_Full | SUPPORTED_AUI, &mi1_mdio_ext_ops, "Chelsio T320"}, + {}, + {}, + {1, 0, + F_GPIO1_OEN | F_GPIO2_OEN | F_GPIO4_OEN | F_GPIO6_OEN | F_GPIO7_OEN | + F_GPIO10_OEN | F_GPIO1_OUT_VAL | F_GPIO6_OUT_VAL | F_GPIO10_OUT_VAL, + { S_GPIO9 }, SUPPORTED_10000baseT_Full | SUPPORTED_AUI, + &mi1_mdio_ext_ops, "Chelsio T310" }, }; /* diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c index 5b910cf6374..b8251e82705 100644 --- a/drivers/net/forcedeth.c +++ b/drivers/net/forcedeth.c @@ -6011,9 +6011,20 @@ static void nv_shutdown(struct pci_dev *pdev) if (netif_running(dev)) nv_close(dev); - nv_restore_mac_addr(pdev); + /* + * Restore the MAC so a kernel started by kexec won't get confused. + * If we really go for poweroff, we must not restore the MAC, + * otherwise the MAC for WOL will be reversed at least on some boards. + */ + if (system_state != SYSTEM_POWER_OFF) { + nv_restore_mac_addr(pdev); + } pci_disable_device(pdev); + /* + * Apparently it is not possible to reinitialise from D3 hot, + * only put the device into D3 if we really go for poweroff. + */ if (system_state == SYSTEM_POWER_OFF) { if (pci_enable_wake(pdev, PCI_D3cold, np->wolenabled)) pci_enable_wake(pdev, PCI_D3hot, np->wolenabled); diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c index 5f31bbb614a..13f11f402a9 100644 --- a/drivers/net/mv643xx_eth.c +++ b/drivers/net/mv643xx_eth.c @@ -1175,7 +1175,7 @@ static void mib_counters_update(struct mv643xx_eth_private *mp) { struct mib_counters *p = &mp->mib_counters; - spin_lock(&mp->mib_counters_lock); + spin_lock_bh(&mp->mib_counters_lock); p->good_octets_received += mib_read(mp, 0x00); p->good_octets_received += (u64)mib_read(mp, 0x04) << 32; p->bad_octets_received += mib_read(mp, 0x08); @@ -1208,7 +1208,7 @@ static void mib_counters_update(struct mv643xx_eth_private *mp) p->bad_crc_event += mib_read(mp, 0x74); p->collision += mib_read(mp, 0x78); p->late_collision += mib_read(mp, 0x7c); - spin_unlock(&mp->mib_counters_lock); + spin_unlock_bh(&mp->mib_counters_lock); mod_timer(&mp->mib_counters_timer, jiffies + 30 * HZ); } @@ -1575,7 +1575,7 @@ oom: return; } - mc_spec = kmalloc(0x200, GFP_KERNEL); + mc_spec = kmalloc(0x200, GFP_ATOMIC); if (mc_spec == NULL) goto oom; mc_other = mc_spec + (0x100 >> 2); @@ -2216,8 +2216,6 @@ static int mv643xx_eth_stop(struct net_device *dev) wrlp(mp, INT_MASK, 0x00000000); rdlp(mp, INT_MASK); - del_timer_sync(&mp->mib_counters_timer); - napi_disable(&mp->napi); del_timer_sync(&mp->rx_oom); @@ -2229,6 +2227,7 @@ static int mv643xx_eth_stop(struct net_device *dev) port_reset(mp); mv643xx_eth_get_stats(dev); mib_counters_update(mp); + del_timer_sync(&mp->mib_counters_timer); skb_queue_purge(&mp->rx_recycle); diff --git a/drivers/net/smsc911x.c b/drivers/net/smsc911x.c index 783c1a7b869..9a78daec2fe 100644 --- a/drivers/net/smsc911x.c +++ b/drivers/net/smsc911x.c @@ -1624,7 +1624,7 @@ static int smsc911x_eeprom_send_cmd(struct smsc911x_data *pdata, u32 op) do { msleep(1); e2cmd = smsc911x_reg_read(pdata, E2P_CMD); - } while ((e2cmd & E2P_CMD_EPC_BUSY_) && (timeout--)); + } while ((e2cmd & E2P_CMD_EPC_BUSY_) && (--timeout)); if (!timeout) { SMSC_TRACE(DRV, "TIMED OUT"); diff --git a/drivers/net/smsc9420.c b/drivers/net/smsc9420.c index a1e4b3895b3..4e15ae068b3 100644 --- a/drivers/net/smsc9420.c +++ b/drivers/net/smsc9420.c @@ -341,7 +341,7 @@ static int smsc9420_eeprom_send_cmd(struct smsc9420_pdata *pd, u32 op) do { msleep(1); e2cmd = smsc9420_reg_read(pd, E2P_CMD); - } while ((e2cmd & E2P_CMD_EPC_BUSY_) && (timeout--)); + } while ((e2cmd & E2P_CMD_EPC_BUSY_) && (--timeout)); if (!timeout) { smsc_info(HW, "TIMED OUT"); @@ -413,6 +413,7 @@ static int smsc9420_ethtool_get_eeprom(struct net_device *dev, } memcpy(data, &eeprom_data[eeprom->offset], len); + eeprom->magic = SMSC9420_EEPROM_MAGIC; eeprom->len = len; return 0; } @@ -423,6 +424,9 @@ static int smsc9420_ethtool_set_eeprom(struct net_device *dev, struct smsc9420_pdata *pd = netdev_priv(dev); int ret; + if (eeprom->magic != SMSC9420_EEPROM_MAGIC) + return -EINVAL; + smsc9420_eeprom_enable_access(pd); smsc9420_eeprom_send_cmd(pd, E2P_CMD_EPC_CMD_EWEN_); ret = smsc9420_eeprom_write_location(pd, eeprom->offset, *data); diff --git a/drivers/net/smsc9420.h b/drivers/net/smsc9420.h index 69c351f93f8..e441402f77a 100644 --- a/drivers/net/smsc9420.h +++ b/drivers/net/smsc9420.h @@ -44,6 +44,7 @@ #define LAN_REGISTER_EXTENT (0x400) #define SMSC9420_EEPROM_SIZE ((u32)11) +#define SMSC9420_EEPROM_MAGIC (0x9420) #define PKT_BUF_SZ (VLAN_ETH_FRAME_LEN + NET_IP_ALIGN + 4) diff --git a/drivers/net/sundance.c b/drivers/net/sundance.c index feaf0e0577d..43695b76606 100644 --- a/drivers/net/sundance.c +++ b/drivers/net/sundance.c @@ -909,7 +909,7 @@ static void check_duplex(struct net_device *dev) printk(KERN_INFO "%s: Setting %s-duplex based on MII #%d " "negotiated capability %4.4x.\n", dev->name, duplex ? "full" : "half", np->phys[0], negotiated); - iowrite16(ioread16(ioaddr + MACCtrl0) | duplex ? 0x20 : 0, ioaddr + MACCtrl0); + iowrite16(ioread16(ioaddr + MACCtrl0) | (duplex ? 0x20 : 0), ioaddr + MACCtrl0); } } diff --git a/drivers/net/sungem.c b/drivers/net/sungem.c index 49187634106..8d64b1da046 100644 --- a/drivers/net/sungem.c +++ b/drivers/net/sungem.c @@ -1157,7 +1157,7 @@ static void gem_pcs_reset(struct gem *gp) if (limit-- <= 0) break; } - if (limit <= 0) + if (limit < 0) printk(KERN_WARNING "%s: PCS reset bit would not clear.\n", gp->dev->name); } diff --git a/drivers/net/sunlance.c b/drivers/net/sunlance.c index 28137328175..16c528db725 100644 --- a/drivers/net/sunlance.c +++ b/drivers/net/sunlance.c @@ -343,7 +343,7 @@ static void lance_init_ring_dvma(struct net_device *dev) ib->phys_addr [5] = dev->dev_addr [4]; /* Setup the Tx ring entries */ - for (i = 0; i <= TX_RING_SIZE; i++) { + for (i = 0; i < TX_RING_SIZE; i++) { leptr = LANCE_ADDR(aib + libbuff_offset(tx_buf, i)); ib->btx_ring [i].tmd0 = leptr; ib->btx_ring [i].tmd1_hadr = leptr >> 16; @@ -399,7 +399,7 @@ static void lance_init_ring_pio(struct net_device *dev) sbus_writeb(dev->dev_addr[4], &ib->phys_addr[5]); /* Setup the Tx ring entries */ - for (i = 0; i <= TX_RING_SIZE; i++) { + for (i = 0; i < TX_RING_SIZE; i++) { leptr = libbuff_offset(tx_buf, i); sbus_writew(leptr, &ib->btx_ring [i].tmd0); sbus_writeb(leptr >> 16,&ib->btx_ring [i].tmd1_hadr); diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index 4595962fb8e..b080f9493d8 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c @@ -2237,8 +2237,8 @@ static int tg3_set_power_state(struct tg3 *tp, pci_power_t state) phyid = phydev->drv->phy_id & phydev->drv->phy_id_mask; if (phyid != TG3_PHY_ID_BCMAC131) { phyid &= TG3_PHY_OUI_MASK; - if (phyid == TG3_PHY_OUI_1 && - phyid == TG3_PHY_OUI_2 && + if (phyid == TG3_PHY_OUI_1 || + phyid == TG3_PHY_OUI_2 || phyid == TG3_PHY_OUI_3) do_low_power = true; } diff --git a/drivers/net/veth.c b/drivers/net/veth.c index 852d0e7c4e6..108bbbeacfb 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -263,10 +263,11 @@ static void veth_dev_free(struct net_device *dev) } static const struct net_device_ops veth_netdev_ops = { - .ndo_init = veth_dev_init, - .ndo_open = veth_open, - .ndo_start_xmit = veth_xmit, - .ndo_get_stats = veth_get_stats, + .ndo_init = veth_dev_init, + .ndo_open = veth_open, + .ndo_start_xmit = veth_xmit, + .ndo_get_stats = veth_get_stats, + .ndo_set_mac_address = eth_mac_addr, }; static void veth_setup(struct net_device *dev) diff --git a/drivers/net/wimax/i2400m/i2400m.h b/drivers/net/wimax/i2400m/i2400m.h index 067c871cc22..3b9d27ea295 100644 --- a/drivers/net/wimax/i2400m/i2400m.h +++ b/drivers/net/wimax/i2400m/i2400m.h @@ -157,7 +157,7 @@ enum { /* Firmware version we request when pulling the fw image file */ -#define I2400M_FW_VERSION "1.3" +#define I2400M_FW_VERSION "1.4" /** diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c index b0ee86c6268..ab13ff22a8c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-tx.c @@ -148,7 +148,7 @@ static void iwl_hw_txq_free_tfd(struct iwl_priv *priv, struct iwl_tx_queue *txq) pci_unmap_single(dev, pci_unmap_addr(&txq->cmd[index]->meta, mapping), pci_unmap_len(&txq->cmd[index]->meta, len), - PCI_DMA_TODEVICE); + PCI_DMA_BIDIRECTIONAL); /* Unmap chunks, if any. */ for (i = 1; i < num_tbs; i++) { @@ -964,7 +964,7 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) * within command buffer array. */ txcmd_phys = pci_map_single(priv->pci_dev, out_cmd, sizeof(struct iwl_cmd), - PCI_DMA_TODEVICE); + PCI_DMA_BIDIRECTIONAL); pci_unmap_addr_set(&out_cmd->meta, mapping, txcmd_phys); pci_unmap_len_set(&out_cmd->meta, len, sizeof(struct iwl_cmd)); /* Add buffer containing Tx command and MAC(!) header to TFD's @@ -1115,7 +1115,7 @@ int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) IWL_MAX_SCAN_SIZE : sizeof(struct iwl_cmd); phys_addr = pci_map_single(priv->pci_dev, out_cmd, - len, PCI_DMA_TODEVICE); + len, PCI_DMA_BIDIRECTIONAL); pci_unmap_addr_set(&out_cmd->meta, mapping, phys_addr); pci_unmap_len_set(&out_cmd->meta, len, len); phys_addr += offsetof(struct iwl_cmd, hdr); @@ -1212,7 +1212,7 @@ static void iwl_hcmd_queue_reclaim(struct iwl_priv *priv, int txq_id, pci_unmap_single(priv->pci_dev, pci_unmap_addr(&txq->cmd[cmd_idx]->meta, mapping), pci_unmap_len(&txq->cmd[cmd_idx]->meta, len), - PCI_DMA_TODEVICE); + PCI_DMA_BIDIRECTIONAL); for (idx = iwl_queue_inc_wrap(idx, q->n_bd); q->read_ptr != idx; q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) { diff --git a/drivers/parport/parport_atari.c b/drivers/parport/parport_atari.c index ad4cdd25613..0b28fccec03 100644 --- a/drivers/parport/parport_atari.c +++ b/drivers/parport/parport_atari.c @@ -84,7 +84,7 @@ parport_atari_frob_control(struct parport *p, unsigned char mask, static unsigned char parport_atari_read_status(struct parport *p) { - return ((mfp.par_dt_reg & 1 ? 0 : PARPORT_STATUS_BUSY) | + return ((st_mfp.par_dt_reg & 1 ? 0 : PARPORT_STATUS_BUSY) | PARPORT_STATUS_SELECT | PARPORT_STATUS_ERROR); } @@ -193,9 +193,9 @@ static int __init parport_atari_init(void) sound_ym.wd_data = sound_ym.rd_data_reg_sel | (1 << 5); local_irq_restore(flags); /* MFP port I0 as input. */ - mfp.data_dir &= ~1; + st_mfp.data_dir &= ~1; /* MFP port I0 interrupt on high->low edge. */ - mfp.active_edge &= ~1; + st_mfp.active_edge &= ~1; p = parport_register_port((unsigned long)&sound_ym.wd_data, IRQ_MFP_BUSY, PARPORT_DMA_NONE, &parport_atari_ops); diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c index f5a662a50ac..26c536b51c5 100644 --- a/drivers/pci/dmar.c +++ b/drivers/pci/dmar.c @@ -330,6 +330,14 @@ parse_dmar_table(void) entry_header = (struct acpi_dmar_header *)(dmar + 1); while (((unsigned long)entry_header) < (((unsigned long)dmar) + dmar_tbl->length)) { + /* Avoid looping forever on bad ACPI tables */ + if (entry_header->length == 0) { + printk(KERN_WARNING PREFIX + "Invalid 0-length structure\n"); + ret = -EINVAL; + break; + } + dmar_table_print_dmar_entry(entry_header); switch (entry_header->type) { @@ -491,7 +499,7 @@ int alloc_iommu(struct dmar_drhd_unit *drhd) int map_size; u32 ver; static int iommu_allocated = 0; - int agaw; + int agaw = 0; iommu = kzalloc(sizeof(*iommu), GFP_KERNEL); if (!iommu) @@ -507,6 +515,7 @@ int alloc_iommu(struct dmar_drhd_unit *drhd) iommu->cap = dmar_readq(iommu->reg + DMAR_CAP_REG); iommu->ecap = dmar_readq(iommu->reg + DMAR_ECAP_REG); +#ifdef CONFIG_DMAR agaw = iommu_calculate_agaw(iommu); if (agaw < 0) { printk(KERN_ERR @@ -514,6 +523,7 @@ int alloc_iommu(struct dmar_drhd_unit *drhd) iommu->seq_id); goto error; } +#endif iommu->agaw = agaw; /* the registers might be more than one page */ @@ -571,19 +581,49 @@ static inline void reclaim_free_desc(struct q_inval *qi) } } +static int qi_check_fault(struct intel_iommu *iommu, int index) +{ + u32 fault; + int head; + struct q_inval *qi = iommu->qi; + int wait_index = (index + 1) % QI_LENGTH; + + fault = readl(iommu->reg + DMAR_FSTS_REG); + + /* + * If IQE happens, the head points to the descriptor associated + * with the error. No new descriptors are fetched until the IQE + * is cleared. + */ + if (fault & DMA_FSTS_IQE) { + head = readl(iommu->reg + DMAR_IQH_REG); + if ((head >> 4) == index) { + memcpy(&qi->desc[index], &qi->desc[wait_index], + sizeof(struct qi_desc)); + __iommu_flush_cache(iommu, &qi->desc[index], + sizeof(struct qi_desc)); + writel(DMA_FSTS_IQE, iommu->reg + DMAR_FSTS_REG); + return -EINVAL; + } + } + + return 0; +} + /* * Submit the queued invalidation descriptor to the remapping * hardware unit and wait for its completion. */ -void qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu) +int qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu) { + int rc = 0; struct q_inval *qi = iommu->qi; struct qi_desc *hw, wait_desc; int wait_index, index; unsigned long flags; if (!qi) - return; + return 0; hw = qi->desc; @@ -601,7 +641,8 @@ void qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu) hw[index] = *desc; - wait_desc.low = QI_IWD_STATUS_DATA(2) | QI_IWD_STATUS_WRITE | QI_IWD_TYPE; + wait_desc.low = QI_IWD_STATUS_DATA(QI_DONE) | + QI_IWD_STATUS_WRITE | QI_IWD_TYPE; wait_desc.high = virt_to_phys(&qi->desc_status[wait_index]); hw[wait_index] = wait_desc; @@ -612,13 +653,11 @@ void qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu) qi->free_head = (qi->free_head + 2) % QI_LENGTH; qi->free_cnt -= 2; - spin_lock(&iommu->register_lock); /* * update the HW tail register indicating the presence of * new descriptors. */ writel(qi->free_head << 4, iommu->reg + DMAR_IQT_REG); - spin_unlock(&iommu->register_lock); while (qi->desc_status[wait_index] != QI_DONE) { /* @@ -628,15 +667,21 @@ void qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu) * a deadlock where the interrupt context can wait indefinitely * for free slots in the queue. */ + rc = qi_check_fault(iommu, index); + if (rc) + goto out; + spin_unlock(&qi->q_lock); cpu_relax(); spin_lock(&qi->q_lock); } - - qi->desc_status[index] = QI_DONE; +out: + qi->desc_status[index] = qi->desc_status[wait_index] = QI_DONE; reclaim_free_desc(qi); spin_unlock_irqrestore(&qi->q_lock, flags); + + return rc; } /* @@ -649,13 +694,13 @@ void qi_global_iec(struct intel_iommu *iommu) desc.low = QI_IEC_TYPE; desc.high = 0; + /* should never fail */ qi_submit_sync(&desc, iommu); } int qi_flush_context(struct intel_iommu *iommu, u16 did, u16 sid, u8 fm, u64 type, int non_present_entry_flush) { - struct qi_desc desc; if (non_present_entry_flush) { @@ -669,10 +714,7 @@ int qi_flush_context(struct intel_iommu *iommu, u16 did, u16 sid, u8 fm, | QI_CC_GRAN(type) | QI_CC_TYPE; desc.high = 0; - qi_submit_sync(&desc, iommu); - - return 0; - + return qi_submit_sync(&desc, iommu); } int qi_flush_iotlb(struct intel_iommu *iommu, u16 did, u64 addr, @@ -702,10 +744,7 @@ int qi_flush_iotlb(struct intel_iommu *iommu, u16 did, u64 addr, desc.high = QI_IOTLB_ADDR(addr) | QI_IOTLB_IH(ih) | QI_IOTLB_AM(size_order); - qi_submit_sync(&desc, iommu); - - return 0; - + return qi_submit_sync(&desc, iommu); } /* diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index f4b7c79023f..f3f686581a9 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -61,6 +61,8 @@ /* global iommu list, set NULL for ignored DMAR units */ static struct intel_iommu **g_iommus; +static int rwbf_quirk; + /* * 0: Present * 1-11: Reserved @@ -785,7 +787,7 @@ static void iommu_flush_write_buffer(struct intel_iommu *iommu) u32 val; unsigned long flag; - if (!cap_rwbf(iommu->cap)) + if (!rwbf_quirk && !cap_rwbf(iommu->cap)) return; val = iommu->gcmd | DMA_GCMD_WBF; @@ -3137,3 +3139,15 @@ static struct iommu_ops intel_iommu_ops = { .unmap = intel_iommu_unmap_range, .iova_to_phys = intel_iommu_iova_to_phys, }; + +static void __devinit quirk_iommu_rwbf(struct pci_dev *dev) +{ + /* + * Mobile 4 Series Chipset neglects to set RWBF capability, + * but needs it: + */ + printk(KERN_INFO "DMAR: Forcing write-buffer flush capability\n"); + rwbf_quirk = 1; +} + +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2a40, quirk_iommu_rwbf); diff --git a/drivers/pci/intr_remapping.c b/drivers/pci/intr_remapping.c index f78371b2252..45effc5726c 100644 --- a/drivers/pci/intr_remapping.c +++ b/drivers/pci/intr_remapping.c @@ -207,7 +207,7 @@ int alloc_irte(struct intel_iommu *iommu, int irq, u16 count) return index; } -static void qi_flush_iec(struct intel_iommu *iommu, int index, int mask) +static int qi_flush_iec(struct intel_iommu *iommu, int index, int mask) { struct qi_desc desc; @@ -215,7 +215,7 @@ static void qi_flush_iec(struct intel_iommu *iommu, int index, int mask) | QI_IEC_SELECTIVE; desc.high = 0; - qi_submit_sync(&desc, iommu); + return qi_submit_sync(&desc, iommu); } int map_irq_to_irte_handle(int irq, u16 *sub_handle) @@ -283,6 +283,7 @@ int clear_irte_irq(int irq, struct intel_iommu *iommu, u16 index) int modify_irte(int irq, struct irte *irte_modified) { + int rc; int index; struct irte *irte; struct intel_iommu *iommu; @@ -303,14 +304,15 @@ int modify_irte(int irq, struct irte *irte_modified) set_64bit((unsigned long *)irte, irte_modified->low | (1 << 1)); __iommu_flush_cache(iommu, irte, sizeof(*irte)); - qi_flush_iec(iommu, index, 0); - + rc = qi_flush_iec(iommu, index, 0); spin_unlock(&irq_2_ir_lock); - return 0; + + return rc; } int flush_irte(int irq) { + int rc; int index; struct intel_iommu *iommu; struct irq_2_iommu *irq_iommu; @@ -326,10 +328,10 @@ int flush_irte(int irq) index = irq_iommu->irte_index + irq_iommu->sub_handle; - qi_flush_iec(iommu, index, irq_iommu->irte_mask); + rc = qi_flush_iec(iommu, index, irq_iommu->irte_mask); spin_unlock(&irq_2_ir_lock); - return 0; + return rc; } struct intel_iommu *map_ioapic_to_ir(int apic) @@ -355,6 +357,7 @@ struct intel_iommu *map_dev_to_ir(struct pci_dev *dev) int free_irte(int irq) { + int rc = 0; int index, i; struct irte *irte; struct intel_iommu *iommu; @@ -375,7 +378,7 @@ int free_irte(int irq) if (!irq_iommu->sub_handle) { for (i = 0; i < (1 << irq_iommu->irte_mask); i++) set_64bit((unsigned long *)irte, 0); - qi_flush_iec(iommu, index, irq_iommu->irte_mask); + rc = qi_flush_iec(iommu, index, irq_iommu->irte_mask); } irq_iommu->iommu = NULL; @@ -385,7 +388,7 @@ int free_irte(int irq) spin_unlock(&irq_2_ir_lock); - return 0; + return rc; } static void iommu_set_intr_remapping(struct intel_iommu *iommu, int mode) diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 94363115a42..b3866ad5022 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -62,6 +62,7 @@ config DELL_LAPTOP depends on EXPERIMENTAL depends on BACKLIGHT_CLASS_DEVICE depends on RFKILL + depends on POWER_SUPPLY default n ---help--- This driver adds support for rfkill and backlight control to Dell @@ -301,6 +302,7 @@ config INTEL_MENLOW config EEEPC_LAPTOP tristate "Eee PC Hotkey Driver (EXPERIMENTAL)" depends on ACPI + depends on INPUT depends on EXPERIMENTAL select BACKLIGHT_CLASS_DEVICE select HWMON diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index 65dc41540c6..45940f31fe9 100644 --- a/drivers/platform/x86/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c @@ -166,6 +166,7 @@ struct fujitsu_hotkey_t { struct platform_device *pf_device; struct kfifo *fifo; spinlock_t fifo_lock; + int rfkill_supported; int rfkill_state; int logolamp_registered; int kblamps_registered; @@ -526,7 +527,7 @@ static ssize_t show_lid_state(struct device *dev, struct device_attribute *attr, char *buf) { - if (fujitsu_hotkey->rfkill_state == UNSUPPORTED_CMD) + if (!(fujitsu_hotkey->rfkill_supported & 0x100)) return sprintf(buf, "unknown\n"); if (fujitsu_hotkey->rfkill_state & 0x100) return sprintf(buf, "open\n"); @@ -538,7 +539,7 @@ static ssize_t show_dock_state(struct device *dev, struct device_attribute *attr, char *buf) { - if (fujitsu_hotkey->rfkill_state == UNSUPPORTED_CMD) + if (!(fujitsu_hotkey->rfkill_supported & 0x200)) return sprintf(buf, "unknown\n"); if (fujitsu_hotkey->rfkill_state & 0x200) return sprintf(buf, "docked\n"); @@ -550,7 +551,7 @@ static ssize_t show_radios_state(struct device *dev, struct device_attribute *attr, char *buf) { - if (fujitsu_hotkey->rfkill_state == UNSUPPORTED_CMD) + if (!(fujitsu_hotkey->rfkill_supported & 0x20)) return sprintf(buf, "unknown\n"); if (fujitsu_hotkey->rfkill_state & 0x20) return sprintf(buf, "on\n"); @@ -928,8 +929,17 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device) ; /* No action, result is discarded */ vdbg_printk(FUJLAPTOP_DBG_INFO, "Discarded %i ringbuffer entries\n", i); - fujitsu_hotkey->rfkill_state = - call_fext_func(FUNC_RFKILL, 0x4, 0x0, 0x0); + fujitsu_hotkey->rfkill_supported = + call_fext_func(FUNC_RFKILL, 0x0, 0x0, 0x0); + + /* Make sure our bitmask of supported functions is cleared if the + RFKILL function block is not implemented, like on the S7020. */ + if (fujitsu_hotkey->rfkill_supported == UNSUPPORTED_CMD) + fujitsu_hotkey->rfkill_supported = 0; + + if (fujitsu_hotkey->rfkill_supported) + fujitsu_hotkey->rfkill_state = + call_fext_func(FUNC_RFKILL, 0x4, 0x0, 0x0); /* Suspect this is a keymap of the application panel, print it */ printk(KERN_INFO "fujitsu-laptop: BTNI: [0x%x]\n", @@ -1005,8 +1015,9 @@ static void acpi_fujitsu_hotkey_notify(acpi_handle handle, u32 event, input = fujitsu_hotkey->input; - fujitsu_hotkey->rfkill_state = - call_fext_func(FUNC_RFKILL, 0x4, 0x0, 0x0); + if (fujitsu_hotkey->rfkill_supported) + fujitsu_hotkey->rfkill_state = + call_fext_func(FUNC_RFKILL, 0x4, 0x0, 0x0); switch (event) { case ACPI_FUJITSU_NOTIFY_CODE1: diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c index 1fd8f2193ed..4377e93a43d 100644 --- a/drivers/s390/char/sclp.c +++ b/drivers/s390/char/sclp.c @@ -280,8 +280,11 @@ sclp_dispatch_evbufs(struct sccb_header *sccb) rc = 0; for (offset = sizeof(struct sccb_header); offset < sccb->length; offset += evbuf->length) { - /* Search for event handler */ evbuf = (struct evbuf_header *) ((addr_t) sccb + offset); + /* Check for malformed hardware response */ + if (evbuf->length == 0) + break; + /* Search for event handler */ reg = NULL; list_for_each(l, &sclp_reg_list) { reg = list_entry(l, struct sclp_register, list); diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c index 50639049641..77ab6e34a10 100644 --- a/drivers/s390/char/sclp_cmd.c +++ b/drivers/s390/char/sclp_cmd.c @@ -19,6 +19,7 @@ #include <linux/memory.h> #include <asm/chpid.h> #include <asm/sclp.h> +#include <asm/setup.h> #include "sclp.h" @@ -474,6 +475,10 @@ static void __init add_memory_merged(u16 rn) goto skip_add; if (start + size > VMEM_MAX_PHYS) size = VMEM_MAX_PHYS - start; + if (memory_end_set && (start >= memory_end)) + goto skip_add; + if (memory_end_set && (start + size > memory_end)) + size = memory_end - start; add_memory(0, start, size); skip_add: first_rn = rn; diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c index a1a511bdec8..ed1e728763a 100644 --- a/drivers/scsi/ibmvscsi/ibmvfc.c +++ b/drivers/scsi/ibmvscsi/ibmvfc.c @@ -1573,9 +1573,6 @@ static int ibmvfc_queuecommand(struct scsi_cmnd *cmnd, vfc_cmd->resp_len = sizeof(vfc_cmd->rsp); vfc_cmd->cancel_key = (unsigned long)cmnd->device->hostdata; vfc_cmd->tgt_scsi_id = rport->port_id; - if ((rport->supported_classes & FC_COS_CLASS3) && - (fc_host_supported_classes(vhost->host) & FC_COS_CLASS3)) - vfc_cmd->flags = IBMVFC_CLASS_3_ERR; vfc_cmd->iu.xfer_len = scsi_bufflen(cmnd); int_to_scsilun(cmnd->device->lun, &vfc_cmd->iu.lun); memcpy(vfc_cmd->iu.cdb, cmnd->cmnd, cmnd->cmd_len); @@ -3266,6 +3263,7 @@ static int ibmvfc_alloc_target(struct ibmvfc_host *vhost, u64 scsi_id) return -ENOMEM; } + memset(tgt, 0, sizeof(*tgt)); tgt->scsi_id = scsi_id; tgt->new_scsi_id = scsi_id; tgt->vhost = vhost; @@ -3576,9 +3574,18 @@ static void ibmvfc_log_ae(struct ibmvfc_host *vhost, int events) static void ibmvfc_tgt_add_rport(struct ibmvfc_target *tgt) { struct ibmvfc_host *vhost = tgt->vhost; - struct fc_rport *rport; + struct fc_rport *rport = tgt->rport; unsigned long flags; + if (rport) { + tgt_dbg(tgt, "Setting rport roles\n"); + fc_remote_port_rolechg(rport, tgt->ids.roles); + spin_lock_irqsave(vhost->host->host_lock, flags); + ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE); + spin_unlock_irqrestore(vhost->host->host_lock, flags); + return; + } + tgt_dbg(tgt, "Adding rport\n"); rport = fc_remote_port_add(vhost->host, 0, &tgt->ids); spin_lock_irqsave(vhost->host->host_lock, flags); diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h index 87dafd0f8d4..b21e071b986 100644 --- a/drivers/scsi/ibmvscsi/ibmvfc.h +++ b/drivers/scsi/ibmvscsi/ibmvfc.h @@ -32,7 +32,7 @@ #define IBMVFC_DRIVER_VERSION "1.0.4" #define IBMVFC_DRIVER_DATE "(November 14, 2008)" -#define IBMVFC_DEFAULT_TIMEOUT 15 +#define IBMVFC_DEFAULT_TIMEOUT 60 #define IBMVFC_INIT_TIMEOUT 120 #define IBMVFC_MAX_REQUESTS_DEFAULT 100 diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.c b/drivers/scsi/ibmvscsi/ibmvscsi.c index 74d07d137da..c9aa7611e40 100644 --- a/drivers/scsi/ibmvscsi/ibmvscsi.c +++ b/drivers/scsi/ibmvscsi/ibmvscsi.c @@ -432,6 +432,7 @@ static int map_sg_data(struct scsi_cmnd *cmd, sdev_printk(KERN_ERR, cmd->device, "Can't allocate memory " "for indirect table\n"); + scsi_dma_unmap(cmd); return 0; } } diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index 257c24115de..809d32d95c7 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -1998,6 +1998,8 @@ int iscsi_host_add(struct Scsi_Host *shost, struct device *pdev) if (!shost->can_queue) shost->can_queue = ISCSI_DEF_XMIT_CMDS_MAX; + if (!shost->transportt->eh_timed_out) + shost->transportt->eh_timed_out = iscsi_eh_cmd_timed_out; return scsi_add_host(shost, pdev); } EXPORT_SYMBOL_GPL(iscsi_host_add); @@ -2020,7 +2022,6 @@ struct Scsi_Host *iscsi_host_alloc(struct scsi_host_template *sht, shost = scsi_host_alloc(sht, sizeof(struct iscsi_host) + dd_data_size); if (!shost) return NULL; - shost->transportt->eh_timed_out = iscsi_eh_cmd_timed_out; if (qdepth > ISCSI_MAX_CMD_PER_LUN || qdepth < 1) { if (qdepth != 0) diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index a8f30bdaff6..a7302480bc4 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -5258,6 +5258,7 @@ lpfc_send_els_event(struct lpfc_vport *vport, sizeof(struct lpfc_name)); break; default: + kfree(els_data); return; } memcpy(els_data->wwpn, &ndlp->nlp_portname, sizeof(struct lpfc_name)); diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index 33a3c13fd89..f4c57227ec1 100644 --- a/drivers/scsi/qla2xxx/qla_attr.c +++ b/drivers/scsi/qla2xxx/qla_attr.c @@ -1265,13 +1265,6 @@ qla24xx_vport_delete(struct fc_vport *fc_vport) test_bit(FCPORT_UPDATE_NEEDED, &vha->dpc_flags)) msleep(1000); - if (ha->mqenable) { - if (qla25xx_delete_queues(vha, 0) != QLA_SUCCESS) - qla_printk(KERN_WARNING, ha, - "Queue delete failed.\n"); - vha->req_ques[0] = ha->req_q_map[0]->id; - } - qla24xx_disable_vp(vha); fc_remove_host(vha->host); @@ -1293,6 +1286,12 @@ qla24xx_vport_delete(struct fc_vport *fc_vport) vha->host_no, vha->vp_idx, vha)); } + if (ha->mqenable) { + if (qla25xx_delete_queues(vha, 0) != QLA_SUCCESS) + qla_printk(KERN_WARNING, ha, + "Queue delete failed.\n"); + } + scsi_host_put(vha->host); qla_printk(KERN_INFO, ha, "vport %d deleted\n", id); return 0; diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index 023ee77fb02..e0c5bb54b25 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -2135,6 +2135,7 @@ struct qla_msix_entry { /* Work events. */ enum qla_work_type { QLA_EVT_AEN, + QLA_EVT_IDC_ACK, }; @@ -2149,6 +2150,10 @@ struct qla_work_evt { enum fc_host_event_code code; u32 data; } aen; + struct { +#define QLA_IDC_ACK_REGS 7 + uint16_t mb[QLA_IDC_ACK_REGS]; + } idc_ack; } u; }; diff --git a/drivers/scsi/qla2xxx/qla_devtbl.h b/drivers/scsi/qla2xxx/qla_devtbl.h index d78d35e681a..d6ea69df7c5 100644 --- a/drivers/scsi/qla2xxx/qla_devtbl.h +++ b/drivers/scsi/qla2xxx/qla_devtbl.h @@ -72,7 +72,7 @@ static char *qla2x00_model_name[QLA_MODEL_NAMES*2] = { "QLA2462", "Sun PCI-X 2.0 to 4Gb FC, Dual Channel", /* 0x141 */ "QLE2460", "Sun PCI-Express to 2Gb FC, Single Channel", /* 0x142 */ "QLE2462", "Sun PCI-Express to 4Gb FC, Single Channel", /* 0x143 */ - "QEM2462" "Server I/O Module 4Gb FC, Dual Channel", /* 0x144 */ + "QEM2462", "Server I/O Module 4Gb FC, Dual Channel", /* 0x144 */ "QLE2440", "PCI-Express to 4Gb FC, Single Channel", /* 0x145 */ "QLE2464", "PCI-Express to 4Gb FC, Quad Channel", /* 0x146 */ "QLA2440", "PCI-X 2.0 to 4Gb FC, Single Channel", /* 0x147 */ diff --git a/drivers/scsi/qla2xxx/qla_fw.h b/drivers/scsi/qla2xxx/qla_fw.h index 7abb045a041..ffff4255408 100644 --- a/drivers/scsi/qla2xxx/qla_fw.h +++ b/drivers/scsi/qla2xxx/qla_fw.h @@ -1402,6 +1402,8 @@ struct access_chip_rsp_84xx { #define MBA_IDC_NOTIFY 0x8101 #define MBA_IDC_TIME_EXT 0x8102 +#define MBC_IDC_ACK 0x101 + struct nvram_81xx { /* NVRAM header. */ uint8_t id[4]; diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h index a336b4bc81a..6de283f8f11 100644 --- a/drivers/scsi/qla2xxx/qla_gbl.h +++ b/drivers/scsi/qla2xxx/qla_gbl.h @@ -72,6 +72,7 @@ extern int qla2x00_loop_reset(scsi_qla_host_t *); extern void qla2x00_abort_all_cmds(scsi_qla_host_t *, int); extern int qla2x00_post_aen_work(struct scsi_qla_host *, enum fc_host_event_code, u32); +extern int qla2x00_post_idc_ack_work(struct scsi_qla_host *, uint16_t *); extern void qla2x00_abort_fcport_cmds(fc_port_t *); extern struct scsi_qla_host *qla2x00_create_host(struct scsi_host_template *, @@ -266,6 +267,8 @@ qla2x00_set_idma_speed(scsi_qla_host_t *, uint16_t, uint16_t, uint16_t *); extern int qla84xx_verify_chip(struct scsi_qla_host *, uint16_t *); +extern int qla81xx_idc_ack(scsi_qla_host_t *, uint16_t *); + /* * Global Function Prototypes in qla_isr.c source file. */ @@ -376,10 +379,8 @@ extern int qla2x00_dfs_remove(scsi_qla_host_t *); /* Globa function prototypes for multi-q */ extern int qla25xx_request_irq(struct rsp_que *); -extern int qla25xx_init_req_que(struct scsi_qla_host *, struct req_que *, - uint8_t); -extern int qla25xx_init_rsp_que(struct scsi_qla_host *, struct rsp_que *, - uint8_t); +extern int qla25xx_init_req_que(struct scsi_qla_host *, struct req_que *); +extern int qla25xx_init_rsp_que(struct scsi_qla_host *, struct rsp_que *); extern int qla25xx_create_req_que(struct qla_hw_data *, uint16_t, uint8_t, uint16_t, uint8_t, uint8_t); extern int qla25xx_create_rsp_que(struct qla_hw_data *, uint16_t, uint8_t, diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index f6368a1d302..986501759ad 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -1226,9 +1226,8 @@ qla24xx_config_rings(struct scsi_qla_host *vha) icb->firmware_options_2 |= __constant_cpu_to_le32(BIT_18); - icb->firmware_options_2 |= __constant_cpu_to_le32(BIT_22); + icb->firmware_options_2 &= __constant_cpu_to_le32(~BIT_22); icb->firmware_options_2 |= __constant_cpu_to_le32(BIT_23); - ha->rsp_q_map[0]->options = icb->firmware_options_2; WRT_REG_DWORD(®->isp25mq.req_q_in, 0); WRT_REG_DWORD(®->isp25mq.req_q_out, 0); @@ -3493,7 +3492,7 @@ qla25xx_init_queues(struct qla_hw_data *ha) rsp = ha->rsp_q_map[i]; if (rsp) { rsp->options &= ~BIT_0; - ret = qla25xx_init_rsp_que(base_vha, rsp, rsp->options); + ret = qla25xx_init_rsp_que(base_vha, rsp); if (ret != QLA_SUCCESS) DEBUG2_17(printk(KERN_WARNING "%s Rsp que:%d init failed\n", __func__, @@ -3507,7 +3506,7 @@ qla25xx_init_queues(struct qla_hw_data *ha) if (req) { /* Clear outstanding commands array. */ req->options &= ~BIT_0; - ret = qla25xx_init_req_que(base_vha, req, req->options); + ret = qla25xx_init_req_que(base_vha, req); if (ret != QLA_SUCCESS) DEBUG2_17(printk(KERN_WARNING "%s Req que:%d init failed\n", __func__, diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index e28ad81baf1..f250e5b7897 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -266,6 +266,40 @@ qla2x00_mbx_completion(scsi_qla_host_t *vha, uint16_t mb0) } } +static void +qla81xx_idc_event(scsi_qla_host_t *vha, uint16_t aen, uint16_t descr) +{ + static char *event[] = + { "Complete", "Request Notification", "Time Extension" }; + int rval; + struct device_reg_24xx __iomem *reg24 = &vha->hw->iobase->isp24; + uint16_t __iomem *wptr; + uint16_t cnt, timeout, mb[QLA_IDC_ACK_REGS]; + + /* Seed data -- mailbox1 -> mailbox7. */ + wptr = (uint16_t __iomem *)®24->mailbox1; + for (cnt = 0; cnt < QLA_IDC_ACK_REGS; cnt++, wptr++) + mb[cnt] = RD_REG_WORD(wptr); + + DEBUG2(printk("scsi(%ld): Inter-Driver Commucation %s -- " + "%04x %04x %04x %04x %04x %04x %04x.\n", vha->host_no, + event[aen & 0xff], + mb[0], mb[1], mb[2], mb[3], mb[4], mb[5], mb[6])); + + /* Acknowledgement needed? [Notify && non-zero timeout]. */ + timeout = (descr >> 8) & 0xf; + if (aen != MBA_IDC_NOTIFY || !timeout) + return; + + DEBUG2(printk("scsi(%ld): Inter-Driver Commucation %s -- " + "ACK timeout=%d.\n", vha->host_no, event[aen & 0xff], timeout)); + + rval = qla2x00_post_idc_ack_work(vha, mb); + if (rval != QLA_SUCCESS) + qla_printk(KERN_WARNING, vha->hw, + "IDC failed to post ACK.\n"); +} + /** * qla2x00_async_event() - Process aynchronous events. * @ha: SCSI driver HA context @@ -714,21 +748,9 @@ skip_rio: "%04x %04x %04x\n", vha->host_no, mb[1], mb[2], mb[3])); break; case MBA_IDC_COMPLETE: - DEBUG2(printk("scsi(%ld): Inter-Driver Commucation " - "Complete -- %04x %04x %04x\n", vha->host_no, mb[1], mb[2], - mb[3])); - break; case MBA_IDC_NOTIFY: - DEBUG2(printk("scsi(%ld): Inter-Driver Commucation " - "Request Notification -- %04x %04x %04x\n", vha->host_no, - mb[1], mb[2], mb[3])); - /**** Mailbox registers 4 - 7 valid!!! */ - break; case MBA_IDC_TIME_EXT: - DEBUG2(printk("scsi(%ld): Inter-Driver Commucation " - "Time Extension -- %04x %04x %04x\n", vha->host_no, mb[1], - mb[2], mb[3])); - /**** Mailbox registers 4 - 7 valid!!! */ + qla81xx_idc_event(vha, mb[0], mb[1]); break; } @@ -1707,7 +1729,6 @@ qla25xx_msix_rsp_q(int irq, void *dev_id) struct qla_hw_data *ha; struct rsp_que *rsp; struct device_reg_24xx __iomem *reg; - uint16_t msix_disabled_hccr = 0; rsp = (struct rsp_que *) dev_id; if (!rsp) { @@ -1720,17 +1741,8 @@ qla25xx_msix_rsp_q(int irq, void *dev_id) spin_lock_irq(&ha->hardware_lock); - msix_disabled_hccr = rsp->options; - if (!rsp->id) - msix_disabled_hccr &= __constant_cpu_to_le32(BIT_22); - else - msix_disabled_hccr &= __constant_cpu_to_le32(BIT_6); - qla24xx_process_response_queue(rsp); - if (!msix_disabled_hccr) - WRT_REG_DWORD(®->hccr, HCCRX_CLR_RISC_INT); - spin_unlock_irq(&ha->hardware_lock); return IRQ_HANDLED; diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c index f94ffbb98e9..4c7504cb399 100644 --- a/drivers/scsi/qla2xxx/qla_mbx.c +++ b/drivers/scsi/qla2xxx/qla_mbx.c @@ -3090,8 +3090,7 @@ verify_done: } int -qla25xx_init_req_que(struct scsi_qla_host *vha, struct req_que *req, - uint8_t options) +qla25xx_init_req_que(struct scsi_qla_host *vha, struct req_que *req) { int rval; unsigned long flags; @@ -3101,7 +3100,7 @@ qla25xx_init_req_que(struct scsi_qla_host *vha, struct req_que *req, struct qla_hw_data *ha = vha->hw; mcp->mb[0] = MBC_INITIALIZE_MULTIQ; - mcp->mb[1] = options; + mcp->mb[1] = req->options; mcp->mb[2] = MSW(LSD(req->dma)); mcp->mb[3] = LSW(LSD(req->dma)); mcp->mb[6] = MSW(MSD(req->dma)); @@ -3128,7 +3127,7 @@ qla25xx_init_req_que(struct scsi_qla_host *vha, struct req_que *req, mcp->tov = 60; spin_lock_irqsave(&ha->hardware_lock, flags); - if (!(options & BIT_0)) { + if (!(req->options & BIT_0)) { WRT_REG_DWORD(®->req_q_in, 0); WRT_REG_DWORD(®->req_q_out, 0); } @@ -3142,8 +3141,7 @@ qla25xx_init_req_que(struct scsi_qla_host *vha, struct req_que *req, } int -qla25xx_init_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp, - uint8_t options) +qla25xx_init_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp) { int rval; unsigned long flags; @@ -3153,7 +3151,7 @@ qla25xx_init_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp, struct qla_hw_data *ha = vha->hw; mcp->mb[0] = MBC_INITIALIZE_MULTIQ; - mcp->mb[1] = options; + mcp->mb[1] = rsp->options; mcp->mb[2] = MSW(LSD(rsp->dma)); mcp->mb[3] = LSW(LSD(rsp->dma)); mcp->mb[6] = MSW(MSD(rsp->dma)); @@ -3178,7 +3176,7 @@ qla25xx_init_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp, mcp->tov = 60; spin_lock_irqsave(&ha->hardware_lock, flags); - if (!(options & BIT_0)) { + if (!(rsp->options & BIT_0)) { WRT_REG_DWORD(®->rsp_q_out, 0); WRT_REG_DWORD(®->rsp_q_in, 0); } @@ -3193,3 +3191,29 @@ qla25xx_init_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp, return rval; } +int +qla81xx_idc_ack(scsi_qla_host_t *vha, uint16_t *mb) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + + DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no)); + + mcp->mb[0] = MBC_IDC_ACK; + memcpy(&mcp->mb[1], mb, QLA_IDC_ACK_REGS * sizeof(uint16_t)); + mcp->out_mb = MBX_7|MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0; + mcp->in_mb = MBX_0; + mcp->tov = MBX_TOV_SECONDS; + mcp->flags = 0; + rval = qla2x00_mailbox_command(vha, mcp); + + if (rval != QLA_SUCCESS) { + DEBUG2_3_11(printk("%s(%ld): failed=%x (%x).\n", __func__, + vha->host_no, rval, mcp->mb[0])); + } else { + DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no)); + } + + return rval; +} diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c index f53179c4642..3f23932210c 100644 --- a/drivers/scsi/qla2xxx/qla_mid.c +++ b/drivers/scsi/qla2xxx/qla_mid.c @@ -396,7 +396,7 @@ qla24xx_create_vhost(struct fc_vport *fc_vport) qla2x00_start_timer(vha, qla2x00_timer, WATCH_INTERVAL); - memset(vha->req_ques, 0, sizeof(vha->req_ques) * QLA_MAX_HOST_QUES); + memset(vha->req_ques, 0, sizeof(vha->req_ques)); vha->req_ques[0] = ha->req_q_map[0]->id; host->can_queue = ha->req_q_map[0]->length + 128; host->this_id = 255; @@ -471,7 +471,7 @@ qla25xx_delete_req_que(struct scsi_qla_host *vha, struct req_que *req) if (req) { req->options |= BIT_0; - ret = qla25xx_init_req_que(vha, req, req->options); + ret = qla25xx_init_req_que(vha, req); } if (ret == QLA_SUCCESS) qla25xx_free_req_que(vha, req); @@ -486,7 +486,7 @@ qla25xx_delete_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp) if (rsp) { rsp->options |= BIT_0; - ret = qla25xx_init_rsp_que(vha, rsp, rsp->options); + ret = qla25xx_init_rsp_que(vha, rsp); } if (ret == QLA_SUCCESS) qla25xx_free_rsp_que(vha, rsp); @@ -502,7 +502,7 @@ int qla25xx_update_req_que(struct scsi_qla_host *vha, uint8_t que, uint8_t qos) req->options |= BIT_3; req->qos = qos; - ret = qla25xx_init_req_que(vha, req, req->options); + ret = qla25xx_init_req_que(vha, req); if (ret != QLA_SUCCESS) DEBUG2_17(printk(KERN_WARNING "%s failed\n", __func__)); /* restore options bit */ @@ -632,7 +632,7 @@ qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options, req->max_q_depth = ha->req_q_map[0]->max_q_depth; mutex_unlock(&ha->vport_lock); - ret = qla25xx_init_req_que(base_vha, req, options); + ret = qla25xx_init_req_que(base_vha, req); if (ret != QLA_SUCCESS) { qla_printk(KERN_WARNING, ha, "%s failed\n", __func__); mutex_lock(&ha->vport_lock); @@ -710,7 +710,7 @@ qla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options, if (ret) goto que_failed; - ret = qla25xx_init_rsp_que(base_vha, rsp, options); + ret = qla25xx_init_rsp_que(base_vha, rsp); if (ret != QLA_SUCCESS) { qla_printk(KERN_WARNING, ha, "%s failed\n", __func__); mutex_lock(&ha->vport_lock); diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index c11f872d3e1..2f5f72531e2 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -2522,6 +2522,19 @@ qla2x00_post_aen_work(struct scsi_qla_host *vha, enum fc_host_event_code code, return qla2x00_post_work(vha, e, 1); } +int +qla2x00_post_idc_ack_work(struct scsi_qla_host *vha, uint16_t *mb) +{ + struct qla_work_evt *e; + + e = qla2x00_alloc_work(vha, QLA_EVT_IDC_ACK, 1); + if (!e) + return QLA_FUNCTION_FAILED; + + memcpy(e->u.idc_ack.mb, mb, QLA_IDC_ACK_REGS * sizeof(uint16_t)); + return qla2x00_post_work(vha, e, 1); +} + static void qla2x00_do_work(struct scsi_qla_host *vha) { @@ -2539,6 +2552,9 @@ qla2x00_do_work(struct scsi_qla_host *vha) fc_host_post_event(vha->host, fc_get_event_number(), e->u.aen.code, e->u.aen.data); break; + case QLA_EVT_IDC_ACK: + qla81xx_idc_ack(vha, e->u.idc_ack.mb); + break; } if (e->flags & QLA_EVT_FLAG_FREE) kfree(e); diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c index 9c3b694c049..284827926ef 100644 --- a/drivers/scsi/qla2xxx/qla_sup.c +++ b/drivers/scsi/qla2xxx/qla_sup.c @@ -684,7 +684,7 @@ qla2xxx_get_flt_info(scsi_qla_host_t *vha, uint32_t flt_addr) "end=0x%x size=0x%x.\n", le32_to_cpu(region->code), start, le32_to_cpu(region->end) >> 2, le32_to_cpu(region->size))); - switch (le32_to_cpu(region->code)) { + switch (le32_to_cpu(region->code) & 0xff) { case FLT_REG_FW: ha->flt_region_fw = start; break; diff --git a/drivers/scsi/qla2xxx/qla_version.h b/drivers/scsi/qla2xxx/qla_version.h index cfa4c11a479..79f7053da99 100644 --- a/drivers/scsi/qla2xxx/qla_version.h +++ b/drivers/scsi/qla2xxx/qla_version.h @@ -7,7 +7,7 @@ /* * Driver version */ -#define QLA2XXX_VERSION "8.03.00-k2" +#define QLA2XXX_VERSION "8.03.00-k3" #define QLA_DRIVER_MAJOR_VER 8 #define QLA_DRIVER_MINOR_VER 3 diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 66505bb7941..8f4de20c9de 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -317,6 +317,7 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget, return sdev; out_device_destroy: + scsi_device_set_state(sdev, SDEV_DEL); transport_destroy_device(&sdev->sdev_gendev); put_device(&sdev->sdev_gendev); out: diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 8f0bd3f7a59..516925d8b57 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -1078,7 +1078,7 @@ sg_ioctl(struct inode *inode, struct file *filp, case BLKTRACESETUP: return blk_trace_setup(sdp->device->request_queue, sdp->disk->disk_name, - sdp->device->sdev_gendev.devt, + MKDEV(SCSI_GENERIC_MAJOR, sdp->index), (char *)arg); case BLKTRACESTART: return blk_trace_startstop(sdp->device->request_queue, 1); diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c index 0d934bfbdd9..b4b39811b44 100644 --- a/drivers/serial/8250.c +++ b/drivers/serial/8250.c @@ -2083,6 +2083,20 @@ static int serial8250_startup(struct uart_port *port) serial8250_set_mctrl(&up->port, up->port.mctrl); + /* Serial over Lan (SoL) hack: + Intel 8257x Gigabit ethernet chips have a + 16550 emulation, to be used for Serial Over Lan. + Those chips take a longer time than a normal + serial device to signalize that a transmission + data was queued. Due to that, the above test generally + fails. One solution would be to delay the reading of + iir. However, this is not reliable, since the timeout + is variable. So, let's just don't test if we receive + TX irq. This way, we'll never enable UART_BUG_TXEN. + */ + if (up->port.flags & UPF_NO_TXEN_TEST) + goto dont_test_tx_en; + /* * Do a quick test to see if we receive an * interrupt when we enable the TX irq. @@ -2102,6 +2116,7 @@ static int serial8250_startup(struct uart_port *port) up->bugs &= ~UART_BUG_TXEN; } +dont_test_tx_en: spin_unlock_irqrestore(&up->port.lock, flags); /* diff --git a/drivers/serial/8250_pci.c b/drivers/serial/8250_pci.c index 536d8e510f6..533f82025ad 100644 --- a/drivers/serial/8250_pci.c +++ b/drivers/serial/8250_pci.c @@ -798,6 +798,21 @@ pci_default_setup(struct serial_private *priv, return setup_port(priv, port, bar, offset, board->reg_shift); } +static int skip_tx_en_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_port *port, int idx) +{ + port->flags |= UPF_NO_TXEN_TEST; + printk(KERN_DEBUG "serial8250: skipping TxEn test for device " + "[%04x:%04x] subsystem [%04x:%04x]\n", + priv->dev->vendor, + priv->dev->device, + priv->dev->subsystem_vendor, + priv->dev->subsystem_device); + + return pci_default_setup(priv, board, port, idx); +} + /* This should be in linux/pci_ids.h */ #define PCI_VENDOR_ID_SBSMODULARIO 0x124B #define PCI_SUBVENDOR_ID_SBSMODULARIO 0x124B @@ -864,6 +879,27 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .init = pci_inteli960ni_init, .setup = pci_default_setup, }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_8257X_SOL, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = skip_tx_en_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_82573L_SOL, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = skip_tx_en_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_82573E_SOL, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = skip_tx_en_setup, + }, /* * ITE */ diff --git a/drivers/serial/atmel_serial.c b/drivers/serial/atmel_serial.c index 89362d733d6..8f58f7ff0dd 100644 --- a/drivers/serial/atmel_serial.c +++ b/drivers/serial/atmel_serial.c @@ -877,6 +877,10 @@ static int atmel_startup(struct uart_port *port) } } + /* Save current CSR for comparison in atmel_tasklet_func() */ + atmel_port->irq_status_prev = UART_GET_CSR(port); + atmel_port->irq_status = atmel_port->irq_status_prev; + /* * Finally, enable the serial port */ diff --git a/drivers/serial/jsm/jsm_driver.c b/drivers/serial/jsm/jsm_driver.c index 92187e28608..ac79cbe4c2c 100644 --- a/drivers/serial/jsm/jsm_driver.c +++ b/drivers/serial/jsm/jsm_driver.c @@ -84,6 +84,8 @@ static int jsm_probe_one(struct pci_dev *pdev, const struct pci_device_id *ent) brd->pci_dev = pdev; if (pdev->device == PCIE_DEVICE_ID_NEO_4_IBM) brd->maxports = 4; + else if (pdev->device == PCI_DEVICE_ID_DIGI_NEO_8) + brd->maxports = 8; else brd->maxports = 2; @@ -212,6 +214,7 @@ static struct pci_device_id jsm_pci_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2RJ45), 0, 0, 2 }, { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2RJ45PRI), 0, 0, 3 }, { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCIE_DEVICE_ID_NEO_4_IBM), 0, 0, 4 }, + { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_NEO_8), 0, 0, 5 }, { 0, } }; MODULE_DEVICE_TABLE(pci, jsm_pci_tbl); diff --git a/drivers/spi/spi_gpio.c b/drivers/spi/spi_gpio.c index 49698cabc30..f5ed9721aab 100644 --- a/drivers/spi/spi_gpio.c +++ b/drivers/spi/spi_gpio.c @@ -114,7 +114,7 @@ static inline void setmosi(const struct spi_device *spi, int is_on) static inline int getmiso(const struct spi_device *spi) { - return gpio_get_value(SPI_MISO_GPIO); + return !!gpio_get_value(SPI_MISO_GPIO); } #undef pdata diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index c54fc40458b..a4301dc02d2 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -298,19 +298,6 @@ int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t message) EXPORT_SYMBOL_GPL(usb_hcd_pci_suspend); /** - * usb_hcd_pci_resume_early - resume a PCI-based HCD before IRQs are enabled - * @dev: USB Host Controller being resumed - * - * Store this function in the HCD's struct pci_driver as .resume_early. - */ -int usb_hcd_pci_resume_early(struct pci_dev *dev) -{ - pci_restore_state(dev); - return 0; -} -EXPORT_SYMBOL_GPL(usb_hcd_pci_resume_early); - -/** * usb_hcd_pci_resume - power management resume of a PCI-based HCD * @dev: USB Host Controller being resumed * @@ -333,6 +320,8 @@ int usb_hcd_pci_resume(struct pci_dev *dev) } #endif + pci_restore_state(dev); + hcd = pci_get_drvdata(dev); if (hcd->state != HC_STATE_SUSPENDED) { dev_dbg(hcd->self.controller, diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index 5b94a56bec2..f750eb1ab59 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -257,7 +257,6 @@ extern void usb_hcd_pci_remove(struct pci_dev *dev); #ifdef CONFIG_PM extern int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t msg); -extern int usb_hcd_pci_resume_early(struct pci_dev *dev); extern int usb_hcd_pci_resume(struct pci_dev *dev); #endif /* CONFIG_PM */ diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index 9b36205c575..0ce4e281984 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -904,8 +904,8 @@ static void pxa25x_ep_fifo_flush(struct usb_ep *_ep) /* most IN status is the same, but ISO can't stall */ *ep->reg_udccs = UDCCS_BI_TPC|UDCCS_BI_FTF|UDCCS_BI_TUR - | (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) - ? 0 : UDCCS_BI_SST; + | (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC + ? 0 : UDCCS_BI_SST); } diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index bb21fb0a496..abb9a7706ec 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -432,7 +432,6 @@ static struct pci_driver ehci_pci_driver = { #ifdef CONFIG_PM .suspend = usb_hcd_pci_suspend, - .resume_early = usb_hcd_pci_resume_early, .resume = usb_hcd_pci_resume, #endif .shutdown = usb_hcd_pci_shutdown, diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index 5d625c3fd42..f9961b4c0da 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -487,7 +487,6 @@ static struct pci_driver ohci_pci_driver = { #ifdef CONFIG_PM .suspend = usb_hcd_pci_suspend, - .resume_early = usb_hcd_pci_resume_early, .resume = usb_hcd_pci_resume, #endif diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 944f7e0ca4d..cf5e4cf7ea4 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -942,7 +942,6 @@ static struct pci_driver uhci_pci_driver = { #ifdef CONFIG_PM .suspend = usb_hcd_pci_suspend, - .resume_early = usb_hcd_pci_resume_early, .resume = usb_hcd_pci_resume, #endif /* PM */ }; diff --git a/drivers/usb/host/whci/asl.c b/drivers/usb/host/whci/asl.c index 2291c5f5af5..958751ccea4 100644 --- a/drivers/usb/host/whci/asl.c +++ b/drivers/usb/host/whci/asl.c @@ -227,13 +227,13 @@ void scan_async_work(struct work_struct *work) * Now that the ASL is updated, complete the removal of any * removed qsets. */ - spin_lock(&whc->lock); + spin_lock_irq(&whc->lock); list_for_each_entry_safe(qset, t, &whc->async_removed_list, list_node) { qset_remove_complete(whc, qset); } - spin_unlock(&whc->lock); + spin_unlock_irq(&whc->lock); } /** diff --git a/drivers/usb/host/whci/pzl.c b/drivers/usb/host/whci/pzl.c index 7dc85a0bee7..df8b85f0709 100644 --- a/drivers/usb/host/whci/pzl.c +++ b/drivers/usb/host/whci/pzl.c @@ -255,13 +255,13 @@ void scan_periodic_work(struct work_struct *work) * Now that the PZL is updated, complete the removal of any * removed qsets. */ - spin_lock(&whc->lock); + spin_lock_irq(&whc->lock); list_for_each_entry_safe(qset, t, &whc->periodic_removed_list, list_node) { qset_remove_complete(whc, qset); } - spin_unlock(&whc->lock); + spin_unlock_irq(&whc->lock); } /** diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index bf0af660df8..fb19803060c 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -1054,10 +1054,7 @@ config FB_RIVA_BACKLIGHT config FB_I810 tristate "Intel 810/815 support (EXPERIMENTAL)" - depends on EXPERIMENTAL && PCI && X86_32 - select AGP - select AGP_INTEL - select FB + depends on EXPERIMENTAL && FB && PCI && X86_32 && AGP_INTEL select FB_MODE_HELPERS select FB_CFB_FILLRECT select FB_CFB_COPYAREA @@ -1120,10 +1117,7 @@ config FB_CARILLO_RANCH config FB_INTEL tristate "Intel 830M/845G/852GM/855GM/865G/915G/945G/945GM/965G/965GM support (EXPERIMENTAL)" - depends on EXPERIMENTAL && PCI && X86 - select FB - select AGP - select AGP_INTEL + depends on EXPERIMENTAL && FB && PCI && X86 && AGP_INTEL select FB_MODE_HELPERS select FB_CFB_FILLRECT select FB_CFB_COPYAREA diff --git a/drivers/video/atafb.c b/drivers/video/atafb.c index 8058572a742..018850c116c 100644 --- a/drivers/video/atafb.c +++ b/drivers/video/atafb.c @@ -841,7 +841,7 @@ static int tt_detect(void) tt_dmasnd.ctrl = DMASND_CTRL_OFF; udelay(20); /* wait a while for things to settle down */ } - mono_moni = (mfp.par_dt_reg & 0x80) == 0; + mono_moni = (st_mfp.par_dt_reg & 0x80) == 0; tt_get_par(&par); tt_encode_var(&atafb_predefined[0], &par); @@ -2035,7 +2035,7 @@ static int stste_detect(void) tt_dmasnd.ctrl = DMASND_CTRL_OFF; udelay(20); /* wait a while for things to settle down */ } - mono_moni = (mfp.par_dt_reg & 0x80) == 0; + mono_moni = (st_mfp.par_dt_reg & 0x80) == 0; stste_get_par(&par); stste_encode_var(&atafb_predefined[0], &par); @@ -2086,20 +2086,20 @@ static void st_ovsc_switch(void) return; local_irq_save(flags); - mfp.tim_ct_b = 0x10; - mfp.active_edge |= 8; - mfp.tim_ct_b = 0; - mfp.tim_dt_b = 0xf0; - mfp.tim_ct_b = 8; - while (mfp.tim_dt_b > 1) /* TOS does it this way, don't ask why */ + st_mfp.tim_ct_b = 0x10; + st_mfp.active_edge |= 8; + st_mfp.tim_ct_b = 0; + st_mfp.tim_dt_b = 0xf0; + st_mfp.tim_ct_b = 8; + while (st_mfp.tim_dt_b > 1) /* TOS does it this way, don't ask why */ ; - new = mfp.tim_dt_b; + new = st_mfp.tim_dt_b; do { udelay(LINE_DELAY); old = new; - new = mfp.tim_dt_b; + new = st_mfp.tim_dt_b; } while (old != new); - mfp.tim_ct_b = 0x10; + st_mfp.tim_ct_b = 0x10; udelay(SYNC_DELAY); if (atari_switches & ATARI_SWITCH_OVSC_IKBD) diff --git a/drivers/video/aty/aty128fb.c b/drivers/video/aty/aty128fb.c index e6e299feb51..2181ce4d7eb 100644 --- a/drivers/video/aty/aty128fb.c +++ b/drivers/video/aty/aty128fb.c @@ -2365,7 +2365,6 @@ static void fbcon_aty128_bmove(struct display *p, int sy, int sx, int dy, int dx static void aty128_set_suspend(struct aty128fb_par *par, int suspend) { u32 pmgt; - u16 pwr_command; struct pci_dev *pdev = par->pdev; if (!par->pm_reg) diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 09a3d5522b4..325c10ff6a2 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -406,7 +406,7 @@ config ITCO_WDT ---help--- Hardware driver for the intel TCO timer based watchdog devices. These drivers are included in the Intel 82801 I/O Controller - Hub family (from ICH0 up to ICH8) and in the Intel 6300ESB + Hub family (from ICH0 up to ICH10) and in the Intel 63xxESB controller hub. The TCO (Total Cost of Ownership) timer is a watchdog timer diff --git a/drivers/watchdog/at91rm9200_wdt.c b/drivers/watchdog/at91rm9200_wdt.c index 5531691f46e..e35d5458923 100644 --- a/drivers/watchdog/at91rm9200_wdt.c +++ b/drivers/watchdog/at91rm9200_wdt.c @@ -107,10 +107,10 @@ static int at91_wdt_close(struct inode *inode, struct file *file) static int at91_wdt_settimeout(int new_time) { /* - * All counting occurs at SLOW_CLOCK / 128 = 0.256 Hz + * All counting occurs at SLOW_CLOCK / 128 = 256 Hz * * Since WDV is a 16-bit counter, the maximum period is - * 65536 / 0.256 = 256 seconds. + * 65536 / 256 = 256 seconds. */ if ((new_time <= 0) || (new_time > WDT_MAX_TIME)) return -EINVAL; diff --git a/drivers/watchdog/at91sam9_wdt.c b/drivers/watchdog/at91sam9_wdt.c index b1da287f90e..a56ac84381b 100644 --- a/drivers/watchdog/at91sam9_wdt.c +++ b/drivers/watchdog/at91sam9_wdt.c @@ -18,6 +18,7 @@ #include <linux/errno.h> #include <linux/fs.h> #include <linux/init.h> +#include <linux/io.h> #include <linux/kernel.h> #include <linux/miscdevice.h> #include <linux/module.h> diff --git a/drivers/watchdog/iTCO_vendor_support.c b/drivers/watchdog/iTCO_vendor_support.c index 2474ebca88f..d8264ad0be4 100644 --- a/drivers/watchdog/iTCO_vendor_support.c +++ b/drivers/watchdog/iTCO_vendor_support.c @@ -1,7 +1,7 @@ /* * intel TCO vendor specific watchdog driver support * - * (c) Copyright 2006-2008 Wim Van Sebroeck <wim@iguana.be>. + * (c) Copyright 2006-2009 Wim Van Sebroeck <wim@iguana.be>. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -19,7 +19,7 @@ /* Module and version information */ #define DRV_NAME "iTCO_vendor_support" -#define DRV_VERSION "1.02" +#define DRV_VERSION "1.03" #define PFX DRV_NAME ": " /* Includes */ @@ -77,6 +77,26 @@ MODULE_PARM_DESC(vendorsupport, "iTCO vendor specific support mode, default=0 (n * 20.6 seconds. */ +static void supermicro_old_pre_start(unsigned long acpibase) +{ + unsigned long val32; + + /* Bit 13: TCO_EN -> 0 = Disables TCO logic generating an SMI# */ + val32 = inl(SMI_EN); + val32 &= 0xffffdfff; /* Turn off SMI clearing watchdog */ + outl(val32, SMI_EN); /* Needed to activate watchdog */ +} + +static void supermicro_old_pre_stop(unsigned long acpibase) +{ + unsigned long val32; + + /* Bit 13: TCO_EN -> 1 = Enables the TCO logic to generate SMI# */ + val32 = inl(SMI_EN); + val32 |= 0x00002000; /* Turn on SMI clearing watchdog */ + outl(val32, SMI_EN); /* Needed to deactivate watchdog */ +} + static void supermicro_old_pre_keepalive(unsigned long acpibase) { /* Reload TCO Timer (done in iTCO_wdt_keepalive) + */ @@ -228,14 +248,18 @@ static void supermicro_new_pre_set_heartbeat(unsigned int heartbeat) void iTCO_vendor_pre_start(unsigned long acpibase, unsigned int heartbeat) { - if (vendorsupport == SUPERMICRO_NEW_BOARD) + if (vendorsupport == SUPERMICRO_OLD_BOARD) + supermicro_old_pre_start(acpibase); + else if (vendorsupport == SUPERMICRO_NEW_BOARD) supermicro_new_pre_start(heartbeat); } EXPORT_SYMBOL(iTCO_vendor_pre_start); void iTCO_vendor_pre_stop(unsigned long acpibase) { - if (vendorsupport == SUPERMICRO_NEW_BOARD) + if (vendorsupport == SUPERMICRO_OLD_BOARD) + supermicro_old_pre_stop(acpibase); + else if (vendorsupport == SUPERMICRO_NEW_BOARD) supermicro_new_pre_stop(); } EXPORT_SYMBOL(iTCO_vendor_pre_stop); diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c index 5b395a4ddfd..352334947ea 100644 --- a/drivers/watchdog/iTCO_wdt.c +++ b/drivers/watchdog/iTCO_wdt.c @@ -1,7 +1,7 @@ /* - * intel TCO Watchdog Driver (Used in i82801 and i6300ESB chipsets) + * intel TCO Watchdog Driver (Used in i82801 and i63xxESB chipsets) * - * (c) Copyright 2006-2008 Wim Van Sebroeck <wim@iguana.be>. + * (c) Copyright 2006-2009 Wim Van Sebroeck <wim@iguana.be>. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -63,7 +63,7 @@ /* Module and version information */ #define DRV_NAME "iTCO_wdt" -#define DRV_VERSION "1.04" +#define DRV_VERSION "1.05" #define PFX DRV_NAME ": " /* Includes */ @@ -236,16 +236,16 @@ MODULE_DEVICE_TABLE(pci, iTCO_wdt_pci_tbl); /* Address definitions for the TCO */ /* TCO base address */ -#define TCOBASE iTCO_wdt_private.ACPIBASE + 0x60 +#define TCOBASE iTCO_wdt_private.ACPIBASE + 0x60 /* SMI Control and Enable Register */ -#define SMI_EN iTCO_wdt_private.ACPIBASE + 0x30 +#define SMI_EN iTCO_wdt_private.ACPIBASE + 0x30 #define TCO_RLD TCOBASE + 0x00 /* TCO Timer Reload and Curr. Value */ #define TCOv1_TMR TCOBASE + 0x01 /* TCOv1 Timer Initial Value */ -#define TCO_DAT_IN TCOBASE + 0x02 /* TCO Data In Register */ -#define TCO_DAT_OUT TCOBASE + 0x03 /* TCO Data Out Register */ -#define TCO1_STS TCOBASE + 0x04 /* TCO1 Status Register */ -#define TCO2_STS TCOBASE + 0x06 /* TCO2 Status Register */ +#define TCO_DAT_IN TCOBASE + 0x02 /* TCO Data In Register */ +#define TCO_DAT_OUT TCOBASE + 0x03 /* TCO Data Out Register */ +#define TCO1_STS TCOBASE + 0x04 /* TCO1 Status Register */ +#define TCO2_STS TCOBASE + 0x06 /* TCO2 Status Register */ #define TCO1_CNT TCOBASE + 0x08 /* TCO1 Control Register */ #define TCO2_CNT TCOBASE + 0x0a /* TCO2 Control Register */ #define TCOv2_TMR TCOBASE + 0x12 /* TCOv2 Timer Initial Value */ @@ -338,7 +338,6 @@ static int iTCO_wdt_unset_NO_REBOOT_bit(void) static int iTCO_wdt_start(void) { unsigned int val; - unsigned long val32; spin_lock(&iTCO_wdt_private.io_lock); @@ -351,11 +350,6 @@ static int iTCO_wdt_start(void) return -EIO; } - /* Bit 13: TCO_EN -> 0 = Disables TCO logic generating an SMI# */ - val32 = inl(SMI_EN); - val32 &= 0xffffdfff; /* Turn off SMI clearing watchdog */ - outl(val32, SMI_EN); - /* Force the timer to its reload value by writing to the TCO_RLD register */ if (iTCO_wdt_private.iTCO_version == 2) @@ -378,7 +372,6 @@ static int iTCO_wdt_start(void) static int iTCO_wdt_stop(void) { unsigned int val; - unsigned long val32; spin_lock(&iTCO_wdt_private.io_lock); @@ -390,11 +383,6 @@ static int iTCO_wdt_stop(void) outw(val, TCO1_CNT); val = inw(TCO1_CNT); - /* Bit 13: TCO_EN -> 1 = Enables the TCO logic to generate SMI# */ - val32 = inl(SMI_EN); - val32 |= 0x00002000; - outl(val32, SMI_EN); - /* Set the NO_REBOOT bit to prevent later reboots, just for sure */ iTCO_wdt_set_NO_REBOOT_bit(); @@ -649,6 +637,7 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev, int ret; u32 base_address; unsigned long RCBA; + unsigned long val32; /* * Find the ACPI/PM base I/O address which is the base @@ -695,6 +684,10 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev, ret = -EIO; goto out; } + /* Bit 13: TCO_EN -> 0 = Disables TCO logic generating an SMI# */ + val32 = inl(SMI_EN); + val32 &= 0xffffdfff; /* Turn off SMI clearing watchdog */ + outl(val32, SMI_EN); /* The TCO I/O registers reside in a 32-byte range pointed to by the TCOBASE value */ diff --git a/drivers/xen/manage.c b/drivers/xen/manage.c index 9b91617b958..56892a142ee 100644 --- a/drivers/xen/manage.c +++ b/drivers/xen/manage.c @@ -45,6 +45,13 @@ static int xen_suspend(void *data) err); return err; } + err = sysdev_suspend(PMSG_SUSPEND); + if (err) { + printk(KERN_ERR "xen_suspend: sysdev_suspend failed: %d\n", + err); + device_power_up(PMSG_RESUME); + return err; + } xen_mm_pin_all(); gnttab_suspend(); @@ -61,6 +68,7 @@ static int xen_suspend(void *data) gnttab_resume(); xen_mm_unpin_all(); + sysdev_resume(); device_power_up(PMSG_RESUME); if (!*cancelled) { |